egui/examples/text_input_test/src/main.rs

206 lines
7.7 KiB
Rust

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![allow(rustdoc::missing_crate_level_docs)]
use eframe::egui;
fn main() -> eframe::Result {
env_logger::init();
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
..Default::default()
};
eframe::run_native(
"Text Input Test - IBus/Wayland",
options,
Box::new(|_cc| Ok(Box::<TestApp>::default())),
)
}
struct TestApp {
// Single-line text inputs
single_line_1: String,
single_line_2: String,
// Multi-line text input
multi_line: String,
// Code editor content
code: String,
// Password field
password: String,
// Input event log
event_log: Vec<String>,
}
impl Default for TestApp {
fn default() -> Self {
Self {
single_line_1: String::new(),
single_line_2: "Pre-filled text".to_owned(),
multi_line: "Type multiple lines here.\nLine 2\nLine 3".to_owned(),
code: r#"fn main() {
println!("Hello, world!");
}
"#.to_owned(),
password: String::new(),
event_log: Vec::new(),
}
}
}
impl eframe::App for TestApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// Log input events
ctx.input(|i| {
for event in &i.events {
match event {
egui::Event::Text(text) => {
self.event_log.push(format!(">>> TEXT: {:?}", text));
}
egui::Event::Key { key, pressed, modifiers, .. } => {
self.event_log.push(format!("KEY: {:?} pressed={} (mods: {:?})", key, pressed, modifiers));
}
egui::Event::Ime(ime) => {
// Filter out the spammy Disabled events
match ime {
egui::ImeEvent::Disabled => {} // Skip logging these
_ => self.event_log.push(format!("IME: {:?}", ime)),
}
}
egui::Event::Paste(text) => {
self.event_log.push(format!("PASTE: {:?}", text));
}
_ => {}
}
}
});
// Keep event log manageable
if self.event_log.len() > 100 {
self.event_log.drain(0..50);
}
egui::SidePanel::right("event_log").show(ctx, |ui| {
ui.heading("Input Events");
if ui.button("Clear").clicked() {
self.event_log.clear();
}
ui.separator();
egui::ScrollArea::vertical().show(ui, |ui| {
for event in self.event_log.iter().rev() {
ui.label(event);
}
});
});
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Text Input Test - IBus/Wayland Bug");
ui.label("Test various text input widgets. If IBus under Wayland is broken, typing won't work.");
// Show environment info
ui.add_space(5.0);
ui.group(|ui| {
ui.label("Environment Detection:");
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "unknown".to_string());
let wayland_display = std::env::var("WAYLAND_DISPLAY").is_ok();
let gtk_im = std::env::var("GTK_IM_MODULE").unwrap_or_else(|_| "not set".to_string());
let qt_im = std::env::var("QT_IM_MODULE").unwrap_or_else(|_| "not set".to_string());
let xmodifiers = std::env::var("XMODIFIERS").unwrap_or_else(|_| "not set".to_string());
let ime_override = std::env::var("EGUI_IME_DISABLED").unwrap_or_else(|_| "not set".to_string());
ui.label(format!(" XDG_SESSION_TYPE: {session_type}"));
ui.label(format!(" WAYLAND_DISPLAY set: {wayland_display}"));
ui.label(format!(" GTK_IM_MODULE: {gtk_im}"));
ui.label(format!(" QT_IM_MODULE: {qt_im}"));
ui.label(format!(" XMODIFIERS: {xmodifiers}"));
ui.label(format!(" EGUI_IME_DISABLED: {ime_override}"));
let is_wayland = session_type == "wayland" || wayland_display;
let is_ibus = gtk_im == "ibus" || qt_im == "ibus" || xmodifiers.contains("ibus");
if is_wayland && is_ibus {
ui.colored_label(egui::Color32::GREEN,
"IBus on Wayland detected - IME workaround is active!");
}
});
ui.separator();
egui::ScrollArea::vertical().show(ui, |ui| {
ui.group(|ui| {
ui.heading("Single-line Text Edits");
ui.horizontal(|ui| {
ui.label("Empty field:");
ui.text_edit_singleline(&mut self.single_line_1);
});
ui.horizontal(|ui| {
ui.label("Pre-filled:");
ui.text_edit_singleline(&mut self.single_line_2);
});
});
ui.add_space(10.0);
ui.group(|ui| {
ui.heading("Password Field");
ui.horizontal(|ui| {
ui.label("Password:");
ui.add(egui::TextEdit::singleline(&mut self.password).password(true));
});
});
ui.add_space(10.0);
ui.group(|ui| {
ui.heading("Multi-line Text Edit");
ui.add(
egui::TextEdit::multiline(&mut self.multi_line)
.desired_width(f32::INFINITY)
.desired_rows(6)
);
});
ui.add_space(10.0);
ui.group(|ui| {
ui.heading("Code Editor (with syntax highlighting)");
let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style());
let mut layouter = |ui: &egui::Ui, string: &dyn egui::TextBuffer, wrap_width: f32| {
let mut layout_job = egui_extras::syntax_highlighting::highlight(
ui.ctx(),
ui.style(),
&theme,
string.as_str(),
"rs",
);
layout_job.wrap.max_width = wrap_width;
ui.fonts_mut(|f| f.layout_job(layout_job))
};
ui.add(
egui::TextEdit::multiline(&mut self.code)
.font(egui::TextStyle::Monospace)
.code_editor()
.desired_width(f32::INFINITY)
.desired_rows(10)
.layouter(&mut layouter)
);
});
ui.add_space(10.0);
ui.group(|ui| {
ui.heading("Current Values");
ui.label(format!("Single line 1: {:?}", self.single_line_1));
ui.label(format!("Single line 2: {:?}", self.single_line_2));
ui.label(format!("Password length: {}", self.password.len()));
ui.label(format!("Multi-line lines: {}", self.multi_line.lines().count()));
ui.label(format!("Code lines: {}", self.code.lines().count()));
});
});
});
}
}