mirror of
https://github.com/encounter/objdiff.git
synced 2025-12-14 15:46:31 +00:00
Compare commits
5 Commits
alt-keys
...
221c1a8cda
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
221c1a8cda | ||
|
|
0b7afa9f1e | ||
|
|
5841d4db8a | ||
|
|
7a360d4a99 | ||
|
|
40c97a1bc3 |
@@ -25,7 +25,6 @@ use time::UtcOffset;
|
||||
use crate::{
|
||||
app_config::{deserialize_config, AppConfigVersion},
|
||||
config::{load_project_config, ProjectObjectNode},
|
||||
hotkeys,
|
||||
jobs::{
|
||||
objdiff::{start_build, ObjDiffConfig},
|
||||
Job, JobQueue, JobResult, JobStatus,
|
||||
@@ -528,10 +527,6 @@ impl App {
|
||||
}
|
||||
|
||||
fn post_update(&mut self, ctx: &egui::Context, action: Option<DiffViewAction>) {
|
||||
if action.is_some() {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
|
||||
self.appearance.post_update(ctx);
|
||||
|
||||
let ViewState { jobs, diff_state, config_state, graphics_state, .. } = &mut self.view_state;
|
||||
@@ -695,13 +690,13 @@ impl eframe::App for App {
|
||||
*show_side_panel = !*show_side_panel;
|
||||
}
|
||||
ui.separator();
|
||||
ui.menu_button(hotkeys::button_alt_text(ui, "_File"), |ui| {
|
||||
ui.menu_button("File", |ui| {
|
||||
#[cfg(debug_assertions)]
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Debug…")).clicked() {
|
||||
if ui.button("Debug…").clicked() {
|
||||
*show_debug = !*show_debug;
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Project…")).clicked() {
|
||||
if ui.button("Project…").clicked() {
|
||||
*show_project_config = !*show_project_config;
|
||||
ui.close_menu();
|
||||
}
|
||||
@@ -710,11 +705,10 @@ impl eframe::App for App {
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let recent_projects_text = hotkeys::button_alt_text(ui, "_Recent Projects…");
|
||||
if recent_projects.is_empty() {
|
||||
ui.add_enabled(false, egui::Button::new(recent_projects_text));
|
||||
ui.add_enabled(false, egui::Button::new("Recent projects…"));
|
||||
} else {
|
||||
ui.menu_button(recent_projects_text, |ui| {
|
||||
ui.menu_button("Recent Projects…", |ui| {
|
||||
if ui.button("Clear").clicked() {
|
||||
state.write().unwrap().config.recent_projects.clear();
|
||||
};
|
||||
@@ -727,39 +721,36 @@ impl eframe::App for App {
|
||||
}
|
||||
});
|
||||
}
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Appearance…")).clicked() {
|
||||
if ui.button("Appearance…").clicked() {
|
||||
*show_appearance_config = !*show_appearance_config;
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Graphics…")).clicked() {
|
||||
if ui.button("Graphics…").clicked() {
|
||||
*show_graphics = !*show_graphics;
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Quit")).clicked() {
|
||||
if ui.button("Quit").clicked() {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
});
|
||||
ui.menu_button(hotkeys::button_alt_text(ui, "_Tools"), |ui| {
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Demangle…")).clicked() {
|
||||
ui.menu_button("Tools", |ui| {
|
||||
if ui.button("Demangle…").clicked() {
|
||||
*show_demangle = !*show_demangle;
|
||||
ui.close_menu();
|
||||
}
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Rlwinm Decoder…")).clicked() {
|
||||
if ui.button("Rlwinm Decoder…").clicked() {
|
||||
*show_rlwinm_decode = !*show_rlwinm_decode;
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
ui.menu_button(hotkeys::button_alt_text(ui, "_Diff Options"), |ui| {
|
||||
if ui.button(hotkeys::button_alt_text(ui, "_Arch Settings…")).clicked() {
|
||||
ui.menu_button("Diff Options", |ui| {
|
||||
if ui.button("Arch Settings…").clicked() {
|
||||
*show_arch_config = !*show_arch_config;
|
||||
ui.close_menu();
|
||||
}
|
||||
let mut state = state.write().unwrap();
|
||||
let response = ui
|
||||
.checkbox(
|
||||
&mut state.config.rebuild_on_changes,
|
||||
hotkeys::button_alt_text(ui, "_Rebuild on changes"),
|
||||
)
|
||||
.checkbox(&mut state.config.rebuild_on_changes, "Rebuild on changes")
|
||||
.on_hover_text("Automatically re-run the build & diff when files change.");
|
||||
if response.changed() {
|
||||
state.watcher_change = true;
|
||||
@@ -768,21 +759,18 @@ impl eframe::App for App {
|
||||
!diff_state.symbol_state.disable_reverse_fn_order,
|
||||
egui::Checkbox::new(
|
||||
&mut diff_state.symbol_state.reverse_fn_order,
|
||||
hotkeys::button_alt_text(
|
||||
ui,
|
||||
"Reverse function order (-inline _deferred)",
|
||||
),
|
||||
"Reverse function order (-inline deferred)",
|
||||
),
|
||||
)
|
||||
.on_disabled_hover_text(CONFIG_DISABLED_TEXT);
|
||||
ui.checkbox(
|
||||
&mut diff_state.symbol_state.show_hidden_symbols,
|
||||
hotkeys::button_alt_text(ui, "Show _hidden symbols"),
|
||||
"Show hidden symbols",
|
||||
);
|
||||
if ui
|
||||
.checkbox(
|
||||
&mut state.config.diff_obj_config.relax_reloc_diffs,
|
||||
hotkeys::button_alt_text(ui, "Rela_x relocation diffs"),
|
||||
"Relax relocation diffs",
|
||||
)
|
||||
.on_hover_text(
|
||||
"Ignores differences in relocation targets. (Address, name, etc)",
|
||||
@@ -794,7 +782,7 @@ impl eframe::App for App {
|
||||
if ui
|
||||
.checkbox(
|
||||
&mut state.config.diff_obj_config.space_between_args,
|
||||
hotkeys::button_alt_text(ui, "_Space between args"),
|
||||
"Space between args",
|
||||
)
|
||||
.changed()
|
||||
{
|
||||
@@ -803,17 +791,14 @@ impl eframe::App for App {
|
||||
if ui
|
||||
.checkbox(
|
||||
&mut state.config.diff_obj_config.combine_data_sections,
|
||||
hotkeys::button_alt_text(ui, "Combine _data sections"),
|
||||
"Combine data sections",
|
||||
)
|
||||
.on_hover_text("Combines data sections with equal names.")
|
||||
.changed()
|
||||
{
|
||||
state.queue_reload = true;
|
||||
}
|
||||
if ui
|
||||
.button(hotkeys::button_alt_text(ui, "_Clear custom symbol mappings"))
|
||||
.clicked()
|
||||
{
|
||||
if ui.button("Clear custom symbol mappings").clicked() {
|
||||
state.clear_mappings();
|
||||
diff_state.post_build_nav = Some(DiffViewNavigation::symbol_diff());
|
||||
state.queue_reload = true;
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
use egui::{
|
||||
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()) }
|
||||
|
||||
pub fn enter_pressed(ctx: &Context) -> bool {
|
||||
if any_widget_focused(ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.input_mut(|i| {
|
||||
i.key_pressed(Key::Enter)
|
||||
|| i.key_pressed(Key::Space)
|
||||
|| i.pointer.button_pressed(PointerButton::Extra2)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn back_pressed(ctx: &Context) -> bool {
|
||||
if any_widget_focused(ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.input_mut(|i| {
|
||||
i.key_pressed(Key::Backspace)
|
||||
|| i.key_pressed(Key::Escape)
|
||||
|| i.pointer.button_pressed(PointerButton::Extra1)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn up_pressed(ctx: &Context) -> bool {
|
||||
if any_widget_focused(ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.input_mut(|i| i.key_pressed(Key::ArrowUp) || i.key_pressed(Key::W))
|
||||
}
|
||||
|
||||
pub fn down_pressed(ctx: &Context) -> bool {
|
||||
if any_widget_focused(ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.input_mut(|i| i.key_pressed(Key::ArrowDown) || i.key_pressed(Key::S))
|
||||
}
|
||||
|
||||
pub fn page_up_pressed(ctx: &Context) -> bool { ctx.input_mut(|i| i.key_pressed(Key::PageUp)) }
|
||||
|
||||
pub fn page_down_pressed(ctx: &Context) -> bool { ctx.input_mut(|i| i.key_pressed(Key::PageDown)) }
|
||||
|
||||
pub fn home_pressed(ctx: &Context) -> bool { ctx.input_mut(|i| i.key_pressed(Key::Home)) }
|
||||
|
||||
pub fn end_pressed(ctx: &Context) -> bool { ctx.input_mut(|i| i.key_pressed(Key::End)) }
|
||||
|
||||
pub fn check_scroll_hotkeys(ui: &mut egui::Ui, include_small_increments: bool) {
|
||||
let ui_height = ui.available_rect_before_wrap().height();
|
||||
if up_pressed(ui.ctx()) && include_small_increments {
|
||||
ui.scroll_with_delta_animation(vec2(0.0, ui_height / 10.0), ScrollAnimation::none());
|
||||
} else if down_pressed(ui.ctx()) && include_small_increments {
|
||||
ui.scroll_with_delta_animation(vec2(0.0, -ui_height / 10.0), ScrollAnimation::none());
|
||||
} else if page_up_pressed(ui.ctx()) {
|
||||
ui.scroll_with_delta_animation(vec2(0.0, ui_height), ScrollAnimation::none());
|
||||
} else if page_down_pressed(ui.ctx()) {
|
||||
ui.scroll_with_delta_animation(vec2(0.0, -ui_height), ScrollAnimation::none());
|
||||
} else if home_pressed(ui.ctx()) {
|
||||
ui.scroll_with_delta_animation(vec2(0.0, f32::INFINITY), ScrollAnimation::none());
|
||||
} else if end_pressed(ui.ctx()) {
|
||||
ui.scroll_with_delta_animation(vec2(0.0, -f32::INFINITY), ScrollAnimation::none());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume_up_key(ctx: &Context) -> bool {
|
||||
if any_widget_focused(ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.input_mut(|i| {
|
||||
i.consume_key(Modifiers::NONE, Key::ArrowUp) || i.consume_key(Modifiers::NONE, Key::W)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn consume_down_key(ctx: &Context) -> bool {
|
||||
if any_widget_focused(ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.input_mut(|i| {
|
||||
i.consume_key(Modifiers::NONE, Key::ArrowDown) || i.consume_key(Modifiers::NONE, Key::S)
|
||||
})
|
||||
}
|
||||
|
||||
const OBJECT_FILTER_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::CTRL, Key::F);
|
||||
|
||||
pub fn consume_object_filter_shortcut(ctx: &Context) -> bool {
|
||||
ctx.input_mut(|i| i.consume_shortcut(&OBJECT_FILTER_SHORTCUT))
|
||||
}
|
||||
|
||||
const SYMBOL_FILTER_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::CTRL, Key::S);
|
||||
|
||||
pub fn consume_symbol_filter_shortcut(ctx: &Context) -> bool {
|
||||
ctx.input_mut(|i| i.consume_shortcut(&SYMBOL_FILTER_SHORTCUT))
|
||||
}
|
||||
|
||||
const CHANGE_TARGET_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::CTRL, Key::T);
|
||||
|
||||
pub fn consume_change_target_shortcut(ctx: &Context) -> bool {
|
||||
ctx.input_mut(|i| i.consume_shortcut(&CHANGE_TARGET_SHORTCUT))
|
||||
}
|
||||
|
||||
const CHANGE_BASE_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::CTRL, Key::B);
|
||||
|
||||
pub fn consume_change_base_shortcut(ctx: &Context) -> bool {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ mod app;
|
||||
mod app_config;
|
||||
mod config;
|
||||
mod fonts;
|
||||
mod hotkeys;
|
||||
mod jobs;
|
||||
mod update;
|
||||
mod views;
|
||||
|
||||
@@ -21,7 +21,6 @@ use strum::{EnumMessage, VariantArray};
|
||||
use crate::{
|
||||
app::{AppConfig, AppState, AppStateRef, ObjectConfig},
|
||||
config::ProjectObjectNode,
|
||||
hotkeys,
|
||||
jobs::{
|
||||
check_update::{start_check_update, CheckUpdateResult},
|
||||
update::start_update,
|
||||
@@ -219,7 +218,7 @@ pub fn config_ui(
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.heading("Project");
|
||||
if ui.button("Settings").clicked() {
|
||||
if ui.button(RichText::new("Settings")).clicked() {
|
||||
*show_config_window = true;
|
||||
}
|
||||
});
|
||||
@@ -255,12 +254,7 @@ pub fn config_ui(
|
||||
}
|
||||
} else {
|
||||
let had_search = !config_state.object_search.is_empty();
|
||||
let response = egui::TextEdit::singleline(&mut config_state.object_search)
|
||||
.hint_text(hotkeys::alt_text(ui, "Filter _objects", false))
|
||||
.ui(ui);
|
||||
if hotkeys::consume_object_filter_shortcut(ui.ctx()) {
|
||||
response.request_focus();
|
||||
}
|
||||
egui::TextEdit::singleline(&mut config_state.object_search).hint_text("Filter").ui(ui);
|
||||
|
||||
let mut root_open = None;
|
||||
let mut node_open = NodeOpen::Default;
|
||||
|
||||
@@ -7,14 +7,11 @@ use objdiff_core::{
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
use crate::{
|
||||
hotkeys,
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_table},
|
||||
symbol_diff::{DiffViewAction, DiffViewNavigation, DiffViewState},
|
||||
write_text,
|
||||
},
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_table},
|
||||
symbol_diff::{DiffViewAction, DiffViewNavigation, DiffViewState},
|
||||
write_text,
|
||||
};
|
||||
|
||||
const BYTES_PER_ROW: usize = 16;
|
||||
@@ -179,8 +176,6 @@ fn data_table_ui(
|
||||
let left_diffs = left_section.map(|(_, section)| split_diffs(§ion.data_diff));
|
||||
let right_diffs = right_section.map(|(_, section)| split_diffs(§ion.data_diff));
|
||||
|
||||
hotkeys::check_scroll_hotkeys(ui, true);
|
||||
|
||||
render_table(ui, available_width, 2, config.code_font.size, total_rows, |row, column| {
|
||||
let i = row.index();
|
||||
let address = i * BYTES_PER_ROW;
|
||||
@@ -229,7 +224,7 @@ pub fn data_diff_ui(
|
||||
render_header(ui, available_width, 2, |ui, column| {
|
||||
if column == 0 {
|
||||
// Left column
|
||||
if ui.button("⏴ Back").clicked() || hotkeys::back_pressed(ui.ctx()) {
|
||||
if ui.button("⏴ Back").clicked() {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,13 @@ use objdiff_core::{
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
use crate::{
|
||||
hotkeys,
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_strips},
|
||||
function_diff::FunctionDiffContext,
|
||||
symbol_diff::{
|
||||
match_color_for_symbol, DiffViewAction, DiffViewNavigation, DiffViewState,
|
||||
SymbolRefByName, View,
|
||||
},
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_strips},
|
||||
function_diff::FunctionDiffContext,
|
||||
symbol_diff::{
|
||||
match_color_for_symbol, DiffViewAction, DiffViewNavigation, DiffViewState, SymbolRefByName,
|
||||
View,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -139,7 +136,7 @@ pub fn extab_diff_ui(
|
||||
if column == 0 {
|
||||
// Left column
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("⏴ Back").clicked() || hotkeys::back_pressed(ui.ctx()) {
|
||||
if ui.button("⏴ Back").clicked() {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
||||
}
|
||||
ui.separator();
|
||||
@@ -235,8 +232,6 @@ pub fn extab_diff_ui(
|
||||
}
|
||||
});
|
||||
|
||||
hotkeys::check_scroll_hotkeys(ui, true);
|
||||
|
||||
// Table
|
||||
render_strips(ui, available_width, 2, |ui, column| {
|
||||
if column == 0 {
|
||||
|
||||
@@ -14,15 +14,12 @@ use objdiff_core::{
|
||||
};
|
||||
use time::format_description;
|
||||
|
||||
use crate::{
|
||||
hotkeys,
|
||||
views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_strips, render_table},
|
||||
symbol_diff::{
|
||||
match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation,
|
||||
DiffViewState, SymbolDiffContext, SymbolFilter, SymbolRefByName, SymbolViewState, View,
|
||||
},
|
||||
use crate::views::{
|
||||
appearance::Appearance,
|
||||
column_layout::{render_header, render_strips, render_table},
|
||||
symbol_diff::{
|
||||
match_color_for_symbol, symbol_list_ui, DiffViewAction, DiffViewNavigation, DiffViewState,
|
||||
SymbolDiffContext, SymbolFilter, SymbolRefByName, SymbolViewState, View,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -152,6 +149,10 @@ fn ins_hover_ui(
|
||||
ui.colored_label(appearance.highlight_color, "Extern".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(demangled) = rlwinmdec::decode(&ins.formatted) {
|
||||
ui.colored_label(appearance.highlight_color, demangled.trim());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -435,7 +436,6 @@ fn asm_table_ui(
|
||||
};
|
||||
if left_len.is_some() && right_len.is_some() {
|
||||
// Joint view
|
||||
hotkeys::check_scroll_hotkeys(ui, true);
|
||||
render_table(
|
||||
ui,
|
||||
available_width,
|
||||
@@ -471,7 +471,6 @@ fn asm_table_ui(
|
||||
if column == 0 {
|
||||
if let Some(ctx) = left_ctx {
|
||||
if ctx.has_symbol() {
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
render_table(
|
||||
ui,
|
||||
available_width / 2.0,
|
||||
@@ -517,6 +516,9 @@ fn asm_table_ui(
|
||||
SymbolRefByName::new(right_symbol, right_section),
|
||||
));
|
||||
}
|
||||
DiffViewAction::SetSymbolHighlight(_, _) => {
|
||||
// Ignore
|
||||
}
|
||||
_ => {
|
||||
ret = Some(action);
|
||||
}
|
||||
@@ -529,7 +531,6 @@ fn asm_table_ui(
|
||||
} else if column == 1 {
|
||||
if let Some(ctx) = right_ctx {
|
||||
if ctx.has_symbol() {
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
render_table(
|
||||
ui,
|
||||
available_width / 2.0,
|
||||
@@ -575,6 +576,9 @@ fn asm_table_ui(
|
||||
right_symbol_ref,
|
||||
));
|
||||
}
|
||||
DiffViewAction::SetSymbolHighlight(_, _) => {
|
||||
// Ignore
|
||||
}
|
||||
_ => {
|
||||
ret = Some(action);
|
||||
}
|
||||
@@ -675,7 +679,7 @@ pub fn function_diff_ui(
|
||||
if column == 0 {
|
||||
// Left column
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("⏴ Back").clicked() || hotkeys::back_pressed(ui.ctx()) {
|
||||
if ui.button("⏴ Back").clicked() {
|
||||
ret = Some(DiffViewAction::Navigate(DiffViewNavigation::symbol_diff()));
|
||||
}
|
||||
ui.separator();
|
||||
@@ -708,11 +712,10 @@ pub fn function_diff_ui(
|
||||
.color(appearance.highlight_color),
|
||||
);
|
||||
if right_ctx.is_some_and(|m| m.has_symbol())
|
||||
&& (ui
|
||||
&& ui
|
||||
.button("Change target")
|
||||
.on_hover_text_at_pointer("Choose a different symbol to use as the target")
|
||||
.clicked()
|
||||
|| hotkeys::consume_change_target_shortcut(ui.ctx()))
|
||||
{
|
||||
if let Some(symbol_ref) = state.symbol_state.right_symbol.as_ref() {
|
||||
ret = Some(DiffViewAction::SelectingLeft(symbol_ref.clone()));
|
||||
@@ -786,7 +789,6 @@ pub fn function_diff_ui(
|
||||
"Choose a different symbol to use as the base",
|
||||
)
|
||||
.clicked()
|
||||
|| hotkeys::consume_change_base_shortcut(ui.ctx())
|
||||
{
|
||||
if let Some(symbol_ref) = state.symbol_state.left_symbol.as_ref() {
|
||||
ret = Some(DiffViewAction::SelectingRight(symbol_ref.clone()));
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::cmp::Ordering;
|
||||
use egui::{ProgressBar, RichText, Widget};
|
||||
|
||||
use crate::{
|
||||
hotkeys,
|
||||
jobs::{JobQueue, JobStatus},
|
||||
views::appearance::Appearance,
|
||||
};
|
||||
@@ -95,14 +94,7 @@ impl From<&JobStatus> for JobStatusDisplay {
|
||||
}
|
||||
|
||||
pub fn jobs_menu_ui(ui: &mut egui::Ui, jobs: &mut JobQueue, appearance: &Appearance) -> bool {
|
||||
let mut clicked = false;
|
||||
if egui::Label::new(hotkeys::alt_text(ui, "_Jobs:", true))
|
||||
.sense(egui::Sense::click())
|
||||
.ui(ui)
|
||||
.clicked()
|
||||
{
|
||||
clicked = true;
|
||||
}
|
||||
ui.label("Jobs:");
|
||||
let mut statuses = Vec::new();
|
||||
for job in jobs.iter_mut() {
|
||||
let Ok(status) = job.context.status.read() else {
|
||||
@@ -113,6 +105,7 @@ 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 error_jobs = statuses.iter().filter(|s| s.error).count();
|
||||
|
||||
let mut clicked = false;
|
||||
let spinner =
|
||||
egui::Spinner::new().size(appearance.ui_font.size * 0.9).color(appearance.text_color);
|
||||
match running_jobs.cmp(&1) {
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn rlwinm_decode_window(
|
||||
if let Some(demangled) = rlwinmdec::decode(&state.text) {
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(TextStyle::Monospace);
|
||||
ui.colored_label(appearance.replace_color, &demangled);
|
||||
ui.colored_label(appearance.replace_color, demangled.trim());
|
||||
});
|
||||
if ui.button("Copy").clicked() {
|
||||
ui.output_mut(|output| output.copied_text = demangled);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::{collections::BTreeMap, mem::take, ops::Bound};
|
||||
use std::{collections::BTreeMap, mem::take};
|
||||
|
||||
use egui::{
|
||||
style::ScrollAnimation, text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea,
|
||||
SelectableLabel, TextEdit, Ui, Widget,
|
||||
text::LayoutJob, CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, SelectableLabel, TextEdit,
|
||||
Ui, Widget,
|
||||
};
|
||||
use objdiff_core::{
|
||||
arch::ObjArch,
|
||||
@@ -15,7 +15,6 @@ use regex::{Regex, RegexBuilder};
|
||||
|
||||
use crate::{
|
||||
app::AppStateRef,
|
||||
hotkeys,
|
||||
jobs::{
|
||||
create_scratch::{start_create_scratch, CreateScratchConfig, CreateScratchResult},
|
||||
objdiff::{BuildStatus, ObjDiffResult},
|
||||
@@ -57,8 +56,8 @@ pub enum DiffViewAction {
|
||||
Build,
|
||||
/// Navigate to a new diff view
|
||||
Navigate(DiffViewNavigation),
|
||||
/// Set the highlighted symbols in the symbols view, optionally scrolling them into view.
|
||||
SetSymbolHighlight(Option<SymbolRef>, Option<SymbolRef>, bool),
|
||||
/// Set the highlighted symbols in the symbols view
|
||||
SetSymbolHighlight(Option<SymbolRef>, Option<SymbolRef>),
|
||||
/// Set the symbols view search filter
|
||||
SetSearch(String),
|
||||
/// Submit the current function to decomp.me
|
||||
@@ -136,7 +135,6 @@ pub struct DiffViewState {
|
||||
#[derive(Default)]
|
||||
pub struct SymbolViewState {
|
||||
pub highlighted_symbol: (Option<SymbolRef>, Option<SymbolRef>),
|
||||
pub autoscroll_to_highlighted_symbols: bool,
|
||||
pub left_symbol: Option<SymbolRefByName>,
|
||||
pub right_symbol: Option<SymbolRefByName>,
|
||||
pub reverse_fn_order: bool,
|
||||
@@ -199,9 +197,6 @@ impl DiffViewState {
|
||||
ctx.output_mut(|o| o.open_url = Some(OpenUrl::new_tab(result.scratch_url)));
|
||||
}
|
||||
|
||||
// Clear the autoscroll flag so that it doesn't scroll continuously.
|
||||
self.symbol_state.autoscroll_to_highlighted_symbols = false;
|
||||
|
||||
let Some(action) = action else {
|
||||
return;
|
||||
};
|
||||
@@ -216,6 +211,7 @@ impl DiffViewState {
|
||||
// Ignore action if we're already navigating
|
||||
return;
|
||||
}
|
||||
self.symbol_state.highlighted_symbol = (None, None);
|
||||
let Ok(mut state) = state.write() else {
|
||||
return;
|
||||
};
|
||||
@@ -251,9 +247,8 @@ impl DiffViewState {
|
||||
self.post_build_nav = Some(nav);
|
||||
}
|
||||
}
|
||||
DiffViewAction::SetSymbolHighlight(left, right, autoscroll) => {
|
||||
DiffViewAction::SetSymbolHighlight(left, right) => {
|
||||
self.symbol_state.highlighted_symbol = (left, right);
|
||||
self.symbol_state.autoscroll_to_highlighted_symbols = autoscroll;
|
||||
}
|
||||
DiffViewAction::SetSearch(search) => {
|
||||
self.search_regex = if search.is_empty() {
|
||||
@@ -539,15 +534,7 @@ fn symbol_ui(
|
||||
ret = Some(DiffViewAction::Navigate(result));
|
||||
}
|
||||
});
|
||||
if selected && state.autoscroll_to_highlighted_symbols {
|
||||
// Automatically scroll the view to encompass the selected symbol in case the user selected
|
||||
// an offscreen symbol by using a keyboard shortcut.
|
||||
ui.scroll_to_rect_animation(response.rect, None, ScrollAnimation::none());
|
||||
// This autoscroll state flag will be reset in DiffViewState::post_update at the end of
|
||||
// every frame so that we don't continuously scroll the view back when the user is trying to
|
||||
// manually scroll away.
|
||||
}
|
||||
if response.clicked() || (selected && hotkeys::enter_pressed(ui.ctx())) {
|
||||
if response.clicked() {
|
||||
if let Some(section) = section {
|
||||
match section.kind {
|
||||
ObjSectionKind::Code => {
|
||||
@@ -574,18 +561,20 @@ fn symbol_ui(
|
||||
}
|
||||
}
|
||||
} else if response.hovered() {
|
||||
ret = Some(if column == 0 {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
Some(symbol_diff.symbol_ref),
|
||||
symbol_diff.target_symbol,
|
||||
false,
|
||||
)
|
||||
ret = Some(if let Some(target_symbol) = symbol_diff.target_symbol {
|
||||
if column == 0 {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
Some(symbol_diff.symbol_ref),
|
||||
Some(target_symbol),
|
||||
)
|
||||
} else {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
Some(target_symbol),
|
||||
Some(symbol_diff.symbol_ref),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
symbol_diff.target_symbol,
|
||||
Some(symbol_diff.symbol_ref),
|
||||
false,
|
||||
)
|
||||
DiffViewAction::SetSymbolHighlight(None, None)
|
||||
});
|
||||
}
|
||||
ret
|
||||
@@ -659,58 +648,6 @@ pub fn symbol_list_ui(
|
||||
}
|
||||
}
|
||||
|
||||
hotkeys::check_scroll_hotkeys(ui, false);
|
||||
|
||||
let mut new_key_value_to_highlight = None;
|
||||
if let Some(sym_ref) =
|
||||
if column == 0 { state.highlighted_symbol.0 } else { state.highlighted_symbol.1 }
|
||||
{
|
||||
let up = if hotkeys::consume_up_key(ui.ctx()) {
|
||||
Some(true)
|
||||
} else if hotkeys::consume_down_key(ui.ctx()) {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(mut up) = up {
|
||||
if state.reverse_fn_order {
|
||||
up = !up;
|
||||
}
|
||||
new_key_value_to_highlight = if up {
|
||||
mapping.range(..sym_ref).next_back()
|
||||
} else {
|
||||
mapping.range((Bound::Excluded(sym_ref), Bound::Unbounded)).next()
|
||||
};
|
||||
};
|
||||
} else {
|
||||
// No symbol is highlighted in this column. Select the topmost symbol instead.
|
||||
// Note that we intentionally do not consume the up/down key presses in this case, but
|
||||
// we do when a symbol is highlighted. This is so that if only one column has a symbol
|
||||
// highlighted, that one takes precedence over the one with nothing highlighted.
|
||||
if hotkeys::up_pressed(ui.ctx()) || hotkeys::down_pressed(ui.ctx()) {
|
||||
new_key_value_to_highlight = if state.reverse_fn_order {
|
||||
mapping.last_key_value()
|
||||
} else {
|
||||
mapping.first_key_value()
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some((new_sym_ref, new_symbol_diff)) = new_key_value_to_highlight {
|
||||
ret = Some(if column == 0 {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
Some(*new_sym_ref),
|
||||
new_symbol_diff.target_symbol,
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
DiffViewAction::SetSymbolHighlight(
|
||||
new_symbol_diff.target_symbol,
|
||||
Some(*new_sym_ref),
|
||||
true,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
|
||||
@@ -901,13 +838,7 @@ pub fn symbol_diff_ui(
|
||||
});
|
||||
|
||||
let mut search = state.search.clone();
|
||||
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()) {
|
||||
response.request_focus();
|
||||
}
|
||||
if response.changed() {
|
||||
if TextEdit::singleline(&mut search).hint_text("Filter symbols").ui(ui).changed() {
|
||||
ret = Some(DiffViewAction::SetSearch(search));
|
||||
}
|
||||
} else if column == 1 {
|
||||
|
||||
Reference in New Issue
Block a user