neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
i_ui_element.hpp
Go to the documentation of this file.
1// i_ui_element.hpp
2/*
3neoGFX Resource Compiler
4Copyright(C) 2019 Leigh Johnston
5
6This program is free software: you can redistribute it and / or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21
22#include <neogfx/neogfx.hpp>
23#include <neolib/neolib.hpp>
29#include <neogfx/core/units.hpp>
30#include <neogfx/app/i_app.hpp>
35
36namespace neogfx::nrc
37{
39
42
43 inline constexpr bool is_widget_or_layout(ui_element_type aType)
44 {
45 return ((category(aType) & (ui_element_type::Widget | ui_element_type::Layout)) != ui_element_type::Invalid);
46 }
47
48 enum class widget_type : uint32_t
49 {
51 Child
52 };
53}
54
59
60namespace neogfx::nrc
61{
62 struct element_not_found : std::runtime_error { element_not_found(std::string const& aElement) : std::runtime_error{ "Element '" + aElement + "' not found." } {} };
63 struct element_ill_formed : std::runtime_error { element_ill_formed(std::string const& aElement) : std::runtime_error{ "Element '" + aElement + "' ill-formed." } {} };
64 struct unsupported_member_element : std::runtime_error { unsupported_member_element() : std::runtime_error{ "Unsupported member element." } {} };
65 struct unknown_data_type : std::runtime_error { unknown_data_type() : std::runtime_error{ "Unknown data type." } {} };
66
67 class i_ui_element : public neolib::i_reference_counted
68 {
69 public:
70 typedef i_ui_element abstract_type;
72 typedef i_ui_element_parser::data_t data_t;
73 typedef i_ui_element_parser::array_data_t array_data_t;
74 public:
75 struct no_parent : std::logic_error { no_parent() : std::logic_error{ "neogfx::nrc::i_ui_element::no_parent" } {} };
76 struct wrong_type : std::logic_error { wrong_type() : std::logic_error{ "neogfx::nrc::i_ui_element::wrong_type" } {} };
77 public:
78 virtual ~i_ui_element() = default;
79 public:
80 virtual const i_ui_element_parser& parser() const = 0;
81 public:
82 virtual const neolib::i_string& name() const = 0;
83 virtual const neolib::i_string& fragment_name() const = 0;
84 virtual const neolib::i_string& type_name() const = 0;
85 virtual void set_type_name(const neolib::i_string& aTypeName) = 0;
86 virtual const neolib::i_vector<neolib::i_string>& headers() const = 0;
87 public:
88 virtual bool is_member_element() const = 0;
89 virtual bool anonymous() const = 0;
90 virtual const neolib::i_string& id() const = 0;
91 virtual const neolib::i_string& anonymous_id() const = 0;
92 virtual void generate_anonymous_id(neolib::i_string& aNewAnonymousId) const = 0;
93 virtual ui_element_type type() const = 0;
94 public:
95 virtual const i_ui_element& fragment() const = 0;
96 virtual i_ui_element& fragment() = 0;
97 virtual bool has_parent() const = 0;
98 virtual const i_ui_element& parent() const = 0;
99 virtual i_ui_element& parent() = 0;
100 virtual const children_t& children() const = 0;
101 virtual children_t& children() = 0;
102 public:
103 virtual void parse(const neolib::i_string& aType, const data_t& aData) = 0;
104 virtual void parse(const neolib::i_string& aType, const array_data_t& aData) = 0;
105 virtual void add_element_ref(const neolib::i_string& aRef) = 0;
106 virtual const neolib::i_set<neolib::i_string>& element_refs() const = 0;
107 virtual const neolib::i_string& generate_ctor_params(bool aParamsAfter = false) const = 0;
108 virtual const neolib::i_string& generate_base_ctor_args(bool aArgsAfter = false) const = 0;
109 virtual void emit() const = 0;
110 virtual void emit_preamble() const = 0;
111 virtual void emit_ctor() const = 0;
112 virtual void emit_body() const = 0;
113 public:
114 virtual void instantiate(i_app& aApp) = 0;
115 virtual void instantiate(i_widget& aWidget) = 0;
116 virtual void instantiate(i_layout& aLayout) = 0;
117 public:
118 neolib::string generate_anonymous_id() const
119 {
120 neolib::string newAnonymousId;
121 generate_anonymous_id(newAnonymousId);
122 return newAnonymousId;
123 }
124 const i_ui_element& ancestor(const neolib::i_string& aId) const
125 {
126 if (!anonymous() && id() == aId)
127 return *this;
128 if (has_parent())
129 return parent().ancestor(aId);
130 throw element_not_found(aId.to_std_string());
131 }
132 i_ui_element& ancestor(const neolib::i_string& aId)
133 {
134 return const_cast<i_ui_element&>(to_const(*this).ancestor(aId));
135 }
136 void check_element_ref(const neolib::i_string& aRef) const
137 {
138 auto const& fullRef = aRef.to_std_string_view();
139 auto part = fullRef.find_first_of('.');
140 auto ref = fullRef.substr(0, part);
141 auto resolved = parser().find(neolib::string{ ref });
142 if (!resolved || (part == std::string::npos && resolved->fragment_name() != fragment_name()))
143 throw element_not_found(aRef.to_std_string());
144 }
145 public:
146 template <typename T>
147 T get_enum(const data_t& aData) const
148 {
149 return neolib::string_to_enum<T>(aData.get<neolib::i_string>());
150 }
151 template <typename T>
152 T get_enum(const array_data_t& aArrayData) const
153 {
154 bool ignore;
155 return get_enum<T>(aArrayData, ignore);
156 }
157 template <typename T>
158 T get_enum(const array_data_t& aArrayData, bool& aUseDefault, const std::optional<std::string>& aDefault = {}) const
159 {
160 T result = {};
161 aUseDefault = false;
162 bool first = true;
163 for (auto const& a : aArrayData)
164 {
165 auto const& e = a.get<neolib::i_string>();
166 if (aDefault && e == aDefault)
167 {
168 aUseDefault = true;
169 continue;
170 }
171 if (first)
172 result = neolib::string_to_enum<T>(e);
173 else
174 result = static_cast<T>(static_cast<std::underlying_type_t<T>>(result) | static_cast<std::underlying_type_t<T>>(neolib::string_to_enum<T>(e)));
175 first = false;
176 }
177 return result;
178 }
179#ifdef _MSC_VER // workaround for VS2022 compiler bug
180 template <typename T>
181 static T ffs1(auto&& v)
182 {
183 return T{ static_cast<typename T::value_type>(v) };
184 }
185 template <typename T>
186 static T ffs2(auto&& v)
187 {
188 return T::from_string(v);
189 }
190#endif
191 template <typename T>
192 T get_scalar(const data_t& aData) const
193 {
194 T result;
195 std::visit([&result, &aData](auto&& v)
196 {
197 typedef std::decay_t<decltype(v)> vt;
198 bool constexpr sourceIsBool = std::is_same_v<vt, bool>;
199 bool constexpr destIsBool = std::is_same_v<T, bool>;
200 bool constexpr sourceIsArithmetic = std::is_arithmetic_v<vt> && !sourceIsBool;
201 bool constexpr destIsArithmetic = std::is_arithmetic_v<T> && !destIsBool;
202 if constexpr (sourceIsArithmetic && destIsArithmetic)
203 result = static_cast<T>(v);
204 else if constexpr (sourceIsBool && destIsBool)
205 result = v;
206#ifndef _MSC_VER
207 else if constexpr (std::is_class_v<T> && sourceIsArithmetic)
208 result = T{ static_cast<typename T::value_type>(v) };
209 else if constexpr (std::is_class_v<T> && std::is_same_v<vt, neolib::i_string>)
210 result = T::from_string(v.to_std_string());
211#else // workaround for VS2022 compiler bug
212 else if constexpr (std::is_class_v<T> && sourceIsArithmetic)
213 result = ffs1<T>(v);
214 else if constexpr (std::is_class_v<T> && std::is_same_v<vt, neolib::i_string>)
215 result = ffs2<T>(v.to_std_string());
216#endif
217 else
218 throw wrong_type();
219 }, aData);
220 return result;
221 }
222 template <typename T>
223 std::vector<T> get_scalars(const array_data_t& aArrayData) const
224 {
225 std::vector<T> result;
226 for (auto const& e : aArrayData)
227 result.push_back(get_scalar<T>(e));
228 return result;
229 }
230 template <typename T>
231 T get_scalar(std::string const& aKey) const
232 {
233 return get_scalar<T>(parser().get_data(aKey));
234 }
235 template <typename T>
236 std::vector<T> get_scalars(std::string const& aKey) const
237 {
238 return get_scalars<T>(parser().get_array_data(aKey));
239 }
240 template <typename T, typename Target>
241 void emplace_2(const neolib::string& aKey, Target& aTarget) const
242 {
243 std::vector<T> scalars;
244 if (parser().data_exists(aKey))
245 scalars.push_back(get_scalar<T>(parser().get_data(aKey)));
246 else if (parser().array_data_exists(aKey))
247 scalars = get_scalars<T>(parser().get_array_data(aKey));
248 else
249 return;
250 switch (scalars.size())
251 {
252 case 1:
253 aTarget.emplace(scalars[0]);
254 break;
255 case 2:
256 aTarget.emplace(scalars[0], scalars[1]);
257 break;
258 default:
259 throw element_ill_formed(id().to_std_string());
260 }
261 }
262 template <typename T, typename Target>
263 void emplace_4(const neolib::string& aKey, Target& aTarget) const
264 {
265 std::vector<T> scalars;
266 if (parser().data_exists(aKey))
267 scalars.push_back(get_scalar<T>(parser().get_data(aKey)));
268 else if (parser().array_data_exists(aKey))
269 scalars = get_scalars<T>(parser().get_array_data(aKey));
270 else
271 return;
272 switch (scalars.size())
273 {
274 case 1:
275 aTarget.emplace(scalars[0]);
276 break;
277 case 2:
278 aTarget.emplace(scalars[0], scalars[1]);
279 break;
280 case 4:
281 aTarget.emplace(scalars[0], scalars[1], scalars[2], scalars[3]);
282 break;
283 default:
284 throw element_ill_formed(id().to_std_string());
285 }
286 }
287 color get_color(const data_t& aData) const
288 {
289 color result;
290 std::visit([&result](auto&& v)
291 {
292 typedef std::decay_t<decltype(v)> vt;
293 if constexpr (std::is_same_v<vt, int64_t>)
294 result = color{ static_cast<uint32_t>(v) };
295 else if constexpr (std::is_same_v<vt, neolib::i_string>)
296 result = v.to_std_string();
297 else
298 throw wrong_type();
299 }, aData);
300 return result;
301 }
302 color get_color(const array_data_t& aArrayData) const
303 {
304 if (aArrayData.size() == 3)
305 return color{ neolib::get_as<uint8_t>(aArrayData[0]), neolib::get_as<uint8_t>(aArrayData[1]), neolib::get_as<uint8_t>(aArrayData[2]) };
306 else if (aArrayData.size() == 4)
307 return color{ neolib::get_as<uint8_t>(aArrayData[0]), neolib::get_as<uint8_t>(aArrayData[1]), neolib::get_as<uint8_t>(aArrayData[2]), neolib::get_as<uint8_t>(aArrayData[3]) };
308 else
309 throw element_ill_formed(id().to_std_string());
310 }
311 color_or_gradient get_color_or_gradient(const array_data_t& aArrayData) const
312 {
313 if (aArrayData[0].which() == neolib::simple_variant_type::String)
314 {
315 auto gradientDirection = neolib::try_string_to_enum<gradient_direction>(aArrayData[0].get<neolib::i_string>().to_std_string());
316 if (gradientDirection != std::nullopt)
317 {
318 gradient::color_stop_list stops;
319 // todo: full gradient specification support
320 auto interval = 1.0 / (aArrayData.size() - 2);
321 for (std::size_t i = 1; i < aArrayData.size(); ++i)
322 stops.push_back(gradient::color_stop{ (i - 1) * interval, get_color(aArrayData[i]) });
323 return gradient{ stops, *gradientDirection };
324 }
325 else
326 return get_color(aArrayData);
327 }
328 else
329 return get_color(aArrayData);
330 }
331 protected:
332 template <typename Enum>
333 static std::string enum_to_string(std::string const& aEnumName, Enum aEnumValue)
334 {
335 auto es = neolib::enum_to_string(aEnumValue);
336 if (es[0] != '0')
337 return aEnumName + "::" + es;
338 else
339 return "static_cast<" + aEnumName + ">(" + es + ")";
340 }
341 template <typename T>
342 static const T& convert_emit_argument(const T& aArgument)
343 {
344 return aArgument;
345 }
346 static std::string convert_emit_argument(const bool& aArgument)
347 {
348 return aArgument ? "true" : "false";
349 }
350 static std::string convert_emit_argument(const neolib::i_string& aArgument)
351 {
352 std::string result;
353 for (auto const ch : aArgument)
354 {
355 switch (ch)
356 {
357 case '\b':
358 result += "\\b";
359 continue;
360 case '\f':
361 result += "\\f";
362 continue;
363 case '\n':
364 result += "\\n";
365 continue;
366 case '\r':
367 result += "\\r";
368 continue;
369 case '\t':
370 result += "\\t";
371 continue;
372 case '\\':
373 case '\"':
374 case '\'':
375 result += '\\';
376 break;
377 }
378 result += ch;
379 }
380 return result;
381 }
382 static std::string convert_emit_argument(const neolib::string& aArgument)
383 {
384 return convert_emit_argument(static_cast<const neolib::i_string&>(aArgument));
385 }
386 static std::string convert_emit_argument(std::string const& aArgument)
387 {
388 return convert_emit_argument(neolib::string{ aArgument });
389 }
390 static std::string convert_emit_argument(const length& aArgument)
391 {
392 return aArgument.to_string(true);
393 }
394 static std::string convert_emit_argument(const size_policy& aArgument)
395 {
396 std::string hp, vp;
397 aArgument.to_string(hp, vp);
398 if (hp == vp)
399 return "size_constraint::" + hp;
400 else
401 return "size_constraint::" + hp + ", size_constraint::" + vp;
402 }
403 static std::string convert_emit_argument(const color& aArgument)
404 {
405 std::ostringstream result;
406 result << "0x" << std::uppercase << std::hex << std::setfill('0') << std::setw(8) << aArgument.as_argb() << "u";
407 return result.str();
408 }
409 static std::string convert_emit_argument(const gradient& aArgument)
410 {
411 std::ostringstream result;
412 // todo: full gradient specification support
413 result << "gradient::color_stop_list{ ";
414 for (auto s = aArgument.color_stops().begin(); s != aArgument.color_stops().end(); ++s)
415 {
416 if (s != aArgument.color_stops().begin())
417 result << ", ";
418 result << "gradient::color_stop{ " << s->first() << ", color{ " << convert_emit_argument(sRGB_color{ s->second() }) << " } }";
419 }
420 result << " }, " << enum_to_string<gradient_direction>("gradient_direction", aArgument.direction());
421 return result.str();
422 }
423 };
424}
std::string_view to_std_string_view() const noexcept
Definition i_string.hpp:76
std::string to_std_string() const
Definition i_string.hpp:75
#define end_declare_enum(enumName)
Definition i_enum.hpp:62
#define declare_enum_string(enumName, enumEnumerator)
Definition i_enum.hpp:59
#define begin_declare_enum(enumName)
Definition i_enum.hpp:52
constexpr bool is_widget_or_layout(ui_element_type aType)
object_type ui_element_type
const member_element_t member_element
uint32_t id
constexpr object_type category(object_type aType)
basic_length< default_geometry_value_type > length
Definition units.hpp:926
neolib::variant< color, gradient > color_or_gradient
Definition gradient.hpp:149
shared_gradient gradient
Definition gradient.hpp:146
sRGB_color color
Definition color.hpp:1067
std::string to_std_string(T const &aValue)
ref_ptr< ConcreteType > make_ref(Args &&... args)
to_const_reference_t< T > to_const(T &&object)
Definition neolib.hpp:113
StringT enum_to_string(Enum aEnumerator, bool aMustEnumerate=false)
Definition i_enum.hpp:78
Definition plf_hive.h:79
constexpr decltype(auto) visit(Visitor &&vis, neolib::variant< Types... > &&var)
Definition variant.hpp:60