diff --git a/xilem/Cargo.toml b/xilem/Cargo.toml index 3d28144f5..e1dce10d8 100644 --- a/xilem/Cargo.toml +++ b/xilem/Cargo.toml @@ -30,6 +30,9 @@ crate-type = ["cdylib"] [[example]] name = "calc" +[[example]] +name = "calendar" + [[example]] name = "calc_android" path = "examples/calc.rs" @@ -66,11 +69,9 @@ vello.workspace = true smallvec.workspace = true accesskit.workspace = true tokio = { version = "1.39.1", features = ["rt", "rt-multi-thread", "time"] } - -[dev-dependencies] -# Used for `variable_clock` time = { workspace = true, features = ["local-offset"] } +[dev-dependencies] # Make wgpu use tracing for its spans. profiling = { version = "1.0.15", features = ["profile-with-tracing"] } diff --git a/xilem/examples/calendar.rs b/xilem/examples/calendar.rs new file mode 100644 index 000000000..e90a1cd01 --- /dev/null +++ b/xilem/examples/calendar.rs @@ -0,0 +1,72 @@ +use std::fmt::format; + +// Copyright 2024 the Xilem Authors +// SPDX-License-Identifier: Apache-2.0 +use masonry::widget::{CrossAxisAlignment, MainAxisAlignment}; +use time::{util::days_in_year_month, Date, Month, OffsetDateTime}; +use winit::error::EventLoopError; +use xilem::{ + view::{button, date, flex, label, sized_box, Axis, DateData, FlexExt as _, FlexSpacer}, + EventLoop, WidgetView, Xilem, +}; +use xilem_core::{frozen, map_state}; + +struct Calendar { + // selected_date: Date, + // month: Month, + // year: i32, + date: DateData, +} + +impl Calendar { + fn new() -> Self { + let now = OffsetDateTime::now_utc(); + Self { + // selected_date: , + // month: now.month(), + // year: now.year(), + date: DateData::new(now.date(), now.month(), now.year()), + } + } +} +fn selected_date(selected_date: Date) -> impl WidgetView { + flex((label("Selected date:"), label(format!("{selected_date}")))).direction(Axis::Horizontal) +} + +/// A component to make a bigger than usual button +fn external_controls() -> impl WidgetView { + flex(( + button("Today", |data: &mut Calendar| { + data.date.selected_date = OffsetDateTime::now_utc().date(); + }), + button("Tomorrow", |data: &mut Calendar| { + data.date.selected_date = OffsetDateTime::now_utc().date().next_day().unwrap(); + }), + )) + .direction(Axis::Horizontal) +} + +fn app_logic(data: &mut Calendar) -> impl WidgetView { + flex(( + selected_date(data.date.selected_date), + external_controls(), + map_state( + date( + &mut data.date.selected_date, + &mut data.date.month, + &mut data.date.year, + ), + |data: &mut Calendar| &mut data.date, + ), + )) + .direction(Axis::Vertical) + .cross_axis_alignment(CrossAxisAlignment::Center) + .main_axis_alignment(MainAxisAlignment::Center) +} + +fn main() -> Result<(), EventLoopError> { + let data = Calendar::new(); + let app = Xilem::new(data, app_logic); + app.run_windowed(EventLoop::with_user_event(), "Calendar".into())?; + Ok(()) +} diff --git a/xilem/src/view/date_selector.rs b/xilem/src/view/date_selector.rs new file mode 100644 index 000000000..623746df0 --- /dev/null +++ b/xilem/src/view/date_selector.rs @@ -0,0 +1,106 @@ +use masonry::widget::{Axis, CrossAxisAlignment, MainAxisAlignment}; +use time::{format_description::modifier::Year, Date, Month, OffsetDateTime}; + +use crate::{view::sized_box, WidgetView}; + +use super::{button, flex, FlexSpacer}; + +pub struct DateData { + pub selected_date: Date, + pub month: Month, + pub year: i32, +} + +impl DateData { + pub fn new(selected_date: Date, month: Month, year: i32) -> Self { + Self { + selected_date, + month, + year, + } + } +} + +pub fn date( + selected_date: &mut Date, + month: &mut Month, + year: &mut i32, +) -> impl WidgetView { + flex(( + date_controls(month, year), + date_grid(selected_date, month, year), + )) + .direction(Axis::Vertical) + .cross_axis_alignment(CrossAxisAlignment::Center) + .main_axis_alignment(MainAxisAlignment::Center) +} + +fn date_controls(month: &mut Month, year: &mut i32) -> impl WidgetView { + flex(( + button("<", |data: &mut DateData| { + data.month = data.month.previous(); + }), + sized_box(button(format!("{month}"), |_| {})).width(100.), + button(">", |data: &mut DateData| { + data.month = data.month.next(); + }), + FlexSpacer::Fixed(40.), + button("<", |data: &mut DateData| { + data.year -= 1; + }), + button(format!("{year}"), |_| {}), + button(">", |data: &mut DateData| { + data.year += 1; + }), + )) + .direction(Axis::Horizontal) + .main_axis_alignment(MainAxisAlignment::Center) +} + +// The selected_date in the interface is needed to highlight the currently selected date +// It is currently not implemented +fn date_grid( + selected_date: &mut Date, + month: &mut Month, + year: &mut i32, +) -> impl WidgetView { + const COLUMNS: u8 = 7; + const ROWS: u8 = 5; + let mut date = Date::from_calendar_date(*year, *month, 1).unwrap(); + let days_from_monday = date.weekday().number_days_from_monday(); + + for _day in 0..days_from_monday { + date = date.previous_day().unwrap(); + } + + let mut rows = Vec::new(); + for _row in 0..ROWS { + let mut columns = Vec::new(); + for _column in 0..COLUMNS { + // Add buttons of each row into columns vec + let day_number = date.day(); + let date_copy = date.clone(); + columns.push( + sized_box(button( + format!("{day_number}"), + move |data: &mut DateData| { + // Set the selected_date + data.selected_date = date_copy; + }, + )) + .width(50.), + ); + date = date.next_day().unwrap(); + } + // Add column vec into flex with horizontal axis + // Add flex into rows vec + rows.push( + flex(columns) + .direction(Axis::Horizontal) + .main_axis_alignment(MainAxisAlignment::Center) + .gap(10.), + ); + } + // Add row vec into flex with vertical axis + flex(rows).direction(Axis::Vertical) +} diff --git a/xilem/src/view/mod.rs b/xilem/src/view/mod.rs index 9cafb2016..52a4a2345 100644 --- a/xilem/src/view/mod.rs +++ b/xilem/src/view/mod.rs @@ -33,3 +33,6 @@ pub use textbox::*; mod portal; pub use portal::*; + +mod date_selector; +pub use date_selector::*;