Skip to content

Commit

Permalink
Added TodoMVC example (Rust mock version) (slint-ui#5396)
Browse files Browse the repository at this point in the history
* Added TodoMVC example (Rust mock version)

* TodoMVC: use visible-width instead of width for selection items

and format

* TodoMVC: layout fix for qt checkbox

* TdodoMVC: fix license issues in the example

* Update examples/todo_mvc/ui/views/task_list_view.slint

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* TdodoMVC: fix license issues in the example

* TodoMVC: code review changes

* TodoMVC: code review changes

* Update .reuse/dep5

Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev>

* Update examples/todo_mvc/rust/src/adapters/navigation_adapter.rs

Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev>

* Update examples/todo_mvc/rust/src/adapters/navigation_adapter.rs

Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev>

* TodoMVC: refactor task list model (code review feedback)

* TodoMVC: code review feedback

* Update examples/todo-mvc/rust/src/mvc/controllers/task_list_controller.rs

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* TodoMVC: add missing link in dep5

* dep5 fix

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Simon Hausmann <simon.hausmann@slint.dev>
  • Loading branch information
3 people authored Jun 13, 2024
1 parent a2e10f8 commit 0870585
Show file tree
Hide file tree
Showing 41 changed files with 1,651 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .github/workflows/wasm_demos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ jobs:
sed -i "s/#wasm# //" Cargo.toml
wasm-pack build --release --target web
working-directory: examples/todo/rust
- name: Todo mvc demo WASM build
run: |
sed -i "s/#wasm# //" Cargo.toml
wasm-pack build --release --target web
working-directory: examples/todo-mvc/rust
- name: Carousel demo WASM build
run: |
sed -i "s/#wasm# //" Cargo.toml
Expand Down
4 changes: 4 additions & 0 deletions .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ Files: internal/compiler/widgets/qt/_*.svg
Copyright: Material Icons <https://github.com/material-icons/material-icons/blob/master/LICENSE>
License: Apache-2.0

Files: examples/todo-mvc/assets/*.svg
Copyright: Material Icons <https://github.com/material-icons/material-icons/blob/master/LICENSE>
License: Apache-2.0

Files: internal/compiler/widgets/cosmic-base/_*.svg
Copyright: "Cosmic Icons" by System76 <https://github.com/pop-os/cosmic-icons>
License: CC-BY-SA-4.0
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
'examples/printerdemo_mcu',
'examples/slide_puzzle',
'examples/todo/rust',
'examples/todo-mvc/rust',
'examples/virtual_keyboard/rust',
'examples/carousel/rust',
'examples/energy-monitor',
Expand Down
10 changes: 9 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,22 @@ A fictional user interface of a device that monitors energy consumption in a bui

### [`todo`](./todo)

A simple todo mvc application
A simple todo application

| `.slint` Design | Rust Source | C++ Source | NodeJS | Online wasm Preview | Open in SlintPad |
| --- | --- | --- | --- | --- | --- |
| [`todo.slint`](./todo/ui/todo.slint) | [`main.rs`](./todo/rust/main.rs) | [`main.cpp`](./todo/cpp/main.cpp) | [`main.js`](./todo/node/main.js) | [Online simulation](https://slint.dev/snapshots/master/demos/todo/) | [Preview in Online Code Editor](https://slint.dev/snapshots/master/editor?load_url=https://raw.githubusercontent.com/slint-ui/slint/master/examples/todo/ui/todo.slint) |

![Screenshot of the Todo Demo](https://slint.dev/resources/todo_screenshot.png "Todo Demo")

### [`todo-mvc`](./todo-mvc)

A simple todo application based on the [Model View Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) pattern.

| `.slint` Design | Rust Source | Online wasm Preview | Open in SlintPad |
| --- | --- | --- | --- | --- | --- |
| [`todo.slint`](./todo/ui/todo.slint) | [`main.rs`](./todo/rust/main.rs) | [Online simulation](https://slint.dev/snapshots/master/demos/todo-mvc/) | [Preview in Online Code Editor](https://slint.dev/snapshots/master/editor?load_url=https://raw.githubusercontent.com/slint-ui/slint/master/examples/todo-mvc/ui/index.slint) |

### [`carousel`](./carousel)

A custom carousel widget that can be controlled by touch, mouse and keyboard
Expand Down
3 changes: 3 additions & 0 deletions examples/todo-mvc/assets/add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions examples/todo-mvc/assets/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions examples/todo-mvc/assets/remove.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions examples/todo-mvc/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: MIT

[package]
name = "todo-mvc"
version = "1.7.0"
authors = ["Slint Developers <info@slint.dev>"]
edition = "2021"
build = "build.rs"
publish = false
license = "MIT"

[lib]
crate-type = ["lib", "cdylib"]
path = "src/lib.rs"
name = "todo_lib_mvc"

[[bin]]
path = "src/main.rs"
name = "todo-mvc"

[dependencies]
slint = { path = "../../../api/rs/slint", features = ["serde", "backend-android-activity-06"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = { version = "0.4" }

[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" }

[dev-dependencies]
i-slint-backend-testing = { workspace = true }
6 changes: 6 additions & 0 deletions examples/todo-mvc/rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

fn main() {
slint_build::compile("../ui/index.slint").unwrap();
}
41 changes: 41 additions & 0 deletions examples/todo-mvc/rust/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>

<!-- Copyright © SixtyFPS GmbH <info@slint.dev> -->
<!-- SPDX-License-Identifier: MIT -->

<html>
<!--
This is a static html file used to display the wasm build.
In order to generate the build
- Run `wasm-pack build --release --target web` in this directory.
-->


<head>
<meta charset="UTF-8">
<title>Slint Todo MVC Demo (Web Assembly version)</title>
<link rel="stylesheet" href="https://slint.dev/css/demos-v1.css">
</head>

<body>
<p>This is the <a href="https://slint.dev">Slint</a> Todo Demo compiled to WebAssembly.</p>
<div id="spinner" style="position: relative;">
<div class="spinner">Loading...</div>
</div>
<canvas id="canvas" unselectable="on" data-slint-auto-resize-to-preferred="true"></canvas>
<p class="links">
<a href="https://github.com/slint-ui/slint/blob/master/examples/todo-mvc/">
View Source Code on GitHub</a> -
<a href="https://slint.dev/editor?load_demo=examples/todo-mvc/ui/index.slint">
Open in SlintPad
</a>
</p>
<script type="module">
import init from './pkg/todo_lib_mvc.js';
init().finally(() => {
document.getElementById("spinner").remove();
});
</script>
</body>

</html>
46 changes: 46 additions & 0 deletions examples/todo-mvc/rust/src/callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

use std::cell::Cell;

type CallbackWrapper<Arguments, Result = ()> =
Cell<Option<Box<dyn FnMut(&Arguments, &mut Result)>>>;

pub struct Callback<Arguments: ?Sized, Result = ()> {
callback: CallbackWrapper<Arguments, Result>,
}

impl<Arguments: ?Sized, Res> Default for Callback<Arguments, Res> {
fn default() -> Self {
Self { callback: Default::default() }
}
}

impl<Arguments: ?Sized, Result: Default> Callback<Arguments, Result> {
pub fn on(&self, mut f: impl FnMut(&Arguments) -> Result + 'static) {
self.callback.set(Some(Box::new(move |a: &Arguments, r: &mut Result| *r = f(a))));
}

pub fn invoke(&self, a: &Arguments) -> Result {
let mut result = Result::default();

if let Some(mut callback) = self.callback.take() {
callback(a, &mut result);
self.callback.set(Some(callback));
}

result
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_invoke() {
let callback: Callback<(i32, i32), i32> = Callback::default();
callback.on(|(a, b)| a + b);
assert_eq!(callback.invoke(&(3, 2)), 5);
}
}
39 changes: 39 additions & 0 deletions examples/todo-mvc/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

pub mod mvc;
pub mod ui;

mod callback;
pub use callback::*;
pub use slint::*;

#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
pub fn main() {
let main_window = init();

main_window.run().unwrap();
}

fn init() -> ui::MainWindow {
let view_handle = ui::MainWindow::new().unwrap();

let task_list_controller = mvc::TaskListController::new(mvc::task_repo());
ui::task_list_adapter::connect(&view_handle, task_list_controller.clone());
ui::navigation_adapter::connect_task_list_controller(
&view_handle,
task_list_controller.clone(),
);

let create_task_controller = mvc::CreateTaskController::new(mvc::date_time_repo());
ui::create_task_adapter::connect(&view_handle, create_task_controller.clone());
ui::navigation_adapter::connect_create_task_controller(&view_handle, create_task_controller);
ui::create_task_adapter::connect_task_list_controller(&view_handle, task_list_controller);

view_handle
}

// FIXME: android example
6 changes: 6 additions & 0 deletions examples/todo-mvc/rust/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

fn main() {
todo_lib_mvc::main();
}
11 changes: 11 additions & 0 deletions examples/todo-mvc/rust/src/mvc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

mod controllers;
pub use controllers::*;

mod models;
pub use models::*;

mod repositories;
pub use repositories::*;
8 changes: 8 additions & 0 deletions examples/todo-mvc/rust/src/mvc/controllers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

mod create_task_controller;
pub use create_task_controller::*;

mod task_list_controller;
pub use task_list_controller::*;
120 changes: 120 additions & 0 deletions examples/todo-mvc/rust/src/mvc/controllers/create_task_controller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

use std::rc::Rc;

use crate::mvc::{traits::DateTimeRepository, DateModel, TimeModel};
use crate::{mvc, Callback};

#[derive(Clone)]
pub struct CreateTaskController {
repo: Rc<dyn mvc::traits::DateTimeRepository>,
back_callback: Rc<Callback<(), ()>>,
}

impl CreateTaskController {
pub fn new(repo: impl DateTimeRepository + 'static) -> Self {
Self { repo: Rc::new(repo), back_callback: Rc::new(Callback::default()) }
}

pub fn current_date(&self) -> DateModel {
self.repo.current_date()
}

pub fn current_time(&self) -> TimeModel {
self.repo.current_time()
}

pub fn date_string(&self, date_model: DateModel) -> String {
self.repo.date_to_string(date_model)
}

pub fn time_string(&self, time_model: TimeModel) -> String {
self.repo.time_to_string(time_model)
}

pub fn back(&self) {
self.back_callback.invoke(&());
}

pub fn on_back(&self, mut callback: impl FnMut() + 'static) {
self.back_callback.on(move |()| {
callback();
});
}

pub fn time_stamp(&self, date_model: DateModel, time_model: TimeModel) -> i32 {
self.repo.time_stamp(date_model, time_model)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::mvc::MockDateTimeRepository;
use std::cell::Cell;

fn test_controller() -> CreateTaskController {
CreateTaskController::new(MockDateTimeRepository::new(
DateModel { year: 2024, month: 6, day: 12 },
TimeModel { hour: 13, minute: 30, second: 29 },
15,
))
}

#[test]
fn test_current_date() {
let controller = test_controller();
assert_eq!(controller.current_date(), DateModel { year: 2024, month: 6, day: 12 });
}

#[test]
fn test_current_time() {
let controller = test_controller();
assert_eq!(controller.current_time(), TimeModel { hour: 13, minute: 30, second: 29 });
}

#[test]
fn test_date_string() {
let controller = test_controller();
assert_eq!(
controller.date_string(DateModel { year: 2020, month: 10, day: 5 }).as_str(),
"2020/10/5"
);
}

#[test]
fn test_time_string() {
let controller = test_controller();
assert_eq!(
controller.time_string(TimeModel { hour: 10, minute: 12, second: 55 }).as_str(),
"10:12"
);
}

#[test]
fn test_back() {
let controller = test_controller();

let callback_invoked = Rc::new(Cell::new(false));

controller.on_back({
let callback_invoked = callback_invoked.clone();

move || {
callback_invoked.set(true);
}
});

controller.back();

assert!(callback_invoked.get());
}

#[test]
fn test_time_stamp() {
let controller = test_controller();

assert_eq!(controller.time_stamp(DateModel::default(), TimeModel::default()), 15);
}
}
Loading

0 comments on commit 0870585

Please sign in to comment.