1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: MIT |
3 | |
4 | #include "dashboard.h" |
5 | |
6 | #include <fmt/core.h> |
7 | |
8 | void Widget::set_property(std::string_view name, const slint::interpreter::Value &value) |
9 | { |
10 | if (m_ui) |
11 | (*m_ui)->set_property(name: qualified_property_name(name), value); |
12 | } |
13 | |
14 | std::optional<slint::interpreter::Value> Widget::property(std::string_view name) const |
15 | { |
16 | if (m_ui) |
17 | return (*m_ui)->get_property(name: qualified_property_name(name)); |
18 | return {}; |
19 | } |
20 | |
21 | void Widget::connect_ui(const slint::ComponentHandle<slint::interpreter::ComponentInstance> &ui, |
22 | std::string_view properties_prefix) |
23 | { |
24 | m_ui = ui; |
25 | m_properties_prefix = properties_prefix; |
26 | } |
27 | |
28 | std::string Widget::qualified_property_name(std::string_view name) const |
29 | { |
30 | std::string qname(m_properties_prefix); |
31 | qname += name; |
32 | return qname; |
33 | } |
34 | |
35 | std::string WidgetLocation::location_bindings() const |
36 | { |
37 | auto maybe_binding = [](std::string_view name, const auto &opt_value) -> std::string { |
38 | if (opt_value.has_value()) { |
39 | return fmt::format(" {}: {};\n" , name, *opt_value); |
40 | } else { |
41 | return "" ; |
42 | } |
43 | }; |
44 | |
45 | return fmt::format( |
46 | R"slint(row: {}; |
47 | col: {}; |
48 | {}{})slint" , |
49 | row, column, maybe_binding("rowspan" , row_span), maybe_binding("colspan" , col_span)); |
50 | } |
51 | |
52 | void DashboardBuilder::add_grid_widget(WidgetPtr widget, const WidgetLocation &location) |
53 | { |
54 | auto widget_id = register_widget(widget); |
55 | grid_widgets.push_back(x: { widget_id, location }); |
56 | } |
57 | |
58 | void DashboardBuilder::add_top_bar_widget(WidgetPtr widget) |
59 | { |
60 | auto widget_id = register_widget(widget); |
61 | top_bar_widgets.push_back(x: widget_id); |
62 | } |
63 | |
64 | int DashboardBuilder::register_widget(WidgetPtr widget) |
65 | { |
66 | auto widget_type_name = widget->type_name(); |
67 | widgets_used.insert(x: widget_type_name); |
68 | |
69 | auto widget_id = int(widgets.size()); |
70 | auto widget_name = fmt::format("widget_{}" , widget_id); |
71 | widgets.push_back({ widget_name, widget }); |
72 | return widget_id; |
73 | } |
74 | |
75 | std::optional<slint::ComponentHandle<slint::interpreter::ComponentInstance>> |
76 | DashboardBuilder::build(slint::interpreter::ComponentCompiler &compiler) const |
77 | { |
78 | std::string widget_imports; |
79 | |
80 | for (const auto &widget : widgets_used) { |
81 | if (widget_imports.size() > 0) { |
82 | widget_imports.append(s: ", " ); |
83 | } |
84 | widget_imports.append(str: widget); |
85 | } |
86 | |
87 | if (widget_imports.size() > 0) { |
88 | widget_imports = |
89 | fmt::format("import {{ {} }} from \"iot-dashboard.slint\";" , widget_imports); |
90 | } |
91 | |
92 | // Vector of name/type_name of properties forwarded through the MainContent {} element. |
93 | std::string main_content_properties; |
94 | std::string main_grid; |
95 | std::string top_bar; |
96 | std::string exposed_properties; |
97 | |
98 | for (const auto &[widget_id, location] : grid_widgets) { |
99 | const auto &[widget_name, widget_ptr] = widgets[widget_id]; |
100 | |
101 | main_grid.append(fmt::format( |
102 | R"slint( |
103 | {0} := {1} {{ |
104 | {2} |
105 | }} |
106 | )slint" , |
107 | widget_name, widget_ptr->type_name(), location.location_bindings())); |
108 | |
109 | std::string properties_prefix = widget_name; |
110 | properties_prefix.append(s: "__" ); |
111 | |
112 | for (const auto &property : widget_ptr->properties()) { |
113 | std::string forwarded_property_name = properties_prefix; |
114 | forwarded_property_name.append(str: property.name); |
115 | |
116 | main_content_properties.append( |
117 | fmt::format(" in-out property <{0}> {1} <=> {2}.{3};\n" , property.type_name, |
118 | forwarded_property_name, widget_name, property.name)); |
119 | |
120 | exposed_properties.append( |
121 | fmt::format(" in-out property <{0}> {1} <=> main_content.{1};\n" , |
122 | property.type_name, forwarded_property_name)); |
123 | } |
124 | } |
125 | |
126 | for (const auto widget_id : top_bar_widgets) { |
127 | const auto &[widget_name, widget_ptr] = widgets[widget_id]; |
128 | |
129 | top_bar.append(fmt::format( |
130 | R"slint( |
131 | {0} := {1} {{ |
132 | }} |
133 | )slint" , |
134 | widget_name, widget_ptr->type_name())); |
135 | |
136 | std::string properties_prefix = widget_name; |
137 | properties_prefix.append(s: "__" ); |
138 | |
139 | for (const auto &property : widget_ptr->properties()) { |
140 | std::string forwarded_property_name = properties_prefix; |
141 | forwarded_property_name.append(str: property.name); |
142 | |
143 | exposed_properties.append(fmt::format(" in-out property <{0}> {1} <=> {2}.{3};\n" , |
144 | property.type_name, forwarded_property_name, |
145 | widget_name, property.name)); |
146 | } |
147 | } |
148 | |
149 | auto source_code = fmt::format( |
150 | R"slint( |
151 | |
152 | {0} |
153 | |
154 | component MainContent inherits VerticalLayout {{ |
155 | {4} |
156 | |
157 | spacing: 24px; |
158 | TopBar {{ |
159 | @children |
160 | }} |
161 | |
162 | GridLayout {{ |
163 | spacing: 6px; |
164 | padding-left: 19px; |
165 | padding-top: 0px; |
166 | padding-right: 17px; |
167 | padding-bottom: 24px; |
168 | |
169 | {2} |
170 | }} |
171 | }} |
172 | |
173 | export component MainWindow inherits Window {{ |
174 | title: "IOT dashboard"; |
175 | |
176 | {3} |
177 | |
178 | HorizontalLayout {{ |
179 | padding: 0; spacing: 0; |
180 | MenuBar {{ |
181 | }} |
182 | main_content := MainContent {{ |
183 | {1} |
184 | }} |
185 | }} |
186 | }} |
187 | )slint" , |
188 | widget_imports, top_bar, main_grid, exposed_properties, main_content_properties); |
189 | |
190 | auto definition = compiler.build_from_source(source_code, SOURCE_DIR); |
191 | |
192 | for (auto diagnostic : compiler.diagnostics()) { |
193 | std::cerr << (diagnostic.level == slint::interpreter::DiagnosticLevel::Warning ? "warning: " |
194 | : "error: " ) |
195 | << diagnostic.message << std::endl; |
196 | std::cerr << "location: " << diagnostic.source_file; |
197 | if (diagnostic.line > 0) |
198 | std::cerr << ":" << diagnostic.line; |
199 | if (diagnostic.column > 0) |
200 | std::cerr << ":" << diagnostic.column; |
201 | std::cerr << std::endl; |
202 | } |
203 | |
204 | if (!definition) { |
205 | std::cerr << "compilation failure!" << std::endl; |
206 | std::cerr << "generated source:" << std::endl << source_code << std::endl; |
207 | return {}; |
208 | } |
209 | |
210 | // std::cerr << source_code << std::endl; |
211 | |
212 | auto ui = definition->create(); |
213 | |
214 | for (const auto &entry : widgets) { |
215 | auto [widget_name, widget_ptr] = entry; |
216 | |
217 | std::string properties_prefix = widget_name; |
218 | properties_prefix += "__" ; |
219 | |
220 | widget_ptr->connect_ui(ui: ui, properties_prefix); |
221 | } |
222 | |
223 | return ui; |
224 | } |
225 | |