From b88bba972841a8114793f3b97504ed73dfe61916 Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Tue, 11 Feb 2025 10:22:00 +0100 Subject: [PATCH 01/11] usecases demo: prepare pr --- Cargo.toml | 1 + demos/usecases/cpp/.gitignore | 1 + demos/usecases/cpp/CMakeLists.txt | 15 + demos/usecases/cpp/main.cpp | 81 +++++ demos/usecases/esp-idf/.gitignore | 6 + demos/usecases/esp-idf/CMakeLists.txt | 15 + demos/usecases/esp-idf/README.md | 6 + demos/usecases/esp-idf/main/CMakeLists.txt | 12 + demos/usecases/esp-idf/main/idf_component.yml | 7 + demos/usecases/esp-idf/main/main.cpp | 50 +++ demos/usecases/esp-idf/rust-toolchain.toml | 5 + demos/usecases/esp-idf/sdkconfig.defaults | 80 +++++ demos/usecases/rust/Cargo.toml | 34 +++ demos/usecases/rust/build.rs | 12 + demos/usecases/rust/src/lib.rs | 98 ++++++ demos/usecases/rust/src/main.rs | 9 + demos/usecases/ui/app.slint | 33 ++ demos/usecases/ui/assets.slint | 21 ++ demos/usecases/ui/assets/archive.svg | 12 + demos/usecases/ui/assets/cloud.svg | 12 + demos/usecases/ui/assets/document.svg | 11 + demos/usecases/ui/assets/forward.svg | 10 + demos/usecases/ui/assets/inbox.svg | 12 + demos/usecases/ui/assets/junk.svg | 10 + demos/usecases/ui/assets/message.svg | 10 + demos/usecases/ui/assets/reply.svg | 11 + demos/usecases/ui/assets/search.svg | 12 + demos/usecases/ui/assets/send.svg | 10 + demos/usecases/ui/assets/trash.svg | 11 + demos/usecases/ui/assets/updates.svg | 12 + demos/usecases/ui/assets/users.svg | 4 + demos/usecases/ui/views.slint | 11 + demos/usecases/ui/views/dashboard_view.slint | 146 +++++++++ demos/usecases/ui/views/header_view.slint | 29 ++ demos/usecases/ui/views/mail_view.slint | 286 ++++++++++++++++++ demos/usecases/ui/views/main_view.slint | 40 +++ demos/usecases/ui/widgets.slint | 35 +++ demos/usecases/ui/widgets/bar_chart.slint | 73 +++++ .../usecases/ui/widgets/card_list_view.slint | 91 ++++++ demos/usecases/ui/widgets/container.slint | 43 +++ .../ui/widgets/extended_line_edit.slint | 178 +++++++++++ demos/usecases/ui/widgets/icon.slint | 9 + demos/usecases/ui/widgets/icon_button.slint | 26 ++ .../ui/widgets/navigation_list_view.slint | 90 ++++++ demos/usecases/ui/widgets/segmented.slint | 4 + demos/usecases/ui/widgets/styling.slint | 39 +++ demos/usecases/ui/widgets/tile.slint | 191 ++++++++++++ demos/usecases/ui/widgets/title_text.slint | 11 + demos/usecases/ui/widgets/value_display.slint | 113 +++++++ .../ui/assets/expand-more.svg | 3 + examples/virtual_keyboard/ui/icons.slint | 3 +- .../ui/virtual_keyboard.slint | 12 +- 52 files changed, 2054 insertions(+), 2 deletions(-) create mode 100644 demos/usecases/cpp/.gitignore create mode 100644 demos/usecases/cpp/CMakeLists.txt create mode 100644 demos/usecases/cpp/main.cpp create mode 100644 demos/usecases/esp-idf/.gitignore create mode 100644 demos/usecases/esp-idf/CMakeLists.txt create mode 100644 demos/usecases/esp-idf/README.md create mode 100644 demos/usecases/esp-idf/main/CMakeLists.txt create mode 100644 demos/usecases/esp-idf/main/idf_component.yml create mode 100644 demos/usecases/esp-idf/main/main.cpp create mode 100644 demos/usecases/esp-idf/rust-toolchain.toml create mode 100644 demos/usecases/esp-idf/sdkconfig.defaults create mode 100644 demos/usecases/rust/Cargo.toml create mode 100644 demos/usecases/rust/build.rs create mode 100644 demos/usecases/rust/src/lib.rs create mode 100644 demos/usecases/rust/src/main.rs create mode 100644 demos/usecases/ui/app.slint create mode 100644 demos/usecases/ui/assets.slint create mode 100644 demos/usecases/ui/assets/archive.svg create mode 100644 demos/usecases/ui/assets/cloud.svg create mode 100644 demos/usecases/ui/assets/document.svg create mode 100644 demos/usecases/ui/assets/forward.svg create mode 100644 demos/usecases/ui/assets/inbox.svg create mode 100644 demos/usecases/ui/assets/junk.svg create mode 100644 demos/usecases/ui/assets/message.svg create mode 100644 demos/usecases/ui/assets/reply.svg create mode 100644 demos/usecases/ui/assets/search.svg create mode 100644 demos/usecases/ui/assets/send.svg create mode 100644 demos/usecases/ui/assets/trash.svg create mode 100644 demos/usecases/ui/assets/updates.svg create mode 100644 demos/usecases/ui/assets/users.svg create mode 100644 demos/usecases/ui/views.slint create mode 100644 demos/usecases/ui/views/dashboard_view.slint create mode 100644 demos/usecases/ui/views/header_view.slint create mode 100644 demos/usecases/ui/views/mail_view.slint create mode 100644 demos/usecases/ui/views/main_view.slint create mode 100644 demos/usecases/ui/widgets.slint create mode 100644 demos/usecases/ui/widgets/bar_chart.slint create mode 100644 demos/usecases/ui/widgets/card_list_view.slint create mode 100644 demos/usecases/ui/widgets/container.slint create mode 100644 demos/usecases/ui/widgets/extended_line_edit.slint create mode 100644 demos/usecases/ui/widgets/icon.slint create mode 100644 demos/usecases/ui/widgets/icon_button.slint create mode 100644 demos/usecases/ui/widgets/navigation_list_view.slint create mode 100644 demos/usecases/ui/widgets/segmented.slint create mode 100644 demos/usecases/ui/widgets/styling.slint create mode 100644 demos/usecases/ui/widgets/tile.slint create mode 100644 demos/usecases/ui/widgets/title_text.slint create mode 100644 demos/usecases/ui/widgets/value_display.slint create mode 100644 examples/virtual_keyboard/ui/assets/expand-more.svg diff --git a/Cargo.toml b/Cargo.toml index 26dedb4dfd9..4a77e4c75c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ 'examples/mcu-board-support', 'examples/uefi-demo', 'demos/weather-demo', + 'demos/usecases/rust', 'helper_crates/const-field-offset', 'helper_crates/vtable', 'helper_crates/vtable/macro', diff --git a/demos/usecases/cpp/.gitignore b/demos/usecases/cpp/.gitignore new file mode 100644 index 00000000000..378eac25d31 --- /dev/null +++ b/demos/usecases/cpp/.gitignore @@ -0,0 +1 @@ +build diff --git a/demos/usecases/cpp/CMakeLists.txt b/demos/usecases/cpp/CMakeLists.txt new file mode 100644 index 00000000000..23842bb294c --- /dev/null +++ b/demos/usecases/cpp/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.21) +project(slint_cpp_usecases LANGUAGES CXX) + +if (NOT TARGET Slint::Slint) + find_package(Slint REQUIRED) +endif() + +set(SLINT_STYLE "cosmic-light" CACHE STRING "Style for demo" FORCE) + +add_executable(usecases main.cpp) +target_link_libraries(usecases PRIVATE Slint::Slint) +slint_target_sources(usecases ../ui/app.slint) diff --git a/demos/usecases/cpp/main.cpp b/demos/usecases/cpp/main.cpp new file mode 100644 index 00000000000..faa2858f063 --- /dev/null +++ b/demos/usecases/cpp/main.cpp @@ -0,0 +1,81 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +#include "app.h" + +void init_virtual_keyboard(slint::ComponentHandle app) +{ + app->global().on_key_pressed([=](auto key) { + app->window().dispatch_key_press_event(key); + app->window().dispatch_key_release_event(key); + }); +} + +int main() +{ + auto app = App::create(); + + init_virtual_keyboard(app); + + auto mails = std::make_shared>(std::vector { + CardListViewItem { "Simon Hausmann", "1 hour ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Tobias Hunger", "1 day ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Olivier Goffart", "2 hour ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Aurindam Jana", "5 hour ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Simon Hausmann", "7 hour ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Tobias Hunger", "1 day ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Olivier Goffart", "8 hour ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + CardListViewItem { "Aurindam Jana", "9 hour ago", "Meeting tomorrow", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat." }, + }); + + app->global().set_mails(mails); + + app->global().on_search_text_changed( + [mails, app = slint::ComponentWeakHandle(app)](const slint::SharedString &text) { + auto app_lock = app.lock(); + + std::string text_str(text.data()); + + (*app_lock)->global().set_mails( + std::make_shared>( + mails, + [text_str](auto e) { + std::string title_str(e.title.data()); + return title_str.find(text_str) != std::string::npos; + })); + }); + + app->run(); +} diff --git a/demos/usecases/esp-idf/.gitignore b/demos/usecases/esp-idf/.gitignore new file mode 100644 index 00000000000..97b051f8b7f --- /dev/null +++ b/demos/usecases/esp-idf/.gitignore @@ -0,0 +1,6 @@ +build +managed_components +sdkconfig +sdkconfig.old +dependencies.lock +.cache diff --git a/demos/usecases/esp-idf/CMakeLists.txt b/demos/usecases/esp-idf/CMakeLists.txt new file mode 100644 index 00000000000..5064f79a372 --- /dev/null +++ b/demos/usecases/esp-idf/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.14) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(SLINT_ESP_LOCAL_EXAMPLE ON) +set(SLINT_FEATURE_EXPERIMENTAL ON) +set(EXTRA_COMPONENT_DIRS ../../../api/cpp/esp-idf/) +set(SLINT_STYLE "cosmic-light" CACHE STRING "Style for demo" FORCE) + +add_compile_options(-fdiagnostics-color=always) + +project(slint_esp_usecases_mcu LANGUAGES CXX) diff --git a/demos/usecases/esp-idf/README.md b/demos/usecases/esp-idf/README.md new file mode 100644 index 00000000000..bc349526afd --- /dev/null +++ b/demos/usecases/esp-idf/README.md @@ -0,0 +1,6 @@ +# Building + +``` +cd examples/usecases/esp-idf +SLINT_SCALE_FACTOR=2 idf.py flash monitor +``` diff --git a/demos/usecases/esp-idf/main/CMakeLists.txt b/demos/usecases/esp-idf/main/CMakeLists.txt new file mode 100644 index 00000000000..961cd3efe4e --- /dev/null +++ b/demos/usecases/esp-idf/main/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: MIT + + +idf_component_register( + SRCS "main.cpp" + INCLUDE_DIRS "." + REQUIRES esp32_p4_function_ev_board_noglib slint +) + +slint_target_sources(${COMPONENT_LIB} ../../ui/app.slint) +target_link_options(${COMPONENT_LIB} PUBLIC -Wl,--allow-multiple-definition) diff --git a/demos/usecases/esp-idf/main/idf_component.yml b/demos/usecases/esp-idf/main/idf_component.yml new file mode 100644 index 00000000000..60a091c9c2a --- /dev/null +++ b/demos/usecases/esp-idf/main/idf_component.yml @@ -0,0 +1,7 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: MIT + +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.1" + espressif/esp32_p4_function_ev_board_noglib: "^1.0.0" diff --git a/demos/usecases/esp-idf/main/main.cpp b/demos/usecases/esp-idf/main/main.cpp new file mode 100644 index 00000000000..14f66b1ac9c --- /dev/null +++ b/demos/usecases/esp-idf/main/main.cpp @@ -0,0 +1,50 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +#include "../../cpp/main.cpp" + +#include "slint-esp.h" +#include +#include +#include "esp_log.h" + +#include + +#include +#include +#include +#include + +#undef BSP_LCD_H_RES +#define BSP_LCD_H_RES 800 +#undef BSP_LCD_V_RES +#define BSP_LCD_V_RES 1280 + + + +extern "C" void app_main(void) +{ + + /* Initialize I2C (for touch and audio) */ + bsp_i2c_init(); + + /* Initialize display */ + esp_lcd_panel_handle_t panel_handle = NULL; + bsp_lcd_handles_t handles{}; + + bsp_display_new_with_handles(nullptr, &handles); + + esp_lcd_touch_handle_t touch_handle = NULL; + const bsp_touch_config_t bsp_touch_cfg = {}; + bsp_touch_new(&bsp_touch_cfg, &touch_handle); + + panel_handle = handles.panel; + + /* Set display brightness to 100% */ + bsp_display_backlight_on(); + + slint_esp_init(slint::PhysicalSize({ BSP_LCD_H_RES, BSP_LCD_V_RES }), panel_handle, + touch_handle); + + main(); +} diff --git a/demos/usecases/esp-idf/rust-toolchain.toml b/demos/usecases/esp-idf/rust-toolchain.toml new file mode 100644 index 00000000000..de3554c8d0d --- /dev/null +++ b/demos/usecases/esp-idf/rust-toolchain.toml @@ -0,0 +1,5 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: MIT + +[toolchain] +channel = "nightly" diff --git a/demos/usecases/esp-idf/sdkconfig.defaults b/demos/usecases/esp-idf/sdkconfig.defaults new file mode 100644 index 00000000000..07ca8f9a629 --- /dev/null +++ b/demos/usecases/esp-idf/sdkconfig.defaults @@ -0,0 +1,80 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32p4" + +# CONFIG_SPIRAM=y +# CONFIG_SPIRAM_MODE_HEX=y +# CONFIG_SPIRAM_SPEED_200M=y +# CONFIG_IDF_EXPERIMENTAL_FEATURES=y +# +# CONFIG_LV_CONF_SKIP=y +# +# #CLIB default +# CONFIG_LV_USE_CLIB_MALLOC=y +# CONFIG_LV_USE_CLIB_SPRINTF=y +# CONFIG_LV_USE_CLIB_STRING=y +# +# # Performance monitor +# CONFIG_LV_USE_OBSERVER=y +# CONFIG_LV_USE_SYSMON=y +# CONFIG_LV_USE_PERF_MONITOR=y +# +# +# # CONFIG_LV_BUILD_EXAMPLES is not set +# +CONFIG_MAIN_TASK_STACK_SIZE=20584 +# #CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +# +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + + + +CONFIG_IDF_TARGET="esp32p4" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +#CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_BSP_LCD_RGB_BUFFER_NUMS=2 +CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE=y +CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR=y +CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE=y +CONFIG_LV_FONT_MONTSERRAT_12=y +CONFIG_LV_FONT_MONTSERRAT_16=y +CONFIG_LV_FONT_MONTSERRAT_24=y +CONFIG_LV_USE_DEMO_WIDGETS=y +CONFIG_LV_USE_DEMO_BENCHMARK=y +CONFIG_LV_USE_DEMO_STRESS=y +CONFIG_LV_USE_DEMO_MUSIC=y +CONFIG_LV_DEMO_MUSIC_AUTO_PLAY=y +CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y +CONFIG_LV_DISP_DEF_REFR_PERIOD=10 + +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_HEX=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +## LVGL8 ## +CONFIG_LV_MEM_SIZE_KILOBYTES=48 +CONFIG_LV_USE_PERF_MONITOR=y + +## LVGL9 ## +CONFIG_LV_CONF_SKIP=y + +#CLIB default +CONFIG_LV_USE_CLIB_MALLOC=y +CONFIG_LV_USE_CLIB_SPRINTF=y +CONFIG_LV_USE_CLIB_STRING=y + +# Performance monitor +#CONFIG_LV_USE_OBSERVER=y +#CONFIG_LV_USE_SYSMON=y +#CONFIG_LV_USE_PERF_MONITOR=y diff --git a/demos/usecases/rust/Cargo.toml b/demos/usecases/rust/Cargo.toml new file mode 100644 index 00000000000..f852a0f0212 --- /dev/null +++ b/demos/usecases/rust/Cargo.toml @@ -0,0 +1,34 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: MIT + +[package] +name = "usecases" +version = "1.10.0" +authors = ["Slint Developers "] +edition = "2021" +build = "build.rs" +publish = false +license = "MIT" + +[lib] +crate-type = ["lib", "cdylib"] +path = "src/lib.rs" +name = "usecases_lib" + +[[bin]] +path = "src/main.rs" +name = "usecases" + +[dependencies] +slint = { path = "../../../api/rs/slint", features = ["serde"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = { version = "0.2" } +console_error_panic_hook = "0.1.5" + +[build-dependencies] +slint-build = { path = "../../../api/rs/build" } + + diff --git a/demos/usecases/rust/build.rs b/demos/usecases/rust/build.rs new file mode 100644 index 00000000000..3f10c9e7640 --- /dev/null +++ b/demos/usecases/rust/build.rs @@ -0,0 +1,12 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +use slint_build::CompilerConfiguration; + +fn main() { + slint_build::compile_with_config( + "../ui/app.slint", + CompilerConfiguration::new().with_style("cosmic".into()), + ) + .unwrap(); +} diff --git a/demos/usecases/rust/src/lib.rs b/demos/usecases/rust/src/lib.rs new file mode 100644 index 00000000000..4ac062f02cc --- /dev/null +++ b/demos/usecases/rust/src/lib.rs @@ -0,0 +1,98 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; + +slint::include_modules!(); + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] +pub fn main() { + // This provides better error messages in debug mode. + // It's disabled in release mode so it doesn't bloat up the file size. + #[cfg(all(debug_assertions, target_arch = "wasm32"))] + console_error_panic_hook::set_once(); + + let app = App::new().unwrap(); + + virtual_keyboard::init(&app); + data::init(&app); + + app.run().unwrap(); +} + +mod virtual_keyboard { + use super::*; + use slint::*; + + pub fn init(app: &App) { + let weak = app.as_weak(); + app.global::().on_key_pressed({ + move |key| { + weak.unwrap() + .window() + .dispatch_event(slint::platform::WindowEvent::KeyPressed { text: key.clone() }); + weak.unwrap() + .window() + .dispatch_event(slint::platform::WindowEvent::KeyReleased { text: key }); + } + }); + } +} + +mod data { + use super::*; + use slint::*; + + pub fn init(app: &App) { + let mail_box_adapter = MailBoxViewAdapter::get(app); + + let mails = VecModel::from_slice(&[ + + CardListViewItem{ + title: "Simon Hausmann".into(), + note: "1 hour ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".into() + }, +// CardListViewItem { title: "Tobias Hunger".into(), note: "1 day ago".into(), sub_title: "Meeting tomorrow".into(), caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".into() }, +// CardListViewItem { +// title: "Olivier Goffart".into(), +// note: "2 hour ago".into(), +// sub_title: "Meeting tomorrow".into(), +// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".into() +// }, +// CardListViewItem { +// title: "Aurindam Jana".into(), +// note: "5 hour ago".into(), +// sub_title: "Meeting tomorrow".into(), +// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." +// .into() +// }, +// CardListViewItem { +// title: "Simon Hausmann".into(), +// note: "7 hour ago".into(), +// sub_title: "Meeting tomorrow".into(), +// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." +// .into() +// }, +// CardListViewItem { title: "Tobias Hunger".into(), note: "1 day ago".into(), sub_title: "Meeting tomorrow".into(), caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."into() }, +// CardListViewItem { +// title: "Olivier Goffart".into(), +// note: "8 hour ago".into(), +// sub_title: "Meeting tomorrow".into(), +// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." +// .into() +// }, +// CardListViewItem { +// title: "Aurindam Jana".into(), +// note: "9 hour ago".into(), +// sub_title: "Meeting tomorrow".into(), +// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." +// .into() +// } + ]); + + mail_box_adapter.set_mails(mails.into()); + } +} diff --git a/demos/usecases/rust/src/main.rs b/demos/usecases/rust/src/main.rs new file mode 100644 index 00000000000..60ff4cb8cc5 --- /dev/null +++ b/demos/usecases/rust/src/main.rs @@ -0,0 +1,9 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +// In order to be compatible with both desktop, wasm, and android, the example is both a binary and a library. +// Just forward to the library in main + +fn main() { + usecases_lib::main(); +} diff --git a/demos/usecases/ui/app.slint b/demos/usecases/ui/app.slint new file mode 100644 index 00000000000..80e5a872923 --- /dev/null +++ b/demos/usecases/ui/app.slint @@ -0,0 +1,33 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { MainView, MainViewAdapter, MailViewAdapter, MailBoxViewAdapter, DashboardViewAdapter } from "views.slint"; +export { MainViewAdapter, MailViewAdapter, MailBoxViewAdapter, DashboardViewAdapter } + +import { CardListViewItem } from "widgets.slint"; +export { CardListViewItem } + +import { VirtualKeyboardHandler, VirtualKeyboard } from "../../../examples/virtual_keyboard/ui/virtual_keyboard.slint"; + + +export { VirtualKeyboardHandler } + + +export component App inherits Window { + preferred-width: 800px; + preferred-height: 1280px; + title: "Slint usecases"; + + main-view := MainView { + width: 100%; + height: 100%; + } + + VirtualKeyboard { + y: TextInputInterface.text-input-focused ? parent.height - self.height : parent.height; + + close => { + main-view.focus(); + } + } +} diff --git a/demos/usecases/ui/assets.slint b/demos/usecases/ui/assets.slint new file mode 100644 index 00000000000..21a361b1b5b --- /dev/null +++ b/demos/usecases/ui/assets.slint @@ -0,0 +1,21 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; + +export global Icons { + out property archive: @image-url("assets/archive.svg"); + out property inbox: @image-url("assets/inbox.svg"); + out property cloud: @image-url("assets/cloud.svg"); + out property document: @image-url("assets/document.svg"); + out property forward: @image-url("assets/forward.svg"); + out property junk: @image-url("assets/junk.svg"); + out property message: @image-url("assets/message.svg"); + out property reply: @image-url("assets/reply.svg"); + out property search: @image-url("assets/search.svg"); + out property send: @image-url("assets/send.svg"); + out property trash: @image-url("assets/trash.svg"); + out property updates: @image-url("assets/updates.svg"); + out property useres: @image-url("assets/users.svg"); + out property slint-logo: Palette.color-scheme == ColorScheme.dark ? @image-url("../../../logo/slint-logo-simple-dark.png") : @image-url("../../../logo/slint-logo-simple-light.png"); +} \ No newline at end of file diff --git a/demos/usecases/ui/assets/archive.svg b/demos/usecases/ui/assets/archive.svg new file mode 100644 index 00000000000..6a9c3344100 --- /dev/null +++ b/demos/usecases/ui/assets/archive.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/cloud.svg b/demos/usecases/ui/assets/cloud.svg new file mode 100644 index 00000000000..3a9d66d54e8 --- /dev/null +++ b/demos/usecases/ui/assets/cloud.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/document.svg b/demos/usecases/ui/assets/document.svg new file mode 100644 index 00000000000..d544b55e9dc --- /dev/null +++ b/demos/usecases/ui/assets/document.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/forward.svg b/demos/usecases/ui/assets/forward.svg new file mode 100644 index 00000000000..600ae0dc3e3 --- /dev/null +++ b/demos/usecases/ui/assets/forward.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/inbox.svg b/demos/usecases/ui/assets/inbox.svg new file mode 100644 index 00000000000..5a21177cc6d --- /dev/null +++ b/demos/usecases/ui/assets/inbox.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/junk.svg b/demos/usecases/ui/assets/junk.svg new file mode 100644 index 00000000000..ac8234200b9 --- /dev/null +++ b/demos/usecases/ui/assets/junk.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/message.svg b/demos/usecases/ui/assets/message.svg new file mode 100644 index 00000000000..493667cff9e --- /dev/null +++ b/demos/usecases/ui/assets/message.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/reply.svg b/demos/usecases/ui/assets/reply.svg new file mode 100644 index 00000000000..27f118082f0 --- /dev/null +++ b/demos/usecases/ui/assets/reply.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/search.svg b/demos/usecases/ui/assets/search.svg new file mode 100644 index 00000000000..fe201508ba5 --- /dev/null +++ b/demos/usecases/ui/assets/search.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/send.svg b/demos/usecases/ui/assets/send.svg new file mode 100644 index 00000000000..50317799800 --- /dev/null +++ b/demos/usecases/ui/assets/send.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/trash.svg b/demos/usecases/ui/assets/trash.svg new file mode 100644 index 00000000000..99e97c668eb --- /dev/null +++ b/demos/usecases/ui/assets/trash.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/updates.svg b/demos/usecases/ui/assets/updates.svg new file mode 100644 index 00000000000..5acaa78e395 --- /dev/null +++ b/demos/usecases/ui/assets/updates.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/usecases/ui/assets/users.svg b/demos/usecases/ui/assets/users.svg new file mode 100644 index 00000000000..39176ec839d --- /dev/null +++ b/demos/usecases/ui/assets/users.svg @@ -0,0 +1,4 @@ + + + + diff --git a/demos/usecases/ui/views.slint b/demos/usecases/ui/views.slint new file mode 100644 index 00000000000..d64385e6fde --- /dev/null +++ b/demos/usecases/ui/views.slint @@ -0,0 +1,11 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { DashboardView, DashboardViewAdapter } from "views/dashboard_view.slint"; +export { DashboardView, DashboardViewAdapter } + +import { MailView, MailViewAdapter, MailBoxViewAdapter } from "views/mail_view.slint"; +export { MailView, MailViewAdapter, MailBoxViewAdapter } + +import { MainView, MainViewAdapter } from "views/main_view.slint"; +export { MainView, MainViewAdapter } \ No newline at end of file diff --git a/demos/usecases/ui/views/dashboard_view.slint b/demos/usecases/ui/views/dashboard_view.slint new file mode 100644 index 00000000000..cb3705ee56f --- /dev/null +++ b/demos/usecases/ui/views/dashboard_view.slint @@ -0,0 +1,146 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { ScrollView, GroupBox, Palette, VerticalBox } from "std-widgets.slint"; +import { TitleText, Tile, BarTileModel, BarTiles, BarChart, Value, ValueDisplay } from "../widgets.slint"; +import { Icons } from "../assets.slint"; + +export global WeatherViewAdapter { + in property current-temperature-icon: Icons.cloud; + in property current-temperature: "22°"; + in property current-day: "May 6th 2023"; + in property current-weather-description: "Very cloudy"; + in property <[BarTileModel]> week-model: [ + { + title: "Thu", + icon: Icons.cloud, + max: 21, + min: 18, + absolute-max: 21, + absolute-min: 15, + unit: "°" + }, + { + title: "Fri", + icon: Icons.cloud, + max: 20, + min: 17, + absolute-max: 21, + absolute-min: 15, + unit: "°" + }, + { + title: "Sat", + icon: Icons.cloud, + max: 18, + min: 15, + absolute-max: 21, + absolute-min: 15, + unit: "°" + } + ]; +} + +export global UsageViewAdapter { + in property title: "Usage"; + in property <[Value]> overview-model: [ + { + value: 16.41, + title: "Daily", + unit: "kWh", + }, + { + value: 15.23, + title: "Weekly", + unit: "kWh", + } + ]; + in property <[float]> model: [ + 10.0, + 9.0, + 11.0, + 12.0, + 8.0, + 14.0, + 9.0, + 16.0, + 18.0, + 12.0, + 11.0, + 14.0, + 12.0, + 16.0 + ]; + in property min: 0.0; + in property max: 24.0; +} + + + +export global DashboardViewAdapter { + +} + +export component DashboardView { + ScrollView { + width: 100%; + height: 100%; + + VerticalLayout { + padding: 4px; + spacing: 4px; + + TitleText { + horizontal-alignment: left; + text: "Dashboard"; + } + + GroupBox { + title: "Weather"; + + HorizontalLayout { + Tile { + value: WeatherViewAdapter.current-temperature; + text: WeatherViewAdapter.current-day; + sub-text: WeatherViewAdapter.current-weather-description; + icon: WeatherViewAdapter.current-temperature-icon; + } + + BarTiles { + model: WeatherViewAdapter.week-model; + active: true; + } + + // stretches the empty element + if WeatherViewAdapter.week-model.length == 0 : Rectangle {} + } + } + + GroupBox { + title: "Usage"; + + Rectangle { + BarChart { + preferred-width: 100%; + preferred-height: 100%; + model: UsageViewAdapter.model; + min: UsageViewAdapter.min; + max: UsageViewAdapter.max; + active: true; + } + + VerticalLayout { + alignment: start; + + ValueDisplay { + model: UsageViewAdapter.overview-model; + transparent-background: true; + alternative-colors: true; + active: true; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/views/header_view.slint b/demos/usecases/ui/views/header_view.slint new file mode 100644 index 00000000000..17efb525274 --- /dev/null +++ b/demos/usecases/ui/views/header_view.slint @@ -0,0 +1,29 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { Palette, HorizontalBox, Switch } from "std-widgets.slint"; +import { Icons } from "../assets.slint"; + +export component HeaderView { + min-height: 48px; + + HorizontalBox { + Image { + max-height: 32px; + source: Icons.slint-logo; + horizontal-alignment: left; + } + + // spaceer + Rectangle {} + + Switch { + text: "Dark Mode"; + checked: Palette.color-scheme == ColorScheme.dark; + + toggled => { + Palette.color-scheme = self.checked ? ColorScheme.dark : ColorScheme.light; + } + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/views/mail_view.slint b/demos/usecases/ui/views/mail_view.slint new file mode 100644 index 00000000000..b793200bbcb --- /dev/null +++ b/demos/usecases/ui/views/mail_view.slint @@ -0,0 +1,286 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { GroupBox, ComboBox, VerticalBox, GroupBox, GridBox, Palette, TextEdit, Button, Switch, ScrollView } from "std-widgets.slint"; +import { NavigationListView, NavigationListViewItem, Container, ExtendedLineEdit, Icon, CardListView, CardListViewItem, IconButton, TitleText } from "../widgets.slint"; +import { Icons } from "../assets.slint"; + +export global MailViewAdapter { } + +export global MailSideBarViewAdapter { + out property <[string]> accounts: ["jon.doe@slint.dev", "jon.doe@my-mail.com", "jon.doe@gmail.com"]; + + out property <[NavigationListViewItem]> boxes: [ + { text: "Inbox", message: "128", icon: Icons.inbox }, + { text: "Drafts", message: "9", icon: Icons.document }, + { text: "Sent", icon: Icons.send }, + { + text: "Junk", + icon: Icons.junk, + message: "23", + }, + { text: "Trash", icon: Icons.trash }, + { text: "Archive", icon: Icons.archive } + ]; + + out property <[NavigationListViewItem]> custom-boxes: [ + { text: "Social", message: "3972", icon: Icons.useres }, + { text: "Updates", message: "342", icon: Icons.updates }, + { text: "Forums", message: "128", icon: Icons.message } + ]; + in-out property current-box; + in-out property current-custom-box: -1; + + public pure function current-title() -> string { + if current-box > -1 && current-box < boxes.length { + return boxes[current-box].text; + } + if current-custom-box > -1 && current-custom-box < custom-boxes.length { + return custom-boxes[current-custom-box].text; + } + "" + } +} + +export component MailSideBarView { + horizontal-stretch: 0; + min-width: 264px / 2; + + VerticalLayout { + spacing: 4px; + + ComboBox { + model: MailSideBarViewAdapter.accounts; + } + + Container { + NavigationListView { + model: MailSideBarViewAdapter.boxes; + current-item <=> MailSideBarViewAdapter.current-box; + min-height: 248px; + vertical-stretch: 0; + + selected(index) => { + MailSideBarViewAdapter.current-custom-box = -1; + } + } + + Rectangle { + background: Palette.border; + height: 1px; + } + + NavigationListView { + model: MailSideBarViewAdapter.custom-boxes; + current-item <=> MailSideBarViewAdapter.current-custom-box; + + selected(index) => { + MailSideBarViewAdapter.current-box = -1; + } + } + } + } +} + +export global MailBoxViewAdapter { + callback search-text-changed(/* search-text */ string); + + in property title: MailSideBarViewAdapter.current-title(); + in property <[CardListViewItem]> mails: [ + { + title: "Simon Hausmann", + note: "1 hour ago", + sub-title: "Meeting tomorrow", + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + { title: "Tobias Hunger", note: "1 day ago", sub-title: "Meeting tomorrow", caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." }, + { + title: "Olivier Goffart", + note: "2 hour ago", + sub-title: "Meeting tomorrow", + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + { + title: "Aurindam Jana", + note: "5 hour ago", + sub-title: "Meeting tomorrow", + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + { + title: "Simon Hausmann", + note: "7 hour ago", + sub-title: "Meeting tomorrow", + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + { title: "Tobias Hunger", note: "1 day ago", sub-title: "Meeting tomorrow", caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." }, + { + title: "Olivier Goffart", + note: "8 hour ago", + sub-title: "Meeting tomorrow", + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + { + title: "Aurindam Jana", + note: "9 hour ago", + sub-title: "Meeting tomorrow", + caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + }, + ]; +} + +export component MailBoxView { + horizontal-stretch: 1; + + VerticalLayout { + spacing: 4px; + + TitleText { + text: MailBoxViewAdapter.title; + min-height: 32px; + + } + + Container { + background: Palette.control-background; + + VerticalLayout { + spacing: 8px; + + ExtendedLineEdit { + vertical-stretch: 0; + placeholder-text: "Search by Sender"; + + Icon { + source: Icons.search; + } + + edited => { + MailBoxViewAdapter.search-text-changed(self.text); + } + } + + CardListView { + model: MailBoxViewAdapter.mails; + } + } + } + } +} + +export global MailMessageViewAdapter { + callback move-to-archive(); + callback move-to-junk(); + callback move-to-trash(); + callback reply(); + callback forward(); + callback send(); + + in property message: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."; + in-out property mute-this-thread: false; +} + +export component MailMessageView { + horizontal-stretch: 1; + + Container { + background: Palette.control-background; + + HorizontalLayout { + spacing: 8px; + + IconButton { + icon: Icons.archive; + + clicked => { + MailMessageViewAdapter.move-to-archive(); + } + } + + IconButton { + icon: Icons.junk; + + clicked => { + MailMessageViewAdapter.move-to-junk(); + } + } + + IconButton { + icon: Icons.trash; + + clicked => { + MailMessageViewAdapter.move-to-trash(); + } + } + + Rectangle {} + + IconButton { + icon: Icons.reply; + + clicked => { + MailMessageViewAdapter.reply(); + } + } + + IconButton { + icon: Icons.forward; + + clicked => { + MailMessageViewAdapter.forward(); + } + } + } + + VerticalBox { + text-edit := TextEdit { + max-height: 94px; + wrap: word-wrap; + } + + ScrollView { + Text { + y: 0; + vertical-alignment: top; + width: parent.width; + font-size: 14px; + font-weight: 400; + color: Palette.foreground; + text: MailMessageViewAdapter.message; + wrap: word-wrap; + } + } + + HorizontalLayout { + Switch { + text: "Mute this thread"; + checked <=> MailMessageViewAdapter.mute-this-thread; + } + + Button { + text: "Send"; + primary: true; + enabled: text-edit.text != ""; + + clicked => { + MailMessageViewAdapter.send(); + } + } + } + } + } +} + +export component MailView { + HorizontalLayout { + spacing: 16px; + + MailSideBarView { } + + VerticalLayout { + spacing: 16px; + + MailBoxView { } + MailMessageView {} + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/views/main_view.slint b/demos/usecases/ui/views/main_view.slint new file mode 100644 index 00000000000..4044d93e842 --- /dev/null +++ b/demos/usecases/ui/views/main_view.slint @@ -0,0 +1,40 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { TabWidget, HorizontalBox } from "std-widgets.slint"; +import { MailView } from "mail_view.slint"; +import { HeaderView } from "header_view.slint"; +import { DashboardView } from "dashboard_view.slint"; + +export global MainViewAdapter { } + +export component MainView { + forward-focus: focus-scope; + focus-scope := FocusScope {} + + VerticalLayout { + HeaderView {} + + HorizontalBox { + TabWidget { + Tab { + title: "Mail"; + + HorizontalBox { + MailView { } + } + } + + Tab { + title: "Dashboard"; + + HorizontalLayout { + padding: 4px; + + DashboardView { } + } + } + } + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets.slint b/demos/usecases/ui/widgets.slint new file mode 100644 index 00000000000..6816ac45d76 --- /dev/null +++ b/demos/usecases/ui/widgets.slint @@ -0,0 +1,35 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { CardListView, CardListViewItem } from "widgets/card_list_view.slint"; +export { CardListView, CardListViewItem } + +import { Container, StateContainer } from "widgets/container.slint"; +export { Container, StateContainer } + +import { ExtendedLineEdit } from "widgets/extended_line_edit.slint"; +export { ExtendedLineEdit } + +import { IconButton } from "widgets/icon_button.slint"; +export { IconButton } + +import { Icon } from "widgets/icon.slint"; +export { Icon } + +import { Segmented } from "widgets/segmented.slint"; +export { Segmented } + +import { NavigationListView, NavigationListViewItem } from "widgets/navigation_list_view.slint"; +export { NavigationListView, NavigationListViewItem } + +import { Tile, BarTileModel, BarTiles } from "widgets/tile.slint"; +export { Tile, BarTileModel, BarTiles } + +import { TitleText } from "widgets/title_text.slint"; +export { TitleText } + +import { BarChart } from "widgets/bar_chart.slint"; +export { BarChart } + +import { Value, ValueDisplay } from "widgets/value_display.slint"; +export { Value, ValueDisplay } \ No newline at end of file diff --git a/demos/usecases/ui/widgets/bar_chart.slint b/demos/usecases/ui/widgets/bar_chart.slint new file mode 100644 index 00000000000..b954c74a2db --- /dev/null +++ b/demos/usecases/ui/widgets/bar_chart.slint @@ -0,0 +1,73 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { CosmicPalette } from "styling.slint"; + +component Bar { + in property bar-height; + + horizontal-stretch: 1; + + Rectangle { + border-radius: 2px; + y: parent.height - self.height; + height: bar-height; + clip: true; + + Rectangle { + height: root.height; + y: parent.height - self.height; + background: CosmicPalette.bar-gradient; + } + } +} + +export component BarBackground inherits Rectangle { + border-radius: 2px; + // background: Theme.palette.bar-background-gradient; + opacity: 0.25; +} + +export component ChartPattern { + in property count; + + HorizontalLayout { + spacing: 1px; + for _ in count : BarBackground {} + } +} + +export component BarChart { + in property <[float]> model; + in property min; + in property max; + in property active; + + cache-rendering-hint: true; + + ChartPattern { + count: model.length / 2; + } + + layout := HorizontalLayout { + spacing: 1px; + + for value in model : Bar { + private property display-value; + + min-height: 120px; + preferred-height: 100%; + bar-height: parent.height * (display-value - root.min) / (root.max - root.min); + + states [ + active when active : { + display-value: value; + + in { + animate display-value { duration: 500ms; easing: ease-in-out; } + } + } + ] + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/card_list_view.slint b/demos/usecases/ui/widgets/card_list_view.slint new file mode 100644 index 00000000000..ec9094e9898 --- /dev/null +++ b/demos/usecases/ui/widgets/card_list_view.slint @@ -0,0 +1,91 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { ListView, Palette, VerticalBox } from "std-widgets.slint"; +import { StateContainer } from "container.slint"; + +export component CardListItem { + callback clicked <=> state-container.clicked; + + in property title <=> title.text; + in property sub-title <=> sub-title.text; + in property note <=> note.text; + in property caption <=> caption.text; + in property selected <=> state-container.checked; + + min-height: max(41px, layout.min-height); + + layout := HorizontalLayout { + padding-bottom: 8px; + + state-container := StateContainer { + VerticalBox { + padding: 16px; + + HorizontalLayout { + spacing: 4px; + + title := Text { + horizontal-stretch: 1; + color: Palette.foreground; + font-size: 10px; + font-weight: 400; + overflow: elide; + } + + note := Text { + horizontal-stretch: 0; + color: Palette.foreground; + font-size: 10px; + font-weight: 400; + } + } + + sub-title := Text { + horizontal-stretch: 1; + color: Palette.foreground; + font-size: 14px; + font-weight: 600; + overflow: elide; + } + + caption := Text { + height: 40px; + wrap: word-wrap; + overflow: elide; + color: Palette.foreground; + font-size: 14px; + font-weight: 400; + } + } + } + } +} + +export struct CardListViewItem { + title: string, + sub-title: string, + note: string, + caption: string +} + +export component CardListView inherits ListView { + callback current-item-changed(/* current-item */ int); + + in property <[CardListViewItem]> model; + in-out property current-item; + + for item[index] in root.model : CardListItem { + height: self.min-height; + title: item.title; + sub-title: item.sub-title; + note: item.note; + caption: item.caption; + selected: index == root.current-item; + + clicked => { + root.current-item = index; + root.current-item-changed(index); + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/container.slint b/demos/usecases/ui/widgets/container.slint new file mode 100644 index 00000000000..a3d81cf5303 --- /dev/null +++ b/demos/usecases/ui/widgets/container.slint @@ -0,0 +1,43 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette, VerticalBox } from "std-widgets.slint"; +import { CosmicPalette } from "styling.slint"; + +export component StateContainer inherits Rectangle { + callback clicked <=> touch-area.clicked; + + in property checked; + + background: Palette.alternate-background; + border-radius: 8px; + + touch-area := TouchArea {} + + state-layer := Rectangle { + border-radius: root.border-radius; + + states [ + pressed when touch-area.pressed : { + state-layer.background: CosmicPalette.state-pressed; + } + hover when touch-area.has-hover : { + state-layer.background: CosmicPalette.state-hover; + } + checked when root.checked : { + state-layer.background: CosmicPalette.state-selected; + } + ] + } + + @children +} + +export component Container inherits Rectangle { + background: Palette.alternate-background; + border-radius: 8px; + + VerticalBox { + @children + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/extended_line_edit.slint b/demos/usecases/ui/widgets/extended_line_edit.slint new file mode 100644 index 00000000000..f78baee2e8a --- /dev/null +++ b/demos/usecases/ui/widgets/extended_line_edit.slint @@ -0,0 +1,178 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; +import { CosmicPalette } from "styling.slint"; + +// copied from common that is not public now +component LineEditBase inherits Rectangle { + in property placeholder-text; + in property font-size <=> text-input.font-size; + in_out property text <=> text-input.text; + in property placeholder-color; + in property enabled <=> text-input.enabled; + in property has-focus: text-input.has-focus; + in property input-type <=> text-input.input-type; + in property horizontal-alignment <=> text-input.horizontal-alignment; + in property read-only <=> text-input.read-only; + in property font-weight <=> text-input.font-weight; + in property text-color; + in property selection-background-color <=> text-input.selection-background-color; + in property selection-foreground-color <=> text-input.selection-foreground-color; + in property margin; + + callback accepted( /* text */ string); + callback edited(/* text */ string); + + public function set-selection-offsets(start: int, end: int) { + text-input.set-selection-offsets(start, end); + } + + public function select-all() { + text-input.select-all(); + } + + public function clear-selection() { + text-input.clear-selection(); + } + + public function cut() { + text-input.cut(); + } + + public function copy() { + text-input.copy(); + } + + public function paste() { + text-input.paste(); + } + + min-height: text-input.preferred-height; + min-width: max(50px, placeholder.min-width); + clip: true; + forward-focus: text-input; + + placeholder := Text { + width: 100%; + height: 100%; + vertical-alignment: center; + text: (root.text == "" && text-input.preedit-text == "") ? root.placeholder-text : ""; + font-size: text-input.font-size; + font-italic: text-input.font-italic; + font-weight: text-input.font-weight; + font-family: text-input.font-family; + color: root.placeholder-color; + horizontal-alignment: root.horizontal-alignment; + } + + text-input := TextInput { + property computed-x; + + x: min(0px, max(parent.width - self.width - self.text-cursor-width, self.computed-x)); + width: max(parent.width - self.text-cursor-width, self.preferred-width); + height: 100%; + vertical-alignment: center; + single-line: true; + color: root.text-color; + + cursor-position-changed(cpos) => { + if (cpos.x + self.computed_x < root.margin) { + self.computed_x = - cpos.x + root.margin; + } else if (cpos.x + self.computed_x > parent.width - root.margin - self.text-cursor-width) { + self.computed_x = parent.width - cpos.x - root.margin - self.text-cursor-width; + } + } + + accepted => { root.accepted(self.text); } + + edited => { root.edited(self.text); } + } +} + +export component ExtendedLineEdit { + in property enabled <=> base.enabled; + in property input-type <=> base.input-type; + in property horizontal-alignment <=> base.horizontal-alignment; + in property read-only <=> base.read-only; + in property font-size <=> base.font-size; + in property placeholder-text <=> base.placeholder-text; + out property has-focus <=> base.has-focus; + in-out property text <=> base.text; + + callback accepted <=> base.accepted; + callback edited <=> base.edited; + accessible-role: text-input; + accessible-value <=> text; + + public function set-selection-offsets(start: int, end: int) { + base.set-selection-offsets(start, end); + } + + public function select-all() { + base.select-all(); + } + + public function clear-selection() { + base.clear-selection(); + } + + public function cut() { + base.cut(); + } + + public function copy() { + base.copy(); + } + + public function paste() { + base.paste(); + } + + vertical-stretch: 0; + horizontal-stretch: 1; + min-width: max(160px, layout.min-width); + min-height: max(32px, layout.min-height); + forward-focus: base; + + states [ + disabled when !root.enabled : { + root.opacity: 0.5; + } + ] + + background := Rectangle { + border-radius: 8px; + background: Palette.control-background; + border-width: 1px; + border-color: CosmicPalette.control-divider; + + layout := HorizontalLayout { + padding-left: 16px; + padding-right: 16px; + spacing: 8px; + + HorizontalLayout { + @children + } + + base := LineEditBase { + font-size: 15 * 0.0769rem; + font-weight: 400; + selection-background-color: Palette.selection-background; + selection-foreground-color: Palette.accent-foreground; + text-color: Palette.foreground; + placeholder-color: CosmicPalette.placeholder-foreground; + margin: layout.padding-left + layout.padding-right; + } + } + + if root.has-focus && root.enabled : Rectangle { + width: parent.width + 2px; + height: parent.height + 2px; + border-radius: parent.border-radius + 2px; + border-color: CosmicPalette.state-focus; + border-width: 1px; + } + } +} diff --git a/demos/usecases/ui/widgets/icon.slint b/demos/usecases/ui/widgets/icon.slint new file mode 100644 index 00000000000..b1031db82da --- /dev/null +++ b/demos/usecases/ui/widgets/icon.slint @@ -0,0 +1,9 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; + +export component Icon inherits Image { + width: 16px; + colorize: Palette.foreground; +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/icon_button.slint b/demos/usecases/ui/widgets/icon_button.slint new file mode 100644 index 00000000000..5e146fd6587 --- /dev/null +++ b/demos/usecases/ui/widgets/icon_button.slint @@ -0,0 +1,26 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; +import { StateContainer } from "container.slint"; + +export component IconButton { + callback clicked <=> state-container.clicked; + + in property icon <=> icon.source; + + width: self.height; + min-height: 32px; + + vertical-stretch: 0; + horizontal-stretch: 0; + + state-container := StateContainer { + background: transparent; + border-radius: max(self.width, self.height) / 2; + icon := Image { + height: 16px; + colorize: Palette.foreground; + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/navigation_list_view.slint b/demos/usecases/ui/widgets/navigation_list_view.slint new file mode 100644 index 00000000000..9fe2badb66a --- /dev/null +++ b/demos/usecases/ui/widgets/navigation_list_view.slint @@ -0,0 +1,90 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { Palette, ListView, HorizontalBox } from "std-widgets.slint"; +import { CosmicPalette } from "styling.slint"; +import { Icon } from "icon.slint"; + +export component NavigationListItem { + private property foreground: Palette.foreground; + + callback select <=> touch-area.clicked; + + in property selected; + in property title <=> text.text; + in property message <=> message.text; + in property icon <=> icon.source; + + min-height: max(41px, layout.min-height); + + layout := HorizontalLayout { + padding-bottom: 8px; + + background := Rectangle { + border-radius: 16px; + + HorizontalLayout { + padding-left: 16px; + padding-right: 16px; + + spacing: 8px; + + icon := Icon { + y: (parent.height - self.height) / 2; + colorize: root.foreground; + } + + text := Text { + color: root.foreground; + horizontal-stretch: 1; + vertical-alignment: center; + font-size: 14px; + font-weight: 600; + } + + message := Text { + color: root.foreground; + horizontal-stretch: 0; + vertical-alignment: center; + font-size: 14px; + font-weight: 600; + } + } + + touch-area := TouchArea {} + } + } + + states [ + selected when root.selected : { + background.background: CosmicPalette.state-selected; + foreground: CosmicPalette.accent-text; + } + ] +} + +export struct NavigationListViewItem { + icon: image, + text: string, + message: string, +} + +export component NavigationListView inherits ListView { + in property <[NavigationListViewItem]> model; + in-out property current-item: -1; + + callback selected(/* current-item */ int); + + for item[index] in root.model : NavigationListItem { + height: self.min-height; + title: item.text; + icon: item.icon; + message: item.message; + selected: index == root.current-item; + + select => { + root.current-item = index; + selected(root.current-item); + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/segmented.slint b/demos/usecases/ui/widgets/segmented.slint new file mode 100644 index 00000000000..5d87a5f5fcf --- /dev/null +++ b/demos/usecases/ui/widgets/segmented.slint @@ -0,0 +1,4 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +export component Segmented {} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/styling.slint b/demos/usecases/ui/widgets/styling.slint new file mode 100644 index 00000000000..01e0142bc62 --- /dev/null +++ b/demos/usecases/ui/widgets/styling.slint @@ -0,0 +1,39 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; + +export global CosmicPalette { + out property state-selected: Palette.color-scheme == ColorScheme.dark ? #4D4D4D4D : #98989833; + out property accent-text: Palette.color-scheme == ColorScheme.dark ? #63D0DF : #00525A; + out property control-divider: Palette.color-scheme == ColorScheme.dark ? #DEDEDE33 : #3D3D3D33; + out property state-focus: Palette.color-scheme == ColorScheme.dark ? #63D0DF : #00525A; + out property placeholder-foreground: Palette.color-scheme == ColorScheme.dark ? #959595 : #585858; + out property state-hover: #63636333; + out property state-pressed: Palette.color-scheme == ColorScheme.dark ? #16161680 : #BEBEBE80; + out property bar-gradient: Palette.color-scheme == ColorScheme.dark ? @linear-gradient(180deg, #63D0DF 0%, #00525A 100%) : @linear-gradient(180deg, #00525A 0%, #63D0DF 100%); +} + +export struct TextStyle { + font-size: relative-font-size, + font-weight: int, +} + +export global CosmicFontSettings { + out property light-font-weight: 300; + out property regular-font-weight: 400; + out property semibold-font-weight: 600; + out property body: { + font-size: 14 * 0.0769rem, + font-weight: regular-font-weight + }; + out property body-strong: { + font-size: 14 * 0.0769rem, + font-weight: semibold-font-weight + }; + + out property title-2: { + font-size: 28 * 0.0769rem, + font-weight: regular-font-weight + }; +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/tile.slint b/demos/usecases/ui/widgets/tile.slint new file mode 100644 index 00000000000..be924d6c8d5 --- /dev/null +++ b/demos/usecases/ui/widgets/tile.slint @@ -0,0 +1,191 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; +import { CosmicFontSettings } from "styling.slint"; + +export struct BarTileModel { + title: string, + icon: image, + max: int, + min: int, + absolute-min: int, + absolute-max: int, + unit: string, +} + +component ValueLabel { + in property text; + in property unit; + + HorizontalLayout { + Text { + color: Palette.foreground; + vertical-stretch: 0; + horizontal-alignment: right; + text: root.text; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + } + + Text { + color: Palette.foreground; + vertical-stretch: 0; + horizontal-alignment: left; + text: "°"; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + } + } +} + +component BarTile { + in property title <=> i-title.text; + in property icon <=> i-icon.source; + in property max; + in property min; + in property unit; + in property absolute-min; + in property absolute-max; + + HorizontalLayout { + alignment: center; + + VerticalLayout { + spacing: 7px; + + i-title := Text { + color: Palette.foreground; + vertical-stretch: 0; + horizontal-alignment: center; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + } + + i-icon := Image { + height: 20px; + vertical-stretch: 0; + colorize: Palette.accent-background; + } + + ValueLabel { + text: floor(max); + unit: unit; + } + + Rectangle { + private property range: root.absolute-max - root.absolute-min; + private property max-y: self.height * (root.max - root.absolute-min) / range; + private property min-y: self.height * (root.min - root.absolute-min) / range; + + vertical-stretch: 1; + + HorizontalLayout { + alignment: center; + y: parent.height - max-y; + height: max-y - min-y; + + Rectangle { + min_width: 12px; + border-radius: 6px; + + background: Palette.accent-background; + } + } + } + + ValueLabel { + text: floor(min); + unit: unit; + } + } + } +} + + + +export component BarTiles { + in property <[BarTileModel]> model; + in property active; + + horizontal-stretch: 1; + vertical-stretch: 1; + + HorizontalLayout { + padding-right: 16px; + padding-left: 16px; + padding-top: 8px; + padding-bottom: 8px; + + for tile in model : BarTile { + private property display-max: tile.max; + + horizontal-stretch: 1; + title: tile.title; + icon: tile.icon; + min: tile.min; + absolute-min: tile.absolute-min; + absolute-max: tile.absolute-max; + unit: tile.unit; + + states [ + active when active : { + max: display-max; + + in { + animate max { duration: 240ms; easing: cubic-bezier(0, 0, 0, 1); } + } + } + ] + } + } +} + +export component Tile { + in property icon <=> i-icon.source; + in property value <=> i-value.text; + in property text <=> i-text.text; + in property sub-text <=> i-sub-text.text; + + horizontal-stretch: 0; + vertical-stretch: 1; + + VerticalLayout { + padding-left: 16px; + padding-right: 16px; + padding-top: 8px; + padding-bottom: 8px; + spacing: 8px; + alignment: center; + + i-icon := Image { + height: 34px; + horizontal-alignment: center; + colorize: Palette.foreground; + image-fit: contain; + } + + i-value := Text { + horizontal-alignment: center; + color: Palette.foreground; + font-size: CosmicFontSettings.title-2.font-size; + font-weight: CosmicFontSettings.title-2.font-weight; + } + + VerticalLayout { + i-text := Text { + horizontal-alignment: center; + color: Palette.foreground; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + } + + i-sub-text := Text { + horizontal-alignment: center; + color: Palette.accent-background; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + } + } + } +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/title_text.slint b/demos/usecases/ui/widgets/title_text.slint new file mode 100644 index 00000000000..50e7290a5a4 --- /dev/null +++ b/demos/usecases/ui/widgets/title_text.slint @@ -0,0 +1,11 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { Palette } from "std-widgets.slint"; +import { CosmicFontSettings } from "styling.slint"; + +export component TitleText inherits Text { + color: Palette.foreground; + font-size: CosmicFontSettings.title-2.font-size; + font-weight: CosmicFontSettings.title-2.font-weight; +} \ No newline at end of file diff --git a/demos/usecases/ui/widgets/value_display.slint b/demos/usecases/ui/widgets/value_display.slint new file mode 100644 index 00000000000..8752e08f7e0 --- /dev/null +++ b/demos/usecases/ui/widgets/value_display.slint @@ -0,0 +1,113 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial + +import { CosmicPalette, CosmicFontSettings } from "styling.slint"; +import { Palette } from "std-widgets.slint"; + +component ValueDelegate { + in property active; + in property title <=> title.text; + in property unit <=> unit.text; + in property value; + in property alternative-colors; + + private property display-value; + + states [ + active when active : { + display-value: value; + + in { + animate display-value { duration: 500ms; } + } + } + ] + + HorizontalLayout { + spacing: 15px; + + Rectangle { + min_width: 1px; + background: Palette.accent-background; + horizontal-stretch: 0; + } + + VerticalLayout { + alignment: center; + horizontal-stretch: 1; + + title := Text { + color: Palette.accent-background; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + } + + HorizontalLayout { + alignment: start; + spacing: 5px; + + Text { + color: Palette.foreground; + text: round(display-value * 100) / 100; + font-size: CosmicFontSettings.body-strong.font-size; + font-weight: CosmicFontSettings.body-strong.font-weight; + vertical-alignment: center; + } + + unit := Text { + y: 4px; + vertical-alignment: center; + color: Palette.accent-background; + font-size: CosmicFontSettings.body.font-size; + font-weight: CosmicFontSettings.body.font-weight; + } + } + } + } +} + +export struct Value { + title: string, + value: float, + unit: string, +} + +export component ValueDisplay { + in property alternative-colors; + in property <[Value]> model; + in property active; + in property transparent-background; + in property vertical; + + min-height: 70px; + + + if(model.length > 0 && !vertical) : HorizontalLayout { + x: 15px; + width: parent.width - 30px; + height: 100%; + padding-top: 12px; + padding-bottom: 12px; + + for value in root.model : ValueDelegate { + width: parent.width / model.length; + horizontal-stretch: 1; + alternative-colors: root.alternative-colors; + title: value.title; + value: value.value; + unit: value.unit; + active: root.active; + } + } + + if(model.length > 0 && vertical) : VerticalLayout { + for value in root.model : ValueDelegate { + vertical-stretch: 1; + alternative-colors: root.alternative-colors; + title: value.title; + value: value.value; + unit: value.unit; + active: root.active; + } + } +} \ No newline at end of file diff --git a/examples/virtual_keyboard/ui/assets/expand-more.svg b/examples/virtual_keyboard/ui/assets/expand-more.svg new file mode 100644 index 00000000000..a8197744dbe --- /dev/null +++ b/examples/virtual_keyboard/ui/assets/expand-more.svg @@ -0,0 +1,3 @@ + + + diff --git a/examples/virtual_keyboard/ui/icons.slint b/examples/virtual_keyboard/ui/icons.slint index 60ccd0519b4..a57a1f40433 100644 --- a/examples/virtual_keyboard/ui/icons.slint +++ b/examples/virtual_keyboard/ui/icons.slint @@ -8,4 +8,5 @@ export global Icons { out property chevron-left: @image-url("assets/chevron-left.svg"); out property arrow-circle-o-left: @image-url("assets/arrow-circle-o-left.svg"); out property globe: @image-url("assets/globe.svg"); -} \ No newline at end of file + out property expand-more: @image-url("assets/expand-more.svg"); +} diff --git a/examples/virtual_keyboard/ui/virtual_keyboard.slint b/examples/virtual_keyboard/ui/virtual_keyboard.slint index 646e59db952..622f37d226a 100644 --- a/examples/virtual_keyboard/ui/virtual_keyboard.slint +++ b/examples/virtual_keyboard/ui/virtual_keyboard.slint @@ -161,6 +161,8 @@ export global VirtualKeyboardHandler { export component VirtualKeyboard { private property shift; + callback close(); + preferred-width: 100%; TouchArea {} @@ -240,6 +242,14 @@ export component VirtualKeyboard { HorizontalLayout { spacing: 4px; + VirtualKeyboardButton { + icon: Icons.expand-more; + + key-pressed(key) => { + root.close(); + } + } + VirtualKeyboardButton { icon: Icons.globe; @@ -276,4 +286,4 @@ export component VirtualKeyboard { } animate y { duration: 500ms; easing: cubic-bezier(0.05, 0.7, 0.1, 1.0); } -} \ No newline at end of file +} From 89cefad96d5bc780f91ad1e72dad84d5aa13d347 Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Tue, 11 Feb 2025 11:48:30 +0100 Subject: [PATCH 02/11] usecases demo added filter to rust example --- demos/usecases/rust/src/lib.rs | 107 ++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 43 deletions(-) diff --git a/demos/usecases/rust/src/lib.rs b/demos/usecases/rust/src/lib.rs index 4ac062f02cc..7ce5044c2cb 100644 --- a/demos/usecases/rust/src/lib.rs +++ b/demos/usecases/rust/src/lib.rs @@ -41,58 +41,79 @@ mod virtual_keyboard { } mod data { + use std::rc::Rc; + use super::*; use slint::*; pub fn init(app: &App) { let mail_box_adapter = MailBoxViewAdapter::get(app); - let mails = VecModel::from_slice(&[ + let message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".to_string(); - CardListViewItem{ - title: "Simon Hausmann".into(), - note: "1 hour ago".into(), - sub_title: "Meeting tomorrow".into(), - caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".into() - }, -// CardListViewItem { title: "Tobias Hunger".into(), note: "1 day ago".into(), sub_title: "Meeting tomorrow".into(), caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".into() }, -// CardListViewItem { -// title: "Olivier Goffart".into(), -// note: "2 hour ago".into(), -// sub_title: "Meeting tomorrow".into(), -// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.".into() -// }, -// CardListViewItem { -// title: "Aurindam Jana".into(), -// note: "5 hour ago".into(), -// sub_title: "Meeting tomorrow".into(), -// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." -// .into() -// }, -// CardListViewItem { -// title: "Simon Hausmann".into(), -// note: "7 hour ago".into(), -// sub_title: "Meeting tomorrow".into(), -// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." -// .into() -// }, -// CardListViewItem { title: "Tobias Hunger".into(), note: "1 day ago".into(), sub_title: "Meeting tomorrow".into(), caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."into() }, -// CardListViewItem { -// title: "Olivier Goffart".into(), -// note: "8 hour ago".into(), -// sub_title: "Meeting tomorrow".into(), -// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." -// .into() -// }, -// CardListViewItem { -// title: "Aurindam Jana".into(), -// note: "9 hour ago".into(), -// sub_title: "Meeting tomorrow".into(), -// caption: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." -// .into() -// } + let mails = VecModel::from_slice(&[ + CardListViewItem { + title: "Simon Hausmann".into(), + note: "1 hour ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Tobias Hunger".into(), + note: "1 day ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Olivier Goffart".into(), + note: "1 day".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Aurindam Jana".into(), + note: "2 hour ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Simon Hausmann".into(), + note: "5 hour ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Tobias Hunger".into(), + note: "7 hours ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Olivier Goffart".into(), + note: "8 hour ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.clone().into(), + }, + CardListViewItem { + title: "Aurindam Jana".into(), + note: "9 hour ago".into(), + sub_title: "Meeting tomorrow".into(), + caption: message.into(), + }, ]); + mail_box_adapter.on_search_text_changed({ + let app_weak = app.as_weak(); + let mails = mails.clone(); + + move |text| { + let mails = mails + .clone() + .filter(move |e| e.title.to_lowercase().contains(text.to_lowercase().as_str())); + MailBoxViewAdapter::get(&app_weak.unwrap()).set_mails(Rc::new(mails).into()); + } + }); + mail_box_adapter.set_mails(mails.into()); } } From 953e111974c26977e778678c8622b1b468f3584c Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Tue, 11 Feb 2025 13:47:38 +0100 Subject: [PATCH 03/11] usecases demo ci and readme --- .github/workflows/wasm_demos.yaml | 1 + demos/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/wasm_demos.yaml b/.github/workflows/wasm_demos.yaml index eba72baa44a..95f8b1b6174 100644 --- a/.github/workflows/wasm_demos.yaml +++ b/.github/workflows/wasm_demos.yaml @@ -82,6 +82,7 @@ jobs: demos/energy-monitor/ demos/home-automation/rust demos/weather-demo/ + demos/usecases/rust !/**/.gitignore - name: Clean cache # Otherwise the cache is much too big run: | diff --git a/demos/README.md b/demos/README.md index 39cc9f11ad6..d8f171f7ccf 100644 --- a/demos/README.md +++ b/demos/README.md @@ -11,6 +11,7 @@ These demos showcase different complex use-cases for building UIs with Slint. | [Printer UI ![Printer Demo image](https://github.com/user-attachments/assets/7e7400ad-283a-4404-b04a-8620ba4df452)](./printerdemo) | A fictional user interface for the touch screen of a printer.
[Project...](./printerdemo) | [Wasm Demo](https://slint.dev/snapshots/master/demos/printerdemo/) | | [Energy Meter![Energy meter demo image](https://github.com/user-attachments/assets/abfe03e3-ded6-4ddc-82b7-8303ee45515c "Energy meter demo image")](./energy-monitor/) | A fictional user interface of a device that monitors energy consumption in a building.
[Project...](./energy-monitor) | [Wasm Demo](https://slint.dev/snapshots/master/demos/energy-monitor/) | | [Weather![Weather demo image](./weather-demo/docs/img/desktop-preview.png "7 GUI's demo image")](./weather-demo/) | A simple, cross-platform (Desktop, Android, Wasm) weather application using real weather data from the [OpenWeather](https://openweathermap.org/) API.
[Project...](./weather-demo/) | [Wasm Demo](https://slint.dev/snapshots/master/demos/weather-demo/) | +| [Usecases ![Usecases Demo image](https://github.com/user-attachments/assets/72dd3e98-36b8-41b6-9d6e-6eb6053ace43)](./usecases) | Different example use cases in one app.
[Project...](./usecases) | [Wasm Demo](https://slint.dev/snapshots/master/demos/usecases/) | --- ### Running the Rust Demos From 9cb8ed6a3bfda750412dd3f4d662771ec6e788f9 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:51:18 +0000 Subject: [PATCH 04/11] [autofix.ci] apply automated fixes --- demos/usecases/cpp/main.cpp | 3 +-- demos/usecases/esp-idf/README.md | 2 ++ demos/usecases/esp-idf/main/main.cpp | 4 +--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/demos/usecases/cpp/main.cpp b/demos/usecases/cpp/main.cpp index faa2858f063..7c86a7a2f22 100644 --- a/demos/usecases/cpp/main.cpp +++ b/demos/usecases/cpp/main.cpp @@ -70,8 +70,7 @@ int main() (*app_lock)->global().set_mails( std::make_shared>( - mails, - [text_str](auto e) { + mails, [text_str](auto e) { std::string title_str(e.title.data()); return title_str.find(text_str) != std::string::npos; })); diff --git a/demos/usecases/esp-idf/README.md b/demos/usecases/esp-idf/README.md index bc349526afd..85bdc06e944 100644 --- a/demos/usecases/esp-idf/README.md +++ b/demos/usecases/esp-idf/README.md @@ -1,3 +1,5 @@ + + # Building ``` diff --git a/demos/usecases/esp-idf/main/main.cpp b/demos/usecases/esp-idf/main/main.cpp index 14f66b1ac9c..eed4dfd9e55 100644 --- a/demos/usecases/esp-idf/main/main.cpp +++ b/demos/usecases/esp-idf/main/main.cpp @@ -20,8 +20,6 @@ #undef BSP_LCD_V_RES #define BSP_LCD_V_RES 1280 - - extern "C" void app_main(void) { @@ -30,7 +28,7 @@ extern "C" void app_main(void) /* Initialize display */ esp_lcd_panel_handle_t panel_handle = NULL; - bsp_lcd_handles_t handles{}; + bsp_lcd_handles_t handles {}; bsp_display_new_with_handles(nullptr, &handles); From 59349a7f7558d5af3210496703bb2fdd5c2f15f3 Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Tue, 11 Feb 2025 14:10:27 +0100 Subject: [PATCH 05/11] add license for usecases icons --- REUSE.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/REUSE.toml b/REUSE.toml index 93962b2e626..219063322af 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -190,6 +190,7 @@ path = [ "internal/compiler/widgets/cupertino/_**.svg", "internal/compiler/widgets/qt/_**.svg", "examples/todo-mvc/assets/**.svg", + "demos/usecases/ui/assets/**.svg" ] precedence = "aggregate" SPDX-FileCopyrightText = "Material Icons " From c7e71638cf5ca7ce527f80007edd225874c60f00 Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Tue, 11 Feb 2025 14:55:27 +0100 Subject: [PATCH 06/11] usecases demo fix licenses of slint files --- demos/usecases/ui/assets.slint | 4 ++-- demos/usecases/ui/widgets/card_list_view.slint | 4 ++-- demos/usecases/ui/widgets/container.slint | 4 ++-- demos/usecases/ui/widgets/extended_line_edit.slint | 2 +- demos/usecases/ui/widgets/icon.slint | 4 ++-- demos/usecases/ui/widgets/icon_button.slint | 4 ++-- demos/usecases/ui/widgets/styling.slint | 4 ++-- demos/usecases/ui/widgets/tile.slint | 4 ++-- demos/usecases/ui/widgets/title_text.slint | 4 ++-- demos/usecases/ui/widgets/value_display.slint | 4 ++-- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/demos/usecases/ui/assets.slint b/demos/usecases/ui/assets.slint index 21a361b1b5b..b591eb60356 100644 --- a/demos/usecases/ui/assets.slint +++ b/demos/usecases/ui/assets.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; @@ -18,4 +18,4 @@ export global Icons { out property updates: @image-url("assets/updates.svg"); out property useres: @image-url("assets/users.svg"); out property slint-logo: Palette.color-scheme == ColorScheme.dark ? @image-url("../../../logo/slint-logo-simple-dark.png") : @image-url("../../../logo/slint-logo-simple-light.png"); -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/card_list_view.slint b/demos/usecases/ui/widgets/card_list_view.slint index ec9094e9898..6680fa8921f 100644 --- a/demos/usecases/ui/widgets/card_list_view.slint +++ b/demos/usecases/ui/widgets/card_list_view.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { ListView, Palette, VerticalBox } from "std-widgets.slint"; import { StateContainer } from "container.slint"; @@ -88,4 +88,4 @@ export component CardListView inherits ListView { root.current-item-changed(index); } } -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/container.slint b/demos/usecases/ui/widgets/container.slint index a3d81cf5303..43f013f2d85 100644 --- a/demos/usecases/ui/widgets/container.slint +++ b/demos/usecases/ui/widgets/container.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette, VerticalBox } from "std-widgets.slint"; import { CosmicPalette } from "styling.slint"; @@ -40,4 +40,4 @@ export component Container inherits Rectangle { VerticalBox { @children } -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/extended_line_edit.slint b/demos/usecases/ui/widgets/extended_line_edit.slint index f78baee2e8a..3578668de13 100644 --- a/demos/usecases/ui/widgets/extended_line_edit.slint +++ b/demos/usecases/ui/widgets/extended_line_edit.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; import { CosmicPalette } from "styling.slint"; diff --git a/demos/usecases/ui/widgets/icon.slint b/demos/usecases/ui/widgets/icon.slint index b1031db82da..7846b3bcd8f 100644 --- a/demos/usecases/ui/widgets/icon.slint +++ b/demos/usecases/ui/widgets/icon.slint @@ -1,9 +1,9 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; export component Icon inherits Image { width: 16px; colorize: Palette.foreground; -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/icon_button.slint b/demos/usecases/ui/widgets/icon_button.slint index 5e146fd6587..5c04fe92792 100644 --- a/demos/usecases/ui/widgets/icon_button.slint +++ b/demos/usecases/ui/widgets/icon_button.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; import { StateContainer } from "container.slint"; @@ -23,4 +23,4 @@ export component IconButton { colorize: Palette.foreground; } } -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/styling.slint b/demos/usecases/ui/widgets/styling.slint index 01e0142bc62..efbf71fe276 100644 --- a/demos/usecases/ui/widgets/styling.slint +++ b/demos/usecases/ui/widgets/styling.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; @@ -36,4 +36,4 @@ export global CosmicFontSettings { font-size: 28 * 0.0769rem, font-weight: regular-font-weight }; -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/tile.slint b/demos/usecases/ui/widgets/tile.slint index be924d6c8d5..ff3b013edd2 100644 --- a/demos/usecases/ui/widgets/tile.slint +++ b/demos/usecases/ui/widgets/tile.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; import { CosmicFontSettings } from "styling.slint"; @@ -188,4 +188,4 @@ export component Tile { } } } -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/title_text.slint b/demos/usecases/ui/widgets/title_text.slint index 50e7290a5a4..1a84948cc05 100644 --- a/demos/usecases/ui/widgets/title_text.slint +++ b/demos/usecases/ui/widgets/title_text.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { Palette } from "std-widgets.slint"; import { CosmicFontSettings } from "styling.slint"; @@ -8,4 +8,4 @@ export component TitleText inherits Text { color: Palette.foreground; font-size: CosmicFontSettings.title-2.font-size; font-weight: CosmicFontSettings.title-2.font-weight; -} \ No newline at end of file +} diff --git a/demos/usecases/ui/widgets/value_display.slint b/demos/usecases/ui/widgets/value_display.slint index 8752e08f7e0..46e217c85dd 100644 --- a/demos/usecases/ui/widgets/value_display.slint +++ b/demos/usecases/ui/widgets/value_display.slint @@ -1,5 +1,5 @@ // Copyright © SixtyFPS GmbH -// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial +// SPDX-License-Identifier: MIT import { CosmicPalette, CosmicFontSettings } from "styling.slint"; import { Palette } from "std-widgets.slint"; @@ -110,4 +110,4 @@ export component ValueDisplay { active: root.active; } } -} \ No newline at end of file +} From 65bb3cde63e7ab5875eabe99077124922a6478eb Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Wed, 12 Feb 2025 07:40:21 +0000 Subject: [PATCH 07/11] Update demos/usecases/esp-idf/README.md Co-authored-by: Olivier Goffart --- demos/usecases/esp-idf/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/usecases/esp-idf/README.md b/demos/usecases/esp-idf/README.md index 85bdc06e944..eb854884848 100644 --- a/demos/usecases/esp-idf/README.md +++ b/demos/usecases/esp-idf/README.md @@ -3,6 +3,6 @@ # Building ``` -cd examples/usecases/esp-idf +cd demos/usecases/esp-idf SLINT_SCALE_FACTOR=2 idf.py flash monitor ``` From 579481c4869ccc0503a82bb734feba400ce71ca1 Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Wed, 12 Feb 2025 07:40:28 +0000 Subject: [PATCH 08/11] Update demos/usecases/esp-idf/rust-toolchain.toml Co-authored-by: Olivier Goffart --- demos/usecases/esp-idf/rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/usecases/esp-idf/rust-toolchain.toml b/demos/usecases/esp-idf/rust-toolchain.toml index de3554c8d0d..7b45edf944d 100644 --- a/demos/usecases/esp-idf/rust-toolchain.toml +++ b/demos/usecases/esp-idf/rust-toolchain.toml @@ -2,4 +2,4 @@ # SPDX-License-Identifier: MIT [toolchain] -channel = "nightly" +channel = "esp" From c1241502ef993144070363e37c8f705c8d418c1a Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Wed, 12 Feb 2025 08:40:50 +0100 Subject: [PATCH 09/11] code review feedback --- demos/usecases/cpp/main.cpp | 7 ++++++- demos/usecases/esp-idf/main/main.cpp | 2 +- tests/driver/interpreter/main.rs | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/demos/usecases/cpp/main.cpp b/demos/usecases/cpp/main.cpp index 7c86a7a2f22..99b90d8240c 100644 --- a/demos/usecases/cpp/main.cpp +++ b/demos/usecases/cpp/main.cpp @@ -11,7 +11,7 @@ void init_virtual_keyboard(slint::ComponentHandle app) }); } -int main() +void run() { auto app = App::create(); @@ -78,3 +78,8 @@ int main() app->run(); } + +int main() +{ + run(); +} diff --git a/demos/usecases/esp-idf/main/main.cpp b/demos/usecases/esp-idf/main/main.cpp index eed4dfd9e55..55fd26201fc 100644 --- a/demos/usecases/esp-idf/main/main.cpp +++ b/demos/usecases/esp-idf/main/main.cpp @@ -44,5 +44,5 @@ extern "C" void app_main(void) slint_esp_init(slint::PhysicalSize({ BSP_LCD_H_RES, BSP_LCD_V_RES }), panel_handle, touch_handle); - main(); + run(); } diff --git a/tests/driver/interpreter/main.rs b/tests/driver/interpreter/main.rs index 7dcc2daa8ce..f02725011d5 100644 --- a/tests/driver/interpreter/main.rs +++ b/tests/driver/interpreter/main.rs @@ -31,6 +31,7 @@ macro_rules! test_example { } test_example!(example_printerdemo, "demos/printerdemo/ui/printerdemo.slint"); +test_example!(example_usecases, "demos/usecases/ui/app.slint"); test_example!(example_memory, "examples/memory/memory.slint"); test_example!(example_slide_puzzle, "examples/slide_puzzle/slide_puzzle.slint"); test_example!(example_todo, "examples/todo/ui/todo.slint"); From 7a420dbf9af7f049449e7b70739a9f175cc56d98 Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Wed, 12 Feb 2025 08:45:28 +0100 Subject: [PATCH 10/11] code review feedback --- .github/workflows/wasm_demos.yaml | 2 +- demos/usecases/rust/index.html | 40 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 demos/usecases/rust/index.html diff --git a/.github/workflows/wasm_demos.yaml b/.github/workflows/wasm_demos.yaml index 95f8b1b6174..7c887cb4423 100644 --- a/.github/workflows/wasm_demos.yaml +++ b/.github/workflows/wasm_demos.yaml @@ -46,7 +46,7 @@ jobs: - name: Remaining wasm demos if: ${{ inputs.build_artifacts }} run: | - for demo in demos/printerdemo/rust examples/todo/rust examples/todo-mvc/rust examples/carousel/rust examples/slide_puzzle examples/memory examples/imagefilter/rust examples/plotter examples/opengl_underlay demos/home-automation/rust; do + for demo in demos/printerdemo/rust demos/usecases/rust examples/todo/rust examples/todo-mvc/rust examples/carousel/rust examples/slide_puzzle examples/memory examples/imagefilter/rust examples/plotter examples/opengl_underlay demos/home-automation/rust; do pushd $demo sed -i "s/#wasm# //" Cargo.toml wasm-pack build --release --target web diff --git a/demos/usecases/rust/index.html b/demos/usecases/rust/index.html new file mode 100644 index 00000000000..7fff70d50bb --- /dev/null +++ b/demos/usecases/rust/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + Slint use cases Demo (Web Assembly version) + + + + +

This is the Slint Use cases Demo compiled to WebAssembly.

+
+
Loading...
+
+ + + + + + From 59c5ade062b8cab9175d5d36c6e6c4ce280d078f Mon Sep 17 00:00:00 2001 From: FloVanGH Date: Wed, 12 Feb 2025 09:03:45 +0100 Subject: [PATCH 11/11] make usecases demo indipendent from keyboard example --- demos/usecases/ui/app.slint | 4 +- .../ui/assets/arrow-circle-o-left.svg | 4 + demos/usecases/ui/assets/arrow-left.svg | 3 + demos/usecases/ui/assets/arrow-right.svg | 3 + demos/usecases/ui/assets/arrow-up.svg | 3 + demos/usecases/ui/assets/chevron-left.svg | 3 + demos/usecases/ui/assets/expand-more.svg | 3 + demos/usecases/ui/assets/globe.svg | 4 + demos/usecases/ui/icons.slint | 12 + demos/usecases/ui/virtual_keyboard.slint | 289 ++++++++++++++++++ 10 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 demos/usecases/ui/assets/arrow-circle-o-left.svg create mode 100644 demos/usecases/ui/assets/arrow-left.svg create mode 100644 demos/usecases/ui/assets/arrow-right.svg create mode 100644 demos/usecases/ui/assets/arrow-up.svg create mode 100644 demos/usecases/ui/assets/chevron-left.svg create mode 100644 demos/usecases/ui/assets/expand-more.svg create mode 100644 demos/usecases/ui/assets/globe.svg create mode 100644 demos/usecases/ui/icons.slint create mode 100644 demos/usecases/ui/virtual_keyboard.slint diff --git a/demos/usecases/ui/app.slint b/demos/usecases/ui/app.slint index 80e5a872923..676fa6391d8 100644 --- a/demos/usecases/ui/app.slint +++ b/demos/usecases/ui/app.slint @@ -7,12 +7,10 @@ export { MainViewAdapter, MailViewAdapter, MailBoxViewAdapter, DashboardViewAdap import { CardListViewItem } from "widgets.slint"; export { CardListViewItem } -import { VirtualKeyboardHandler, VirtualKeyboard } from "../../../examples/virtual_keyboard/ui/virtual_keyboard.slint"; - +import { VirtualKeyboardHandler, VirtualKeyboard } from "virtual_keyboard.slint"; export { VirtualKeyboardHandler } - export component App inherits Window { preferred-width: 800px; preferred-height: 1280px; diff --git a/demos/usecases/ui/assets/arrow-circle-o-left.svg b/demos/usecases/ui/assets/arrow-circle-o-left.svg new file mode 100644 index 00000000000..c5fca3ad996 --- /dev/null +++ b/demos/usecases/ui/assets/arrow-circle-o-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/demos/usecases/ui/assets/arrow-left.svg b/demos/usecases/ui/assets/arrow-left.svg new file mode 100644 index 00000000000..2288ba1a853 --- /dev/null +++ b/demos/usecases/ui/assets/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/usecases/ui/assets/arrow-right.svg b/demos/usecases/ui/assets/arrow-right.svg new file mode 100644 index 00000000000..f6524a8d73d --- /dev/null +++ b/demos/usecases/ui/assets/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/usecases/ui/assets/arrow-up.svg b/demos/usecases/ui/assets/arrow-up.svg new file mode 100644 index 00000000000..1b97c1325a7 --- /dev/null +++ b/demos/usecases/ui/assets/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/usecases/ui/assets/chevron-left.svg b/demos/usecases/ui/assets/chevron-left.svg new file mode 100644 index 00000000000..3951e22d2c6 --- /dev/null +++ b/demos/usecases/ui/assets/chevron-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/usecases/ui/assets/expand-more.svg b/demos/usecases/ui/assets/expand-more.svg new file mode 100644 index 00000000000..a8197744dbe --- /dev/null +++ b/demos/usecases/ui/assets/expand-more.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/usecases/ui/assets/globe.svg b/demos/usecases/ui/assets/globe.svg new file mode 100644 index 00000000000..abf1c09e0c3 --- /dev/null +++ b/demos/usecases/ui/assets/globe.svg @@ -0,0 +1,4 @@ + + + + diff --git a/demos/usecases/ui/icons.slint b/demos/usecases/ui/icons.slint new file mode 100644 index 00000000000..a57a1f40433 --- /dev/null +++ b/demos/usecases/ui/icons.slint @@ -0,0 +1,12 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +export global Icons { + out property arrow-up: @image-url("assets/arrow-up.svg"); + out property arrow-left: @image-url("assets/arrow-left.svg"); + out property arrow-right: @image-url("assets/arrow-right.svg"); + out property chevron-left: @image-url("assets/chevron-left.svg"); + out property arrow-circle-o-left: @image-url("assets/arrow-circle-o-left.svg"); + out property globe: @image-url("assets/globe.svg"); + out property expand-more: @image-url("assets/expand-more.svg"); +} diff --git a/demos/usecases/ui/virtual_keyboard.slint b/demos/usecases/ui/virtual_keyboard.slint new file mode 100644 index 00000000000..622f37d226a --- /dev/null +++ b/demos/usecases/ui/virtual_keyboard.slint @@ -0,0 +1,289 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: MIT + +import { Button, Palette } from "std-widgets.slint"; + +import { Icons } from "icons.slint"; + +component VirtualKeyboardButton { + in property key; + in property icon; + + callback key-pressed(/* key */ string); + + min-width: 32px; + min-height: 32px; + horizontal-stretch: 0; + + states [ + pressed when i-touch-area.pressed : { + i-state-area.opacity: 0.5; + } + ] + + i-container := Rectangle { + border-radius: 4px; + background: Palette.color-scheme == ColorScheme.dark ? #373737 : #ffffff; + + HorizontalLayout { + padding: 8px; + + if (root.key != "") : Text { + text: root.key; + color: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000; + font-size: 12px; + vertical-alignment: center; + horizontal-alignment: center; + } + + if (root.key == "") : Image { + y: (parent.height - self.height) / 2; + source: root.icon; + height: 18px; + colorize: Palette.color-scheme == ColorScheme.dark ? #ffffff : #000000; + } + } + } + + i-state-area := Rectangle { + border-radius: i-container.border-radius; + opacity: 0; + background: #000000; + + animate opacity { duration: 150ms; } + } + + i-touch-area := TouchArea { + pointer-event(event) => { + if(event.kind == PointerEventKind.down) { + root.key-pressed(key); + } + } + } +} + +export struct KeyModel { + key: string, + shift-key: string, +} + +export global VirtualKeyboardHandler { + in property <[[[KeyModel]]]> default-key-sets: [ + [ + [ + { key: "q", shift-key: "Q" }, + { key: "w", shift-key: "W" }, + { key: "e", shift-key: "E" }, + { key: "r", shift-key: "R" }, + { key: "t", shift-key: "T" }, + { key: "y", shift-key: "Y" }, + { key: "u", shift-key: "U" }, + { key: "i", shift-key: "I" }, + { key: "o", shift-key: "O" }, + { key: "p", shift-key: "P" } + ], + [ + { key: "a", shift-key: "A" }, + { key: "s", shift-key: "S" }, + { key: "d", shift-key: "D" }, + { key: "f", shift-key: "F" }, + { key: "g", shift-key: "G" }, + { key: "h", shift-key: "H" }, + { key: "j", shift-key: "J" }, + { key: "k", shift-key: "K" }, + { key: "l", shift-key: "L" } + ], + [ + { key: "z", shift-key: "Z" }, + { key: "x", shift-key: "X" }, + { key: "c", shift-key: "C" }, + { key: "v", shift-key: "V" }, + { key: "b", shift-key: "B" }, + { key: "n", shift-key: "N" }, + { key: "m", shift-key: "M" }, + { key: ",", shift-key: ";" }, + { key: ".", shift-key: ":" }, + { key: "?", shift-key: "?" } + ], + ], + [ + [ + { key: "1", shift-key: "[" }, + { key: "2", shift-key: "]" }, + { key: "3", shift-key: "{" }, + { key: "4", shift-key: "}" }, + { key: "5", shift-key: "#" }, + { key: "6", shift-key: "%" }, + { key: "7", shift-key: "^" }, + { key: "8", shift-key: "*" }, + { key: "9", shift-key: "+" }, + { key: "0", shift-key: "=" } + ], + [ + { key: "-", shift-key: "_" }, + { key: "/", shift-key: "\\" }, + { key: ":", shift-key: "|" }, + { key: ";", shift-key: "~" }, + { key: "(", shift-key: "<" }, + { key: ")", shift-key: ">" }, + { key: "€", shift-key: "$" }, + { key: "&", shift-key: "€" }, + { key: "@", shift-key: "°" }, + { key: "'", shift-key: "#" }, + ], + [ + { key: ".", shift-key: "." }, + { key: ",", shift-key: "," }, + { key: "?", shift-key: "?" }, + { key: "!", shift-key: "!" }, + { key: "'", shift-key: "'" }, + ], + ] + ]; + + out property current-key-set; + out property <[[KeyModel]]> keys: default-key-sets[self.current-key-set]; + in-out property open; + + callback key_pressed(/* key */ string); + + public function switch-keyboard() { + if (self.current-key-set < self.default-key-sets.length - 1) { + self.current-key-set += 1; + } else { + self.current-key-set -= 1; + } + + self.current-key-set = min(self.default-key-sets.length - 1, max(0, self.current-key-set)) + } +} + +export component VirtualKeyboard { + private property shift; + + callback close(); + + preferred-width: 100%; + + TouchArea {} + + Rectangle { + background: Palette.color-scheme == ColorScheme.dark ? #1c1c1c : #d4d4d4; + height: 100%; + } + + i-layout := VerticalLayout { + padding: 8px; + spacing: 4px; + + for row[index] in VirtualKeyboardHandler.keys : HorizontalLayout { + spacing: 4px; + + if (index == 0) : VirtualKeyboardButton { + key: "ESC"; + + key-pressed => { + VirtualKeyboardHandler.key-pressed(Key.Escape); + } + } + + if (index == 1) : VirtualKeyboardButton { + key: "Tab"; + + key-pressed => { + VirtualKeyboardHandler.key-pressed(Key.Tab); + } + } + + // shift + if (index == 2) : VirtualKeyboardButton { + icon: Icons.arrow-up; + + key-pressed => { + root.shift = !root.shift; + } + } + + for km in row : VirtualKeyboardButton { + key: root.shift ? km.shift-key : km.key; + + key-pressed(key) => { + VirtualKeyboardHandler.key-pressed(key); + root.shift = false; + } + } + + if (index == 0) : VirtualKeyboardButton { + icon: Icons.chevron-left; + + key-pressed => { + VirtualKeyboardHandler.key-pressed(Key.Backspace); + } + } + + if (index == 1) : VirtualKeyboardButton { + icon: Icons.arrow-circle-o-left; + + key-pressed => { + VirtualKeyboardHandler.key-pressed(Key.Return); + } + } + + // shift + if (index == 2) : VirtualKeyboardButton { + icon: Icons.arrow-up; + + key-pressed => { + root.shift = !root.shift; + } + } + } + + HorizontalLayout { + spacing: 4px; + + VirtualKeyboardButton { + icon: Icons.expand-more; + + key-pressed(key) => { + root.close(); + } + } + + VirtualKeyboardButton { + icon: Icons.globe; + + key-pressed(key) => { + VirtualKeyboardHandler.switch-keyboard(); + } + } + VirtualKeyboardButton { + horizontal-stretch: 1; + key: " "; + + key-pressed(key) => { + root.shift = false; + VirtualKeyboardHandler.key-pressed(key); + } + } + VirtualKeyboardButton { + icon: Icons.arrow-left; + + key-pressed(key) => { + VirtualKeyboardHandler.key-pressed(Key.LeftArrow); + } + } + VirtualKeyboardButton { + icon: Icons.arrow-right; + + key-pressed(key) => { + VirtualKeyboardHandler.key-pressed(Key.RightArrow); + } + } + } + + + } + + animate y { duration: 500ms; easing: cubic-bezier(0.05, 0.7, 0.1, 1.0); } +}