mirror of https://github.com/encounter/objdiff.git
Add alt shortcut support to many elements
This commit is contained in:
parent
95528fa8d2
commit
0899e6779c
|
@ -25,6 +25,7 @@ use time::UtcOffset;
|
||||||
use crate::{
|
use crate::{
|
||||||
app_config::{deserialize_config, AppConfigVersion},
|
app_config::{deserialize_config, AppConfigVersion},
|
||||||
config::{load_project_config, ProjectObjectNode},
|
config::{load_project_config, ProjectObjectNode},
|
||||||
|
hotkeys,
|
||||||
jobs::{
|
jobs::{
|
||||||
objdiff::{start_build, ObjDiffConfig},
|
objdiff::{start_build, ObjDiffConfig},
|
||||||
Job, JobQueue, JobResult, JobStatus,
|
Job, JobQueue, JobResult, JobStatus,
|
||||||
|
@ -527,6 +528,10 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_update(&mut self, ctx: &egui::Context, action: Option<DiffViewAction>) {
|
fn post_update(&mut self, ctx: &egui::Context, action: Option<DiffViewAction>) {
|
||||||
|
if action.is_some() {
|
||||||
|
ctx.request_repaint();
|
||||||
|
}
|
||||||
|
|
||||||
self.appearance.post_update(ctx);
|
self.appearance.post_update(ctx);
|
||||||
|
|
||||||
let ViewState { jobs, diff_state, config_state, graphics_state, .. } = &mut self.view_state;
|
let ViewState { jobs, diff_state, config_state, graphics_state, .. } = &mut self.view_state;
|
||||||
|
@ -690,13 +695,13 @@ impl eframe::App for App {
|
||||||
*show_side_panel = !*show_side_panel;
|
*show_side_panel = !*show_side_panel;
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.menu_button("File", |ui| {
|
ui.menu_button(hotkeys::button_alt_text(ui, "_File"), |ui| {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if ui.button("Debug…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Debug…")).clicked() {
|
||||||
*show_debug = !*show_debug;
|
*show_debug = !*show_debug;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
if ui.button("Project…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Project…")).clicked() {
|
||||||
*show_project_config = !*show_project_config;
|
*show_project_config = !*show_project_config;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
|
@ -705,10 +710,11 @@ impl eframe::App for App {
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
let recent_projects_text = hotkeys::button_alt_text(ui, "_Recent Projects…");
|
||||||
if recent_projects.is_empty() {
|
if recent_projects.is_empty() {
|
||||||
ui.add_enabled(false, egui::Button::new("Recent projects…"));
|
ui.add_enabled(false, egui::Button::new(recent_projects_text));
|
||||||
} else {
|
} else {
|
||||||
ui.menu_button("Recent Projects…", |ui| {
|
ui.menu_button(recent_projects_text, |ui| {
|
||||||
if ui.button("Clear").clicked() {
|
if ui.button("Clear").clicked() {
|
||||||
state.write().unwrap().config.recent_projects.clear();
|
state.write().unwrap().config.recent_projects.clear();
|
||||||
};
|
};
|
||||||
|
@ -721,36 +727,39 @@ impl eframe::App for App {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ui.button("Appearance…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Appearance…")).clicked() {
|
||||||
*show_appearance_config = !*show_appearance_config;
|
*show_appearance_config = !*show_appearance_config;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
if ui.button("Graphics…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Graphics…")).clicked() {
|
||||||
*show_graphics = !*show_graphics;
|
*show_graphics = !*show_graphics;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
if ui.button("Quit").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Quit")).clicked() {
|
||||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.menu_button("Tools", |ui| {
|
ui.menu_button(hotkeys::button_alt_text(ui, "_Tools"), |ui| {
|
||||||
if ui.button("Demangle…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Demangle…")).clicked() {
|
||||||
*show_demangle = !*show_demangle;
|
*show_demangle = !*show_demangle;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
if ui.button("Rlwinm Decoder…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Rlwinm Decoder…")).clicked() {
|
||||||
*show_rlwinm_decode = !*show_rlwinm_decode;
|
*show_rlwinm_decode = !*show_rlwinm_decode;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.menu_button("Diff Options", |ui| {
|
ui.menu_button(hotkeys::button_alt_text(ui, "_Diff Options"), |ui| {
|
||||||
if ui.button("Arch Settings…").clicked() {
|
if ui.button(hotkeys::button_alt_text(ui, "_Arch Settings…")).clicked() {
|
||||||
*show_arch_config = !*show_arch_config;
|
*show_arch_config = !*show_arch_config;
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
let mut state = state.write().unwrap();
|
let mut state = state.write().unwrap();
|
||||||
let response = ui
|
let response = ui
|
||||||
.checkbox(&mut state.config.rebuild_on_changes, "Rebuild on changes")
|
.checkbox(
|
||||||
|
&mut state.config.rebuild_on_changes,
|
||||||
|
hotkeys::button_alt_text(ui, "_Rebuild on changes"),
|
||||||
|
)
|
||||||
.on_hover_text("Automatically re-run the build & diff when files change.");
|
.on_hover_text("Automatically re-run the build & diff when files change.");
|
||||||
if response.changed() {
|
if response.changed() {
|
||||||
state.watcher_change = true;
|
state.watcher_change = true;
|
||||||
|
@ -759,18 +768,21 @@ impl eframe::App for App {
|
||||||
!diff_state.symbol_state.disable_reverse_fn_order,
|
!diff_state.symbol_state.disable_reverse_fn_order,
|
||||||
egui::Checkbox::new(
|
egui::Checkbox::new(
|
||||||
&mut diff_state.symbol_state.reverse_fn_order,
|
&mut diff_state.symbol_state.reverse_fn_order,
|
||||||
"Reverse function order (-inline deferred)",
|
hotkeys::button_alt_text(
|
||||||
|
ui,
|
||||||
|
"Reverse function order (-inline _deferred)",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
||||||
ui.checkbox(
|
ui.checkbox(
|
||||||
&mut diff_state.symbol_state.show_hidden_symbols,
|
&mut diff_state.symbol_state.show_hidden_symbols,
|
||||||
"Show hidden symbols",
|
hotkeys::button_alt_text(ui, "Show _hidden symbols"),
|
||||||
);
|
);
|
||||||
if ui
|
if ui
|
||||||
.checkbox(
|
.checkbox(
|
||||||
&mut state.config.diff_obj_config.relax_reloc_diffs,
|
&mut state.config.diff_obj_config.relax_reloc_diffs,
|
||||||
"Relax relocation diffs",
|
hotkeys::button_alt_text(ui, "Rela_x relocation diffs"),
|
||||||
)
|
)
|
||||||
.on_hover_text(
|
.on_hover_text(
|
||||||
"Ignores differences in relocation targets. (Address, name, etc)",
|
"Ignores differences in relocation targets. (Address, name, etc)",
|
||||||
|
@ -782,7 +794,7 @@ impl eframe::App for App {
|
||||||
if ui
|
if ui
|
||||||
.checkbox(
|
.checkbox(
|
||||||
&mut state.config.diff_obj_config.space_between_args,
|
&mut state.config.diff_obj_config.space_between_args,
|
||||||
"Space between args",
|
hotkeys::button_alt_text(ui, "_Space between args"),
|
||||||
)
|
)
|
||||||
.changed()
|
.changed()
|
||||||
{
|
{
|
||||||
|
@ -791,14 +803,17 @@ impl eframe::App for App {
|
||||||
if ui
|
if ui
|
||||||
.checkbox(
|
.checkbox(
|
||||||
&mut state.config.diff_obj_config.combine_data_sections,
|
&mut state.config.diff_obj_config.combine_data_sections,
|
||||||
"Combine data sections",
|
hotkeys::button_alt_text(ui, "Combine _data sections"),
|
||||||
)
|
)
|
||||||
.on_hover_text("Combines data sections with equal names.")
|
.on_hover_text("Combines data sections with equal names.")
|
||||||
.changed()
|
.changed()
|
||||||
{
|
{
|
||||||
state.queue_reload = true;
|
state.queue_reload = true;
|
||||||
}
|
}
|
||||||
if ui.button("Clear custom symbol mappings").clicked() {
|
if ui
|
||||||
|
.button(hotkeys::button_alt_text(ui, "_Clear custom symbol mappings"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
state.clear_mappings();
|
state.clear_mappings();
|
||||||
diff_state.post_build_nav = Some(DiffViewNavigation::symbol_diff());
|
diff_state.post_build_nav = Some(DiffViewNavigation::symbol_diff());
|
||||||
state.queue_reload = true;
|
state.queue_reload = true;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use egui::{
|
use egui::{
|
||||||
style::ScrollAnimation, vec2, Context, Key, KeyboardShortcut, Modifiers, PointerButton,
|
style::ScrollAnimation, text::LayoutJob, vec2, Align, Context, FontSelection, Key,
|
||||||
|
KeyboardShortcut, Modifiers, PointerButton, RichText, Ui, WidgetText,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn any_widget_focused(ctx: &Context) -> bool { ctx.memory(|mem| mem.focused().is_some()) }
|
fn any_widget_focused(ctx: &Context) -> bool { ctx.memory(|mem| mem.focused().is_some()) }
|
||||||
|
@ -106,3 +107,122 @@ const CHANGE_BASE_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::
|
||||||
pub fn consume_change_base_shortcut(ctx: &Context) -> bool {
|
pub fn consume_change_base_shortcut(ctx: &Context) -> bool {
|
||||||
ctx.input_mut(|i| i.consume_shortcut(&CHANGE_BASE_SHORTCUT))
|
ctx.input_mut(|i| i.consume_shortcut(&CHANGE_BASE_SHORTCUT))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shortcut_key(text: &str) -> (usize, char, Key) {
|
||||||
|
let i = text.chars().position(|c| c == '_').expect("No underscore in text");
|
||||||
|
let key = text.chars().nth(i + 1).expect("No character after underscore");
|
||||||
|
let shortcut_key = match key {
|
||||||
|
'a' | 'A' => Key::A,
|
||||||
|
'b' | 'B' => Key::B,
|
||||||
|
'c' | 'C' => Key::C,
|
||||||
|
'd' | 'D' => Key::D,
|
||||||
|
'e' | 'E' => Key::E,
|
||||||
|
'f' | 'F' => Key::F,
|
||||||
|
'g' | 'G' => Key::G,
|
||||||
|
'h' | 'H' => Key::H,
|
||||||
|
'i' | 'I' => Key::I,
|
||||||
|
'j' | 'J' => Key::J,
|
||||||
|
'k' | 'K' => Key::K,
|
||||||
|
'l' | 'L' => Key::L,
|
||||||
|
'm' | 'M' => Key::M,
|
||||||
|
'n' | 'N' => Key::N,
|
||||||
|
'o' | 'O' => Key::O,
|
||||||
|
'p' | 'P' => Key::P,
|
||||||
|
'q' | 'Q' => Key::Q,
|
||||||
|
'r' | 'R' => Key::R,
|
||||||
|
's' | 'S' => Key::S,
|
||||||
|
't' | 'T' => Key::T,
|
||||||
|
'u' | 'U' => Key::U,
|
||||||
|
'v' | 'V' => Key::V,
|
||||||
|
'w' | 'W' => Key::W,
|
||||||
|
'x' | 'X' => Key::X,
|
||||||
|
'y' | 'Y' => Key::Y,
|
||||||
|
'z' | 'Z' => Key::Z,
|
||||||
|
_ => panic!("Invalid key {}", key),
|
||||||
|
};
|
||||||
|
(i, key, shortcut_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alt_text_ui(ui: &Ui, text: &str, i: usize, key: char, interactive: bool) -> WidgetText {
|
||||||
|
let color = if interactive {
|
||||||
|
ui.visuals().widgets.inactive.text_color()
|
||||||
|
} else {
|
||||||
|
ui.visuals().widgets.noninteractive.text_color()
|
||||||
|
};
|
||||||
|
let mut job = LayoutJob::default();
|
||||||
|
if i > 0 {
|
||||||
|
let text = &text[..i];
|
||||||
|
RichText::new(text).color(color).append_to(
|
||||||
|
&mut job,
|
||||||
|
ui.style(),
|
||||||
|
FontSelection::Default,
|
||||||
|
Align::Center,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let mut rt = RichText::new(key).color(color);
|
||||||
|
if ui.input(|i| i.modifiers.alt) {
|
||||||
|
rt = rt.underline();
|
||||||
|
}
|
||||||
|
rt.append_to(&mut job, ui.style(), FontSelection::Default, Align::Center);
|
||||||
|
if i < text.len() - 1 {
|
||||||
|
let text = &text[i + 2..];
|
||||||
|
RichText::new(text).color(color).append_to(
|
||||||
|
&mut job,
|
||||||
|
ui.style(),
|
||||||
|
FontSelection::Default,
|
||||||
|
Align::Center,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
job.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button_alt_text(ui: &Ui, text: &str) -> WidgetText {
|
||||||
|
let (n, c, key) = shortcut_key(text);
|
||||||
|
let result = alt_text_ui(ui, text, n, c, true);
|
||||||
|
if ui.input_mut(|i| check_alt_key(i, c, key)) {
|
||||||
|
let btn_id = ui.next_auto_id();
|
||||||
|
ui.memory_mut(|m| m.request_focus(btn_id));
|
||||||
|
ui.input_mut(|i| {
|
||||||
|
i.events.push(egui::Event::Key {
|
||||||
|
key: Key::Enter,
|
||||||
|
physical_key: None,
|
||||||
|
pressed: true,
|
||||||
|
repeat: false,
|
||||||
|
modifiers: Default::default(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alt_text(ui: &Ui, text: &str, enter: bool) -> WidgetText {
|
||||||
|
let (n, c, key) = shortcut_key(text);
|
||||||
|
let result = alt_text_ui(ui, text, n, c, false);
|
||||||
|
if ui.input_mut(|i| check_alt_key(i, c, key)) {
|
||||||
|
let btn_id = ui.next_auto_id();
|
||||||
|
ui.memory_mut(|m| m.request_focus(btn_id));
|
||||||
|
if enter {
|
||||||
|
ui.input_mut(|i| {
|
||||||
|
i.events.push(egui::Event::Key {
|
||||||
|
key: Key::Enter,
|
||||||
|
physical_key: None,
|
||||||
|
pressed: true,
|
||||||
|
repeat: false,
|
||||||
|
modifiers: Default::default(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_alt_key(i: &mut egui::InputState, c: char, key: Key) -> bool {
|
||||||
|
if i.consume_key(Modifiers::ALT, key) {
|
||||||
|
// Remove any text input events that match the key
|
||||||
|
let cs = c.to_string();
|
||||||
|
i.events.retain(|e| !matches!(e, egui::Event::Text(t) if *t == cs));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ pub fn config_ui(
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.heading("Project");
|
ui.heading("Project");
|
||||||
if ui.button(RichText::new("Settings")).clicked() {
|
if ui.button("Settings").clicked() {
|
||||||
*show_config_window = true;
|
*show_config_window = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -255,8 +255,9 @@ pub fn config_ui(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let had_search = !config_state.object_search.is_empty();
|
let had_search = !config_state.object_search.is_empty();
|
||||||
let response =
|
let response = egui::TextEdit::singleline(&mut config_state.object_search)
|
||||||
egui::TextEdit::singleline(&mut config_state.object_search).hint_text("Filter").ui(ui);
|
.hint_text(hotkeys::alt_text(ui, "Filter _objects", false))
|
||||||
|
.ui(ui);
|
||||||
if hotkeys::consume_object_filter_shortcut(ui.ctx()) {
|
if hotkeys::consume_object_filter_shortcut(ui.ctx()) {
|
||||||
response.request_focus();
|
response.request_focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::cmp::Ordering;
|
||||||
use egui::{ProgressBar, RichText, Widget};
|
use egui::{ProgressBar, RichText, Widget};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hotkeys,
|
||||||
jobs::{JobQueue, JobStatus},
|
jobs::{JobQueue, JobStatus},
|
||||||
views::appearance::Appearance,
|
views::appearance::Appearance,
|
||||||
};
|
};
|
||||||
|
@ -94,7 +95,14 @@ impl From<&JobStatus> for JobStatusDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jobs_menu_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appearance) -> bool {
|
pub fn jobs_menu_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appearance) -> bool {
|
||||||
ui.label("Jobs:");
|
let mut clicked = false;
|
||||||
|
if egui::Label::new(hotkeys::alt_text(ui, "_Jobs:", true))
|
||||||
|
.sense(egui::Sense::click())
|
||||||
|
.ui(ui)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
clicked = true;
|
||||||
|
}
|
||||||
let mut statuses = Vec::new();
|
let mut statuses = Vec::new();
|
||||||
for job in jobs.iter_mut() {
|
for job in jobs.iter_mut() {
|
||||||
let Ok(status) = job.context.status.read() else {
|
let Ok(status) = job.context.status.read() else {
|
||||||
|
@ -105,7 +113,6 @@ pub fn jobs_menu_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appeara
|
||||||
let running_jobs = statuses.iter().filter(|s| !s.error).count();
|
let running_jobs = statuses.iter().filter(|s| !s.error).count();
|
||||||
let error_jobs = statuses.iter().filter(|s| s.error).count();
|
let error_jobs = statuses.iter().filter(|s| s.error).count();
|
||||||
|
|
||||||
let mut clicked = false;
|
|
||||||
let spinner =
|
let spinner =
|
||||||
egui::Spinner::new().size(appearance.ui_font.size * 0.9).color(appearance.text_color);
|
egui::Spinner::new().size(appearance.ui_font.size * 0.9).color(appearance.text_color);
|
||||||
match running_jobs.cmp(&1) {
|
match running_jobs.cmp(&1) {
|
||||||
|
|
|
@ -901,7 +901,9 @@ pub fn symbol_diff_ui(
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut search = state.search.clone();
|
let mut search = state.search.clone();
|
||||||
let response = TextEdit::singleline(&mut search).hint_text("Filter symbols").ui(ui);
|
let response = TextEdit::singleline(&mut search)
|
||||||
|
.hint_text(hotkeys::alt_text(ui, "Filter _symbols", false))
|
||||||
|
.ui(ui);
|
||||||
if hotkeys::consume_symbol_filter_shortcut(ui.ctx()) {
|
if hotkeys::consume_symbol_filter_shortcut(ui.ctx()) {
|
||||||
response.request_focus();
|
response.request_focus();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue