diff --git a/.github/workflows/wasm_demos.yaml b/.github/workflows/wasm_demos.yaml index d890e7523..b5c9b50a0 100644 --- a/.github/workflows/wasm_demos.yaml +++ b/.github/workflows/wasm_demos.yaml @@ -40,6 +40,11 @@ jobs: sed -i "s/#wasm# //" Cargo.toml wasm-pack build --release --target web working-directory: examples/todo/rust + - name: Carousel demo WASM build + run: | + sed -i "s/#wasm# //" Cargo.toml + wasm-pack build --release --target web + working-directory: examples/carousel/rust - name: Sliding Puzzle demo WASM build run: | sed -i "s/#wasm# //" Cargo.toml @@ -74,6 +79,7 @@ jobs: examples/printerdemo/rust/ examples/printerdemo_old/rust/ examples/todo/ + examples/carousel/ examples/memory/ examples/slide_puzzle/ examples/imagefilter/ diff --git a/.gitignore b/.gitignore index 18199f30d..0e6769b5b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ tools/figma_import/target tools/figma_import/figma_output *.code-workspace /build -/_deps \ No newline at end of file +/_deps +package-lock.json \ No newline at end of file diff --git a/.reuse/dep5 b/.reuse/dep5 index af820e660..7bd62b5aa 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -62,3 +62,11 @@ License: GPL-3.0-only OR LicenseRef-Slint-commercial Files: editors/vscode/*.json editors/vscode/README.md Copyright: Copyright © SixtyFPS GmbH License: GPL-3.0-only OR LicenseRef-Slint-commercial + +Files: examples/carousel/icons/*.svg +Copyright: Material Icons +License: Apache-2.0 + +Files: examples/carousel/fonts/*.ttf +Copyright: Roboto +License: Apache-2.0 \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1de4bcfc3..3954e05a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ 'examples/printerdemo_mcu', 'examples/slide_puzzle', 'examples/todo/rust', + 'examples/carousel/rust', 'examples/mcu-board-support', 'helper_crates/const-field-offset', 'helper_crates/vtable', @@ -58,6 +59,7 @@ default-members = [ 'examples/printerdemo/rust', 'examples/slide_puzzle', 'examples/todo/rust', + 'examples/carousel/rust', 'internal/backends/winit', 'internal/backends/qt', 'internal/backends/selector', diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6833a6686..53714f15c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,6 +19,7 @@ if (SLINT_FEATURE_INTERPRETER) add_subdirectory(printerdemo/cpp_interpreted/) endif() if (TARGET Slint::slint-compiler) + add_subdirectory(carousel/cpp/) add_subdirectory(printerdemo_old/cpp/) add_subdirectory(todo/cpp/) add_subdirectory(gallery/) diff --git a/examples/README.md b/examples/README.md index 3e97a9f97..120383eea 100644 --- a/examples/README.md +++ b/examples/README.md @@ -32,6 +32,16 @@ A simple todo mvc application ![Screenshot of the Todo Demo](https://slint-ui.com/resources/todo_screenshot.png "Todo Demo") +### [`carousel`](./carousel) + +A custom carousel widget that can be controlled by touch, mouse and keyboard + +| `.slint` Design | Rust Source | C++ Source | Node Source | Online wasm Preview | Open in code editor | +| --- | --- | --- | --- | --- | --- | +| [`ui.slint`](./carousel/ui/carousel_demo.slint) | [`main.rs`](./carousel/rust/main.rs) | [`main.cpp`](./carousel/cpp/main.cpp) | [`main.js`](./carousel/node/main.js) | [Online simulation](https://slint-ui.com/snapshots/master/demos/carousel/) | [Preview in Online Code Editor](https://slint-ui.com/snapshots/master/editor?load_url=https://raw.githubusercontent.com/slint-ui/slint/master/examples/carousel/ui/carousel_demo.slint) | + +![Screenshot of the Carousel Demo](https://user-images.githubusercontent.com/6715107/196391434-a8e9db64-a14c-4a9c-b08c-5a9fc74e4e3d.png "Carousel Demo") + ### [`slide_puzzle`](./slide_puzzle) Puzzle game based on a Flutter example. See [Readme](./slide_puzzle) diff --git a/examples/carousel/cpp/CMakeLists.txt b/examples/carousel/cpp/CMakeLists.txt new file mode 100644 index 000000000..6e580e7ae --- /dev/null +++ b/examples/carousel/cpp/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +cmake_minimum_required(VERSION 3.14) +project(slint_cpp_carousel LANGUAGES CXX) + +if (NOT TARGET Slint::Slint) + find_package(Slint REQUIRED) +endif() + +add_executable(carousel main.cpp) +target_link_libraries(carousel PRIVATE Slint::Slint) +slint_target_sources(carousel ../ui/carousel_demo.slint) diff --git a/examples/carousel/cpp/main.cpp b/examples/carousel/cpp/main.cpp new file mode 100644 index 000000000..6ab24f2c8 --- /dev/null +++ b/examples/carousel/cpp/main.cpp @@ -0,0 +1,9 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +#include "carousel_demo.h" + +int main() +{ + MainWindow::create()->run(); +} diff --git a/examples/carousel/node/README b/examples/carousel/node/README new file mode 100644 index 000000000..9af35b6a8 --- /dev/null +++ b/examples/carousel/node/README @@ -0,0 +1,4 @@ +Run with + +# npm install +# npm start diff --git a/examples/carousel/node/main.js b/examples/carousel/node/main.js new file mode 100644 index 000000000..f9a46d87e --- /dev/null +++ b/examples/carousel/node/main.js @@ -0,0 +1,11 @@ +#!/usr/bin/env node +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +const path = require("path"); +let slint = require("slint-ui"); + +let demo = require("../ui/carousel_demo.slint"); +let app = new demo.MainWindow(); + +app.run(); diff --git a/examples/carousel/node/package.json b/examples/carousel/node/package.json new file mode 100644 index 000000000..4bed05259 --- /dev/null +++ b/examples/carousel/node/package.json @@ -0,0 +1,11 @@ +{ + "name": "carousel", + "version": "0.3.1", + "main": "main.js", + "dependencies": { + "slint-ui": "../../../api/node" + }, + "scripts": { + "start": "node ." + } +} \ No newline at end of file diff --git a/examples/carousel/rust/Cargo.toml b/examples/carousel/rust/Cargo.toml new file mode 100644 index 000000000..13c746b19 --- /dev/null +++ b/examples/carousel/rust/Cargo.toml @@ -0,0 +1,34 @@ +# Copyright © SixtyFPS GmbH +# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +[package] +name = "carousel" +version = "0.3.1" +authors = ["Slint Developers "] +edition = "2021" +build = "build.rs" +publish = false +license = "GPL-3.0-only OR LicenseRef-Slint-commercial" + +[[bin]] +path = "main.rs" +name = "carousel" + +[dependencies] +slint = { path = "../../../api/rs/slint" } + +[build-dependencies] +slint-build = { path = "../../../api/rs/build" } + +# Remove the `#wasm#` to uncomment the wasm build. +# This is commented out by default because we don't want to build it as a library by default +# The CI has a script that does sed "s/#wasm# //" to generate the wasm build. + +#wasm# [lib] +#wasm# crate-type = ["cdylib"] +#wasm# path = "main.rs" +#wasm# +#wasm# [target.'cfg(target_arch = "wasm32")'.dependencies] +#wasm# wasm-bindgen = { version = "0.2" } +#wasm# web-sys = { version = "0.3", features=["console"] } +#wasm# console_error_panic_hook = "0.1.5" diff --git a/examples/carousel/rust/build.rs b/examples/carousel/rust/build.rs new file mode 100644 index 000000000..e8b88f7bb --- /dev/null +++ b/examples/carousel/rust/build.rs @@ -0,0 +1,6 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +fn main() { + slint_build::compile("../ui/carousel_demo.slint").unwrap(); +} diff --git a/examples/carousel/rust/index.html b/examples/carousel/rust/index.html new file mode 100644 index 000000000..138033558 --- /dev/null +++ b/examples/carousel/rust/index.html @@ -0,0 +1,62 @@ + + + + + + + + + + + Slint Carousel example (Web Assembly version) + + + + + +

Carousel example

+

This is the Slint Carousel example compiled to + WebAssembly.

+
+
Loading...
+
+ + + + + + diff --git a/examples/carousel/rust/main.rs b/examples/carousel/rust/main.rs new file mode 100644 index 000000000..1fe68cb72 --- /dev/null +++ b/examples/carousel/rust/main.rs @@ -0,0 +1,17 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +#[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(); + + MainWindow::new().run() +} diff --git a/examples/carousel/ui/card.slint b/examples/carousel/ui/card.slint new file mode 100644 index 000000000..d64304887 --- /dev/null +++ b/examples/carousel/ui/card.slint @@ -0,0 +1,72 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "theme.slint"; +import { TitleLabel } from "title_label.slint"; + +export Card := Rectangle { + callback clicked <=> touch-area.clicked; + + property title: "title"; + property is-selected: false; + property image-source <=> image.source; + property spacing: Theme.spacing-medium; + property title-spacing: Theme.spacing-medium; + property title-area-height: Theme.size-small; + + background: transparent; + width: Theme.size-medium; + height: Theme.size-medium; + + // background + background := Rectangle { + opacity: 0.95; + clip: true; + border-radius: Theme.radius-regular; + background: Theme.background-regular; + + touch-area := TouchArea {} + + image := Image { + x: (parent.width - width) / 2; + y: (parent.height - height) / 2; + width: 80%; + height: 80%; + colorize: Theme.foreground; + + animate colorize { duration: Theme.duration-fast; } + } + + states [ + pressed when touch-area.pressed : { + background: Theme.background-pressed; + image.colorize: Theme.foreground-hover; + } + hover when touch-area.has-hover : { + background: Theme.background-hover; + image.colorize: Theme.foreground-hover; + } + ] + + animate background { duration: Theme.duration-fast; } + } + + // Selection text + titleArea := TitleLabel { + x: root.spacing + parent.width; + y: parent.height - self.height; + text <=> title; + visible: false; + } + + states [ + selected when is-selected : { + width: Theme.size-big; + height: Theme.size-big; + titleArea.visible: true; + } + ] + + animate width { duration: Theme.duration-regular; easing: ease-in; } + animate height { duration: Theme.duration-regular; easing: ease-in; } +} \ No newline at end of file diff --git a/examples/carousel/ui/carousel.slint b/examples/carousel/ui/carousel.slint new file mode 100644 index 000000000..5ee0a4e87 --- /dev/null +++ b/examples/carousel/ui/carousel.slint @@ -0,0 +1,76 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "theme.slint"; + +export Carousel := FocusScope { + callback move-right(); + callback move-left(); + callback move-focus-up(); + + property selected-index; + property spacing; + property itemWidth; + property count: 0; + property center-x: (width - Theme.size-big) / 2; + property duation: Theme.duration-regular; + + forward-focus: focus-scope; + height: Theme.size-big; + + move-right => { + root.selected-index = min(root.selected-index + 1, root.count - 1); + } + + move-left => { + root.selected-index = max(root.selected-index - 1, 0); + } + + focus-scope:= FocusScope { + key-pressed(event) => { + if(event.text == Keys.UpArrow) { + root.move-focus-up(); + return accept; + } + + if(event.text == Keys.RightArrow) { + root.move-right(); + return accept; + } + + if(event.text == Keys.LeftArrow) { + root.move-left(); + return accept; + } + + return accept; + } + } + + TouchArea { + width: parent.width; + height: parent.height; + + clicked => { + focus-scope.focus() + } + } + + + Rectangle { + clip: true; + background: transparent; + + Flickable { + interactive: false; + animate viewport-x { duration: duation; easing: ease-in; } + viewport-x: center-x - root.selected-index * (root.itemWidth + root.spacing); + + HorizontalLayout { + spacing <=> root.spacing; + + @children + } + } + } +} \ No newline at end of file diff --git a/examples/carousel/ui/carousel_demo.slint b/examples/carousel/ui/carousel_demo.slint new file mode 100644 index 000000000..a02f5e72e --- /dev/null +++ b/examples/carousel/ui/carousel_demo.slint @@ -0,0 +1,40 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Carousel } from "carousel.slint"; +import { Card } from "card.slint"; +import { Theme } from "theme.slint"; + +MainWindow := Window { + property<[{ title: string, image: image}]> navigation-items: [ + { title: "Home", image: @image-url("icons/home_black_24dp.svg") }, + { title: "Settings", image: @image-url("icons/settings_black_24dp.svg") }, + { title: "About", image: @image-url("icons/info_black_24dp.svg") }, + ]; + property selected-index: 0; + + title: "Carousel example"; + preferred-width: 600px; + preferred-height: 400px; + background: Theme.window-background; + padding: Theme.spacing-regular; + forward-focus: carousel; + + carousel := Carousel { + y: (root.height - height) / 2; + // width of a `Card` + itemWidth: Theme.size-medium; + count: navigation-items.length; + selected-index <=> root.selected-index; + spacing: Theme.spacing-medium; + + for item[index] in navigation-items : Card { + is-selected: index == selected-index; + title: item.title; + image-source: item.image; + y: (parent.height - height) / 2; + + clicked => { selected-index = index; } + } + } +} \ No newline at end of file diff --git a/examples/carousel/ui/fonts/Roboto-Bold.ttf b/examples/carousel/ui/fonts/Roboto-Bold.ttf new file mode 100644 index 000000000..374245790 Binary files /dev/null and b/examples/carousel/ui/fonts/Roboto-Bold.ttf differ diff --git a/examples/carousel/ui/fonts/Roboto-Bold.ttf.license b/examples/carousel/ui/fonts/Roboto-Bold.ttf.license new file mode 100644 index 000000000..1be608fc7 --- /dev/null +++ b/examples/carousel/ui/fonts/Roboto-Bold.ttf.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Google Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/carousel/ui/fonts/Roboto-Regular.ttf b/examples/carousel/ui/fonts/Roboto-Regular.ttf new file mode 100644 index 000000000..3d6861b42 Binary files /dev/null and b/examples/carousel/ui/fonts/Roboto-Regular.ttf differ diff --git a/examples/carousel/ui/fonts/Roboto-Regular.ttf.license b/examples/carousel/ui/fonts/Roboto-Regular.ttf.license new file mode 100644 index 000000000..1be608fc7 --- /dev/null +++ b/examples/carousel/ui/fonts/Roboto-Regular.ttf.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Google Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/carousel/ui/icons/README.md b/examples/carousel/ui/icons/README.md new file mode 100644 index 000000000..c34f1ca67 --- /dev/null +++ b/examples/carousel/ui/icons/README.md @@ -0,0 +1,3 @@ +The icons originate from Material-Icons font ( https://github.com/marella/material-design-icons/tree/main/svg ) and licensed under the Apache License Version 2.0 (SVG download) + + https://github.com/marella/material-design-icons/blob/main/svg/LICENSE \ No newline at end of file diff --git a/examples/carousel/ui/icons/home_black_24dp.svg b/examples/carousel/ui/icons/home_black_24dp.svg new file mode 100644 index 000000000..403bf3b00 --- /dev/null +++ b/examples/carousel/ui/icons/home_black_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/carousel/ui/icons/home_black_24dp.svg.license b/examples/carousel/ui/icons/home_black_24dp.svg.license new file mode 100644 index 000000000..3e8e5bff9 --- /dev/null +++ b/examples/carousel/ui/icons/home_black_24dp.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Google Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/carousel/ui/icons/info_black_24dp.svg b/examples/carousel/ui/icons/info_black_24dp.svg new file mode 100644 index 000000000..d6564d6e8 --- /dev/null +++ b/examples/carousel/ui/icons/info_black_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/carousel/ui/icons/info_black_24dp.svg.license b/examples/carousel/ui/icons/info_black_24dp.svg.license new file mode 100644 index 000000000..3e8e5bff9 --- /dev/null +++ b/examples/carousel/ui/icons/info_black_24dp.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Google Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/carousel/ui/icons/settings_black_24dp.svg b/examples/carousel/ui/icons/settings_black_24dp.svg new file mode 100644 index 000000000..4165162bf --- /dev/null +++ b/examples/carousel/ui/icons/settings_black_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/carousel/ui/icons/settings_black_24dp.svg.license b/examples/carousel/ui/icons/settings_black_24dp.svg.license new file mode 100644 index 000000000..3e8e5bff9 --- /dev/null +++ b/examples/carousel/ui/icons/settings_black_24dp.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Google Inc. + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/carousel/ui/label.slint b/examples/carousel/ui/label.slint new file mode 100644 index 000000000..f40185ebf --- /dev/null +++ b/examples/carousel/ui/label.slint @@ -0,0 +1,11 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "theme.slint"; + +export Label := Text { + font-family: Theme.font-family; + font-size: Theme.font-size-regular; + font-weight: Theme.font-weight-regular; + color: Theme.foreground; +} \ No newline at end of file diff --git a/examples/carousel/ui/theme.slint b/examples/carousel/ui/theme.slint new file mode 100644 index 000000000..7cbc17bec --- /dev/null +++ b/examples/carousel/ui/theme.slint @@ -0,0 +1,39 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import "fonts/Roboto-Regular.ttf"; +import "fonts/Roboto-Bold.ttf"; + +export global Theme := { + // brushes + property window-background: #2C2F36; + property background-regular: #0025FF; + property background-hover: #FFFFFF; + property background-pressed: #BDBDBD; + property foreground: #FFFFFF; + property foreground-hover: #0025FF; + + // durations + property duration-fast: 100ms; + property duration-regular: 250ms; + + // radius + property radius-regular: 16px; + + // sizes + property size-small: 24px; + property size-regular: 32px; + property size-medium: 128px; + property size-big: 200px; + + // spacings + property spacing-regular: 4px; + property spacing-medium: 8px; + + // typo + property font-family: "Roboto"; + property font-size-regular: 12px; + property font-size-extra-medium: 24px; + property font-weight-regular: 400; + property font-weight-bold: 900; +} \ No newline at end of file diff --git a/examples/carousel/ui/title_label.slint b/examples/carousel/ui/title_label.slint new file mode 100644 index 000000000..8c9f4fe9c --- /dev/null +++ b/examples/carousel/ui/title_label.slint @@ -0,0 +1,10 @@ +// Copyright © SixtyFPS GmbH +// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial + +import { Theme } from "theme.slint"; +import { Label } from "label.slint"; + +export TitleLabel := Label { + font-size: Theme.font-size-extra-medium; + font-weight: Theme.font-weight-bold; +} \ No newline at end of file