improv!(calendar): do not select date when navigating prev/next months
This commit is contained in:
parent
90c5c84cce
commit
8211fb68bd
2 changed files with 101 additions and 37 deletions
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue