From 8c30e8c5f7fd85517fb807cbd069e0c3d62c5ab7 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 7 Jan 2024 22:08:32 +0100 Subject: [PATCH] Highlight submenu buttons when hovered and open (#3780) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Submenu buttons in menues now properly highlight when hovered and when opened. …plus a bunch of other cleanup --- crates/eframe/src/native/glow_integration.rs | 2 +- crates/egui/src/containers/window.rs | 4 +- crates/egui/src/menu.rs | 12 +++--- crates/egui/src/style.rs | 8 +++- crates/egui_extras/src/datepicker/button.rs | 6 +-- crates/egui_extras/src/datepicker/popup.rs | 39 +++++++++---------- .../egui_extras/src/loaders/image_loader.rs | 2 + crates/egui_extras/src/table.rs | 2 +- crates/egui_plot/src/lib.rs | 23 +++++------ crates/emath/src/history.rs | 4 +- crates/epaint/src/image.rs | 5 +-- scripts/generate_changelog.py | 10 +++-- scripts/setup_web.sh | 2 +- 13 files changed, 62 insertions(+), 57 deletions(-) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 922039bf307..537e7394206 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -1218,7 +1218,7 @@ impl GlutinWindowContext { } fn initialize_or_update_viewport<'vp>( - egu_ctx: &'_ egui::Context, + egu_ctx: &egui::Context, viewports: &'vp mut ViewportIdMap, ids: ViewportIdPair, class: ViewportClass, diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index f1b5f130844..4ea40637ce8 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -502,7 +502,7 @@ impl<'open> Window<'open> { // END FRAME -------------------------------- if let Some(title_bar) = title_bar { - if on_top { + if on_top && area_content_ui.visuals().window_highlight_topmost { let rect = Rect::from_min_size( outer_rect.min, Vec2 { @@ -515,7 +515,7 @@ impl<'open> Window<'open> { round.se = 0.0; round.sw = 0.0; } - let header_color = area_content_ui.visuals().widgets.hovered.bg_fill; + let header_color = area_content_ui.visuals().widgets.open.weak_bg_fill; area_content_ui.painter().set( *where_to_put_header_background, diff --git a/crates/egui/src/menu.rs b/crates/egui/src/menu.rs index 5c33cebb61f..e1fca02544b 100644 --- a/crates/egui/src/menu.rs +++ b/crates/egui/src/menu.rs @@ -440,11 +440,11 @@ impl SubMenuButton { fn visuals<'a>( ui: &'a Ui, - response: &'_ Response, - menu_state: &'_ MenuState, + response: &Response, + menu_state: &MenuState, sub_id: Id, ) -> &'a WidgetVisuals { - if menu_state.is_open(sub_id) { + if menu_state.is_open(sub_id) && !response.hovered() { &ui.style().visuals.widgets.open } else { ui.style().interact(response) @@ -528,15 +528,15 @@ impl SubMenu { add_contents: impl FnOnce(&mut Ui) -> R, ) -> InnerResponse> { let sub_id = ui.id().with(self.button.index); - let button = self.button.show(ui, &self.parent_state.read(), sub_id); + let response = self.button.show(ui, &self.parent_state.read(), sub_id); self.parent_state .write() - .submenu_button_interaction(ui, sub_id, &button); + .submenu_button_interaction(ui, sub_id, &response); let inner = self .parent_state .write() .show_submenu(ui.ctx(), sub_id, add_contents); - InnerResponse::new(inner, button) + InnerResponse::new(inner, response) } } diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 7eb32a06ef2..4cbf6255d97 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -779,6 +779,9 @@ pub struct Visuals { pub window_fill: Color32, pub window_stroke: Stroke, + /// Highlight the topmost window. + pub window_highlight_topmost: bool, + pub menu_rounding: Rounding, /// Panel background color @@ -1130,6 +1133,7 @@ impl Visuals { window_shadow: Shadow::big_dark(), window_fill: Color32::from_gray(27), window_stroke: Stroke::new(1.0, Color32::from_gray(60)), + window_highlight_topmost: true, menu_rounding: Rounding::same(6.0), @@ -1247,7 +1251,7 @@ impl Widgets { expansion: 1.0, }, open: WidgetVisuals { - weak_bg_fill: Color32::from_gray(27), + weak_bg_fill: Color32::from_gray(45), bg_fill: Color32::from_gray(27), bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), fg_stroke: Stroke::new(1.0, Color32::from_gray(210)), @@ -1694,6 +1698,7 @@ impl Visuals { window_shadow, window_fill, window_stroke, + window_highlight_topmost, menu_rounding, @@ -1736,6 +1741,7 @@ impl Visuals { stroke_ui(ui, window_stroke, "Outline"); rounding_ui(ui, window_rounding); shadow_ui(ui, window_shadow, "Shadow"); + ui.checkbox(window_highlight_topmost, "Highlight topmost Window"); }); ui.collapsing("Menus and popups", |ui| { diff --git a/crates/egui_extras/src/datepicker/button.rs b/crates/egui_extras/src/datepicker/button.rs index 5b6367f8fa4..5aa6eaeadfb 100644 --- a/crates/egui_extras/src/datepicker/button.rs +++ b/crates/egui_extras/src/datepicker/button.rs @@ -79,7 +79,7 @@ impl<'a> Widget for DatePickerButton<'a> { fn ui(self, ui: &mut Ui) -> egui::Response { let id = ui.make_persistent_id(self.id_source); let mut button_state = ui - .memory_mut(|mem| mem.data.get_persisted::(id)) + .data_mut(|data| data.get_persisted::(id)) .unwrap_or_default(); let mut text = if self.show_icon { @@ -98,7 +98,7 @@ impl<'a> Widget for DatePickerButton<'a> { let mut button_response = ui.add(button); if button_response.clicked() { button_state.picker_visible = true; - ui.memory_mut(|mem| mem.data.insert_persisted(id, button_state.clone())); + ui.data_mut(|data| data.insert_persisted(id, button_state.clone())); } if button_state.picker_visible { @@ -152,7 +152,7 @@ impl<'a> Widget for DatePickerButton<'a> { && (ui.input(|i| i.key_pressed(Key::Escape)) || area_response.clicked_elsewhere()) { button_state.picker_visible = false; - ui.memory_mut(|mem| mem.data.insert_persisted(id, button_state)); + ui.data_mut(|data| data.insert_persisted(id, button_state)); } } diff --git a/crates/egui_extras/src/datepicker/popup.rs b/crates/egui_extras/src/datepicker/popup.rs index d4c2370274c..a787d3c55ba 100644 --- a/crates/egui_extras/src/datepicker/popup.rs +++ b/crates/egui_extras/src/datepicker/popup.rs @@ -41,14 +41,14 @@ impl<'a> DatePickerPopup<'a> { let id = ui.make_persistent_id("date_picker"); let today = chrono::offset::Utc::now().date_naive(); let mut popup_state = ui - .memory_mut(|mem| mem.data.get_persisted::(id)) + .data_mut(|data| data.get_persisted::(id)) .unwrap_or_default(); if !popup_state.setup { popup_state.year = self.selection.year(); popup_state.month = self.selection.month(); popup_state.day = self.selection.day(); popup_state.setup = true; - ui.memory_mut(|mem| mem.data.insert_persisted(id, popup_state.clone())); + ui.data_mut(|data| data.insert_persisted(id, popup_state.clone())); } let weeks = month_data(popup_state.year, popup_state.month); @@ -161,8 +161,8 @@ impl<'a> DatePickerPopup<'a> { popup_state.year -= 1; popup_state.day = popup_state.day.min(popup_state.last_day_of_month()); - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state.clone()); + ui.data_mut(|data| { + data.insert_persisted(id, popup_state.clone()); }); } }); @@ -181,8 +181,8 @@ impl<'a> DatePickerPopup<'a> { } popup_state.day = popup_state.day.min(popup_state.last_day_of_month()); - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state.clone()); + ui.data_mut(|data| { + data.insert_persisted(id, popup_state.clone()); }); } }); @@ -199,8 +199,8 @@ impl<'a> DatePickerPopup<'a> { } popup_state.day = popup_state.last_day_of_month(); } - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state.clone()); + ui.data_mut(|data| { + data.insert_persisted(id, popup_state.clone()); }); } }); @@ -217,8 +217,8 @@ impl<'a> DatePickerPopup<'a> { popup_state.year += 1; } } - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state.clone()); + ui.data_mut(|data| { + data.insert_persisted(id, popup_state.clone()); }); } }); @@ -233,8 +233,8 @@ impl<'a> DatePickerPopup<'a> { } popup_state.day = popup_state.day.min(popup_state.last_day_of_month()); - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state.clone()); + ui.data_mut(|data| { + data.insert_persisted(id, popup_state.clone()); }); } }); @@ -245,8 +245,8 @@ impl<'a> DatePickerPopup<'a> { popup_state.year += 1; popup_state.day = popup_state.day.min(popup_state.last_day_of_month()); - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state.clone()); + ui.data_mut(|data| { + data.insert_persisted(id, popup_state.clone()); }); } }); @@ -355,8 +355,8 @@ impl<'a> DatePickerPopup<'a> { popup_state.year = day.year(); popup_state.month = day.month(); popup_state.day = day.day(); - ui.memory_mut(|mem| { - mem.data.insert_persisted( + ui.data_mut(|data| { + data.insert_persisted( id, popup_state.clone(), ); @@ -402,10 +402,9 @@ impl<'a> DatePickerPopup<'a> { if close { popup_state.setup = false; - ui.memory_mut(|mem| { - mem.data.insert_persisted(id, popup_state); - mem.data - .get_persisted_mut_or_default::(self.button_id) + ui.data_mut(|data| { + data.insert_persisted(id, popup_state); + data.get_persisted_mut_or_default::(self.button_id) .picker_visible = false; }); } diff --git a/crates/egui_extras/src/loaders/image_loader.rs b/crates/egui_extras/src/loaders/image_loader.rs index 02e0cdd4d93..19a91105a63 100644 --- a/crates/egui_extras/src/loaders/image_loader.rs +++ b/crates/egui_extras/src/loaders/image_loader.rs @@ -18,6 +18,7 @@ impl ImageCrateLoader { } fn is_supported_uri(uri: &str) -> bool { + // TODO(emilk): use https://github.com/image-rs/image/pull/2038 when new `image` crate is released. let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else { // `true` because if there's no extension, assume that we support it return true; @@ -27,6 +28,7 @@ fn is_supported_uri(uri: &str) -> bool { } fn is_unsupported_mime(mime: &str) -> bool { + // TODO(emilk): use https://github.com/image-rs/image/pull/2038 when new `image` crate is released. mime.contains("svg") } diff --git a/crates/egui_extras/src/table.rs b/crates/egui_extras/src/table.rs index 510f593c045..8406d8d5c81 100644 --- a/crates/egui_extras/src/table.rs +++ b/crates/egui_extras/src/table.rs @@ -1087,7 +1087,7 @@ impl<'a> TableBody<'a> { if is_row_hovered { self.layout .ui - .memory_mut(|w| w.data.insert_temp(self.hovered_row_index_id, row_index)); + .data_mut(|data| data.insert_temp(self.hovered_row_index_id, row_index)); } } } diff --git a/crates/egui_plot/src/lib.rs b/crates/egui_plot/src/lib.rs index 6c29ccd2f01..ec048297193 100644 --- a/crates/egui_plot/src/lib.rs +++ b/crates/egui_plot/src/lib.rs @@ -854,9 +854,8 @@ impl Plot { ui.ctx().check_for_id_clash(plot_id, rect, "Plot"); let memory = if reset { if let Some((name, _)) = linked_axes.as_ref() { - ui.memory_mut(|memory| { - let link_groups: &mut BoundsLinkGroups = - memory.data.get_temp_mut_or_default(Id::NULL); + ui.data_mut(|data| { + let link_groups: &mut BoundsLinkGroups = data.get_temp_mut_or_default(Id::NULL); link_groups.0.remove(name); }); }; @@ -941,8 +940,8 @@ impl Plot { // Find the cursors from other plots we need to draw let draw_cursors: Vec = if let Some((id, _)) = linked_cursors.as_ref() { - ui.memory_mut(|memory| { - let frames: &mut CursorLinkGroups = memory.data.get_temp_mut_or_default(Id::NULL); + ui.data_mut(|data| { + let frames: &mut CursorLinkGroups = data.get_temp_mut_or_default(Id::NULL); let cursors = frames.0.entry(*id).or_default(); // Look for our previous frame @@ -969,9 +968,8 @@ impl Plot { // Transfer the bounds from a link group. if let Some((id, axes)) = linked_axes.as_ref() { - ui.memory_mut(|memory| { - let link_groups: &mut BoundsLinkGroups = - memory.data.get_temp_mut_or_default(Id::NULL); + ui.data_mut(|data| { + let link_groups: &mut BoundsLinkGroups = data.get_temp_mut_or_default(Id::NULL); if let Some(linked_bounds) = link_groups.0.get(id) { if axes.x { bounds.set_x(&linked_bounds.bounds); @@ -1218,8 +1216,8 @@ impl Plot { if let Some((id, _)) = linked_cursors.as_ref() { // Push the frame we just drew to the list of frames - ui.memory_mut(|memory| { - let frames: &mut CursorLinkGroups = memory.data.get_temp_mut_or_default(Id::NULL); + ui.data_mut(|data| { + let frames: &mut CursorLinkGroups = data.get_temp_mut_or_default(Id::NULL); let cursors = frames.0.entry(*id).or_default(); cursors.push(PlotFrameCursors { id: plot_id, @@ -1230,9 +1228,8 @@ impl Plot { if let Some((id, _)) = linked_axes.as_ref() { // Save the linked bounds. - ui.memory_mut(|memory| { - let link_groups: &mut BoundsLinkGroups = - memory.data.get_temp_mut_or_default(Id::NULL); + ui.data_mut(|data| { + let link_groups: &mut BoundsLinkGroups = data.get_temp_mut_or_default(Id::NULL); link_groups.0.insert( *id, LinkedBounds { diff --git a/crates/emath/src/history.rs b/crates/emath/src/history.rs index 482db925ce2..bacd9625fb8 100644 --- a/crates/emath/src/history.rs +++ b/crates/emath/src/history.rs @@ -109,11 +109,11 @@ where /// `(time, value)` pairs /// Time difference between values can be zero, but never negative. // TODO(emilk): impl IntoIter - pub fn iter(&'_ self) -> impl ExactSizeIterator + '_ { + pub fn iter(&self) -> impl ExactSizeIterator + '_ { self.values.iter().map(|(time, value)| (*time, *value)) } - pub fn values(&'_ self) -> impl ExactSizeIterator + '_ { + pub fn values(&self) -> impl ExactSizeIterator + '_ { self.values.iter().map(|(_time, value)| *value) } diff --git a/crates/epaint/src/image.rs b/crates/epaint/src/image.rs index 34917a3ad6c..31b61f8020f 100644 --- a/crates/epaint/src/image.rs +++ b/crates/epaint/src/image.rs @@ -290,10 +290,7 @@ impl FontImage { /// /// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`. #[inline] - pub fn srgba_pixels( - &'_ self, - gamma: Option, - ) -> impl ExactSizeIterator + '_ { + pub fn srgba_pixels(&self, gamma: Option) -> impl ExactSizeIterator + '_ { let gamma = gamma.unwrap_or(0.55); // TODO(emilk): this default coverage gamma is a magic constant, chosen by eye. I don't even know why we need it. self.pixels.iter().map(move |coverage| { let alpha = coverage.powf(gamma); diff --git a/scripts/generate_changelog.py b/scripts/generate_changelog.py index a95bf6aaac3..0254256179e 100755 --- a/scripts/generate_changelog.py +++ b/scripts/generate_changelog.py @@ -7,6 +7,7 @@ though it often needs some manual editing too. """ +import argparse import multiprocessing import re import sys @@ -19,7 +20,6 @@ OWNER = "emilk" REPO = "egui" -COMMIT_RANGE = "latest..HEAD" INCLUDE_LABELS = False # It adds quite a bit of visual noise OFFICIAL_DEVS = [ "emilk", @@ -118,8 +118,12 @@ def print_section(crate: str, items: List[str]) -> None: def main() -> None: + parser = argparse.ArgumentParser(description="Generate a changelog.") + parser.add_argument("--commit-range", help="e.g. 0.24.0..HEAD") + args = parser.parse_args() + repo = Repo(".") - commits = list(repo.iter_commits(COMMIT_RANGE)) + commits = list(repo.iter_commits(args.commit_range)) commits.reverse() # Most recent last commit_infos = list(map(get_commit_info, commits)) @@ -191,7 +195,7 @@ def main() -> None: unsorted_prs.append(summary) print() - print(f"Full diff at https://github.com/emilk/egui/compare/{COMMIT_RANGE}") + print(f"Full diff at https://github.com/emilk/egui/compare/{args.commit_range}") print() for crate in crate_names: if crate in sections: diff --git a/scripts/setup_web.sh b/scripts/setup_web.sh index 1c535dbd355..e5dbb08fdbe 100755 --- a/scripts/setup_web.sh +++ b/scripts/setup_web.sh @@ -7,4 +7,4 @@ cd "$script_path/.." rustup target add wasm32-unknown-unknown # For generating JS bindings: -cargo install wasm-bindgen-cli --version 0.2.89 +cargo install --quiet wasm-bindgen-cli --version 0.2.89