added close-confirmation for editor. TODO: make this behaviour dependent on the current file being saved (buffer is modified etc. maybe use a hash for memory efficiency)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use egui::{Modal, Id, Layout, Align, RichText};
|
||||
use crate::ui::debugger::ui_helpers::PaddedWidget;
|
||||
|
||||
pub struct ConfirmDialog<T> {
|
||||
open: bool,
|
||||
@@ -46,11 +47,13 @@ impl<T> ConfirmDialog<T> {
|
||||
ui.separator();
|
||||
|
||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
if ui.button(RichText::new("Confirm").strong()).clicked() {
|
||||
|
||||
if PaddedWidget::button("Confirm").ui(ui).clicked() {
|
||||
result = Some(true);
|
||||
}
|
||||
|
||||
ui.add_space(6.0);
|
||||
if ui.button("Cancel").clicked() {
|
||||
if PaddedWidget::button("Cancel").ui(ui).clicked() {
|
||||
result = Some(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use egui::{CentralPanel, Panel, Ui};
|
||||
use egui_tiles::TileId;
|
||||
use common::formats::binary_dse::DseExecutable;
|
||||
use emulator_core::memory::ram::MemoryBank;
|
||||
@@ -30,10 +32,23 @@ pub mod dialog;
|
||||
pub mod panels;
|
||||
pub mod ui_helpers;
|
||||
|
||||
trait FileFormat: Debug + Clone {
|
||||
fn extension(&self) -> &'static str;
|
||||
}
|
||||
|
||||
impl FileFormat for DseExecutable {
|
||||
fn extension(&self) -> &'static str {
|
||||
"dse"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Action {
|
||||
RequestCloseTab(TileId),
|
||||
CloseTab(TileId),
|
||||
Quit,
|
||||
OpenFile(PathBuf),
|
||||
// OpenCachedFile(Box<dyn FileFormat>),
|
||||
LoadBinary(PathBuf),
|
||||
SaveFile(PathBuf, TileId),
|
||||
RequestSaveAs(TileId),
|
||||
@@ -125,12 +140,25 @@ impl DebuggerUi {
|
||||
|
||||
pub fn perform_action(&mut self, action: Action) {
|
||||
match action {
|
||||
|
||||
Action::RequestCloseTab(tile_id) => {
|
||||
self.confirm_dialog.open(
|
||||
"Close Tab",
|
||||
"Closing this tab may result in losing unsaved work",
|
||||
Action::CloseTab(tile_id)
|
||||
);
|
||||
}
|
||||
|
||||
Action::CloseTab(tile_id) => {
|
||||
self.central_tiles.close_tab(tile_id);
|
||||
}
|
||||
|
||||
Action::Quit => std::process::exit(0),
|
||||
|
||||
// Opens a file (usually constructed by the file dialogue)
|
||||
Action::OpenFile(path) => {
|
||||
match path.extension().map(|s| s.to_str()).flatten() {
|
||||
Some("dsa") => { self.central_tiles.add_tab(Editor::new_open(&path)); },
|
||||
Some("dsa" | "dsc") => { self.central_tiles.add_tab(Editor::new_open(&path)); },
|
||||
Some("dsb") => { self.central_tiles.add_tab(Disassembler::new_open(&path)); },
|
||||
Some("md") => { self.central_tiles.add_tab(Documentation::open(&path)); },
|
||||
Some(_) => todo!(),
|
||||
@@ -160,8 +188,6 @@ impl DebuggerUi {
|
||||
}
|
||||
|
||||
Action::SaveFile(path, tile) => {
|
||||
println!("ACTION: saving file {} from tile {tile:?}", path.display());
|
||||
|
||||
if let Some(editor) = self.central_tiles.pane_mut::<Editor>(tile) {
|
||||
if let Err(e) = std::fs::write(path, editor.get_buffer().as_bytes()) {
|
||||
self.perform_action(Action::ShowError(e.to_string()))
|
||||
@@ -174,23 +200,8 @@ impl DebuggerUi {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for DebuggerUi {
|
||||
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
ui.request_repaint_after(Duration::from_millis(16));
|
||||
|
||||
if let Some(action) = self.confirm_dialog.show(ui) {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
if let Some(action) = self.file_dialog.show(ui) {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
self.error_dialog.show(ui);
|
||||
|
||||
egui::Panel::top("toolbar").resizable(false).show(ui, |ui| {
|
||||
fn toolbar(&mut self, ui: &mut Ui) {
|
||||
egui::MenuBar::new().ui(ui, |ui| {
|
||||
|
||||
let save_enabled = self.central_tiles.active_tile()
|
||||
@@ -246,14 +257,40 @@ impl eframe::App for DebuggerUi {
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for DebuggerUi {
|
||||
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
ui.request_repaint_after(Duration::from_millis(16));
|
||||
|
||||
if let Some(action) = self.confirm_dialog.show(ui) {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
if let Some(action) = self.file_dialog.show(ui) {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
self.error_dialog.show(ui);
|
||||
|
||||
Panel::top("menu_bar").resizable(false).show(ui, |ui| {
|
||||
self.toolbar(ui);
|
||||
});
|
||||
egui::Panel::top("info_panel").resizable(false).show(ui, |ui| {
|
||||
|
||||
Panel::top("control_panel").resizable(false).show(ui, |ui| {
|
||||
self.controller.ui(ui);
|
||||
});
|
||||
egui::Panel::left("left_panel").resizable(false).show(ui, |ui| {});
|
||||
|
||||
egui::Panel::right("right_panel").resizable(false).show(ui, |ui| {});
|
||||
egui::CentralPanel::default().show(ui, |ui| {
|
||||
Panel::left("left_panel").resizable(false).show(ui, |ui| {
|
||||
// TODO: File Explorer!
|
||||
});
|
||||
|
||||
// Panel::right("right_panel").resizable(false).show(ui, |ui| {
|
||||
// // TODO: not sure what to put here!
|
||||
// });
|
||||
|
||||
CentralPanel::default().show(ui, |ui| {
|
||||
if let Some(action) = self.central_tiles.ui(ui) {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
@@ -86,4 +86,8 @@ impl TilePane for Editor {
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
|
||||
fn close_requires_confirmation(&mut self, _tile_id: TileId) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -6,19 +6,31 @@ use std::any::Any;
|
||||
use egui::{Id, Response, Stroke, StrokeKind, Ui, Widget};
|
||||
use egui_tiles::{Behavior, Container, SimplificationOptions, TabState, Tile, TileId, Tiles, Tree, UiResponse};
|
||||
use crate::ui::debugger::Action;
|
||||
use crate::ui::debugger::Action::RequestCloseTab;
|
||||
|
||||
/// Anything that can live inside a tab in the central tiled area.
|
||||
/// `as_any_mut` lets callers reach back into a specific pane's concrete
|
||||
/// type after it's been handed off to the tree (e.g. to write into the
|
||||
/// editor's buffer from an `Action` handler).
|
||||
pub trait TilePane: Any {
|
||||
|
||||
/// the title that's shown on the tab in the UI.
|
||||
fn title(&self) -> egui::WidgetText;
|
||||
|
||||
/// main UI function. returns an action in order to communicate with the parent UI
|
||||
fn ui(&mut self, ui: &mut egui::Ui, _tile_id: TileId) -> Option<Action>;
|
||||
|
||||
/// required in order to be used with a pane
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
/// the menu options exposed by the tile. these inform the main application menu based on the current focused tile
|
||||
fn menu(&mut self, _ui: &mut egui::Ui, _tile_id: TileId) -> Option<Action> {
|
||||
None
|
||||
}
|
||||
|
||||
fn close_requires_confirmation(&mut self, _tile_id: TileId) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
type Pane = Box<dyn TilePane>;
|
||||
@@ -29,24 +41,6 @@ struct TileBehavior {
|
||||
}
|
||||
|
||||
impl Behavior<Pane> for TileBehavior {
|
||||
fn is_tab_closable(&self, _tiles: &Tiles<Pane>, _tile_id: TileId) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn on_tab_close(&mut self, tiles: &mut Tiles<Pane>, tile_id: TileId) -> bool {
|
||||
tiles.remove(tile_id);
|
||||
true
|
||||
}
|
||||
|
||||
fn tab_bar_height(&self, _style: &egui::Style) -> f32 {
|
||||
36.0 // taller tab row = more vertical padding around the label
|
||||
}
|
||||
|
||||
fn tab_bg_color(&self, visuals: &egui::Visuals, _tiles: &egui_tiles::Tiles<Box<dyn TilePane>>, _tile_id: egui_tiles::TileId, tab_state: &egui_tiles::TabState) -> egui::Color32 {
|
||||
visuals.widgets.inactive.bg_fill
|
||||
}
|
||||
|
||||
|
||||
fn pane_ui(
|
||||
&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
@@ -65,11 +59,34 @@ impl Behavior<Pane> for TileBehavior {
|
||||
UiResponse::None
|
||||
}
|
||||
|
||||
|
||||
fn tab_title_for_pane(&mut self, pane: &Pane) -> egui::WidgetText {
|
||||
pane.title()
|
||||
}
|
||||
|
||||
fn is_tab_closable(&self, _tiles: &Tiles<Pane>, _tile_id: TileId) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn on_tab_close(&mut self, tiles: &mut Tiles<Pane>, tile_id: TileId) -> bool {
|
||||
let Some(Tile::Pane(pane)) = tiles.get_mut(tile_id) else {
|
||||
return false
|
||||
};
|
||||
|
||||
if pane.close_requires_confirmation(tile_id) {
|
||||
self.pending_action = Some(RequestCloseTab(tile_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
tiles.remove(tile_id);
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
fn tab_bar_height(&self, _style: &egui::Style) -> f32 {
|
||||
36.0 // taller tab row = more vertical padding around the label
|
||||
}
|
||||
|
||||
|
||||
fn simplification_options(&self) -> SimplificationOptions {
|
||||
SimplificationOptions {
|
||||
// Keep the tab bar around even with only one tab, and don't let
|
||||
@@ -78,6 +95,10 @@ impl Behavior<Pane> for TileBehavior {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_bg_color(&self, visuals: &egui::Visuals, _tiles: &Tiles<Box<dyn TilePane>>, _tile_id: TileId, tab_state: &TabState) -> egui::Color32 {
|
||||
visuals.widgets.inactive.bg_fill
|
||||
}
|
||||
}
|
||||
|
||||
/// A single top-level tab strip that tools can add tabs to at runtime.
|
||||
@@ -111,6 +132,9 @@ impl CentralTiles {
|
||||
self.add_dyn_tab(Box::new(pane))
|
||||
}
|
||||
|
||||
pub fn close_tab(&mut self, tile_id: TileId) {
|
||||
self.tree.tiles.remove(tile_id);
|
||||
}
|
||||
|
||||
/// Add a new tab and make it active. Returns the `TileId` so the
|
||||
/// caller can hold onto it for later access via `pane_mut`.
|
||||
|
||||
Reference in New Issue
Block a user