Highlight submenu buttons when hovered and open (#3780)

Submenu buttons in menues now properly highlight when hovered and when
opened.

…plus a bunch of other cleanup
This commit is contained in:
Emil Ernerfeldt 2024-01-07 22:08:32 +01:00 committed by GitHub
parent 327f599407
commit 8c30e8c5f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 62 additions and 57 deletions

View File

@ -1218,7 +1218,7 @@ impl GlutinWindowContext {
} }
fn initialize_or_update_viewport<'vp>( fn initialize_or_update_viewport<'vp>(
egu_ctx: &'_ egui::Context, egu_ctx: &egui::Context,
viewports: &'vp mut ViewportIdMap<Viewport>, viewports: &'vp mut ViewportIdMap<Viewport>,
ids: ViewportIdPair, ids: ViewportIdPair,
class: ViewportClass, class: ViewportClass,

View File

@ -502,7 +502,7 @@ impl<'open> Window<'open> {
// END FRAME -------------------------------- // END FRAME --------------------------------
if let Some(title_bar) = title_bar { 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( let rect = Rect::from_min_size(
outer_rect.min, outer_rect.min,
Vec2 { Vec2 {
@ -515,7 +515,7 @@ impl<'open> Window<'open> {
round.se = 0.0; round.se = 0.0;
round.sw = 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( area_content_ui.painter().set(
*where_to_put_header_background, *where_to_put_header_background,

View File

@ -440,11 +440,11 @@ impl SubMenuButton {
fn visuals<'a>( fn visuals<'a>(
ui: &'a Ui, ui: &'a Ui,
response: &'_ Response, response: &Response,
menu_state: &'_ MenuState, menu_state: &MenuState,
sub_id: Id, sub_id: Id,
) -> &'a WidgetVisuals { ) -> &'a WidgetVisuals {
if menu_state.is_open(sub_id) { if menu_state.is_open(sub_id) && !response.hovered() {
&ui.style().visuals.widgets.open &ui.style().visuals.widgets.open
} else { } else {
ui.style().interact(response) ui.style().interact(response)
@ -528,15 +528,15 @@ impl SubMenu {
add_contents: impl FnOnce(&mut Ui) -> R, add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<Option<R>> { ) -> InnerResponse<Option<R>> {
let sub_id = ui.id().with(self.button.index); 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 self.parent_state
.write() .write()
.submenu_button_interaction(ui, sub_id, &button); .submenu_button_interaction(ui, sub_id, &response);
let inner = self let inner = self
.parent_state .parent_state
.write() .write()
.show_submenu(ui.ctx(), sub_id, add_contents); .show_submenu(ui.ctx(), sub_id, add_contents);
InnerResponse::new(inner, button) InnerResponse::new(inner, response)
} }
} }

View File

@ -779,6 +779,9 @@ pub struct Visuals {
pub window_fill: Color32, pub window_fill: Color32,
pub window_stroke: Stroke, pub window_stroke: Stroke,
/// Highlight the topmost window.
pub window_highlight_topmost: bool,
pub menu_rounding: Rounding, pub menu_rounding: Rounding,
/// Panel background color /// Panel background color
@ -1130,6 +1133,7 @@ impl Visuals {
window_shadow: Shadow::big_dark(), window_shadow: Shadow::big_dark(),
window_fill: Color32::from_gray(27), window_fill: Color32::from_gray(27),
window_stroke: Stroke::new(1.0, Color32::from_gray(60)), window_stroke: Stroke::new(1.0, Color32::from_gray(60)),
window_highlight_topmost: true,
menu_rounding: Rounding::same(6.0), menu_rounding: Rounding::same(6.0),
@ -1247,7 +1251,7 @@ impl Widgets {
expansion: 1.0, expansion: 1.0,
}, },
open: WidgetVisuals { open: WidgetVisuals {
weak_bg_fill: Color32::from_gray(27), weak_bg_fill: Color32::from_gray(45),
bg_fill: Color32::from_gray(27), bg_fill: Color32::from_gray(27),
bg_stroke: Stroke::new(1.0, Color32::from_gray(60)), bg_stroke: Stroke::new(1.0, Color32::from_gray(60)),
fg_stroke: Stroke::new(1.0, Color32::from_gray(210)), fg_stroke: Stroke::new(1.0, Color32::from_gray(210)),
@ -1694,6 +1698,7 @@ impl Visuals {
window_shadow, window_shadow,
window_fill, window_fill,
window_stroke, window_stroke,
window_highlight_topmost,
menu_rounding, menu_rounding,
@ -1736,6 +1741,7 @@ impl Visuals {
stroke_ui(ui, window_stroke, "Outline"); stroke_ui(ui, window_stroke, "Outline");
rounding_ui(ui, window_rounding); rounding_ui(ui, window_rounding);
shadow_ui(ui, window_shadow, "Shadow"); shadow_ui(ui, window_shadow, "Shadow");
ui.checkbox(window_highlight_topmost, "Highlight topmost Window");
}); });
ui.collapsing("Menus and popups", |ui| { ui.collapsing("Menus and popups", |ui| {

View File

@ -79,7 +79,7 @@ impl<'a> Widget for DatePickerButton<'a> {
fn ui(self, ui: &mut Ui) -> egui::Response { fn ui(self, ui: &mut Ui) -> egui::Response {
let id = ui.make_persistent_id(self.id_source); let id = ui.make_persistent_id(self.id_source);
let mut button_state = ui let mut button_state = ui
.memory_mut(|mem| mem.data.get_persisted::<DatePickerButtonState>(id)) .data_mut(|data| data.get_persisted::<DatePickerButtonState>(id))
.unwrap_or_default(); .unwrap_or_default();
let mut text = if self.show_icon { let mut text = if self.show_icon {
@ -98,7 +98,7 @@ impl<'a> Widget for DatePickerButton<'a> {
let mut button_response = ui.add(button); let mut button_response = ui.add(button);
if button_response.clicked() { if button_response.clicked() {
button_state.picker_visible = true; 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 { 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()) && (ui.input(|i| i.key_pressed(Key::Escape)) || area_response.clicked_elsewhere())
{ {
button_state.picker_visible = false; 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));
} }
} }

View File

@ -41,14 +41,14 @@ impl<'a> DatePickerPopup<'a> {
let id = ui.make_persistent_id("date_picker"); let id = ui.make_persistent_id("date_picker");
let today = chrono::offset::Utc::now().date_naive(); let today = chrono::offset::Utc::now().date_naive();
let mut popup_state = ui let mut popup_state = ui
.memory_mut(|mem| mem.data.get_persisted::<DatePickerPopupState>(id)) .data_mut(|data| data.get_persisted::<DatePickerPopupState>(id))
.unwrap_or_default(); .unwrap_or_default();
if !popup_state.setup { if !popup_state.setup {
popup_state.year = self.selection.year(); popup_state.year = self.selection.year();
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;
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); let weeks = month_data(popup_state.year, popup_state.month);
@ -161,8 +161,8 @@ impl<'a> DatePickerPopup<'a> {
popup_state.year -= 1; popup_state.year -= 1;
popup_state.day = popup_state.day =
popup_state.day.min(popup_state.last_day_of_month()); popup_state.day.min(popup_state.last_day_of_month());
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state.clone()); data.insert_persisted(id, popup_state.clone());
}); });
} }
}); });
@ -181,8 +181,8 @@ impl<'a> DatePickerPopup<'a> {
} }
popup_state.day = popup_state.day =
popup_state.day.min(popup_state.last_day_of_month()); popup_state.day.min(popup_state.last_day_of_month());
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state.clone()); data.insert_persisted(id, popup_state.clone());
}); });
} }
}); });
@ -199,8 +199,8 @@ impl<'a> DatePickerPopup<'a> {
} }
popup_state.day = popup_state.last_day_of_month(); popup_state.day = popup_state.last_day_of_month();
} }
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state.clone()); data.insert_persisted(id, popup_state.clone());
}); });
} }
}); });
@ -217,8 +217,8 @@ impl<'a> DatePickerPopup<'a> {
popup_state.year += 1; popup_state.year += 1;
} }
} }
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state.clone()); data.insert_persisted(id, popup_state.clone());
}); });
} }
}); });
@ -233,8 +233,8 @@ impl<'a> DatePickerPopup<'a> {
} }
popup_state.day = popup_state.day =
popup_state.day.min(popup_state.last_day_of_month()); popup_state.day.min(popup_state.last_day_of_month());
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state.clone()); data.insert_persisted(id, popup_state.clone());
}); });
} }
}); });
@ -245,8 +245,8 @@ impl<'a> DatePickerPopup<'a> {
popup_state.year += 1; popup_state.year += 1;
popup_state.day = popup_state.day =
popup_state.day.min(popup_state.last_day_of_month()); popup_state.day.min(popup_state.last_day_of_month());
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state.clone()); data.insert_persisted(id, popup_state.clone());
}); });
} }
}); });
@ -355,8 +355,8 @@ impl<'a> DatePickerPopup<'a> {
popup_state.year = day.year(); popup_state.year = day.year();
popup_state.month = day.month(); popup_state.month = day.month();
popup_state.day = day.day(); popup_state.day = day.day();
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted( data.insert_persisted(
id, id,
popup_state.clone(), popup_state.clone(),
); );
@ -402,10 +402,9 @@ impl<'a> DatePickerPopup<'a> {
if close { if close {
popup_state.setup = false; popup_state.setup = false;
ui.memory_mut(|mem| { ui.data_mut(|data| {
mem.data.insert_persisted(id, popup_state); data.insert_persisted(id, popup_state);
mem.data data.get_persisted_mut_or_default::<DatePickerButtonState>(self.button_id)
.get_persisted_mut_or_default::<DatePickerButtonState>(self.button_id)
.picker_visible = false; .picker_visible = false;
}); });
} }

View File

@ -18,6 +18,7 @@ impl ImageCrateLoader {
} }
fn is_supported_uri(uri: &str) -> bool { 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 { 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 // `true` because if there's no extension, assume that we support it
return true; return true;
@ -27,6 +28,7 @@ fn is_supported_uri(uri: &str) -> bool {
} }
fn is_unsupported_mime(mime: &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") mime.contains("svg")
} }

View File

@ -1087,7 +1087,7 @@ impl<'a> TableBody<'a> {
if is_row_hovered { if is_row_hovered {
self.layout self.layout
.ui .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));
} }
} }
} }

