/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann SPDX-License-Identifier: GPL-3.0-only This file is also available under commercial licensing terms. Please contact info@sixtyfps.io for more information. LICENSE END */ /*! # SixtyFPS This crate is the main entry point for embedding user interfaces designed with [SixtyFPS UI](https://sixtyfps.io/) in Rust programs. Included in this documentation is also the [language reference](docs::langref), documentation of [builtin elements](docs::builtin_elements), [widgets](docs::widgets) and [layouting](docs::layouting). ## How to use: The user interfaces are described in the `.60` design markup language. There are two ways of including the design in Rust: - The `.60` code is inline in a macro. - The `.60` code in external files compiled with `build.rs` This markup code is translated to Rust code and each component is turned into a Rust struct with functions to instantiated, show or access properties. This documentation includes an [example](docs::generated_code::SampleComponent) of how the API looks like. ### The .60 code in a macro This method combines your Rust code with the `.60` design markup in one file, using a macro: ```rust sixtyfps::sixtyfps!{ HelloWorld := Window { Text { text: "hello world"; color: green; } } } fn main() { # return; // Don't run a window in an example HelloWorld::new().run(); } ``` ### The .60 file in external files compiled with `build.rs` This method allows you to a separate `.60` file on the file system, which works well if your design becomes bigger and you split it up across multiple files. You need to use a so-called [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) to trigger the compilation of the `.60` file. In your Cargo.toml: ```toml [package] ... build = "build.rs" [dependencies] sixtyfps = "0.0.5" ... [build-dependencies] sixtyfps-build = "0.0.5" ``` In the `build.rs` file: ```ignore fn main() { sixtyfps_build::compile("ui/hello.60").unwrap(); } ``` Then in your main file ```ignore sixtyfps::include_modules!(); fn main() { HelloWorld::new().run(); } ``` ### Generated components As of now, only the last component of a .60 source is generated. It is planned to generate all exported components. The component is generated and re-exported at the location of the [`include_modules!`] or [`sixtyfps!`] macro. it consist of a struct of the same name of the component. For example, if you have `export MyComponent := Window { /*...*/ }` in the .60 file, it will create a `struct MyComponent{ /*...*/ }`. This documentation contains a documented generated component: [`docs::generated_code::SampleComponent`]. The following associated function are added to the component: - [`fn new() -> Self`](docs::generated_code::SampleComponent::new): to instantiate the component. - [`fn show(&self)`]()docs::generated_code::SampleComponent::show): to show the window of the component. - [`fn hide(&self)`]()docs::generated_code::SampleComponent::hide): to hide the window of the component. - [`fn run(&self)`]()docs::generated_code::SampleComponent::run): a convenience function that first calls `show()`, followed by spinning the event loop, and `hide()` when returning from the event loop. For each top-level property - A setter [`fn set_(&self, value: )`](docs::generated_code::SampleComponent::set_counter) - A getter [`fn get_(&self) -> `](docs::generated_code::SampleComponent::get_counter) For each top-level callback - [`fn call_(&self)`](docs::generated_code::SampleComponent::call_hello): to emit the callback - [`fn on_(&self, callback: impl Fn() + 'static)`](docs::generated_code::SampleComponent::on_hello): to set the callback handler. After instantiating the component you can call just [`fn run(&self)`] on it, in order to show it and spin the event loop to render and react to input events. If you want to show multiple components simultaneously, then you can also call just `show()` first. When you're ready to enter the event loop, just call [`run_event_loop()`]. ### Type Mappings The types used for properties in `.60` design markup each translate to specific types in Rust. The follow table summarizes the entire mapping: | `.60` Type | Rust Type | Note | | --- | --- | --- | | `int` | `i32` | | | `float` | `f32` | | | `string` | [`SharedString`] | A reference-counted string type that can be easily converted to a str reference. | | `color` | [`Color`] | | | `length` | `f32` | The unit are physical pixels. | | `logical_length` | `f32` | At run-time, logical lengths are automatically translated to physical pixels using the device pixel ratio. | | `duration` | `i64` | At run-time, durations are always represented as signed 64-bit integers with milisecond precision. | | structure | `struct` of the same name | | | array | [`ModelHandle`] | | For user defined structures in the .60, an extra struct is generated. For example, if the `.60` contains ```60 export struct MyStruct := { foo: int, bar: string, } ``` The following struct would be generated: ```rust #[derive(Default, Clone, Debug, PartialEq)] struct MyStruct { foo : i32, bar: sixtyfps::SharedString, } ``` */ #![cfg_attr(nightly, feature(doc_cfg, external_doc))] #![warn(missing_docs)] #![deny(unsafe_code)] pub use sixtyfps_macros::sixtyfps; pub use sixtyfps_corelib::model::{ Model, ModelHandle, ModelNotify, ModelPeer, StandardListViewItem, VecModel, }; pub use sixtyfps_corelib::sharedvector::SharedVector; pub use sixtyfps_corelib::string::SharedString; pub use sixtyfps_corelib::timers::{Timer, TimerMode}; pub use sixtyfps_corelib::{Color, RgbaColor}; /// This function can be used to register a custom TrueType font with SixtyFPS, /// for use with the `font-family` property. The provided slice must be a valid TrueType /// font. pub fn register_application_font_from_memory( data: &'static [u8], ) -> Result<(), Box> { sixtyfps_rendering_backend_default::backend().register_application_font_from_memory(data) } // FIXME: this should not be in this namespace // but the name is `sixtyfps::StateInfo` in builtin.60 #[doc(hidden)] pub use sixtyfps_corelib::properties::StateInfo; /// internal re_exports used by the macro generated #[doc(hidden)] pub mod re_exports { pub use const_field_offset::{self, FieldOffsets, PinnedDrop}; pub use core::iter::FromIterator; pub use once_cell::sync::Lazy; pub use once_cell::unsync::OnceCell; pub use sixtyfps_corelib::animations::EasingCurve; pub use sixtyfps_corelib::callbacks::Callback; pub use sixtyfps_corelib::component::{ init_component_items, Component, ComponentRefPin, ComponentVTable, }; pub use sixtyfps_corelib::graphics::{ PathArcTo, PathCubicTo, PathData, PathElement, PathEvent, PathLineTo, PathMoveTo, PathQuadraticTo, Point, Rect, Size, }; pub use sixtyfps_corelib::input::{ FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyboardModifiers, MouseEvent, }; pub use sixtyfps_corelib::item_tree::{ visit_item_tree, ItemTreeNode, ItemVisitorRefMut, ItemVisitorVTable, TraversalOrder, VisitChildrenResult, }; pub use sixtyfps_corelib::items::*; pub use sixtyfps_corelib::layout::*; pub use sixtyfps_corelib::model::*; pub use sixtyfps_corelib::properties::{set_state_binding, Property, PropertyTracker}; pub use sixtyfps_corelib::slice::Slice; pub use sixtyfps_corelib::window::ComponentWindow; pub use sixtyfps_corelib::Color; pub use sixtyfps_corelib::ComponentVTable_static; pub use sixtyfps_corelib::Resource; pub use sixtyfps_corelib::SharedString; pub use sixtyfps_corelib::SharedVector; pub use sixtyfps_rendering_backend_default::native_widgets::*; pub use vtable::{self, *}; } /// Creates a new window to render components in. #[doc(hidden)] pub fn create_window() -> re_exports::ComponentWindow { sixtyfps_rendering_backend_default::backend().create_window() } /// Enters the main event loop. This is necessary in order to receive /// events from the windowing system in order to render to the screen /// and react to user input. pub fn run_event_loop() { sixtyfps_rendering_backend_default::backend().run_event_loop(); } /// This trait describes the conversion of a strongly referenced SixtyFPS component, /// held by a [vtable::VRc] into a weak reference. pub trait IntoWeak { /// The type of the generated component. #[doc(hidden)] type Inner; /// Returns a new weak pointer. fn as_weak(&self) -> Weak where Self: Sized; /// Internal function used when upgrading a weak reference to a strong one. #[doc(hidden)] fn from_inner(_: vtable::VRc) -> Self; } /// Struct that's used to hold weak references for SixtyFPS components. pub struct Weak { inner: vtable::VWeak, } impl Clone for Weak { fn clone(&self) -> Self { Self { inner: self.inner.clone() } } } impl Weak { #[doc(hidden)] pub fn new(rc: &vtable::VRc) -> Self { Self { inner: vtable::VRc::downgrade(&rc) } } /// Returns a new strongly referenced component if some other instance still /// holds a strong reference. Otherwise, returns None. pub fn upgrade(&self) -> Option where T: IntoWeak, { self.inner.upgrade().map(|inner| T::from_inner(inner)) } /// Convenience function that returns a new stronlyg referenced component if /// some other instance still holds a strong reference. Otherwise, this function /// panics. pub fn unwrap(&self) -> T { self.upgrade().unwrap() } } /// This module contains functions useful for unit tests pub mod testing { use core::cell::Cell; thread_local!(static KEYBOARD_MODIFIERS : Cell = Default::default()); /// This trait gives access to the underyling Window of a component for the /// purposes of testing. pub trait HasWindow { /// Returns a reference to the component's window. fn component_window(&self) -> &super::re_exports::ComponentWindow; } pub use sixtyfps_corelib::tests::sixtyfps_mock_elapsed_time as mock_elapsed_time; /// Simulate a mouse click pub fn send_mouse_click< X: vtable::HasStaticVTable + HasWindow + 'static, Component: Into> + Clone, >( component: &Component, x: f32, y: f32, ) { let rc = component.clone().into(); let dyn_rc = vtable::VRc::into_dyn(rc.clone()); sixtyfps_corelib::tests::sixtyfps_send_mouse_click(&dyn_rc, x, y, rc.component_window()); } /// Simulate a change in keyboard modifiers being pressed pub fn set_current_keyboard_modifiers< X: vtable::HasStaticVTable + HasWindow, Component: Into> + Clone, >( _component: &Component, modifiers: crate::re_exports::KeyboardModifiers, ) { KEYBOARD_MODIFIERS.with(|x| x.set(modifiers)) } /// Simulate entering a sequence of ascii characters key by key. pub fn send_keyboard_string_sequence< X: vtable::HasStaticVTable + HasWindow, Component: Into> + Clone, >( component: &Component, sequence: &str, ) { let component = component.clone().into(); sixtyfps_corelib::tests::send_keyboard_string_sequence( &super::SharedString::from(sequence), KEYBOARD_MODIFIERS.with(|x| x.get()), component.component_window(), ) } /// Applies the specified rectangular constraints to the component's layout. pub fn apply_layout< X: vtable::HasStaticVTable, Component: Into> + Clone, >( component: &Component, rect: sixtyfps_corelib::graphics::Rect, ) { let rc = component.clone().into(); vtable::VRc::borrow_pin(&rc).as_ref().apply_layout(rect); } /// Applies the specified scale factor to the window that's associated with the given component. /// This overrides the value provided by the windowing system. pub fn set_window_scale_factor< X: vtable::HasStaticVTable + HasWindow, Component: Into> + Clone, >( component: &Component, factor: f32, ) { let component = component.clone().into(); component.component_window().set_scale_factor(factor) } } /// Include the code generated with the sixtyfps-build crate from the build script. After calling `sixtyfps_build::compile` /// in your `build.rs` build script, the use of this macro includes the generated Rust code and makes the exported types /// available for you to instantiate. /// /// Check the documentation of the `sixtyfps-build` crate for more information. #[macro_export] macro_rules! include_modules { () => { include!(env!("SIXTYFPS_INCLUDE_GENERATED")); }; } /// Helper type that helps checking that the generated code is generated for the right version #[doc(hidden)] #[allow(non_camel_case_types)] pub struct VersionCheck_0_0_5; #[cfg(doctest)] mod compile_fail_tests; #[cfg(all(doc, nightly))] pub mod docs;