feat: show Save/Discard/Cancel dialog before closing unsaved tabs or quitting
Opens a floating OS window when the user tries to close a dirty tab or quit with unsaved changes. The main window stays alive until the dialog is resolved — Cancel aborts the close entirely. For quit with multiple dirty tabs the dialog loops through each one individually. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
59e7f6b832
commit
fc0aa8698b
3 changed files with 300 additions and 2 deletions
|
|
@ -147,6 +147,12 @@ pub(super) struct H7CAD {
|
||||||
ps_lineweight_buf: String,
|
ps_lineweight_buf: String,
|
||||||
ps_screening_buf: String,
|
ps_screening_buf: String,
|
||||||
|
|
||||||
|
// ── Unsaved-changes dialog ────────────────────────────────────────────
|
||||||
|
/// Set when the user tries to close a tab or quit while there are unsaved changes.
|
||||||
|
pending_close: Option<PendingClose>,
|
||||||
|
/// OS window for the unsaved-changes confirmation dialog.
|
||||||
|
unsaved_dialog_window: Option<window::Id>,
|
||||||
|
|
||||||
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
||||||
/// Name of the style currently shown in the dialog.
|
/// Name of the style currently shown in the dialog.
|
||||||
dimstyle_selected: String,
|
dimstyle_selected: String,
|
||||||
|
|
@ -167,6 +173,15 @@ pub(super) struct H7CAD {
|
||||||
ds_dimtdec: String, ds_dimtfac: String,
|
ds_dimtdec: String, ds_dimtfac: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What triggered the "unsaved changes" dialog.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(super) enum PendingClose {
|
||||||
|
/// User tried to close the tab at this index.
|
||||||
|
Tab(usize),
|
||||||
|
/// User tried to quit the application.
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
/// Identifies a DimStyle field that can be edited in the dialog.
|
/// Identifies a DimStyle field that can be edited in the dialog.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum DsField {
|
pub enum DsField {
|
||||||
|
|
@ -210,6 +225,15 @@ pub enum Message {
|
||||||
TabSwitch(usize),
|
TabSwitch(usize),
|
||||||
/// Close the given tab index.
|
/// Close the given tab index.
|
||||||
TabClose(usize),
|
TabClose(usize),
|
||||||
|
// ── Unsaved-changes confirmation dialog ───────────────────────────────
|
||||||
|
/// User clicked "Save" in the unsaved-changes dialog.
|
||||||
|
UnsavedDialogSave,
|
||||||
|
/// User clicked "Discard" in the unsaved-changes dialog.
|
||||||
|
UnsavedDialogDiscard,
|
||||||
|
/// User clicked "Cancel" in the unsaved-changes dialog.
|
||||||
|
UnsavedDialogCancel,
|
||||||
|
/// Save-as path picked for the unsaved-changes → save → close flow.
|
||||||
|
UnsavedPickedSavePath(Option<std::path::PathBuf>),
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
CommandInput(String),
|
CommandInput(String),
|
||||||
CommandSubmit,
|
CommandSubmit,
|
||||||
|
|
@ -398,7 +422,9 @@ pub enum Message {
|
||||||
AboutCopyInfo,
|
AboutCopyInfo,
|
||||||
/// Close the viewport right-click context menu without performing any action.
|
/// Close the viewport right-click context menu without performing any action.
|
||||||
ViewportContextMenuClose,
|
ViewportContextMenuClose,
|
||||||
/// A window was closed by the OS (e.g. the user clicked the title-bar ✕).
|
/// The user clicked the title-bar ✕ (fires before the window closes).
|
||||||
|
WindowCloseRequested(window::Id),
|
||||||
|
/// A window was fully closed (fires after `window::close()` is called).
|
||||||
OsWindowClosed(window::Id),
|
OsWindowClosed(window::Id),
|
||||||
/// No-op — used as a fallback when a TabEvent has no host mapping.
|
/// No-op — used as a fallback when a TabEvent has no host mapping.
|
||||||
Noop,
|
Noop,
|
||||||
|
|
@ -596,6 +622,8 @@ impl H7CAD {
|
||||||
page_setup_offset_y: "0.0".to_string(),
|
page_setup_offset_y: "0.0".to_string(),
|
||||||
page_setup_rotation: "0".to_string(),
|
page_setup_rotation: "0".to_string(),
|
||||||
page_setup_scale: "Fit".to_string(),
|
page_setup_scale: "Fit".to_string(),
|
||||||
|
pending_close: None,
|
||||||
|
unsaved_dialog_window: None,
|
||||||
// Plot style
|
// Plot style
|
||||||
active_plot_style: None,
|
active_plot_style: None,
|
||||||
// Color scheme (default: dark CAD-style)
|
// Color scheme (default: dark CAD-style)
|
||||||
|
|
@ -650,6 +678,7 @@ impl H7CAD {
|
||||||
let (id, open_task) = window::open(window::Settings {
|
let (id, open_task) = window::open(window::Settings {
|
||||||
maximized: true,
|
maximized: true,
|
||||||
icon: window::icon::from_rgba(build_window_icon(), 32, 32).ok(),
|
icon: window::icon::from_rgba(build_window_icon(), 32, 32).ok(),
|
||||||
|
exit_on_close_request: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let mut s = state;
|
let mut s = state;
|
||||||
|
|
@ -674,7 +703,8 @@ pub fn run() -> iced::Result {
|
||||||
if Some(window_id) == state.plotstyle_window { return "Plot Style Table Editor".into(); }
|
if Some(window_id) == state.plotstyle_window { return "Plot Style Table Editor".into(); }
|
||||||
if Some(window_id) == state.dimstyle_window { return "Dimension Style Manager".into(); }
|
if Some(window_id) == state.dimstyle_window { return "Dimension Style Manager".into(); }
|
||||||
if Some(window_id) == state.shortcuts_window { return "Keyboard Shortcuts".into(); }
|
if Some(window_id) == state.shortcuts_window { return "Keyboard Shortcuts".into(); }
|
||||||
if Some(window_id) == state.about_window { return "About H7CAD".into(); }
|
if Some(window_id) == state.about_window { return "About H7CAD".into(); }
|
||||||
|
if Some(window_id) == state.unsaved_dialog_window { return "Unsaved Changes".into(); }
|
||||||
if let Some(tab) = state.tabs.get(state.active_tab) {
|
if let Some(tab) = state.tabs.get(state.active_tab) {
|
||||||
let dot = if tab.dirty { "● " } else { "" };
|
let dot = if tab.dirty { "● " } else { "" };
|
||||||
let name = tab.tab_display_name();
|
let name = tab.tab_display_name();
|
||||||
|
|
|
||||||
|
|
@ -532,6 +532,10 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::TabClose(idx) => {
|
Message::TabClose(idx) => {
|
||||||
|
if self.tabs.get(idx).map_or(false, |t| t.dirty) {
|
||||||
|
self.pending_close = Some(super::PendingClose::Tab(idx));
|
||||||
|
return self.open_unsaved_dialog_window();
|
||||||
|
}
|
||||||
if self.tabs.len() == 1 {
|
if self.tabs.len() == 1 {
|
||||||
self.tab_counter += 1;
|
self.tab_counter += 1;
|
||||||
self.tabs[0] = super::document::DocumentTab::new_drawing(self.tab_counter);
|
self.tabs[0] = super::document::DocumentTab::new_drawing(self.tab_counter);
|
||||||
|
|
@ -705,10 +709,28 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Message::WindowCloseRequested(id) => {
|
||||||
|
if self.main_window == Some(id) {
|
||||||
|
if self.tabs.iter().any(|t| t.dirty) {
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
return self.open_unsaved_dialog_window();
|
||||||
|
}
|
||||||
|
return iced::exit();
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
Message::OsWindowClosed(id) => {
|
Message::OsWindowClosed(id) => {
|
||||||
if self.main_window == Some(id) {
|
if self.main_window == Some(id) {
|
||||||
|
// Main window was explicitly closed by us — exit.
|
||||||
return iced::exit();
|
return iced::exit();
|
||||||
}
|
}
|
||||||
|
if self.unsaved_dialog_window == Some(id) {
|
||||||
|
// User closed the dialog window via OS ✕ — treat as Cancel.
|
||||||
|
self.unsaved_dialog_window = None;
|
||||||
|
self.pending_close = None;
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
if self.layer_window == Some(id) { self.layer_window = None; }
|
if self.layer_window == Some(id) { self.layer_window = None; }
|
||||||
if self.page_setup_window == Some(id) { self.page_setup_window = None; }
|
if self.page_setup_window == Some(id) { self.page_setup_window = None; }
|
||||||
if self.textstyle_window == Some(id) { self.textstyle_window = None; }
|
if self.textstyle_window == Some(id) { self.textstyle_window = None; }
|
||||||
|
|
@ -2664,6 +2686,163 @@ impl H7CAD {
|
||||||
|
|
||||||
Message::Noop => Task::none(),
|
Message::Noop => Task::none(),
|
||||||
|
|
||||||
|
// ── Unsaved-changes dialog ────────────────────────────────────
|
||||||
|
Message::UnsavedDialogCancel => {
|
||||||
|
self.pending_close = None;
|
||||||
|
self.close_unsaved_dialog_window()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::UnsavedDialogDiscard => {
|
||||||
|
match self.pending_close.take() {
|
||||||
|
Some(super::PendingClose::Tab(idx)) => {
|
||||||
|
let close_win = self.close_unsaved_dialog_window();
|
||||||
|
if self.tabs.len() == 1 {
|
||||||
|
self.tab_counter += 1;
|
||||||
|
self.tabs[0] = super::document::DocumentTab::new_drawing(self.tab_counter);
|
||||||
|
self.active_tab = 0;
|
||||||
|
} else {
|
||||||
|
self.tabs.remove(idx);
|
||||||
|
if self.active_tab >= self.tabs.len() {
|
||||||
|
self.active_tab = self.tabs.len() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return close_win;
|
||||||
|
}
|
||||||
|
Some(super::PendingClose::Quit) => {
|
||||||
|
if let Some(idx) = self.tabs.iter().position(|t| t.dirty) {
|
||||||
|
self.tabs[idx].dirty = false;
|
||||||
|
}
|
||||||
|
if self.tabs.iter().any(|t| t.dirty) {
|
||||||
|
// More dirty tabs remain — keep window open.
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
} else {
|
||||||
|
let close_win = self.close_unsaved_dialog_window();
|
||||||
|
return Task::batch(vec![close_win, iced::exit()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::UnsavedDialogSave => {
|
||||||
|
match self.pending_close.take() {
|
||||||
|
Some(super::PendingClose::Tab(idx)) => {
|
||||||
|
if let Some(path) = self.tabs[idx].current_path.clone() {
|
||||||
|
match crate::io::save(&self.tabs[idx].scene.document, &path) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.command_line.push_output(&format!("Saved: {}", path.display()));
|
||||||
|
self.tabs[idx].dirty = false;
|
||||||
|
let close_win = self.close_unsaved_dialog_window();
|
||||||
|
let close_tab = self.update(Message::TabClose(idx));
|
||||||
|
return Task::batch(vec![close_win, close_tab]);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Keep dialog open for retry.
|
||||||
|
self.command_line.push_error(&format!("Save failed: {e}"));
|
||||||
|
self.pending_close = Some(super::PendingClose::Tab(idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No path — close dialog, show save-as, then come back.
|
||||||
|
self.pending_close = Some(super::PendingClose::Tab(idx));
|
||||||
|
let close_win = self.close_unsaved_dialog_window();
|
||||||
|
return Task::batch(vec![
|
||||||
|
close_win,
|
||||||
|
Task::perform(crate::io::pick_save_path(), Message::UnsavedPickedSavePath),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(super::PendingClose::Quit) => {
|
||||||
|
if let Some(idx) = self.tabs.iter().position(|t| t.dirty) {
|
||||||
|
if let Some(path) = self.tabs[idx].current_path.clone() {
|
||||||
|
match crate::io::save(&self.tabs[idx].scene.document, &path) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.command_line.push_output(&format!("Saved: {}", path.display()));
|
||||||
|
self.tabs[idx].dirty = false;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.command_line.push_error(&format!("Save failed: {e}"));
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No path — close dialog, show save-as, then come back.
|
||||||
|
self.active_tab = idx;
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
let close_win = self.close_unsaved_dialog_window();
|
||||||
|
return Task::batch(vec![
|
||||||
|
close_win,
|
||||||
|
Task::perform(crate::io::pick_save_path(), Message::UnsavedPickedSavePath),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.tabs.iter().any(|t| t.dirty) {
|
||||||
|
// More dirty tabs — keep window open.
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
} else {
|
||||||
|
let close_win = self.close_unsaved_dialog_window();
|
||||||
|
return Task::batch(vec![close_win, iced::exit()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::UnsavedPickedSavePath(Some(path)) => {
|
||||||
|
match self.pending_close.take() {
|
||||||
|
Some(super::PendingClose::Tab(idx)) => {
|
||||||
|
match crate::io::save(&self.tabs[idx].scene.document, &path) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.command_line.push_output(&format!("Saved: {}", path.display()));
|
||||||
|
self.tabs[idx].current_path = Some(path);
|
||||||
|
self.tabs[idx].dirty = false;
|
||||||
|
return self.update(Message::TabClose(idx));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.command_line.push_error(&format!("Save failed: {e}"));
|
||||||
|
// Re-open dialog for retry.
|
||||||
|
self.pending_close = Some(super::PendingClose::Tab(idx));
|
||||||
|
return self.open_unsaved_dialog_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(super::PendingClose::Quit) => {
|
||||||
|
let i = self.active_tab;
|
||||||
|
match crate::io::save(&self.tabs[i].scene.document, &path) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.command_line.push_output(&format!("Saved: {}", path.display()));
|
||||||
|
self.tabs[i].current_path = Some(path);
|
||||||
|
self.tabs[i].dirty = false;
|
||||||
|
if self.tabs.iter().any(|t| t.dirty) {
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
return self.open_unsaved_dialog_window();
|
||||||
|
} else {
|
||||||
|
return iced::exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.command_line.push_error(&format!("Save failed: {e}"));
|
||||||
|
self.pending_close = Some(super::PendingClose::Quit);
|
||||||
|
return self.open_unsaved_dialog_window();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::UnsavedPickedSavePath(None) => {
|
||||||
|
// User cancelled the save-as dialog — re-open the confirmation dialog.
|
||||||
|
if self.pending_close.is_some() {
|
||||||
|
return self.open_unsaved_dialog_window();
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
// ── Page Setup ────────────────────────────────────────────────
|
// ── Page Setup ────────────────────────────────────────────────
|
||||||
Message::PageSetupOpen => {
|
Message::PageSetupOpen => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
|
|
@ -3660,6 +3839,27 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Populate edit buffers from the currently selected text style.
|
/// Populate edit buffers from the currently selected text style.
|
||||||
|
fn open_unsaved_dialog_window(&mut self) -> Task<Message> {
|
||||||
|
if let Some(id) = self.unsaved_dialog_window {
|
||||||
|
return window::gain_focus(id);
|
||||||
|
}
|
||||||
|
let (id, task) = window::open(window::Settings {
|
||||||
|
size: iced::Size::new(420.0, 155.0),
|
||||||
|
resizable: false,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
self.unsaved_dialog_window = Some(id);
|
||||||
|
task.map(|_| Message::Noop)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_unsaved_dialog_window(&mut self) -> Task<Message> {
|
||||||
|
if let Some(id) = self.unsaved_dialog_window.take() {
|
||||||
|
window::close(id)
|
||||||
|
} else {
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn load_textstyle_bufs(&mut self, tab: usize) {
|
fn load_textstyle_bufs(&mut self, tab: usize) {
|
||||||
let doc = &self.tabs[tab].scene.document;
|
let doc = &self.tabs[tab].scene.document;
|
||||||
if let Some(s) = doc.text_styles.get(&self.textstyle_selected) {
|
if let Some(s) = doc.text_styles.get(&self.textstyle_selected) {
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,20 @@ impl H7CAD {
|
||||||
if Some(window_id) == self.about_window {
|
if Some(window_id) == self.about_window {
|
||||||
return crate::ui::about::view_window();
|
return crate::ui::about::view_window();
|
||||||
}
|
}
|
||||||
|
if Some(window_id) == self.unsaved_dialog_window {
|
||||||
|
let tab_name = match &self.pending_close {
|
||||||
|
Some(super::PendingClose::Tab(idx)) => self
|
||||||
|
.tabs.get(*idx)
|
||||||
|
.map(|t| t.tab_display_name())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Some(super::PendingClose::Quit) => self
|
||||||
|
.tabs.iter().find(|t| t.dirty)
|
||||||
|
.map(|t| t.tab_display_name())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
return unsaved_changes_dialog_window(&tab_name);
|
||||||
|
}
|
||||||
|
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let tab = &self.tabs[i];
|
let tab = &self.tabs[i];
|
||||||
|
|
@ -382,6 +396,9 @@ impl H7CAD {
|
||||||
event::listen_with(|ev, status, win_id| {
|
event::listen_with(|ev, status, win_id| {
|
||||||
use iced::event::Status;
|
use iced::event::Status;
|
||||||
match ev {
|
match ev {
|
||||||
|
iced::Event::Window(window::Event::CloseRequested) => {
|
||||||
|
Some(Message::WindowCloseRequested(win_id))
|
||||||
|
}
|
||||||
iced::Event::Window(window::Event::Closed) => {
|
iced::Event::Window(window::Event::Closed) => {
|
||||||
Some(Message::OsWindowClosed(win_id))
|
Some(Message::OsWindowClosed(win_id))
|
||||||
}
|
}
|
||||||
|
|
@ -812,3 +829,54 @@ fn layout_context_menu_overlay(name: &str) -> Element<'_, Message> {
|
||||||
stack![catcher, positioned].into()
|
stack![catcher, positioned].into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Content for the floating "Unsaved Changes" OS window.
|
||||||
|
fn unsaved_changes_dialog_window(name: &str) -> Element<'static, Message> {
|
||||||
|
const BG: Color = Color { r: 0.18, g: 0.18, b: 0.20, a: 1.0 };
|
||||||
|
const BORDER_COL: Color = Color { r: 0.38, g: 0.38, b: 0.42, a: 1.0 };
|
||||||
|
const TEXT_COL: Color = Color { r: 0.90, g: 0.90, b: 0.90, a: 1.0 };
|
||||||
|
const BTN_SAVE: Color = Color { r: 0.20, g: 0.46, b: 0.80, a: 1.0 };
|
||||||
|
const BTN_HOVER: Color = Color { r: 0.26, g: 0.55, b: 0.92, a: 1.0 };
|
||||||
|
const BTN_DISC: Color = Color { r: 0.28, g: 0.28, b: 0.30, a: 1.0 };
|
||||||
|
const BTN_DHOV: Color = Color { r: 0.36, g: 0.36, b: 0.40, a: 1.0 };
|
||||||
|
|
||||||
|
let body_text = format!("Do you want to save changes to \"{}\"?", name);
|
||||||
|
|
||||||
|
let btn = |label: &'static str, msg: Message, base: Color, hov: Color| {
|
||||||
|
button(text(label).size(13).color(TEXT_COL))
|
||||||
|
.on_press(msg)
|
||||||
|
.style(move |_: &Theme, status| button::Style {
|
||||||
|
background: Some(Background::Color(match status {
|
||||||
|
button::Status::Hovered | button::Status::Pressed => hov,
|
||||||
|
_ => base,
|
||||||
|
})),
|
||||||
|
text_color: TEXT_COL,
|
||||||
|
border: Border { color: BORDER_COL, width: 1.0, radius: 4.0.into() },
|
||||||
|
shadow: iced::Shadow::default(),
|
||||||
|
snap: false,
|
||||||
|
})
|
||||||
|
.padding([6, 18])
|
||||||
|
};
|
||||||
|
|
||||||
|
container(
|
||||||
|
column![
|
||||||
|
text(body_text).size(13).color(TEXT_COL),
|
||||||
|
iced::widget::Space::new().height(20),
|
||||||
|
row![
|
||||||
|
btn("Save", Message::UnsavedDialogSave, BTN_SAVE, BTN_HOVER),
|
||||||
|
iced::widget::Space::new().width(8),
|
||||||
|
btn("Discard", Message::UnsavedDialogDiscard, BTN_DISC, BTN_DHOV),
|
||||||
|
iced::widget::Space::new().width(8),
|
||||||
|
btn("Cancel", Message::UnsavedDialogCancel, BTN_DISC, BTN_DHOV),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
.spacing(0),
|
||||||
|
)
|
||||||
|
.style(move |_: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(BG)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.center(Fill)
|
||||||
|
.padding([24, 28])
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue