egui/egui_demo_lib/src/apps/demo/demo_windows.rs

239 lines
7.1 KiB
Rust
Raw Normal View History

2021-02-07 13:46:53 +00:00
use egui::{CtxRef, ScrollArea, Ui, Window};
use std::collections::BTreeSet;
// ----------------------------------------------------------------------------
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
struct Demos {
#[cfg_attr(feature = "persistence", serde(skip))]
demos: Vec<Box<dyn super::Demo>>,
open: BTreeSet<String>,
}
impl Default for Demos {
fn default() -> Self {
let demos: Vec<Box<dyn super::Demo>> = vec![
Box::new(super::dancing_strings::DancingStrings::default()),
Box::new(super::drag_and_drop::DragAndDropDemo::default()),
Basic multi touch support (issue #279) (#306) * translate touch events from glium to egui Unfortunately, winit does not seem to create _Touch_ events for the touch pad on my mac. Only _TouchpadPressure_ events are sent. Found some issues (like [this](https://github.com/rust-windowing/winit/issues/54)), but I am not sure what they exactly mean: Sometimes, touch events are mixed with touch-to-pointer translation in the discussions. * translate touch events from web_sys to egui The are a few open topics: - egui_web currently translates touch events into pointer events. I guess this should change, such that egui itself performs this kind of conversion. - `pub fn egui_web::pos_from_touch_event` is a public function, but I would like to change the return type to an `Option`. Shouldn't this function be private, anyway? * introduce `TouchState` and `Gesture` InputState.touch was introduced with type `TouchState`, just as InputState.pointer is of type `Pointer`. The TouchState internally relies on a collection of `Gesture`s. This commit provides the first rudimentary implementation of a Gesture, but has no functionality, yet. * add method InputState::zoom() So far, the method always returns `None`, but it should work as soon as the `Zoom` gesture is implemented. * manage one `TouchState` per individual device Although quite unlikely, it is still possible to connect more than one touch device. (I have three touch pads connected to my MacBook in total, but unfortunately `winit` sends touch events for none of them.) We do not want to mix-up the touches from different devices. * implement control loop for gesture detection The basic idea is that each gesture can focus on detection logic and does not have to care (too much) about managing touch state in general. * streamline `Gesture` trait, simplifying impl's * implement first version of Zoom gesture * fix failing doctest a simple `TODO` should be enough * get rid of `Gesture`s * Provide a Zoom/Rotate window in the demo app For now, it works for two fingers only. The third finger interrupts the gesture. Bugs: - Pinching in the demo window also moves the window -> Pointer events must be ignored when touch is active - Pinching also works when doing it outside the demo window -> it would be nice to return the touch info in the `Response` of the painter allocation * fix comments and non-idiomatic code * update touch state *each frame* * change egui_demo to use *relative* touch data * support more than two fingers This commit includes an improved Demo Window for egui_demo, and a complete re-write of the gesture detection. The PR should be ready for review, soon. * cleanup code and comments for review * minor code simplifications * oops – forgot the changelog * resolve comment https://github.com/emilk/egui/pull/306/files/fee8ed83dbe715b5b70433faacfe74b59c99e4a4#r623226656 * accept suggestion https://github.com/emilk/egui/pull/306#discussion_r623229228 Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com> * fix syntax error (dough!) * remove `dbg!` (why didnt clippy see this?) * apply suggested diffs from review * fix conversion of physical location to Pos2 * remove redundanct type `TouchAverages` * remove trailing space * avoid initial translation jump in plot demo * extend the demo so it shows off translation Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2021-05-06 19:01:10 +00:00
Box::new(super::zoom_rotate::ZoomRotate::default()),
Box::new(super::font_book::FontBook::default()),
2021-02-07 13:46:53 +00:00
Box::new(super::DemoWindow::default()),
Box::new(super::painting::Painting::default()),
2021-02-14 20:39:04 +00:00
Box::new(super::plot_demo::PlotDemo::default()),
Box::new(super::scrolling::Scrolling::default()),
Box::new(super::sliders::Sliders::default()),
Box::new(super::widget_gallery::WidgetGallery::default()),
Box::new(super::window_options::WindowOptions::default()),
2021-02-07 13:46:53 +00:00
Box::new(super::tests::WindowResizeTest::default()),
// Tests:
2021-03-13 11:38:03 +00:00
Box::new(super::tests::CursorTest::default()),
Box::new(super::tests::IdTest::default()),
2021-02-06 15:54:38 +00:00
Box::new(super::tests::InputTest::default()),
Box::new(super::layout_test::LayoutTest::default()),
Box::new(super::tests::ManualLayoutTest::default()),
Box::new(super::tests::TableTest::default()),
];
use crate::apps::demo::Demo;
let mut open = BTreeSet::new();
open.insert(
super::widget_gallery::WidgetGallery::default()
.name()
.to_owned(),
);
2021-05-06 18:49:22 +00:00
Self { demos, open }
}
}
impl Demos {
pub fn checkboxes(&mut self, ui: &mut Ui) {
2021-05-06 18:49:22 +00:00
let Self { demos, open } = self;
for demo in demos {
let mut is_open = open.contains(demo.name());
ui.checkbox(&mut is_open, demo.name());
set_open(open, demo.name(), is_open);
}
}
pub fn show(&mut self, ctx: &CtxRef) {
2021-05-06 18:49:22 +00:00
let Self { demos, open } = self;
for demo in demos {
let mut is_open = open.contains(demo.name());
demo.show(ctx, &mut is_open);
set_open(open, demo.name(), is_open);
}
}
}
fn set_open(open: &mut BTreeSet<String>, key: &'static str, is_open: bool) {
if is_open {
if !open.contains(key) {
open.insert(key.to_owned());
}
} else {
open.remove(key);
}
}
// ----------------------------------------------------------------------------
/// A menu bar in which you can select different demo windows to show.
#[derive(Default)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
pub struct DemoWindows {
open_windows: OpenWindows,
demos: Demos,
}
impl DemoWindows {
/// Show the app ui (menu bar and windows).
/// `sidebar_ui` can be used to optionally show some things in the sidebar
pub fn ui(&mut self, ctx: &CtxRef) {
egui::SidePanel::left("side_panel", 190.0).show(ctx, |ui| {
ui.heading("✒ egui demos");
2020-12-12 18:43:12 +00:00
ui.separator();
ScrollArea::auto_sized().show(ui, |ui| {
use egui::special_emojis::{GITHUB, OS_APPLE, OS_LINUX, OS_WINDOWS};
ui.label("egui is an immediate mode GUI library written in Rust.");
ui.hyperlink_to(
format!("{} egui home page", GITHUB),
"https://github.com/emilk/egui",
);
ui.label(format!(
"egui can be run on the web, or natively on {}{}{}",
OS_APPLE, OS_LINUX, OS_WINDOWS,
));
ui.separator();
ui.heading("Windows:");
2021-02-07 13:46:53 +00:00
self.demos.checkboxes(ui);
ui.separator();
ui.label("egui:");
2021-02-07 13:46:53 +00:00
self.open_windows.checkboxes(ui);
ui.separator();
if ui.button("Organize windows").clicked() {
ui.ctx().memory().reset_areas();
}
});
});
2020-10-21 20:10:55 +00:00
egui::TopPanel::top("menu_bar").show(ctx, |ui| {
2021-01-01 20:27:10 +00:00
show_menu_bar(ui);
});
{
let mut fill = ctx.style().visuals.extreme_bg_color;
if !cfg!(target_arch = "wasm32") {
// Native: WrapApp uses a transparent window, so let's show that off:
// NOTE: the OS compositor assumes "normal" blending, so we need to hack it:
let [r, g, b, _] = fill.to_array();
fill = egui::Color32::from_rgba_premultiplied(r, g, b, 180);
}
let frame = egui::Frame::none().fill(fill);
egui::CentralPanel::default().frame(frame).show(ctx, |_| {});
}
2021-02-03 00:08:23 +00:00
2021-01-01 23:13:34 +00:00
self.windows(ctx);
}
/// Show the open windows.
2021-01-01 23:13:34 +00:00
fn windows(&mut self, ctx: &CtxRef) {
let Self {
open_windows,
demos,
..
} = self;
2020-12-12 18:43:12 +00:00
Window::new("🔧 Settings")
.open(&mut open_windows.settings)
2021-01-02 11:00:14 +00:00
.scroll(true)
.show(ctx, |ui| {
ctx.settings_ui(ui);
});
2020-12-12 18:43:12 +00:00
Window::new("🔍 Inspection")
.open(&mut open_windows.inspection)
.scroll(true)
.show(ctx, |ui| {
ctx.inspection_ui(ui);
});
2020-12-12 18:43:12 +00:00
Window::new("📝 Memory")
.open(&mut open_windows.memory)
.resizable(false)
.show(ctx, |ui| {
ctx.memory_ui(ui);
});
demos.show(ctx);
}
}
// ----------------------------------------------------------------------------
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
struct OpenWindows {
// egui stuff:
settings: bool,
inspection: bool,
memory: bool,
}
impl Default for OpenWindows {
fn default() -> Self {
2021-02-07 13:46:53 +00:00
OpenWindows::none()
}
}
impl OpenWindows {
fn none() -> Self {
Self {
settings: false,
inspection: false,
memory: false,
}
}
fn checkboxes(&mut self, ui: &mut Ui) {
let Self {
settings,
inspection,
memory,
} = self;
2021-02-07 13:46:53 +00:00
2020-12-12 18:43:12 +00:00
ui.checkbox(settings, "🔧 Settings");
ui.checkbox(inspection, "🔍 Inspection");
ui.checkbox(memory, "📝 Memory");
}
}
2021-01-01 20:27:10 +00:00
fn show_menu_bar(ui: &mut Ui) {
use egui::*;
menu::bar(ui, |ui| {
menu::menu(ui, "File", |ui| {
if ui.button("Organize windows").clicked() {
ui.ctx().memory().reset_areas();
}
if ui
.button("Clear egui memory")
.on_hover_text("Forget scroll, collapsing headers etc")
.clicked()
{
*ui.ctx().memory() = Default::default();
}
});
});
}