From 0126ea212f3d72d48fa42a6ea616f7dedbb7d733 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 16 Oct 2026 01:08:07 +0300 Subject: [PATCH] feat(layout): copy a layout from its tab menu (#1822) The layout tab's right-click menu gains New Layout, Copy, Page Setup and Plot beside Rename and Delete. Copy makes " (2)" with the source's page setup, sheet view and every paper-space object, viewports included, and opens it. Co-Authored-By: Claude Opus 5.5 --- locales/ar-SA/opencadstudio.ftl | 1 + locales/bg-BG/opencadstudio.ftl | 1 + locales/cs-CZ/opencadstudio.ftl | 1 + locales/de-DE/opencadstudio.ftl | 1 + locales/el-GR/opencadstudio.ftl | 1 + locales/en-US/opencadstudio.ftl | 1 + locales/es-ES/opencadstudio.ftl | 1 + locales/fi-FI/opencadstudio.ftl | 1 + locales/fr-FR/opencadstudio.ftl | 1 + locales/hi-IN/opencadstudio.ftl | 1 + locales/hu-HU/opencadstudio.ftl | 1 + locales/it-IT/opencadstudio.ftl | 1 + locales/ja-JP/opencadstudio.ftl | 1 + locales/ko-KR/opencadstudio.ftl | 1 + locales/lo-LA/opencadstudio.ftl | 1 + locales/nl-NL/opencadstudio.ftl | 1 + locales/pl-PL/opencadstudio.ftl | 1 + locales/pt-BR/opencadstudio.ftl | 1 + locales/ru-RU/opencadstudio.ftl | 1 + locales/tr-TR/opencadstudio.ftl | 1 + locales/zh-CN/opencadstudio.ftl | 1 + locales/zh-TW/opencadstudio.ftl | 1 + src/app/mod.rs | 6 +++ src/app/update/mod.rs | 12 ++++++ src/app/update/viewport.rs | 41 +++++++++++++++++++ src/locale_catalog.rs | 1 + src/scene/layout.rs | 72 +++++++++++++++++++++++++++++++++ src/ui/statusbar/mod.rs | 34 ++++++++-------- 28 files changed, 172 insertions(+), 16 deletions(-) diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index e135b89d3..6429a0f89 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -695,6 +695,7 @@ common = .layers = الطبقات .layout = التخطيط .layout-name-silindi = حُذف التخطيط "__ocs_fmt_0__" + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = أُنشئ التخطيط "__ocs_fmt_0__" — استخدم MVIEW لإضافة منفذ عرض .layout-orig-new-name = التخطيط "__ocs_fmt_0__" ← "__ocs_fmt_1__" .layout-name-created = أُنشئ التخطيط '__ocs_fmt_0__'. diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index ce46a93f6..c37642d03 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = Слоеве .layout = Оформление .layout-name-silindi = Оформление "__ocs_fmt_0__" изтрито + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Оформление "__ocs_fmt_0__" създадено — използвай MVIEW, за да добавиш изглед .layout-orig-new-name = Оформление "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Оформление "__ocs_fmt_0__" създадено. diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 85a433500..abf843f86 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = Vrstvy .layout = Rozložení .layout-name-silindi = Rozložení „__ocs_fmt_0__“ bylo odstraněno + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Bylo vytvořeno rozvržení „__ocs_fmt_0__“ – pomocí MVIEW přidejte výřez .layout-orig-new-name = Rozložení "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Bylo vytvořeno rozvržení '__ocs_fmt_0__'. diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 5bf936ee4..ba3802c7f 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -683,6 +683,7 @@ common = .layers = Ebenen .layout = Layout .layout-name-silindi = Layout „__ocs_fmt_0__“ gelöscht + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Layout „__ocs_fmt_0__“ erstellt — mit MVIEW kann ein Ansichtsfenster hinzugefügt werden .layout-orig-new-name = Layout „__ocs_fmt_0__“ → „__ocs_fmt_1__“ .layout-name-created = Layout „__ocs_fmt_0__“ erstellt. diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index acf6e2b58..9ea02d2ca 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -684,6 +684,7 @@ common = .layers = Επίπεδα .layout = Διάταξη .layout-name-silindi = Η διάταξη "__ocs_fmt_0__" διαγράφηκε + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Η διάταξη "__ocs_fmt_0__" δημιουργήθηκε — χρησιμοποιήστε MVIEW για να προσθέσετε θύρα προβολής .layout-orig-new-name = Διάταξη "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Η διάταξη '__ocs_fmt_0__' δημιουργήθηκε. diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 236b34ea8..0b25e247b 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -679,6 +679,7 @@ common = .layers = Layers .layout = Layout .layout-name-silindi = Layout "__ocs_fmt_0__" deleted + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Layout "__ocs_fmt_0__" created — use MVIEW to add a viewport .layout-orig-new-name = Layout "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Layout '__ocs_fmt_0__' created. diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 842c132a1..a5efe2c3e 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -683,6 +683,7 @@ common = .layers = Capas .layout = Presentación .layout-name-silindi = Se eliminó la presentación «__ocs_fmt_0__» + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Se creó la presentación «__ocs_fmt_0__»; use MVIEW para añadir una ventana gráfica .layout-orig-new-name = Presentación «__ocs_fmt_0__» → «__ocs_fmt_1__» .layout-name-created = Se creó la presentación «__ocs_fmt_0__». diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 419dfaa62..d4e00edd1 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = Kerrokset .layout = Asettelu .layout-name-silindi = Asettelu "__ocs_fmt_0__" poistettiin + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Asettelu "__ocs_fmt_0__" luotu — käytä MVIEW:tä lisätäksesi näkymän .layout-orig-new-name = Asettelu "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Asettelu '__ocs_fmt_0__' luotu. diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 738a361e4..0e5473ea4 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -683,6 +683,7 @@ common = .layers = Calques .layout = Mise en page .layout-name-silindi = Présentation « __ocs_fmt_0__ » supprimée + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Présentation « __ocs_fmt_0__ » créée — utilisez MVIEW pour ajouter une fenêtre .layout-orig-new-name = Disposition "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Mise en page '__ocs_fmt_0__' créée. diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index ad0634ff0..e7f26c977 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -671,6 +671,7 @@ common = .layers = लेयर .layout = लेआउट .layout-name-silindi = लेआउट “__ocs_fmt_0__” हटाया गया + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = लेआउट "__ocs_fmt_0__" बनाया गया है - एक व्यूपोर्ट जोड़ने के लिए MVIEW का उपयोग करें .layout-orig-new-name = लेआउट "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = लेआउट '__ocs_fmt_0__' बनाया गया है। diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 2d788cda8..15bee6825 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -687,6 +687,7 @@ common = .layers = Fóliák .layout = Elrendezés .layout-name-silindi = A(z) „__ocs_fmt_0__” elrendezés törölve + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = A „__ocs_fmt_0__” elrendezés létrehozva – használja a MVIEW elemet nézetablak hozzáadásához .layout-orig-new-name = „__ocs_fmt_0__” → „__ocs_fmt_1__” elrendezés .layout-name-created = „__ocs_fmt_0__” elrendezés létrehozva. diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 73f7fc2a1..df881cc37 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = Livelli .layout = Disposizione .layout-name-silindi = Layout "__ocs_fmt_0__" eliminato + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Layout "__ocs_fmt_0__" creato: utilizza MVIEW per aggiungere una finestra .layout-orig-new-name = Disposizione "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Layout '__ocs_fmt_0__' creato. diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 8c496cfa7..271c127f7 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -673,6 +673,7 @@ common = .layers = レイヤー .layout = レイアウト .layout-name-silindi = レイアウト "__ocs_fmt_0__" を削除しました + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = レイアウト "__ocs_fmt_0__" を作成しました — MVIEW でビューポートを追加できます .layout-orig-new-name = レイアウト "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = レイアウト '__ocs_fmt_0__' を作成しました。 diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 5828cb7cd..63f5bfb85 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = 레이어 .layout = 레이아웃 .layout-name-silindi = 레이아웃 "__ocs_fmt_0__" 삭제됨 + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = "__ocs_fmt_0__" 레이아웃이 생성되었습니다. MVIEW를 사용하여 뷰포트를 추가하세요. .layout-orig-new-name = 레이아웃 "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = '__ocs_fmt_0__' 레이아웃이 생성되었습니다. diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index 9cbd0f602..fc88b2625 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -686,6 +686,7 @@ common = .layers = ເລເຢີ .layout = ເລເອົາຕ໌ .layout-name-silindi = ລຶບເລເອົາຕ໌ "__ocs_fmt_0__" ແລ້ວ + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = ສ້າງເລເອົາຕ໌ "__ocs_fmt_0__" ແລ້ວ — ໃຊ້ MVIEW ເພື່ອເພີ່ມວິວພອດ .layout-orig-new-name = ເລເອົາຕ໌ "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = ສ້າງເລເອົາຕ໌ '__ocs_fmt_0__' ແລ້ວ. diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 0e833a32c..60ff5d8e4 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -681,6 +681,7 @@ common = .layers = Lagen .layout = Indeling .layout-name-silindi = Lay-out "__ocs_fmt_0__" verwijderd + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Lay-out "__ocs_fmt_0__" gemaakt — voeg een weergavevenster toe met MVIEW .layout-orig-new-name = Indeling "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Layout '__ocs_fmt_0__' aangemaakt. diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index a8c680f87..3801846f2 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = Warstwy .layout = Układ .layout-name-silindi = Układ „__ocs_fmt_0__” został usunięty + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Utworzono układ „__ocs_fmt_0__” — użyj MVIEW, aby dodać rzutnię .layout-orig-new-name = Układ „__ocs_fmt_0__” → „__ocs_fmt_1__” .layout-name-created = Utworzono układ „__ocs_fmt_0__”. diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index ee9061395..22723a1dc 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -683,6 +683,7 @@ common = .layers = Camadas .layout = Layout .layout-name-silindi = O layout “__ocs_fmt_0__” foi excluído + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = O layout “__ocs_fmt_0__” foi criado — use MVIEW para adicionar uma janela de visualização .layout-orig-new-name = Layout “__ocs_fmt_0__” → “__ocs_fmt_1__” .layout-name-created = O layout “__ocs_fmt_0__” foi criado. diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index f432c23e7..ae46aa3b6 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -686,6 +686,7 @@ common = .layers = Слои .layout = Расположение .layout-name-silindi = Лист «__ocs_fmt_0__» удалён + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = Создан лист «__ocs_fmt_0__» — используйте MVIEW для добавления видового экрана .layout-orig-new-name = Выравнивание "__ocs_fmt_0__" → "__ocs_fmt_1__" .layout-name-created = Создан лист «__ocs_fmt_0__». diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index d9fd70437..80109e3fe 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -669,6 +669,7 @@ common = .layers = Katmanlar .layout = Yerleşim .layout-name-silindi = "__ocs_fmt_0__" yerleşimi silindi + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = "__ocs_fmt_0__" yerleşimi oluşturuldu — görünüm alanı eklemek için MVIEW komutunu kullanın .layout-orig-new-name = "__ocs_fmt_0__" yerleşimi → "__ocs_fmt_1__" .layout-name-created = '__ocs_fmt_0__' yerleşimi oluşturuldu. diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 5c90421f8..a87e01d6e 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -670,6 +670,7 @@ common = .layers = 图层 .layout = 布局 .layout-name-silindi = 布局“__ocs_fmt_0__”已删除 + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = 创建的版式“ __ocs_fmt_0__ ” — 使用 MVIEW 添加视图 .layout-orig-new-name = 版式“ __ocs_fmt_0__” − “__ocs_fmt_1__ ” .layout-name-created = 创建了版式“ __ocs_fmt_0__ ” 。 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 2e7b7164b..0ff11fa53 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -682,6 +682,7 @@ common = .layers = 圖層 .layout = 配置 .layout-name-silindi = 配置“__ocs_fmt_0__”已刪除 + .layout-copied = Layout "__ocs_fmt_0__" copied as "__ocs_fmt_1__" .layout-new-name-created-use-mview-to-add-a-vie = 建立的版式“ __ocs_fmt_0__ ” — 使用 MVIEW 新增視圖 .layout-orig-new-name = 版式“ __ocs_fmt_0__” − “__ocs_fmt_1__ ” .layout-name-created = 建立了版式“ __ocs_fmt_0__ ” 。 diff --git a/src/app/mod.rs b/src/app/mod.rs index f81649499..043888a51 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -3765,6 +3765,12 @@ pub enum Message { }, /// Create a new paper space layout. LayoutCreate, + /// Copy the named layout, its page setup and paper-space objects. + LayoutCopy(String), + /// Open the named layout's page setup. + LayoutPageSetup(String), + /// Plot the named layout. + LayoutPlot(String), /// Delete the named paper space layout (Model cannot be deleted). LayoutDelete(String), /// Begin inline rename for the given layout tab. diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index b0e36bbe5..07935a80e 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -8297,6 +8297,18 @@ impl OpenCADStudio { Message::LayoutCreate => self.on_layout_create(), + Message::LayoutCopy(name) => self.on_layout_copy(name), + + Message::LayoutPageSetup(name) => { + let switch_task = self.on_layout_switch(name); + Task::batch([switch_task, self.on_plot_dialog_open()]) + } + + Message::LayoutPlot(name) => { + let switch_task = self.on_layout_switch(name); + Task::batch([switch_task, Task::done(Message::Command("PLOT".into()))]) + } + Message::LayoutDelete(name) => { let i = self.active_tab; let deleting_current = self.tabs[i].scene.current_layout == name; diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 25ca93d02..e8b0198ef 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -6920,6 +6920,47 @@ properties={:.1}ms picked={}", cancel_task } + /// Copy layout `source` as "source (2)": its page setup and every + /// paper-space object, viewports included. The copy opens. + pub(super) fn on_layout_copy(&mut self, source: String) -> Task { + let i = self.active_tab; + if self.tabs[i].is_start || source == "Model" { + return Task::none(); + } + let existing = self.tabs[i].scene.layout_names(); + let Some(name) = (2..) + .map(|n| format!("{source} ({n})")) + .find(|candidate| !existing.contains(candidate)) + else { + return Task::none(); + }; + let cancel_task = self.cancel_active_command_for_space_change(); + self.push_undo_snapshot(i, "LAYOUT"); + let scene = &mut self.tabs[i].scene; + if let Err(e) = scene.add_layout(&name) { + self.command_line + .push_error(crate::tf!("Failed to create layout: {e}").as_ref()); + return cancel_task; + } + scene.copy_layout_page(&source, &name); + let handles = scene.layout_paper_entities(&source); + let (entities, deps) = crate::app::copy_to_clipboard_kernel(&scene.document, &handles); + crate::app::paste_entities_kernel( + &mut scene.document, + &entities, + &deps, + None, + crate::app::PasteSpace::Layout(&name), + ); + scene.populate_meshes_from_document(); + scene.bump_geometry(); + self.tabs[i].dirty = true; + self.command_line + .push_output(crate::tf!("Layout \"{source}\" copied as \"{name}\"").as_ref()); + let switch_task = self.on_layout_switch(name); + Task::batch([cancel_task, switch_task]) + } + pub(super) fn on_layout_rename_commit(&mut self) -> Task { if let Some((orig, new_name)) = self.layout_rename_state.take() { let new_name = new_name.trim().to_string(); diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 33b411619..8868a5457 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -1963,6 +1963,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "Layout" => Some(("common", "layout")), "Layout\nTabs" => Some(("view", "layout-tabs")), "Layout \"{name}\" silindi" => Some(("common", "layout-name-silindi")), + "Layout \"{source}\" copied as \"{name}\"" => Some(("common", "layout-copied")), "Layout \"{new_name}\" created — use MVIEW to add a viewport" => Some(("common", "layout-new-name-created-use-mview-to-add-a-vie")), "Layout \"{orig}\" → \"{new_name}\"" => Some(("common", "layout-orig-new-name")), "Layout '{name}' created." => Some(("common", "layout-name-created")), diff --git a/src/scene/layout.rs b/src/scene/layout.rs index 765683c04..a727d8fe7 100644 --- a/src/scene/layout.rs +++ b/src/scene/layout.rs @@ -32,6 +32,78 @@ impl Scene { Ok(handle) } + /// Give layout `name` the page of layout `source`: its page setup, + /// limits and UCS, and the sheet viewport's view. It opens as it is, so + /// it does not get the first viewport a new layout gets. The paper-space + /// objects are copied by the caller. + pub fn copy_layout_page(&mut self, source: &str, name: &str) { + let find = |document: &codec::CadDocument, wanted: &str| { + document.objects.values().find_map(|object| match object { + ObjectType::Layout(layout) if layout.name == wanted => Some(layout.clone()), + _ => None, + }) + }; + let (Some(from), Some(to)) = (find(&self.document, source), find(&self.document, name)) + else { + return; + }; + if let Some(ObjectType::Layout(layout)) = self.document.objects.get_mut(&to.handle) { + *layout = codec::objects::Layout { + handle: to.handle, + owner: to.owner, + name: to.name.clone(), + tab_order: to.tab_order, + block_record: to.block_record, + viewport: to.viewport, + viewports: to.viewports.clone(), + reactors: to.reactors.clone(), + xdictionary_handle: to.xdictionary_handle, + ..from.clone() + }; + } + if let Some(EntityType::Viewport(sheet)) = self.document.get_entity(from.viewport).cloned() + { + if let Some(EntityType::Viewport(target)) = self.document.get_entity_mut(to.viewport) { + *target = codec::entities::Viewport { + common: target.common.clone(), + ..sheet + }; + } + } + self.unopened_layouts.remove(name); + self.bump_layout_epoch(); + } + + /// The paper-space objects of layout `name`, without its sheet viewport. + pub fn layout_paper_entities(&self, name: &str) -> Vec { + let Some((record, sheet)) = self.document.objects.values().find_map(|object| match object { + ObjectType::Layout(layout) if layout.name == name => { + Some((layout.block_record, layout.viewport)) + } + _ => None, + }) else { + return Vec::new(); + }; + self.document + .block_records + .iter() + .find(|br| br.handle == record) + .map(|br| { + br.entity_handles + .iter() + .copied() + .filter(|handle| *handle != sheet) + .filter(|handle| { + !matches!( + self.document.get_entity(*handle), + None | Some(EntityType::Block(_) | EntityType::BlockEnd(_)) + ) + }) + .collect() + }) + .unwrap_or_default() + } + /// Rename a paper-space layout. Updates the Layout object name in the document. pub fn rename_layout(&mut self, old_name: &str, new_name: &str) { for obj in self.document.objects.values_mut() { diff --git a/src/ui/statusbar/mod.rs b/src/ui/statusbar/mod.rs index 0fbcc0880..9afa4d8bc 100644 --- a/src/ui/statusbar/mod.rs +++ b/src/ui/statusbar/mod.rs @@ -937,23 +937,25 @@ fn osnap_btn<'a>( // ── Helpers ─────────────────────────────────────────────────────────────── fn layout_tab_context_menu(name: String) -> Element<'static, Message> { - let rename = mouse_area( - container(text(t!("Rename")).size(12)) - .padding([4, 12]) - .width(Length::Fill), - ) - .on_press(Message::LayoutRenameStart(name.clone())) - .interaction(iced::mouse::Interaction::Pointer); + let item = |label: std::borrow::Cow<'static, str>, message: Message| { + mouse_area( + container(text(label).size(12)) + .padding([4, 12]) + .width(Length::Fill), + ) + .on_press(message) + .interaction(iced::mouse::Interaction::Pointer) + }; + let items = column![ + item(t!("New Layout"), Message::LayoutCreate), + item(t!("Copy"), Message::LayoutCopy(name.clone())), + item(t!("Rename"), Message::LayoutRenameStart(name.clone())), + item(t!("Delete"), Message::LayoutDelete(name.clone())), + item(t!("Page Setup"), Message::LayoutPageSetup(name.clone())), + item(t!("Plot"), Message::LayoutPlot(name)), + ]; - let delete = mouse_area( - container(text(t!("Delete")).size(12)) - .padding([4, 12]) - .width(Length::Fill), - ) - .on_press(Message::LayoutDelete(name)) - .interaction(iced::mouse::Interaction::Pointer); - - container(column![rename, delete].spacing(0).width(160)) + container(items.spacing(0).width(160)) .style(container::bordered_box) .padding([4, 0]) .into()