From f03bc1b5a5fc8ff509877106bc0bdcff8d97058d Mon Sep 17 00:00:00 2001 From: Felix Zwettler Date: Tue, 22 Sep 2026 18:20:21 +0200 Subject: [PATCH 1/2] feat: add example for guillotine reactive UI lib --- Cargo.toml | 3 + inkview-eg/Cargo.toml | 53 ++++++----- inkview-eg/examples/guillotine.rs | 151 ++++++++++++++++++++++++++++++ justfile | 6 +- 4 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 inkview-eg/examples/guillotine.rs diff --git a/Cargo.toml b/Cargo.toml index 8b40b4f..684deb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,3 +111,6 @@ topo_order = false # Order of commits in each group/release within the changelog. # Allowed values: newest, oldest sort_commits = "newest" + +[patch.crates-io] +guillotine = { git = "https://github.com/mempirate/guillotine", branch = "main" } diff --git a/inkview-eg/Cargo.toml b/inkview-eg/Cargo.toml index bd0ed27..11b45d5 100644 --- a/inkview-eg/Cargo.toml +++ b/inkview-eg/Cargo.toml @@ -1,31 +1,42 @@ [package] +description = "embedded-graphics-core driver and guillotine UI library support for inkview-rs" +edition = "2024" name = "inkview-eg" version = "0.3.0" -edition = "2024" -description = "embedded-graphics-core driver for inkview-rs" -license = "MIT" +authors = ["flxzt"] keywords = ["inkview", "pocketbook", "eink"] +license = "MIT" repository = "https://github.com/simmsb/inkview-rs" -authors = ["flxzt"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -inkview = { path = "../inkview", version = "0.3.0", default-features = false} +embedded-graphics = { version = "0.8.2", optional = true } embedded-graphics-core = "0.4.1" +guillotine = { version = "0.4.0", optional = true } +inkview = { path = "../inkview", version = "0.3.0", default-features = false } [dev-dependencies] -embedded-graphics = "0.8.2" anyhow = "1.0" +embedded-graphics = "0.8.2" [features] default = ["sdk-6-11"] +guillotine = ["dep:guillotine", "dep:embedded-graphics", "guillotine/flexbox"] sdk-5-19 = ["inkview/sdk-5-19"] -sdk-6-5 = ["inkview/sdk-6-5"] -sdk-6-8 = ["inkview/sdk-6-8"] sdk-6-10 = ["inkview/sdk-6-10"] sdk-6-11 = ["inkview/sdk-6-11"] +sdk-6-5 = ["inkview/sdk-6-5"] +sdk-6-8 = ["inkview/sdk-6-8"] + +[[example]] +name = "shapes_and_text" +required-features = [] + +[[example]] +name = "guillotine" +required-features = ["guillotine"] [package.metadata.docs.rs] features = ["sdk-6-10"] @@ -109,24 +120,24 @@ conventional_commits = true filter_unconventional = false # An array of regex based parsers to modify commit messages prior to further processing. commit_preprocessors = [ - # Remove issue numbers. - { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, + # Remove issue numbers. + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, ] # An array of regex based parsers for extracting data from the commit message. # Assigns commits to groups. # Optionally sets the commit's scope and can decide to exclude commits from further processing. commit_parsers = [ - { message = "^[a|A]dd", group = "Added" }, - { message = "^[s|S]upport", group = "Added" }, - { message = "^[r|R]emove", group = "Removed" }, - { message = "^.*: add", group = "Added" }, - { message = "^.*: support", group = "Added" }, - { message = "^.*: remove", group = "Removed" }, - { message = "^.*: delete", group = "Removed" }, - { message = "^test", group = "Fixed" }, - { message = "^fix", group = "Fixed" }, - { message = "^.*: fix", group = "Fixed" }, - { message = "^.*", group = "Changed" }, + { message = "^[a|A]dd", group = "Added" }, + { message = "^[s|S]upport", group = "Added" }, + { message = "^[r|R]emove", group = "Removed" }, + { message = "^.*: add", group = "Added" }, + { message = "^.*: support", group = "Added" }, + { message = "^.*: remove", group = "Removed" }, + { message = "^.*: delete", group = "Removed" }, + { message = "^test", group = "Fixed" }, + { message = "^fix", group = "Fixed" }, + { message = "^.*: fix", group = "Fixed" }, + { message = "^.*", group = "Changed" }, ] # Exclude commits that are not matched by any commit parser. filter_commits = false diff --git a/inkview-eg/examples/guillotine.rs b/inkview-eg/examples/guillotine.rs new file mode 100644 index 0000000..577e544 --- /dev/null +++ b/inkview-eg/examples/guillotine.rs @@ -0,0 +1,151 @@ +use embedded_graphics::mono_font::ascii::FONT_10X20; +use embedded_graphics::prelude::*; +use embedded_graphics_core::pixelcolor::Gray8; +use guillotine::style::{AlignItems, JustifyContent}; +use guillotine::{ + Context, DirectTarget, ElementBuilder, Font, FrameStorage, ParentElement, Render, + StyledElement, StyledFlexContainer, TextStyledElement, Ui, +}; +use inkview::Event; +use inkview::event::Key; +use inkview_eg::InkviewDisplay; +use std::sync::LazyLock; + +const ACCENT: Gray8 = Gray8::new(0x88); + +struct BasicView { + size: Size, + dark: bool, +} + +impl Render for BasicView { + // Render lets you declaratively build your UI tree. + fn render(&self, cx: &Context<'_, Gray8>) -> impl ElementBuilder { + let bg = if self.dark { + Gray8::BLACK + } else { + Gray8::WHITE + }; + let fg = if self.dark { + Gray8::WHITE + } else { + Gray8::BLACK + }; + let greeting = if self.dark { + "Dark theme" + } else { + "Light theme" + }; + + cx.column() + .size(self.size) + .padding(12) + .gap(8) + .background(bg) + .justify_content(JustifyContent::Center) + .align_items(AlignItems::Center) + .child( + cx.row() + .gap(8) + .child( + cx.text("GUILLOTINE") + .flex_grow(1) + .font(Font::mono(&FONT_10X20)) + .text_color(ACCENT), + ) + .child( + cx.text("READY") + .padding((4, 9)) + .background(ACCENT) + .text_color(fg) + .font(Font::mono(&FONT_10X20)), + ), + ) + .child( + cx.row() + .gap(8) + .child( + cx.text(greeting) + .font(Font::mono(&FONT_10X20)) + .flex(3) + .padding(10) + .background(bg) + .text_color(fg), + ) + .child( + cx.text("7 nodes, 54 bytes") + .font(Font::mono(&FONT_10X20)) + .flex(2) + .padding(6) + .background(bg) + .text_color(fg), + ), + ) + } +} + +fn main() { + let (event_tx, event_rx) = std::sync::mpsc::channel::(); + let iv = Box::leak(Box::new(inkview::load())) as &_; + + std::thread::spawn(move || { + let mut view = BasicView { + dark: false, + size: Size::new(320, 240), + }; + let mut ui = LazyLock::new(|| { + let mut display = InkviewDisplay::new(iv); + display.clear(Gray8::WHITE); + let storage = FrameStorage::::default(); + let theme = guillotine::Theme { + background: Gray8::WHITE, + foreground: Gray8::BLACK, + }; + + Ui::with_theme(DirectTarget::new(display), storage, theme) + }); + + loop { + let mut render = false; + + let event = match event_rx.recv() { + Ok(e) => e, + Err(e) => { + eprintln!("Receiving inkview event failed, Err: {e:?}"); + break; + } + }; + match event { + Event::Init => { + let ui = LazyLock::force(&ui); + view.size = ui.display().size(); + } + Event::Show | Event::Repaint => { + render = true; + } + Event::KeyDown { key: Key::Menu } => { + view.dark = !view.dark; + render = true; + } + Event::KeyDown { .. } | Event::Exit => break, + _ => {} + } + + if render && let Some(ui) = LazyLock::get_mut(&mut ui) { + if let Err(err) = ui.render(&view) { + eprintln!("Unable to render app: {err}"); + } + ui.display_mut().flush(); + } + } + + unsafe { iv.CloseApp() } + }); + + inkview::iv_main(&iv, move |event| { + if let Err(e) = event_tx.send(event) { + eprintln!("Sending inkview event failed, Err: {e:?}"); + } + Some(()) + }); +} diff --git a/justfile b/justfile index 8a2ef2b..f7b0c1c 100644 --- a/justfile +++ b/justfile @@ -2,6 +2,8 @@ # Cargo build profile. cargo_profile := "dev" +# Additional cargo features +cargo_features := "" # Pocketbook device identifier as it's folder name when connected with USB. # - Pocketbook Inkpad 4: "PB743G" # - Pocketbook Touch Lux 3: "PB626" @@ -62,12 +64,12 @@ prerequisites: build-app name: cargo zigbuild --target {{zigbuild_target}} --profile {{cargo_profile}} -p {{name}} --no-default-features \ - --features={{cargo_sdk_feature}} + --features="{{cargo_features}} {{cargo_sdk_feature}}" execstack -s "target/{{build_target / cargo_out_profile / name}}" build-example crate name: cargo zigbuild --target {{zigbuild_target}} --profile {{cargo_profile}} -p {{crate}} --example {{name}} \ - --no-default-features --features={{cargo_sdk_feature}} + --no-default-features --features="{{cargo_features}} {{cargo_sdk_feature}}" execstack -s "target/{{build_target / cargo_out_profile / 'examples' / name}}" [doc(""" From 84698e0a6f836858bce60a84012179b0cc349c36 Mon Sep 17 00:00:00 2001 From: Felix Zwettler Date: Tue, 22 Sep 2026 18:20:40 +0200 Subject: [PATCH 2/2] chore: add vscode config --- .vscode/settings.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b72f212 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "rust-analyzer.cargo.features": [ + "sdk-6-11", + "guillotine", + ], + "rust-analyzer.cargo.noDefaultFeatures": true, + "search.exclude": { + "**/pocketbook-sdks": true, + } +} \ No newline at end of file