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 | // cSpell: ignore buildrs |
5 | |
6 | /*! |
7 | # Slint |
8 | |
9 | This crate is the main entry point for embedding user interfaces designed with |
10 | [Slint](https://slint.rs/) in Rust programs. |
11 | */ |
12 | #, "/docs/tutorial/rust)" )] |
13 | /*! If you are already familiar with Slint, the following topics provide related information. |
14 | |
15 | ## Topics |
16 | |
17 | */ |
18 | #, "/docs/slint)" )] |
19 | /*! - [Type mappings between .slint and Rust](docs::type_mappings) |
20 | - [Feature flags and backend selection](docs::cargo_features) |
21 | - [Slint on Microcontrollers](docs::mcu) |
22 | |
23 | ## How to use this crate: |
24 | |
25 | Designs of user interfaces are described in the `.slint` design markup language. There are three ways |
26 | of including them in Rust: |
27 | |
28 | - The `.slint` code is [inline in a macro](#the-slint-code-in-a-macro). |
29 | - The `.slint` code in [external files compiled with `build.rs`](#the-slint-code-in-external-files-is-compiled-with-buildrs) |
30 | */ |
31 | #, "/docs/rust/slint_interpreter/)." )] |
32 | /*! |
33 | |
34 | With the first two methods, the markup code is translated to Rust code and each component is turned into a Rust |
35 | struct with functions. Use these functions to instantiate and show the component, and |
36 | to access declared properties. Check out our [sample component](docs::generated_code::SampleComponent) for more |
37 | information about the generation functions and how to use them. |
38 | |
39 | ### The .slint code in a macro |
40 | |
41 | This method combines your Rust code with the `.slint` design markup in one file, using a macro: |
42 | |
43 | ```rust |
44 | slint::slint!{ |
45 | export component HelloWorld { |
46 | Text { |
47 | text: "hello world" ; |
48 | color: green; |
49 | } |
50 | } |
51 | } |
52 | fn main() { |
53 | # return; // Don't run a window in an example |
54 | HelloWorld::new().unwrap().run().unwrap(); |
55 | } |
56 | ``` |
57 | |
58 | ### The .slint code in external files is compiled with `build.rs` |
59 | |
60 | When your design becomes bigger in terms of markup code, you may want move it to a dedicated*/ |
61 | #, "/docs/slint/src/language/syntax/modules.html)." )] |
62 | /*!Use a [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) to compile |
63 | your main `.slint` file: |
64 | |
65 | In your Cargo.toml add a `build` assignment and use the `slint-build` crate in `build-dependencies`: |
66 | |
67 | ```toml |
68 | [package] |
69 | ... |
70 | build = "build.rs" |
71 | edition = "2021" |
72 | |
73 | [dependencies] |
74 | slint = "1.5.0" |
75 | ... |
76 | |
77 | [build-dependencies] |
78 | slint-build = "1.5.0" |
79 | ``` |
80 | |
81 | Use the API of the slint-build crate in the `build.rs` file: |
82 | |
83 | ```rust,no_run |
84 | fn main() { |
85 | slint_build::compile("ui/hello.slint" ).unwrap(); |
86 | } |
87 | ``` |
88 | |
89 | Finally, use the [`include_modules!`] macro in your `main.rs`: |
90 | |
91 | ```ignore |
92 | slint::include_modules!(); |
93 | fn main() { |
94 | HelloWorld::new().unwrap().run().unwrap(); |
95 | } |
96 | ``` |
97 | |
98 | The [cargo-generate](https://github.com/cargo-generate/cargo-generate) tool is a great tool to up and running quickly with a new |
99 | Rust project. You can use it in combination with our [Template Repository](https://github.com/slint-ui/slint-rust-template) to |
100 | create a skeleton file hierarchy that uses this method: |
101 | |
102 | ```bash |
103 | cargo install cargo-generate |
104 | cargo generate --git https://github.com/slint-ui/slint-rust-template |
105 | ``` |
106 | |
107 | ## Generated components |
108 | |
109 | Currently, only the last component in a `.slint` source file is mapped to a Rust structure that be instantiated. We are tracking the |
110 | resolution of this limitation in <https://github.com/slint-ui/slint/issues/784>. |
111 | |
112 | The component is generated and re-exported to the location of the [`include_modules!`] or [`slint!`] macro. It is represented |
113 | as a struct with the same name as the component. |
114 | |
115 | For example, if you have |
116 | |
117 | ```slint,no-preview |
118 | export component MyComponent inherits Window { /*...*/ } |
119 | ``` |
120 | |
121 | in the .slint file, it will create a |
122 | ```rust |
123 | struct MyComponent{ /*...*/ } |
124 | ``` |
125 | |
126 | See also our [sample component](docs::generated_code::SampleComponent) for more information about the API of the generated struct. |
127 | |
128 | A component is instantiated using the [`fn new() -> Self`](docs::generated_code::SampleComponent::new) function. The following |
129 | convenience functions are available through the [`ComponentHandle`] implementation: |
130 | |
131 | - [`fn clone_strong(&self) -> Self`](docs::generated_code::SampleComponent::clone_strong): creates a strongly referenced clone of the component instance. |
132 | - [`fn as_weak(&self) -> Weak`](docs::generated_code::SampleComponent::as_weak): to create a [weak](Weak) reference to the component instance. |
133 | - [`fn show(&self)`](docs::generated_code::SampleComponent::show): to show the window of the component. |
134 | - [`fn hide(&self)`](docs::generated_code::SampleComponent::hide): to hide the window of the component. |
135 | - [`fn run(&self)`](docs::generated_code::SampleComponent::run): a convenience function that first calls `show()`, |
136 | followed by spinning the event loop, and `hide()` when returning from the event loop. |
137 | - [`fn global<T: Global<Self>>(&self) -> T`](docs::generated_code::SampleComponent::global): an accessor to the global singletons, |
138 | |
139 | For each top-level property |
140 | - A setter [`fn set_<property_name>(&self, value: <PropertyType>)`](docs::generated_code::SampleComponent::set_counter) |
141 | - A getter [`fn get_<property_name>(&self) -> <PropertyType>`](docs::generated_code::SampleComponent::get_counter) |
142 | |
143 | For each top-level callback |
144 | - [`fn invoke_<callback_name>(&self)`](docs::generated_code::SampleComponent::invoke_hello): to invoke the callback |
145 | - [`fn on_<callback_name>(&self, callback: impl Fn(<CallbackArgs>) + 'static)`](docs::generated_code::SampleComponent::on_hello): to set the callback handler. |
146 | |
147 | Note: All dashes (`-`) are replaced by underscores (`_`) in names of types or functions. |
148 | |
149 | After instantiating the component, call [`ComponentHandle::run()`] on show it on the screen and spin the event loop to |
150 | react to input events. To show multiple components simultaneously, call [`ComponentHandle::show()`] on each instance. |
151 | Call [`run_event_loop()`] when you're ready to enter the event loop. |
152 | |
153 | The generated component struct acts as a handle holding a strong reference (similar to an `Rc`). The `Clone` trait is |
154 | not implemented. Instead you need to make explicit [`ComponentHandle::clone_strong`] and [`ComponentHandle::as_weak`] |
155 | calls. A strong reference should not be captured by the closures given to a callback, as this would produce a reference |
156 | loop and leak the component. Instead, the callback function should capture a weak component. |
157 | |
158 | ## Threading and Event-loop |
159 | |
160 | For platform-specific reasons, the event loop must run in the main thread, in most backends, and all the components |
161 | must be created in the same thread as the thread the event loop is running or is going to run. |
162 | |
163 | You should perform the minimum amount of work in the main thread and delegate the actual logic to another |
164 | thread to avoid blocking animations. Use the [`invoke_from_event_loop`] function to communicate from your worker thread to the UI thread. |
165 | |
166 | To run a function with a delay or with an interval use a [`Timer`]. |
167 | |
168 | To run an async function or a future, use [`spawn_local()`]. |
169 | |
170 | ## Exported Global singletons |
171 | |
172 | */ |
173 | #, "/docs/slint/src/language/syntax/globals.html) from the main file," )] |
174 | /*! it is also generated with the exported name. Like the main component, the generated struct have |
175 | inherent method to access the properties and callback: |
176 | |
177 | For each property |
178 | - A setter: `fn set_<property_name>(&self, value: <PropertyType>)` |
179 | - A getter: `fn get_<property_name>(&self) -> <PropertyType>` |
180 | |
181 | For each callback |
182 | - `fn invoke_<callback_name>(&self, <CallbackArgs>) -> <ReturnValue>` to invoke the callback |
183 | - `fn on_<callback_name>(&self, callback: impl Fn(<CallbackArgs>) + 'static)` to set the callback handler. |
184 | |
185 | The global can be accessed with the [`ComponentHandle::global()`] function, or with [`Global::get()`] |
186 | |
187 | See the [documentation of the `Global` trait](Global) for an example. |
188 | */ |
189 | |
190 | #![warn (missing_docs)] |
191 | #![deny (unsafe_code)] |
192 | #![doc (html_logo_url = "https://slint.dev/logo/slint-logo-square-light.svg" )] |
193 | #![cfg_attr (not(feature = "std" ), no_std)] |
194 | #![allow (clippy::needless_doctest_main)] // We document how to write a main function |
195 | |
196 | extern crate alloc; |
197 | |
198 | #[cfg (not(feature = "compat-1-2" ))] |
199 | compile_error!( |
200 | "The feature `compat-1-2` must be enabled to ensure \ |
201 | forward compatibility with future version of this crate" |
202 | ); |
203 | |
204 | pub use slint_macros::slint; |
205 | |
206 | pub use i_slint_core::api::*; |
207 | #[doc (hidden)] |
208 | #[deprecated (note = "Experimental type was made public by mistake" )] |
209 | pub use i_slint_core::component_factory::ComponentFactory; |
210 | #[cfg (not(target_arch = "wasm32" ))] |
211 | pub use i_slint_core::graphics::{BorrowedOpenGLTextureBuilder, BorrowedOpenGLTextureOrigin}; |
212 | // keep in sync with internal/interpreter/api.rs |
213 | pub use i_slint_core::graphics::{ |
214 | Brush, Color, Image, LoadImageError, Rgb8Pixel, Rgba8Pixel, RgbaColor, SharedPixelBuffer, |
215 | }; |
216 | pub use i_slint_core::model::{ |
217 | FilterModel, MapModel, Model, ModelExt, ModelNotify, ModelPeer, ModelRc, ModelTracker, |
218 | ReverseModel, SortModel, StandardListViewItem, TableColumn, VecModel, |
219 | }; |
220 | pub use i_slint_core::sharedvector::SharedVector; |
221 | pub use i_slint_core::timers::{Timer, TimerMode}; |
222 | pub use i_slint_core::{format, string::SharedString}; |
223 | |
224 | pub mod private_unstable_api; |
225 | |
226 | /// Enters the main event loop. This is necessary in order to receive |
227 | /// events from the windowing system for rendering to the screen |
228 | /// and reacting to user input. |
229 | /// This function will run until the last window is closed or until |
230 | /// [`quit_event_loop()`] is called. |
231 | /// |
232 | /// See also [`run_event_loop_until_quit()`] to keep the event loop running until |
233 | /// [`quit_event_loop()`] is called, even if all windows are closed. |
234 | pub fn run_event_loop() -> Result<(), PlatformError> { |
235 | i_slint_backend_selector::with_platform(|b: &dyn Platform| b.run_event_loop()) |
236 | } |
237 | |
238 | /// Similar to [`run_event_loop()`], but this function enters the main event loop |
239 | /// and continues to run even when the last window is closed, until |
240 | /// [`quit_event_loop()`] is called. |
241 | /// |
242 | /// This is useful for system tray applications where the application needs to stay alive |
243 | /// even if no windows are visible. |
244 | pub fn run_event_loop_until_quit() -> Result<(), PlatformError> { |
245 | i_slint_backend_selector::with_platform(|b: &dyn Platform| { |
246 | #[allow (deprecated)] |
247 | b.set_event_loop_quit_on_last_window_closed(false); |
248 | b.run_event_loop() |
249 | }) |
250 | } |
251 | |
252 | /// Include the code generated with the slint-build crate from the build script. After calling `slint_build::compile` |
253 | /// in your `build.rs` build script, the use of this macro includes the generated Rust code and makes the exported types |
254 | /// available for you to instantiate. |
255 | /// |
256 | /// Check the documentation of the `slint-build` crate for more information. |
257 | #[macro_export ] |
258 | macro_rules! include_modules { |
259 | () => { |
260 | include!(env!("SLINT_INCLUDE_GENERATED" )); |
261 | }; |
262 | } |
263 | |
264 | /// Initialize translations when using the `gettext` feature. |
265 | /// |
266 | /// Call this in your main function with the path where translations are located. |
267 | /// This macro internally calls the [`bindtextdomain`](https://man7.org/linux/man-pages/man3/bindtextdomain.3.html) function from gettext. |
268 | /// |
269 | /// The first argument of the macro must be an expression that implements `Into<std::path::PathBuf>`. |
270 | /// It specifies the directory in which gettext should search for translations. |
271 | /// |
272 | /// Translations are expected to be found at `<dirname>/<locale>/LC_MESSAGES/<crate>.mo`, |
273 | /// where `dirname` is the directory passed as an argument to this macro, |
274 | /// `locale` is a locale name (e.g., `en`, `en_GB`, `fr`), and |
275 | /// `crate` is the package name obtained from the `CARGO_PKG_NAME` environment variable. |
276 | /// |
277 | /// ### Example |
278 | /// ```rust |
279 | /// fn main() { |
280 | /// slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR" ), "/translations/" )); |
281 | /// // ... |
282 | /// } |
283 | /// ``` |
284 | /// |
285 | /// For example, assuming this is in a crate called `example` and the default locale |
286 | /// is configured to be French, it will load translations at runtime from |
287 | /// `/path/to/example/translations/fr/LC_MESSAGES/example.mo`. |
288 | /// |
289 | /// Another example of loading translations relative to the executable: |
290 | /// ```rust |
291 | /// slint::init_translations!(std::env::current_exe().unwrap().parent().unwrap().join("translations" )); |
292 | /// ``` |
293 | #[cfg (feature = "gettext" )] |
294 | #[macro_export ] |
295 | macro_rules! init_translations { |
296 | ($dirname:expr) => { |
297 | $crate::private_unstable_api::init_translations(env!("CARGO_PKG_NAME" ), $dirname); |
298 | }; |
299 | } |
300 | |
301 | /// This module contains items that you need to use or implement if you want use Slint in an environment without |
302 | /// one of the supplied platform backends such as qt or winit. |
303 | /// |
304 | /// The primary interface is the [`platform::Platform`] trait. Pass your implementation of it to Slint by calling |
305 | /// [`platform::set_platform()`] early on in your application, before creating any Slint components. |
306 | /// |
307 | /// The [Slint on Microcontrollers](crate::docs::mcu) documentation has additional examples. |
308 | pub mod platform { |
309 | pub use i_slint_core::platform::*; |
310 | |
311 | /// This module contains the [`femtovg_renderer::FemtoVGRenderer`] and related types. |
312 | /// |
313 | /// It is only enabled when the `renderer-femtovg` Slint feature is enabled. |
314 | #[cfg (all(feature = "renderer-femtovg" , not(target_os = "android" )))] |
315 | pub mod femtovg_renderer { |
316 | pub use i_slint_renderer_femtovg::FemtoVGRenderer; |
317 | pub use i_slint_renderer_femtovg::OpenGLInterface; |
318 | } |
319 | } |
320 | |
321 | #[cfg (any(doc, all(target_os = "android" , feature = "backend-android-activity-05" )))] |
322 | pub mod android; |
323 | |
324 | /// Helper type that helps checking that the generated code is generated for the right version |
325 | #[doc (hidden)] |
326 | #[allow (non_camel_case_types)] |
327 | pub struct VersionCheck_1_5_1; |
328 | |
329 | #[cfg (doctest)] |
330 | mod compile_fail_tests; |
331 | |
332 | #[cfg (doc)] |
333 | pub mod docs; |
334 | |