1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
3 | |
4 | #pragma once |
5 | |
6 | #include "slint.h" |
7 | |
8 | #ifndef SLINT_FEATURE_INTERPRETER |
9 | # warning "slint-interpreter.h API only available when SLINT_FEATURE_INTERPRETER is activated" |
10 | #else |
11 | |
12 | # include "slint_interpreter_internal.h" |
13 | |
14 | # include <optional> |
15 | |
16 | # ifdef SLINT_FEATURE_BACKEND_QT |
17 | class QWidget; |
18 | # endif |
19 | |
20 | namespace slint::cbindgen_private { |
21 | // This has to stay opaque, but VRc don't compile if it is just forward declared |
22 | struct ErasedItemTreeBox : vtable::Dyn |
23 | { |
24 | ~ErasedItemTreeBox() = delete; |
25 | ErasedItemTreeBox() = delete; |
26 | ErasedItemTreeBox(ErasedItemTreeBox &) = delete; |
27 | }; |
28 | } |
29 | |
30 | /// The types in this namespace allow you to load a .slint file at runtime and show its UI. |
31 | /// |
32 | /// You only need to use them if you do not want to use pre-compiled .slint code, which is |
33 | /// the normal way to use Slint. |
34 | /// |
35 | /// The entry point for this namespace is the \ref ComponentCompiler, which you can |
36 | /// use to create \ref ComponentDefinition instances with the |
37 | /// \ref ComponentCompiler::build_from_source() or \ref ComponentCompiler::build_from_path() |
38 | /// functions. |
39 | namespace slint::interpreter { |
40 | |
41 | class Value; |
42 | |
43 | /// This type represents a runtime instance of structure in `.slint`. |
44 | /// |
45 | /// This can either be an instance of a name structure introduced |
46 | /// with the `struct` keyword in the .slint file, or an anonymous struct |
47 | /// written with the `{ key: value, }` notation. |
48 | /// |
49 | /// It can be constructed with the range constructor or initializer lst, |
50 | /// and converted into or from a Value with the Value constructor and |
51 | /// Value::to_struct(). |
52 | struct Struct |
53 | { |
54 | public: |
55 | /// Constructs a new empty struct. You can add fields with set_field() and |
56 | /// read them with get_field(). |
57 | Struct() { cbindgen_private::slint_interpreter_struct_new(&inner); } |
58 | |
59 | /// Creates a new Struct as a copy from \a other. All fields are copied as well. |
60 | Struct(const Struct &other) |
61 | { |
62 | cbindgen_private::slint_interpreter_struct_clone(&other.inner, &inner); |
63 | } |
64 | /// Creates a new Struct by moving all fields from \a other into this struct. |
65 | Struct(Struct &&other) |
66 | { |
67 | inner = other.inner; |
68 | cbindgen_private::slint_interpreter_struct_new(&other.inner); |
69 | } |
70 | /// Assigns all the fields of \a other to this struct. |
71 | Struct &operator=(const Struct &other) |
72 | { |
73 | if (this == &other) |
74 | return *this; |
75 | cbindgen_private::slint_interpreter_struct_destructor(&inner); |
76 | slint_interpreter_struct_clone(&other.inner, &inner); |
77 | return *this; |
78 | } |
79 | /// Moves all the fields of \a other to this struct. |
80 | Struct &operator=(Struct &&other) |
81 | { |
82 | if (this == &other) |
83 | return *this; |
84 | cbindgen_private::slint_interpreter_struct_destructor(&inner); |
85 | inner = other.inner; |
86 | cbindgen_private::slint_interpreter_struct_new(&other.inner); |
87 | return *this; |
88 | } |
89 | /// Destroys this struct. |
90 | ~Struct() { cbindgen_private::slint_interpreter_struct_destructor(&inner); } |
91 | |
92 | /// Creates a new struct with the fields of the std::initializer_list given by args. |
93 | inline Struct(std::initializer_list<std::pair<std::string_view, Value>> args); |
94 | |
95 | /// Creates a new struct with the fields produced by the iterator \a it. \a it is |
96 | /// advanced until it equals \a end. |
97 | template<typename InputIterator |
98 | // Doxygen doesn't understand this template wizardry |
99 | # if !defined(DOXYGEN) |
100 | , |
101 | typename std::enable_if_t< |
102 | std::is_convertible<decltype(std::get<0>(*std::declval<InputIterator>())), |
103 | std::string_view>::value |
104 | && std::is_convertible<decltype(std::get<1>(*std::declval<InputIterator>())), |
105 | Value>::value |
106 | |
107 | > * = nullptr |
108 | # endif |
109 | > |
110 | Struct(InputIterator it, InputIterator end) : Struct() |
111 | { |
112 | for (; it != end; ++it) { |
113 | auto [key, value] = *it; |
114 | set_field(name: key, value); |
115 | } |
116 | } |
117 | |
118 | // FIXME: this probably miss a lot of iterator api |
119 | /// The Struct::iterator class implements the typical C++ iterator protocol and conveniently |
120 | /// provides access to the field names and values of a Struct. It is created by calling either |
121 | /// Struct::begin() or Struct::end(). |
122 | /// |
123 | /// Make sure to compare the iterator to the iterator returned by Struct::end() before |
124 | /// de-referencing it. The value returned when de-referencing is a std::pair that holds a |
125 | /// std::string_view of the field name as well as a const reference of the value. Both |
126 | /// references become invalid when the iterator or the Struct is changed, so make sure to make |
127 | /// copies if you want to retain the name or value. |
128 | /// |
129 | /// Note that the order in which the iterator exposes the fields is not defined. |
130 | /// |
131 | /// If you're using C++ 17, you can use the convenience destructuring syntax to extract the name |
132 | /// and value in one go: |
133 | /// |
134 | /// ``` |
135 | /// Struct stru = ...; |
136 | /// auto it = stru.begin(); |
137 | /// ... |
138 | /// ++it; // advance iterator to the next field |
139 | /// ... |
140 | /// // Check iterator before dereferencing it |
141 | /// if (it != stru.end()) { |
142 | /// // Extract a view of the name and a const reference to the value in one go. |
143 | /// auto [field_name, field_value] = *it; |
144 | /// } |
145 | /// ``` |
146 | struct iterator |
147 | { |
148 | /// A typedef for std::pair<std::string_view, const Value &> that's returned |
149 | /// when dereferencing the iterator. |
150 | using value_type = std::pair<std::string_view, const Value &>; |
151 | |
152 | private: |
153 | cbindgen_private::StructIteratorOpaque inner; |
154 | Value *v = nullptr; |
155 | std::string_view k; |
156 | friend Struct; |
157 | explicit iterator(cbindgen_private::StructIteratorOpaque inner) : inner(inner) { next(); } |
158 | // construct a end iterator |
159 | iterator() = default; |
160 | inline void next(); |
161 | |
162 | public: |
163 | /// Destroys this field iterator. |
164 | inline ~iterator(); |
165 | // FIXME I believe iterators are supposed to be copy constructible |
166 | iterator(const iterator &) = delete; |
167 | iterator &operator=(const iterator &) = delete; |
168 | /// Move-constructs a new iterator from \a other. |
169 | iterator(iterator &&other) = default; |
170 | /// Move-assigns the iterator \a other to this and returns a reference to this. |
171 | iterator &operator=(iterator &&other) = default; |
172 | /// The prefix ++ operator advances the iterator to the next entry and returns |
173 | /// a reference to this. |
174 | iterator &operator++() |
175 | { |
176 | if (v) |
177 | next(); |
178 | return *this; |
179 | } |
180 | /// Dereferences the iterator to return a pair of the key and value. |
181 | value_type operator*() const { return { k, *v }; } |
182 | /// Returns true if \a a is pointing to the same entry as \a b; false otherwise. |
183 | friend bool operator==(const iterator &a, const iterator &b) { return a.v == b.v; } |
184 | /// Returns false if \a a is pointing to the same entry as \a b; true otherwise. |
185 | friend bool operator!=(const iterator &a, const iterator &b) { return a.v != b.v; } |
186 | }; |
187 | |
188 | /// Returns an iterator over the fields of the struct. |
189 | iterator begin() const |
190 | { |
191 | return iterator(cbindgen_private::slint_interpreter_struct_make_iter(&inner)); |
192 | } |
193 | /// Returns an iterator that when compared with an iterator returned by begin() can be |
194 | /// used to detect when all fields have been visited. |
195 | iterator end() const { return iterator(); } |
196 | |
197 | /// Returns the value of the field with the given \a name; Returns an std::optional without |
198 | /// value if the field does not exist. |
199 | inline std::optional<Value> get_field(std::string_view name) const; |
200 | /// Sets the value of the field with the given \a name to the specified \a value. If the field |
201 | /// does not exist yet, it is created; otherwise the existing field is updated to hold the new |
202 | /// value. |
203 | inline void set_field(std::string_view name, const Value &value); |
204 | |
205 | /// \private |
206 | Struct(const slint::cbindgen_private::StructOpaque &other) |
207 | { |
208 | cbindgen_private::slint_interpreter_struct_clone(&other, &inner); |
209 | } |
210 | |
211 | private: |
212 | using StructOpaque = slint::cbindgen_private::StructOpaque; |
213 | StructOpaque inner; |
214 | friend class Value; |
215 | }; |
216 | |
217 | /// This is a dynamically typed value used in the Slint interpreter. |
218 | /// It can hold a value of different types, and you should use the |
219 | /// different overloaded constructors and the to_xxx() functions to access the |
220 | //// value within. |
221 | /// |
222 | /// It is also possible to query the type the value holds by calling the Value::type() |
223 | /// function. |
224 | /// |
225 | /// Note that models are only represented in one direction: You can create a slint::Model<Value> |
226 | /// in C++, store it in a std::shared_ptr and construct Value from it. Then you can set it on a |
227 | /// property in your .slint code that was declared to be either an array (`property <[sometype]> |
228 | /// foo;`) or an object literal (`property <{foo: string, bar: int}> my_prop;`). Such properties are |
229 | /// dynamic and accept models implemented in C++. |
230 | /// |
231 | /// ``` |
232 | /// Value v(42.0); // Creates a value that holds a double with the value 42. |
233 | /// |
234 | /// Value some_value = ...; |
235 | /// // Check if the value has a string |
236 | /// if (std::optional<slint::SharedString> string_value = some_value.to_string()) |
237 | /// do_something(*string_value); // Extract the string by de-referencing |
238 | /// ``` |
239 | class Value |
240 | { |
241 | public: |
242 | /// Constructs a new value of type Value::Type::Void. |
243 | Value() : inner(cbindgen_private::slint_interpreter_value_new()) { } |
244 | |
245 | /// Constructs a new value by copying \a other. |
246 | Value(const Value &other) : inner(slint_interpreter_value_clone(other.inner)) { } |
247 | /// Constructs a new value by moving \a other to this. |
248 | Value(Value &&other) |
249 | { |
250 | inner = other.inner; |
251 | other.inner = cbindgen_private::slint_interpreter_value_new(); |
252 | } |
253 | /// Assigns the value \a other to this. |
254 | Value &operator=(const Value &other) |
255 | { |
256 | if (this == &other) |
257 | return *this; |
258 | cbindgen_private::slint_interpreter_value_destructor(inner); |
259 | inner = slint_interpreter_value_clone(other.inner); |
260 | return *this; |
261 | } |
262 | /// Moves the value \a other to this. |
263 | Value &operator=(Value &&other) |
264 | { |
265 | if (this == &other) |
266 | return *this; |
267 | cbindgen_private::slint_interpreter_value_destructor(inner); |
268 | inner = other.inner; |
269 | other.inner = cbindgen_private::slint_interpreter_value_new(); |
270 | return *this; |
271 | } |
272 | /// Destroys the value. |
273 | ~Value() { cbindgen_private::slint_interpreter_value_destructor(inner); } |
274 | |
275 | /// A convenience alias for the value type enum. |
276 | using Type = ValueType; |
277 | |
278 | // optional<int> to_int() const; |
279 | // optional<float> to_float() const; |
280 | /// Returns a std::optional that contains a double if the type of this Value is |
281 | /// Type::Double, otherwise an empty optional is returned. |
282 | std::optional<double> to_number() const |
283 | { |
284 | if (auto *number = cbindgen_private::slint_interpreter_value_to_number(inner)) { |
285 | return *number; |
286 | } else { |
287 | return {}; |
288 | } |
289 | } |
290 | |
291 | /// Returns a std::optional that contains a string if the type of this Value is |
292 | /// Type::String, otherwise an empty optional is returned. |
293 | std::optional<slint::SharedString> to_string() const |
294 | { |
295 | if (auto *str = cbindgen_private::slint_interpreter_value_to_string(inner)) { |
296 | return *str; |
297 | } else { |
298 | return {}; |
299 | } |
300 | } |
301 | |
302 | /// Returns a std::optional that contains a bool if the type of this Value is |
303 | /// Type::Bool, otherwise an empty optional is returned. |
304 | std::optional<bool> to_bool() const |
305 | { |
306 | if (auto *b = cbindgen_private::slint_interpreter_value_to_bool(inner)) { |
307 | return *b; |
308 | } else { |
309 | return {}; |
310 | } |
311 | } |
312 | |
313 | /// Returns a std::optional that contains a vector of values if the type of this Value is |
314 | /// Type::Model, otherwise an empty optional is returned. |
315 | /// |
316 | /// The vector will be constructed by serializing all the elements of the model. |
317 | inline std::optional<slint::SharedVector<Value>> to_array() const; |
318 | |
319 | /// Returns a std::optional that contains a brush if the type of this Value is |
320 | /// Type::Brush, otherwise an empty optional is returned. |
321 | std::optional<slint::Brush> to_brush() const |
322 | { |
323 | if (auto *brush = cbindgen_private::slint_interpreter_value_to_brush(inner)) { |
324 | return *brush; |
325 | } else { |
326 | return {}; |
327 | } |
328 | } |
329 | |
330 | /// Returns a std::optional that contains a Struct if the type of this Value is |
331 | /// Type::Struct, otherwise an empty optional is returned. |
332 | std::optional<Struct> to_struct() const |
333 | { |
334 | if (auto *opaque_struct = cbindgen_private::slint_interpreter_value_to_struct(inner)) { |
335 | return Struct(*opaque_struct); |
336 | } else { |
337 | return {}; |
338 | } |
339 | } |
340 | |
341 | /// Returns a std::optional that contains an Image if the type of this Value is |
342 | /// Type::Image, otherwise an empty optional is returned. |
343 | std::optional<Image> to_image() const |
344 | { |
345 | if (auto *img = cbindgen_private::slint_interpreter_value_to_image(inner)) { |
346 | return *reinterpret_cast<const Image *>(img); |
347 | } else { |
348 | return {}; |
349 | } |
350 | } |
351 | |
352 | // template<typename T> std::optional<T> get() const; |
353 | |
354 | /// Constructs a new Value that holds the double \a value. |
355 | Value(double value) : inner(cbindgen_private::slint_interpreter_value_new_double(value)) { } |
356 | /// Constructs a new Value that holds the int \a value. |
357 | /// Internally this is stored as a double and Value::type() will return Value::Type::Number. |
358 | Value(int value) : Value(static_cast<double>(value)) { } |
359 | /// Constructs a new Value that holds the string \a str. |
360 | Value(const SharedString &str) |
361 | : inner(cbindgen_private::slint_interpreter_value_new_string(&str)) |
362 | { |
363 | } |
364 | /// Constructs a new Value that holds the boolean \a b. |
365 | Value(bool b) : inner(cbindgen_private::slint_interpreter_value_new_bool(b)) { } |
366 | /// Constructs a new Value that holds the value vector \a v as a model. |
367 | inline Value(const SharedVector<Value> &v); |
368 | /// Constructs a new Value that holds the value model \a m. |
369 | Value(const std::shared_ptr<slint::Model<Value>> &m); |
370 | /// Constructs a new Value that holds the brush \a b. |
371 | Value(const slint::Brush &brush) |
372 | : inner(cbindgen_private::slint_interpreter_value_new_brush(&brush)) |
373 | { |
374 | } |
375 | /// Constructs a new Value that holds the Struct \a struc. |
376 | Value(const Struct &struc) |
377 | : inner(cbindgen_private::slint_interpreter_value_new_struct(&struc.inner)) |
378 | { |
379 | } |
380 | |
381 | /// Constructs a new Value that holds the Image \a img. |
382 | Value(const Image &img) : inner(cbindgen_private::slint_interpreter_value_new_image(&img)) { } |
383 | |
384 | /// Returns the type the variant holds. |
385 | Type type() const { return cbindgen_private::slint_interpreter_value_type(inner); } |
386 | |
387 | /// Returns true if \a a and \a b hold values of the same type and the underlying vales are |
388 | /// equal. |
389 | friend bool operator==(const Value &a, const Value &b) |
390 | { |
391 | return cbindgen_private::slint_interpreter_value_eq(a.inner, b.inner); |
392 | } |
393 | |
394 | private: |
395 | inline Value(const void *) = delete; // Avoid that for example Value("foo") turns to Value(bool) |
396 | slint::cbindgen_private::Value *inner; |
397 | friend struct Struct; |
398 | friend class ComponentInstance; |
399 | // Internal constructor that takes ownership of the value |
400 | explicit Value(slint::cbindgen_private::Value *&&inner) : inner(inner) { } |
401 | }; |
402 | |
403 | inline Value::Value(const slint::SharedVector<Value> &array) |
404 | : inner(cbindgen_private::slint_interpreter_value_new_array_model( |
405 | reinterpret_cast<const slint::SharedVector<slint::cbindgen_private::Value *> *>( |
406 | &array))) |
407 | { |
408 | } |
409 | |
410 | inline std::optional<slint::SharedVector<Value>> Value::to_array() const |
411 | { |
412 | slint::SharedVector<Value> array; |
413 | if (cbindgen_private::slint_interpreter_value_to_array( |
414 | &inner, |
415 | reinterpret_cast<slint::SharedVector<slint::cbindgen_private::Value *> *>( |
416 | &array))) { |
417 | return array; |
418 | } else { |
419 | return {}; |
420 | } |
421 | } |
422 | inline Value::Value(const std::shared_ptr<slint::Model<Value>> &model) |
423 | { |
424 | using cbindgen_private::ModelAdaptorVTable; |
425 | using vtable::VRef; |
426 | struct ModelWrapper : private_api::ModelChangeListener |
427 | { |
428 | std::shared_ptr<slint::Model<Value>> model; |
429 | cbindgen_private::ModelNotifyOpaque notify; |
430 | // This kind of mean that the rust code has ownership of "this" until the drop function is |
431 | // called |
432 | std::shared_ptr<ModelChangeListener> self; |
433 | ~ModelWrapper() { cbindgen_private::slint_interpreter_model_notify_destructor(¬ify); } |
434 | |
435 | void row_added(size_t index, size_t count) override |
436 | { |
437 | cbindgen_private::slint_interpreter_model_notify_row_added(¬ify, index, count); |
438 | } |
439 | void row_changed(size_t index) override |
440 | { |
441 | cbindgen_private::slint_interpreter_model_notify_row_changed(¬ify, index); |
442 | } |
443 | void row_removed(size_t index, size_t count) override |
444 | { |
445 | cbindgen_private::slint_interpreter_model_notify_row_removed(¬ify, index, count); |
446 | } |
447 | void reset() override { cbindgen_private::slint_interpreter_model_notify_reset(¬ify); } |
448 | }; |
449 | |
450 | auto wrapper = std::make_shared<ModelWrapper>(); |
451 | wrapper->model = model; |
452 | wrapper->self = wrapper; |
453 | cbindgen_private::slint_interpreter_model_notify_new(&wrapper->notify); |
454 | model->attach_peer(wrapper); |
455 | |
456 | auto row_count = [](VRef<ModelAdaptorVTable> self) -> uintptr_t { |
457 | return reinterpret_cast<ModelWrapper *>(self.instance)->model->row_count(); |
458 | }; |
459 | auto row_data = [](VRef<ModelAdaptorVTable> self, |
460 | uintptr_t row) -> slint::cbindgen_private::Value * { |
461 | std::optional<Value> v = |
462 | reinterpret_cast<ModelWrapper *>(self.instance)->model->row_data(int(row)); |
463 | if (v.has_value()) { |
464 | slint::cbindgen_private::Value *rval = v->inner; |
465 | v->inner = cbindgen_private::slint_interpreter_value_new(); |
466 | return rval; |
467 | } else { |
468 | return nullptr; |
469 | } |
470 | }; |
471 | auto set_row_data = [](VRef<ModelAdaptorVTable> self, uintptr_t row, |
472 | slint::cbindgen_private::Value *value) { |
473 | Value v(std::move(value)); |
474 | reinterpret_cast<ModelWrapper *>(self.instance)->model->set_row_data(int(row), v); |
475 | }; |
476 | auto get_notify = |
477 | [](VRef<ModelAdaptorVTable> self) -> const cbindgen_private::ModelNotifyOpaque * { |
478 | return &reinterpret_cast<ModelWrapper *>(self.instance)->notify; |
479 | }; |
480 | auto drop = [](vtable::VRefMut<ModelAdaptorVTable> self) { |
481 | reinterpret_cast<ModelWrapper *>(self.instance)->self = nullptr; |
482 | }; |
483 | |
484 | static const ModelAdaptorVTable vt { row_count, row_data, set_row_data, get_notify, drop }; |
485 | inner = cbindgen_private::slint_interpreter_value_new_model( |
486 | reinterpret_cast<uint8_t *>(wrapper.get()), &vt); |
487 | } |
488 | |
489 | inline Struct::Struct(std::initializer_list<std::pair<std::string_view, Value>> args) |
490 | : Struct(args.begin(), args.end()) |
491 | { |
492 | } |
493 | |
494 | inline std::optional<Value> Struct::get_field(std::string_view name) const |
495 | { |
496 | using namespace cbindgen_private; |
497 | cbindgen_private::Slice<uint8_t> name_view { |
498 | const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(name.data())), |
499 | name.size() |
500 | }; |
501 | if (cbindgen_private::Value *field_val = |
502 | cbindgen_private::slint_interpreter_struct_get_field(&inner, name_view)) { |
503 | return Value(std::move(field_val)); |
504 | } else { |
505 | return {}; |
506 | } |
507 | } |
508 | inline void Struct::set_field(std::string_view name, const Value &value) |
509 | { |
510 | cbindgen_private::Slice<uint8_t> name_view { |
511 | const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(name.data())), |
512 | name.size() |
513 | }; |
514 | cbindgen_private::slint_interpreter_struct_set_field(&inner, name_view, value.inner); |
515 | } |
516 | |
517 | inline void Struct::iterator::next() |
518 | { |
519 | cbindgen_private::Slice<uint8_t> name_slice; |
520 | |
521 | if (cbindgen_private::Value *nextval_inner = |
522 | cbindgen_private::slint_interpreter_struct_iterator_next(&inner, &name_slice)) { |
523 | k = std::string_view(reinterpret_cast<char *>(name_slice.ptr), name_slice.len); |
524 | if (!v) |
525 | v = new Value(); |
526 | *v = Value(std::move(nextval_inner)); |
527 | } else { |
528 | cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner); |
529 | delete v; |
530 | v = nullptr; |
531 | } |
532 | } |
533 | |
534 | inline Struct::iterator::~iterator() |
535 | { |
536 | if (v) { |
537 | cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner); |
538 | delete v; |
539 | } |
540 | } |
541 | |
542 | class ComponentDefinition; |
543 | |
544 | /// The ComponentInstance represents a running instance of a component. |
545 | /// |
546 | /// You can create an instance with the ComponentDefinition::create() function. |
547 | /// |
548 | /// Properties and callback can be accessed using the associated functions. |
549 | /// |
550 | /// An instance can be put on screen with the ComponentInstance::show() or the |
551 | /// ComponentInstance::run() |
552 | class ComponentInstance : vtable::Dyn |
553 | { |
554 | ComponentInstance() = delete; |
555 | ComponentInstance(ComponentInstance &) = delete; |
556 | ComponentInstance &operator=(ComponentInstance &) = delete; |
557 | friend class ComponentDefinition; |
558 | |
559 | // ComponentHandle<ComponentInstance> is in fact a VRc<ItemTreeVTable, ErasedItemTreeBox> |
560 | const cbindgen_private::ErasedItemTreeBox *inner() const |
561 | { |
562 | slint::private_api::assert_main_thread(); |
563 | return reinterpret_cast<const cbindgen_private::ErasedItemTreeBox *>(this); |
564 | } |
565 | |
566 | public: |
567 | /// Marks the window of this component to be shown on the screen. This registers |
568 | /// the window with the windowing system. In order to react to events from the windowing system, |
569 | /// such as draw requests or mouse/touch input, it is still necessary to spin the event loop, |
570 | /// using slint::run_event_loop(). |
571 | void show() const |
572 | { |
573 | cbindgen_private::slint_interpreter_component_instance_show(inner(), true); |
574 | } |
575 | /// Marks the window of this component to be hidden on the screen. This de-registers |
576 | /// the window from the windowing system and it will not receive any further events. |
577 | void hide() const |
578 | { |
579 | cbindgen_private::slint_interpreter_component_instance_show(inner(), false); |
580 | } |
581 | /// Returns the Window associated with this component. The window API can be used |
582 | /// to control different aspects of the integration into the windowing system, |
583 | /// such as the position on the screen. |
584 | const slint::Window &window() |
585 | { |
586 | const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr; |
587 | cbindgen_private::slint_interpreter_component_instance_window(inner(), &win_ptr); |
588 | return *reinterpret_cast<const slint::Window *>(win_ptr); |
589 | } |
590 | /// This is a convenience function that first calls show(), followed by |
591 | /// slint::run_event_loop() and hide(). |
592 | void run() const |
593 | { |
594 | show(); |
595 | slint::run_event_loop(); |
596 | hide(); |
597 | } |
598 | # if defined(SLINT_FEATURE_BACKEND_QT) || defined(DOXYGEN) |
599 | /// Return a QWidget for this instance. |
600 | /// This function is only available if the qt graphical backend was compiled in, and |
601 | /// it may return nullptr if the Qt backend is not used at runtime. |
602 | QWidget *qwidget() const |
603 | { |
604 | const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr; |
605 | cbindgen_private::slint_interpreter_component_instance_window(inner(), &win_ptr); |
606 | auto wid = reinterpret_cast<QWidget *>(cbindgen_private::slint_qt_get_widget( |
607 | reinterpret_cast<const cbindgen_private::WindowAdapterRc *>(win_ptr))); |
608 | return wid; |
609 | } |
610 | # endif |
611 | |
612 | /// Set the value for a public property of this component |
613 | /// |
614 | /// For example, if the component has a `property <string> hello;`, |
615 | /// we can set this property |
616 | /// ``` |
617 | /// instance->set_property("hello", slint::SharedString("world")); |
618 | /// ``` |
619 | /// |
620 | /// Returns true if the property was correctly set. Returns false if the property |
621 | /// could not be set because it either do not exist (was not declared in .slint) or if |
622 | /// the value is not of the proper type for the property's type. |
623 | bool set_property(std::string_view name, const Value &value) const |
624 | { |
625 | using namespace cbindgen_private; |
626 | return slint_interpreter_component_instance_set_property( |
627 | inner(), slint::private_api::string_to_slice(str: name), value.inner); |
628 | } |
629 | /// Returns the value behind a property declared in .slint. |
630 | std::optional<Value> get_property(std::string_view name) const |
631 | { |
632 | using namespace cbindgen_private; |
633 | if (cbindgen_private::Value *prop_inner = slint_interpreter_component_instance_get_property( |
634 | inner(), slint::private_api::string_to_slice(name))) { |
635 | return Value(std::move(prop_inner)); |
636 | } else { |
637 | return {}; |
638 | } |
639 | } |
640 | /// Invoke the specified callback or function declared in .slint with the given arguments |
641 | /// |
642 | /// Example: imagine the .slint file contains the given callback declaration: |
643 | /// ``` |
644 | /// callback foo(string, int) -> string; |
645 | /// ``` |
646 | /// Then one can call it with this function |
647 | /// ``` |
648 | /// slint::Value args[] = { SharedString("Hello"), 42. }; |
649 | /// instance->invoke("foo", { args, 2 }); |
650 | /// ``` |
651 | /// |
652 | /// Returns an null optional if the callback don't exist or if the argument don't match |
653 | /// Otherwise return the returned value from the callback, which may be an empty Value if |
654 | /// the callback did not return a value. |
655 | std::optional<Value> invoke(std::string_view name, std::span<const Value> args) const |
656 | { |
657 | using namespace cbindgen_private; |
658 | Slice<Box<cbindgen_private::Value>> args_view { |
659 | const_cast<Box<cbindgen_private::Value> *>( |
660 | reinterpret_cast<const Box<cbindgen_private::Value> *>(args.data())), |
661 | args.size() |
662 | }; |
663 | if (cbindgen_private::Value *rval_inner = slint_interpreter_component_instance_invoke( |
664 | inner(), slint::private_api::string_to_slice(name), args_view)) { |
665 | return Value(std::move(rval_inner)); |
666 | } else { |
667 | return {}; |
668 | } |
669 | } |
670 | |
671 | /// Set a handler for the callback with the given name. |
672 | /// |
673 | /// A callback with that name must be defined in the document otherwise the function |
674 | /// returns false. |
675 | /// |
676 | /// The \a callback parameter is a functor which takes as argument a slice of Value |
677 | /// and must return a Value. |
678 | /// |
679 | /// Example: imagine the .slint file contains the given callback declaration: |
680 | /// ``` |
681 | /// callback foo(string, int) -> string; |
682 | /// ``` |
683 | /// Then one can set the callback handler with this function |
684 | /// ``` |
685 | /// instance->set_callback("foo", [](auto args) { |
686 | /// std::cout << "foo(" << *args[0].to_string() << ", " << *args[1].to_number() << ")\n"; |
687 | /// }); |
688 | /// ``` |
689 | /// |
690 | /// Note: Since the ComponentInstance holds the handler, the handler itself should not |
691 | /// capture a strong reference to the instance. |
692 | template<std::invocable<std::span<const Value>> F> |
693 | requires(std::is_convertible_v<std::invoke_result_t<F, std::span<const Value>>, Value>) |
694 | auto set_callback(std::string_view name, F callback) const -> bool |
695 | { |
696 | using namespace cbindgen_private; |
697 | auto actual_cb = |
698 | [](void *data, |
699 | cbindgen_private::Slice<cbindgen_private::Box<cbindgen_private::Value>> arg) { |
700 | std::span<const Value> args_view { reinterpret_cast<const Value *>(arg.ptr), |
701 | arg.len }; |
702 | Value r = (*reinterpret_cast<F *>(data))(args_view); |
703 | auto inner = r.inner; |
704 | r.inner = cbindgen_private::slint_interpreter_value_new(); |
705 | return inner; |
706 | }; |
707 | return cbindgen_private::slint_interpreter_component_instance_set_callback( |
708 | inner(), slint::private_api::string_to_slice(name), actual_cb, |
709 | new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); }); |
710 | } |
711 | |
712 | /// Set the value for a property within an exported global singleton. |
713 | /// |
714 | /// For example, if the main file has an exported global `TheGlobal` with a |
715 | /// `property <int> hello`, we can set this property |
716 | /// ``` |
717 | /// instance->set_global_property("TheGlobal", "hello", 42); |
718 | /// ``` |
719 | /// |
720 | /// Returns true if the property was correctly set. Returns false if the property |
721 | /// could not be set because it either does not exist (was not declared in .slint) or if |
722 | /// the value is not of the correct type for the property's type. |
723 | /// |
724 | /// **Note:** Only globals that are exported or re-exported from the main .slint file will |
725 | /// be accessible |
726 | bool set_global_property(std::string_view global, std::string_view prop_name, |
727 | const Value &value) const |
728 | { |
729 | using namespace cbindgen_private; |
730 | return slint_interpreter_component_instance_set_global_property( |
731 | inner(), slint::private_api::string_to_slice(str: global), |
732 | slint::private_api::string_to_slice(str: prop_name), value.inner); |
733 | } |
734 | /// Returns the value behind a property in an exported global singleton. |
735 | std::optional<Value> get_global_property(std::string_view global, |
736 | std::string_view prop_name) const |
737 | { |
738 | using namespace cbindgen_private; |
739 | if (cbindgen_private::Value *rval_inner = |
740 | slint_interpreter_component_instance_get_global_property( |
741 | inner(), slint::private_api::string_to_slice(global), |
742 | slint::private_api::string_to_slice(prop_name))) { |
743 | return Value(std::move(rval_inner)); |
744 | } else { |
745 | return {}; |
746 | } |
747 | } |
748 | |
749 | /// Like `set_callback()` but on a callback in the specified exported global singleton. |
750 | /// |
751 | /// Example: imagine the .slint file contains the given global: |
752 | /// ```slint,no-preview |
753 | /// export global Logic { |
754 | /// pure callback to_uppercase(string) -> string; |
755 | /// } |
756 | /// ``` |
757 | /// Then you can set the callback handler |
758 | /// ```cpp |
759 | /// instance->set_global_callback("Logic", "to_uppercase", [](auto args) { |
760 | /// std::string arg1(*args[0].to_string()); |
761 | /// std::transform(arg1.begin(), arg1.end(), arg1.begin(), toupper); |
762 | /// return SharedString(arg1); |
763 | /// }) |
764 | /// ``` |
765 | /// |
766 | /// **Note:** Only globals that are exported or re-exported from the main .slint file will |
767 | /// be accessible |
768 | template<std::invocable<std::span<const Value>> F> |
769 | bool set_global_callback(std::string_view global, std::string_view name, F callback) const |
770 | { |
771 | using namespace cbindgen_private; |
772 | auto actual_cb = |
773 | [](void *data, |
774 | cbindgen_private::Slice<cbindgen_private::Box<cbindgen_private::Value>> arg) { |
775 | std::span<const Value> args_view { reinterpret_cast<const Value *>(arg.ptr), |
776 | arg.len }; |
777 | Value r = (*reinterpret_cast<F *>(data))(args_view); |
778 | auto inner = r.inner; |
779 | r.inner = cbindgen_private::slint_interpreter_value_new(); |
780 | return inner; |
781 | }; |
782 | return cbindgen_private::slint_interpreter_component_instance_set_global_callback( |
783 | inner(), slint::private_api::string_to_slice(global), |
784 | slint::private_api::string_to_slice(name), actual_cb, new F(std::move(callback)), |
785 | [](void *data) { delete reinterpret_cast<F *>(data); }); |
786 | } |
787 | |
788 | /// Invoke the specified callback or function declared in an exported global singleton |
789 | std::optional<Value> invoke_global(std::string_view global, std::string_view callable_name, |
790 | std::span<const Value> args) const |
791 | { |
792 | using namespace cbindgen_private; |
793 | Slice<cbindgen_private::Box<cbindgen_private::Value>> args_view { |
794 | const_cast<cbindgen_private::Box<cbindgen_private::Value> *>( |
795 | reinterpret_cast<const cbindgen_private::Box<cbindgen_private::Value> *>( |
796 | args.data())), |
797 | args.size() |
798 | }; |
799 | if (cbindgen_private::Value *rval_inner = |
800 | slint_interpreter_component_instance_invoke_global( |
801 | inner(), slint::private_api::string_to_slice(global), |
802 | slint::private_api::string_to_slice(callable_name), args_view)) { |
803 | return Value(std::move(rval_inner)); |
804 | } else { |
805 | return {}; |
806 | } |
807 | } |
808 | |
809 | /// Return the ComponentDefinition that was used to create this instance. |
810 | inline ComponentDefinition definition() const; |
811 | }; |
812 | |
813 | /// ComponentDefinition is a representation of a compiled component from .slint markup. |
814 | /// |
815 | /// It can be constructed from a .slint file using the ComponentCompiler::build_from_path() or |
816 | /// ComponentCompiler::build_from_source() functions. And then it can be instantiated with the |
817 | /// create() function. |
818 | /// |
819 | /// The ComponentDefinition acts as a factory to create new instances. When you've finished |
820 | /// creating the instances it is safe to destroy the ComponentDefinition. |
821 | class ComponentDefinition |
822 | { |
823 | friend class ComponentCompiler; |
824 | friend class ComponentInstance; |
825 | |
826 | using ComponentDefinitionOpaque = slint::cbindgen_private::ComponentDefinitionOpaque; |
827 | ComponentDefinitionOpaque inner; |
828 | |
829 | ComponentDefinition() = delete; |
830 | // Internal constructor that takes ownership of the component definition |
831 | explicit ComponentDefinition(ComponentDefinitionOpaque &inner) : inner(inner) { } |
832 | |
833 | public: |
834 | /// Constructs a new ComponentDefinition as a copy of \a other. |
835 | ComponentDefinition(const ComponentDefinition &other) |
836 | { |
837 | slint_interpreter_component_definition_clone(&other.inner, &inner); |
838 | } |
839 | /// Assigns \a other to this ComponentDefinition. |
840 | ComponentDefinition &operator=(const ComponentDefinition &other) |
841 | { |
842 | using namespace slint::cbindgen_private; |
843 | |
844 | if (this == &other) |
845 | return *this; |
846 | |
847 | slint_interpreter_component_definition_destructor(&inner); |
848 | slint_interpreter_component_definition_clone(&other.inner, &inner); |
849 | |
850 | return *this; |
851 | } |
852 | /// Destroys this ComponentDefinition. |
853 | ~ComponentDefinition() { slint_interpreter_component_definition_destructor(&inner); } |
854 | /// Creates a new instance of the component and returns a shared handle to it. |
855 | ComponentHandle<ComponentInstance> create() const |
856 | { |
857 | union CI { |
858 | cbindgen_private::ComponentInstance i; |
859 | ComponentHandle<ComponentInstance> result; |
860 | ~CI() { result.~ComponentHandle(); } |
861 | CI() { } |
862 | } u; |
863 | cbindgen_private::slint_interpreter_component_instance_create(&inner, &u.i); |
864 | return u.result; |
865 | } |
866 | |
867 | /// Returns a vector of PropertyDescriptor instances that describe the list of |
868 | /// public properties that can be read and written using ComponentInstance::set_property and |
869 | /// ComponentInstance::get_property. |
870 | slint::SharedVector<PropertyDescriptor> properties() const |
871 | { |
872 | slint::SharedVector<PropertyDescriptor> props; |
873 | cbindgen_private::slint_interpreter_component_definition_properties(&inner, &props); |
874 | return props; |
875 | } |
876 | |
877 | /// Returns a vector of strings that describe the list of public callbacks that can be invoked |
878 | /// using ComponentInstance::invoke and set using ComponentInstance::set_callback. |
879 | slint::SharedVector<slint::SharedString> callbacks() const |
880 | { |
881 | slint::SharedVector<slint::SharedString> callbacks; |
882 | cbindgen_private::slint_interpreter_component_definition_callbacks(&inner, &callbacks); |
883 | return callbacks; |
884 | } |
885 | |
886 | /// Returns a vector of strings that describe the list of public functions that can be invoked |
887 | /// using ComponentInstance::invoke. |
888 | slint::SharedVector<slint::SharedString> functions() const |
889 | { |
890 | slint::SharedVector<slint::SharedString> functions; |
891 | cbindgen_private::slint_interpreter_component_definition_functions(&inner, &functions); |
892 | return functions; |
893 | } |
894 | |
895 | /// Returns the name of this Component as written in the .slint file |
896 | slint::SharedString name() const |
897 | { |
898 | slint::SharedString name; |
899 | cbindgen_private::slint_interpreter_component_definition_name(&inner, &name); |
900 | return name; |
901 | } |
902 | |
903 | /// Returns a vector of strings with the names of all exported global singletons. |
904 | slint::SharedVector<slint::SharedString> globals() const |
905 | { |
906 | slint::SharedVector<slint::SharedString> names; |
907 | cbindgen_private::slint_interpreter_component_definition_globals(&inner, &names); |
908 | return names; |
909 | } |
910 | |
911 | /// Returns a vector of the property descriptors of the properties of the specified |
912 | /// publicly exported global singleton. An empty optional is returned if there exists no |
913 | /// exported global singleton under the specified name. |
914 | std::optional<slint::SharedVector<PropertyDescriptor>> |
915 | global_properties(std::string_view global_name) const |
916 | { |
917 | slint::SharedVector<PropertyDescriptor> properties; |
918 | if (cbindgen_private::slint_interpreter_component_definition_global_properties( |
919 | &inner, slint::private_api::string_to_slice(global_name), &properties)) { |
920 | return properties; |
921 | } |
922 | return {}; |
923 | } |
924 | |
925 | /// Returns a vector of the names of the callbacks of the specified publicly exported global |
926 | /// singleton. An empty optional is returned if there exists no exported global singleton |
927 | /// under the specified name. |
928 | std::optional<slint::SharedVector<slint::SharedString>> |
929 | global_callbacks(std::string_view global_name) const |
930 | { |
931 | slint::SharedVector<slint::SharedString> names; |
932 | if (cbindgen_private::slint_interpreter_component_definition_global_callbacks( |
933 | &inner, slint::private_api::string_to_slice(global_name), &names)) { |
934 | return names; |
935 | } |
936 | return {}; |
937 | } |
938 | |
939 | /// Returns a vector of the names of the functions of the specified publicly exported global |
940 | /// singleton. An empty optional is returned if there exists no exported global singleton |
941 | /// under the specified name. |
942 | std::optional<slint::SharedVector<slint::SharedString>> |
943 | global_functions(std::string_view global_name) const |
944 | { |
945 | slint::SharedVector<slint::SharedString> names; |
946 | if (cbindgen_private::slint_interpreter_component_definition_global_functions( |
947 | &inner, slint::private_api::string_to_slice(global_name), &names)) { |
948 | return names; |
949 | } |
950 | return {}; |
951 | } |
952 | }; |
953 | |
954 | inline ComponentDefinition ComponentInstance::definition() const |
955 | { |
956 | cbindgen_private::ComponentDefinitionOpaque result; |
957 | cbindgen_private::slint_interpreter_component_instance_component_definition(inner(), &result); |
958 | return ComponentDefinition(result); |
959 | } |
960 | |
961 | /// ComponentCompiler is the entry point to the Slint interpreter that can be used |
962 | /// to load .slint files or compile them on-the-fly from a string |
963 | /// (using build_from_source()) or from a path (using build_from_source()) |
964 | class ComponentCompiler |
965 | { |
966 | cbindgen_private::ComponentCompilerOpaque inner; |
967 | |
968 | ComponentCompiler(ComponentCompiler &) = delete; |
969 | ComponentCompiler &operator=(ComponentCompiler &) = delete; |
970 | |
971 | public: |
972 | /// Constructs a new ComponentCompiler instance. |
973 | ComponentCompiler() { cbindgen_private::slint_interpreter_component_compiler_new(&inner); } |
974 | |
975 | /// Destroys this ComponentCompiler. |
976 | ~ComponentCompiler() |
977 | { |
978 | cbindgen_private::slint_interpreter_component_compiler_destructor(&inner); |
979 | } |
980 | |
981 | /// Sets the include paths used for looking up `.slint` imports to the specified vector of |
982 | /// paths. |
983 | void set_include_paths(const slint::SharedVector<slint::SharedString> &paths) |
984 | { |
985 | cbindgen_private::slint_interpreter_component_compiler_set_include_paths(&inner, &paths); |
986 | } |
987 | |
988 | /// Sets the style to be used for widgets. |
989 | void set_style(std::string_view style) |
990 | { |
991 | cbindgen_private::slint_interpreter_component_compiler_set_style( |
992 | &inner, slint::private_api::string_to_slice(style)); |
993 | } |
994 | |
995 | /// Returns the widget style the compiler is currently using when compiling .slint files. |
996 | slint::SharedString style() const |
997 | { |
998 | slint::SharedString s; |
999 | cbindgen_private::slint_interpreter_component_compiler_get_style(&inner, &s); |
1000 | return s; |
1001 | } |
1002 | |
1003 | /// Sets the domain used for translations. |
1004 | void set_translation_domain(std::string_view domain) |
1005 | { |
1006 | cbindgen_private::slint_interpreter_component_compiler_set_translation_domain( |
1007 | &inner, slint::private_api::string_to_slice(domain)); |
1008 | } |
1009 | |
1010 | /// Returns the include paths the component compiler is currently configured with. |
1011 | slint::SharedVector<slint::SharedString> include_paths() const |
1012 | { |
1013 | slint::SharedVector<slint::SharedString> paths; |
1014 | cbindgen_private::slint_interpreter_component_compiler_get_include_paths(&inner, &paths); |
1015 | return paths; |
1016 | } |
1017 | |
1018 | /// Returns the diagnostics that were produced in the last call to build_from_path() or |
1019 | /// build_from_source(). |
1020 | slint::SharedVector<Diagnostic> diagnostics() const |
1021 | { |
1022 | slint::SharedVector<Diagnostic> result; |
1023 | cbindgen_private::slint_interpreter_component_compiler_get_diagnostics(&inner, &result); |
1024 | return result; |
1025 | } |
1026 | |
1027 | /// Compile a .slint file into a ComponentDefinition |
1028 | /// |
1029 | /// Returns the compiled `ComponentDefinition` if there were no errors. |
1030 | /// |
1031 | /// Any diagnostics produced during the compilation, such as warnings or errors, are collected |
1032 | /// in this ComponentCompiler and can be retrieved after the call using the diagnostics() |
1033 | /// function. |
1034 | /// |
1035 | /// Diagnostics from previous calls are cleared when calling this function. |
1036 | std::optional<ComponentDefinition> build_from_source(std::string_view source_code, |
1037 | std::string_view path) |
1038 | { |
1039 | cbindgen_private::ComponentDefinitionOpaque result; |
1040 | if (cbindgen_private::slint_interpreter_component_compiler_build_from_source( |
1041 | &inner, slint::private_api::string_to_slice(source_code), |
1042 | slint::private_api::string_to_slice(path), &result)) { |
1043 | |
1044 | return ComponentDefinition(result); |
1045 | } else { |
1046 | return {}; |
1047 | } |
1048 | } |
1049 | |
1050 | /// Compile some .slint code into a ComponentDefinition |
1051 | /// |
1052 | /// The `path` argument will be used for diagnostics and to compute relative |
1053 | /// paths while importing. |
1054 | /// |
1055 | /// Any diagnostics produced during the compilation, such as warnings or errors, are collected |
1056 | /// in this ComponentCompiler and can be retrieved after the call using the |
1057 | /// Self::diagnostics() function. |
1058 | /// |
1059 | /// Diagnostics from previous calls are cleared when calling this function. |
1060 | std::optional<ComponentDefinition> build_from_path(std::string_view path) |
1061 | { |
1062 | cbindgen_private::ComponentDefinitionOpaque result; |
1063 | if (cbindgen_private::slint_interpreter_component_compiler_build_from_path( |
1064 | &inner, slint::private_api::string_to_slice(path), &result)) { |
1065 | |
1066 | return ComponentDefinition(result); |
1067 | } else { |
1068 | return {}; |
1069 | } |
1070 | } |
1071 | }; |
1072 | } |
1073 | |
1074 | namespace slint::private_api::testing { |
1075 | /// Send a key events to the given component instance |
1076 | inline void send_keyboard_string_sequence(const slint::interpreter::ComponentInstance *component, |
1077 | const slint::SharedString &str) |
1078 | { |
1079 | const cbindgen_private::WindowAdapterRcOpaque *win_ptr = nullptr; |
1080 | cbindgen_private::slint_interpreter_component_instance_window( |
1081 | reinterpret_cast<const cbindgen_private::ErasedItemTreeBox *>(component), &win_ptr); |
1082 | cbindgen_private::send_keyboard_string_sequence( |
1083 | sequence: &str, window_adapter: reinterpret_cast<const cbindgen_private::WindowAdapterRc *>(win_ptr)); |
1084 | } |
1085 | } |
1086 | |
1087 | #endif |
1088 | |