Project Alice
Loading...
Searching...
No Matches
gui_element_types.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <cmath>
3#include <stddef.h>
4#include <stdint.h>
5#include <unordered_map>
6#include <variant>
7#include <codecvt>
8#include <locale>
9#include "color.hpp"
10#include "culture.hpp"
11#include "cyto_any.hpp"
12#include "dcon_generated.hpp"
13#include "demographics.hpp"
14#include "gui_element_base.hpp"
15#include "gui_element_types.hpp"
16#include "fonts.hpp"
17#include "gui_graphics.hpp"
18#include "nations.hpp"
19#include "opengl_wrapper.hpp"
20#include "text.hpp"
21#include "sound.hpp"
22#include "unit_tooltip.hpp"
23#include "triggers.hpp"
24#include "effects.hpp"
25
26namespace ui {
27
34}
35
37 for(auto& c : children) {
38 if(c->is_visible()) {
39 auto relative_location = child_relative_location(state, *this, *c);
40 auto res = c->impl_probe_mouse(state, x - relative_location.x, y - relative_location.y, type);
41 if(res.under_mouse)
42 return res;
43 }
44 }
46}
47
49 for(auto& c : children) {
50 if(c->is_visible()) {
51 auto relative_location = child_relative_location(state, *this, *c);
52 auto res = c->impl_probe_mouse(state, x - relative_location.x, y - relative_location.y, type);
53 if(res.under_mouse)
54 return res;
55 }
56 }
58}
59
62 for(auto& c : children) {
63 if(c->is_visible()) {
64 res = greater_result(res, c->impl_on_key_down(state, key, mods));
67 }
68 }
70}
73 for(auto& c : children) {
74 if(c->is_visible()) {
75 res = greater_result(res, c->impl_on_key_down(state, key, mods));
78 }
79 }
81}
82
84 on_update(state);
85 if(is_visible()) {
86 for(auto& c : children) {
87 if(c->is_visible() || (c->flags & element_base::wants_update_when_hidden_mask) != 0) {
88 c->impl_on_update(state);
89 }
90 }
91 }
92}
94 on_update(state);
95 if(is_visible()) {
96 for(size_t i = children.size(); i-- > 0;) {
97 if(children[i]->is_visible() || (children[i]->flags & element_base::wants_update_when_hidden_mask) != 0) {
98 children[i]->impl_on_update(state);
99 }
100 }
101 }
102}
104 for(auto& c : children) {
105 c->impl_on_reset_text(state);
106 }
107 on_reset_text(state);
108}
110 for(auto& c : children) {
111 c->impl_on_reset_text(state);
112 }
113 on_reset_text(state);
114}
117 for(auto& c : children) {
118 res = greater_result(res, c->impl_set(state, payload));
119 }
120 return greater_result(res, set(state, payload));
121}
124 for(auto& c : children) {
125 res = greater_result(res, c->impl_set(state, payload));
126 }
127 return greater_result(res, set(state, payload));
128}
129
130void container_base::impl_render(sys::state& state, int32_t x, int32_t y) noexcept {
132
133 for(size_t i = children.size(); i-- > 0;) {
134 if(children[i]->is_visible()) {
135 auto relative_location = child_relative_location(state, *this, *(children[i]));
136 children[i]->impl_render(state, x + relative_location.x, y + relative_location.y);
137 }
138 }
139}
140void non_owning_container_base::impl_render(sys::state& state, int32_t x, int32_t y) noexcept {
142
143 for(size_t i = children.size(); i-- > 0;) {
144 if(children[i]->is_visible()) {
145 auto relative_location = child_relative_location(state, *this, *(children[i]));
146 children[i]->impl_render(state, x + relative_location.x, y + relative_location.y);
147 }
148 }
149}
150
151std::unique_ptr<element_base> container_base::remove_child(element_base* child) noexcept {
152 if(auto it = std::find_if(children.begin(), children.end(), [child](std::unique_ptr<element_base>& p) { return p.get() == child; }); it != children.end()) {
153 if(it + 1 != children.end())
154 std::rotate(it, it + 1, children.end());
155 auto temp = std::move(children.back());
156 children.pop_back();
157 temp->parent = nullptr;
158 return temp;
159 }
160 return std::unique_ptr<element_base>{};
161}
163 if(auto it = std::find_if(children.begin(), children.end(), [child](std::unique_ptr<element_base>& p) { return p.get() == child; }); it != children.end()) {
164 if(it != children.begin())
165 std::rotate(children.begin(), it, it + 1);
166 }
167}
169 if(auto it = std::find_if(children.begin(), children.end(), [child](element_base* p) { return p == child; }); it != children.end()) {
170 if(it != children.begin())
171 std::rotate(children.begin(), it, it + 1);
172 }
173}
175 if(auto it = std::find_if(children.begin(), children.end(), [child](std::unique_ptr<element_base>& p) { return p.get() == child; }); it != children.end()) {
176 if(it + 1 != children.end())
177 std::rotate(it, it + 1, children.end());
178 }
179}
181 if(auto it = std::find_if(children.begin(), children.end(), [child](element_base* p) { return p == child; }); it != children.end()) {
182 if(it + 1 != children.end())
183 std::rotate(it, it + 1, children.end());
184 }
185}
186void container_base::add_child_to_front(std::unique_ptr<element_base> child) noexcept {
187 child->parent = this;
188 children.emplace_back(std::move(child));
189 if(children.size() > 1) {
190 std::rotate(children.begin(), children.end() - 1, children.end());
191 }
192}
193void container_base::add_child_to_back(std::unique_ptr<element_base> child) noexcept {
194 child->parent = this;
195 children.emplace_back(std::move(child));
196}
198 if(auto it = std::find_if(children.begin(), children.end(), [&state, name](std::unique_ptr<element_base>& p) { return parsers::lowercase_str(state.to_string_view(p->base_data.name)) == parsers::lowercase_str(name); }); it != children.end()) {
199 return it->get();
200 }
201 return nullptr;
202}
204 if(0 <= index && index < int32_t(children.size()))
205 return children[index].get();
206 return nullptr;
207}
209 if(0 <= index && index < int32_t(children.size()))
210 return children[index];
211 return nullptr;
212}
213
214ogl::color_modification get_color_modification(bool is_under_mouse, bool is_disabled, bool is_interactable) {
215 if(!is_under_mouse || !is_interactable) {
216 if(is_disabled) {
218 } else {
220 }
221 } else {
222 if(is_disabled) {
224 } else {
226 }
227 }
228}
229
232 if(base_data.get_element_type() == element_type::image) {
233 frame = base_data.data.image.frame();
234 }
235}
236
237void image_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
238 dcon::gfx_object_id gid;
239 if(base_data.get_element_type() == element_type::image) {
240 gid = base_data.data.image.gfx_object;
241 } else if(base_data.get_element_type() == element_type::button) {
242 gid = base_data.data.button.button_image;
243 }
244 // TODO: More elements defaults?
245 if(gid) {
246 auto& gfx_def = state.ui_defs.gfx[gid];
247 if(gfx_def.primary_texture_handle) {
248 if(gfx_def.get_object_type() == ui::object_type::bordered_rect) {
249 ogl::render_bordered_rect(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
250 gfx_def.type_dependent, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
251 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
252 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
253 get_horizontal_flip(state));
254 } else if(gfx_def.number_of_frames > 1) {
255 ogl::render_subsprite(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable), frame,
256 gfx_def.number_of_frames, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
257 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
258 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
259 get_horizontal_flip(state));
260 } else {
261 ogl::render_textured_rect(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
262 float(x), float(y), float(base_data.size.x), float(base_data.size.y),
263 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
264 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
265 get_horizontal_flip(state));
266 }
267 }
268 }
269}
270
271void tinted_image_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
272 dcon::gfx_object_id gid;
273 if(base_data.get_element_type() == element_type::image) {
274 gid = base_data.data.image.gfx_object;
275 } else if(base_data.get_element_type() == element_type::button) {
276 gid = base_data.data.button.button_image;
277 }
278 if(gid) {
279 auto const& gfx_def = state.ui_defs.gfx[gid];
280 if(gfx_def.primary_texture_handle) {
281 if(gfx_def.number_of_frames > 1) {
283 gfx_def.number_of_frames, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
285 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
286 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
287 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
288 } else {
289 ogl::render_tinted_textured_rect(state, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
291 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
292 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
293 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
294 }
295 }
296 }
297}
298
299void progress_bar::render(sys::state& state, int32_t x, int32_t y) noexcept {
300 if(base_data.get_element_type() == element_type::image) {
301 dcon::gfx_object_id gid = base_data.data.image.gfx_object;
302 if(gid) {
303 auto const& gfx_def = state.ui_defs.gfx[gid];
304 auto secondary_texture_handle = dcon::texture_id(gfx_def.type_dependent - 1);
305 if(gfx_def.primary_texture_handle) {
306 ogl::render_progress_bar(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
307 progress, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
308 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
309 ogl::get_texture_handle(state, secondary_texture_handle, gfx_def.is_partially_transparent()),
310 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
311 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
312 }
313 }
314 }
315}
316
317// From ui_f_shader.glsl
319 float amount = (r + g + b) / 4.f;
320 return sys::pack_color(std::min(1.f, amount + 0.1f), std::min(1.f, amount + 0.1f), std::min(1.f, amount + 0.1f));
321}
322uint32_t internal_get_interactable_color(float r, float g, float b) {
323 return sys::pack_color(std::min(1.f, r + 0.1f), std::min(1.f, g + 0.1f), std::min(1.f, b + 0.1f));
324}
325uint32_t internal_get_disabled_color(float r, float g, float b) {
326 float amount = (r + g + b) / 4.f;
327 return sys::pack_color(amount, amount, amount);
328}
329
333 float x,
334 float baseline_y,
335 uint16_t font_id,
338) {
339 auto font_size = float(text::size_from_font_id(font_id));
340 auto font_index = text::font_index_from_font_id(state, font_id);
341 auto& current_font = state.font_collection.get_font(state, font_index);
342
343 if(std::holds_alternative<text::embedded_flag>(t.source)) {
345 state,
346 std::get<text::embedded_flag>(t.source),
347 x,
348 baseline_y,
349 font_size,
350 current_font,
351 cmod
352 );
353 } else if(std::holds_alternative<text::embedded_unit_icon>(t.source)) {
355 state,
356 std::get<text::embedded_unit_icon>(t.source),
357 x,
358 baseline_y,
359 font_size,
360 current_font,
361 cmod
362 );
363 } else if(std::holds_alternative<text::embedded_commodity_icon>(t.source)) {
365 state,
366 std::get<text::embedded_commodity_icon>(t.source),
367 x,
368 baseline_y,
369 font_size,
370 current_font
371 );
372 } else if(std::holds_alternative<text::embedded_icon>(t.source)) {
374 state,
375 std::get<text::embedded_icon>(t.source),
376 x,
377 baseline_y,
378 font_size,
379 current_font,
380 cmod
381 );
382 } else {
384 state,
385 t.unicodechars,
386 cmod,
387 x,
388 baseline_y,
390 font_id
391 );
392 }
393}
394
395void button_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
396 auto text_color = black_text ? ogl::color3f{ 0.0f, 0.0f, 0.0f } : ogl::color3f{ 1.0f, 1.0f, 1.0f };
397 if(state.user_settings.color_blind_mode != sys::color_blind_mode::none) {
398 /* On colour blind mode we tint things brigther to the user can see interactables better */
399 dcon::gfx_object_id gid;
400 if(base_data.get_element_type() == element_type::image) {
401 gid = base_data.data.image.gfx_object;
402 } else if(base_data.get_element_type() == element_type::button) {
403 gid = base_data.data.button.button_image;
404 }
405 if(gid) {
406 auto const& gfx_def = state.ui_defs.gfx[gid];
407 if(gfx_def.primary_texture_handle) {
408 float r = 1.f;
409 float g = 1.f;
410 float b = 1.f;
411 ogl::color_modification cmod = get_color_modification(this == state.ui_state.under_mouse, disabled, interactable);
412 auto tcolor = sys::pack_color(1.f, 1.f, 1.f);
414 if(state.user_settings.color_blind_mode == sys::color_blind_mode::tritan) {
415 tcolor = sys::pack_color(0.75f, 1.f, 0.75f);
416 } else {
417 tcolor = sys::pack_color(0.75f, 0.75f, 1.f);
418 }
420 tcolor = sys::pack_color(0.66f, 0.66f, 0.66f);
421 text_color = black_text ? ogl::color3f{ 1.f, 1.f, 1.f } : ogl::color3f{ 0.f, 0.f, 0.f };
422 } else if(cmod == ogl::color_modification::disabled) {
423 tcolor = sys::pack_color(0.44f, 0.44f, 0.44f);
424 text_color = black_text ? ogl::color3f{ 1.f, 1.f, 1.f } : ogl::color3f{ 0.f, 0.f, 0.f };
425 }
426 if(gfx_def.number_of_frames > 1) {
428 gfx_def.number_of_frames, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
430 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
431 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
432 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
433 } else {
434 ogl::render_tinted_textured_rect(state, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
436 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
437 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
438 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
439 }
440 }
441 }
442 } else {
444 }
445
446 if(internal_layout.contents.empty())
447 return;
448
449 auto linesz = state.font_collection.line_height(state, base_data.data.button.font_handle);
450 if(linesz == 0.f)
451 return;
452 auto ycentered = (base_data.size.y - linesz) / 2;
453 auto cmod = get_color_modification(this == state.ui_state.under_mouse, disabled, interactable);
454
455 for(auto& t : internal_layout.contents) {
457 state,
458 t,
459 float(x) + t.x,
460 float(y + int32_t(ycentered)),
461 base_data.data.button.font_handle,
463 cmod
464 );
465 }
466}
467
468void tinted_button_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
469 dcon::gfx_object_id gid;
470 if(base_data.get_element_type() == element_type::image) {
471 gid = base_data.data.image.gfx_object;
472 } else if(base_data.get_element_type() == element_type::button) {
473 gid = base_data.data.button.button_image;
474 }
475 if(gid) {
476 auto const& gfx_def = state.ui_defs.gfx[gid];
477 if(gfx_def.primary_texture_handle) {
478 auto tcolor = color;
479 float r = sys::red_from_int(color);
480 float g = sys::green_from_int(color);
481 float b = sys::blue_from_int(color);
482 ogl::color_modification cmod = get_color_modification(this == state.ui_state.under_mouse, disabled, interactable);
484 tcolor = internal_get_interactable_color(r, g, b);
487 } else if(cmod == ogl::color_modification::disabled) {
488 tcolor = internal_get_disabled_color(r, g, b);
489 }
490
491 if(gfx_def.number_of_frames > 1) {
493 gfx_def.number_of_frames, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
495 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
496 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
497 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
498 } else {
499 ogl::render_tinted_textured_rect(state, float(x), float(y), float(base_data.size.x), float(base_data.size.y),
501 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
502 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
503 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
504 }
505 }
506 }
507
508 if(internal_layout.contents.empty())
509 return;
510
511 auto linesz = state.font_collection.line_height(state, base_data.data.button.font_handle);
512 if(linesz == 0.f)
513 return;
514 auto ycentered = (base_data.size.y - linesz) / 2;
515 auto cmod = get_color_modification(this == state.ui_state.under_mouse, disabled, interactable);
516
517 for(auto& t : internal_layout.contents) {
519 state,
520 t,
521 float(x) + t.x,
522 float(y + int32_t(ycentered)),
523 base_data.data.button.font_handle,
524 black_text ? ogl::color3f{ 0.0f, 0.0f, 0.0f } : ogl::color3f{ 1.0f, 1.0f, 1.0f },
525 cmod
526 );
527 }
528}
529
531 if(state.user_settings.color_blind_mode == sys::color_blind_mode::achroma) {
534 return ogl::color3f{ 0.f, 0.f, 0.f };
535 } else if(text_color == text::text_color::white) {
536 return ogl::color3f{ 1.f, 1.f, 1.f };
541 return ogl::color3f{ 0.25f, 0.25f, 0.25f };
544 return ogl::color3f{ 0.75f, 0.75f, 0.75f };
545 }
546 return ogl::color3f{ 0.5f, 0.5f, 0.5f };
547 }
548
549 switch(text_color) {
552 return ogl::color3f{0.0f, 0.0f, 0.0f};
554 return ogl::color3f{1.0f, 1.0f, 1.0f};
556 if(state.user_settings.color_blind_mode == sys::color_blind_mode::deutan || state.user_settings.color_blind_mode == sys::color_blind_mode::protan) {
557 return ogl::color3f{ 0.66f, 0.66f, 1.f }; //Remap to blue
558 }
559 return ogl::color3f{0.9f, 0.2f, 0.1f};
561 if(state.user_settings.color_blind_mode == sys::color_blind_mode::deutan || state.user_settings.color_blind_mode == sys::color_blind_mode::protan) {
562 return ogl::color3f{ 0.95f, 0.95f, 0.2f }; //Remap to yellow
563 }
564 return ogl::color3f{0.2f, 0.95f, 0.2f};
566 return ogl::color3f{0.9f, 0.9f, 0.1f};
568 if(state.user_settings.color_blind_mode == sys::color_blind_mode::deutan || state.user_settings.color_blind_mode == sys::color_blind_mode::protan) {
569 return ogl::color3f{ 0.33f, 0.33f, 1.f }; //increase intensity
570 } else if(state.user_settings.color_blind_mode == sys::color_blind_mode::tritan) {
571 return ogl::color3f{ 0.5f, 1.f, 0.5f };
572 }
573 return ogl::color3f{0.5f, 0.5f, 1.0f};
575 if(state.user_settings.color_blind_mode == sys::color_blind_mode::deutan || state.user_settings.color_blind_mode == sys::color_blind_mode::protan) {
576 return ogl::color3f{ 0.125f, 0.125f, 1.f }; //increase intensity
577 } else if(state.user_settings.color_blind_mode == sys::color_blind_mode::tritan) {
578 return ogl::color3f{ 0.2f, 0.8f, 0.2f };
579 }
580 return ogl::color3f{0.2f, 0.2f, 0.8f};
582 return ogl::color3f{1.f, 0.7f, 0.1f};
584 return ogl::color3f{0.8f, 0.7f, 0.3f};
586 return ogl::color3f{0.5f, 0.5f, 0.5f};
588 if(state.user_settings.color_blind_mode == sys::color_blind_mode::deutan || state.user_settings.color_blind_mode == sys::color_blind_mode::protan) {
589 return ogl::color3f{ 0.42f, 0.42f, 1.f }; //Remap to blue
590 }
591 return ogl::color3f{0.5f, 0.f, 0.f};
593 if(state.user_settings.color_blind_mode == sys::color_blind_mode::deutan || state.user_settings.color_blind_mode == sys::color_blind_mode::protan) {
594 return ogl::color3f{ 0.5f, 0.5f, 0.f }; //Remap to yellow
595 }
596 return ogl::color3f{0.f, 0.5f, 0.f};
598 return ogl::color3f{232.0f / 255.0f, 210.0f / 255.0f, 124.0f / 255.0f};
600 return ogl::color3f{ 150.0f / 255.0f, 75.0f / 255.0f, 0.f };
601 default:
602 return ogl::color3f{0.f, 0.f, 0.f};
603 }
604}
605
606void button_element_base::set_button_text(sys::state& state, std::string const& new_text) {
607 if(new_text != cached_text) {
608 cached_text = new_text;
611
612 text::single_line_layout sl{ internal_layout, text::layout_parameters{ 0, 0, static_cast<int16_t>(base_data.size.x), static_cast<int16_t>(base_data.size.y),
614 sl.add_text(state, cached_text);
615 }
616}
617
619}
620
622 if(base_data.get_element_type() == element_type::button) {
623 auto base_text_handle = base_data.data.button.txt;
624 black_text = text::is_black_from_font_id(base_data.data.button.font_handle);
625 if(base_text_handle) {
626 cached_text = text::produce_simple_string(state, base_data.data.button.txt);
627 internal_layout.contents.clear();
628 internal_layout.number_of_lines = 0;
629
630 text::single_line_layout sl{ internal_layout, text::layout_parameters{ 0, 0, static_cast<int16_t>(base_data.size.x), static_cast<int16_t>(base_data.size.y),
631 base_data.data.button.font_handle, 0, text::alignment::center, black_text ? text::text_color::black : text::text_color::white, true, true },
632 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()) ? text::layout_base::rtl_status::rtl : text::layout_base::rtl_status::ltr };
633 sl.add_text(state, cached_text);
634 }
635 }
636}
637
639 on_reset_text(state);
640}
641
643 // Set edit control so we get on_text events
644 state.ui_state.edit_target = this;
645 sound::play_interface_sound(state, sound::get_click_sound(state), state.user_settings.interface_volume * state.user_settings.master_volume);
647}
648
649void edit_box_element_base::on_text(sys::state& state, char32_t ch) noexcept {
650 if(state.ui_state.edit_target == this && state.ui_state.edit_target->is_visible()) {
651 if(ch >= 32 && ch != U'`' && ch != 127) {
652 auto s = std::string(get_text(state));
653 s += char(ch & 0xff);
654 edit_index++;
655 set_text(state, s);
656 edit_box_update(state, s);
657 } else if(ch == 0x16) { // this is ctrl+v on windows
658#ifdef _WIN64
659 std::wstring return_value;
660 if(OpenClipboard(state.win_ptr->hwnd)) {
661 HGLOBAL hClipboardData = GetClipboardData(CF_UNICODETEXT);
662
663 if(hClipboardData != NULL) {
664 size_t byteSize = GlobalSize(hClipboardData);
665 void* memory = GlobalLock(hClipboardData);
666 if(memory != NULL) {
667 const wchar_t* text = reinterpret_cast<const wchar_t*>(memory);
668 return_value = std::wstring(text, text + byteSize / sizeof(wchar_t));
669 GlobalUnlock(hClipboardData);
670 if(return_value.length() > 0 && return_value.back() == 0) {
671 return_value.pop_back();
672 }
673 }
674 }
675 CloseClipboard();
676 }
677 if(return_value.size() > 0) {
678 auto utf8_val = simple_fs::native_to_utf8(return_value);
679 auto new_text = std::string(get_text(state)) + utf8_val;
680 edit_index += int32_t(utf8_val.length());
681 set_text(state, new_text);
682 edit_box_update(state, new_text);
683 }
684#endif
685 }
686 }
687}
688
690 if(state.ui_state.edit_target == this && state.ui_state.edit_target->is_visible()) {
691 // Typable keys are handled by on_text callback, we only handle control keys
692 auto s = std::string(get_text(state));
693 switch(key) {
695 edit_box_enter(state, s);
696 s.clear();
697 set_text(state, s);
698 edit_index = 0;
699 break;
701 edit_box_esc(state);
702 break;
704 edit_box_tab(state, s);
705 break;
707 edit_box_up(state);
708 break;
710 edit_box_down(state);
711 break;
713 edit_box_backtick(state);
714 break;
716 edit_box_back_slash(state);
717 break;
719 edit_index = std::max<int32_t>(edit_index - 1, 0);
720 break;
722 edit_index = std::min<int32_t>(edit_index + 1, int32_t(s.length()));
723 break;
725 if(edit_index < int32_t(s.length())) {
726 s.erase(edit_index, 1);
727 set_text(state, s);
728 edit_box_update(state, s);
729 }
730 break;
732 if(mods == sys::key_modifiers::modifiers_ctrl && edit_index > 0 && edit_index <= int32_t(s.length())) {
733 int32_t index = edit_index - 1;
734 bool skip = false;
735 while(index > 0 && (s.at(index) != ' ' || !skip)) {
736 if(s.at(index) != ' ')
737 skip = true;
738 --index;
739 }
740 s.erase(index, edit_index);
741 set_text(state, s);
742 edit_index = index;
743 edit_box_update(state, s);
744 } else if(edit_index > 0 && edit_index <= int32_t(s.length())) {
745 s.erase(edit_index - 1, 1);
746 set_text(state, s);
747 edit_index--;
748 edit_box_update(state, s);
749 }
750 break;
751 default:
752 break;
753 }
758
760 if(base_data.get_element_type() == element_type::button) {
761 simple_text_element_base::black_text = text::is_black_from_font_id(base_data.data.button.font_handle);
762 } else if(base_data.get_element_type() == element_type::text) {
763 simple_text_element_base::black_text = text::is_black_from_font_id(base_data.data.text.font_handle);
764 }
765}
766
768 if(base_data.get_element_type() == element_type::button) {
769 //simple_text_element_base::text_offset = 0.0f;
770 } else if(base_data.get_element_type() == element_type::text) {
771 //simple_text_element_base::text_offset = base_data.data.text.border_size.x;
772 }
773 on_reset_text(state);
774}
775
776void edit_box_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
777 if(base_data.get_element_type() == element_type::text) {
778 dcon::texture_id background_texture_id;
779 if((base_data.data.text.flags & uint8_t(ui::text_background::tiles_dialog)) != 0) {
780 background_texture_id = definitions::tiles_dialog;
781 } else if((base_data.data.text.flags & uint8_t(ui::text_background::transparency)) != 0) {
782 background_texture_id = definitions::transparency;
783 } else if((base_data.data.text.flags & uint8_t(ui::text_background::small_tiles_dialog)) != 0) {
784 background_texture_id = definitions::small_tiles_dialog;
785 }
786 // TODO: Partial transparency is ignored, might cause issues with "transparency"?
787 // is-vertically-flipped is also ignored, also the border size **might** be
788 // variable/stored somewhere, but I don't know where?
789 if(bool(background_texture_id)) {
790 ogl::render_bordered_rect(state, ogl::color_modification::none, 16.0f, float(x), float(y), float(base_data.size.x),
791 float(base_data.size.y), ogl::get_texture_handle(state, background_texture_id, true), base_data.get_rotation(), false,
792 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
793 }
794 }
795
796 // TODO: A better way to show the cursor!
797 auto old_s = std::string(get_text(state));
798 auto blink_s = std::string(get_text(state));
799 blink_s.insert(size_t(edit_index), 1, '|');
800 set_text(state, blink_s);
802 set_text(state, old_s);
803}
804
805void tool_tip::render(sys::state& state, int32_t x, int32_t y) noexcept {
806 ogl::render_bordered_rect(state, ogl::color_modification::none, 16.0f, float(x), float(y), float(base_data.size.x),
807 float(base_data.size.y), ogl::get_texture_handle(state, definitions::tiles_dialog, true), ui::rotation::upright, false, false);
808 auto black_text = text::is_black_from_font_id(state.ui_state.tooltip_font);
809 for(auto& t : internal_layout.contents) {
810
811 auto& f = state.font_collection.get_font(state, text::font_index_from_font_id(state, state.ui_state.tooltip_font));
812
814 state,
815 t,
816 float(x) + t.x,
817 float(y + t.y),
818 state.ui_state.tooltip_font,
819 get_text_color(state, t.color),
821 );
822 }
823}
824
825void line_graph::set_data_points(sys::state& state, std::vector<float> const& datapoints) noexcept {
826 assert(datapoints.size() == count);
827 float min = *std::min_element(datapoints.begin(), datapoints.end());
828 float max = *std::max_element(datapoints.begin(), datapoints.end());
829 float y_height = max - min;
830 std::vector<float> scaled_datapoints = std::vector<float>(count);
831 if(y_height == 0.f) {
832 for(size_t i = 0; i < count; i++) {
833 scaled_datapoints[i] = .5f;
834 }
835 } else {
836 for(size_t i = 0; i < count; i++) {
837 scaled_datapoints[i] = (datapoints[i] - min) / y_height;
838 }
839 }
840
841
842 lines.set_y(scaled_datapoints.data());
843}
844
845void line_graph::set_data_points(sys::state& state, std::vector<float> const& datapoints, float min, float max) noexcept {
846 assert(datapoints.size() == count);
847 float y_height = max - min;
848 std::vector<float> scaled_datapoints = std::vector<float>(count);
849 if(y_height == 0.f) {
850 for(size_t i = 0; i < count; i++) {
851 scaled_datapoints[i] = .5f;
852 }
853 } else {
854 for(size_t i = 0; i < count; i++) {
855 scaled_datapoints[i] = (datapoints[i] - min) / y_height;
856 }
857 }
858
859
860 lines.set_y(scaled_datapoints.data());
861}
862
865}
866
867void line_graph::render(sys::state& state, int32_t x, int32_t y) noexcept {
868 if(!is_coloured) {
869 ogl::render_linegraph(state, ogl::color_modification::none, float(x), float(y), base_data.size.x, base_data.size.y, lines);
870 } else {
871 ogl::render_linegraph(state, ogl::color_modification::none, float(x), float(y), base_data.size.x, base_data.size.y, r, g, b, lines);
872 }
873}
874
875void simple_text_element_base::set_text(sys::state& state, std::string const& new_text) {
877 if(new_text != cached_text) {
878 cached_text = new_text;
879 {
882
884 text::single_line_layout sl{ internal_layout, text::layout_parameters{ 0, 0, static_cast<int16_t>(base_data.size.x - base_data.data.text.border_size.x * 2), static_cast<int16_t>(base_data.size.y),
886 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()) ? text::layout_base::rtl_status::rtl : text::layout_base::rtl_status::ltr };
887 sl.add_text(state, cached_text);
888 }
890 }
892 if(new_text != cached_text) {
893 cached_text = new_text;
894 {
897
899 text::single_line_layout sl{ internal_layout, text::layout_parameters{ 0, 0, static_cast<int16_t>(base_data.size.x - base_data.data.text.border_size.x * 2), static_cast<int16_t>(base_data.size.y),
901 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()) ? text::layout_base::rtl_status::rtl : text::layout_base::rtl_status::ltr };
902 sl.add_text(state, cached_text);
903 }
905 }
906 }
907}
908
910 float extent = 0.f;
911 uint16_t font_handle = 0;
913 font_handle = base_data.data.button.font_handle;
915 font_handle = base_data.data.text.font_handle;
916
917 float x_limit = float(base_data.size.x);
919 x_limit -= base_data.data.text.border_size.x;
920 }
921 auto& font = state.font_collection.get_font(state, text::font_index_from_font_id(state, font_handle));
922 auto font_size = text::size_from_font_id(font_handle);
923
924 for(size_t i = internal_layout.contents.size(); i-- > 0; ) {
925 if(internal_layout.contents[i].x >= x_limit) {
926 internal_layout.contents.resize(i);
927 }
928 }
929}
930
932 if(base_data.get_element_type() == element_type::button) {
933 black_text = text::is_black_from_font_id(base_data.data.button.font_handle);
934 cached_text = text::produce_simple_string(state, base_data.data.button.txt);
935 {
936 internal_layout.contents.clear();
937 internal_layout.number_of_lines = 0;
938
939 auto al = text::to_text_alignment(base_data.data.button.get_alignment());
940 text::single_line_layout sl{ internal_layout, text::layout_parameters{ 0, 0, static_cast<int16_t>(base_data.size.x - base_data.data.text.border_size.x * 2), static_cast<int16_t>(base_data.size.y),
941 base_data.data.button.font_handle, 0, al, black_text ? text::text_color::black : text::text_color::white, true, true },
942 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()) ? text::layout_base::rtl_status::rtl : text::layout_base::rtl_status::ltr };
943 sl.add_text(state, cached_text);
944 }
945 format_text(state);
946 } else if(base_data.get_element_type() == element_type::text) {
947 black_text = text::is_black_from_font_id(base_data.data.text.font_handle);
948 cached_text = text::produce_simple_string(state, base_data.data.text.txt);
949 {
950 internal_layout.contents.clear();
951 internal_layout.number_of_lines = 0;
952
953 auto al = text::to_text_alignment(base_data.data.text.get_alignment());
954 text::single_line_layout sl{ internal_layout, text::layout_parameters{ 0, 0, static_cast<int16_t>(base_data.size.x), static_cast<int16_t>(base_data.size.y),
955 base_data.data.text.font_handle, 0, al, black_text ? text::text_color::black : text::text_color::white, true, true },
956 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()) ? text::layout_base::rtl_status::rtl : text::layout_base::rtl_status::ltr };
957 sl.add_text(state, cached_text);
958 }
959 format_text(state);
960 }
961}
963 on_reset_text(state);
964}
965void simple_text_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
967
968 if(base_data.get_element_type() == element_type::button) {
969 auto linesz = state.font_collection.line_height(state, base_data.data.button.font_handle);
970 if(linesz == 0.f)
971 return;
972 auto ycentered = (base_data.size.y - linesz) / 2;
973
974 for(auto& t : internal_layout.contents) {
976 state,
977 t,
978 float(x) + t.x,
979 float(y + int32_t(ycentered)),
980 base_data.data.button.font_handle,
981 get_text_color(state, t.color),
983 );
984 }
985 } else {
986 for(auto& t : internal_layout.contents) {
988 state,
989 t,
990 float(x + base_data.data.text.border_size.x) + t.x,
991 float(y + base_data.data.text.border_size.y),
992 base_data.data.button.font_handle,
993 get_text_color(state, t.color),
995 );
996 }
997 }
998}
999
1001 if(base_data.get_element_type() == element_type::button) {
1002 set_text(state, text::produce_simple_string(state, base_data.data.button.txt));
1003 black_text = text::is_black_from_font_id(base_data.data.button.font_handle);
1004 } else if(base_data.get_element_type() == element_type::text) {
1005 set_text(state, text::produce_simple_string(state, base_data.data.text.txt));
1006 black_text = text::is_black_from_font_id(base_data.data.text.font_handle);
1007 }
1008}
1009void simple_body_text::set_text(sys::state& state, std::string const& new_text) {
1010 auto old_handle = base_data.data.text_common.font_handle;
1011 base_data.data.text_common.font_handle &= ~(0x01 << 7);
1012 auto old_value = base_data.data.text_common.font_handle & 0x3F;
1014 base_data.data.text_common.font_handle |= (old_value - 2);
1015
1018}
1019void simple_body_text::render(sys::state& state, int32_t x, int32_t y) noexcept {
1020 auto old_handle = base_data.data.text_common.font_handle;
1021 base_data.data.text_common.font_handle &= ~(0x01 << 7);
1022 auto old_value = base_data.data.text_common.font_handle & 0x3F;
1023 base_data.data.text_common.font_handle &= ~(0x003F);
1024 base_data.data.text_common.font_handle |= (old_value - 2);
1025
1027 base_data.data.text_common.font_handle = old_handle;
1028}
1030 auto old_handle = base_data.data.text_common.font_handle;
1031 base_data.data.text_common.font_handle &= ~(0x01 << 7);
1032 auto old_value = base_data.data.text_common.font_handle & 0x3F;
1033 base_data.data.text_common.font_handle &= ~(0x003F);
1034 base_data.data.text_common.font_handle |= (old_value - 2);
1035
1037 base_data.data.text_common.font_handle = old_handle;
1038}
1039
1040void color_text_element::render(sys::state& state, int32_t x, int32_t y) noexcept {
1041 auto tc = get_text_color(state, color);
1043 if(base_data.get_element_type() == element_type::button) {
1044 auto linesz = state.font_collection.line_height(state, base_data.data.button.font_handle);
1045 if(linesz == 0.f)
1046 return;
1047 auto ycentered = (base_data.size.y - linesz) / 2;
1048
1049 for(auto& t : internal_layout.contents) {
1051 state,
1052 t,
1053 float(x) + t.x,
1054 float(y + int32_t(ycentered)),
1055 base_data.data.button.font_handle,
1056 tc,
1058 );
1059 }
1060 } else {
1061 for(auto& t : internal_layout.contents) {
1063 state,
1064 t,
1065 float(x + base_data.data.text.border_size.x) + t.x,
1066 float(y + base_data.data.text.border_size.y),
1067 base_data.data.button.font_handle,
1068 tc,
1070 );
1071 }
1072 }
1073}
1074
1076 if(base_data.get_element_type() == element_type::text) {
1077 black_text = text::is_black_from_font_id(base_data.data.text.font_handle);
1078 line_height = state.font_collection.line_height(state, base_data.data.text.font_handle);
1079 visible_lines = base_data.size.y / std::max<int32_t>(int32_t(line_height), 1);
1080 }
1081}
1082
1086
1087void multiline_text_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
1088 if(base_data.get_element_type() == element_type::text) {
1089 for(auto& t : internal_layout.contents) {
1090 float line_offset = t.y - line_height * float(current_line);
1091
1092 if(0 <= line_offset && line_offset < base_data.size.y) {
1094 state,
1095 t,
1096 float(x) + t.x,
1097 float(y + line_offset),
1098 base_data.data.text.font_handle,
1099 get_text_color(state, t.color),
1101 );
1102 }
1103 }
1104 }
1105}
1106
1108 auto const* chunk = internal_layout.get_chunk_from_position(x, y + int32_t(line_height * float(current_line)));
1109 if(chunk != nullptr) {
1110 if(std::holds_alternative<dcon::nation_id>(chunk->source)) {
1111 sound::play_interface_sound(state, sound::get_click_sound(state), state.user_settings.interface_volume * state.user_settings.master_volume);
1112 state.open_diplomacy(std::get<dcon::nation_id>(chunk->source));
1113 auto cap = state.world.nation_get_capital(std::get<dcon::nation_id>(chunk->source));
1114 if(cap) {
1115 auto map_pos = state.world.province_get_mid_point(cap);
1116 map_pos.x /= float(state.map_state.map_data.size_x);
1117 map_pos.y /= float(state.map_state.map_data.size_y);
1118 map_pos.y = 1.0f - map_pos.y;
1119 state.map_state.set_pos(map_pos);
1120 }
1121 } else if(std::holds_alternative<dcon::province_id>(chunk->source)) {
1122 auto prov = std::get<dcon::province_id>(chunk->source);
1123 if(prov && prov.value < state.province_definitions.first_sea_province.value) {
1124 sound::play_interface_sound(state, sound::get_click_sound(state), state.user_settings.interface_volume * state.user_settings.master_volume);
1125 state.map_state.set_selected_province(prov);
1126 static_cast<ui::province_view_window*>(state.ui_state.province_window)->set_active_province(state, prov);
1127 if(state.map_state.get_zoom() < map::zoom_very_close)
1128 state.map_state.zoom = map::zoom_very_close;
1129 state.map_state.center_map_on_province(state, prov);
1130 }
1131 } else if(std::holds_alternative<dcon::state_instance_id>(chunk->source)) {
1132 auto s = std::get<dcon::state_instance_id>(chunk->source);
1133 auto prov = state.world.state_instance_get_capital(s);
1134 if(prov && prov.id.value < state.province_definitions.first_sea_province.value) {
1135 sound::play_interface_sound(state, sound::get_click_sound(state), state.user_settings.interface_volume * state.user_settings.master_volume);
1136 state.map_state.set_selected_province(prov);
1137 static_cast<ui::province_view_window*>(state.ui_state.province_window)->set_active_province(state, prov);
1138 if(state.map_state.get_zoom() < map::zoom_very_close)
1139 state.map_state.zoom = map::zoom_very_close;
1140 state.map_state.center_map_on_province(state, prov);
1141 }
1142 } else if(std::holds_alternative<dcon::national_identity_id>(chunk->source)) {
1143 auto id = std::get<dcon::national_identity_id>(chunk->source);
1144 auto nat = state.world.national_identity_get_nation_from_identity_holder(id);
1145 if(nat) {
1146 sound::play_interface_sound(state, sound::get_click_sound(state), state.user_settings.interface_volume * state.user_settings.master_volume);
1147 state.open_diplomacy(nat);
1148 auto cap = state.world.nation_get_capital(nat);
1149 if(cap) {
1150 auto map_pos = state.world.province_get_mid_point(cap);
1151 map_pos.x /= float(state.map_state.map_data.size_x);
1152 map_pos.y /= float(state.map_state.map_data.size_y);
1153 map_pos.y = 1.0f - map_pos.y;
1154 state.map_state.set_pos(map_pos);
1155 }
1156 }
1157 } else if(std::holds_alternative<dcon::state_definition_id>(chunk->source)) {
1158 auto s = std::get<dcon::state_definition_id>(chunk->source);
1159 auto prov_rng = state.world.state_definition_get_abstract_state_membership(s);
1160 dcon::province_id prov = prov_rng.begin() != prov_rng.end() ? (*prov_rng.begin()).get_province().id : dcon::province_id{ };
1161 if(prov && prov.value < state.province_definitions.first_sea_province.value) {
1162 sound::play_interface_sound(state, sound::get_click_sound(state), state.user_settings.interface_volume * state.user_settings.master_volume);
1163 state.map_state.set_selected_province(prov);
1164 static_cast<ui::province_view_window*>(state.ui_state.province_window)->set_active_province(state, prov);
1165 if(state.map_state.get_zoom() < map::zoom_very_close)
1166 state.map_state.zoom = map::zoom_very_close;
1167 state.map_state.center_map_on_province(state, prov);
1168 }
1169 }
1171 }
1173}
1174
1177}
1178
1180 if(line_height == 0.f)
1182 switch(type) {
1184 {
1185 auto chunk = internal_layout.get_chunk_from_position(x, y + int32_t(line_height * float(current_line)));
1186 if(!chunk)
1188 if(std::holds_alternative<dcon::nation_id>(chunk->source)
1189 || std::holds_alternative<dcon::province_id>(chunk->source)
1190 || std::holds_alternative<dcon::state_instance_id>(chunk->source)
1191 || std::holds_alternative<dcon::national_identity_id>(chunk->source)
1192 || std::holds_alternative<dcon::state_definition_id>(chunk->source)) {
1193
1195 }
1197 }
1199 if(has_tooltip(state) != tooltip_behavior::no_tooltip)
1204 }
1206}
1207
1210 if(base_data.get_element_type() == element_type::button) {
1211 black_text = text::is_black_from_font_id(base_data.data.button.font_handle);
1212 line_height = state.font_collection.line_height(state, base_data.data.button.font_handle);
1213 if(line_height == 0.f)
1214 return;
1215 visible_lines = base_data.size.y / int32_t(line_height);
1216 }
1217}
1218
1220 on_reset_text(state);
1221}
1222
1223void multiline_button_element_base::render(sys::state& state, int32_t x, int32_t y) noexcept {
1225 if(internal_layout.contents.empty())
1226 return;
1227
1228 if(base_data.get_element_type() == element_type::button) {
1229 if(line_height == 0.f)
1230 return;
1231 for(auto& t : internal_layout.contents) {
1232 float line_offset = t.y - line_height * float(current_line);
1233 if(0 <= line_offset && line_offset < base_data.size.y) {
1235 state,
1236 t,
1237 float(x) + t.x,
1238 float(y + line_offset),
1239 base_data.data.button.font_handle,
1240 get_text_color(state, t.color),
1242 );
1243 }
1244 }
1245 }
1246}
1247
1249 if(dat.size.x == 0 || dat.size.y == 0) {
1250 dcon::gfx_object_id gfx_handle;
1251 float scale = 1.0f;
1253 gfx_handle = dat.data.image.gfx_object;
1254 scale = dat.data.image.scale;
1255 } else if(dat.get_element_type() == ui::element_type::button) {
1256 gfx_handle = dat.data.button.button_image;
1257 }
1258 if(gfx_handle) {
1259 auto const& gfx_def = state.ui_defs.gfx[gfx_handle];
1260 if(gfx_def.size.x != 0) {
1261 dat.size = gfx_def.size;
1262 } else {
1263 auto tex_handle = gfx_def.primary_texture_handle;
1264 if(tex_handle) {
1265 ogl::get_texture_handle(state, tex_handle, gfx_def.is_partially_transparent());
1266 dat.size.y = int16_t(state.open_gl.asset_textures[tex_handle].size_y);
1267 dat.size.x = int16_t(state.open_gl.asset_textures[tex_handle].size_x / gfx_def.number_of_frames);
1268 }
1269 }
1270 if(scale != 1.0f) {
1271 dat.size.x = int16_t(dat.size.x * dat.data.image.scale);
1272 dat.size.y = int16_t(dat.size.y * dat.data.image.scale);
1273 }
1274 }
1275 }
1276}
1277
1278std::unique_ptr<element_base> make_element(sys::state& state, std::string_view name) {
1279 auto it = state.ui_state.defs_by_name.find(state.lookup_key(name));
1280 if(it != state.ui_state.defs_by_name.end()) {
1281 if(it->second.generator) {
1282 auto res = it->second.generator(state, it->second.definition);
1283 if(res) {
1284 std::memcpy(&(res->base_data), &(state.ui_defs.gui[it->second.definition]), sizeof(ui::element_data));
1285 make_size_from_graphics(state, res->base_data);
1286 res->on_create(state);
1287 return res;
1288 }
1289 }
1290 return make_element_immediate(state, it->second.definition);
1291 }
1292 return std::unique_ptr<element_base>{};
1293}
1294
1296 units_root = std::make_unique<container_base>();
1297 rgos_root = std::make_unique<container_base>();
1298 province_details_root = std::make_unique<container_base>();
1299 root = std::make_unique<container_base>();
1300 military_root = std::make_unique<container_base>();
1301 army_group_selector_root = std::make_unique<container_base>();
1302 tooltip = std::make_unique<tool_tip>();
1304}
1305
1306state::~state() = default;
1307
1309 if(base_data.get_element_type() == element_type::window) {
1310 auto first_child = base_data.data.window.first_child;
1311 auto num_children = base_data.data.window.num_children;
1312 for(auto ex : state.ui_defs.extensions) {
1313 if(ex.window == base_data.name) {
1314 auto ch_res = make_child(state, parsers::lowercase_str(state.to_string_view(state.ui_defs.gui[ex.child].name)), ex.child);
1315 if(!ch_res) {
1316 ch_res = ui::make_element_immediate(state, ex.child);
1317 }
1318 if(ch_res) {
1319 this->add_child_to_back(std::move(ch_res));
1320 }
1321 }
1322 }
1323 for(uint32_t i = num_children; i-- > 0;) {
1324 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(i + first_child.index()));
1325 auto ch_res = make_child(state, parsers::lowercase_str(state.to_string_view(state.ui_defs.gui[child_tag].name)), child_tag);
1326 if(!ch_res) {
1327 ch_res = ui::make_element_immediate(state, child_tag);
1328 }
1329 if(ch_res) {
1330 this->add_child_to_back(std::move(ch_res));
1331 }
1332 }
1333 }
1334}
1335
1336void window_element_base::on_drag(sys::state& state, int32_t oldx, int32_t oldy, int32_t x, int32_t y,
1337 sys::key_modifiers mods) noexcept {
1338 auto location_abs = get_absolute_location(state, *this);
1339 if(location_abs.x <= oldx && oldx < base_data.size.x + location_abs.x && location_abs.y <= oldy &&
1340 oldy < base_data.size.y + location_abs.y) {
1341 xy_pair new_abs_pos = location_abs;
1342 new_abs_pos.x += int16_t(x - oldx);
1343 new_abs_pos.y += int16_t(y - oldy);
1344
1345 if(ui_width(state) > base_data.size.x)
1346 new_abs_pos.x = int16_t(std::clamp(int32_t(new_abs_pos.x), 0, ui_width(state) - base_data.size.x));
1347 if(ui_height(state) > base_data.size.y)
1348 new_abs_pos.y = int16_t(std::clamp(int32_t(new_abs_pos.y), 0, ui_height(state) - base_data.size.y));
1349
1350 if(state.world.locale_get_native_rtl(state.font_collection.get_current_locale())) {
1351 base_data.position.x -= int16_t(new_abs_pos.x - location_abs.x);
1352 } else {
1353 base_data.position.x += int16_t(new_abs_pos.x - location_abs.x);
1354 }
1355 base_data.position.y += int16_t(new_abs_pos.y - location_abs.y);
1356 }
1357}
1358
1359template<class T>
1360void piechart<T>::render(sys::state& state, int32_t x, int32_t y) noexcept {
1361 if(distribution.size() > 0)
1362 ogl::render_piechart(state, ogl::color_modification::none, float(x), float(y), float(base_data.size.x), data_texture);
1363}
1364
1365template<class T>
1367 base_data.position.x -= base_data.size.x;
1368 radius = float(base_data.size.x);
1369 base_data.size.x *= 2;
1370 base_data.size.y *= 2;
1371}
1372
1373template<class T>
1375 std::sort(distribution.begin(), distribution.end(), [](auto const& a, auto const& b) { return a.value > b.value; });
1376 float total = 0.0f;
1377 for(auto& e : distribution) {
1378 total += e.value;
1379 }
1380 int32_t int_total = 0;
1381
1382 if(total != 0.0f) {
1383 for(auto& e : distribution) {
1384 auto ivalue = int32_t(e.value * float(resolution) / total);
1385 e.slices = uint8_t(ivalue);
1386 e.value /= total;
1387 int_total += ivalue;
1388 }
1389 } else {
1390 distribution.clear();
1391 }
1392
1393 if(int_total < resolution && distribution.size() > 0) {
1394 auto rem = resolution - int_total;
1395 while(rem > 0) {
1396 for(auto& e : distribution) {
1397 e.slices += uint8_t(1);
1398 rem -= 1;
1399 if(rem == 0)
1400 break;
1401 }
1402 }
1403 } else if(int_total > resolution) {
1404 assert(false);
1405 }
1406
1407 size_t i = 0;
1408 for(auto& e : distribution) {
1409 uint32_t color = ogl::get_ui_color<T>(state, e.key);
1410 auto slice_count = size_t(e.slices);
1411
1412 for(size_t j = 0; j < slice_count; j++) {
1413 data_texture.data[(i + j) * channels] = uint8_t(color & 0xFF);
1414 data_texture.data[(i + j) * channels + 1] = uint8_t(color >> 8 & 0xFF);
1415 data_texture.data[(i + j) * channels + 2] = uint8_t(color >> 16 & 0xFF);
1416 }
1417
1418 i += slice_count;
1419 }
1420 for(; i < resolution; i++) {
1421 data_texture.data[i * channels] = uint8_t(0);
1422 data_texture.data[i * channels + 1] = uint8_t(0);
1423 data_texture.data[i * channels + 2] = uint8_t(0);
1424 }
1425
1426 data_texture.data_updated = true;
1427}
1428
1429template<class T>
1430void piechart<T>::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
1431 float const PI = 3.141592653589793238463f;
1432 float dx = float(x) - radius;
1433 float dy = float(y) - radius;
1434 size_t index = 0;
1435 if(dx != 0.0f || dy != 0.0f) {
1436 float dist = std::sqrt(dx * dx + dy * dy);
1437 float angle = std::acos(-dx / dist);
1438 if(dy > 0.f) {
1439 angle = PI + (PI - angle);
1440 }
1441 index = size_t(angle / (2.f * PI) * float(resolution));
1442 }
1443 for(auto const& e : distribution) {
1444 if(index < size_t(e.slices)) {
1445 populate_tooltip(state, e.key, e.value, contents);
1446 return;
1447 }
1448 index -= size_t(e.slices);
1449 }
1450}
1451
1452template<class T>
1453void piechart<T>::populate_tooltip(sys::state& state, T t, float percentage, text::columnar_layout& contents) noexcept {
1454 auto fat_t = dcon::fatten(state.world, t);
1455 auto box = text::open_layout_box(contents, 0);
1456 if constexpr(!std::is_same_v<dcon::nation_id, T>)
1457 text::add_to_layout_box(state, contents, box, fat_t.get_name(), text::substitution_map{});
1458 else
1460 text::add_to_layout_box(state, contents, box, std::string(":"), text::text_color::white);
1461 text::add_space_to_layout_box(state, contents, box);
1463 text::close_layout_box(contents, box);
1464}
1465
1466template<class SrcT, class DemoT>
1468 this->distribution.clear();
1469
1470 Cyto::Any obj_id_payload = SrcT{};
1471 size_t i = 0;
1472 if(this->parent) {
1473 this->parent->impl_get(state, obj_id_payload);
1474 float total_pops = 0.f;
1475
1476 for_each_demo(state, [&](DemoT demo_id) {
1477 float volume = 0.f;
1478 if(obj_id_payload.holds_type<dcon::province_id>()) {
1479 auto demo_key = demographics::to_key(state, demo_id);
1480 auto prov_id = any_cast<dcon::province_id>(obj_id_payload);
1481 volume = state.world.province_get_demographics(prov_id, demo_key);
1482 } else if(obj_id_payload.holds_type<dcon::nation_id>()) {
1483 auto demo_key = demographics::to_key(state, demo_id);
1484 auto nat_id = any_cast<dcon::nation_id>(obj_id_payload);
1485 volume = state.world.nation_get_demographics(nat_id, demo_key);
1486 }
1487
1488 if constexpr(std::is_same_v<SrcT, dcon::pop_id>) {
1489 if(obj_id_payload.holds_type<dcon::pop_id>()) {
1490 auto demo_key = pop_demographics::to_key(state, demo_id);
1491 auto pop_id = any_cast<dcon::pop_id>(obj_id_payload);
1492 volume = pop_demographics::get_demo(state, pop_id, demo_key);
1493 }
1494 }
1495 if(volume > 0)
1496 this->distribution.emplace_back(demo_id, volume);
1497 });
1498 }
1499
1500 this->update_chart(state);
1501}
1502
1503void autoscaling_scrollbar::on_create(sys::state& state) noexcept {
1504 base_data.data.scrollbar.flags &= ~ui::scrollbar_data::step_size_mask;
1505 scrollbar::on_create(state);
1506}
1507
1508void autoscaling_scrollbar::scale_to_parent() {
1509 base_data.size.y = parent->base_data.size.y;
1510 base_data.data.scrollbar.border_size = base_data.size;
1511 base_data.position.x = parent->base_data.size.x; // base_data.size.x / 3;
1512
1513 left->base_data.position.y = parent->base_data.size.y - left->base_data.size.y;
1514 right->base_data.position.y = 0;
1515 track->base_data.size.y = parent->base_data.size.y - 2 * right->base_data.size.y;
1516 track->base_data.position.y = right->base_data.size.y;
1517 track->base_data.position.x = 5;
1518 slider->base_data.position.x = 0;
1519 settings.track_size = track->base_data.size.y;
1520
1521 left->step_size = -settings.scaling_factor;
1522 right->step_size = -settings.scaling_factor;
1523}
1524
1525void multiline_text_scrollbar::on_value_change(sys::state& state, int32_t v) noexcept {
1526 Cyto::Any payload = multiline_text_scroll_event{int32_t(scaled_value())};
1527 impl_get(state, payload);
1528}
1529
1530void scrollable_text::on_create(sys::state& state) noexcept {
1531 auto res = std::make_unique<multiline_text_element_base>();
1532 std::memcpy(&(res->base_data), &(base_data), sizeof(ui::element_data));
1533 make_size_from_graphics(state, res->base_data);
1534 res->base_data.position.x = 0;
1535 res->base_data.position.y = 0;
1536 res->on_create(state);
1537 delegate = res.get();
1538 delegate->base_data.flags &= ~uint8_t(ui::element_data::orientation_mask);
1539 add_child_to_front(std::move(res));
1540
1541 auto ptr = make_element_by_type<multiline_text_scrollbar>(state, "standardlistbox_slider");
1542 text_scrollbar = static_cast<multiline_text_scrollbar*>(ptr.get());
1543 add_child_to_front(std::move(ptr));
1544 text_scrollbar->scale_to_parent();
1545}
1546
1547void scrollable_text::calibrate_scrollbar(sys::state& state) noexcept {
1548 if(delegate->internal_layout.number_of_lines > delegate->visible_lines) {
1549 text_scrollbar->set_visible(state, true);
1550 text_scrollbar->change_settings(state,
1551 mutable_scrollbar_settings{0, delegate->internal_layout.number_of_lines - delegate->visible_lines, 0, 0, false});
1552 } else {
1553 text_scrollbar->set_visible(state, false);
1554 delegate->current_line = 0;
1555 }
1556}
1557
1558message_result scrollable_text::on_scroll(sys::state& state, int32_t x, int32_t y, float amount, sys::key_modifiers mods) noexcept {
1559 if(delegate->internal_layout.number_of_lines > delegate->visible_lines) {
1560 text_scrollbar->update_scaled_value(state, text_scrollbar->scaled_value() + std::clamp(-amount, -1.f, 1.f));
1561 delegate->current_line = int32_t(text_scrollbar->scaled_value());
1562 return message_result::consumed;
1563 }
1564 return message_result::unseen;
1565}
1566
1567message_result scrollable_text::get(sys::state& state, Cyto::Any& payload) noexcept {
1568 if(payload.holds_type<multiline_text_scroll_event>()) {
1569 auto event = any_cast<multiline_text_scroll_event>(payload);
1570 delegate->current_line = event.new_value;
1571 return message_result::consumed;
1572 } else {
1573 return message_result::unseen;
1574 }
1575}
1576
1577template<class RowWinT, class RowConT>
1579 static_cast<listbox_element_base<RowWinT, RowConT>*>(parent)->update(state);
1580}
1581
1582void listbox2_scrollbar::on_value_change(sys::state& state, int32_t v) noexcept {
1583 send(state, parent, listbox2_scroll_event{ });
1584}
1585
1586message_result listbox2_row_element::get(sys::state& state, Cyto::Any& payload) noexcept {
1587 send(state, parent, listbox2_row_view{ this });
1588 return message_result::unseen;
1589}
1590
1591
1592template<class RowConT>
1594 if(payload.holds_type<RowConT>()) {
1595 payload.emplace<RowConT>(content);
1596 return message_result::consumed;
1597 } else if(payload.holds_type<wrapped_listbox_row_content<RowConT>>()) {
1598 content = any_cast<wrapped_listbox_row_content<RowConT>>(payload).content;
1599 impl_on_update(state);
1600 return message_result::consumed;
1601 }
1602 return message_result::unseen;
1603}
1604
1605template<class RowConT>
1607 if(payload.holds_type<RowConT>()) {
1608 payload.emplace<RowConT>(content);
1609 return message_result::consumed;
1610 } else if(payload.holds_type<wrapped_listbox_row_content<RowConT>>()) {
1611 content = any_cast<wrapped_listbox_row_content<RowConT>>(payload).content;
1612 update(state);
1613 return message_result::consumed;
1614 }
1615 return message_result::unseen;
1616}
1617
1618template<class RowWinT, class RowConT>
1620 auto content_off_screen = int32_t(row_contents.size() - row_windows.size());
1621 int32_t scroll_pos = list_scrollbar->raw_value();
1622 if(content_off_screen <= 0) {
1623 list_scrollbar->set_visible(state, false);
1624 scroll_pos = 0;
1625 } else {
1626 list_scrollbar->change_settings(state, mutable_scrollbar_settings{0, content_off_screen, 0, 0, false});
1627 list_scrollbar->set_visible(state, true);
1628 scroll_pos = std::min(scroll_pos, content_off_screen);
1629 }
1630
1631 if(is_reversed()) {
1632 auto i = int32_t(row_contents.size()) - scroll_pos - 1;
1633 for(int32_t rw_i = int32_t(row_windows.size()) - 1; rw_i >= 0; rw_i--) {
1634 RowWinT* row_window = row_windows[size_t(rw_i)];
1635 if(i >= 0) {
1636 row_window->set_visible(state, true);
1637 auto prior_content = retrieve<RowConT>(state, row_window);
1638 auto new_content = row_contents[i--];
1639
1640 if(prior_content != new_content) {
1641 send(state, row_window, wrapped_listbox_row_content<RowConT>{ new_content });
1642 if(!row_window->is_visible()) {
1643 row_window->set_visible(state, true);
1644 } else {
1645 row_window->impl_on_update(state);
1646 }
1647 } else {
1648 row_window->set_visible(state, true);
1649 }
1650 } else {
1651 row_window->set_visible(state, false);
1652 }
1653 }
1654 } else {
1655 auto i = size_t(scroll_pos);
1656 for(RowWinT* row_window : row_windows) {
1657 if(i < row_contents.size()) {
1658 auto prior_content = retrieve<RowConT>(state, row_window);
1659 auto new_content = row_contents[i++];
1660
1661 if(prior_content != new_content) {
1662 send(state, row_window, wrapped_listbox_row_content<RowConT>{ new_content });
1663 if(!row_window->is_visible()) {
1664 row_window->set_visible(state, true);
1665 } else {
1666 row_window->impl_on_update(state);
1667 }
1668 } else {
1669 row_window->set_visible(state, true);
1670 }
1671 } else {
1672 row_window->set_visible(state, false);
1673 }
1674 }
1675 }
1676}
1677
1678template<class RowWinT, class RowConT>
1680 if(row_contents.size() > row_windows.size()) {
1681 amount = is_reversed() ? -amount : amount;
1682 list_scrollbar->update_raw_value(state, list_scrollbar->raw_value() + (amount < 0 ? 1 : -1));
1683 state.ui_state.last_tooltip = nullptr; //force update of tooltip
1684 update(state);
1685 return message_result::consumed;
1686 }
1687 return message_result::unseen;
1688}
1689
1690template<class RowWinT, class RowConT>
1692 uint32_t list_size = 0;
1693 for(auto rc : row_contents) {
1694 list_size++;
1695 }
1696 list_scrollbar->update_raw_value(state, list_size);
1697 state.ui_state.last_tooltip = nullptr; //force update of tooltip
1698 update(state);
1699}
1700
1701template<typename contents_type>
1703 if(int32_t(row_contents.size()) > visible_row_count) {
1704 //amount = is_reversed() ? -amount : amount;
1705 list_scrollbar->update_raw_value(state, list_scrollbar->raw_value() + (amount < 0 ? 1 : -1));
1706 impl_on_update(state);
1707 return message_result::consumed;
1708 }
1709 return message_result::unseen;
1710}
1711
1712template<typename contents_type>
1714 auto content_off_screen = int32_t(row_contents.size()) - visible_row_count;
1715 int32_t scroll_pos = list_scrollbar->raw_value();
1716
1717 if(content_off_screen <= 0) {
1718 list_scrollbar->set_visible(state, false);
1719 list_scrollbar->update_raw_value(state, 0);
1720
1721 int32_t i = 0;
1722 for(; i < int32_t(row_contents.size()); ++i) {
1723 row_windows[i]->set_visible(state, true);
1724 }
1725 for(; i < int32_t(row_windows.size()); ++i) {
1726 row_windows[i]->set_visible(state, false);
1727 }
1728 } else {
1729 list_scrollbar->change_settings(state, mutable_scrollbar_settings{ 0, content_off_screen, 0, 0, false });
1730 list_scrollbar->set_visible(state, true);
1731 scroll_pos = std::min(scroll_pos, content_off_screen);
1732
1733 int32_t i = 0;
1734 for(; i < visible_row_count; ++i) {
1735 row_windows[i]->set_visible(state, true);
1736 }
1737 for(; i < int32_t(row_windows.size()); ++i) {
1738 row_windows[i]->set_visible(state, false);
1739 }
1740 }
1741}
1742
1743template<typename contents_type>
1745 if(payload.holds_type<listbox2_scroll_event>()) {
1746 impl_on_update(state);
1747 return message_result::consumed;
1748 } else if(payload.holds_type<listbox2_row_view>()) {
1749 listbox2_row_view ptr = any_cast<listbox2_row_view>(payload);
1750 for(int32_t index = 0; index < int32_t(row_windows.size()); ++index) {
1751 if(row_windows[index] == ptr.row) {
1752 stored_index = index + list_scrollbar->raw_value();
1753 return message_result::consumed;
1754 }
1755 }
1756 stored_index = -1;
1757 return message_result::consumed;
1758 } else if(payload.holds_type<contents_type>()) {
1759 if(0 <= stored_index && stored_index < int32_t(row_contents.size())) {
1760 payload = row_contents[stored_index];
1761 }
1762 return message_result::consumed;
1763 } else {
1764 return message_result::unseen;
1765 }
1766}
1767
1768template<typename contents_type>
1770 int32_t row_height = row_windows[0]->base_data.position.y + row_windows[0]->base_data.size.y;
1771 int32_t height_covered = int32_t(row_windows.size()) * row_height;
1772 int32_t required_rows = (height - height_covered) / row_height;
1773
1774 while(required_rows > 0) {
1775 auto new_row = make_row(state);
1776 row_windows.push_back(new_row.get());
1777 new_row->base_data.position.y += int16_t(height_covered);
1778 add_child_to_back(std::move(new_row));
1779
1780 height_covered += row_height;
1781 --required_rows;
1782 }
1783
1784 visible_row_count = height / row_height;
1785 base_data.size.y = int16_t(row_height * visible_row_count);
1786
1787 if(visible_row_count != 0) {
1788 if(scrollbar_is_internal)
1789 base_data.size.x -= 16;
1790 list_scrollbar->scale_to_parent();
1791 if(scrollbar_is_internal)
1792 base_data.size.x += 16;
1793 }
1794}
1795
1796template<typename contents_type>
1798 auto ptr = make_element_by_type<listbox2_scrollbar>(state, "standardlistbox_slider");
1799 list_scrollbar = static_cast<listbox2_scrollbar*>(ptr.get());
1800 add_child_to_back(std::move(ptr));
1801
1802 auto base_row = make_row(state);
1803 row_windows.push_back(base_row.get());
1804 add_child_to_back(std::move(base_row));
1805
1806 resize(state, base_data.size.y);
1807}
1808template<typename contents_type>
1809void listbox2_base<contents_type>::render(sys::state& state, int32_t x, int32_t y) noexcept {
1810 dcon::gfx_object_id gid = base_data.data.list_box.background_image;
1811 if(gid) {
1812 auto const& gfx_def = state.ui_defs.gfx[gid];
1813 if(gfx_def.primary_texture_handle) {
1814 if(gfx_def.get_object_type() == ui::object_type::bordered_rect) {
1815 ogl::render_bordered_rect(state, get_color_modification(false, false, true), gfx_def.type_dependent, float(x), float(y),
1816 float(base_data.size.x), float(base_data.size.y),
1817 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
1818 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
1819 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
1820 } else {
1821 ogl::render_textured_rect(state, get_color_modification(false, false, true), float(x), float(y), float(base_data.size.x),
1822 float(base_data.size.y),
1823 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
1824 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
1825 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
1826 }
1827 }
1828 }
1829 container_base::render(state, x, y);
1830}
1831
1832template<class RowWinT, class RowConT>
1834 int16_t current_y = 0;
1835 int16_t subwindow_y_size = 0;
1836 while(current_y + subwindow_y_size <= base_data.size.y) {
1837 auto ptr = make_element_by_type<RowWinT>(state, get_row_element_name());
1838 row_windows.push_back(static_cast<RowWinT*>(ptr.get()));
1839 int16_t offset = ptr->base_data.position.y;
1840 ptr->base_data.position.y += current_y;
1841 subwindow_y_size = ptr->base_data.size.y;
1842 current_y += ptr->base_data.size.y + offset;
1843 add_child_to_front(std::move(ptr));
1844 }
1845 auto ptr = make_element_by_type<standard_listbox_scrollbar<RowWinT, RowConT>>(state, "standardlistbox_slider");
1846 list_scrollbar = static_cast<standard_listbox_scrollbar<RowWinT, RowConT>*>(ptr.get());
1847 add_child_to_front(std::move(ptr));
1848 list_scrollbar->scale_to_parent();
1849
1850 update(state);
1851}
1852
1853template<class RowWinT, class RowConT>
1855 dcon::gfx_object_id gid = base_data.data.list_box.background_image;
1856 if(gid) {
1857 auto const& gfx_def = state.ui_defs.gfx[gid];
1858 if(gfx_def.primary_texture_handle) {
1859 if(gfx_def.get_object_type() == ui::object_type::bordered_rect) {
1860 ogl::render_bordered_rect(state, get_color_modification(false, false, true), gfx_def.type_dependent, float(x), float(y),
1861 float(base_data.size.x), float(base_data.size.y),
1862 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
1863 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
1864 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
1865 } else {
1866 ogl::render_textured_rect(state, get_color_modification(false, false, true), float(x), float(y), float(base_data.size.x),
1867 float(base_data.size.y),
1868 ogl::get_texture_handle(state, gfx_def.primary_texture_handle, gfx_def.is_partially_transparent()),
1869 base_data.get_rotation(), gfx_def.is_vertically_flipped(),
1870 state.world.locale_get_native_rtl(state.font_collection.get_current_locale()));
1871 }
1872 }
1873 }
1874 container_base::render(state, x, y);
1875}
1876template<class ItemWinT, class ItemConT>
1878 auto spacing = int16_t(base_data.data.overlapping.spacing);
1879 if(base_data.get_element_type() == element_type::overlapping) {
1880 while(row_contents.size() > windows.size()) {
1881 auto ptr = make_element_by_type<ItemWinT>(state, get_row_element_name());
1882 if(subwindow_width <= 0) {
1883 subwindow_width = ptr->base_data.size.x;
1884 }
1885 windows.push_back(static_cast<ItemWinT*>(ptr.get()));
1886 add_child_to_front(std::move(ptr));
1887 }
1888
1889 float size_ratio = float(row_contents.size() * (subwindow_width + spacing)) / float(base_data.size.x);
1890 int16_t offset = spacing + subwindow_width;
1891 if(size_ratio > 1.f) {
1892 offset = int16_t(float(subwindow_width) / size_ratio);
1893 }
1894 int16_t current_x = 0;
1895 if(base_data.data.overlapping.image_alignment == alignment::right) {
1896 current_x = base_data.size.x - subwindow_width - offset * int16_t(row_contents.size() - 1);
1897 }
1898 for(size_t i = 0; i < windows.size(); i++) {
1899 if(i < row_contents.size()) {
1900 update_subwindow(state, *windows[i], row_contents[i]);
1901 windows[i]->base_data.position.x = current_x;
1902 current_x += offset;
1903 windows[i]->set_visible(state, true);
1904 } else {
1905 windows[i]->set_visible(state, false);
1906 }
1907 }
1908 }
1909}
1910
1911std::string_view overlapping_flags_box::get_row_element_name() {
1912 return "flag_list_flag";
1913}
1914
1915void overlapping_flags_box::update_subwindow(sys::state& state, overlapping_flags_flag_button& subwindow,
1916 dcon::national_identity_id content) {
1917 subwindow.set_current_nation(state, content);
1918}
1919
1920void overlapping_flags_box::on_update(sys::state& state) noexcept {
1921 current_nation = retrieve<dcon::nation_id>(state, parent);
1922 populate_flags(state);
1923}
1924
1925void overlapping_friendly_flags::populate_flags(sys::state& state) {
1926 if(bool(current_nation)) {
1927 row_contents.clear();
1928 for(auto it : state.world.nation_get_gp_relationship_as_great_power(current_nation)) {
1930 row_contents.push_back(it.get_influence_target().get_identity_from_identity_holder());
1931 }
1932 }
1933 update(state);
1934 }
1935}
1936
1937void overlapping_cordial_flags::populate_flags(sys::state& state) {
1938 if(bool(current_nation)) {
1939 row_contents.clear();
1940 for(auto it : state.world.nation_get_gp_relationship_as_great_power(current_nation)) {
1942 row_contents.push_back(it.get_influence_target().get_identity_from_identity_holder());
1943 }
1944 }
1945 update(state);
1946 }
1947}
1948
1949void overlapping_sphere_flags::populate_flags(sys::state& state) {
1950 if(bool(current_nation)) {
1951 row_contents.clear();
1952 if(state.world.nation_get_in_sphere_of(current_nation)) {
1953 row_contents.push_back(state.world.nation_get_in_sphere_of(current_nation).get_identity_from_identity_holder());
1954 }
1955 for(auto it : state.world.nation_get_gp_relationship_as_great_power(current_nation)) {
1957 row_contents.push_back(it.get_influence_target().get_identity_from_identity_holder());
1958 }
1959 }
1960 update(state);
1961 }
1962}
1963
1964void overlapping_puppet_flags::populate_flags(sys::state& state) {
1965 if(bool(current_nation)) {
1966 row_contents.clear();
1967 auto fat_id = dcon::fatten(state.world, current_nation);
1968 auto overlord = state.world.nation_get_overlord_as_subject(current_nation);
1969 auto overlord_nation = dcon::fatten(state.world, overlord).get_ruler();
1970 if(bool(overlord_nation)) {
1971 row_contents.push_back(overlord_nation.get_identity_from_identity_holder().id);
1972 } else {
1973 for(auto puppet : state.world.nation_get_overlord_as_ruler(current_nation)) {
1974 row_contents.push_back(puppet.get_subject().get_identity_from_identity_holder().id);
1975 }
1976 }
1977 update(state);
1978 }
1979}
1980
1981void overlapping_ally_flags::populate_flags(sys::state& state) {
1982 if(bool(current_nation)) {
1983 row_contents.clear();
1984 for(auto rel : state.world.nation_get_diplomatic_relation(current_nation)) {
1985 if(rel.get_are_allied()) {
1986 auto ally = nations::get_relationship_partner(state, rel.id, current_nation);
1987 auto fat_ally = dcon::fatten(state.world, ally);
1988 row_contents.push_back(fat_ally.get_identity_from_identity_holder().id);
1989 }
1990 }
1991 update(state);
1992 }
1993}
1994
1995void overlapping_enemy_flags::populate_flags(sys::state& state) {
1996 if(bool(current_nation)) {
1997 row_contents.clear();
1998 for(auto wa : state.world.nation_get_war_participant(current_nation)) {
1999 bool is_attacker = wa.get_is_attacker();
2000 for(auto o : wa.get_war().get_war_participant()) {
2001 if(o.get_is_attacker() != is_attacker) {
2002 row_contents.push_back(o.get_nation().get_identity_from_identity_holder().id);
2003 }
2004 }
2005 }
2006 update(state);
2007 }
2008}
2009
2010
2011std::string_view overlapping_truce_flags::get_row_element_name() {
2012 return "flag_list_flag";
2013}
2014
2015void overlapping_truce_flag_button::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2016 text::add_line(state, contents, "trucewith", text::variable_type::list, n);
2017 text::add_line(state, contents, "until_date", text::variable_type::x, until);
2018}
2019
2020void overlapping_truce_flags::update_subwindow(sys::state& state, overlapping_truce_flag_button& subwindow, truce_pair content) {
2021 subwindow.upate_truce(state, content.n, content.until);
2022}
2023
2024void overlapping_truce_flags::on_update(sys::state& state) noexcept {
2025 current_nation = retrieve<dcon::nation_id>(state, parent);
2026
2027 row_contents.clear();
2028 for(auto rel : state.world.nation_get_diplomatic_relation(current_nation)) {
2029 if(rel.get_truce_until() && state.current_date < rel.get_truce_until()) {
2030 auto other = rel.get_related_nations(0) != current_nation ? rel.get_related_nations(0) : rel.get_related_nations(1);
2031 if(!military::are_at_war(state, current_nation, other)) {
2032 row_contents.push_back(truce_pair{ other, rel.get_truce_until() });
2033 }
2034 }
2035 }
2036 update(state);
2037}
2038
2039
2040dcon::national_identity_id flag_button::get_current_nation(sys::state& state) noexcept {
2041 auto nation = retrieve<dcon::national_identity_id>(state, parent);
2042 if(bool(nation)) {
2043 return nation;
2044 } else {
2045 return state.world.nation_get_identity_from_identity_holder(retrieve<dcon::nation_id>(state, parent));
2046 }
2047}
2048
2049dcon::rebel_faction_id flag_button::get_current_rebel_faction(sys::state& state) noexcept {
2050 return retrieve<dcon::rebel_faction_id>(state, parent);
2051}
2052
2053void flag_button::button_action(sys::state& state) noexcept {
2054 auto ident = get_current_nation(state);
2055 if(!ident)
2056 return;
2057
2058 auto fat_id = dcon::fatten(state.world, ident);
2059 auto nation = fat_id.get_nation_from_identity_holder();
2060 if(bool(nation.id) && nation.get_owned_province_count() != 0) {
2061 state.open_diplomacy(nation.id);
2062 }
2063}
2064
2065void flag_button2::button_action(sys::state& state) noexcept {
2066 auto nid = retrieve<dcon::nation_id>(state, this);
2067 auto tid = retrieve<dcon::national_identity_id>(state, this);
2068 if(!nid && tid) {
2069 nid = state.world.national_identity_get_nation_from_identity_holder(tid);
2070 }
2071 auto rid = retrieve<dcon::rebel_faction_id>(state, this);
2072 if(!nid && rid) {
2073 nid = state.world.rebel_faction_get_ruler_from_rebellion_within(rid);
2074 }
2075 if(nid && state.world.nation_get_owned_province_count(nid) != 0)
2076 state.open_diplomacy(nid);
2077}
2078
2079void flag_button2::on_update(sys::state& state) noexcept {
2080 auto nid = retrieve<dcon::nation_id>(state, this);
2081 if(nid) {
2082 flag_texture_handle = ogl::get_flag_handle(state, state.world.nation_get_identity_from_identity_holder(nid), culture::get_current_flag_type(state, nid));
2083 return;
2084 }
2085
2086 auto tid = retrieve<dcon::national_identity_id>(state, this);
2087 if(!nid && tid) {
2088 flag_texture_handle = ogl::get_flag_handle(state, tid, culture::get_current_flag_type(state, tid));
2089 return;
2090 }
2091
2092 auto rid = retrieve<dcon::rebel_faction_id>(state, this);
2093 if(!nid && !tid && rid) {
2094 flag_texture_handle = ogl::get_rebel_flag_handle(state, rid);
2095 return;
2096 }
2097
2098 auto reb_tag = state.national_definitions.rebel_id;
2099 flag_texture_handle = ogl::get_flag_handle(state, reb_tag, culture::flag_type::default_flag);
2100}
2101
2102void flag_button2::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2103 auto nid = retrieve<dcon::nation_id>(state, this);
2104 if(nid) {
2105 auto box = text::open_layout_box(contents, 0);
2106 text::add_to_layout_box(state, contents, box, nid);
2107 text::close_layout_box(contents, box);
2108 return;
2109 }
2110 auto tid = retrieve<dcon::national_identity_id>(state, this);
2111 if(tid) {
2112 auto box = text::open_layout_box(contents, 0);
2113 text::add_to_layout_box(state, contents, box, tid);
2114 text::close_layout_box(contents, box);
2115 return;
2116 }
2117 auto rid = retrieve<dcon::rebel_faction_id>(state, this);
2118 if(rid) {
2119 auto box = text::open_layout_box(contents, 0);
2121 text::close_layout_box(contents, box);
2122 return;
2123 }
2124}
2125
2126void flag_button2::render(sys::state& state, int32_t x, int32_t y) noexcept {
2127 dcon::gfx_object_id gid;
2128 if(base_data.get_element_type() == element_type::image) {
2129 gid = base_data.data.image.gfx_object;
2130 } else if(base_data.get_element_type() == element_type::button) {
2131 gid = base_data.data.button.button_image;
2132 }
2133 if(gid && flag_texture_handle > 0) {
2134 auto const& gfx_def = state.ui_defs.gfx[gid];
2135 if(gfx_def.type_dependent) {
2136 auto mask_handle = ogl::get_texture_handle(state, dcon::texture_id(gfx_def.type_dependent - 1), true);
2137 auto& mask_tex = state.open_gl.asset_textures[dcon::texture_id(gfx_def.type_dependent - 1)];
2138 ogl::render_masked_rect(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
2139 float(x) + float(base_data.size.x - mask_tex.size_x) * 0.5f,
2140 float(y) + float(base_data.size.y - mask_tex.size_y) * 0.5f,
2141 float(mask_tex.size_x),
2142 float(mask_tex.size_y),
2143 flag_texture_handle, mask_handle, base_data.get_rotation(), gfx_def.is_vertically_flipped(),
2144 false);
2145 } else {
2146 ogl::render_textured_rect(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
2147 float(x), float(y), float(base_data.size.x), float(base_data.size.y), flag_texture_handle, base_data.get_rotation(),
2148 gfx_def.is_vertically_flipped(),
2149 false);
2150 }
2151 }
2152 image_element_base::render(state, x, y);
2153}
2154
2155void flag_button::set_current_nation(sys::state& state, dcon::national_identity_id ident) noexcept {
2156 if(!bool(ident))
2157 ident = state.national_definitions.rebel_id;
2158
2159 auto fat_id = dcon::fatten(state.world, ident);
2160 auto nation = fat_id.get_nation_from_identity_holder();
2162 if(bool(nation.id) && nation.get_owned_province_count() != 0) {
2163 flag_type = culture::get_current_flag_type(state, nation.id);
2164 } else {
2166 }
2167 flag_texture_handle = ogl::get_flag_handle(state, ident, flag_type);
2168}
2169
2170void flag_button::on_update(sys::state& state) noexcept {
2171 set_current_nation(state, get_current_nation(state));
2172}
2173
2174void flag_button::on_create(sys::state& state) noexcept {
2175 button_element_base::on_create(state);
2176}
2177
2178void flag_button::render(sys::state& state, int32_t x, int32_t y) noexcept {
2179 dcon::gfx_object_id gid;
2180 if(base_data.get_element_type() == element_type::image) {
2181 gid = base_data.data.image.gfx_object;
2182 } else if(base_data.get_element_type() == element_type::button) {
2183 gid = base_data.data.button.button_image;
2184 }
2185 if(gid && flag_texture_handle > 0) {
2186 auto const& gfx_def = state.ui_defs.gfx[gid];
2187 if(gfx_def.type_dependent) {
2188 auto mask_handle = ogl::get_texture_handle(state, dcon::texture_id(gfx_def.type_dependent - 1), true);
2189 auto& mask_tex = state.open_gl.asset_textures[dcon::texture_id(gfx_def.type_dependent - 1)];
2190 ogl::render_masked_rect(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
2191 float(x) + float(base_data.size.x - mask_tex.size_x) * 0.5f,
2192 float(y) + float(base_data.size.y - mask_tex.size_y) * 0.5f,
2193 float(mask_tex.size_x),
2194 float(mask_tex.size_y),
2195 flag_texture_handle, mask_handle, base_data.get_rotation(), gfx_def.is_vertically_flipped(),
2196 false);
2197 } else {
2198 ogl::render_textured_rect(state, get_color_modification(this == state.ui_state.under_mouse, disabled, interactable),
2199 float(x), float(y), float(base_data.size.x), float(base_data.size.y), flag_texture_handle, base_data.get_rotation(),
2200 gfx_def.is_vertically_flipped(),
2201 false);
2202 }
2203 }
2204 image_element_base::render(state, x, y);
2205}
2206void flag_button::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2207 auto ident = get_current_nation(state);
2208 if(ident) {
2210 if(name) {
2211 auto box = text::open_layout_box(contents, 0);
2212 text::add_to_layout_box(state, contents, box, name);
2213 text::close_layout_box(contents, box);
2214 }
2215 } else {
2216 if(auto reb = get_current_rebel_faction(state); reb) {
2217 auto box = text::open_layout_box(contents, 0);
2219 text::close_layout_box(contents, box);
2220 return;
2221 }
2222 }
2223}
2224
2225void province_script_button::button_action(sys::state& state) noexcept {
2226 auto p = retrieve<dcon::province_id>(state, parent);
2227 if(p && state.local_player_nation)
2228 command::use_province_button(state, state.local_player_nation, base_definition, p);
2229}
2230void province_script_button::on_update(sys::state& state) noexcept {
2231 disabled = false;
2232 auto& def = state.ui_defs.gui[base_definition];
2233 if(def.get_element_type() != ui::element_type::button) {
2234 disabled = true;
2235 return;
2236 }
2237 if(def.data.button.get_button_scripting() != ui::button_scripting::province) {
2238 disabled = true;
2239 return;
2240 }
2241 auto p = retrieve<dcon::province_id>(state, parent);
2242 if(!p) {
2243 disabled = true;
2244 return;
2245 }
2246 disabled = !command::can_use_province_button(state, state.local_player_nation, base_definition, p);
2247}
2248void province_script_button::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2249 auto& def = state.ui_defs.gui[base_definition];
2250
2251 if(def.get_element_type() != ui::element_type::button)
2252 return;
2253 if(def.data.button.get_button_scripting() != ui::button_scripting::province)
2254 return;
2255 auto p = retrieve<dcon::province_id>(state, parent);
2256 if(!p)
2257 return;
2258 if(!state.local_player_nation)
2259 return;
2260
2261 auto name = state.to_string_view(def.name);
2262 auto tt_name = std::string{ name } + "_tooltip";
2263 if(state.key_is_localized(tt_name)) {
2264 text::add_line(state, contents, std::string_view{tt_name}, text::variable_type::province, p, text::variable_type::nation, state.world.province_get_nation_from_province_ownership(p), text::variable_type::player, state.local_player_nation);
2266 }
2267
2268 if(def.data.button.scriptable_enable) {
2269 text::add_line(state, contents, "allow_reform_cond");
2270 ui::trigger_description(state, contents, def.data.button.scriptable_enable, trigger::to_generic(p), trigger::to_generic(p), trigger::to_generic(state.local_player_nation));
2272 }
2273 if(def.data.button.scriptable_effect) {
2274 text::add_line(state, contents, "msg_decision_2");
2275 ui::effect_description(state, contents, def.data.button.scriptable_effect, trigger::to_generic(p), trigger::to_generic(p), trigger::to_generic(state.local_player_nation), uint32_t(state.current_date.value), uint32_t(p.index() ^ (base_definition.index() << 4)));
2276 }
2277}
2278void nation_script_button::button_action(sys::state& state) noexcept {
2279 auto n = retrieve<dcon::nation_id>(state, parent);
2280 if(n && state.local_player_nation) {
2281 command::use_nation_button(state, state.local_player_nation, base_definition, n);
2282 } else if(state.local_player_nation) {
2283 command::use_nation_button(state, state.local_player_nation, base_definition, state.local_player_nation);
2284 }
2285}
2286void nation_script_button::on_update(sys::state& state) noexcept {
2287 disabled = false;
2288 auto& def = state.ui_defs.gui[base_definition];
2289 if(def.get_element_type() != ui::element_type::button) {
2290 disabled = true;
2291 return;
2292 }
2293 if(def.data.button.get_button_scripting() != ui::button_scripting::nation) {
2294 disabled = true;
2295 return;
2296 }
2297 auto n = retrieve<dcon::nation_id>(state, parent);
2298 if(!state.local_player_nation) {
2299 disabled = true;
2300 return;
2301 }
2302 disabled = !command::can_use_nation_button(state, state.local_player_nation, base_definition, n ? n : state.local_player_nation);
2303}
2304void nation_script_button::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2305 auto& def = state.ui_defs.gui[base_definition];
2306
2307 if(def.get_element_type() != ui::element_type::button)
2308 return;
2309 if(def.data.button.get_button_scripting() != ui::button_scripting::nation)
2310 return;
2311 auto n = retrieve<dcon::nation_id>(state, parent);
2312 if(!n)
2313 n = state.local_player_nation;
2314 if(!state.local_player_nation)
2315 return;
2316
2317 auto name = state.to_string_view(def.name);
2318 auto tt_name = std::string{ name } + "_tooltip";
2319 if(state.key_is_localized(tt_name)) {
2320 text::add_line(state, contents, std::string_view{ tt_name }, text::variable_type::nation, n, text::variable_type::player, state.local_player_nation);
2322 }
2323
2324 if(def.data.button.scriptable_enable) {
2325 text::add_line(state, contents, "allow_reform_cond");
2326 ui::trigger_description(state, contents, def.data.button.scriptable_enable, trigger::to_generic(n), trigger::to_generic(n), trigger::to_generic(state.local_player_nation));
2328 }
2329 if(def.data.button.scriptable_effect) {
2330 text::add_line(state, contents, "msg_decision_2");
2331 ui::effect_description(state, contents, def.data.button.scriptable_effect, trigger::to_generic(n), trigger::to_generic(n), trigger::to_generic(state.local_player_nation), uint32_t(state.current_date.value), uint32_t(n.index() ^ (base_definition.index() << 4)));
2332 }
2333}
2334
2335message_result draggable_target::on_lbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept {
2336 for(auto tmp = parent; tmp != nullptr; tmp = tmp->parent) {
2337 if(tmp->base_data.get_element_type() == element_type::window && tmp->base_data.data.window.is_moveable()) {
2338 state.ui_state.drag_target = tmp;
2339 return message_result::consumed;
2340 }
2341 }
2342 return message_result::consumed;
2343}
2344
2345std::unique_ptr<element_base> make_element_immediate(sys::state& state, dcon::gui_def_id id) {
2346 auto& def = state.ui_defs.gui[id];
2347 if(def.get_element_type() == ui::element_type::image) {
2348 auto res = std::make_unique<image_element_base>();
2349 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2350 make_size_from_graphics(state, res->base_data);
2351 res->on_create(state);
2352 return res;
2353 } else if(def.get_element_type() == ui::element_type::button) {
2354 if(def.data.button.get_button_scripting() == ui::button_scripting::province) {
2355 auto res = std::make_unique<province_script_button>(id);
2356 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2357 make_size_from_graphics(state, res->base_data);
2358 res->on_create(state);
2359 return res;
2360 } else if(def.data.button.get_button_scripting() == ui::button_scripting::nation) {
2361 auto res = std::make_unique<nation_script_button>(id);
2362 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2363 make_size_from_graphics(state, res->base_data);
2364 res->on_create(state);
2365 return res;
2366 } else {
2367 auto res = std::make_unique<button_element_base>();
2368 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2369 make_size_from_graphics(state, res->base_data);
2370 res->on_create(state);
2371 return res;
2372 }
2373 } else if(def.get_element_type() == ui::element_type::window) {
2374 auto res = std::make_unique<window_element_base>();
2375 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2376 res->on_create(state);
2377 return res;
2378 } else if(def.get_element_type() == ui::element_type::scrollbar) {
2379 auto res = std::make_unique<scrollbar>();
2380 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2381 res->on_create(state);
2382 return res;
2383 } else if(def.get_element_type() == ui::element_type::text) {
2384 auto res = std::make_unique<simple_text_element_base>();
2385 std::memcpy(&(res->base_data), &def, sizeof(ui::element_data));
2386 res->on_create(state);
2387 return res;
2388 }
2389 // TODO: other defaults
2390
2391 return nullptr;
2392}
2393
2394void scrollbar_left::button_action(sys::state& state) noexcept {
2395 send(state, parent, value_change{ -step_size, true, true });
2396 if(parent) {
2397 parent->impl_on_drag_finish(state);
2398 }
2399}
2400void scrollbar_left::button_shift_action(sys::state& state) noexcept {
2401 send(state, parent, value_change{ -step_size * 5, true, true });
2402 if(parent) {
2403 parent->impl_on_drag_finish(state);
2404 }
2405}
2406void scrollbar_left::button_shift_right_action(sys::state& state) noexcept {
2407 send(state, parent, value_change{ -step_size * 10000, true, true });
2408 if(parent) {
2409 parent->impl_on_drag_finish(state);
2410 }
2411}
2412void scrollbar_right::button_action(sys::state& state) noexcept {
2413 send(state, parent, value_change{ step_size, true, true });
2414 if(parent) {
2415 parent->impl_on_drag_finish(state);
2416 }
2417}
2418void scrollbar_right::button_shift_action(sys::state& state) noexcept {
2419 send(state, parent, value_change{ step_size * 5, true, true });
2420 if(parent) {
2421 parent->impl_on_drag_finish(state);
2422 }
2423}
2424void scrollbar_right::button_shift_right_action(sys::state& state) noexcept {
2425 send(state, parent, value_change{ step_size * 10000, true, true });
2426 if(parent) {
2427 parent->impl_on_drag_finish(state);
2428 }
2429}
2430
2431message_result scrollbar_right::set(sys::state& state, Cyto::Any& payload) noexcept {
2432 if(payload.holds_type<scrollbar_settings>()) {
2433 return message_result::consumed;
2434 }
2435 return message_result::unseen;
2436}
2437message_result scrollbar_left::set(sys::state& state, Cyto::Any& payload) noexcept {
2438 if(payload.holds_type<scrollbar_settings>()) {
2439 return message_result::consumed;
2440 }
2441 return message_result::unseen;
2442}
2443
2444message_result scrollbar_track::on_lbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept {
2445 scrollbar_settings parent_state = retrieve<scrollbar_settings>(state, parent);
2446 int32_t pos_in_track = parent_state.vertical ? y : x;
2447 int32_t clamped_pos = std::clamp(pos_in_track, parent_state.buttons_size / 2, parent_state.track_size - parent_state.buttons_size / 2);
2448 float fp_pos = float(clamped_pos - parent_state.buttons_size / 2) / float(parent_state.track_size - parent_state.buttons_size);
2449 if(!parent_state.vertical && state.world.locale_get_native_rtl(state.font_collection.get_current_locale())) {
2450 fp_pos = 1.f - fp_pos;
2451 assert(fp_pos >= 0.f && fp_pos <= 1.f);
2452 }
2453 send(state, parent, value_change{ int32_t(parent_state.lower_value + fp_pos * (parent_state.upper_value - parent_state.lower_value)), true, false });
2454 return message_result::consumed;
2455}
2456
2457tooltip_behavior scrollbar_track::has_tooltip(sys::state& state) noexcept {
2458 if(parent)
2459 return parent->has_tooltip(state);
2460 return opaque_element_base::has_tooltip(state);
2461}
2462void scrollbar_track::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2463 if(parent)
2464 return parent->update_tooltip(state, x, y, contents);
2465 return opaque_element_base::update_tooltip(state, x, y, contents);
2466}
2467
2468message_result scrollbar_slider::on_lbutton_down(sys::state& state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept {
2469 state.ui_state.drag_target = this;
2470 return message_result::consumed;
2471}
2472void scrollbar_slider::on_drag(sys::state& state, int32_t oldx, int32_t oldy, int32_t x, int32_t y, sys::key_modifiers mods) noexcept {
2473 if(!parent)
2474 return;
2475
2476 auto location_abs = get_absolute_location(state, *this);
2477 scrollbar_settings parent_settings = retrieve<scrollbar_settings>(state, parent);
2478 if(parent_settings.vertical) {
2479 if(!(location_abs.y <= oldy && oldy < base_data.size.y + location_abs.y)) {
2480 return; // drag has left slider behind
2481 }
2482 } else {
2483 if(!(location_abs.x <= oldx && oldx < base_data.size.x + location_abs.x)) {
2484 return; // drag has left slider behind
2485 }
2486 }
2487
2488 int32_t pos_in_track = 0;
2489
2490 // TODO: take care of case where there are partial range limits
2491
2492 float min_percentage = float(parent_settings.lower_limit - parent_settings.lower_value) / float(parent_settings.upper_value - parent_settings.lower_value);
2493 auto min_offest = parent_settings.buttons_size + int32_t((parent_settings.track_size - parent_settings.buttons_size) * min_percentage);
2494
2495 float max_percentage = float(parent_settings.upper_limit - parent_settings.lower_value) / float(parent_settings.upper_value - parent_settings.lower_value);
2496 auto max_offest = parent_settings.buttons_size + int32_t((parent_settings.track_size - parent_settings.buttons_size) * max_percentage);
2497
2498 if(parent_settings.vertical) {
2499 base_data.position.y += int16_t(y - oldy);
2500 base_data.position.y = int16_t(std::clamp(int32_t(base_data.position.y), parent_settings.using_limits ? min_offest : parent_settings.buttons_size, parent_settings.using_limits ? max_offest : parent_settings.track_size));
2501 pos_in_track = base_data.position.y - parent_settings.buttons_size / 2;
2502 } else {
2503 if(state.world.locale_get_native_rtl(state.font_collection.get_current_locale())) {
2504 base_data.position.x += int16_t(oldx - x);
2505 } else {
2506 base_data.position.x += int16_t(x - oldx);
2507 }
2508 base_data.position.x = int16_t(std::clamp(int32_t(base_data.position.x), parent_settings.using_limits ? min_offest : parent_settings.buttons_size, parent_settings.using_limits ? max_offest : parent_settings.track_size));
2509 pos_in_track = base_data.position.x - parent_settings.buttons_size / 2;
2510 }
2511 float fp_pos = float(pos_in_track - parent_settings.buttons_size / 2) / float(parent_settings.track_size - parent_settings.buttons_size);
2512
2513 Cyto::Any adjustment_payload = value_change{ int32_t(parent_settings.lower_value + fp_pos * (parent_settings.upper_value - parent_settings.lower_value)), true, false};
2514 parent->impl_get(state, adjustment_payload);
2515}
2516
2517void scrollbar::update_raw_value(sys::state& state, int32_t v) {
2518 stored_value = settings.using_limits ? std::clamp(v, settings.lower_limit, settings.upper_limit) : std::clamp(v, settings.lower_value, settings.upper_value);
2519
2520 float percentage = float(stored_value - settings.lower_value) / float(settings.upper_value - settings.lower_value);
2521 auto offset = settings.buttons_size + int32_t((settings.track_size - settings.buttons_size) * percentage);
2522 if(slider && state.ui_state.drag_target != slider) {
2523 if(settings.vertical)
2524 slider->base_data.position.y = int16_t(offset);
2525 else
2526 slider->base_data.position.x = int16_t(offset);
2527 }
2528}
2529void scrollbar::update_scaled_value(sys::state& state, float v) {
2530 float scaling_factor = float(settings.scaling_factor);
2531 // Non-vanilla special accomodation
2532 if(base_data.data.scrollbar.get_step_size() == ui::step_size::twenty_five) {
2533 scaling_factor = 0.25f;
2534 }
2535 int32_t rv = std::clamp(int32_t(v * scaling_factor), settings.lower_value, settings.upper_value);
2536 update_raw_value(state, rv);
2537}
2538float scrollbar::scaled_value() const {
2539 return float(stored_value) / float(settings.scaling_factor);
2540}
2541int32_t scrollbar::raw_value() const {
2542 return stored_value;
2543}
2544
2545void scrollbar::change_settings(sys::state& state, mutable_scrollbar_settings const& settings_s) {
2546 settings.lower_limit = settings_s.lower_limit * settings.scaling_factor;
2547 settings.lower_value = settings_s.lower_value * settings.scaling_factor;
2548 settings.upper_limit = settings_s.upper_limit * settings.scaling_factor;
2549 settings.upper_value = settings_s.upper_value * settings.scaling_factor;
2550 settings.using_limits = settings_s.using_limits;
2551
2552 settings.upper_value = std::max(settings.upper_value, settings.lower_value + 1); // ensure the scrollbar is never of range zero
2553
2554 if(settings.using_limits) {
2555 if(right_limit) {
2556 right_limit->set_visible(state, settings.lower_value < settings.lower_limit);
2557
2558 float percentage = float(settings.lower_limit - settings.lower_value) / float(settings.upper_value - settings.lower_value);
2559 auto offset = settings.buttons_size + int32_t((settings.track_size - settings.buttons_size) * percentage);
2560 if(settings.vertical)
2561 right_limit->base_data.position.y = int16_t(offset);
2562 else
2563 right_limit->base_data.position.x = int16_t(offset);
2564 }
2565 if(left_limit) {
2566 left_limit->set_visible(state, settings.upper_value > settings.upper_limit);
2567
2568 float percentage = float(settings.upper_limit - settings.lower_value) / float(settings.upper_value - settings.lower_value);
2569 auto offset = settings.buttons_size + int32_t((settings.track_size - settings.buttons_size) * percentage);
2570 if(settings.vertical)
2571 left_limit->base_data.position.y = int16_t(offset + settings.buttons_size - 10);
2572 else
2573 left_limit->base_data.position.x = int16_t(offset + settings.buttons_size - 10);
2574 }
2575 if(stored_value < settings.lower_limit || stored_value > settings.upper_limit) {
2576 update_raw_value(state, stored_value);
2577 //on_value_change(state, stored_value);
2578 }
2579 } else {
2580 if(right_limit)
2581 right_limit->set_visible(state, false);
2582 if(left_limit)
2583 left_limit->set_visible(state, false);
2584 if(stored_value < settings.lower_value || stored_value > settings.upper_value) {
2585 update_raw_value(state, stored_value);
2586 //on_value_change(state, stored_value);
2587 }
2588 }
2589}
2590
2591void scrollbar::on_create(sys::state& state) noexcept {
2592 // create & position sub elements
2593 // fill out settings data
2594 // set initial slider pos
2595
2596 if(base_data.get_element_type() == element_type::scrollbar) {
2597 auto step = base_data.data.scrollbar.get_step_size();
2598 settings.scaling_factor = 1;
2599 switch(step) {
2600 case step_size::twenty_five:
2601 break;
2602 case step_size::two:
2603 break;
2604 case step_size::one:
2605 break;
2606 case step_size::one_tenth:
2607 settings.scaling_factor = 10;
2608 break;
2609 case step_size::one_hundredth:
2610 settings.scaling_factor = 100;
2611 break;
2612 case step_size::one_thousandth:
2613 settings.scaling_factor = 1000;
2614 break;
2615 }
2616 settings.lower_value = 0;
2617 settings.upper_value = base_data.data.scrollbar.max_value * settings.scaling_factor;
2618 settings.lower_limit = 0;
2619 settings.upper_limit = settings.upper_value;
2620
2621 settings.vertical = !base_data.data.scrollbar.is_horizontal();
2622 stored_value = settings.lower_value;
2623
2624 auto first_child = base_data.data.scrollbar.first_child;
2625 auto num_children = base_data.data.scrollbar.num_children;
2626
2627 if(num_children >= 6) {
2628 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(5 + first_child.index()));
2629 auto ch_res = make_element_by_type<image_element_base>(state, child_tag);
2630 right_limit = ch_res.get();
2631 right_limit->set_visible(state, false);
2632 add_child_to_back(std::move(ch_res));
2633 }
2634 if(num_children >= 5) {
2635 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(4 + first_child.index()));
2636 auto ch_res = make_element_by_type<image_element_base>(state, child_tag);
2637 left_limit = ch_res.get();
2638 left_limit->set_visible(state, false);
2639 add_child_to_back(std::move(ch_res));
2640 }
2641
2642 if(num_children >= 4) {
2643 {
2644 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(2 + first_child.index()));
2645 auto ch_res = make_element_by_type<scrollbar_slider>(state, child_tag);
2646 slider = ch_res.get();
2647 add_child_to_back(std::move(ch_res));
2648 }
2649 {
2650 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(0 + first_child.index()));
2651 auto ch_res = make_element_by_type<scrollbar_left>(state, child_tag);
2652 left = ch_res.get();
2653 add_child_to_back(std::move(ch_res));
2654
2655 settings.buttons_size = settings.vertical ? left->base_data.size.y : left->base_data.size.x;
2656 if(step_size::twenty_five == step)
2657 left->step_size = 25;
2658 else if(step_size::two == step)
2659 left->step_size = 2;
2660 else
2661 left->step_size = 1;
2662 }
2663 {
2664 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(1 + first_child.index()));
2665 auto ch_res = make_element_by_type<scrollbar_right>(state, child_tag);
2666 right = ch_res.get();
2667 add_child_to_back(std::move(ch_res));
2668
2669 if(step_size::twenty_five == step)
2670 right->step_size = 25;
2671 else if(step_size::two == step)
2672 right->step_size = 2;
2673 else
2674 right->step_size = 1;
2675 }
2676 {
2677 auto child_tag = dcon::gui_def_id(dcon::gui_def_id::value_base_t(3 + first_child.index()));
2678 auto ch_res = make_element_by_type<scrollbar_track>(state, child_tag);
2679 track = ch_res.get();
2680 add_child_to_back(std::move(ch_res));
2681
2682 settings.track_size = settings.vertical ? track->base_data.size.y : track->base_data.size.x;
2683 }
2684 left->base_data.position.x = 0;
2685 left->base_data.position.y = 0;
2686 if(settings.vertical) {
2687 track->base_data.position.y = int16_t(settings.buttons_size);
2688 slider->base_data.position.y = int16_t(settings.buttons_size);
2689 right->base_data.position.y = int16_t(settings.track_size + settings.buttons_size);
2690 // track->base_data.position.x = 0;
2691 slider->base_data.position.x = 0;
2692 right->base_data.position.x = 0;
2693 } else {
2694 track->base_data.position.x = int16_t(settings.buttons_size);
2695 slider->base_data.position.x = int16_t(settings.buttons_size);
2696 right->base_data.position.x = int16_t(settings.track_size + settings.buttons_size);
2697 // track->base_data.position.y = 0;
2698 slider->base_data.position.y = 0;
2699 right->base_data.position.y = 0;
2700 }
2701 }
2702 }
2703}
2704void scrollbar_slider::on_drag_finish(sys::state& state) noexcept {
2705 if(parent) {
2706 parent->impl_on_drag_finish(state);
2707 }
2708}
2709message_result scrollbar::get(sys::state& state, Cyto::Any& payload) noexcept {
2710 if(payload.holds_type<scrollbar_settings>()) {
2711 settings.track_size = track ? int32_t(settings.vertical ? track->base_data.size.y : track->base_data.size.x) : 1;
2712 payload = settings;
2713 return message_result::consumed;
2714 } else if(payload.holds_type<value_change>()) {
2715 auto adjustments = any_cast<value_change>(payload);
2716
2717 if(adjustments.is_relative)
2718 stored_value += adjustments.new_value;
2719 else
2720 stored_value = adjustments.new_value;
2721
2722 if(adjustments.move_slider) {
2723 update_raw_value(state, stored_value);
2724 } else {
2725 stored_value = settings.using_limits ? std::clamp(stored_value, settings.lower_limit, settings.upper_limit)
2726 : std::clamp(stored_value, settings.lower_value, settings.upper_value);
2727 }
2728
2729 if(adjustments.move_slider == true && adjustments.is_relative == false && !state.ui_state.drag_target) { // track click
2730 state.ui_state.drag_target = slider;
2731 }
2732
2733 on_value_change(state, stored_value);
2734 return message_result::unseen;
2735 } else {
2736 return message_result::unseen;
2737 }
2738}
2739
2740void unit_frame_bg::update_tooltip(sys::state& state, int32_t x, int32_t y, text::columnar_layout& contents) noexcept {
2741 auto display_unit = retrieve< unit_var>(state, parent);
2742 if(std::holds_alternative<dcon::army_id>(display_unit))
2743 single_unit_tooltip(state, contents, std::get<dcon::army_id>(display_unit));
2744 else if(std::holds_alternative<dcon::navy_id>(display_unit))
2745 single_unit_tooltip(state, contents, std::get<dcon::navy_id>(display_unit));
2746 text::add_line(state, contents, "unit_controls_tooltip_1");
2748 text::add_line(state, contents, "unit_controls_tooltip_2");
2749 text::add_line(state, contents, "unit_control_group_tooltip");
2750}
2751
2753 if(elm.base_data.get_element_type() != ui::element_type::button)
2754 return;
2755 if(elm.base_data.data.button.shortcut == sys::virtual_key::NONE)
2756 return;
2757 static const std::string_view key_names[] = { //enum class virtual_key : uint8_t {
2758 "", //NONE = 0x00,
2759 "Left-Button", //LBUTTON = 0x01,
2760 "Right-Button", //RBUTTON = 0x02,
2761 "Cancel", //CANCEL = 0x03,
2762 "Multimedia button", //MBUTTON = 0x04,
2763 "XButton1", //XBUTTON_1 = 0x05,
2764 "XButton2", //XBUTTON_2 = 0x06,
2765 "", //0x07
2766 "Backspace", //BACK = 0x08,
2767 "TAB", //TAB = 0x09,
2768 "", // 0x0A
2769 "", // 0x0B
2770 "Clear", //CLEAR = 0x0C,
2771 "Return", //RETURN = 0x0D,
2772 "", // 0x0E
2773 "", // 0x0F
2774 "Shift", //SHIFT = 0x10,
2775 "Control", //CONTROL = 0x11,
2776 "Menu", //MENU = 0x12,
2777 "Pause", //PAUSE = 0x13,
2778 "Capital", //CAPITAL = 0x14,
2779 "Kana", //KANA = 0x15,
2780 "", // 0x16
2781 "Junja", //JUNJA = 0x17,
2782 "Final", //FINAL = 0x18,
2783 "Kanji", //KANJI = 0x19,
2784 "", // 0x1A
2785 "Escape", //ESCAPE = 0x1B,
2786 "Convert", //CONVERT = 0x1C,
2787 "Nonconvert", //NONCONVERT = 0x1D,
2788 "Accept", //ACCEPT = 0x1E,
2789 "Modechange", //MODECHANGE = 0x1F,
2790 "Spacebar", //SPACE = 0x20,
2791 "Prior", //PRIOR = 0x21,
2792 "Next", //NEXT = 0x22,
2793 "End", //END = 0x23,
2794 "Home", //HOME = 0x24,
2795 "Left", //LEFT = 0x25,
2796 "Up", //UP = 0x26,
2797 "Right", //RIGHT = 0x27,
2798 "Down", //DOWN = 0x28,
2799 "Select", //SELECT = 0x29,
2800 "Print", //PRINT = 0x2A,
2801 "Execute", //EXECUTE = 0x2B,
2802 "Snapshot", //SNAPSHOT = 0x2C,
2803 "Insert", //INSERT = 0x2D,
2804 "Delete", //DELETE_KEY = 0x2E,
2805 "Help", //HELP = 0x2F,
2806 "0", //NUM_0 = 0x30,
2807 "1", //NUM_1 = 0x31,
2808 "2", //NUM_2 = 0x32,
2809 "3", //NUM_3 = 0x33,
2810 "4", //NUM_4 = 0x34,
2811 "5", //NUM_5 = 0x35,
2812 "6", //NUM_6 = 0x36,
2813 "7", //NUM_7 = 0x37,
2814 "8", //NUM_8 = 0x38,
2815 "9", //NUM_9 = 0x39,
2816 "", // 0x3A
2817 "", // 0x3B
2818 "", // 0x3C
2819 "", // 0x3D
2820 "", // 0x3E
2821 "", // 0x3F
2822 "", // 0x40
2823 "A", //A = 0x41,
2824 "B", //B = 0x42,
2825 "C", //C = 0x43,
2826 "D", //D = 0x44,
2827 "E", //E = 0x45,
2828 "F", //F = 0x46,
2829 "G", //G = 0x47,
2830 "H", //H = 0x48,
2831 "I", //I = 0x49,
2832 "J", //J = 0x4A,
2833 "K", //K = 0x4B,
2834 "L", //L = 0x4C,
2835 "M", //M = 0x4D,
2836 "N", //N = 0x4E,
2837 "O", //O = 0x4F,
2838 "P", //P = 0x50,
2839 "Q", //Q = 0x51,
2840 "R", //R = 0x52,
2841 "S", //S = 0x53,
2842 "T", //T = 0x54,
2843 "U", //U = 0x55,
2844 "V", //V = 0x56,
2845 "W", //W = 0x57,
2846 "X", //X = 0x58,
2847 "Y", //Y = 0x59,
2848 "Z", //Z = 0x5A,
2849 "Left Windows", //LWIN = 0x5B,
2850 "Right Windows", //RWIN = 0x5C,
2851 "Apps", //APPS = 0x5D,
2852 "", // 0x5E
2853 "Sleep", //SLEEP = 0x5F,
2854 "Numpad 0", //NUMPAD0 = 0x60,
2855 "Numpad 1", //NUMPAD1 = 0x61,
2856 "Numpad 2", //NUMPAD2 = 0x62,
2857 "Numpad 3", //NUMPAD3 = 0x63,
2858 "Numpad 4", //NUMPAD4 = 0x64,
2859 "Numpad 5", //NUMPAD5 = 0x65,
2860 "Numpad 6", //NUMPAD6 = 0x66,
2861 "Numpad 7", //NUMPAD7 = 0x67,
2862 "Numpad 8", //NUMPAD8 = 0x68,
2863 "Numpad 9", //NUMPAD9 = 0x69,
2864 "Numpad *", //MULTIPLY = 0x6A,
2865 "Numpad +", //ADD = 0x6B,
2866 "Numpad .", //SEPARATOR = 0x6C,
2867 "Numpad -", //SUBTRACT = 0x6D,
2868 "Numpad .", //DECIMAL = 0x6E,
2869 "Numpad /", //DIVIDE = 0x6F,
2870 "F1", //F1 = 0x70,
2871 "F2", //F2 = 0x71,
2872 "F3", //F3 = 0x72,
2873 "F4", //F4 = 0x73,
2874 "F5", //F5 = 0x74,
2875 "F6", //F6 = 0x75,
2876 "F7", //F7 = 0x76,
2877 "F8", //F8 = 0x77,
2878 "F9", //F9 = 0x78,
2879 "F10", //F10 = 0x79,
2880 "F11", //F11 = 0x7A,
2881 "F12", //F12 = 0x7B,
2882 "F13", //F13 = 0x7C,
2883 "F14", //F14 = 0x7D,
2884 "F15", //F15 = 0x7E,
2885 "F16", //F16 = 0x7F,
2886 "F17", //F17 = 0x80,
2887 "F18", //F18 = 0x81,
2888 "F19", //F19 = 0x82,
2889 "F20", //F20 = 0x83,
2890 "F21", //F21 = 0x84,
2891 "F22", //F22 = 0x85,
2892 "F23", //F23 = 0x86,
2893 "F24", //F24 = 0x87,
2894 "Navigation View", //NAVIGATION_VIEW = 0x88,
2895 "Navigation Menu", //NAVIGATION_MENU = 0x89,
2896 "Navigation Up", //NAVIGATION_UP = 0x8A,
2897 "Navigation Down", //NAVIGATION_DOWN = 0x8B,
2898 "Navigation Left", //NAVIGATION_LEFT = 0x8C,
2899 "Navigation Right", //NAVIGATION_RIGHT = 0x8D,
2900 "Navigation Accept", //NAVIGATION_ACCEPT = 0x8E,
2901 "Navigation Cancel", //NAVIGATION_CANCEL = 0x8F,
2902 "Numlock", //NUMLOCK = 0x90,
2903 "Scroll lock", //SCROLL = 0x91,
2904 "=", //OEM_NEC_EQUAL = 0x92,
2905 "", // 0x93
2906 "", // 0x94
2907 "", // 0x95
2908 "", // 0x96
2909 "", // 0x97
2910 "", // 0x98
2911 "", // 0x99
2912 "", // 0x9A
2913 "", // 0x9B
2914 "", // 0x9C
2915 "", // 0x9D
2916 "", // 0x9E
2917 "", // 0x9F
2918 "Left Shift", //LSHIFT = 0xA0,
2919 "Right Shift", //RSHIFT = 0xA1,
2920 "Left Control", //LCONTROL = 0xA2,
2921 "Right Control", //RCONTROL = 0xA3,
2922 "Left Menu", //LMENU = 0xA4,
2923 "Right Menu", //RMENU = 0xA5,
2924 "", // 0xA6
2925 "", // 0xA7
2926 "", // 0xA8
2927 "", // 0xA9
2928 "", // 0xAA
2929 "", // 0xAB
2930 "", // 0xAC
2931 "", // 0xAD
2932 "", // 0xAE
2933 "", // 0xAF
2934 "", // 0xB0
2935 "", // 0xB1
2936 "", // 0xB2
2937 "", // 0xB3
2938 "", // 0xB4
2939 "", // 0xB5
2940 "", // 0xB6
2941 "", // 0xB7
2942 "", // 0xB8
2943 "", // 0xB9
2944 ";", //SEMICOLON = 0xBA,
2945 "+", //PLUS = 0xBB,
2946 ",", //COMMA = 0xBC,
2947 "-", //MINUS = 0xBD,
2948 ".", //PERIOD = 0xBE,
2949 "\\", //FORWARD_SLASH = 0xBF,
2950 "~", //TILDA = 0xC0,
2951 "", // 0xC1
2952 "", // 0xC2
2953 "", // 0xC3
2954 "", // 0xC4
2955 "", // 0xC5
2956 "", // 0xC6
2957 "", // 0xC7
2958 "", // 0xC8
2959 "", // 0xC9
2960 "", // 0xCA
2961 "", // 0xCB
2962 "", // 0xCC
2963 "", // 0xCD
2964 "", // 0xCE
2965 "", // 0xCF
2966 "[", //OPEN_BRACKET = 0xDB,
2967 "/", //BACK_SLASH = 0xDC,
2968 "]", //CLOSED_BRACKET = 0xDD,
2969 "\"", //QUOTE = 0xDE
2970 };
2971 text::add_line(state, contents, "shortcut_tooltip", text::variable_type::x, key_names[uint8_t(elm.base_data.data.button.shortcut)]);
2972}
2973
2974} // namespace ui
ANY_ALWAYS_INLINE bool holds_type() const noexcept
Definition: cyto_any.hpp:429
void set_button_text(sys::state &state, std::string const &new_text)
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_create(sys::state &state) noexcept override
void format_text(sys::state &state)
void on_reset_text(sys::state &state) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void move_child_to_back(element_base *child) noexcept final
void impl_render(sys::state &state, int32_t x, int32_t y) noexcept override
message_result impl_set(sys::state &state, Cyto::Any &payload) noexcept final
void add_child_to_back(std::unique_ptr< element_base > child) noexcept final
std::unique_ptr< element_base > remove_child(element_base *child) noexcept final
void impl_on_reset_text(sys::state &state) noexcept override
void add_child_to_front(std::unique_ptr< element_base > child) noexcept final
mouse_probe impl_probe_mouse(sys::state &state, int32_t x, int32_t y, mouse_probe_type type) noexcept override
void move_child_to_front(element_base *child) noexcept final
element_base * get_child_by_name(sys::state const &state, std::string_view name) noexcept final
message_result impl_on_key_down(sys::state &state, sys::virtual_key key, sys::key_modifiers mods) noexcept final
void impl_on_update(sys::state &state) noexcept override
element_base * get_child_by_index(sys::state const &state, int32_t index) noexcept final
static constexpr dcon::texture_id small_tiles_dialog
static constexpr dcon::texture_id tiles_dialog
static constexpr dcon::texture_id transparency
void on_update(sys::state &state) noexcept override
void on_create(sys::state &state) noexcept override
message_result on_key_down(sys::state &state, sys::virtual_key key, sys::key_modifiers mods) noexcept override
void on_reset_text(sys::state &state) noexcept override
message_result on_lbutton_down(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
static constexpr uint8_t is_invisible_mask
virtual message_result get(sys::state &state, Cyto::Any &payload) noexcept
static constexpr uint8_t wants_update_when_hidden_mask
virtual void impl_render(sys::state &state, int32_t x, int32_t y) noexcept
bool is_visible() const
virtual message_result impl_on_key_down(sys::state &state, sys::virtual_key key, sys::key_modifiers mods) noexcept
virtual void on_create(sys::state &state) noexcept
element_data base_data
void set_visible(sys::state &state, bool vis)
virtual mouse_probe impl_probe_mouse(sys::state &state, int32_t x, int32_t y, mouse_probe_type type) noexcept
friend void sys::state::on_text(char32_t c)
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_create(sys::state &state) noexcept override
void on_create(sys::state &state) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void set_data_points(sys::state &state, std::vector< float > const &datapoints) noexcept
void on_reset_text(sys::state &state) noexcept override
void on_create(sys::state &state) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
message_result on_lbutton_down(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override
message_result on_rbutton_down(sys::state &state, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override
void on_create(sys::state &state) noexcept override
message_result test_mouse(sys::state &state, int32_t x, int32_t y, mouse_probe_type type) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_reset_text(sys::state &state) noexcept override
message_result impl_set(sys::state &state, Cyto::Any &payload) noexcept final
message_result impl_on_key_down(sys::state &state, sys::virtual_key key, sys::key_modifiers mods) noexcept final
void impl_on_reset_text(sys::state &state) noexcept override
mouse_probe impl_probe_mouse(sys::state &state, int32_t x, int32_t y, mouse_probe_type type) noexcept override
void move_child_to_front(element_base *child) noexcept final
element_base * get_child_by_index(sys::state const &state, int32_t index) noexcept final
void move_child_to_back(element_base *child) noexcept final
void impl_render(sys::state &state, int32_t x, int32_t y) noexcept override
void impl_on_update(sys::state &state) noexcept override
void set_current_nation(sys::state &state, dcon::national_identity_id identity) noexcept override
void upate_truce(sys::state &state, dcon::nation_id i, sys::date u)
virtual void populate_tooltip(sys::state &state, T t, float percentage, text::columnar_layout &contents) noexcept
void update_chart(sys::state &state)
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_create(sys::state &state) noexcept override
void update_tooltip(sys::state &state, int32_t x, int32_t y, text::columnar_layout &contents) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_create(sys::state &state) noexcept override
void on_reset_text(sys::state &state) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void set_text(sys::state &state, std::string const &new_text)
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_create(sys::state &state) noexcept override
void set_text(sys::state &state, std::string const &new_text)
void on_reset_text(sys::state &state) noexcept override
void format_text(sys::state &state)
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void render(sys::state &state, int32_t x, int32_t y) noexcept override
void on_create(sys::state &state) noexcept override
void on_drag(sys::state &state, int32_t oldx, int32_t oldy, int32_t x, int32_t y, sys::key_modifiers mods) noexcept override
#define assert(condition)
Definition: debug.h:74
void use_nation_button(sys::state &state, dcon::nation_id source, dcon::gui_def_id d, dcon::nation_id n)
Definition: commands.cpp:4832
bool can_use_province_button(sys::state &state, dcon::nation_id source, dcon::gui_def_id d, dcon::province_id p)
Definition: commands.cpp:4816
void use_province_button(sys::state &state, dcon::nation_id source, dcon::gui_def_id d, dcon::province_id i)
Definition: commands.cpp:4807
bool can_use_nation_button(sys::state &state, dcon::nation_id source, dcon::gui_def_id d, dcon::nation_id n)
Definition: commands.cpp:4841
flag_type get_current_flag_type(sys::state const &state, dcon::nation_id target_nation)
Definition: culture.cpp:782
pop_satisfaction_wrapper_fat fatten(data_container const &c, pop_satisfaction_wrapper_id id) noexcept
constexpr float zoom_very_close
Definition: constants.hpp:606
bool are_at_war(sys::state const &state, dcon::nation_id a, dcon::nation_id b)
Definition: military.cpp:649
constexpr uint8_t level_friendly
Definition: nations.hpp:172
constexpr uint8_t level_in_sphere
Definition: nations.hpp:173
constexpr uint8_t level_mask
Definition: nations.hpp:167
constexpr uint8_t level_cordial
Definition: nations.hpp:171
dcon::nation_id get_relationship_partner(sys::state const &state, dcon::diplomatic_relation_id rel_id, dcon::nation_id query)
Definition: nations.cpp:694
dcon::text_key name_from_tag(sys::state &state, dcon::national_identity_id tag)
Definition: nations.cpp:710
void render_tinted_subsprite(sys::state const &state, int frame, int total_frames, float x, float y, float width, float height, float r, float g, float b, GLuint texture_handle, ui::rotation rot, bool flipped, bool rtl)
color_modification
void render_bordered_rect(sys::state const &state, color_modification enabled, float border_size, float x, float y, float width, float height, GLuint texture_handle, ui::rotation r, bool flipped, bool rtl)
void render_piechart(sys::state const &state, color_modification enabled, float x, float y, float size, data_texture &t)
void render_masked_rect(sys::state const &state, color_modification enabled, float x, float y, float width, float height, GLuint texture_handle, GLuint mask_texture_handle, ui::rotation r, bool flipped, bool rtl)
GLuint get_rebel_flag_handle(sys::state &state, dcon::rebel_faction_id faction)
Definition: texture.cpp:1010
void render_text_commodity_icon(sys::state &state, text::embedded_commodity_icon ico, float x, float baseline_y, float font_size, text::font &f)
void render_subsprite(sys::state const &state, color_modification enabled, int frame, int total_frames, float x, float y, float width, float height, GLuint texture_handle, ui::rotation r, bool flipped, bool rtl)
void render_text(sys::state &state, text::stored_glyphs const &txt, color_modification enabled, float x, float y, color3f const &c, uint16_t font_id)
void render_linegraph(sys::state const &state, color_modification enabled, float x, float y, float width, float height, lines &l)
void render_text_flag(sys::state &state, text::embedded_flag ico, float x, float baseline_y, float font_size, text::font &f, ogl::color_modification cmod)
void render_textured_rect(sys::state const &state, color_modification enabled, float x, float y, float width, float height, GLuint texture_handle, ui::rotation r, bool flipped, bool rtl)
GLuint get_texture_handle(sys::state &state, dcon::texture_id id, bool keep_data)
Definition: texture.cpp:1066
void render_text_icon(sys::state &state, text::embedded_icon ico, float x, float baseline_y, float font_size, text::font &f, ogl::color_modification cmod)
void render_tinted_textured_rect(sys::state const &state, float x, float y, float width, float height, float r, float g, float b, GLuint texture_handle, ui::rotation rot, bool flipped, bool rtl)
void render_text_unit_icon(sys::state &state, text::embedded_unit_icon ico, float x, float baseline_y, float font_size, text::font &f, ogl::color_modification cmod)
void render_progress_bar(sys::state const &state, color_modification enabled, float progress, float x, float y, float width, float height, GLuint left_texture_handle, GLuint right_texture_handle, ui::rotation r, bool flipped, bool rtl)
GLuint get_flag_handle(sys::state &state, dcon::national_identity_id nat_id, culture::flag_type type)
Definition: texture.cpp:977
std::string lowercase_str(std::string_view sv)
std::string rebel_name(sys::state &state, dcon::rebel_faction_id reb)
Definition: rebels.cpp:1276
std::string native_to_utf8(native_string_view data_in)
void play_interface_sound(sys::state &state, audio_instance &s, float volume)
Definition: sound_nix.cpp:203
audio_instance & get_click_sound(sys::state &state)
Definition: sound_nix.cpp:263
float blue_from_int(uint32_t v)
virtual_key
Definition: constants.hpp:5
float green_from_int(uint32_t v)
uint32_t pack_color(float r, float g, float b)
key_modifiers
Definition: constants.hpp:156
float red_from_int(uint32_t v)
Definition: bmfont.cpp:118
void add_to_layout_box(sys::state &state, layout_base &dest, layout_box &box, embedded_flag ico)
Definition: text.cpp:1165
int32_t size_from_font_id(uint16_t id)
Definition: fonts.cpp:119
layout_box open_layout_box(layout_base &dest, int32_t indent)
Definition: text.cpp:1823
bool is_black_from_font_id(uint16_t id)
Definition: fonts.cpp:127
void add_line(sys::state &state, layout_base &dest, dcon::text_key txt, int32_t indent)
Definition: text.cpp:1923
void add_line_break_to_layout(sys::state &state, columnar_layout &dest)
Definition: text.cpp:1152
text::alignment to_text_alignment(ui::alignment in)
Definition: text.cpp:1228
ankerl::unordered_dense::map< uint32_t, substitution > substitution_map
Definition: text.hpp:797
std::string produce_simple_string(sys::state const &state, dcon::text_key id)
Definition: text.cpp:617
text_color
Definition: text.hpp:18
dcon::text_key get_name(sys::state &state, dcon::nation_id id)
Definition: text.cpp:880
font_selection font_index_from_font_id(sys::state &state, uint16_t id)
Definition: fonts.cpp:130
std::string format_percentage(float num, size_t digits)
Definition: text.cpp:977
void add_space_to_layout_box(sys::state &state, layout_base &dest, layout_box &box)
Definition: text.cpp:1812
void close_layout_box(columnar_layout &dest, layout_box &box)
Definition: text.cpp:1831
int32_t to_generic(dcon::province_id v)
Definition: triggers.hpp:12
int32_t ui_height(sys::state const &state)
void make_size_from_graphics(sys::state &state, ui::element_data &dat)
xy_pair child_relative_location(sys::state &state, element_base const &parent, element_base const &child)
uint32_t internal_get_interactable_disabled_color(float r, float g, float b)
uint32_t internal_get_disabled_color(float r, float g, float b)
void effect_description(sys::state &state, text::layout_base &layout, dcon::effect_key k, int32_t primary_slot, int32_t this_slot, int32_t from_slot, uint32_t r_lo, uint32_t r_hi)
int32_t ui_width(sys::state const &state)
void render_text_chunk(sys::state &state, text::text_chunk t, float x, float baseline_y, uint16_t font_id, ogl::color3f text_color, ogl::color_modification cmod)
void populate_shortcut_tooltip(sys::state &state, ui::element_base &elm, text::columnar_layout &contents) noexcept
xy_pair get_absolute_location(sys::state &state, element_base const &node)
tooltip_behavior
void send(sys::state &state, element_base *parent, T value)
void trigger_description(sys::state &state, text::layout_base &layout, dcon::trigger_key k, int32_t primary_slot=-1, int32_t this_slot=-1, int32_t from_slot=-1)
ogl::color3f get_text_color(sys::state &state, text::text_color text_color)
@ count
Definition: gui_event.hpp:126
message_result
std::unique_ptr< element_base > make_element(sys::state &state, std::string_view name)
ogl::color_modification get_color_modification(bool is_under_mouse, bool is_disabled, bool is_interactable)
std::unique_ptr< element_base > make_element_immediate(sys::state &state, dcon::gui_def_id id)
uint32_t internal_get_interactable_color(float r, float g, float b)
message_result greater_result(message_result a, message_result b)
void single_unit_tooltip(sys::state &state, text::columnar_layout &contents, dcon::army_id a)
uint uint32_t
uchar uint8_t
@ ident
Holds important data about the game world, state, and other data regarding windowing,...
std::vector< text_chunk > contents
Definition: text.hpp:821
int32_t number_of_lines
Definition: text.hpp:822
substitution source
Definition: text.hpp:802
text::stored_glyphs unicodechars
Definition: text.hpp:800
dcon::gfx_object_id button_image
element_type get_element_type() const
static constexpr uint8_t orientation_mask
union ui::element_data::internal_data data
dcon::gfx_object_id gfx_object
listbox2_row_element * row
std::unique_ptr< element_base > army_group_selector_root
element_base * last_tooltip
std::unique_ptr< element_base > province_details_root
std::unique_ptr< tool_tip > tooltip
std::unique_ptr< element_base > rgos_root
ankerl::unordered_dense::map< dcon::text_key, element_target, hash_text_key > defs_by_name
std::unique_ptr< element_base > root
element_base * edit_target
element_base * province_window
uint16_t tooltip_font
std::unique_ptr< element_base > military_root
element_base * drag_target
element_base * under_mouse
std::unique_ptr< element_base > units_root
alignment get_alignment() const
xy_pair border_size
dcon::nation_id n