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