2023-08-12 11:18:09 -07:00
|
|
|
use std::mem::take;
|
|
|
|
|
2022-09-08 14:19:20 -07:00
|
|
|
use egui::{
|
2023-09-09 20:43:12 -07:00
|
|
|
text::LayoutJob, Align, CollapsingHeader, Color32, Layout, ScrollArea, SelectableLabel,
|
2023-01-20 22:11:40 -08:00
|
|
|
TextEdit, Ui, Vec2, Widget,
|
2022-09-08 14:19:20 -07:00
|
|
|
};
|
|
|
|
use egui_extras::{Size, StripBuilder};
|
|
|
|
|
|
|
|
use crate::{
|
2023-08-12 11:18:09 -07:00
|
|
|
app::AppConfigRef,
|
|
|
|
jobs::{
|
|
|
|
objdiff::{BuildStatus, ObjDiffResult},
|
|
|
|
Job, JobQueue, JobResult,
|
|
|
|
},
|
2022-11-05 21:49:46 -07:00
|
|
|
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags},
|
2023-10-05 20:40:45 -07:00
|
|
|
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
|
2022-09-08 14:19:20 -07:00
|
|
|
};
|
|
|
|
|
2023-08-09 18:53:04 -07:00
|
|
|
pub struct SymbolReference {
|
|
|
|
pub symbol_name: String,
|
|
|
|
pub section_name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::enum_variant_names)]
|
2023-08-12 11:18:09 -07:00
|
|
|
#[derive(Default, Eq, PartialEq, Copy, Clone)]
|
2023-08-09 18:53:04 -07:00
|
|
|
pub enum View {
|
|
|
|
#[default]
|
|
|
|
SymbolDiff,
|
|
|
|
FunctionDiff,
|
|
|
|
DataDiff,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct DiffViewState {
|
|
|
|
pub build: Option<Box<ObjDiffResult>>,
|
|
|
|
pub current_view: View,
|
2023-08-12 11:18:09 -07:00
|
|
|
pub symbol_state: SymbolViewState,
|
2023-10-05 20:40:45 -07:00
|
|
|
pub function_state: FunctionViewState,
|
2023-08-12 11:18:09 -07:00
|
|
|
pub search: String,
|
|
|
|
pub queue_build: bool,
|
|
|
|
pub build_running: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct SymbolViewState {
|
2023-08-09 18:53:04 -07:00
|
|
|
pub highlighted_symbol: Option<String>,
|
|
|
|
pub selected_symbol: Option<SymbolReference>,
|
2023-08-12 11:18:09 -07:00
|
|
|
pub reverse_fn_order: bool,
|
|
|
|
pub disable_reverse_fn_order: bool,
|
2023-10-07 10:27:12 -07:00
|
|
|
pub show_hidden_symbols: bool,
|
2023-08-12 11:18:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DiffViewState {
|
|
|
|
pub fn pre_update(&mut self, jobs: &mut JobQueue, config: &AppConfigRef) {
|
|
|
|
jobs.results.retain_mut(|result| {
|
|
|
|
if let JobResult::ObjDiff(result) = result {
|
|
|
|
self.build = take(result);
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
self.build_running = jobs.is_running(Job::ObjDiff);
|
|
|
|
|
|
|
|
self.symbol_state.disable_reverse_fn_order = false;
|
|
|
|
if let Ok(config) = config.read() {
|
2023-09-03 06:28:22 -07:00
|
|
|
if let Some(obj_config) = &config.selected_obj {
|
|
|
|
if let Some(value) = obj_config.reverse_fn_order {
|
|
|
|
self.symbol_state.reverse_fn_order = value;
|
|
|
|
self.symbol_state.disable_reverse_fn_order = true;
|
2023-08-12 11:18:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Repaint rework: more responsive, less energy
Previously, we repainted every frame on Windows at full refresh rate.
This is an enormous waste, as the UI will be static most of the time.
This was to work around a bug with `rfd` + `eframe`.
On other platforms, we only repainted every frame when a job was running,
which was better, but still not ideal. We also had a 100ms deadline, so
we'd repaint at ~10fps minimum to catch new events (file watcher, jobs).
This removes all repaint logic from the main loop and moves it into the
individual places where we change state from another thread.
For example, the file watcher thread will now immediately notify egui
to repaint, rather than relying on the 100ms deadline we had previously.
Jobs, when updating their status, also notify egui to repaint.
For `rfd` file dialogs, this migrates to using the async API built on top of
a polling thread + `pollster`. This interacts better with `eframe` on Windows.
Overall, this should reduce repaints and improve responsiveness to
file changes and background tasks.
2023-11-21 11:34:26 -08:00
|
|
|
pub fn post_update(&mut self, config: &AppConfigRef) {
|
2023-08-12 11:18:09 -07:00
|
|
|
if self.queue_build {
|
|
|
|
self.queue_build = false;
|
|
|
|
if let Ok(mut config) = config.write() {
|
|
|
|
config.queue_build = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-09 18:53:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn match_color_for_symbol(match_percent: f32, appearance: &Appearance) -> Color32 {
|
2022-12-06 14:53:32 -08:00
|
|
|
if match_percent == 100.0 {
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance.insert_color
|
2022-12-06 14:53:32 -08:00
|
|
|
} else if match_percent >= 50.0 {
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance.replace_color
|
2022-09-08 14:19:20 -07:00
|
|
|
} else {
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance.delete_color
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-14 10:12:45 -07:00
|
|
|
fn symbol_context_menu_ui(ui: &mut Ui, symbol: &ObjSymbol) {
|
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
|
|
|
if let Some(name) = &symbol.demangled_name {
|
2022-12-06 15:09:19 -08:00
|
|
|
if ui.button(format!("Copy \"{name}\"")).clicked() {
|
2023-05-10 23:47:57 -07:00
|
|
|
ui.output_mut(|output| output.copied_text = name.clone());
|
2022-09-14 10:12:45 -07:00
|
|
|
ui.close_menu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ui.button(format!("Copy \"{}\"", symbol.name)).clicked() {
|
2023-05-10 23:47:57 -07:00
|
|
|
ui.output_mut(|output| output.copied_text = symbol.name.clone());
|
2022-09-14 10:12:45 -07:00
|
|
|
ui.close_menu();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-09 18:53:04 -07:00
|
|
|
fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
|
2022-09-14 10:12:45 -07:00
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
2023-08-09 18:53:04 -07:00
|
|
|
ui.colored_label(appearance.highlight_color, format!("Name: {}", symbol.name));
|
|
|
|
ui.colored_label(appearance.highlight_color, format!("Address: {:x}", symbol.address));
|
2022-12-06 14:53:32 -08:00
|
|
|
if symbol.size_known {
|
2023-08-09 18:53:04 -07:00
|
|
|
ui.colored_label(appearance.highlight_color, format!("Size: {:x}", symbol.size));
|
2022-12-06 14:53:32 -08:00
|
|
|
} else {
|
2023-08-09 18:53:04 -07:00
|
|
|
ui.colored_label(
|
|
|
|
appearance.highlight_color,
|
|
|
|
format!("Size: {:x} (assumed)", symbol.size),
|
|
|
|
);
|
2022-12-06 14:53:32 -08:00
|
|
|
}
|
2022-09-14 10:12:45 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-12 11:18:09 -07:00
|
|
|
#[must_use]
|
2022-09-08 14:19:20 -07:00
|
|
|
fn symbol_ui(
|
|
|
|
ui: &mut Ui,
|
|
|
|
symbol: &ObjSymbol,
|
2022-12-10 17:28:01 -08:00
|
|
|
section: Option<&ObjSection>,
|
2023-08-12 11:18:09 -07:00
|
|
|
state: &mut SymbolViewState,
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance: &Appearance,
|
2023-08-12 11:18:09 -07:00
|
|
|
) -> Option<View> {
|
2023-10-07 10:27:12 -07:00
|
|
|
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) && !state.show_hidden_symbols {
|
|
|
|
return None;
|
|
|
|
}
|
2023-08-12 11:18:09 -07:00
|
|
|
let mut ret = None;
|
2022-09-08 14:19:20 -07:00
|
|
|
let mut job = LayoutJob::default();
|
|
|
|
let name: &str =
|
|
|
|
if let Some(demangled) = &symbol.demangled_name { demangled } else { &symbol.name };
|
|
|
|
let mut selected = false;
|
2023-08-12 11:18:09 -07:00
|
|
|
if let Some(sym) = &state.highlighted_symbol {
|
2022-09-08 14:19:20 -07:00
|
|
|
selected = sym == &symbol.name;
|
|
|
|
}
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text("[", appearance.text_color, &mut job, appearance.code_font.clone());
|
2022-09-08 14:19:20 -07:00
|
|
|
if symbol.flags.0.contains(ObjSymbolFlags::Common) {
|
2023-08-12 11:18:09 -07:00
|
|
|
write_text("c", appearance.replace_color, &mut job, appearance.code_font.clone());
|
2022-09-08 14:19:20 -07:00
|
|
|
} else if symbol.flags.0.contains(ObjSymbolFlags::Global) {
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text("g", appearance.insert_color, &mut job, appearance.code_font.clone());
|
2022-09-08 14:19:20 -07:00
|
|
|
} else if symbol.flags.0.contains(ObjSymbolFlags::Local) {
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text("l", appearance.text_color, &mut job, appearance.code_font.clone());
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
if symbol.flags.0.contains(ObjSymbolFlags::Weak) {
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text("w", appearance.text_color, &mut job, appearance.code_font.clone());
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
2023-10-07 10:27:12 -07:00
|
|
|
if symbol.flags.0.contains(ObjSymbolFlags::Hidden) {
|
|
|
|
write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone());
|
|
|
|
}
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text("] ", appearance.text_color, &mut job, appearance.code_font.clone());
|
2022-12-06 14:53:32 -08:00
|
|
|
if let Some(match_percent) = symbol.match_percent {
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text("(", appearance.text_color, &mut job, appearance.code_font.clone());
|
2022-09-13 16:52:25 -07:00
|
|
|
write_text(
|
2022-12-06 15:09:19 -08:00
|
|
|
&format!("{match_percent:.0}%"),
|
2023-08-09 18:53:04 -07:00
|
|
|
match_color_for_symbol(match_percent, appearance),
|
2022-09-13 16:52:25 -07:00
|
|
|
&mut job,
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance.code_font.clone(),
|
2022-09-13 16:52:25 -07:00
|
|
|
);
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text(") ", appearance.text_color, &mut job, appearance.code_font.clone());
|
2022-09-11 10:52:55 -07:00
|
|
|
}
|
2023-08-09 18:53:04 -07:00
|
|
|
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
|
2022-09-14 10:12:45 -07:00
|
|
|
let response = SelectableLabel::new(selected, job)
|
|
|
|
.ui(ui)
|
|
|
|
.context_menu(|ui| symbol_context_menu_ui(ui, symbol))
|
2023-08-09 18:53:04 -07:00
|
|
|
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol, appearance));
|
2022-09-08 14:19:20 -07:00
|
|
|
if response.clicked() {
|
2022-12-10 17:28:01 -08:00
|
|
|
if let Some(section) = section {
|
2022-11-05 21:49:46 -07:00
|
|
|
if section.kind == ObjSectionKind::Code {
|
2023-08-12 11:18:09 -07:00
|
|
|
state.selected_symbol = Some(SymbolReference {
|
2022-12-10 17:28:01 -08:00
|
|
|
symbol_name: symbol.name.clone(),
|
|
|
|
section_name: section.name.clone(),
|
|
|
|
});
|
2023-08-12 11:18:09 -07:00
|
|
|
ret = Some(View::FunctionDiff);
|
2022-11-05 21:49:46 -07:00
|
|
|
} else if section.kind == ObjSectionKind::Data {
|
2023-08-12 11:18:09 -07:00
|
|
|
state.selected_symbol = Some(SymbolReference {
|
2022-12-10 17:28:01 -08:00
|
|
|
symbol_name: section.name.clone(),
|
|
|
|
section_name: section.name.clone(),
|
|
|
|
});
|
2023-08-12 11:18:09 -07:00
|
|
|
ret = Some(View::DataDiff);
|
2022-11-05 21:49:46 -07:00
|
|
|
}
|
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
} else if response.hovered() {
|
2023-08-12 11:18:09 -07:00
|
|
|
state.highlighted_symbol = Some(symbol.name.clone());
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
2023-08-12 11:18:09 -07:00
|
|
|
ret
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
|
2022-09-20 15:19:44 -07:00
|
|
|
fn symbol_matches_search(symbol: &ObjSymbol, search_str: &str) -> bool {
|
|
|
|
search_str.is_empty()
|
|
|
|
|| symbol.name.contains(search_str)
|
|
|
|
|| symbol
|
|
|
|
.demangled_name
|
|
|
|
.as_ref()
|
|
|
|
.map(|s| s.to_ascii_lowercase().contains(search_str))
|
|
|
|
.unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
2023-08-12 11:18:09 -07:00
|
|
|
#[must_use]
|
2022-09-08 14:19:20 -07:00
|
|
|
fn symbol_list_ui(
|
|
|
|
ui: &mut Ui,
|
|
|
|
obj: &ObjInfo,
|
2023-08-12 11:18:09 -07:00
|
|
|
state: &mut SymbolViewState,
|
2023-01-20 22:11:40 -08:00
|
|
|
lower_search: &str,
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance: &Appearance,
|
2023-08-12 11:18:09 -07:00
|
|
|
) -> Option<View> {
|
|
|
|
let mut ret = None;
|
2022-09-08 14:19:20 -07:00
|
|
|
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
|
|
|
if !obj.common.is_empty() {
|
|
|
|
CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| {
|
|
|
|
for symbol in &obj.common {
|
2023-08-12 11:18:09 -07:00
|
|
|
ret = ret.or(symbol_ui(ui, symbol, None, state, appearance));
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-12-10 17:28:01 -08:00
|
|
|
for section in &obj.sections {
|
2022-09-08 14:19:20 -07:00
|
|
|
CollapsingHeader::new(format!("{} ({:x})", section.name, section.size))
|
|
|
|
.default_open(true)
|
|
|
|
.show(ui, |ui| {
|
2023-08-12 11:18:09 -07:00
|
|
|
if section.kind == ObjSectionKind::Code && state.reverse_fn_order {
|
2022-09-08 14:19:20 -07:00
|
|
|
for symbol in section.symbols.iter().rev() {
|
2023-01-20 22:14:16 -08:00
|
|
|
if !symbol_matches_search(symbol, lower_search) {
|
2022-09-20 15:19:44 -07:00
|
|
|
continue;
|
|
|
|
}
|
2023-08-12 11:18:09 -07:00
|
|
|
ret =
|
|
|
|
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for symbol in §ion.symbols {
|
2023-01-20 22:14:16 -08:00
|
|
|
if !symbol_matches_search(symbol, lower_search) {
|
2022-09-20 15:19:44 -07:00
|
|
|
continue;
|
|
|
|
}
|
2023-08-12 11:18:09 -07:00
|
|
|
ret =
|
|
|
|
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2023-08-12 11:18:09 -07:00
|
|
|
ret
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|
|
|
|
|
2023-08-09 18:53:04 -07:00
|
|
|
fn build_log_ui(ui: &mut Ui, status: &BuildStatus, appearance: &Appearance) {
|
2022-09-14 10:12:45 -07:00
|
|
|
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
2023-08-09 18:53:04 -07:00
|
|
|
ui.colored_label(appearance.replace_color, &status.log);
|
2022-09-14 10:12:45 -07:00
|
|
|
});
|
2022-09-08 14:19:20 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-09 20:43:12 -07:00
|
|
|
fn missing_obj_ui(ui: &mut Ui, appearance: &Appearance) {
|
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
|
|
|
ui.colored_label(appearance.replace_color, "No object configured");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-09 18:53:04 -07:00
|
|
|
pub fn symbol_diff_ui(ui: &mut Ui, state: &mut DiffViewState, appearance: &Appearance) {
|
2023-08-12 11:18:09 -07:00
|
|
|
let DiffViewState { build, current_view, symbol_state, search, .. } = state;
|
2023-08-09 18:53:04 -07:00
|
|
|
let Some(result) = build else {
|
2023-01-20 22:11:40 -08:00
|
|
|
return;
|
|
|
|
};
|
2022-09-08 14:19:20 -07:00
|
|
|
|
2023-01-20 22:11:40 -08:00
|
|
|
// Header
|
|
|
|
let available_width = ui.available_width();
|
|
|
|
let column_width = available_width / 2.0;
|
|
|
|
ui.allocate_ui_with_layout(
|
|
|
|
Vec2 { x: available_width, y: 100.0 },
|
|
|
|
Layout::left_to_right(Align::Min),
|
|
|
|
|ui| {
|
|
|
|
// Left column
|
|
|
|
ui.allocate_ui_with_layout(
|
|
|
|
Vec2 { x: column_width, y: 100.0 },
|
|
|
|
Layout::top_down(Align::Min),
|
|
|
|
|ui| {
|
|
|
|
ui.set_width(column_width);
|
2022-09-08 14:19:20 -07:00
|
|
|
|
2023-01-20 22:11:40 -08:00
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
|
|
|
ui.label("Build target:");
|
|
|
|
if result.first_status.success {
|
2023-09-09 20:43:12 -07:00
|
|
|
if result.first_obj.is_none() {
|
|
|
|
ui.colored_label(appearance.replace_color, "Missing");
|
|
|
|
} else {
|
|
|
|
ui.label("OK");
|
|
|
|
}
|
2023-01-20 22:11:40 -08:00
|
|
|
} else {
|
2023-09-09 20:43:12 -07:00
|
|
|
ui.colored_label(appearance.delete_color, "Fail");
|
2023-01-20 22:11:40 -08:00
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
});
|
2023-01-20 22:11:40 -08:00
|
|
|
|
|
|
|
TextEdit::singleline(search).hint_text("Filter symbols").ui(ui);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
// Right column
|
|
|
|
ui.allocate_ui_with_layout(
|
|
|
|
Vec2 { x: column_width, y: 100.0 },
|
|
|
|
Layout::top_down(Align::Min),
|
|
|
|
|ui| {
|
|
|
|
ui.set_width(column_width);
|
|
|
|
|
|
|
|
ui.scope(|ui| {
|
|
|
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
|
|
|
ui.style_mut().wrap = Some(false);
|
|
|
|
|
|
|
|
ui.label("Build base:");
|
|
|
|
if result.second_status.success {
|
2023-09-09 20:43:12 -07:00
|
|
|
if result.second_obj.is_none() {
|
|
|
|
ui.colored_label(appearance.replace_color, "Missing");
|
|
|
|
} else {
|
|
|
|
ui.label("OK");
|
|
|
|
}
|
2023-01-20 22:11:40 -08:00
|
|
|
} else {
|
2023-09-09 20:43:12 -07:00
|
|
|
ui.colored_label(appearance.delete_color, "Fail");
|
2023-01-20 22:11:40 -08:00
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
});
|
2023-08-12 11:18:09 -07:00
|
|
|
|
2023-10-07 10:27:12 -07:00
|
|
|
if ui.add_enabled(!state.build_running, egui::Button::new("Build")).clicked() {
|
|
|
|
state.queue_build = true;
|
|
|
|
}
|
2023-01-20 22:11:40 -08:00
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
ui.separator();
|
|
|
|
|
|
|
|
// Table
|
2023-08-12 11:18:09 -07:00
|
|
|
let mut ret = None;
|
2023-01-20 22:11:40 -08:00
|
|
|
let lower_search = search.to_ascii_lowercase();
|
|
|
|
StripBuilder::new(ui).size(Size::remainder()).vertical(|mut strip| {
|
|
|
|
strip.strip(|builder| {
|
|
|
|
builder.sizes(Size::remainder(), 2).horizontal(|mut strip| {
|
|
|
|
strip.cell(|ui| {
|
2023-05-10 23:47:57 -07:00
|
|
|
ui.push_id("left", |ui| {
|
|
|
|
if result.first_status.success {
|
|
|
|
if let Some(obj) = &result.first_obj {
|
2023-08-12 11:18:09 -07:00
|
|
|
ret = ret.or(symbol_list_ui(
|
2023-01-20 22:11:40 -08:00
|
|
|
ui,
|
|
|
|
obj,
|
2023-08-12 11:18:09 -07:00
|
|
|
symbol_state,
|
2023-01-20 22:11:40 -08:00
|
|
|
&lower_search,
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance,
|
2023-08-12 11:18:09 -07:00
|
|
|
));
|
2023-09-09 20:43:12 -07:00
|
|
|
} else {
|
|
|
|
missing_obj_ui(ui, appearance);
|
2023-05-10 23:47:57 -07:00
|
|
|
}
|
|
|
|
} else {
|
2023-08-09 18:53:04 -07:00
|
|
|
build_log_ui(ui, &result.first_status, appearance);
|
2023-01-20 22:11:40 -08:00
|
|
|
}
|
2023-05-10 23:47:57 -07:00
|
|
|
});
|
2022-09-08 14:19:20 -07:00
|
|
|
});
|
2023-01-20 22:11:40 -08:00
|
|
|
strip.cell(|ui| {
|
2023-05-10 23:47:57 -07:00
|
|
|
ui.push_id("right", |ui| {
|
|
|
|
if result.second_status.success {
|
|
|
|
if let Some(obj) = &result.second_obj {
|
2023-08-12 11:18:09 -07:00
|
|
|
ret = ret.or(symbol_list_ui(
|
2023-01-20 22:11:40 -08:00
|
|
|
ui,
|
|
|
|
obj,
|
2023-08-12 11:18:09 -07:00
|
|
|
symbol_state,
|
2023-01-20 22:11:40 -08:00
|
|
|
&lower_search,
|
2023-08-09 18:53:04 -07:00
|
|
|
appearance,
|
2023-08-12 11:18:09 -07:00
|
|
|
));
|
2023-09-09 20:43:12 -07:00
|
|
|
} else {
|
|
|
|
missing_obj_ui(ui, appearance);
|
2023-05-10 23:47:57 -07:00
|
|
|
}
|
|
|
|
} else {
|
2023-08-09 18:53:04 -07:00
|
|
|
build_log_ui(ui, &result.second_status, appearance);
|
2023-01-20 22:11:40 -08:00
|
|
|
}
|
2023-05-10 23:47:57 -07:00
|
|
|
});
|
2023-01-20 22:11:40 -08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2023-08-12 11:18:09 -07:00
|
|
|
|
|
|
|
if let Some(view) = ret {
|
|
|
|
*current_view = view;
|
|
|
|
}
|
2022-09-08 14:19:20 -07:00
|
|
|
}
|