mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 13:50:04 -04:00
The allows us to pass any state to the ui closure. While it is possible to just store state in the closure itself, accessing that state after the harness was created to e.g. read or modify it would require interior mutability. With this change there are new `Harness::new_state`, `Harness::run_state`, ... methods that allow passing state on each run. This builds on top of #5301, which should be merged first --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
153 lines
5.0 KiB
Rust
153 lines
5.0 KiB
Rust
/// Showcase [`egui::TextEdit`].
|
|
#[derive(PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
|
#[cfg_attr(feature = "serde", serde(default))]
|
|
pub struct TextEditDemo {
|
|
pub text: String,
|
|
}
|
|
|
|
impl Default for TextEditDemo {
|
|
fn default() -> Self {
|
|
Self {
|
|
text: "Edit this text".to_owned(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl crate::Demo for TextEditDemo {
|
|
fn name(&self) -> &'static str {
|
|
"🖹 TextEdit"
|
|
}
|
|
|
|
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
|
egui::Window::new(self.name())
|
|
.open(open)
|
|
.resizable(false)
|
|
.show(ctx, |ui| {
|
|
use crate::View as _;
|
|
self.ui(ui);
|
|
});
|
|
}
|
|
}
|
|
|
|
impl crate::View for TextEditDemo {
|
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
|
ui.vertical_centered(|ui| {
|
|
ui.add(crate::egui_github_link_file!());
|
|
});
|
|
|
|
let Self { text } = self;
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.spacing_mut().item_spacing.x = 0.0;
|
|
ui.label("Advanced usage of ");
|
|
ui.code("TextEdit");
|
|
ui.label(".");
|
|
});
|
|
|
|
let output = egui::TextEdit::multiline(text)
|
|
.hint_text("Type something!")
|
|
.show(ui);
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.spacing_mut().item_spacing.x = 0.0;
|
|
ui.label("Selected text: ");
|
|
if let Some(text_cursor_range) = output.cursor_range {
|
|
let selected_text = text_cursor_range.slice_str(text);
|
|
ui.code(selected_text);
|
|
}
|
|
});
|
|
|
|
let anything_selected = output
|
|
.cursor_range
|
|
.map_or(false, |cursor| !cursor.is_empty());
|
|
|
|
ui.add_enabled(
|
|
anything_selected,
|
|
egui::Label::new("Press ctrl+Y to toggle the case of selected text (cmd+Y on Mac)"),
|
|
);
|
|
|
|
if ui.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::Y)) {
|
|
if let Some(text_cursor_range) = output.cursor_range {
|
|
use egui::TextBuffer as _;
|
|
let selected_chars = text_cursor_range.as_sorted_char_range();
|
|
let selected_text = text.char_range(selected_chars.clone());
|
|
let upper_case = selected_text.to_uppercase();
|
|
let new_text = if selected_text == upper_case {
|
|
selected_text.to_lowercase()
|
|
} else {
|
|
upper_case
|
|
};
|
|
text.delete_char_range(selected_chars.clone());
|
|
text.insert_text(&new_text, selected_chars.start);
|
|
}
|
|
}
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.label("Move cursor to the:");
|
|
|
|
if ui.button("start").clicked() {
|
|
let text_edit_id = output.response.id;
|
|
if let Some(mut state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) {
|
|
let ccursor = egui::text::CCursor::new(0);
|
|
state
|
|
.cursor
|
|
.set_char_range(Some(egui::text::CCursorRange::one(ccursor)));
|
|
state.store(ui.ctx(), text_edit_id);
|
|
ui.ctx().memory_mut(|mem| mem.request_focus(text_edit_id)); // give focus back to the [`TextEdit`].
|
|
}
|
|
}
|
|
|
|
if ui.button("end").clicked() {
|
|
let text_edit_id = output.response.id;
|
|
if let Some(mut state) = egui::TextEdit::load_state(ui.ctx(), text_edit_id) {
|
|
let ccursor = egui::text::CCursor::new(text.chars().count());
|
|
state
|
|
.cursor
|
|
.set_char_range(Some(egui::text::CCursorRange::one(ccursor)));
|
|
state.store(ui.ctx(), text_edit_id);
|
|
ui.ctx().memory_mut(|mem| mem.request_focus(text_edit_id)); // give focus back to the [`TextEdit`].
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use egui::{accesskit, CentralPanel};
|
|
use egui_kittest::kittest::{Key, Queryable};
|
|
use egui_kittest::Harness;
|
|
|
|
#[test]
|
|
pub fn should_type() {
|
|
let text = "Hello, world!".to_owned();
|
|
let mut harness = Harness::new_state(
|
|
move |ctx, text| {
|
|
CentralPanel::default().show(ctx, |ui| {
|
|
ui.text_edit_singleline(text);
|
|
});
|
|
},
|
|
text,
|
|
);
|
|
|
|
harness.run();
|
|
|
|
let text_edit = harness.get_by_role(accesskit::Role::TextInput);
|
|
assert_eq!(text_edit.value().as_deref(), Some("Hello, world!"));
|
|
|
|
text_edit.key_combination(&[Key::Command, Key::A]);
|
|
text_edit.type_text("Hi ");
|
|
|
|
harness.run();
|
|
harness
|
|
.get_by_role(accesskit::Role::TextInput)
|
|
.type_text("there!");
|
|
|
|
harness.run();
|
|
let text_edit = harness.get_by_role(accesskit::Role::TextInput);
|
|
assert_eq!(text_edit.value().as_deref(), Some("Hi there!"));
|
|
assert_eq!(harness.state(), "Hi there!");
|
|
}
|
|
}
|