mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Context menus (#543)
Main usage: `response.context_menu(…)` and `ui.menu_button`
This commit is contained in:
174
egui_demo_lib/src/apps/demo/context_menu.rs
Normal file
174
egui_demo_lib/src/apps/demo/context_menu.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
enum Plot {
|
||||
Sin,
|
||||
Bell,
|
||||
Sigmoid,
|
||||
}
|
||||
|
||||
fn gaussian(x: f64) -> f64 {
|
||||
let var: f64 = 2.0;
|
||||
f64::exp(-(x / var).powi(2)) / (var * f64::sqrt(std::f64::consts::TAU))
|
||||
}
|
||||
fn sigmoid(x: f64) -> f64 {
|
||||
-1.0 + 2.0 / (1.0 + f64::exp(-x))
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct ContextMenus {
|
||||
title: String,
|
||||
plot: Plot,
|
||||
show_axes: [bool; 2],
|
||||
allow_drag: bool,
|
||||
allow_zoom: bool,
|
||||
center_x_axis: bool,
|
||||
center_y_axis: bool,
|
||||
width: 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 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
title: DEFAULT_TITLE.to_owned(),
|
||||
plot: Plot::Sin,
|
||||
show_axes: [true, true],
|
||||
allow_drag: true,
|
||||
allow_zoom: true,
|
||||
center_x_axis: false,
|
||||
center_y_axis: false,
|
||||
width: 400.0,
|
||||
height: 200.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl super::Demo for ContextMenus {
|
||||
fn name(&self) -> &'static str {
|
||||
DEFAULT_TITLE
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
let Self { title, .. } = self.clone();
|
||||
|
||||
use super::View;
|
||||
let window = egui::Window::new(title)
|
||||
.id(egui::Id::new("demo_context_menus")) // required since we change the title
|
||||
.vscroll(false)
|
||||
.open(open);
|
||||
window.show(ctx, |ui| self.ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for ContextMenus {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.horizontal(|ui| ui.text_edit_singleline(&mut self.title));
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(self.example_plot())
|
||||
.on_hover_text("Right click for options")
|
||||
.context_menu(|ui| {
|
||||
ui.menu_button("Plot", |ui| {
|
||||
if ui.radio_value(&mut self.plot, Plot::Sin, "Sin").clicked()
|
||||
|| ui
|
||||
.radio_value(&mut self.plot, Plot::Bell, "Gaussian")
|
||||
.clicked()
|
||||
|| ui
|
||||
.radio_value(&mut self.plot, Plot::Sigmoid, "Sigmoid")
|
||||
.clicked()
|
||||
{
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
egui::Grid::new("button_grid").show(ui, |ui| {
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.width)
|
||||
.speed(1.0)
|
||||
.prefix("Width:"),
|
||||
);
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.height)
|
||||
.speed(1.0)
|
||||
.prefix("Height:"),
|
||||
);
|
||||
ui.end_row();
|
||||
ui.checkbox(&mut self.show_axes[0], "x-Axis");
|
||||
ui.checkbox(&mut self.show_axes[1], "y-Axis");
|
||||
ui.end_row();
|
||||
if ui.checkbox(&mut self.allow_drag, "Drag").changed()
|
||||
|| ui.checkbox(&mut self.allow_zoom, "Zoom").changed()
|
||||
{
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.label("Right-click plot to edit it!");
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
ui.menu_button("Click for menu", Self::nested_menus);
|
||||
ui.button("Right-click for menu")
|
||||
.context_menu(Self::nested_menus);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ impl Default for Demos {
|
||||
Self::from_demos(vec![
|
||||
Box::new(super::code_editor::CodeEditor::default()),
|
||||
Box::new(super::code_example::CodeExample::default()),
|
||||
Box::new(super::context_menu::ContextMenus::default()),
|
||||
Box::new(super::dancing_strings::DancingStrings::default()),
|
||||
Box::new(super::drag_and_drop::DragAndDropDemo::default()),
|
||||
Box::new(super::font_book::FontBook::default()),
|
||||
@@ -227,9 +228,10 @@ fn show_menu_bar(ui: &mut Ui) {
|
||||
use egui::*;
|
||||
|
||||
menu::bar(ui, |ui| {
|
||||
menu::menu(ui, "File", |ui| {
|
||||
ui.menu_button("File", |ui| {
|
||||
if ui.button("Organize windows").clicked() {
|
||||
ui.ctx().memory().reset_areas();
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui
|
||||
.button("Reset egui memory")
|
||||
@@ -237,6 +239,7 @@ fn show_menu_bar(ui: &mut Ui) {
|
||||
.clicked()
|
||||
{
|
||||
*ui.ctx().memory() = Default::default();
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,12 +75,12 @@ pub fn drop_target<R>(
|
||||
|
||||
InnerResponse::new(ret, response)
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct DragAndDropDemo {
|
||||
/// columns with items
|
||||
columns: Vec<Vec<&'static str>>,
|
||||
columns: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Default for DragAndDropDemo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -88,7 +88,10 @@ impl Default for DragAndDropDemo {
|
||||
vec!["Item A", "Item B", "Item C"],
|
||||
vec!["Item D", "Item E"],
|
||||
vec!["Item F", "Item G", "Item H"],
|
||||
],
|
||||
]
|
||||
.into_iter()
|
||||
.map(|v| v.into_iter().map(ToString::to_string).collect())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,20 +117,25 @@ impl super::View for DragAndDropDemo {
|
||||
ui.label("This is a proof-of-concept of drag-and-drop in egui.");
|
||||
ui.label("Drag items between columns.");
|
||||
|
||||
let id_source = "my_drag_and_drop_demo";
|
||||
let mut source_col_row = None;
|
||||
let mut drop_col = None;
|
||||
|
||||
ui.columns(self.columns.len(), |uis| {
|
||||
for (col_idx, column) in self.columns.iter().enumerate() {
|
||||
for (col_idx, column) in self.columns.clone().into_iter().enumerate() {
|
||||
let ui = &mut uis[col_idx];
|
||||
let can_accept_what_is_being_dragged = true; // We accept anything being dragged (for now) ¯\_(ツ)_/¯
|
||||
let response = drop_target(ui, can_accept_what_is_being_dragged, |ui| {
|
||||
ui.set_min_size(vec2(64.0, 100.0));
|
||||
|
||||
for (row_idx, &item) in column.iter().enumerate() {
|
||||
let item_id = Id::new("item").with(col_idx).with(row_idx);
|
||||
for (row_idx, item) in column.iter().enumerate() {
|
||||
let item_id = Id::new(id_source).with(col_idx).with(row_idx);
|
||||
drag_source(ui, item_id, |ui| {
|
||||
ui.label(item);
|
||||
let response = ui.add(Label::new(item).sense(Sense::click()));
|
||||
response.context_menu(|ui| {
|
||||
if ui.button("Remove").clicked() {
|
||||
self.columns[col_idx].remove(row_idx);
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ui.memory().is_being_dragged(item_id) {
|
||||
@@ -137,6 +145,13 @@ impl super::View for DragAndDropDemo {
|
||||
})
|
||||
.response;
|
||||
|
||||
response.context_menu(|ui| {
|
||||
if ui.button("New Item").clicked() {
|
||||
self.columns[col_idx].push("New Item".to_string());
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
|
||||
let is_being_dragged = ui.memory().is_anything_being_dragged();
|
||||
if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
|
||||
drop_col = Some(col_idx);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
mod app;
|
||||
pub mod code_editor;
|
||||
pub mod code_example;
|
||||
pub mod context_menu;
|
||||
pub mod dancing_strings;
|
||||
pub mod demo_app_windows;
|
||||
pub mod drag_and_drop;
|
||||
|
||||
@@ -212,6 +212,7 @@ impl WidgetGallery {
|
||||
|
||||
ui.add(doc_link_label("Plot", "plot"));
|
||||
ui.add(example_plot());
|
||||
|
||||
ui.end_row();
|
||||
|
||||
ui.hyperlink_to(
|
||||
@@ -227,14 +228,14 @@ impl WidgetGallery {
|
||||
}
|
||||
|
||||
fn example_plot() -> egui::plot::Plot {
|
||||
use egui::plot::{Line, Plot, Value, Values};
|
||||
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);
|
||||
let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU);
|
||||
Value::new(x, x.sin())
|
||||
})));
|
||||
Plot::new("example_plot")
|
||||
egui::plot::Plot::new("example_plot")
|
||||
.line(line)
|
||||
.height(32.0)
|
||||
.data_aspect(1.0)
|
||||
|
||||
@@ -87,7 +87,6 @@ impl super::View for WindowOptions {
|
||||
anchor,
|
||||
anchor_offset,
|
||||
} = self;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("title:");
|
||||
ui.text_edit_singleline(title);
|
||||
|
||||
Reference in New Issue
Block a user