View File

@ -854,9 +854,8 @@ impl Plot {
ui.ctx().check_for_id_clash(plot_id, rect, "Plot"); ui.ctx().check_for_id_clash(plot_id, rect, "Plot");
let memory = if reset { let memory = if reset {
if let Some((name, _)) = linked_axes.as_ref() { if let Some((name, _)) = linked_axes.as_ref() {
ui.memory_mut(|memory| { ui.data_mut(|data| {
let link_groups: &mut BoundsLinkGroups = let link_groups: &mut BoundsLinkGroups = data.get_temp_mut_or_default(Id::NULL);
memory.data.get_temp_mut_or_default(Id::NULL);
link_groups.0.remove(name); link_groups.0.remove(name);
}); });
}; };
@ -941,8 +940,8 @@ impl Plot {
// Find the cursors from other plots we need to draw // Find the cursors from other plots we need to draw
let draw_cursors: Vec<Cursor> = if let Some((id, _)) = linked_cursors.as_ref() { let draw_cursors: Vec<Cursor> = if let Some((id, _)) = linked_cursors.as_ref() {
ui.memory_mut(|memory| { ui.data_mut(|data| {
let frames: &mut CursorLinkGroups = memory.data.get_temp_mut_or_default(Id::NULL); let frames: &mut CursorLinkGroups = data.get_temp_mut_or_default(Id::NULL);
let cursors = frames.0.entry(*id).or_default(); let cursors = frames.0.entry(*id).or_default();
// Look for our previous frame // Look for our previous frame
@ -969,9 +968,8 @@ impl Plot {
// Transfer the bounds from a link group. // Transfer the bounds from a link group.
if let Some((id, axes)) = linked_axes.as_ref() { if let Some((id, axes)) = linked_axes.as_ref() {
ui.memory_mut(|memory| { ui.data_mut(|data| {
let link_groups: &mut BoundsLinkGroups = let link_groups: &mut BoundsLinkGroups = data.get_temp_mut_or_default(Id::NULL);
memory.data.get_temp_mut_or_default(Id::NULL);
if let Some(linked_bounds) = link_groups.0.get(id) { if let Some(linked_bounds) = link_groups.0.get(id) {
if axes.x { if axes.x {
bounds.set_x(&linked_bounds.bounds); bounds.set_x(&linked_bounds.bounds);
@ -1218,8 +1216,8 @@ impl Plot {
if let Some((id, _)) = linked_cursors.as_ref() { if let Some((id, _)) = linked_cursors.as_ref() {
// Push the frame we just drew to the list of frames // Push the frame we just drew to the list of frames
ui.memory_mut(|memory| { ui.data_mut(|data| {
let frames: &mut CursorLinkGroups = memory.data.get_temp_mut_or_default(Id::NULL); let frames: &mut CursorLinkGroups = data.get_temp_mut_or_default(Id::NULL);
let cursors = frames.0.entry(*id).or_default(); let cursors = frames.0.entry(*id).or_default();
cursors.push(PlotFrameCursors { cursors.push(PlotFrameCursors {
id: plot_id, id: plot_id,
@ -1230,9 +1228,8 @@ impl Plot {
if let Some((id, _)) = linked_axes.as_ref() { if let Some((id, _)) = linked_axes.as_ref() {
// Save the linked bounds. // Save the linked bounds.
ui.memory_mut(|memory| { ui.data_mut(|data| {
let link_groups: &mut BoundsLinkGroups = let link_groups: &mut BoundsLinkGroups = data.get_temp_mut_or_default(Id::NULL);
memory.data.get_temp_mut_or_default(Id::NULL);
link_groups.0.insert( link_groups.0.insert(
*id, *id,
LinkedBounds { LinkedBounds {

View File

@ -109,11 +109,11 @@ where
/// `(time, value)` pairs /// `(time, value)` pairs
/// Time difference between values can be zero, but never negative. /// Time difference between values can be zero, but never negative.
// TODO(emilk): impl IntoIter // TODO(emilk): impl IntoIter
pub fn iter(&'_ self) -> impl ExactSizeIterator<Item = (f64, T)> + '_ { pub fn iter(&self) -> impl ExactSizeIterator<Item = (f64, T)> + '_ {
self.values.iter().map(|(time, value)| (*time, *value)) self.values.iter().map(|(time, value)| (*time, *value))
} }
pub fn values(&'_ self) -> impl ExactSizeIterator<Item = T> + '_ { pub fn values(&self) -> impl ExactSizeIterator<Item = T> + '_ {
self.values.iter().map(|(_time, value)| *value) self.values.iter().map(|(_time, value)| *value)
} }

View File

@ -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`. /// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`.
#[inline] #[inline]
pub fn srgba_pixels( pub fn srgba_pixels(&self, gamma: Option<f32>) -> impl ExactSizeIterator<Item = Color32> + '_ {
&'_ self,
gamma: Option<f32>,
) -> impl ExactSizeIterator<Item = Color32> + '_ {
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. 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| { self.pixels.iter().map(move |coverage| {
let alpha = coverage.powf(gamma); let alpha = coverage.powf(gamma);

View File

@ -7,6 +7,7 @@ The result can be copy-pasted into CHANGELOG.md,
though it often needs some manual editing too. though it often needs some manual editing too.
""" """
import argparse
import multiprocessing import multiprocessing
import re import re
import sys import sys
@ -19,7 +20,6 @@ from tqdm import tqdm
OWNER = "emilk" OWNER = "emilk"
REPO = "egui" REPO = "egui"
COMMIT_RANGE = "latest..HEAD"
INCLUDE_LABELS = False # It adds quite a bit of visual noise INCLUDE_LABELS = False # It adds quite a bit of visual noise
OFFICIAL_DEVS = [ OFFICIAL_DEVS = [
"emilk", "emilk",
@ -118,8 +118,12 @@ def print_section(crate: str, items: List[str]) -> None:
def main() -> 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(".") repo = Repo(".")
commits = list(repo.iter_commits(COMMIT_RANGE)) commits = list(repo.iter_commits(args.commit_range))
commits.reverse() # Most recent last commits.reverse() # Most recent last
commit_infos = list(map(get_commit_info, commits)) commit_infos = list(map(get_commit_info, commits))
@ -191,7 +195,7 @@ def main() -> None:
unsorted_prs.append(summary) unsorted_prs.append(summary)
print() 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() print()
for crate in crate_names: for crate in crate_names:
if crate in sections: if crate in sections:

View File

@ -7,4 +7,4 @@ cd "$script_path/.."
rustup target add wasm32-unknown-unknown rustup target add wasm32-unknown-unknown
# For generating JS bindings: # For generating JS bindings:
cargo install wasm-bindgen-cli --version 0.2.89 cargo install --quiet wasm-bindgen-cli --version 0.2.89