Skip to content

Commit

Permalink
improv!(calendar): do not select date when navigating prev/next months
Browse files Browse the repository at this point in the history
  • Loading branch information
Tony4dev authored Jan 16, 2025
1 parent 90c5c84 commit 8211fb6
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 38 deletions.
29 changes: 20 additions & 9 deletions examples/calendar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

//! Calendar widget example
use chrono::{Local, NaiveDate};
use chrono::NaiveDate;
use cosmic::app::{Core, Settings, Task};
use cosmic::widget::calendar::CalendarModel;
use cosmic::{executor, iced, ApplicationExt, Element};

/// Runs application with these settings
Expand All @@ -19,12 +20,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Clone, Debug)]
pub enum Message {
DateSelected(NaiveDate),
PrevMonth,
NextMonth,
}

/// The [`App`] stores application-specific state.
pub struct App {
core: Core,
date_selected: NaiveDate,
calendar_model: CalendarModel,
}

/// Implement [`cosmic::Application`] to integrate with COSMIC.
Expand All @@ -51,11 +54,9 @@ impl cosmic::Application for App {

/// Creates the application, and optionally emits task on initialize.
fn init(core: Core, _input: Self::Flags) -> (Self, Task<Self::Message>) {
let now = Local::now();

let mut app = App {
core,
date_selected: NaiveDate::from(now.naive_local()),
calendar_model: CalendarModel::now(),
};

let command = app.update_title();
Expand All @@ -67,11 +68,17 @@ impl cosmic::Application for App {
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
match message {
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()
}
Expand All @@ -80,8 +87,12 @@ impl cosmic::Application for App {
fn view(&self) -> Element<Self::Message> {
let mut content = cosmic::widget::column().spacing(12);

let calendar =
cosmic::widget::calendar(&self.date_selected, |date| Message::DateSelected(date));
let calendar = cosmic::widget::calendar(
&self.calendar_model,
|date| Message::DateSelected(date),
|| Message::PrevMonth,
|| Message::NextMonth,
);

content = content.push(calendar);

Expand Down
111 changes: 82 additions & 29 deletions src/widget/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ use std::cmp;

use crate::iced_core::{Alignment, Length, Padding};
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.
pub fn calendar<M>(
selected: &NaiveDate,
model: &CalendarModel,
on_select: impl Fn(NaiveDate) -> M + 'static,
on_prev: impl Fn() -> M + 'static,
on_next: impl Fn() -> M + 'static,
) -> Calendar<M> {
Calendar {
selected,
model,
on_select: Box::new(on_select),
on_prev: Box::new(on_prev),
on_next: Box::new(on_next),
}
}

Expand All @@ -38,41 +42,83 @@ pub fn set_day(date_selected: NaiveDate, day: u32) -> NaiveDate {
}
}

pub fn set_prev_month(date_selected: NaiveDate) -> NaiveDate {
date_selected
.checked_sub_months(Months::new(1))
.expect("valid naivedate")
pub struct CalendarModel {
pub selected: NaiveDate,
visible: NaiveDate,
}

pub fn set_next_month(date_selected: NaiveDate) -> NaiveDate {
date_selected
.checked_add_months(Months::new(1))
.expect("valid naivedate")
impl CalendarModel {
pub fn now() -> Self {
let now = Local::now();
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> {
selected: &'a NaiveDate,
model: &'a CalendarModel,
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>
where
Message: Clone + 'static,
{
fn from(this: Calendar<'a, Message>) -> Self {
let date = text(this.selected.format("%B %-d, %Y").to_string()).size(18);
let day_of_week = text::body(this.selected.format("%A").to_string());
let date = text(this.model.visible.format("%B %Y").to_string()).size(18);

let month_controls = row::with_capacity(2)
.push(
button::icon(icon::from_name("go-previous-symbolic"))
.padding([0, 12])
.on_press((this.on_select)(set_prev_month(this.selected.clone()))),
.on_press((this.on_prev)()),
)
.push(
button::icon(icon::from_name("go-next-symbolic"))
.padding([0, 12])
.on_press((this.on_select)(set_next_month(this.selected.clone()))),
.on_press((this.on_next)()),
);

// Calender
Expand All @@ -93,8 +139,8 @@ where
calendar_grid = calendar_grid.insert_row();

let monday = get_calender_first(
this.selected.year(),
this.selected.month(),
this.model.visible.year(),
this.model.visible.month(),
first_day_of_week,
);
let mut day_iter = monday.iter_days();
Expand All @@ -104,17 +150,24 @@ where
}

let date = day_iter.next().unwrap();
let is_month =
date.month() == this.selected.month() && date.year_ce() == this.selected.year_ce();
let is_day = date.day() == this.selected.day() && is_month;

calendar_grid =
calendar_grid.push(date_button(date, is_month, is_day, &this.on_select));
let is_currently_viewed_month = date.month() == this.model.visible.month()
&& date.year_ce() == this.model.visible.year_ce();
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.push(date_button(
date,
is_currently_viewed_month,
is_currently_selected_day,
&this.on_select,
));
}

let content_list = column::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(),
month_controls.into(),
])
Expand All @@ -132,11 +185,11 @@ where

fn date_button<Message>(
date: NaiveDate,
is_month: bool,
is_day: bool,
is_currently_viewed_month: bool,
is_currently_selected_day: bool,
on_select: &dyn Fn(NaiveDate) -> Message,
) -> crate::widget::Button<'static, Message> {
let style = if is_day {
let style = if is_currently_selected_day {
button::ButtonClass::Suggested
} else {
button::ButtonClass::Text
Expand All @@ -147,7 +200,7 @@ fn date_button<Message>(
.height(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())))
} else {
button
Expand Down

0 comments on commit 8211fb6

Please sign in to comment.