2022-09-08 21:19:20 +00:00
|
|
|
use std::{
|
|
|
|
default::Default,
|
|
|
|
path::{Path, PathBuf},
|
2022-12-06 22:53:32 +00:00
|
|
|
rc::Rc,
|
2022-09-08 21:19:20 +00:00
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
2022-12-06 22:53:32 +00:00
|
|
|
Arc, Mutex, RwLock,
|
2022-09-08 21:19:20 +00:00
|
|
|
},
|
|
|
|
time::Duration,
|
|
|
|
};
|
|
|
|
|
2023-08-08 00:11:56 +00:00
|
|
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
2022-09-08 21:19:20 +00:00
|
|
|
use notify::{RecursiveMode, Watcher};
|
2023-08-10 01:53:04 +00:00
|
|
|
use time::UtcOffset;
|
2022-09-08 21:19:20 +00:00
|
|
|
|
|
|
|
use crate::{
|
2023-09-10 03:43:12 +00:00
|
|
|
app_config::{deserialize_config, AppConfigVersion},
|
2023-08-12 18:18:09 +00:00
|
|
|
config::{
|
|
|
|
build_globset, load_project_config, ProjectObject, ProjectObjectNode, CONFIG_FILENAMES,
|
2022-09-08 21:19:20 +00:00
|
|
|
},
|
2023-08-12 18:18:09 +00:00
|
|
|
jobs::{objdiff::start_build, Job, JobQueue, JobResult, JobStatus},
|
2022-09-08 21:19:20 +00:00
|
|
|
views::{
|
2023-08-10 01:53:04 +00:00
|
|
|
appearance::{appearance_window, Appearance},
|
2023-09-10 03:43:12 +00:00
|
|
|
config::{config_ui, project_window, ConfigViewState, DEFAULT_WATCH_PATTERNS},
|
2023-08-08 00:11:56 +00:00
|
|
|
data_diff::data_diff_ui,
|
2023-08-10 01:53:04 +00:00
|
|
|
demangle::{demangle_window, DemangleViewState},
|
2023-08-08 00:11:56 +00:00
|
|
|
function_diff::function_diff_ui,
|
|
|
|
jobs::jobs_ui,
|
2023-08-10 01:53:04 +00:00
|
|
|
symbol_diff::{symbol_diff_ui, DiffViewState, View},
|
2022-09-08 21:19:20 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2023-08-10 01:53:04 +00:00
|
|
|
#[derive(Default)]
|
2022-09-08 21:19:20 +00:00
|
|
|
pub struct ViewState {
|
2023-08-09 23:39:06 +00:00
|
|
|
pub jobs: JobQueue,
|
2023-08-12 18:18:09 +00:00
|
|
|
pub config_state: ConfigViewState,
|
2023-08-10 01:53:04 +00:00
|
|
|
pub demangle_state: DemangleViewState,
|
|
|
|
pub diff_state: DiffViewState,
|
2023-08-12 18:18:09 +00:00
|
|
|
pub show_appearance_config: bool,
|
|
|
|
pub show_demangle: bool,
|
2023-08-10 01:53:04 +00:00
|
|
|
pub show_project_config: bool,
|
2022-09-20 23:04:32 +00:00
|
|
|
}
|
|
|
|
|
2023-09-03 13:28:22 +00:00
|
|
|
/// The configuration for a single object file.
|
|
|
|
#[derive(Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
|
|
pub struct ObjectConfig {
|
|
|
|
pub name: String,
|
2023-09-10 03:43:12 +00:00
|
|
|
pub target_path: Option<PathBuf>,
|
|
|
|
pub base_path: Option<PathBuf>,
|
2023-09-03 13:28:22 +00:00
|
|
|
pub reverse_fn_order: Option<bool>,
|
2023-09-10 03:43:12 +00:00
|
|
|
pub complete: Option<bool>,
|
2023-09-03 13:28:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn bool_true() -> bool { true }
|
|
|
|
|
2023-09-10 03:43:12 +00:00
|
|
|
#[derive(Clone, serde::Deserialize, serde::Serialize)]
|
2022-09-08 21:19:20 +00:00
|
|
|
pub struct AppConfig {
|
2023-09-10 03:43:12 +00:00
|
|
|
// TODO: https://github.com/ron-rs/ron/pull/455
|
|
|
|
// #[serde(flatten)]
|
|
|
|
// pub version: AppConfigVersion,
|
|
|
|
pub version: u32,
|
2022-12-06 22:53:32 +00:00
|
|
|
pub custom_make: Option<String>,
|
2022-09-12 00:31:58 +00:00
|
|
|
pub selected_wsl_distro: Option<String>,
|
2022-09-08 21:19:20 +00:00
|
|
|
pub project_dir: Option<PathBuf>,
|
2022-09-13 23:52:25 +00:00
|
|
|
pub target_obj_dir: Option<PathBuf>,
|
|
|
|
pub base_obj_dir: Option<PathBuf>,
|
2023-09-03 13:28:22 +00:00
|
|
|
pub selected_obj: Option<ObjectConfig>,
|
2022-09-13 23:52:25 +00:00
|
|
|
pub build_target: bool,
|
2023-09-03 13:28:22 +00:00
|
|
|
#[serde(default = "bool_true")]
|
|
|
|
pub rebuild_on_changes: bool,
|
2022-12-06 22:53:32 +00:00
|
|
|
pub auto_update_check: bool,
|
2023-08-08 00:11:56 +00:00
|
|
|
pub watch_patterns: Vec<Glob>,
|
2023-10-03 17:52:16 +00:00
|
|
|
pub recent_projects: Vec<PathBuf>,
|
2023-08-10 01:53:04 +00:00
|
|
|
|
2023-08-08 00:11:56 +00:00
|
|
|
#[serde(skip)]
|
2023-08-12 18:18:09 +00:00
|
|
|
pub objects: Vec<ProjectObject>,
|
2023-08-08 00:11:56 +00:00
|
|
|
#[serde(skip)]
|
2023-08-12 18:18:09 +00:00
|
|
|
pub object_nodes: Vec<ProjectObjectNode>,
|
2023-08-08 00:11:56 +00:00
|
|
|
#[serde(skip)]
|
2023-08-10 01:53:04 +00:00
|
|
|
pub watcher_change: bool,
|
|
|
|
#[serde(skip)]
|
|
|
|
pub config_change: bool,
|
2023-08-11 05:36:22 +00:00
|
|
|
#[serde(skip)]
|
|
|
|
pub obj_change: bool,
|
2023-08-12 18:18:09 +00:00
|
|
|
#[serde(skip)]
|
|
|
|
pub queue_build: bool,
|
2023-09-03 13:28:22 +00:00
|
|
|
#[serde(skip)]
|
|
|
|
pub project_config_loaded: bool,
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
|
2023-09-10 03:43:12 +00:00
|
|
|
impl Default for AppConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
version: AppConfigVersion::default().version,
|
|
|
|
custom_make: None,
|
|
|
|
selected_wsl_distro: None,
|
|
|
|
project_dir: None,
|
|
|
|
target_obj_dir: None,
|
|
|
|
base_obj_dir: None,
|
|
|
|
selected_obj: None,
|
|
|
|
build_target: false,
|
|
|
|
rebuild_on_changes: true,
|
|
|
|
auto_update_check: true,
|
|
|
|
watch_patterns: DEFAULT_WATCH_PATTERNS.iter().map(|s| Glob::new(s).unwrap()).collect(),
|
2023-10-03 17:52:16 +00:00
|
|
|
recent_projects: vec![],
|
2023-09-10 03:43:12 +00:00
|
|
|
objects: vec![],
|
|
|
|
object_nodes: vec![],
|
|
|
|
watcher_change: false,
|
|
|
|
config_change: false,
|
|
|
|
obj_change: false,
|
|
|
|
queue_build: false,
|
|
|
|
project_config_loaded: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-11 05:36:22 +00:00
|
|
|
impl AppConfig {
|
|
|
|
pub fn set_project_dir(&mut self, path: PathBuf) {
|
2023-10-03 17:52:16 +00:00
|
|
|
self.recent_projects.retain(|p| p != &path);
|
|
|
|
if self.recent_projects.len() > 9 {
|
|
|
|
self.recent_projects.truncate(9);
|
|
|
|
}
|
|
|
|
self.recent_projects.insert(0, path.clone());
|
2023-08-11 05:36:22 +00:00
|
|
|
self.project_dir = Some(path);
|
|
|
|
self.target_obj_dir = None;
|
|
|
|
self.base_obj_dir = None;
|
2023-09-03 13:28:22 +00:00
|
|
|
self.selected_obj = None;
|
2023-08-11 05:36:22 +00:00
|
|
|
self.build_target = false;
|
2023-08-12 18:18:09 +00:00
|
|
|
self.objects.clear();
|
|
|
|
self.object_nodes.clear();
|
2023-08-11 05:36:22 +00:00
|
|
|
self.watcher_change = true;
|
|
|
|
self.config_change = true;
|
|
|
|
self.obj_change = true;
|
2023-08-12 18:18:09 +00:00
|
|
|
self.queue_build = false;
|
2023-09-03 13:28:22 +00:00
|
|
|
self.project_config_loaded = false;
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_target_obj_dir(&mut self, path: PathBuf) {
|
|
|
|
self.target_obj_dir = Some(path);
|
2023-09-03 13:28:22 +00:00
|
|
|
self.selected_obj = None;
|
2023-08-11 05:36:22 +00:00
|
|
|
self.obj_change = true;
|
2023-08-12 18:18:09 +00:00
|
|
|
self.queue_build = false;
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_base_obj_dir(&mut self, path: PathBuf) {
|
|
|
|
self.base_obj_dir = Some(path);
|
2023-09-03 13:28:22 +00:00
|
|
|
self.selected_obj = None;
|
2023-08-11 05:36:22 +00:00
|
|
|
self.obj_change = true;
|
2023-08-12 18:18:09 +00:00
|
|
|
self.queue_build = false;
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
|
2023-09-03 13:28:22 +00:00
|
|
|
pub fn set_selected_obj(&mut self, object: ObjectConfig) {
|
|
|
|
self.selected_obj = Some(object);
|
2023-08-11 05:36:22 +00:00
|
|
|
self.obj_change = true;
|
2023-08-12 18:18:09 +00:00
|
|
|
self.queue_build = false;
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
|
|
|
|
2023-08-12 18:18:09 +00:00
|
|
|
pub type AppConfigRef = Arc<RwLock<AppConfig>>;
|
|
|
|
|
2023-08-10 01:53:04 +00:00
|
|
|
#[derive(Default)]
|
2022-09-08 21:19:20 +00:00
|
|
|
pub struct App {
|
2023-08-10 01:53:04 +00:00
|
|
|
appearance: Appearance,
|
2022-09-08 21:19:20 +00:00
|
|
|
view_state: ViewState,
|
2023-08-12 18:18:09 +00:00
|
|
|
config: AppConfigRef,
|
2022-09-08 21:19:20 +00:00
|
|
|
modified: Arc<AtomicBool>,
|
2023-08-08 00:11:56 +00:00
|
|
|
config_modified: Arc<AtomicBool>,
|
2022-09-08 21:19:20 +00:00
|
|
|
watcher: Option<notify::RecommendedWatcher>,
|
2022-12-06 22:53:32 +00:00
|
|
|
relaunch_path: Rc<Mutex<Option<PathBuf>>>,
|
|
|
|
should_relaunch: bool,
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
|
|
|
|
2023-09-10 03:43:12 +00:00
|
|
|
pub const APPEARANCE_KEY: &str = "appearance";
|
|
|
|
pub const CONFIG_KEY: &str = "app_config";
|
2022-09-08 21:19:20 +00:00
|
|
|
|
|
|
|
impl App {
|
|
|
|
/// Called once before the first frame.
|
2022-12-06 22:53:32 +00:00
|
|
|
pub fn new(
|
|
|
|
cc: &eframe::CreationContext<'_>,
|
|
|
|
utc_offset: UtcOffset,
|
|
|
|
relaunch_path: Rc<Mutex<Option<PathBuf>>>,
|
|
|
|
) -> Self {
|
2022-09-08 21:19:20 +00:00
|
|
|
// This is also where you can customized the look at feel of egui using
|
|
|
|
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
|
|
|
|
|
|
|
|
// Load previous app state (if any).
|
|
|
|
// Note that you must enable the `persistence` feature for this to work.
|
2023-08-10 01:53:04 +00:00
|
|
|
let mut app = Self::default();
|
2022-09-08 21:19:20 +00:00
|
|
|
if let Some(storage) = cc.storage {
|
2023-08-10 01:53:04 +00:00
|
|
|
if let Some(appearance) = eframe::get_value::<Appearance>(storage, APPEARANCE_KEY) {
|
|
|
|
app.appearance = appearance;
|
|
|
|
}
|
2023-09-10 03:43:12 +00:00
|
|
|
if let Some(mut config) = deserialize_config(storage) {
|
2023-08-10 01:53:04 +00:00
|
|
|
if config.project_dir.is_some() {
|
|
|
|
config.config_change = true;
|
|
|
|
config.watcher_change = true;
|
|
|
|
app.modified.store(true, Ordering::Relaxed);
|
|
|
|
}
|
2023-08-12 18:18:09 +00:00
|
|
|
app.view_state.config_state.queue_check_update = config.auto_update_check;
|
2023-08-10 01:53:04 +00:00
|
|
|
app.config = Arc::new(RwLock::new(config));
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
|
|
|
}
|
2023-08-10 01:53:04 +00:00
|
|
|
app.appearance.utc_offset = utc_offset;
|
|
|
|
app.relaunch_path = relaunch_path;
|
|
|
|
app
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
2023-08-11 05:36:22 +00:00
|
|
|
|
|
|
|
fn pre_update(&mut self) {
|
|
|
|
let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state;
|
|
|
|
|
2023-08-12 18:18:09 +00:00
|
|
|
let mut results = vec![];
|
2023-08-11 05:36:22 +00:00
|
|
|
for (job, result) in jobs.iter_finished() {
|
|
|
|
match result {
|
|
|
|
Ok(result) => {
|
|
|
|
log::info!("Job {} finished", job.id);
|
|
|
|
match result {
|
|
|
|
JobResult::None => {
|
|
|
|
if let Some(err) = &job.status.read().unwrap().error {
|
|
|
|
log::error!("{:?}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
JobResult::Update(state) => {
|
|
|
|
if let Ok(mut guard) = self.relaunch_path.lock() {
|
|
|
|
*guard = Some(state.exe_path);
|
|
|
|
}
|
|
|
|
self.should_relaunch = true;
|
|
|
|
}
|
2023-08-12 18:18:09 +00:00
|
|
|
_ => results.push(result),
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
let err = if let Some(msg) = err.downcast_ref::<&'static str>() {
|
|
|
|
anyhow::Error::msg(*msg)
|
|
|
|
} else if let Some(msg) = err.downcast_ref::<String>() {
|
|
|
|
anyhow::Error::msg(msg.clone())
|
|
|
|
} else {
|
|
|
|
anyhow::Error::msg("Thread panicked")
|
|
|
|
};
|
|
|
|
let result = job.status.write();
|
|
|
|
if let Ok(mut guard) = result {
|
|
|
|
guard.error = Some(err);
|
|
|
|
} else {
|
|
|
|
drop(result);
|
|
|
|
job.status = Arc::new(RwLock::new(JobStatus {
|
|
|
|
title: "Error".to_string(),
|
|
|
|
progress_percent: 0.0,
|
|
|
|
progress_items: None,
|
|
|
|
status: "".to_string(),
|
|
|
|
error: Some(err),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-12 18:18:09 +00:00
|
|
|
jobs.results.append(&mut results);
|
2023-08-11 05:36:22 +00:00
|
|
|
jobs.clear_finished();
|
2023-08-12 18:18:09 +00:00
|
|
|
|
|
|
|
diff_state.pre_update(jobs, &self.config);
|
|
|
|
config_state.pre_update(jobs);
|
|
|
|
debug_assert!(jobs.results.is_empty());
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn post_update(&mut self) {
|
|
|
|
let ViewState { jobs, diff_state, config_state, .. } = &mut self.view_state;
|
2023-08-12 18:18:09 +00:00
|
|
|
config_state.post_update(jobs, &self.config);
|
|
|
|
diff_state.post_update(jobs, &self.config);
|
|
|
|
|
2023-08-11 05:36:22 +00:00
|
|
|
let Ok(mut config) = self.config.write() else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
let config = &mut *config;
|
|
|
|
|
|
|
|
if self.config_modified.swap(false, Ordering::Relaxed) {
|
|
|
|
config.config_change = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.config_change {
|
|
|
|
config.config_change = false;
|
|
|
|
match load_project_config(config) {
|
|
|
|
Ok(()) => config_state.load_error = None,
|
|
|
|
Err(e) => {
|
|
|
|
log::error!("Failed to load project config: {e}");
|
|
|
|
config_state.load_error = Some(format!("{e}"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.watcher_change {
|
|
|
|
drop(self.watcher.take());
|
|
|
|
|
|
|
|
if let Some(project_dir) = &config.project_dir {
|
|
|
|
if !config.watch_patterns.is_empty() {
|
|
|
|
match build_globset(&config.watch_patterns)
|
|
|
|
.map_err(anyhow::Error::new)
|
|
|
|
.and_then(|globset| {
|
|
|
|
create_watcher(
|
|
|
|
self.modified.clone(),
|
|
|
|
self.config_modified.clone(),
|
|
|
|
project_dir,
|
|
|
|
globset,
|
|
|
|
)
|
|
|
|
.map_err(anyhow::Error::new)
|
|
|
|
}) {
|
|
|
|
Ok(watcher) => self.watcher = Some(watcher),
|
|
|
|
Err(e) => log::error!("Failed to create watcher: {e}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
config.watcher_change = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.obj_change {
|
|
|
|
*diff_state = Default::default();
|
2023-09-03 13:28:22 +00:00
|
|
|
if config.selected_obj.is_some() {
|
2023-08-12 18:18:09 +00:00
|
|
|
config.queue_build = true;
|
|
|
|
}
|
2023-08-11 05:36:22 +00:00
|
|
|
config.obj_change = false;
|
|
|
|
}
|
|
|
|
|
2023-09-03 13:28:22 +00:00
|
|
|
if self.modified.swap(false, Ordering::Relaxed) && config.rebuild_on_changes {
|
2023-08-12 18:18:09 +00:00
|
|
|
config.queue_build = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't clear `queue_build` if a build is running. A file may have been modified during
|
|
|
|
// the build, so we'll start another build after the current one finishes.
|
2023-09-03 13:28:22 +00:00
|
|
|
if config.queue_build && config.selected_obj.is_some() && !jobs.is_running(Job::ObjDiff) {
|
2023-08-12 18:18:09 +00:00
|
|
|
jobs.push(start_build(self.config.clone()));
|
|
|
|
config.queue_build = false;
|
2023-08-11 05:36:22 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl eframe::App for App {
|
|
|
|
/// Called each time the UI needs repainting, which may be many times per second.
|
|
|
|
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
2022-12-06 22:53:32 +00:00
|
|
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
|
|
|
if self.should_relaunch {
|
|
|
|
frame.close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-11 05:36:22 +00:00
|
|
|
self.pre_update();
|
|
|
|
|
2023-08-10 01:53:04 +00:00
|
|
|
let Self { config, appearance, view_state, .. } = self;
|
|
|
|
ctx.set_style(appearance.apply(ctx.style().as_ref()));
|
2022-09-08 21:19:20 +00:00
|
|
|
|
2023-08-10 01:53:04 +00:00
|
|
|
let ViewState {
|
|
|
|
jobs,
|
|
|
|
show_appearance_config,
|
|
|
|
demangle_state,
|
|
|
|
show_demangle,
|
|
|
|
diff_state,
|
|
|
|
config_state,
|
|
|
|
show_project_config,
|
|
|
|
} = view_state;
|
2022-12-06 22:53:32 +00:00
|
|
|
|
2022-09-08 21:19:20 +00:00
|
|
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
|
|
|
egui::menu::bar(ui, |ui| {
|
|
|
|
ui.menu_button("File", |ui| {
|
2023-10-03 17:52:16 +00:00
|
|
|
let recent_projects = if let Ok(guard) = config.read() {
|
|
|
|
guard.recent_projects.clone()
|
|
|
|
} else {
|
|
|
|
vec![]
|
|
|
|
};
|
|
|
|
if recent_projects.is_empty() {
|
|
|
|
ui.add_enabled(false, egui::Button::new("Recent Projects…"));
|
|
|
|
} else {
|
|
|
|
ui.menu_button("Recent Projects…", |ui| {
|
|
|
|
for path in recent_projects {
|
|
|
|
if ui.button(format!("{}", path.display())).clicked() {
|
|
|
|
config.write().unwrap().set_project_dir(path);
|
|
|
|
ui.close_menu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-08-08 00:11:56 +00:00
|
|
|
if ui.button("Appearance…").clicked() {
|
2023-08-10 01:53:04 +00:00
|
|
|
*show_appearance_config = !*show_appearance_config;
|
2023-08-11 05:36:22 +00:00
|
|
|
ui.close_menu();
|
2022-12-06 22:53:32 +00:00
|
|
|
}
|
2022-09-08 21:19:20 +00:00
|
|
|
if ui.button("Quit").clicked() {
|
|
|
|
frame.close();
|
|
|
|
}
|
2022-12-06 22:53:32 +00:00
|
|
|
});
|
|
|
|
ui.menu_button("Tools", |ui| {
|
2023-08-08 00:11:56 +00:00
|
|
|
if ui.button("Demangle…").clicked() {
|
2023-08-10 01:53:04 +00:00
|
|
|
*show_demangle = !*show_demangle;
|
2023-08-11 05:36:22 +00:00
|
|
|
ui.close_menu();
|
2022-09-11 17:52:55 +00:00
|
|
|
}
|
2022-09-08 21:19:20 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-08-12 18:18:09 +00:00
|
|
|
let build_success = matches!(&diff_state.build, Some(b) if b.first_status.success && b.second_status.success);
|
|
|
|
if diff_state.current_view == View::FunctionDiff && build_success {
|
2022-09-08 21:19:20 +00:00
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
2023-08-12 18:18:09 +00:00
|
|
|
function_diff_ui(ui, diff_state, appearance);
|
2022-09-08 21:19:20 +00:00
|
|
|
});
|
2023-08-12 18:18:09 +00:00
|
|
|
} else if diff_state.current_view == View::DataDiff && build_success {
|
2022-11-06 04:49:46 +00:00
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
2023-08-12 18:18:09 +00:00
|
|
|
data_diff_ui(ui, diff_state, appearance);
|
2022-11-06 04:49:46 +00:00
|
|
|
});
|
2022-09-08 21:19:20 +00:00
|
|
|
} else {
|
|
|
|
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
2023-08-11 05:36:22 +00:00
|
|
|
egui::ScrollArea::both().show(ui, |ui| {
|
2023-08-12 18:18:09 +00:00
|
|
|
config_ui(ui, config, show_project_config, config_state, appearance);
|
2023-08-11 05:36:22 +00:00
|
|
|
jobs_ui(ui, jobs, appearance);
|
|
|
|
});
|
2022-09-08 21:19:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
2023-08-10 01:53:04 +00:00
|
|
|
symbol_diff_ui(ui, diff_state, appearance);
|
2022-09-08 21:19:20 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-08-10 01:53:04 +00:00
|
|
|
project_window(ctx, config, show_project_config, config_state, appearance);
|
|
|
|
appearance_window(ctx, show_appearance_config, appearance);
|
|
|
|
demangle_window(ctx, show_demangle, demangle_state, appearance);
|
2022-09-11 17:52:55 +00:00
|
|
|
|
2023-08-11 05:36:22 +00:00
|
|
|
self.post_update();
|
|
|
|
|
2022-09-12 00:31:58 +00:00
|
|
|
// Windows + request_repaint_after breaks dialogs:
|
|
|
|
// https://github.com/emilk/egui/issues/2003
|
2023-08-11 05:36:22 +00:00
|
|
|
if cfg!(windows) || self.view_state.jobs.any_running() {
|
2022-09-08 21:19:20 +00:00
|
|
|
ctx.request_repaint();
|
|
|
|
} else {
|
|
|
|
ctx.request_repaint_after(Duration::from_millis(100));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Called by the frame work to save state before shutdown.
|
|
|
|
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
|
|
|
if let Ok(config) = self.config.read() {
|
|
|
|
eframe::set_value(storage, CONFIG_KEY, &*config);
|
|
|
|
}
|
2023-08-10 01:53:04 +00:00
|
|
|
eframe::set_value(storage, APPEARANCE_KEY, &self.appearance);
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_watcher(
|
|
|
|
modified: Arc<AtomicBool>,
|
2023-08-08 00:11:56 +00:00
|
|
|
config_modified: Arc<AtomicBool>,
|
2022-09-08 21:19:20 +00:00
|
|
|
project_dir: &Path,
|
2023-08-08 00:11:56 +00:00
|
|
|
patterns: GlobSet,
|
2022-09-08 21:19:20 +00:00
|
|
|
) -> notify::Result<notify::RecommendedWatcher> {
|
2023-08-08 00:11:56 +00:00
|
|
|
let mut config_patterns = GlobSetBuilder::new();
|
|
|
|
for filename in CONFIG_FILENAMES {
|
2023-08-14 03:56:13 +00:00
|
|
|
config_patterns.add(Glob::new(filename).unwrap());
|
2023-08-08 00:11:56 +00:00
|
|
|
}
|
|
|
|
let config_patterns = config_patterns.build().unwrap();
|
|
|
|
|
2023-08-14 03:56:13 +00:00
|
|
|
let base_dir = project_dir.to_owned();
|
2022-09-08 21:19:20 +00:00
|
|
|
let mut watcher =
|
|
|
|
notify::recommended_watcher(move |res: notify::Result<notify::Event>| match res {
|
|
|
|
Ok(event) => {
|
2023-08-14 03:56:13 +00:00
|
|
|
if matches!(
|
|
|
|
event.kind,
|
|
|
|
notify::EventKind::Modify(..)
|
|
|
|
| notify::EventKind::Create(..)
|
|
|
|
| notify::EventKind::Remove(..)
|
|
|
|
) {
|
2023-08-08 00:11:56 +00:00
|
|
|
for path in &event.paths {
|
2023-08-14 03:56:13 +00:00
|
|
|
let Ok(path) = path.strip_prefix(&base_dir) else {
|
|
|
|
continue;
|
|
|
|
};
|
2023-08-08 00:11:56 +00:00
|
|
|
if config_patterns.is_match(path) {
|
|
|
|
config_modified.store(true, Ordering::Relaxed);
|
2023-08-14 03:56:13 +00:00
|
|
|
} else if patterns.is_match(path) {
|
2023-08-08 00:11:56 +00:00
|
|
|
modified.store(true, Ordering::Relaxed);
|
|
|
|
}
|
2022-09-08 21:19:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-07-06 14:37:57 +00:00
|
|
|
Err(e) => log::error!("watch error: {e:?}"),
|
2022-09-08 21:19:20 +00:00
|
|
|
})?;
|
|
|
|
watcher.watch(project_dir, RecursiveMode::Recursive)?;
|
|
|
|
Ok(watcher)
|
|
|
|
}
|