mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -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,
|
||||
highlight_weekends: bool,
|
||||
start_end_years: Option<RangeInclusive<i32>>,
|
||||
reverse_years: bool,
|
||||
year_scroll_to: Option<i32>,
|
||||
}
|
||||
|
||||
impl<'a> DatePickerButton<'a> {
|
||||
@@ -36,6 +38,8 @@ impl<'a> DatePickerButton<'a> {
|
||||
format: "%Y-%m-%d".to_owned(),
|
||||
highlight_weekends: true,
|
||||
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
|
||||
}
|
||||
|
||||
/// 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<'_> {
|
||||
@@ -154,7 +173,6 @@ impl Widget for DatePickerButton<'_> {
|
||||
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());
|
||||
|
||||
//TODO(elwerene): Better positioning
|
||||
@@ -182,6 +200,8 @@ impl Widget for DatePickerButton<'_> {
|
||||
calendar_week: self.calendar_week,
|
||||
highlight_weekends: self.highlight_weekends,
|
||||
start_end_years: self.start_end_years,
|
||||
reverse_years: self.reverse_years,
|
||||
year_scroll_to: self.year_scroll_to,
|
||||
}
|
||||
.draw(ui)
|
||||
})
|
||||
|
||||
@@ -13,6 +13,7 @@ struct DatePickerPopupState {
|
||||
month: u32,
|
||||
day: u32,
|
||||
setup: bool,
|
||||
year_scroll_needed: bool,
|
||||
}
|
||||
|
||||
impl DatePickerPopupState {
|
||||
@@ -36,6 +37,8 @@ pub(crate) struct DatePickerPopup<'a> {
|
||||
pub calendar_week: bool,
|
||||
pub highlight_weekends: bool,
|
||||
pub start_end_years: Option<std::ops::RangeInclusive<i32>>,
|
||||
pub reverse_years: bool,
|
||||
pub year_scroll_to: Option<i32>,
|
||||
}
|
||||
|
||||
impl DatePickerPopup<'_> {
|
||||
@@ -51,6 +54,7 @@ impl DatePickerPopup<'_> {
|
||||
popup_state.month = self.selection.month();
|
||||
popup_state.day = self.selection.day();
|
||||
popup_state.setup = true;
|
||||
popup_state.year_scroll_needed = true;
|
||||
ui.data_mut(|data| data.insert_persisted(id, popup_state.clone()));
|
||||
}
|
||||
|
||||
@@ -60,7 +64,7 @@ impl DatePickerPopup<'_> {
|
||||
let spacing = 2.0;
|
||||
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)
|
||||
.clip(false)
|
||||
@@ -89,15 +93,30 @@ impl DatePickerPopup<'_> {
|
||||
Some(range) => (*range.start(), *range.end()),
|
||||
None => (today.year() - 100, today.year() + 10),
|
||||
};
|
||||
for year in start_year..=end_year {
|
||||
if ui
|
||||
.selectable_value(
|
||||
&mut popup_state.year,
|
||||
year,
|
||||
year.to_string(),
|
||||
)
|
||||
.changed()
|
||||
let scroll_to_year =
|
||||
self.year_scroll_to.unwrap_or(popup_state.year);
|
||||
let years: Vec<i32> = if self.reverse_years {
|
||||
(start_year..=end_year).rev().collect()
|
||||
} else {
|
||||
(start_year..=end_year).collect()
|
||||
};
|
||||
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
|
||||
.day
|
||||
.min(popup_state.last_day_of_month());
|
||||
@@ -349,7 +368,6 @@ impl DatePickerPopup<'_> {
|
||||
);
|
||||
|
||||
if day == today {
|
||||
// Encircle today's date
|
||||
let stroke = ui
|
||||
.visuals()
|
||||
.widgets
|
||||
|
||||
Reference in New Issue
Block a user