parent
7f1a1ff57b
commit
9d973a0fd6
8 changed files with 388 additions and 26 deletions
|
|
@ -23,6 +23,34 @@ use iced::{mouse, Point, Task};
|
|||
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn begin_tab_close_queue(&mut self, tab_ids: Vec<u64>) -> Task<Message> {
|
||||
self.pending_tab_closes.clear();
|
||||
self.pending_tab_closes.extend(tab_ids);
|
||||
self.continue_tab_close_queue()
|
||||
}
|
||||
|
||||
/// Close queued drawings until a dirty tab requires confirmation. Queue
|
||||
/// entries use stable document ids because removing an earlier tab changes
|
||||
/// every later vector index.
|
||||
pub(super) fn continue_tab_close_queue(&mut self) -> Task<Message> {
|
||||
let mut tasks = Vec::new();
|
||||
while let Some(tab_id) = self.pending_tab_closes.pop_front() {
|
||||
let Some(idx) = self.tabs.iter().position(|tab| tab.id == tab_id) else {
|
||||
continue;
|
||||
};
|
||||
if self.tabs[idx].is_start {
|
||||
continue;
|
||||
}
|
||||
if self.tabs[idx].dirty {
|
||||
self.pending_close = Some(crate::app::PendingClose::Tab(idx));
|
||||
tasks.push(self.open_unsaved_dialog_window());
|
||||
break;
|
||||
}
|
||||
tasks.push(self.on_tab_close(idx));
|
||||
}
|
||||
Task::batch(tasks)
|
||||
}
|
||||
|
||||
pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
// Start tab is fixed — close requests on it are no-ops.
|
||||
if self.tabs.get(idx).map_or(false, |t| t.is_start) {
|
||||
|
|
@ -541,6 +569,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
|||
}
|
||||
if self.layout_rename_state.take().is_some()
|
||||
|| self.layout_context_menu.take().is_some()
|
||||
|| self.doc_tab_context_menu.take().is_some()
|
||||
{
|
||||
return Task::none();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||
// last selection. #21.
|
||||
self.sync_ribbon_layers();
|
||||
self.sync_ribbon_from_selection();
|
||||
return close_win;
|
||||
return Task::batch([close_win, self.continue_tab_close_queue()]);
|
||||
}
|
||||
Some(crate::app::PendingClose::Quit) => {
|
||||
if let Some(idx) = self.tabs.iter().position(|t| t.dirty) {
|
||||
|
|
|
|||
|
|
@ -937,6 +937,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
self.pending_close = None;
|
||||
tasks.push(self.close_unsaved_dialog_window());
|
||||
tasks.push(self.update(Message::TabClose(i)));
|
||||
tasks.push(self.continue_tab_close_queue());
|
||||
}
|
||||
crate::app::SaveContinuation::Quit if snapshot_is_current => {
|
||||
self.pending_close = None;
|
||||
|
|
@ -1084,19 +1085,32 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
self.stamp_header_sysvars(i);
|
||||
self.sync_truck_solids_to_acis(i);
|
||||
self.stamp_thumbnail(i, version);
|
||||
match crate::io::save_to_bytes(&self.tabs[i].scene.document, ext, version) {
|
||||
let saved =
|
||||
match crate::io::save_to_bytes(&self.tabs[i].scene.document, ext, version) {
|
||||
Ok(bytes) => {
|
||||
crate::sys::download_bytes(&filename, &bytes);
|
||||
self.tabs[i].dirty = false;
|
||||
self.command_line.push_output(&format!("Saved: {filename}"));
|
||||
true
|
||||
}
|
||||
Err(e) => self.command_line.push_error(&format!("Save failed: {e}")),
|
||||
}
|
||||
Err(e) => {
|
||||
self.command_line.push_error(&format!("Save failed: {e}"));
|
||||
false
|
||||
}
|
||||
};
|
||||
// Continue a pending tab close.
|
||||
if self.save_dialog_for_unsaved {
|
||||
if let Some(crate::app::PendingClose::Tab(idx)) = self.pending_close.take() {
|
||||
let cont = self.update(Message::TabClose(idx));
|
||||
return Task::batch([close, cont]);
|
||||
if saved {
|
||||
if let Some(crate::app::PendingClose::Tab(idx)) =
|
||||
self.pending_close.take()
|
||||
{
|
||||
let cont = self.update(Message::TabClose(idx));
|
||||
let rest = self.continue_tab_close_queue();
|
||||
return Task::batch([close, cont, rest]);
|
||||
}
|
||||
} else if self.pending_close.is_some() {
|
||||
let retry = self.open_unsaved_dialog_window();
|
||||
return Task::batch([close, retry]);
|
||||
}
|
||||
}
|
||||
close
|
||||
|
|
|
|||
|
|
@ -906,6 +906,7 @@ impl OpenCADStudio {
|
|||
}
|
||||
|
||||
Message::TabSwitch(idx) => {
|
||||
self.doc_tab_context_menu = None;
|
||||
if idx < self.tabs.len() {
|
||||
if idx != self.active_tab {
|
||||
// The attribute editor is tab-scoped; leaving its tab
|
||||
|
|
@ -940,7 +941,116 @@ impl OpenCADStudio {
|
|||
Task::none()
|
||||
}
|
||||
|
||||
Message::TabClose(idx) => self.on_tab_close(idx),
|
||||
Message::TabClose(idx) => {
|
||||
self.doc_tab_context_menu = None;
|
||||
self.on_tab_close(idx)
|
||||
}
|
||||
|
||||
Message::DocTabContextMenu(idx) => {
|
||||
if self.tabs.get(idx).is_some_and(|tab| !tab.is_start) {
|
||||
self.layout_context_menu = None;
|
||||
self.doc_tab_context_menu = Some(idx);
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::DocTabContextMenuClose => {
|
||||
self.doc_tab_context_menu = None;
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::DocTabSaveAll => {
|
||||
self.doc_tab_context_menu = None;
|
||||
self.dispatch_command("SAVEALL")
|
||||
}
|
||||
|
||||
Message::DocTabCloseAll => {
|
||||
self.doc_tab_context_menu = None;
|
||||
let ids = self
|
||||
.tabs
|
||||
.iter()
|
||||
.filter(|tab| !tab.is_start)
|
||||
.map(|tab| tab.id)
|
||||
.collect();
|
||||
self.begin_tab_close_queue(ids)
|
||||
}
|
||||
|
||||
Message::DocTabCloseOthers(idx) => {
|
||||
self.doc_tab_context_menu = None;
|
||||
let Some(keep_id) = self.tabs.get(idx).filter(|tab| !tab.is_start).map(|t| t.id)
|
||||
else {
|
||||
return Task::none();
|
||||
};
|
||||
let switch = self.update(Message::TabSwitch(idx));
|
||||
let ids = self
|
||||
.tabs
|
||||
.iter()
|
||||
.filter(|tab| !tab.is_start && tab.id != keep_id)
|
||||
.map(|tab| tab.id)
|
||||
.collect();
|
||||
Task::batch([switch, self.begin_tab_close_queue(ids)])
|
||||
}
|
||||
|
||||
Message::DocTabCopyFullPath(idx) => {
|
||||
self.doc_tab_context_menu = None;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let Some(path) = self.tabs.get(idx).and_then(|tab| tab.current_path.clone())
|
||||
else {
|
||||
self.command_line
|
||||
.push_error("Save the drawing before copying its file path.");
|
||||
return Task::none();
|
||||
};
|
||||
let full_path = path.canonicalize().unwrap_or_else(|_| {
|
||||
if path.is_absolute() {
|
||||
path
|
||||
} else {
|
||||
std::env::current_dir()
|
||||
.map(|dir| dir.join(&path))
|
||||
.unwrap_or(path)
|
||||
}
|
||||
});
|
||||
self.command_line
|
||||
.push_output(&format!("Copied path: {}", full_path.display()));
|
||||
return iced::clipboard::write(full_path.to_string_lossy().into_owned());
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let _ = idx;
|
||||
self.command_line
|
||||
.push_error("Full file paths are unavailable in the web application.");
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
|
||||
Message::DocTabOpenFileLocation(idx) => {
|
||||
self.doc_tab_context_menu = None;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let Some(path) = self.tabs.get(idx).and_then(|tab| tab.current_path.clone())
|
||||
else {
|
||||
self.command_line
|
||||
.push_error("Save the drawing before opening its file location.");
|
||||
return Task::none();
|
||||
};
|
||||
match crate::sys::reveal_in_file_manager(&path) {
|
||||
Ok(()) => self
|
||||
.command_line
|
||||
.push_output(&format!("Opened file location: {}", path.display())),
|
||||
Err(error) => self
|
||||
.command_line
|
||||
.push_error(&format!("Could not open file location: {error}")),
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let _ = idx;
|
||||
self.command_line
|
||||
.push_error("File locations are unavailable in the web application.");
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
|
||||
Message::CommandInput(s) => {
|
||||
// Space submits (acts like Enter) so a command advances
|
||||
|
|
@ -3227,6 +3337,7 @@ impl OpenCADStudio {
|
|||
.map(|be| be.block_name == name)
|
||||
.unwrap_or(false);
|
||||
if name != "Model" && !is_block_tab {
|
||||
self.doc_tab_context_menu = None;
|
||||
self.layout_context_menu = Some(name);
|
||||
}
|
||||
Task::none()
|
||||
|
|
@ -3936,6 +4047,7 @@ impl OpenCADStudio {
|
|||
// ── Unsaved-changes dialog ────────────────────────────────────
|
||||
Message::UnsavedDialogCancel => {
|
||||
self.pending_close = None;
|
||||
self.pending_tab_closes.clear();
|
||||
self.close_unsaved_dialog_window()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue