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