-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduces date selector to xilem #567
Open
giannissc
wants to merge
4
commits into
linebender:main
Choose a base branch
from
giannissc:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+197
−3
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
dc08341
Introduces date selector to xilem
giannissc e784966
Rework date picker so that selected_date is separate from DatePicker
giannissc 5910d2b
When the selected date changes externally it should update the month/…
giannissc 2b428e9
Rename date_selector to date_picker
giannissc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright 2024 the Xilem Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
use masonry::widget::{CrossAxisAlignment, MainAxisAlignment}; | ||
use time::{Date, OffsetDateTime}; | ||
use winit::error::EventLoopError; | ||
use xilem::{ | ||
view::{button, flex, label, Axis, DatePicker, DatePickerMessage}, | ||
EventLoop, WidgetView, Xilem, | ||
}; | ||
use xilem_core::{adapt, MessageResult}; | ||
|
||
struct Calendar { | ||
selected_date: Date, | ||
date: DatePicker, | ||
} | ||
|
||
impl Calendar { | ||
fn new() -> Self { | ||
let now = OffsetDateTime::now_utc(); | ||
Self { | ||
selected_date: now.date(), | ||
date: DatePicker::new(now.month(), now.year()), | ||
} | ||
} | ||
} | ||
fn selected_date(selected_date: Date) -> impl WidgetView<Calendar> { | ||
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<Calendar> { | ||
flex(( | ||
button("Today", |data: &mut Calendar| { | ||
data.selected_date = OffsetDateTime::now_utc().date(); | ||
}), | ||
button("Tomorrow", |data: &mut Calendar| { | ||
data.selected_date = OffsetDateTime::now_utc().date().next_day().unwrap(); | ||
}), | ||
)) | ||
.direction(Axis::Horizontal) | ||
} | ||
|
||
fn app_logic(data: &mut Calendar) -> impl WidgetView<Calendar> { | ||
flex(( | ||
selected_date(data.selected_date), | ||
external_controls(), | ||
adapt( | ||
data.date.view(&mut data.selected_date), | ||
|state: &mut Calendar, thunk| match thunk.call(&mut state.date) { | ||
MessageResult::Action(DatePickerMessage::Select(date)) => { | ||
state.selected_date = date; | ||
MessageResult::Action(()) | ||
} | ||
MessageResult::Action(DatePickerMessage::Nop) => MessageResult::Nop, | ||
MessageResult::Action(DatePickerMessage::ChangeView) => MessageResult::Action(()), | ||
message_result => message_result.map(|_| ()), | ||
}, | ||
), | ||
)) | ||
.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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use masonry::widget::{Axis, CrossAxisAlignment, MainAxisAlignment}; | ||
use time::{Date, Month, OffsetDateTime}; | ||
|
||
use crate::{view::sized_box, WidgetView}; | ||
|
||
use super::{button, flex, FlexSpacer}; | ||
|
||
pub enum DatePickerMessage { | ||
Select(Date), | ||
ChangeView, | ||
Nop, | ||
} | ||
|
||
pub struct DatePicker { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have some docs here. |
||
previous_date: Date, | ||
month: Month, | ||
year: i32, | ||
} | ||
|
||
impl DatePicker { | ||
pub fn new(month: Month, year: i32) -> Self { | ||
let previous_date = OffsetDateTime::now_utc().date(); | ||
Self { | ||
previous_date, | ||
month, | ||
year, | ||
} | ||
} | ||
|
||
pub fn view( | ||
&mut self, | ||
selected_date: &mut Date, | ||
) -> impl WidgetView<DatePicker, DatePickerMessage> { | ||
if self.previous_date != *selected_date { | ||
self.month = selected_date.month(); | ||
self.year = selected_date.year(); | ||
self.previous_date = selected_date.clone(); | ||
} | ||
flex((self.date_controls(), self.date_grid(selected_date))) | ||
.direction(Axis::Vertical) | ||
.cross_axis_alignment(CrossAxisAlignment::Center) | ||
.main_axis_alignment(MainAxisAlignment::Center) | ||
} | ||
|
||
fn date_controls(&self) -> impl WidgetView<DatePicker, DatePickerMessage> { | ||
let month = self.month; | ||
let year = self.year; | ||
|
||
flex(( | ||
button("<", |data: &mut DatePicker| { | ||
data.month = data.month.previous(); | ||
DatePickerMessage::ChangeView | ||
}), | ||
sized_box(button(format!("{month}"), |_| DatePickerMessage::Nop)).width(100.), | ||
button(">", |data: &mut DatePicker| { | ||
data.month = data.month.next(); | ||
DatePickerMessage::ChangeView | ||
}), | ||
FlexSpacer::Fixed(40.), | ||
button("<", |data: &mut DatePicker| { | ||
data.year -= 1; | ||
DatePickerMessage::ChangeView | ||
}), | ||
button(format!("{year}"), |_| DatePickerMessage::Nop), | ||
button(">", |data: &mut DatePicker| { | ||
data.year += 1; | ||
DatePickerMessage::ChangeView | ||
}), | ||
)) | ||
.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( | ||
&self, | ||
_selected_date: &mut Date, | ||
) -> impl WidgetView<DatePicker, DatePickerMessage> { | ||
const COLUMNS: u8 = 7; | ||
const ROWS: u8 = 5; | ||
let mut date = Date::from_calendar_date(self.year, self.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(); | ||
columns.push( | ||
sized_box(button( | ||
format!("{day_number}"), | ||
move |data: &mut DatePicker| { | ||
// Set the selected_date | ||
data.previous_date = date; | ||
DatePickerMessage::Select(date) | ||
}, | ||
)) | ||
.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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,3 +33,6 @@ pub use textbox::*; | |
|
||
mod portal; | ||
pub use portal::*; | ||
|
||
mod date_picker; | ||
pub use date_picker::*; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this should live in a
components
folder? From a developer of Xilem perspective, there is a meaningful difference between "raw" views and composed components. But from an end-user perspective, they are the same.I don't know what the right answer is here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to actually create an RFC to discuss this among ither things that i have in mind. For now I would suggest to do both. In the internal folder organisation there should be a primitives folder and a components folder but at the tip the should all be re-exported together. Does this sound reasonable?