1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-03 15:20:05 -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:
Deuracell
2026-03-24 12:50:20 +00:00
committed by GitHub
parent d985bf9b83
commit 3a2d437bd7
2 changed files with 49 additions and 11 deletions

View File

@@ -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