Clean up demos

This commit is contained in:
Emil Ernerfeldt 2021-05-09 10:53:35 +02:00
parent aa3c40c49f
commit 9dc092b778
7 changed files with 291 additions and 349 deletions

View file

@ -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<Id>,
/// 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<Id>,

View file

@ -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<Box<dyn super::Demo>>,
demos: Vec<Box<dyn Demo>>,
open: BTreeSet<String>,
}
impl Default for Demos {
fn default() -> Self {
let demos: Vec<Box<dyn super::Demo>> = 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<Box<dyn Demo>>) -> 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<Box<dyn Demo>>,
open: BTreeSet<String>,
}
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<Box<dyn Demo>>) -> 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<String>, key: &'static str, is_open: bool) {
if is_open {
if !open.contains(key) {
@ -83,16 +137,25 @@ fn set_open(open: &mut BTreeSet<String>, 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) {

View file

@ -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))]

View file

@ -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,
};
// ----------------------------------------------------------------------------

View file

@ -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::<State>(id);
let mut plaintext = *ui.memory().id_data_temp.get_or_default::<State>(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::<State>(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 {

View file

@ -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();
}
}

View file

@ -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),
);
}
}