diff --git a/egui/src/memory.rs b/egui/src/memory.rs index 215264f1..0a082738 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -11,18 +11,21 @@ use crate::{any, area, window, Id, InputState, LayerId, Pos2, Rect, Style}; /// /// If you want this to persist when closing your app you should serialize `Memory` and store it. /// -/// If you want to store data for your widgets, you should look at `data`/`data_temp` and `id_data`/`id_data_temp` fields, and read the documentation of [`any`] module. +/// If you want to store data for your widgets, you should look at `data`/`data_temp` and +/// `id_data`/`id_data_temp` fields, and read the documentation of [`any`] module. #[derive(Clone, Debug, Default)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] pub struct Memory { pub options: Options, - /// This map stores current states for widgets that don't require `Id`. This will be saved between different program runs if you use the `persistence` feature. + /// This map stores current states for widgets that don't require `Id`. + /// This will be saved between different program runs if you use the `persistence` feature. #[cfg(feature = "persistence")] pub data: any::serializable::TypeMap, - /// This map stores current states for widgets that don't require `Id`. This will be saved between different program runs if you use the `persistence` feature. + /// This map stores current states for widgets that don't require `Id`. + /// This will be saved between different program runs if you use the `persistence` feature. #[cfg(not(feature = "persistence"))] pub data: any::TypeMap, @@ -30,11 +33,13 @@ pub struct Memory { #[cfg_attr(feature = "persistence", serde(skip))] pub data_temp: any::TypeMap, - /// This map stores current states for all widgets with custom `Id`s. This will be saved between different program runs if you use the `persistence` feature. + /// This map stores current states for all widgets with custom `Id`s. + /// This will be saved between different program runs if you use the `persistence` feature. #[cfg(feature = "persistence")] pub id_data: any::serializable::AnyMap, - /// This map stores current states for all widgets with custom `Id`s. This will be saved between different program runs if you use the `persistence` feature. + /// This map stores current states for all widgets with custom `Id`s. + /// This will be saved between different program runs if you use the `persistence` feature. #[cfg(not(feature = "persistence"))] pub id_data: any::AnyMap, diff --git a/egui_demo_lib/src/apps/demo/demo_windows.rs b/egui_demo_lib/src/apps/demo/demo_app_windows.rs similarity index 66% rename from egui_demo_lib/src/apps/demo/demo_windows.rs rename to egui_demo_lib/src/apps/demo/demo_app_windows.rs index 5c00aab0..36e8c52a 100644 --- a/egui_demo_lib/src/apps/demo/demo_windows.rs +++ b/egui_demo_lib/src/apps/demo/demo_app_windows.rs @@ -1,3 +1,4 @@ +use super::Demo; use egui::{CtxRef, ScrollArea, Ui, Window}; use std::collections::BTreeSet; @@ -7,17 +8,18 @@ use std::collections::BTreeSet; #[cfg_attr(feature = "persistence", serde(default))] struct Demos { #[cfg_attr(feature = "persistence", serde(skip))] - demos: Vec>, + demos: Vec>, open: BTreeSet, } + impl Default for Demos { fn default() -> Self { - let demos: Vec> = vec![ + Self::from_demos(vec![ Box::new(super::dancing_strings::DancingStrings::default()), Box::new(super::drag_and_drop::DragAndDropDemo::default()), Box::new(super::font_book::FontBook::default()), - Box::new(super::DemoWindow::default()), + Box::new(super::MiscDemoWindow::default()), Box::new(super::multi_touch::MultiTouch::default()), Box::new(super::painting::Painting::default()), Box::new(super::plot_demo::PlotDemo::default()), @@ -26,16 +28,12 @@ impl Default for Demos { Box::new(super::widget_gallery::WidgetGallery::default()), Box::new(super::window_options::WindowOptions::default()), Box::new(super::tests::WindowResizeTest::default()), - // Tests: - Box::new(super::tests::CursorTest::default()), - Box::new(super::tests::IdTest::default()), - 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; +impl Demos { + pub fn from_demos(demos: Vec>) -> Self { let mut open = BTreeSet::new(); open.insert( super::widget_gallery::WidgetGallery::default() @@ -45,8 +43,7 @@ impl Default for Demos { Self { demos, open } } -} -impl Demos { + pub fn checkboxes(&mut self, ui: &mut Ui) { let Self { demos, open } = self; for demo in demos { @@ -56,7 +53,7 @@ impl Demos { } } - pub fn show(&mut self, ctx: &CtxRef) { + pub fn windows(&mut self, ctx: &CtxRef) { let Self { demos, open } = self; for demo in demos { let mut is_open = open.contains(demo.name()); @@ -66,6 +63,63 @@ impl Demos { } } +// ---------------------------------------------------------------------------- + +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] +struct Tests { + #[cfg_attr(feature = "persistence", serde(skip))] + demos: Vec>, + + open: BTreeSet, +} + +impl Default for Tests { + fn default() -> Self { + Self::from_demos(vec![ + Box::new(super::tests::CursorTest::default()), + Box::new(super::tests::IdTest::default()), + 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()), + ]) + } +} + +impl Tests { + pub fn from_demos(demos: Vec>) -> Self { + let mut open = BTreeSet::new(); + open.insert( + super::widget_gallery::WidgetGallery::default() + .name() + .to_owned(), + ); + + Self { demos, open } + } + + pub fn checkboxes(&mut self, ui: &mut Ui) { + 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 windows(&mut self, ctx: &CtxRef) { + 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, key: &'static str, is_open: bool) { if is_open { if !open.contains(key) { @@ -83,16 +137,25 @@ fn set_open(open: &mut BTreeSet, key: &'static str, is_open: bool) { #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] pub struct DemoWindows { - open_windows: OpenWindows, demos: Demos, + tests: Tests, + egui_windows: EguiWindows, } 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) { + let Self { + demos, + tests, + egui_windows, + } = self; + egui::SidePanel::left("side_panel", 190.0).show(ctx, |ui| { - ui.heading("✒ egui demos"); + ui.vertical_centered(|ui| { + ui.heading("✒ egui demos"); + }); ui.separator(); @@ -100,31 +163,32 @@ impl DemoWindows { 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 {}{}{}", + "egui runs on the web, or natively on {}{}{}", OS_APPLE, OS_LINUX, OS_WINDOWS, )); - ui.separator(); - - ui.heading("Windows:"); - self.demos.checkboxes(ui); + ui.vertical_centered(|ui| { + ui.hyperlink_to( + format!("{} egui home page", GITHUB), + "https://github.com/emilk/egui", + ); + }); ui.separator(); - - ui.label("egui:"); - self.open_windows.checkboxes(ui); - + demos.checkboxes(ui); + ui.separator(); + tests.checkboxes(ui); + ui.separator(); + egui_windows.checkboxes(ui); ui.separator(); - if ui.button("Organize windows").clicked() { - ui.ctx().memory().reset_areas(); - } + ui.vertical_centered(|ui| { + if ui.button("Organize windows").clicked() { + ui.ctx().memory().reset_areas(); + } + }); }); }); @@ -150,53 +214,34 @@ impl DemoWindows { /// Show the open windows. fn windows(&mut self, ctx: &CtxRef) { let Self { - open_windows, demos, - .. + tests, + egui_windows, } = self; - Window::new("🔧 Settings") - .open(&mut open_windows.settings) - .scroll(true) - .show(ctx, |ui| { - ctx.settings_ui(ui); - }); - - Window::new("🔍 Inspection") - .open(&mut open_windows.inspection) - .scroll(true) - .show(ctx, |ui| { - ctx.inspection_ui(ui); - }); - - Window::new("📝 Memory") - .open(&mut open_windows.memory) - .resizable(false) - .show(ctx, |ui| { - ctx.memory_ui(ui); - }); - - demos.show(ctx); + demos.windows(ctx); + tests.windows(ctx); + egui_windows.windows(ctx); } } // ---------------------------------------------------------------------------- #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -struct OpenWindows { +struct EguiWindows { // egui stuff: settings: bool, inspection: bool, memory: bool, } -impl Default for OpenWindows { +impl Default for EguiWindows { fn default() -> Self { - OpenWindows::none() + EguiWindows::none() } } -impl OpenWindows { +impl EguiWindows { fn none() -> Self { Self { settings: false, @@ -216,6 +261,35 @@ impl OpenWindows { ui.checkbox(inspection, "🔍 Inspection"); ui.checkbox(memory, "📝 Memory"); } + + fn windows(&mut self, ctx: &CtxRef) { + let Self { + settings, + inspection, + memory, + } = self; + + Window::new("🔧 Settings") + .open(settings) + .scroll(true) + .show(ctx, |ui| { + ctx.settings_ui(ui); + }); + + Window::new("🔍 Inspection") + .open(inspection) + .scroll(true) + .show(ctx, |ui| { + ctx.inspection_ui(ui); + }); + + Window::new("📝 Memory") + .open(memory) + .resizable(false) + .show(ctx, |ui| { + ctx.memory_ui(ui); + }); + } } fn show_menu_bar(ui: &mut Ui) { diff --git a/egui_demo_lib/src/apps/demo/demo_window.rs b/egui_demo_lib/src/apps/demo/misc_demo_window.rs similarity index 68% rename from egui_demo_lib/src/apps/demo/demo_window.rs rename to egui_demo_lib/src/apps/demo/misc_demo_window.rs index 60c177c1..8a7d2a8b 100644 --- a/egui_demo_lib/src/apps/demo/demo_window.rs +++ b/egui_demo_lib/src/apps/demo/misc_demo_window.rs @@ -4,7 +4,7 @@ use egui::{color::*, *}; /// Showcase some ui code #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] -pub struct DemoWindow { +pub struct MiscDemoWindow { num_columns: usize, widgets: Widgets, @@ -13,9 +13,9 @@ pub struct DemoWindow { box_painting: BoxPainting, } -impl Default for DemoWindow { - fn default() -> DemoWindow { - DemoWindow { +impl Default for MiscDemoWindow { + fn default() -> MiscDemoWindow { + MiscDemoWindow { num_columns: 2, widgets: Default::default(), @@ -26,7 +26,7 @@ impl Default for DemoWindow { } } -impl Demo for DemoWindow { +impl Demo for MiscDemoWindow { fn name(&self) -> &'static str { "✨ Misc Demos" } @@ -39,7 +39,7 @@ impl Demo for DemoWindow { } } -impl View for DemoWindow { +impl View for MiscDemoWindow { fn ui(&mut self, ui: &mut Ui) { CollapsingHeader::new("Widgets") .default_open(true) @@ -105,6 +105,116 @@ impl View for DemoWindow { // ---------------------------------------------------------------------------- +#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "persistence", serde(default))] +pub struct Widgets { + angle: f32, + password: String, + lock_focus: bool, + code_snippet: String, +} + +impl Default for Widgets { + fn default() -> Self { + Self { + angle: std::f32::consts::TAU / 3.0, + password: "hunter2".to_owned(), + lock_focus: true, + code_snippet: "\ +fn main() { +\tprintln!(\"Hello world!\"); +} +" + .to_owned(), + } + } +} + +impl Widgets { + pub fn ui(&mut self, ui: &mut Ui) { + let Self { + angle, + password, + lock_focus, + code_snippet, + } = self; + ui.vertical_centered(|ui| { + ui.add(crate::__egui_github_link_file_line!()); + }); + + ui.horizontal_wrapped(|ui| { + // Trick so we don't have to add spaces in the text below: + ui.spacing_mut().item_spacing.x = ui.fonts()[TextStyle::Body].glyph_width(' '); + + ui.add(Label::new("Text can have").text_color(Color32::from_rgb(110, 255, 110))); + ui.colored_label(Color32::from_rgb(128, 140, 255), "color"); // Shortcut version + ui.label("and tooltips.").on_hover_text( + "This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\nThis is the second line.\nThis is the third.", + ); + + ui.label("You can mix in other widgets into text, like"); + let _ = ui.small_button("this button"); + ui.label("."); + + ui.label("The default font supports all latin and cyrillic characters (ИÅđ…), common math symbols (∫√∞²⅓…), and many emojis (💓🌟🖩…).") + .on_hover_text("There is currently no support for right-to-left languages."); + ui.label("See the 🔤 Font Book for more!"); + + ui.monospace("There is also a monospace font."); + }); + + let tooltip_ui = |ui: &mut Ui| { + ui.heading("The name of the tooltip"); + ui.horizontal(|ui| { + ui.label("This tooltip was created with"); + ui.monospace(".on_hover_ui(...)"); + }); + let _ = ui.button("A button you can never press"); + }; + ui.label("Tooltips can be more than just simple text.") + .on_hover_ui(tooltip_ui); + + ui.separator(); + + ui.horizontal(|ui| { + ui.label("An angle:"); + ui.drag_angle(angle); + ui.label(format!("≈ {:.3}τ", *angle / std::f32::consts::TAU)) + .on_hover_text("Each τ represents one turn (τ = 2π)"); + }) + .response + .on_hover_text("The angle is stored in radians, but presented in degrees"); + + ui.separator(); + + ui.horizontal(|ui| { + ui.hyperlink_to("Password:", super::password::url_to_file_source_code()) + .on_hover_text("See the example code for how to use egui to store UI state"); + ui.add(super::password::password(password)); + }); + + ui.separator(); + + ui.horizontal(|ui| { + ui.label("Code editor:"); + + ui.separator(); + + ui.checkbox(lock_focus, "Lock focus").on_hover_text( + "When checked, pressing TAB will insert a tab instead of moving focus", + ); + }); + + ui.add( + TextEdit::multiline(code_snippet) + .code_editor() + .lock_focus(*lock_focus), + ); + } +} + +// ---------------------------------------------------------------------------- + #[derive(PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] diff --git a/egui_demo_lib/src/apps/demo/mod.rs b/egui_demo_lib/src/apps/demo/mod.rs index 82614837..14acb7c6 100644 --- a/egui_demo_lib/src/apps/demo/mod.rs +++ b/egui_demo_lib/src/apps/demo/mod.rs @@ -6,13 +6,13 @@ mod app; pub mod dancing_strings; -pub mod demo_window; -mod demo_windows; +pub mod demo_app_windows; pub mod drag_and_drop; pub mod font_book; pub mod font_contents_emoji; pub mod font_contents_ubuntu; pub mod layout_test; +pub mod misc_demo_window; pub mod multi_touch; pub mod painting; pub mod password; @@ -22,10 +22,12 @@ pub mod sliders; pub mod tests; pub mod toggle_switch; pub mod widget_gallery; -mod widgets; pub mod window_options; -pub use {app::*, demo_window::DemoWindow, demo_windows::*, widgets::Widgets}; +pub use { + app::DemoApp, demo_app_windows::DemoWindows, misc_demo_window::MiscDemoWindow, + widget_gallery::WidgetGallery, +}; // ---------------------------------------------------------------------------- diff --git a/egui_demo_lib/src/apps/demo/password.rs b/egui_demo_lib/src/apps/demo/password.rs index d12ee821..8ed568c6 100644 --- a/egui_demo_lib/src/apps/demo/password.rs +++ b/egui_demo_lib/src/apps/demo/password.rs @@ -1,114 +1,72 @@ -//! Source code example about creating other type of your widget which uses `egui::Memory` and -//! created using a combination of existing widgets. +//! Source code example about creating a widget which uses `egui::Memory` to store UI state. +//! //! This is meant to be read as a tutorial, hence the plethora of comments. -use egui::Layout; -use std::fmt::Debug; -use std::hash::Hash; - /// Password entry field with ability to toggle character hiding. /// /// ## Example: /// ``` ignore -/// password_ui(ui, &mut password, "password_1"); +/// password_ui(ui, &mut password); /// ``` -pub fn password_ui( - ui: &mut egui::Ui, - text: &mut String, - id_source: impl Hash + Debug, -) -> egui::Response { - // This widget has its own state — enabled or disabled, - // so there is the algorithm for this type of widgets: - // 1. Declare state struct - // 2. Create id - // 3. Get state for this widget - // 4. Process ui, change a local copy of the state - // 5. Insert changed state back +pub fn password_ui(ui: &mut egui::Ui, text: &mut String) -> egui::Response { + // This widget has its own state — show or hide password characters. // 1. Declare state struct // This struct represents the state of this widget. - // It must implement at least `Clone` and be `'static`. If you use the `persistence` feature, - // it also must implement `serde::{Deserialize, Serialize}`. - // You should prefer creating custom newtype structs or enums like this, to avoid TypeId + // It must implement at least `Clone` and be `'static`. + // If you use the `persistence` feature, it also must implement `serde::{Deserialize, Serialize}`. + // You should prefer creating custom newtype structs or enums like this, to avoid `TypeId` // intersection errors, especially when you use `Memory::data` without `Id`. #[derive(Clone, Copy, Default)] - #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] struct State(bool); // 2. Create id - let id = ui.make_persistent_id(id_source); + let id = ui.id().with("show_password"); // 3. Get state for this widget // You can read more about available `Memory` functions in the documentation of `egui::Memory` // struct and `egui::any` module. // You should get state by value, not by reference to avoid borrowing of `Memory`. - let mut state = *ui.memory().id_data.get_or_default::(id); + let mut plaintext = *ui.memory().id_data_temp.get_or_default::(id); // 4. Process ui, change a local copy of the state // We want TextEdit to fill entire space, and have button after that, so in that case we can // change direction to right_to_left. - let result = ui.with_layout(Layout::right_to_left(), |ui| { + let result = ui.with_layout(egui::Layout::right_to_left(), |ui| { // Here a local copy of the state can be changed by a user. let response = ui - .add(egui::SelectableLabel::new(state.0, "👁")) - .on_hover_text("Toggle symbols hiding"); + .add(egui::SelectableLabel::new(plaintext.0, "👁")) + .on_hover_text("Show/hide password"); if response.clicked() { - state.0 = !state.0; + plaintext.0 = !plaintext.0; } + let text_edit_size = ui.available_size(); + // Here we use this local state. - ui.add(egui::TextEdit::singleline(text).password(!state.0)); + ui.add_sized( + text_edit_size, + egui::TextEdit::singleline(text).password(!plaintext.0), + ); }); // 5. Insert changed state back - ui.memory().id_data.insert(id, state); + ui.memory().id_data_temp.insert(id, plaintext); // All done! Return the interaction response so the user can check what happened // (hovered, clicked, ...) and maybe show a tooltip: result.response } -/// Here is the same code again, but a bit more compact: -#[allow(dead_code)] -fn password_ui_compact( - ui: &mut egui::Ui, - text: &mut String, - id_source: impl Hash + Debug, -) -> egui::Response { - #[derive(Clone, Copy, Default)] - #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] - struct State(bool); - - let id = ui.make_persistent_id(id_source); - let mut state = *ui.memory().id_data.get_or_default::(id); - - let result = ui.with_layout(Layout::right_to_left(), |ui| { - let response = ui - .add(egui::SelectableLabel::new(state.0, "👁")) - .on_hover_text("Toggle symbols hiding"); - if response.clicked() { - state.0 = !state.0; - } - - ui.add(egui::TextEdit::singleline(text).password(!state.0)); - }); - - ui.memory().id_data.insert(id, state); - result.response -} - // A wrapper that allows the more idiomatic usage pattern: `ui.add(...)` /// Password entry field with ability to toggle character hiding. /// /// ## Example: /// ``` ignore -/// ui.add(password(&mut password, "password_1")); +/// ui.add(password(&mut password)); /// ``` -pub fn password<'a>( - text: &'a mut String, - id_source: impl Hash + Debug + 'a, -) -> impl egui::Widget + 'a { - move |ui: &mut egui::Ui| password_ui(ui, text, id_source) +pub fn password(text: &mut String) -> impl egui::Widget + '_ { + move |ui: &mut egui::Ui| password_ui(ui, text) } pub fn url_to_file_source_code() -> String { diff --git a/egui_demo_lib/src/apps/demo/widget_gallery.rs b/egui_demo_lib/src/apps/demo/widget_gallery.rs index 0561657d..d8ff190a 100644 --- a/egui_demo_lib/src/apps/demo/widget_gallery.rs +++ b/egui_demo_lib/src/apps/demo/widget_gallery.rs @@ -14,7 +14,6 @@ pub struct WidgetGallery { scalar: f32, string: String, color: egui::Color32, - memory_example: String, } impl Default for WidgetGallery { @@ -26,7 +25,6 @@ impl Default for WidgetGallery { scalar: 42.0, string: Default::default(), color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5), - memory_example: "qwerty_is_bad_password".to_owned(), } } } @@ -88,7 +86,6 @@ impl WidgetGallery { scalar, string, color, - memory_example, } = self; ui.set_enabled(*enabled); @@ -204,19 +201,6 @@ impl WidgetGallery { This toggle switch is just 15 lines of code.", ); ui.end_row(); - - ui.hyperlink_to( - "egui::Memory usage:", - super::password::url_to_file_source_code(), - ) - .on_hover_text( - "You can use `egui::Memory` to store your own data.\n\ - And state of this widget can be saved and loaded \n\ - between runs automatically under the `persistence`\n\ - feature.", - ); - ui.add(super::password::password(memory_example, "memory example")); - ui.end_row(); } } diff --git a/egui_demo_lib/src/apps/demo/widgets.rs b/egui_demo_lib/src/apps/demo/widgets.rs deleted file mode 100644 index 723ae7cd..00000000 --- a/egui_demo_lib/src/apps/demo/widgets.rs +++ /dev/null @@ -1,191 +0,0 @@ -use egui::{color::*, *}; - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -enum Enum { - First, - Second, - Third, -} - -impl Default for Enum { - fn default() -> Self { - Enum::First - } -} - -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "persistence", serde(default))] -pub struct Widgets { - group_enabled: bool, - count: usize, - radio: Enum, - angle: f32, - color: Color32, - single_line_text_input: String, - multiline_text_input: String, - lock_focus: bool, - code_snippet: String, -} - -impl Default for Widgets { - fn default() -> Self { - Self { - group_enabled: true, - radio: Enum::First, - count: 0, - angle: std::f32::consts::TAU / 3.0, - color: (Rgba::from_rgb(0.0, 1.0, 0.5) * 0.75).into(), - single_line_text_input: "Hello World!".to_owned(), - lock_focus: true, - - multiline_text_input: "Text can both be so wide that it needs a line break, but you can also add manual line break by pressing enter, creating new paragraphs.\nThis is the start of the next paragraph.\n\nClick me to edit me!".to_owned(), - code_snippet: "\ -fn main() { -\tprintln!(\"Hello world!\"); -} -".to_owned(), - } - } -} - -impl Widgets { - pub fn ui(&mut self, ui: &mut Ui) { - ui.vertical_centered(|ui| { - ui.add(crate::__egui_github_link_file_line!()); - }); - - egui::ComboBox::from_label("Version") - .width(150.0) - .selected_text("foo") - .show_ui(ui, |ui| { - egui::CollapsingHeader::new("Dev") - .default_open(true) - .show(ui, |ui| { - ui.label("contents"); - }); - }); - - ui.horizontal_wrapped(|ui| { - // Trick so we don't have to add spaces in the text below: - ui.spacing_mut().item_spacing.x = ui.fonts()[TextStyle::Body].glyph_width(' '); - - ui.add(Label::new("Text can have").text_color(Color32::from_rgb(110, 255, 110))); - ui.colored_label(Color32::from_rgb(128, 140, 255), "color"); // Shortcut version - ui.label("and tooltips.").on_hover_text( - "This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\nThis is the second line.\nThis is the third.", - ); - - ui.label("You can mix in other widgets into text, like"); - let _ = ui.small_button("this button"); - ui.label("."); - - ui.label("The default font supports all latin and cyrillic characters (ИÅđ…), common math symbols (∫√∞²⅓…), and many emojis (💓🌟🖩…).") - .on_hover_text("There is currently no support for right-to-left languages."); - ui.label("See the 🔤 Font Book for more!"); - - ui.monospace("There is also a monospace font."); - }); - - let tooltip_ui = |ui: &mut Ui| { - ui.heading("The name of the tooltip"); - ui.horizontal(|ui| { - ui.label("This tooltip was created with"); - ui.monospace(".on_hover_ui(...)"); - }); - let _ = ui.button("A button you can never press"); - }; - ui.label("Tooltips can be more than just simple text.") - .on_hover_ui(tooltip_ui); - - ui.group(|ui| { - ui.checkbox(&mut self.group_enabled, "Group enabled"); - ui.set_enabled(self.group_enabled); - ui.horizontal(|ui| { - ui.radio_value(&mut self.radio, Enum::First, "First"); - ui.radio_value(&mut self.radio, Enum::Second, "Second"); - ui.radio_value(&mut self.radio, Enum::Third, "Third"); - }); - - egui::ComboBox::from_label("Combo Box") - .selected_text(format!("{:?}", self.radio)) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.radio, Enum::First, "First"); - ui.selectable_value(&mut self.radio, Enum::Second, "Second"); - ui.selectable_value(&mut self.radio, Enum::Third, "Third"); - }); - }); - - ui.horizontal(|ui| { - if ui - .button("Click me") - .on_hover_text("This will just increase a counter.") - .clicked() - { - self.count += 1; - } - ui.label(format!("The button has been clicked {} times.", self.count)); - }); - - ui.separator(); - - ui.horizontal(|ui| { - ui.label("An angle:"); - ui.drag_angle(&mut self.angle); - ui.label(format!("≈ {:.3}τ", self.angle / std::f32::consts::TAU)) - .on_hover_text("Each τ represents one turn (τ = 2π)"); - }) - .response - .on_hover_text("The angle is stored in radians, but presented in degrees"); - - ui.separator(); - - ui.horizontal(|ui| { - ui.colored_label(self.color, "Click to select a different text color: "); - ui.color_edit_button_srgba(&mut self.color); - }); - - ui.separator(); - - ui.horizontal(|ui| { - ui.label("Password:"); - // We let `egui` store the show/hide password toggle: - let show_password_id = Id::new("show_password"); - let mut show_password: bool = *ui.memory().id_data.get_or_default(show_password_id); - let response = ui.add_sized( - [140.0, 20.0], - egui::TextEdit::singleline(&mut self.single_line_text_input) - .password(!show_password), - ); - if response.lost_focus() && ui.input().key_pressed(egui::Key::Enter) { - // … - } - ui.checkbox(&mut show_password, "Show password"); - ui.memory().id_data.insert(show_password_id, show_password); - }); - - ui.separator(); - - ui.label("Multiline text input:"); - ui.text_edit_multiline(&mut self.multiline_text_input); - - ui.separator(); - - ui.horizontal(|ui| { - ui.label("Code editor:"); - - ui.separator(); - - ui.checkbox(&mut self.lock_focus, "Lock focus") - .on_hover_text( - "When checked, pressing TAB will insert a tab instead of moving focus", - ); - }); - - ui.add( - TextEdit::multiline(&mut self.code_snippet) - .code_editor() - .lock_focus(self.lock_focus), - ); - } -}