diff --git a/README.md b/README.md index a26ac6cd..a9ba8240 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Logo

+

Logo

## About [![CI](https://github.com/obhq/obliteration/actions/workflows/main.yml/badge.svg)](https://github.com/obhq/obliteration/actions/workflows/main.yml) diff --git a/gui/build.rs b/gui/build.rs index b48021ad..2a428ed0 100644 --- a/gui/build.rs +++ b/gui/build.rs @@ -10,7 +10,7 @@ fn main() { .with_style(String::from("fluent-dark")) .with_library_paths(HashMap::from([("root".into(), root.join("ui"))])); - slint_build::compile_with_config(PathBuf::from_iter(["ui", "main.slint"]), config).unwrap(); + slint_build::compile_with_config(PathBuf::from_iter(["ui", "lib.slint"]), config).unwrap(); // Compile resources.rc. #[cfg(windows)] diff --git a/gui/src/main.rs b/gui/src/main.rs index 9c2f406d..734cb5d0 100644 --- a/gui/src/main.rs +++ b/gui/src/main.rs @@ -60,6 +60,114 @@ struct MainProgram { } impl MainProgram { + async fn run_launcher( + graphics: &impl EngineBuilder, + data: &Arc, + profiles: Vec, + ) -> Result, ProgramError> { + // Create window and register callback handlers. + let win = MainWindow::new().map_err(ProgramError::CreateMainWindow)?; + let resolutions = Rc::new(ResolutionModel::default()); + let profiles = Rc::new(ProfileModel::new(profiles, resolutions.clone())); + let exit = Rc::new(Cell::new(None)); + + win.on_settings({ + let win = win.as_weak(); + + move || spawn_handler(&win, |w| MainProgram::settings(w)) + }); + + win.on_report_issue({ + let win = win.as_weak(); + + move || spawn_handler(&win, |w| MainProgram::report_issue(w)) + }); + + win.on_about({ + let win = win.as_weak(); + + move || spawn_handler(&win, |w| MainProgram::about(w)) + }); + + win.on_profile_selected({ + let win = win.as_weak(); + let profiles = profiles.clone(); + + move || { + // TODO: Check if previous profile has unsaved data before switch the profile. + let win = win.unwrap(); + let row: usize = win.get_selected_profile().try_into().unwrap(); + + profiles.select(row, &win); + } + }); + + win.on_save_profile({ + let data = data.clone(); + let win = win.as_weak(); + let profiles = profiles.clone(); + + move || spawn_handler(&win, |w| save_profile(w, data.clone(), profiles.clone())) + }); + + win.on_start_vmm({ + let win = win.as_weak(); + let profiles = profiles.clone(); + let exit = exit.clone(); + let ty = ExitAction::Run; + + move || spawn_handler(&win, |w| start_vmm(w, profiles.clone(), exit.clone(), ty)) + }); + + win.on_start_debug({ + let win = win.as_weak(); + let profiles = profiles.clone(); + let exit = exit.clone(); + let ty = ExitAction::Debug; + + move || spawn_handler(&win, |w| start_vmm(w, profiles.clone(), exit.clone(), ty)) + }); + + // Set window properties. + let physical_devices = ModelRc::new(VecModel::from_iter( + graphics + .physical_devices() + .iter() + .map(|p| SharedString::from(p.name())), + )); + + win.set_devices(physical_devices); + win.set_resolutions(resolutions.into()); + win.set_profiles(profiles.clone().into()); + + // Load selected profile. + let row: usize = win.get_selected_profile().try_into().unwrap(); + + profiles.select(row, &win); + + // Run the window. + win.show().map_err(ProgramError::ShowMainWindow)?; + win.set_center().map_err(ProgramError::CenterMainWindow)?; + win.wait().await; + + // Extract window states. + let profile = win.get_selected_profile(); + + drop(win); + + // Check how we exit. + let exit = match Rc::into_inner(exit).unwrap().into_inner() { + Some(v) => v, + None => return Ok(None), + }; + + // Get selected profile. + let mut profiles = Rc::into_inner(profiles).unwrap().into_inner(); + let profile = profiles.remove(profile.try_into().unwrap()); + + Ok(Some((profile, exit))) + } + async fn settings(main: MainWindow) { // Setup window. let win = match SettingsWindow::new() { @@ -71,6 +179,12 @@ impl MainProgram { } }; + #[cfg(target_os = "macos")] + win.set_graphics_debug_layer_name("MTL_DEBUG_LAYER".into()); + + #[cfg(not(target_os = "macos"))] + win.set_graphics_debug_layer_name("VK_LAYER_KHRONOS_validation".into()); + // Run the window. if let Err(e) = win.show() { let m = slint::format!("Failed to show settings window: {}.", e.display()); @@ -237,7 +351,7 @@ impl App for MainProgram { // TODO: Select last used profile. (profiles.pop().unwrap(), Some(v)) } else { - let (profile, exit) = match run_launcher(&graphics, &data, profiles).await? { + let (profile, exit) = match Self::run_launcher(&graphics, &data, profiles).await? { Some(v) => v, None => return Ok(()), }; @@ -318,114 +432,6 @@ impl App for MainProgram { } } -async fn run_launcher( - graphics: &impl EngineBuilder, - data: &Arc, - profiles: Vec, -) -> Result, ProgramError> { - // Create window and register callback handlers. - let win = MainWindow::new().map_err(ProgramError::CreateMainWindow)?; - let resolutions = Rc::new(ResolutionModel::default()); - let profiles = Rc::new(ProfileModel::new(profiles, resolutions.clone())); - let exit = Rc::new(Cell::new(None)); - - win.on_settings({ - let win = win.as_weak(); - - move || spawn_handler(&win, |w| MainProgram::settings(w)) - }); - - win.on_report_issue({ - let win = win.as_weak(); - - move || spawn_handler(&win, |w| MainProgram::report_issue(w)) - }); - - win.on_about({ - let win = win.as_weak(); - - move || spawn_handler(&win, |w| MainProgram::about(w)) - }); - - win.on_profile_selected({ - let win = win.as_weak(); - let profiles = profiles.clone(); - - move || { - // TODO: Check if previous profile has unsaved data before switch the profile. - let win = win.unwrap(); - let row: usize = win.get_selected_profile().try_into().unwrap(); - - profiles.select(row, &win); - } - }); - - win.on_save_profile({ - let data = data.clone(); - let win = win.as_weak(); - let profiles = profiles.clone(); - - move || spawn_handler(&win, |w| save_profile(w, data.clone(), profiles.clone())) - }); - - win.on_start_vmm({ - let win = win.as_weak(); - let profiles = profiles.clone(); - let exit = exit.clone(); - let ty = ExitAction::Run; - - move || spawn_handler(&win, |w| start_vmm(w, profiles.clone(), exit.clone(), ty)) - }); - - win.on_start_debug({ - let win = win.as_weak(); - let profiles = profiles.clone(); - let exit = exit.clone(); - let ty = ExitAction::Debug; - - move || spawn_handler(&win, |w| start_vmm(w, profiles.clone(), exit.clone(), ty)) - }); - - // Set window properties. - let physical_devices = ModelRc::new(VecModel::from_iter( - graphics - .physical_devices() - .iter() - .map(|p| SharedString::from(p.name())), - )); - - win.set_devices(physical_devices); - win.set_resolutions(resolutions.into()); - win.set_profiles(profiles.clone().into()); - - // Load selected profile. - let row: usize = win.get_selected_profile().try_into().unwrap(); - - profiles.select(row, &win); - - // Run the window. - win.show().map_err(ProgramError::ShowMainWindow)?; - win.set_center().map_err(ProgramError::CenterMainWindow)?; - win.wait().await; - - // Extract window states. - let profile = win.get_selected_profile(); - - drop(win); - - // Check how we exit. - let exit = match Rc::into_inner(exit).unwrap().into_inner() { - Some(v) => v, - None => return Ok(None), - }; - - // Get selected profile. - let mut profiles = Rc::into_inner(profiles).unwrap().into_inner(); - let profile = profiles.remove(profile.try_into().unwrap()); - - Ok(Some((profile, exit))) -} - async fn wait_for_debugger(addr: SocketAddr) -> Result, ProgramError> { // Start server. let server = TcpListener::bind(addr) diff --git a/gui/ui/about.slint b/gui/ui/about.slint index c98e3762..c7862104 100644 --- a/gui/ui/about.slint +++ b/gui/ui/about.slint @@ -3,7 +3,7 @@ import { AboutSlint, VerticalBox, TabWidget } from "std-widgets.slint"; component Obliteration { VerticalBox { Image { - source: @image-url("about/logo.png"); + source: @image-url("@root/assets/logo.png"); } Text { diff --git a/gui/ui/about/logo.png b/gui/ui/assets/logo.png similarity index 100% rename from gui/ui/about/logo.png rename to gui/ui/assets/logo.png diff --git a/gui/ui/main/monitor.svg b/gui/ui/assets/monitor.svg similarity index 100% rename from gui/ui/main/monitor.svg rename to gui/ui/assets/monitor.svg diff --git a/gui/ui/lib.slint b/gui/ui/lib.slint new file mode 100644 index 00000000..f0c5f199 --- /dev/null +++ b/gui/ui/lib.slint @@ -0,0 +1,6 @@ +export { AboutWindow } from "about.slint"; +export { WaitForDebugger } from "debug.slint"; +export { ErrorWindow } from "error.slint"; +export { MainWindow } from "main.slint"; +export { SettingsWindow } from "settings.slint"; +export { InstallFirmware, SetupWizard } from "setup.slint"; diff --git a/gui/ui/main.slint b/gui/ui/main.slint index 648485a3..0d173d71 100644 --- a/gui/ui/main.slint +++ b/gui/ui/main.slint @@ -1,13 +1,7 @@ import { VerticalBox, HorizontalBox, Button, ComboBox } from "std-widgets.slint"; +import { TabBar, TabContainer } from "@root/widgets/tab.slint"; import { DisplayTab } from "main/display.slint"; import { CpuTab } from "main/cpu.slint"; -import { TabBar, TabContainer } from "main/tab.slint"; - -export { AboutWindow } from "about.slint"; -export { WaitForDebugger } from "debug.slint"; -export { ErrorWindow } from "error.slint"; -export { SettingsWindow } from "settings.slint"; -export { InstallFirmware, SetupWizard } from "setup.slint"; export component MainWindow inherits Window { in property <[string]> devices; @@ -72,7 +66,7 @@ export component MainWindow inherits Window { tab := TabBar { tabs: [ - { text: "Display", icon: @image-url("main/monitor.svg") }, + { text: "Display", icon: @image-url("@root/assets/monitor.svg") }, { text: "CPU", icon: @image-url("main/cpu-64-bit.svg") } ]; } diff --git a/gui/ui/settings.slint b/gui/ui/settings.slint index 4a70c563..77f657d2 100644 --- a/gui/ui/settings.slint +++ b/gui/ui/settings.slint @@ -1,3 +1,27 @@ -export component SettingsWindow inherits Window { +import { StandardButton } from "std-widgets.slint"; +import { TabBar, TabContainer } from "@root/widgets/tab.slint"; +import { GraphicsTab } from "settings/graphics.slint"; + +export component SettingsWindow inherits Dialog { + in property graphics-debug-layer-name: "VK_LAYER_KHRONOS_validation"; + title: "Settings"; + + TabContainer { + tab := TabBar { + tabs: [{ text: "Graphics", icon: @image-url("@root/assets/monitor.svg") }]; + } + + if tab.current-page == 0: GraphicsTab { + debug-layer-name: root.graphics-debug-layer-name; + } + } + + StandardButton { + kind: ok; + } + + StandardButton { + kind: cancel; + } } diff --git a/gui/ui/settings/graphics.slint b/gui/ui/settings/graphics.slint new file mode 100644 index 00000000..573fbc86 --- /dev/null +++ b/gui/ui/settings/graphics.slint @@ -0,0 +1,11 @@ +import { CheckBox, VerticalBox } from "std-widgets.slint"; + +export component GraphicsTab { + in property debug-layer-name; + + VerticalBox { + CheckBox { + text: "Enable \{debug-layer-name}"; + } + } +} diff --git a/gui/ui/main/tab.slint b/gui/ui/widgets/tab.slint similarity index 100% rename from gui/ui/main/tab.slint rename to gui/ui/widgets/tab.slint diff --git a/logo.png b/logo.png deleted file mode 100644 index b465ed55..00000000 Binary files a/logo.png and /dev/null differ