[menu] simplify menu interactions, fixing an annoying bug

This commit is contained in:
Emil Ernerfeldt 2020-09-25 15:31:27 +02:00
parent 22fffc1793
commit d8021843f8
2 changed files with 17 additions and 67 deletions

View file

@ -25,6 +25,7 @@ pub struct Memory {
// states of various types of widgets // states of various types of widgets
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>, pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
#[cfg_attr(feature = "serde", serde(skip))]
pub(crate) menu_bar: HashMap<Id, menu::BarState>, pub(crate) menu_bar: HashMap<Id, menu::BarState>,
pub(crate) resize: HashMap<Id, resize::State>, pub(crate) resize: HashMap<Id, resize::State>,
pub(crate) scroll_areas: HashMap<Id, scroll_area::State>, pub(crate) scroll_areas: HashMap<Id, scroll_area::State>,

View file

@ -18,23 +18,9 @@
use crate::{color::TRANSPARENT, paint::Stroke, widgets::*, *}; use crate::{color::TRANSPARENT, paint::Stroke, widgets::*, *};
/// What is saved between frames. /// What is saved between frames.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub(crate) struct BarState { pub(crate) struct BarState {
#[cfg_attr(feature = "serde", serde(skip))]
open_menu: Option<Id>, open_menu: Option<Id>,
#[cfg_attr(feature = "serde", serde(skip))]
/// When did we open a menu?
open_time: f64,
}
impl Default for BarState {
fn default() -> Self {
Self {
open_menu: None,
open_time: f64::NEG_INFINITY,
}
}
} }
impl BarState { impl BarState {
@ -49,12 +35,6 @@ impl BarState {
fn save(self, ctx: &Context, bar_id: Id) { fn save(self, ctx: &Context, bar_id: Id) {
ctx.memory().menu_bar.insert(bar_id, self); ctx.memory().menu_bar.insert(bar_id, self);
} }
fn close_menus(ctx: &Context, bar_id: Id) {
let mut bar_state = BarState::load(ctx, &bar_id);
bar_state.open_menu = None;
bar_state.save(ctx, bar_id);
}
} }
pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect) { pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect) {
@ -75,16 +55,7 @@ pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect)
ui.set_desired_height(height); ui.set_desired_height(height);
ui.expand_to_size(vec2(ui.available().width(), height)); ui.expand_to_size(vec2(ui.available().width(), height));
let ret = add_contents(ui); add_contents(ui)
let clicked_outside = !ui.hovered(ui.rect()) && ui.input().mouse.click;
if clicked_outside || ui.input().key_pressed(Key::Escape) {
// TODO: this prevents sub-menus in menus. We should fix that.
let bar_id = ui.id();
BarState::close_menus(ui.ctx(), bar_id);
}
ret
}) })
}) })
} }
@ -112,8 +83,16 @@ fn menu_impl<'c>(
} }
let button_response = ui.add(button); let button_response = ui.add(button);
if button_response.clicked {
interact_with_menu_button(&mut bar_state, ui.input(), menu_id, &button_response); // Toggle
if bar_state.open_menu == Some(menu_id) {
bar_state.open_menu = None;
} else {
bar_state.open_menu = Some(menu_id);
}
} else if button_response.hovered && bar_state.open_menu.is_some() {
bar_state.open_menu = Some(menu_id);
}
if bar_state.open_menu == Some(menu_id) { if bar_state.open_menu == Some(menu_id) {
let area = Area::new(menu_id) let area = Area::new(menu_id)
@ -121,7 +100,7 @@ fn menu_impl<'c>(
.fixed_pos(button_response.rect.left_bottom()); .fixed_pos(button_response.rect.left_bottom());
let frame = Frame::menu(ui.style()); let frame = Frame::menu(ui.style());
let menu_response = area.show(ui.ctx(), |ui| { area.show(ui.ctx(), |ui| {
frame.show(ui, |ui| { frame.show(ui, |ui| {
let mut style = ui.style().clone(); let mut style = ui.style().clone();
style.spacing.button_padding = vec2(2.0, 0.0); style.spacing.button_padding = vec2(2.0, 0.0);
@ -137,42 +116,12 @@ fn menu_impl<'c>(
}) })
}); });
if menu_response.hovered && ui.input().mouse.released { // TODO: this prevents sub-menus in menus. We should fix that.
if ui.input().key_pressed(Key::Escape) || ui.input().mouse.click && !button_response.clicked
{
bar_state.open_menu = None; bar_state.open_menu = None;
} }
} }
bar_state.save(ui.ctx(), bar_id); bar_state.save(ui.ctx(), bar_id);
} }
fn interact_with_menu_button(
bar_state: &mut BarState,
input: &InputState,
menu_id: Id,
button_response: &Response,
) {
if button_response.hovered && input.mouse.pressed {
if bar_state.open_menu.is_some() {
bar_state.open_menu = None;
} else {
bar_state.open_menu = Some(menu_id);
bar_state.open_time = input.time;
}
}
if button_response.hovered && input.mouse.released && bar_state.open_menu.is_some() {
let time_since_open = input.time - bar_state.open_time;
if time_since_open < 0.4 {
// A quick click
bar_state.open_menu = Some(menu_id);
bar_state.open_time = input.time;
} else {
// A long hold, then release
bar_state.open_menu = None;
}
}
if button_response.hovered && bar_state.open_menu.is_some() {
bar_state.open_menu = Some(menu_id);
}
}