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
17class QWidget;
18# endif
19
20namespace slint::cbindgen_private {
21// This has to stay opaque, but VRc don't compile if it is just forward declared
22struct 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.
39namespace slint::interpreter {
40
41class 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().
52struct Struct
53{
54public:
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
214private:
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/// ```
242class Value
243{
244public:
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
397private:
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
406inline 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
413inline 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}
425inline 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(&notify); }
437
438 void row_added(size_t index, size_t count) override
439 {
440 cbindgen_private::slint_interpreter_model_notify_row_added(&notify, index, count);
441 }
442 void row_changed(size_t index) override
443 {
444 cbindgen_private::slint_interpreter_model_notify_row_changed(&notify, index);
445 }
446 void row_removed(size_t index, size_t count) override
447 {
448 cbindgen_private::slint_interpreter_model_notify_row_removed(&notify, index, count);
449 }
450 void reset() override { cbindgen_private::slint_interpreter_model_notify_reset(&notify); }
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
492inline Struct::Struct(std::initializer_list<std::pair<std::string_view, Value>> args)
493 : Struct(args.begin(), args.end())
494{
495}
496
497inline 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}
511inline 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
520inline 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
537inline Struct::iterator::~iterator()
538{
539 if (v) {
540 cbindgen_private::slint_interpreter_struct_iterator_destructor(&inner);
541 delete v;
542 }
543}
544
545class 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()
555class 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
569public:
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.
824class 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
836public:
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
934inline 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())
944class ComponentCompiler
945{
946 cbindgen_private::ComponentCompilerOpaque inner;
947
948 ComponentCompiler(ComponentCompiler &) = delete;
949 ComponentCompiler &operator=(ComponentCompiler &) = delete;
950
951public:
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
1054namespace slint::testing {
1055
1056using cbindgen_private::KeyboardModifiers;
1057
1058/// Send a key events to the given component instance
1059inline 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
1070inline 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

source code of slint/api/cpp/include/slint-interpreter.h