206 lines
7.7 KiB
Rust
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()));
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|