Clean up some of the new context menus
Follow-up to https://github.com/emilk/egui/pull/543 * Add entry to CHANGELOG.md * Add entry to contributors in README.md * Improve documentation * Simplify demo
This commit is contained in:
parent
46fb9ff09b
commit
41f77ba7d7
7 changed files with 106 additions and 88 deletions
|
@ -7,6 +7,9 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added ⭐
|
||||||
|
* Add context menus: See `Ui::menu_button` and `Response::context_menu` ([#543](https://github.com/emilk/egui/pull/543)).
|
||||||
|
|
||||||
|
|
||||||
## 0.15.0 - 2021-10-24 - Syntax highlighting and hscroll
|
## 0.15.0 - 2021-10-24 - Syntax highlighting and hscroll
|
||||||
|
|
||||||
|
|
|
@ -362,6 +362,7 @@ Notable contributions by:
|
||||||
* [@EmbersArc](https://github.com/EmbersArc): [Plots](https://github.com/emilk/egui/pulls?q=+is%3Apr+author%3AEmbersArc)
|
* [@EmbersArc](https://github.com/EmbersArc): [Plots](https://github.com/emilk/egui/pulls?q=+is%3Apr+author%3AEmbersArc)
|
||||||
* [@AsmPrgmC3](https://github.com/AsmPrgmC3): [Proper sRGBA blending in `egui_web`](https://github.com/emilk/egui/pull/650)
|
* [@AsmPrgmC3](https://github.com/AsmPrgmC3): [Proper sRGBA blending in `egui_web`](https://github.com/emilk/egui/pull/650)
|
||||||
* [@AlexApps99](https://github.com/AlexApps99): [`egui_glow`](https://github.com/emilk/egui/pull/685)
|
* [@AlexApps99](https://github.com/AlexApps99): [`egui_glow`](https://github.com/emilk/egui/pull/685)
|
||||||
|
* [@mankinskin](https://github.com/mankinskin): [Context menus](https://github.com/emilk/egui/pull/543)
|
||||||
* And [many more](https://github.com/emilk/egui/graphs/contributors)
|
* And [many more](https://github.com/emilk/egui/graphs/contributors)
|
||||||
|
|
||||||
egui is licensed under [MIT](LICENSE-MIT) OR [Apache-2.0](LICENSE-APACHE).
|
egui is licensed under [MIT](LICENSE-MIT) OR [Apache-2.0](LICENSE-APACHE).
|
||||||
|
|
|
@ -305,6 +305,7 @@ impl CtxRef {
|
||||||
Self::layer_painter(self, LayerId::debug())
|
Self::layer_painter(self, LayerId::debug())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Respond to secondary clicks (right-clicks) by showing the given menu.
|
||||||
pub(crate) fn show_context_menu(
|
pub(crate) fn show_context_menu(
|
||||||
&self,
|
&self,
|
||||||
response: &Response,
|
response: &Response,
|
||||||
|
|
|
@ -349,6 +349,7 @@ impl MenuRoot {
|
||||||
MenuResponse::Stay => {}
|
MenuResponse::Stay => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Respond to secondary (right) clicks.
|
||||||
pub fn context_click_interaction(response: &Response, root: &mut MenuRootManager, id: Id) {
|
pub fn context_click_interaction(response: &Response, root: &mut MenuRootManager, id: Id) {
|
||||||
let menu_response = Self::context_interaction(response, root, id);
|
let menu_response = Self::context_interaction(response, root, id);
|
||||||
Self::handle_menu_response(root, menu_response);
|
Self::handle_menu_response(root, menu_response);
|
||||||
|
|
|
@ -471,6 +471,17 @@ impl Response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Response to secondary clicks (right-clicks) by showing the given menu.
|
||||||
|
///
|
||||||
|
/// ``` rust
|
||||||
|
/// # let mut ui = &mut egui::Ui::__test();
|
||||||
|
/// let response = ui.label("Right-click me!");
|
||||||
|
/// response.context_menu(|ui|{
|
||||||
|
/// if ui.button("Close the menu").clicked() {
|
||||||
|
/// ui.close_menu();
|
||||||
|
/// }
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
pub fn context_menu(&self, add_contents: impl FnOnce(&mut Ui)) -> &Self {
|
pub fn context_menu(&self, add_contents: impl FnOnce(&mut Ui)) -> &Self {
|
||||||
self.ctx.show_context_menu(self, add_contents);
|
self.ctx.show_context_menu(self, add_contents);
|
||||||
self
|
self
|
||||||
|
|
|
@ -1737,6 +1737,7 @@ impl Ui {
|
||||||
self.advance_cursor_after_rect(Rect::from_min_size(top_left, size));
|
self.advance_cursor_after_rect(Rect::from_min_size(top_left, size));
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close menu (with submenus), if any.
|
/// Close menu (with submenus), if any.
|
||||||
pub fn close_menu(&mut self) {
|
pub fn close_menu(&mut self) {
|
||||||
if let Some(menu_state) = &mut self.menu_state {
|
if let Some(menu_state) = &mut self.menu_state {
|
||||||
|
@ -1744,12 +1745,15 @@ impl Ui {
|
||||||
}
|
}
|
||||||
self.menu_state = None;
|
self.menu_state = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_menu_state(&self) -> Option<Arc<RwLock<MenuState>>> {
|
pub(crate) fn get_menu_state(&self) -> Option<Arc<RwLock<MenuState>>> {
|
||||||
self.menu_state.clone()
|
self.menu_state.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_menu_state(&mut self, menu_state: Option<Arc<RwLock<MenuState>>>) {
|
pub(crate) fn set_menu_state(&mut self, menu_state: Option<Arc<RwLock<MenuState>>>) {
|
||||||
self.menu_state = menu_state;
|
self.menu_state = menu_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
/// Create a menu button. Creates a button for a sub-menu when the `Ui` is inside a menu.
|
/// Create a menu button. Creates a button for a sub-menu when the `Ui` is inside a menu.
|
||||||
///
|
///
|
||||||
|
@ -1757,7 +1761,9 @@ impl Ui {
|
||||||
/// # let mut ui = egui::Ui::__test();
|
/// # let mut ui = egui::Ui::__test();
|
||||||
/// ui.menu_button("My menu", |ui| {
|
/// ui.menu_button("My menu", |ui| {
|
||||||
/// ui.menu_button("My sub-menu", |ui| {
|
/// ui.menu_button("My sub-menu", |ui| {
|
||||||
/// ui.label("Item");
|
/// if ui.button("Close the menu").clicked() {
|
||||||
|
/// ui.close_menu();
|
||||||
|
/// }
|
||||||
/// });
|
/// });
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -17,7 +17,6 @@ fn sigmoid(x: f64) -> f64 {
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct ContextMenus {
|
pub struct ContextMenus {
|
||||||
title: String,
|
|
||||||
plot: Plot,
|
plot: Plot,
|
||||||
show_axes: [bool; 2],
|
show_axes: [bool; 2],
|
||||||
allow_drag: bool,
|
allow_drag: bool,
|
||||||
|
@ -28,71 +27,9 @@ pub struct ContextMenus {
|
||||||
height: f32,
|
height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenus {
|
|
||||||
fn example_plot(&self) -> egui::plot::Plot {
|
|
||||||
use egui::plot::{Line, Value, Values};
|
|
||||||
let n = 128;
|
|
||||||
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
|
|
||||||
use std::f64::consts::TAU;
|
|
||||||
let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU);
|
|
||||||
match self.plot {
|
|
||||||
Plot::Sin => Value::new(x, x.sin()),
|
|
||||||
Plot::Bell => Value::new(x, 10.0 * gaussian(x)),
|
|
||||||
Plot::Sigmoid => Value::new(x, sigmoid(x)),
|
|
||||||
}
|
|
||||||
})));
|
|
||||||
egui::plot::Plot::new("example_plot")
|
|
||||||
.show_axes(self.show_axes)
|
|
||||||
.allow_drag(self.allow_drag)
|
|
||||||
.allow_zoom(self.allow_zoom)
|
|
||||||
.center_x_axis(self.center_x_axis)
|
|
||||||
.center_x_axis(self.center_y_axis)
|
|
||||||
.line(line)
|
|
||||||
.width(self.width)
|
|
||||||
.height(self.height)
|
|
||||||
.data_aspect(1.0)
|
|
||||||
}
|
|
||||||
fn nested_menus(ui: &mut egui::Ui) {
|
|
||||||
if ui.button("Open...").clicked() {
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
ui.menu_button("SubMenu", |ui| {
|
|
||||||
ui.menu_button("SubMenu", |ui| {
|
|
||||||
if ui.button("Open...").clicked() {
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
let _ = ui.button("Item");
|
|
||||||
});
|
|
||||||
ui.menu_button("SubMenu", |ui| {
|
|
||||||
if ui.button("Open...").clicked() {
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
let _ = ui.button("Item");
|
|
||||||
});
|
|
||||||
let _ = ui.button("Item");
|
|
||||||
if ui.button("Open...").clicked() {
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.menu_button("SubMenu", |ui| {
|
|
||||||
let _ = ui.button("Item1");
|
|
||||||
let _ = ui.button("Item2");
|
|
||||||
let _ = ui.button("Item3");
|
|
||||||
let _ = ui.button("Item4");
|
|
||||||
if ui.button("Open...").clicked() {
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let _ = ui.button("Very long text for this item");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_TITLE: &str = "☰ Context Menus";
|
|
||||||
|
|
||||||
impl Default for ContextMenus {
|
impl Default for ContextMenus {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
title: DEFAULT_TITLE.to_owned(),
|
|
||||||
plot: Plot::Sin,
|
plot: Plot::Sin,
|
||||||
show_axes: [true, true],
|
show_axes: [true, true],
|
||||||
allow_drag: true,
|
allow_drag: true,
|
||||||
|
@ -104,30 +41,35 @@ impl Default for ContextMenus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Demo for ContextMenus {
|
impl super::Demo for ContextMenus {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
DEFAULT_TITLE
|
"☰ Context Menus"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||||
let Self { title, .. } = self.clone();
|
|
||||||
|
|
||||||
use super::View;
|
use super::View;
|
||||||
let window = egui::Window::new(title)
|
egui::Window::new(self.name())
|
||||||
.id(egui::Id::new("demo_context_menus")) // required since we change the title
|
|
||||||
.vscroll(false)
|
.vscroll(false)
|
||||||
.open(open);
|
.resizable(false)
|
||||||
window.show(ctx, |ui| self.ui(ui));
|
.open(open)
|
||||||
|
.show(ctx, |ui| self.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::View for ContextMenus {
|
impl super::View for ContextMenus {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
ui.horizontal(|ui| ui.text_edit_singleline(&mut self.title));
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(self.example_plot())
|
ui.menu_button("Click for menu", Self::nested_menus);
|
||||||
.on_hover_text("Right click for options")
|
ui.button("Right-click for menu")
|
||||||
.context_menu(|ui| {
|
.context_menu(Self::nested_menus);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
ui.label("Right-click plot to edit it!");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.add(self.example_plot()).context_menu(|ui| {
|
||||||
ui.menu_button("Plot", |ui| {
|
ui.menu_button("Plot", |ui| {
|
||||||
if ui.radio_value(&mut self.plot, Plot::Sin, "Sin").clicked()
|
if ui.radio_value(&mut self.plot, Plot::Sin, "Sin").clicked()
|
||||||
|| ui
|
|| ui
|
||||||
|
@ -163,12 +105,65 @@ impl super::View for ContextMenus {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.label("Right-click plot to edit it!");
|
}
|
||||||
ui.separator();
|
}
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.menu_button("Click for menu", Self::nested_menus);
|
impl ContextMenus {
|
||||||
ui.button("Right-click for menu")
|
fn example_plot(&self) -> egui::plot::Plot {
|
||||||
.context_menu(Self::nested_menus);
|
use egui::plot::{Line, Value, Values};
|
||||||
});
|
let n = 128;
|
||||||
|
let line = Line::new(Values::from_values_iter((0..=n).map(|i| {
|
||||||
|
use std::f64::consts::TAU;
|
||||||
|
let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU);
|
||||||
|
match self.plot {
|
||||||
|
Plot::Sin => Value::new(x, x.sin()),
|
||||||
|
Plot::Bell => Value::new(x, 10.0 * gaussian(x)),
|
||||||
|
Plot::Sigmoid => Value::new(x, sigmoid(x)),
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
egui::plot::Plot::new("example_plot")
|
||||||
|
.show_axes(self.show_axes)
|
||||||
|
.allow_drag(self.allow_drag)
|
||||||
|
.allow_zoom(self.allow_zoom)
|
||||||
|
.center_x_axis(self.center_x_axis)
|
||||||
|
.center_x_axis(self.center_y_axis)
|
||||||
|
.line(line)
|
||||||
|
.width(self.width)
|
||||||
|
.height(self.height)
|
||||||
|
.data_aspect(1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nested_menus(ui: &mut egui::Ui) {
|
||||||
|
if ui.button("Open...").clicked() {
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
ui.menu_button("SubMenu", |ui| {
|
||||||
|
ui.menu_button("SubMenu", |ui| {
|
||||||
|
if ui.button("Open...").clicked() {
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
let _ = ui.button("Item");
|
||||||
|
});
|
||||||
|
ui.menu_button("SubMenu", |ui| {
|
||||||
|
if ui.button("Open...").clicked() {
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
let _ = ui.button("Item");
|
||||||
|
});
|
||||||
|
let _ = ui.button("Item");
|
||||||
|
if ui.button("Open...").clicked() {
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.menu_button("SubMenu", |ui| {
|
||||||
|
let _ = ui.button("Item1");
|
||||||
|
let _ = ui.button("Item2");
|
||||||
|
let _ = ui.button("Item3");
|
||||||
|
let _ = ui.button("Item4");
|
||||||
|
if ui.button("Open...").clicked() {
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _ = ui.button("Very long text for this item");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue