mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Add DatePickerButton::reverse_years/year_scroll_to (#7978)
## What Two new builder methods on `DatePickerButton`: - **`reverse_years(bool)`** — lists years in descending order (newest first). Useful when users are more likely to pick recent years. - **`year_scroll_to(i32)`** — scrolls the year dropdown to a specific year when it first opens, centred in the list. Defaults to the currently selected year, so the picker no longer opens at the top of a 110-item list. ## Why The year `ComboBox` currently always renders in ascending order and opens scrolled to the top. For a range spanning e.g. 1925–2035, the current year is buried near the bottom. Users have to scroll past ~100 entries every time they open the picker. ## Notes - Both options are purely additive builder methods — no breaking changes. - `year_scroll_needed` is a persisted state flag that is set on popup open and cleared after the first scroll, so the user can freely scroll the list after that. - Existing behaviour is unchanged when neither method is called. Co-authored-by: Simon Deurell <simon@deurell.se>
This commit is contained in:
@@ -21,6 +21,8 @@ pub struct DatePickerButton<'a> {
|
|||||||
format: String,
|
format: String,
|
||||||
highlight_weekends: bool,
|
highlight_weekends: bool,
|
||||||
start_end_years: Option<RangeInclusive<i32>>,
|
start_end_years: Option<RangeInclusive<i32>>,
|
||||||
|
reverse_years: bool,
|
||||||
|
year_scroll_to: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DatePickerButton<'a> {
|
impl<'a> DatePickerButton<'a> {
|
||||||
@@ -36,6 +38,8 @@ impl<'a> DatePickerButton<'a> {
|
|||||||
format: "%Y-%m-%d".to_owned(),
|
format: "%Y-%m-%d".to_owned(),
|
||||||
highlight_weekends: true,
|
highlight_weekends: true,
|
||||||
start_end_years: None,
|
start_end_years: None,
|
||||||
|
reverse_years: false,
|
||||||
|
year_scroll_to: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +119,21 @@ impl<'a> DatePickerButton<'a> {
|
|||||||
self.start_end_years = Some(start_end_years);
|
self.start_end_years = Some(start_end_years);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// List years in descending order in the year dropdown. (Default: false)
|
||||||
|
#[inline]
|
||||||
|
pub fn reverse_years(mut self, reverse_years: bool) -> Self {
|
||||||
|
self.reverse_years = reverse_years;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll the year dropdown to this year when the picker first opens.
|
||||||
|
/// Defaults to the currently selected year.
|
||||||
|
#[inline]
|
||||||
|
pub fn year_scroll_to(mut self, year: i32) -> Self {
|
||||||
|
self.year_scroll_to = Some(year);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for DatePickerButton<'_> {
|
impl Widget for DatePickerButton<'_> {
|
||||||
@@ -154,7 +173,6 @@ impl Widget for DatePickerButton<'_> {
|
|||||||
pos.x = button_response.rect.right() - width_with_padding;
|
pos.x = button_response.rect.right() - width_with_padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to make sure the calendar never is displayed out of window
|
|
||||||
pos.x = pos.x.max(ui.style().spacing.window_margin.leftf());
|
pos.x = pos.x.max(ui.style().spacing.window_margin.leftf());
|
||||||
|
|
||||||
//TODO(elwerene): Better positioning
|
//TODO(elwerene): Better positioning
|
||||||
@@ -182,6 +200,8 @@ impl Widget for DatePickerButton<'_> {
|
|||||||
calendar_week: self.calendar_week,
|
calendar_week: self.calendar_week,
|
||||||
highlight_weekends: self.highlight_weekends,
|
highlight_weekends: self.highlight_weekends,
|
||||||
start_end_years: self.start_end_years,
|
start_end_years: self.start_end_years,
|
||||||
|
reverse_years: self.reverse_years,
|
||||||
|
year_scroll_to: self.year_scroll_to,
|
||||||
}
|
}
|
||||||
.draw(ui)
|
.draw(ui)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ struct DatePickerPopupState {
|
|||||||
month: u32,
|
month: u32,
|
||||||
day: u32,
|
day: u32,
|
||||||
setup: bool,
|
setup: bool,
|
||||||
|
year_scroll_needed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatePickerPopupState {
|
impl DatePickerPopupState {
|
||||||
@@ -36,6 +37,8 @@ pub(crate) struct DatePickerPopup<'a> {
|
|||||||
pub calendar_week: bool,
|
pub calendar_week: bool,
|
||||||
pub highlight_weekends: bool,
|
pub highlight_weekends: bool,
|
||||||
pub start_end_years: Option<std::ops::RangeInclusive<i32>>,
|
pub start_end_years: Option<std::ops::RangeInclusive<i32>>,
|
||||||
|
pub reverse_years: bool,
|
||||||
|
pub year_scroll_to: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatePickerPopup<'_> {
|
impl DatePickerPopup<'_> {
|
||||||
@@ -51,6 +54,7 @@ impl DatePickerPopup<'_> {
|
|||||||
popup_state.month = self.selection.month();
|
popup_state.month = self.selection.month();
|
||||||
popup_state.day = self.selection.day();
|
popup_state.day = self.selection.day();
|
||||||
popup_state.setup = true;
|
popup_state.setup = true;
|
||||||
|
popup_state.year_scroll_needed = true;
|
||||||
ui.data_mut(|data| data.insert_persisted(id, popup_state.clone()));
|
ui.data_mut(|data| data.insert_persisted(id, popup_state.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +64,7 @@ impl DatePickerPopup<'_> {
|
|||||||
let spacing = 2.0;
|
let spacing = 2.0;
|
||||||
ui.spacing_mut().item_spacing = Vec2::splat(spacing);
|
ui.spacing_mut().item_spacing = Vec2::splat(spacing);
|
||||||
|
|
||||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend); // Don't wrap any text
|
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||||
|
|
||||||
StripBuilder::new(ui)
|
StripBuilder::new(ui)
|
||||||
.clip(false)
|
.clip(false)
|
||||||
@@ -89,15 +93,30 @@ impl DatePickerPopup<'_> {
|
|||||||
Some(range) => (*range.start(), *range.end()),
|
Some(range) => (*range.start(), *range.end()),
|
||||||
None => (today.year() - 100, today.year() + 10),
|
None => (today.year() - 100, today.year() + 10),
|
||||||
};
|
};
|
||||||
for year in start_year..=end_year {
|
let scroll_to_year =
|
||||||
if ui
|
self.year_scroll_to.unwrap_or(popup_state.year);
|
||||||
.selectable_value(
|
let years: Vec<i32> = if self.reverse_years {
|
||||||
&mut popup_state.year,
|
(start_year..=end_year).rev().collect()
|
||||||
year,
|
} else {
|
||||||
year.to_string(),
|
(start_year..=end_year).collect()
|
||||||
)
|
};
|
||||||
.changed()
|
for year in years {
|
||||||
|
let resp = ui.selectable_value(
|
||||||
|
&mut popup_state.year,
|
||||||
|
year,
|
||||||
|
year.to_string(),
|
||||||
|
);
|
||||||
|
if popup_state.year_scroll_needed
|
||||||
|
&& year == scroll_to_year
|
||||||
{
|
{
|
||||||
|
resp.scroll_to_me(Some(Align::Center));
|
||||||
|
popup_state.year_scroll_needed = false;
|
||||||
|
ui.memory_mut(|mem| {
|
||||||
|
mem.data
|
||||||
|
.insert_persisted(id, popup_state.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if resp.changed() {
|
||||||
popup_state.day = popup_state
|
popup_state.day = popup_state
|
||||||
.day
|
.day
|
||||||
.min(popup_state.last_day_of_month());
|
.min(popup_state.last_day_of_month());
|
||||||
@@ -349,7 +368,6 @@ impl DatePickerPopup<'_> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if day == today {
|
if day == today {
|
||||||
// Encircle today's date
|
|
||||||
let stroke = ui
|
let stroke = ui
|
||||||
.visuals()
|
.visuals()
|
||||||
.widgets
|
.widgets
|
||||||
|
|||||||
Reference in New Issue
Block a user