CYD-UI
A C++ library for building native graphic user interfaces
Loading...
Searching...
No Matches
component_updater.cppm
Go to the documentation of this file.
1// Copyright (c) 2024, Víctor Castillo Agüero.
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4module;
5#include <tracy/Tracy.hpp>
6#include "cyd_fabric_modules/headers/macros/async_events.h"
7
8export module cydui.components.updater;
9
10import std;
11import fabric.logging;
12import fabric.wiring.signals;
13
15import cydui.graphics;
16export import cydui.components.base;
18
19namespace cydui::components {
20 export class component_updater_t {
21 public:
22 using sptr = std::shared_ptr<component_updater_t>;
23
24 static sptr make() {
25 return std::make_shared<component_updater_t>();
26 }
27
28 fabric::wiring::output_signal<component_updater_t, const component_base_t::sptr&> queue_render_signal{};
29 fabric::wiring::output_signal<component_updater_t, const component_base_t::sptr&> apply_style_signal{};
30 fabric::wiring::output_signal<component_updater_t, const component_base_t::sptr&, StyleArchive&> compile_style_rules_signal{};
31 public:
32 void update(component_base_t::sptr component, StyleArchive& style_archive) {
33 ZoneScopedN("Update");
34 component->state()->_dirty = false;
35 queue_render_signal.emit(component);
36
37 // apply_style_signal.emit(component);
38
39 std::unordered_map<
40 std::shared_ptr<component_base_t>,
41 std::list<std::shared_ptr<component_base_t>>::iterator>
42 pending_remove { };
43 std::list<std::shared_ptr<component_base_t>> pending_redraw { };
44 for (auto it = component->children.begin(); it != component->children.end(); ++it) {
45 pending_remove.emplace(*it, it);
46 }
47
48 component_builder_t content_children_builder { }; {
49 std::vector<component_builder_t> &content_children = component->attrs()->_content;
50 for (auto &item: content_children) {
51 for (auto &component: item.get_component_constructors()) {
52 content_children_builder.append_component(component);
53 }
54 }
55 }
56
57
58 std::vector<component_holder_t> new_children = component->get_event_dispatcher()->update(style_archive, content_children_builder);
59
60 // Update handler may add to style override
61 component->get_style_data().apply_override();
62
63 add_children(component, new_children, pending_redraw, pending_remove, style_archive);
64
65 for (const auto &remove: pending_remove) {
66 dismount_child(component, remove.second);
67 }
68
69 for (const auto &child: pending_redraw) {
70 // Update children
71 update(child, style_archive);
72 }
73 }
74
75 private:
76 void add_children(
77 component_base_t::sptr component,
78 std::vector<component_holder_t> &children_to_add,
79 std::list<std::shared_ptr<component_base_t>> &pending_redraw,
80 std::unordered_map<
81 std::shared_ptr<component_base_t>,
82 std::list<std::shared_ptr<component_base_t> >::iterator> &pending_remove,
83 StyleArchive &style_archive
84 ) {
85 ZoneScopedN("Add Children");
86 std::optional<std::shared_ptr<component_base_t>> prev {std::nullopt};
87
88 // Keep track of used IDs just in case some are duplicated.
89 // The type is part of the ID, so if there is a mix up there won't be a SEGFAULT
90 std::unordered_map<std::string, std::size_t> used_ids{};
91
92 for (auto &item: children_to_add) {
93 for (const auto &child: item.get_components()) {
94 std::string name = child->name();
95 std::string _id = child->get_id();
96 std::string id = std::format("{}:{}", name, _id);
97
98 if (used_ids.contains(id)) {
99 id = std::format("{}[{}]", id, used_ids[id]++);
100 } else {
101 used_ids[id] = 1;
102 id = std::format("{}[0]", id);
103 }
104
105 auto mounted_child = mount_child(component, id, child, pending_redraw, pending_remove, style_archive);
106 // Configure dimensional context
107 anchors::configure_anchors(mounted_child, prev);
108
109 prev.reset();
110 prev.emplace(mounted_child);
111 }
112 }
113 }
114
115 std::shared_ptr<component_base_t> mount_child(
116 component_base_t::sptr component,
117 const std::string &id,
118 std::shared_ptr<component_base_t> child,
119 std::list<std::shared_ptr<component_base_t>> &pending_redraw,
120 std::unordered_map<
121 std::shared_ptr<component_base_t>,
122 std::list<std::shared_ptr<component_base_t>>::iterator> &pending_remove,
123 StyleArchive &style_archive
124 ) {
125 ZoneScopedN("Mount Children");
126 std::shared_ptr<component_base_t> mounted_child {child};
127 // Get or Create state for component
128 component_state_ref child_state;
129 if (component->state()->children_states.contains(id)) {
130 child_state = component->state()->children_states[id];
131 } else {
132 child_state = component_actor_t::create_state_instance(child.get());
133 component->state()->add_children_state(id, child_state);
134 }
135
136 if (child_state->component_instance.has_value()) {
137 pending_remove.erase(child_state->component_instance.value());
138 mounted_child = child_state->component_instance.value();
139
140 // Redraw child
141 if (component_actor_t::update_component_with(child_state->component_instance.value().get(), child)) {
142 pending_redraw.push_back(child_state->component_instance.value());
143 }
144 } else {
145 // Set child's variables
146 child->parent = component.get();
147 auto c_dims = child->get_dimensional_relations();
148 child->get_internal_relations().cx = component->get_internal_relations().cx + c_dims.x
149 + c_dims.margin_left + c_dims.padding_left;
150 child->get_internal_relations().cy = component->get_internal_relations().cy + c_dims.y
151 + c_dims.margin_top + c_dims.padding_top;
152
153 component_actor_t::set_component_state(child.get(), child_state);
154 compile_style_rules_signal.emit(child, style_archive);
155
156 child_state->component_instance = child;
157 component->children.push_back(child);
158
159 // Configure event handler
161
162 // Apply style
163 apply_style_signal.emit(child);
164
165 // Redraw child
166 pending_redraw.push_back(child);
167 }
168
169 return mounted_child;
170 }
171
172 void dismount_child(const component_base_t::sptr& component, const std::list<std::shared_ptr<component_base_t>>::iterator &child) {
173 ZoneScopedN("Unmount Children");
175 component->children.erase(child);
176 }
177
178 };
179}
std::shared_ptr< component_base_t > sptr
fabric::wiring::output_signal< component_updater_t, const component_base_t::sptr & > apply_style_signal
void update(component_base_t::sptr component, StyleArchive &style_archive)
fabric::wiring::output_signal< component_updater_t, const component_base_t::sptr &, StyleArchive & > compile_style_rules_signal
fabric::wiring::output_signal< component_updater_t, const component_base_t::sptr & > queue_render_signal
std::shared_ptr< component_updater_t > sptr
void configure_anchors(std::shared_ptr< component_base_t > child, std::optional< std::shared_ptr< component_base_t > > prev)
std::shared_ptr< component_state_t > component_state_ref
static void dismount_component(component_base_t *component)
static void set_component_state(component_base_t *component, component_state_ref state)
static void mount_component(component_base_t *component)
static bool update_component_with(component_base_t *component, std::shared_ptr< component_base_t > other)
static std::shared_ptr< component_state_t > create_state_instance(component_base_t *component)