From 4bb3a91b4ce6a73c3db78f38090129de90223d86 Mon Sep 17 00:00:00 2001 From: bikathi-martin Date: Mon, 20 Oct 2025 13:07:56 +0300 Subject: [PATCH] Provide calculator UI --- examples/calculator-ui/Cargo.toml | 7 + examples/calculator-ui/src/base_styles.rs | 76 +++++++++++ examples/calculator-ui/src/main.rs | 152 ++++++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 examples/calculator-ui/Cargo.toml create mode 100644 examples/calculator-ui/src/base_styles.rs create mode 100644 examples/calculator-ui/src/main.rs diff --git a/examples/calculator-ui/Cargo.toml b/examples/calculator-ui/Cargo.toml new file mode 100644 index 000000000..6324f1912 --- /dev/null +++ b/examples/calculator-ui/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "floem-calculator" +version = "0.1.0" +edition = "2024" + +[dependencies] +floem = { git = "https://github.com/lapce/floem" } diff --git a/examples/calculator-ui/src/base_styles.rs b/examples/calculator-ui/src/base_styles.rs new file mode 100644 index 000000000..a92974c2f --- /dev/null +++ b/examples/calculator-ui/src/base_styles.rs @@ -0,0 +1,76 @@ +use std::time::Duration; + +use floem::{ + peniko::Color, + style::{Style, TextColor, Transition}, + style_class, + views::{Button, Decorators, button}, +}; + +style_class!(pub InputBtn); +style_class!(pub AppWindow); +style_class!(pub InputButtonIsland); +style_class!(pub OutputTxt); + +pub fn default_theme_buttons_style() -> Style { + Style::new() + .height_full() + .flex() + .items_center() + .justify_center() + .width(50f64) +} + +pub fn render_input_button(text: &'static str) -> Button { + button(text).class(InputBtn).style(|s| { + s.flex() + .items_center() + .justify_center() + .border_top_left_radius(50) + .border_bottom_left_radius(50) + .border_bottom_right_radius(50) + .border_top_right_radius(50) + .font_bold() + .font_size(24f64) + }) +} + +fn dark_input_button() -> Style { + Style::new() + .background(Color::from_rgb8(223, 208, 184)) + .color(Color::from_rgb8(148, 137, 121)) + .transition( + TextColor, + Transition::ease_in_out(Duration::from_millis(5000)), + ) +} + +fn light_input_button() -> Style { + Style::new() + .background(Color::from_rgb8(255, 239, 239)) + .color(Color::from_rgb8(243, 208, 215)) + .transition( + TextColor, + Transition::ease_in_out(Duration::from_millis(1000)), + ) +} + +pub fn dark_theme() -> Style { + Style::new() + .background(Color::from_rgb8(34, 40, 49)) + .class(InputBtn, move |_| dark_input_button()) + .class(InputButtonIsland, move |s| { + s.background(Color::from_rgb8(57, 62, 70)) + }) + .class(OutputTxt, move |s| s.color(Color::from_rgb8(148, 137, 121))) +} + +pub fn light_theme() -> Style { + Style::new() + .background(Color::from_rgb8(246, 245, 242)) + .class(InputBtn, move |_| light_input_button()) + .class(InputButtonIsland, move |s| { + s.background(Color::from_rgb8(240, 235, 227)) + }) + .class(OutputTxt, move |s| s.color(Color::from_rgb8(243, 208, 215))) +} diff --git a/examples/calculator-ui/src/main.rs b/examples/calculator-ui/src/main.rs new file mode 100644 index 000000000..16e1cb9bf --- /dev/null +++ b/examples/calculator-ui/src/main.rs @@ -0,0 +1,152 @@ +use floem::{ + kurbo::Size, + prelude::{palette::css, *}, + reactive::WriteSignal, + taffy::{self, prelude::FromFr}, + unit::{Px, PxPct}, + views::{Decorators, container, svg}, + window::WindowConfig, +}; +mod base_styles; + +fn theme_switch_stack(set_theme: WriteSignal) -> Stack { + // sun icon + let svg_content: &str = r##" + + + + "##; + + let moon_content: &str = r##" + + + "##; + + h_stack(( + button(svg(svg_content).style(|s| s.size(22, 22).color(css::WHITE))) + .style(|s| { + s.border_top_left_radius(PxPct::Px(10f64)) + .border_bottom_left_radius(PxPct::Px(10f64)) + .apply(base_styles::default_theme_buttons_style()) + }) + .action(move || set_theme.update(|is_theme_dark| *is_theme_dark = false)), + button(svg(moon_content).style(|s| s.size(22, 22).color(css::WHITE))) + .style(|s| { + s.border_top_right_radius(PxPct::Px(10f64)) + .border_bottom_right_radius(PxPct::Px(10f64)) + .apply(base_styles::default_theme_buttons_style()) + }) + .action(move || set_theme.update(|is_theme_dark| *is_theme_dark = true)), + )) + .style(|s| { + s.flex() + .items_center() + .justify_center() + .width_full() + .height(36f64) + }) +} + +fn calculator_buttons_stack() -> Stack { + v_stack(( + base_styles::render_input_button("7"), + base_styles::render_input_button("8"), + base_styles::render_input_button("9"), + base_styles::render_input_button("/"), + base_styles::render_input_button("4"), + base_styles::render_input_button("5"), + base_styles::render_input_button("6"), + base_styles::render_input_button("*"), + base_styles::render_input_button("1"), + base_styles::render_input_button("2"), + base_styles::render_input_button("3"), + base_styles::render_input_button("-"), + base_styles::render_input_button("0"), + base_styles::render_input_button("CLS"), + base_styles::render_input_button("="), + base_styles::render_input_button("+"), + )) + .style(|s| { + s.display(taffy::style::Display::Grid) + .grid_template_columns(vec![ + taffy::style::TrackSizingFunction::from_fr(25.0), + taffy::style::TrackSizingFunction::from_fr(25.0), + taffy::style::TrackSizingFunction::from_fr(25.0), + taffy::style::TrackSizingFunction::from_fr(25.0), + ]) + .gap(5) + .flex_grow(100f32) + .border_top_left_radius(30f32) + .border_top_right_radius(30f32) + .border_bottom_left_radius(30f32) + .border_bottom_right_radius(30f32) + .padding(16f32) + }) + .class(base_styles::InputButtonIsland) +} + +fn calculator_view() -> impl IntoView { + let (is_dark_theme, set_theme) = create_signal(true); + + v_stack(( + theme_switch_stack(set_theme), + container((label(move || String::from("0")) + .style(|s| s.font_size(Px(32f64)).font_bold()) + .class(base_styles::OutputTxt),)) + .style(|s| { + s.width_full() + .flex() + .justify_end() + .items_end() + .height(200f64) + }), + calculator_buttons_stack(), + )) + .style(move |_| { + { + if is_dark_theme.get() { + base_styles::dark_theme() + } else { + base_styles::light_theme() + } + } + .size_full() + .flex() + .flex_col() + .gap(10) + .padding(5) + }) +} + +fn main() { + floem::Application::new() + .window( + |_| calculator_view(), + Some( + WindowConfig::default() + .apply_default_theme(false) + .resizable(false) + .size(Size { + width: 400f64, + height: 800f64, + }), + ), + ) + .run() +}