Clean up demos
This commit is contained in:
parent
aa3c40c49f
commit
9dc092b778
7 changed files with 291 additions and 349 deletions
|
@ -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 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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "persistence", serde(default))]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
pub options: Options,
|
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")]
|
#[cfg(feature = "persistence")]
|
||||||
pub data: any::serializable::TypeMap,
|
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"))]
|
#[cfg(not(feature = "persistence"))]
|
||||||
pub data: any::TypeMap,
|
pub data: any::TypeMap,
|
||||||
|
|
||||||
|
@ -30,11 +33,13 @@ pub struct Memory {
|
||||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
pub data_temp: any::TypeMap,
|
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")]
|
#[cfg(feature = "persistence")]
|
||||||
pub id_data: any::serializable::AnyMap<Id>,
|
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"))]
|
#[cfg(not(feature = "persistence"))]
|
||||||
pub id_data: any::AnyMap<Id>,
|
pub id_data: any::AnyMap<Id>,
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use super::Demo;
|
||||||
use egui::{CtxRef, ScrollArea, Ui, Window};
|
use egui::{CtxRef, ScrollArea, Ui, Window};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
@ -7,17 +8,18 @@ use std::collections::BTreeSet;
|
||||||
#[cfg_attr(feature = "persistence", serde(default))]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
struct Demos {
|
struct Demos {
|
||||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
demos: Vec<Box<dyn super::Demo>>,
|
demos: Vec<Box<dyn Demo>>,
|
||||||
|
|
||||||
open: BTreeSet<String>,
|
open: BTreeSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Demos {
|
impl Default for Demos {
|
||||||
fn default() -> Self {
|
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::dancing_strings::DancingStrings::default()),
|
||||||
Box::new(super::drag_and_drop::DragAndDropDemo::default()),
|
Box::new(super::drag_and_drop::DragAndDropDemo::default()),
|
||||||
Box::new(super::font_book::FontBook::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::multi_touch::MultiTouch::default()),
|
||||||
Box::new(super::painting::Painting::default()),
|
Box::new(super::painting::Painting::default()),
|
||||||
Box::new(super::plot_demo::PlotDemo::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::widget_gallery::WidgetGallery::default()),
|
||||||
Box::new(super::window_options::WindowOptions::default()),
|
Box::new(super::window_options::WindowOptions::default()),
|
||||||
Box::new(super::tests::WindowResizeTest::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();
|
let mut open = BTreeSet::new();
|
||||||
open.insert(
|
open.insert(
|
||||||
super::widget_gallery::WidgetGallery::default()
|
super::widget_gallery::WidgetGallery::default()
|
||||||
|
@ -45,8 +43,7 @@ impl Default for Demos {
|
||||||
|
|
||||||
Self { demos, open }
|
Self { demos, open }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl Demos {
|
|
||||||
pub fn checkboxes(&mut self, ui: &mut Ui) {
|
pub fn checkboxes(&mut self, ui: &mut Ui) {
|
||||||
let Self { demos, open } = self;
|
let Self { demos, open } = self;
|
||||||
for demo in demos {
|
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;
|
let Self { demos, open } = self;
|
||||||
for demo in demos {
|
for demo in demos {
|
||||||
let mut is_open = open.contains(demo.name());
|
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) {
|
fn set_open(open: &mut BTreeSet<String>, key: &'static str, is_open: bool) {
|
||||||
if is_open {
|
if is_open {
|
||||||
if !open.contains(key) {
|
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", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "persistence", serde(default))]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct DemoWindows {
|
pub struct DemoWindows {
|
||||||
open_windows: OpenWindows,
|
|
||||||
demos: Demos,
|
demos: Demos,
|
||||||
|
tests: Tests,
|
||||||
|
egui_windows: EguiWindows,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DemoWindows {
|
impl DemoWindows {
|
||||||
/// Show the app ui (menu bar and windows).
|
/// Show the app ui (menu bar and windows).
|
||||||
/// `sidebar_ui` can be used to optionally show some things in the sidebar
|
/// `sidebar_ui` can be used to optionally show some things in the sidebar
|
||||||
pub fn ui(&mut self, ctx: &CtxRef) {
|
pub fn ui(&mut self, ctx: &CtxRef) {
|
||||||
|
let Self {
|
||||||
|
demos,
|
||||||
|
tests,
|
||||||
|
egui_windows,
|
||||||
|
} = self;
|
||||||
|
|
||||||
egui::SidePanel::left("side_panel", 190.0).show(ctx, |ui| {
|
egui::SidePanel::left("side_panel", 190.0).show(ctx, |ui| {
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("✒ egui demos");
|
ui.heading("✒ egui demos");
|
||||||
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
@ -100,33 +163,34 @@ impl DemoWindows {
|
||||||
use egui::special_emojis::{GITHUB, OS_APPLE, OS_LINUX, OS_WINDOWS};
|
use egui::special_emojis::{GITHUB, OS_APPLE, OS_LINUX, OS_WINDOWS};
|
||||||
|
|
||||||
ui.label("egui is an immediate mode GUI library written in Rust.");
|
ui.label("egui is an immediate mode GUI library written in Rust.");
|
||||||
|
|
||||||
|
ui.label(format!(
|
||||||
|
"egui runs on the web, or natively on {}{}{}",
|
||||||
|
OS_APPLE, OS_LINUX, OS_WINDOWS,
|
||||||
|
));
|
||||||
|
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
ui.hyperlink_to(
|
ui.hyperlink_to(
|
||||||
format!("{} egui home page", GITHUB),
|
format!("{} egui home page", GITHUB),
|
||||||
"https://github.com/emilk/egui",
|
"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.separator();
|
||||||
|
demos.checkboxes(ui);
|
||||||
ui.heading("Windows:");
|
ui.separator();
|
||||||
self.demos.checkboxes(ui);
|
tests.checkboxes(ui);
|
||||||
|
ui.separator();
|
||||||
ui.separator();
|
egui_windows.checkboxes(ui);
|
||||||
|
|
||||||
ui.label("egui:");
|
|
||||||
self.open_windows.checkboxes(ui);
|
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
if ui.button("Organize windows").clicked() {
|
if ui.button("Organize windows").clicked() {
|
||||||
ui.ctx().memory().reset_areas();
|
ui.ctx().memory().reset_areas();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
egui::TopPanel::top("menu_bar").show(ctx, |ui| {
|
egui::TopPanel::top("menu_bar").show(ctx, |ui| {
|
||||||
show_menu_bar(ui);
|
show_menu_bar(ui);
|
||||||
|
@ -150,53 +214,34 @@ impl DemoWindows {
|
||||||
/// Show the open windows.
|
/// Show the open windows.
|
||||||
fn windows(&mut self, ctx: &CtxRef) {
|
fn windows(&mut self, ctx: &CtxRef) {
|
||||||
let Self {
|
let Self {
|
||||||
open_windows,
|
|
||||||
demos,
|
demos,
|
||||||
..
|
tests,
|
||||||
|
egui_windows,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
Window::new("🔧 Settings")
|
demos.windows(ctx);
|
||||||
.open(&mut open_windows.settings)
|
tests.windows(ctx);
|
||||||
.scroll(true)
|
egui_windows.windows(ctx);
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
struct OpenWindows {
|
struct EguiWindows {
|
||||||
// egui stuff:
|
// egui stuff:
|
||||||
settings: bool,
|
settings: bool,
|
||||||
inspection: bool,
|
inspection: bool,
|
||||||
memory: bool,
|
memory: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for OpenWindows {
|
impl Default for EguiWindows {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
OpenWindows::none()
|
EguiWindows::none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpenWindows {
|
impl EguiWindows {
|
||||||
fn none() -> Self {
|
fn none() -> Self {
|
||||||
Self {
|
Self {
|
||||||
settings: false,
|
settings: false,
|
||||||
|
@ -216,6 +261,35 @@ impl OpenWindows {
|
||||||
ui.checkbox(inspection, "🔍 Inspection");
|
ui.checkbox(inspection, "🔍 Inspection");
|
||||||
ui.checkbox(memory, "📝 Memory");
|
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) {
|
fn show_menu_bar(ui: &mut Ui) {
|
|
@ -4,7 +4,7 @@ use egui::{color::*, *};
|
||||||
/// Showcase some ui code
|
/// Showcase some ui code
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "persistence", serde(default))]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
||||||
pub struct DemoWindow {
|
pub struct MiscDemoWindow {
|
||||||
num_columns: usize,
|
num_columns: usize,
|
||||||
|
|
||||||
widgets: Widgets,
|
widgets: Widgets,
|
||||||
|
@ -13,9 +13,9 @@ pub struct DemoWindow {
|
||||||
box_painting: BoxPainting,
|
box_painting: BoxPainting,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DemoWindow {
|
impl Default for MiscDemoWindow {
|
||||||
fn default() -> DemoWindow {
|
fn default() -> MiscDemoWindow {
|
||||||
DemoWindow {
|
MiscDemoWindow {
|
||||||
num_columns: 2,
|
num_columns: 2,
|
||||||
|
|
||||||
widgets: Default::default(),
|
widgets: Default::default(),
|
||||||
|
@ -26,7 +26,7 @@ impl Default for DemoWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Demo for DemoWindow {
|
impl Demo for MiscDemoWindow {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"✨ Misc Demos"
|
"✨ 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) {
|
fn ui(&mut self, ui: &mut Ui) {
|
||||||
CollapsingHeader::new("Widgets")
|
CollapsingHeader::new("Widgets")
|
||||||
.default_open(true)
|
.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)]
|
#[derive(PartialEq)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "persistence", serde(default))]
|
#[cfg_attr(feature = "persistence", serde(default))]
|
|
@ -6,13 +6,13 @@
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
pub mod dancing_strings;
|
pub mod dancing_strings;
|
||||||
pub mod demo_window;
|
pub mod demo_app_windows;
|
||||||
mod demo_windows;
|
|
||||||
pub mod drag_and_drop;
|
pub mod drag_and_drop;
|
||||||
pub mod font_book;
|
pub mod font_book;
|
||||||
pub mod font_contents_emoji;
|
pub mod font_contents_emoji;
|
||||||
pub mod font_contents_ubuntu;
|
pub mod font_contents_ubuntu;
|
||||||
pub mod layout_test;
|
pub mod layout_test;
|
||||||
|
pub mod misc_demo_window;
|
||||||
pub mod multi_touch;
|
pub mod multi_touch;
|
||||||
pub mod painting;
|
pub mod painting;
|
||||||
pub mod password;
|
pub mod password;
|
||||||
|
@ -22,10 +22,12 @@ pub mod sliders;
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
pub mod toggle_switch;
|
pub mod toggle_switch;
|
||||||
pub mod widget_gallery;
|
pub mod widget_gallery;
|
||||||
mod widgets;
|
|
||||||
pub mod window_options;
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,114 +1,72 @@
|
||||||
//! Source code example about creating other type of your widget which uses `egui::Memory` and
|
//! Source code example about creating a widget which uses `egui::Memory` to store UI state.
|
||||||
//! created using a combination of existing widgets.
|
//!
|
||||||
//! This is meant to be read as a tutorial, hence the plethora of comments.
|
//! 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.
|
/// Password entry field with ability to toggle character hiding.
|
||||||
///
|
///
|
||||||
/// ## Example:
|
/// ## Example:
|
||||||
/// ``` ignore
|
/// ``` ignore
|
||||||
/// password_ui(ui, &mut password, "password_1");
|
/// password_ui(ui, &mut password);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn password_ui(
|
pub fn password_ui(ui: &mut egui::Ui, text: &mut String) -> egui::Response {
|
||||||
ui: &mut egui::Ui,
|
// This widget has its own state — show or hide password characters.
|
||||||
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
|
|
||||||
|
|
||||||
// 1. Declare state struct
|
// 1. Declare state struct
|
||||||
// This struct represents the state of this widget.
|
// This struct represents the state of this widget.
|
||||||
// It must implement at least `Clone` and be `'static`. If you use the `persistence` feature,
|
// It must implement at least `Clone` and be `'static`.
|
||||||
// it also must implement `serde::{Deserialize, Serialize}`.
|
// 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
|
// You should prefer creating custom newtype structs or enums like this, to avoid `TypeId`
|
||||||
// intersection errors, especially when you use `Memory::data` without `Id`.
|
// intersection errors, especially when you use `Memory::data` without `Id`.
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
struct State(bool);
|
struct State(bool);
|
||||||
|
|
||||||
// 2. Create id
|
// 2. Create id
|
||||||
let id = ui.make_persistent_id(id_source);
|
let id = ui.id().with("show_password");
|
||||||
|
|
||||||
// 3. Get state for this widget
|
// 3. Get state for this widget
|
||||||
// You can read more about available `Memory` functions in the documentation of `egui::Memory`
|
// You can read more about available `Memory` functions in the documentation of `egui::Memory`
|
||||||
// struct and `egui::any` module.
|
// struct and `egui::any` module.
|
||||||
// You should get state by value, not by reference to avoid borrowing of `Memory`.
|
// 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
|
// 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
|
// We want TextEdit to fill entire space, and have button after that, so in that case we can
|
||||||
// change direction to right_to_left.
|
// 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.
|
// Here a local copy of the state can be changed by a user.
|
||||||
let response = ui
|
let response = ui
|
||||||
.add(egui::SelectableLabel::new(state.0, "👁"))
|
.add(egui::SelectableLabel::new(plaintext.0, "👁"))
|
||||||
.on_hover_text("Toggle symbols hiding");
|
.on_hover_text("Show/hide password");
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
state.0 = !state.0;
|
plaintext.0 = !plaintext.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let text_edit_size = ui.available_size();
|
||||||
|
|
||||||
// Here we use this local state.
|
// 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
|
// 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
|
// All done! Return the interaction response so the user can check what happened
|
||||||
// (hovered, clicked, ...) and maybe show a tooltip:
|
// (hovered, clicked, ...) and maybe show a tooltip:
|
||||||
result.response
|
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(...)`
|
// A wrapper that allows the more idiomatic usage pattern: `ui.add(...)`
|
||||||
/// Password entry field with ability to toggle character hiding.
|
/// Password entry field with ability to toggle character hiding.
|
||||||
///
|
///
|
||||||
/// ## Example:
|
/// ## Example:
|
||||||
/// ``` ignore
|
/// ``` ignore
|
||||||
/// ui.add(password(&mut password, "password_1"));
|
/// ui.add(password(&mut password));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn password<'a>(
|
pub fn password(text: &mut String) -> impl egui::Widget + '_ {
|
||||||
text: &'a mut String,
|
move |ui: &mut egui::Ui| password_ui(ui, text)
|
||||||
id_source: impl Hash + Debug + 'a,
|
|
||||||
) -> impl egui::Widget + 'a {
|
|
||||||
move |ui: &mut egui::Ui| password_ui(ui, text, id_source)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn url_to_file_source_code() -> String {
|
pub fn url_to_file_source_code() -> String {
|
||||||
|
|
|
@ -14,7 +14,6 @@ pub struct WidgetGallery {
|
||||||
scalar: f32,
|
scalar: f32,
|
||||||
string: String,
|
string: String,
|
||||||
color: egui::Color32,
|
color: egui::Color32,
|
||||||
memory_example: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WidgetGallery {
|
impl Default for WidgetGallery {
|
||||||
|
@ -26,7 +25,6 @@ impl Default for WidgetGallery {
|
||||||
scalar: 42.0,
|
scalar: 42.0,
|
||||||
string: Default::default(),
|
string: Default::default(),
|
||||||
color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),
|
color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),
|
||||||
memory_example: "qwerty_is_bad_password".to_owned(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +86,6 @@ impl WidgetGallery {
|
||||||
scalar,
|
scalar,
|
||||||
string,
|
string,
|
||||||
color,
|
color,
|
||||||
memory_example,
|
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.set_enabled(*enabled);
|
ui.set_enabled(*enabled);
|
||||||
|
@ -204,19 +201,6 @@ impl WidgetGallery {
|
||||||
This toggle switch is just 15 lines of code.",
|
This toggle switch is just 15 lines of code.",
|
||||||
);
|
);
|
||||||
ui.end_row();
|
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue