improv!(calendar): do not select date when navigating prev/next months

This commit is contained in:
Tony4dev 2025-01-16 05:30:21 +00:00 committed by GitHub
parent 90c5c84cce
commit 8211fb68bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 101 additions and 37 deletions

View file

@ -3,8 +3,9 @@
//! Calendar widget example //! Calendar widget example
use chrono::{Local, NaiveDate}; use chrono::NaiveDate;
use cosmic::app::{Core, Settings, Task}; use cosmic::app::{Core, Settings, Task};
use cosmic::widget::calendar::CalendarModel;
use cosmic::{executor, iced, ApplicationExt, Element}; use cosmic::{executor, iced, ApplicationExt, Element};
/// Runs application with these settings /// Runs application with these settings
@ -19,12 +20,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Message { pub enum Message {
DateSelected(NaiveDate), DateSelected(NaiveDate),
PrevMonth,
NextMonth,
} }
/// The [`App`] stores application-specific state. /// The [`App`] stores application-specific state.
pub struct App { pub struct App {
core: Core, core: Core,
date_selected: NaiveDate, calendar_model: CalendarModel,
} }
/// Implement [`cosmic::Application`] to integrate with COSMIC. /// Implement [`cosmic::Application`] to integrate with COSMIC.
@ -51,11 +54,9 @@ impl cosmic::Application for App {
/// Creates the application, and optionally emits task on initialize. /// Creates the application, and optionally emits task on initialize.
fn init(core: Core, _input: Self::Flags) -> (Self, Task<Self::Message>) { fn init(core: Core, _input: Self::Flags) -> (Self, Task<Self::Message>) {
let now = Local::now();
let mut app = App { let mut app = App {
core, core,
date_selected: NaiveDate::from(now.naive_local()), calendar_model: CalendarModel::now(),
}; };
let command = app.update_title(); let command = app.update_title();
@ -67,11 +68,17 @@ impl cosmic::Application for App {
fn update(&mut self, message: Self::Message) -> Task<Self::Message> { fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
match message { match message {
Message::DateSelected(date) => { Message::DateSelected(date) => {
self.date_selected = date; self.calendar_model.selected = date;
}
Message::PrevMonth => {
self.calendar_model.show_prev_month();
}
Message::NextMonth => {
self.calendar_model.show_next_month();
} }
} }
println!("Date selected: {:?}", self.date_selected); println!("Date selected: {:?}", &self.calendar_model.selected);
Task::none() Task::none()
} }
@ -80,8 +87,12 @@ impl cosmic::Application for App {
fn view(&self) -> Element<Self::Message> { fn view(&self) -> Element<Self::Message> {
let mut content = cosmic::widget::column().spacing(12); let mut content = cosmic::widget::column().spacing(12);
let calendar = let calendar = cosmic::widget::calendar(
cosmic::widget::calendar(&self.date_selected, |date| Message::DateSelected(date)); &self.calendar_model,
|date| Message::DateSelected(date),
|| Message::PrevMonth,
|| Message::NextMonth,
);
content = content.push(calendar); content = content.push(calendar);

View file

@ -7,16 +7,20 @@ use std::cmp;
use crate::iced_core::{Alignment, Length, Padding}; use crate::iced_core::{Alignment, Length, Padding};
use crate::widget::{button, column, grid, icon, row, text, Grid}; use crate::widget::{button, column, grid, icon, row, text, Grid};
use chrono::{Datelike, Days, Months, NaiveDate, Weekday}; use chrono::{Datelike, Days, Local, Months, NaiveDate, Weekday};
/// A widget that displays an interactive calendar. /// A widget that displays an interactive calendar.
pub fn calendar<M>( pub fn calendar<M>(
selected: &NaiveDate, model: &CalendarModel,
on_select: impl Fn(NaiveDate) -> M + 'static, on_select: impl Fn(NaiveDate) -> M + 'static,
on_prev: impl Fn() -> M + 'static,
on_next: impl Fn() -> M + 'static,
) -> Calendar<M> { ) -> Calendar<M> {
Calendar { Calendar {
selected, model,
on_select: Box::new(on_select), on_select: Box::new(on_select),
on_prev: Box::new(on_prev),
on_next: Box::new(on_next),
} }
} }
@ -38,21 +42,64 @@ pub fn set_day(date_selected: NaiveDate, day: u32) -> NaiveDate {
} }
} }
pub fn set_prev_month(date_selected: NaiveDate) -> NaiveDate { pub struct CalendarModel {
date_selected pub selected: NaiveDate,
.checked_sub_months(Months::new(1)) visible: NaiveDate,
.expect("valid naivedate")
} }
pub fn set_next_month(date_selected: NaiveDate) -> NaiveDate { impl CalendarModel {
date_selected pub fn now() -> Self {
.checked_add_months(Months::new(1)) let now = Local::now();
.expect("valid naivedate") let naive_now = NaiveDate::from(now.naive_local());
CalendarModel {
selected: naive_now.clone(),
visible: naive_now,
}
}
pub fn new(selected: NaiveDate) -> Self {
CalendarModel {
selected,
visible: selected.clone(),
}
}
pub fn show_prev_month(&mut self) {
let prev_month_date = self
.visible
.clone()
.checked_sub_months(Months::new(1))
.expect("valid naivedate");
self.visible = prev_month_date.clone();
}
pub fn show_next_month(&mut self) {
let next_month_date = self
.visible
.clone()
.checked_add_months(Months::new(1))
.expect("valid naivedate");
self.visible = next_month_date.clone();
}
pub fn set_prev_month(&mut self) {
self.show_prev_month();
self.selected = self.visible.clone();
}
pub fn set_next_month(&mut self) {
self.show_next_month();
self.selected = self.visible.clone();
}
} }
pub struct Calendar<'a, M> { pub struct Calendar<'a, M> {
selected: &'a NaiveDate, model: &'a CalendarModel,
on_select: Box<dyn Fn(NaiveDate) -> M>, on_select: Box<dyn Fn(NaiveDate) -> M>,
on_prev: Box<dyn Fn() -> M>,
on_next: Box<dyn Fn() -> M>,
} }
impl<'a, Message> From<Calendar<'a, Message>> for crate::Element<'a, Message> impl<'a, Message> From<Calendar<'a, Message>> for crate::Element<'a, Message>
@ -60,19 +107,18 @@ where
Message: Clone + 'static, Message: Clone + 'static,
{ {
fn from(this: Calendar<'a, Message>) -> Self { fn from(this: Calendar<'a, Message>) -> Self {
let date = text(this.selected.format("%B %-d, %Y").to_string()).size(18); let date = text(this.model.visible.format("%B %Y").to_string()).size(18);
let day_of_week = text::body(this.selected.format("%A").to_string());
let month_controls = row::with_capacity(2) let month_controls = row::with_capacity(2)
.push( .push(
button::icon(icon::from_name("go-previous-symbolic")) button::icon(icon::from_name("go-previous-symbolic"))
.padding([0, 12]) .padding([0, 12])
.on_press((this.on_select)(set_prev_month(this.selected.clone()))), .on_press((this.on_prev)()),
) )
.push( .push(
button::icon(icon::from_name("go-next-symbolic")) button::icon(icon::from_name("go-next-symbolic"))
.padding([0, 12]) .padding([0, 12])
.on_press((this.on_select)(set_next_month(this.selected.clone()))), .on_press((this.on_next)()),
); );
// Calender // Calender
@ -93,8 +139,8 @@ where
calendar_grid = calendar_grid.insert_row(); calendar_grid = calendar_grid.insert_row();
let monday = get_calender_first( let monday = get_calender_first(
this.selected.year(), this.model.visible.year(),
this.selected.month(), this.model.visible.month(),
first_day_of_week, first_day_of_week,
); );
let mut day_iter = monday.iter_days(); let mut day_iter = monday.iter_days();
@ -104,17 +150,24 @@ where
} }
let date = day_iter.next().unwrap(); let date = day_iter.next().unwrap();
let is_month = let is_currently_viewed_month = date.month() == this.model.visible.month()
date.month() == this.selected.month() && date.year_ce() == this.selected.year_ce(); && date.year_ce() == this.model.visible.year_ce();
let is_day = date.day() == this.selected.day() && is_month; let is_currently_selected_month = date.month() == this.model.selected.month()
&& date.year_ce() == this.model.selected.year_ce();
let is_currently_selected_day =
date.day() == this.model.selected.day() && is_currently_selected_month;
calendar_grid = calendar_grid = calendar_grid.push(date_button(
calendar_grid.push(date_button(date, is_month, is_day, &this.on_select)); date,
is_currently_viewed_month,
is_currently_selected_day,
&this.on_select,
));
} }
let content_list = column::with_children(vec![ let content_list = column::with_children(vec![
row::with_children(vec![ row::with_children(vec![
column::with_children(vec![date.into(), day_of_week.into()]).into(), date.into(),
crate::widget::Space::with_width(Length::Fill).into(), crate::widget::Space::with_width(Length::Fill).into(),
month_controls.into(), month_controls.into(),
]) ])
@ -132,11 +185,11 @@ where
fn date_button<Message>( fn date_button<Message>(
date: NaiveDate, date: NaiveDate,
is_month: bool, is_currently_viewed_month: bool,
is_day: bool, is_currently_selected_day: bool,
on_select: &dyn Fn(NaiveDate) -> Message, on_select: &dyn Fn(NaiveDate) -> Message,
) -> crate::widget::Button<'static, Message> { ) -> crate::widget::Button<'static, Message> {
let style = if is_day { let style = if is_currently_selected_day {
button::ButtonClass::Suggested button::ButtonClass::Suggested
} else { } else {
button::ButtonClass::Text button::ButtonClass::Text
@ -147,7 +200,7 @@ fn date_button<Message>(
.height(Length::Fixed(36.0)) .height(Length::Fixed(36.0))
.width(Length::Fixed(36.0)); .width(Length::Fixed(36.0));
if is_month { if is_currently_viewed_month {
button.on_press((on_select)(set_day(date, date.day()))) button.on_press((on_select)(set_day(date, date.day())))
} else { } else {
button button