feat(ribbon): contextual Block Editor tab while editing a block

Opening the block editor brings up a Block Editor ribbon tab with
Edit / Save / Discard, Attribute Definition, and the geometric,
dimensional and parameter tools that drive parametric blocks. The tab
is only listed while a block editor session is open, and leaving the
session returns to the tab that was active before.

Refs #509

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-13 15:53:55 +03:00
1 parent 5dd05bca18
commit 9724447dd0
28 files changed
+126 -2

No files matched your search

+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = الإضافات
ribbon-tab =
.block-editor = محرر الكتل
.draw = رسم
.parametric = بارامتري
.annotate = تعليقات توضيحية
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Приставки
ribbon-tab =
.block-editor = Редактор на блокове
.draw = Чертане
.parametric = Параметричен
.annotate = Анотиране
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Zásuvné moduly
ribbon-tab =
.block-editor = Editor bloků
.draw = Kreslení
.parametric = Parametrické
.annotate = Poznámky
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Plug-ins
ribbon-tab =
.block-editor = Blockeditor
.draw = Zeichnen
.parametric = Parametrisch
.annotate = Beschriften
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Πρόσθετα
ribbon-tab =
.block-editor = Επεξεργαστής μπλοκ
.draw = Σχεδίαση
.parametric = Παραμετρικό
.annotate = Επισημείωση
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Plugins
ribbon-tab =
.block-editor = Block Editor
.draw = Draw
.parametric = Parametric
.annotate = Annotate
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Complementos
ribbon-tab =
.block-editor = Editor de bloques
.draw = Dibujo
.parametric = Paramétrico
.annotate = Anotar
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Liitännäiset
ribbon-tab =
.block-editor = Lohkoeditori
.draw = Piirrä
.parametric = Parametrinen
.annotate = Merkinnät
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Extensions
ribbon-tab =
.block-editor = Éditeur de blocs
.draw = Dessin
.parametric = Paramétrique
.annotate = Annoter
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = प्लग-इन
ribbon-tab =
.block-editor = ब्लॉक संपादक
.draw = ड्रॉ
.parametric = पैरामीट्रिक
.annotate = एनोटेट
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Bővítmények
ribbon-tab =
.block-editor = Blokkszerkesztő
.draw = Rajz
.parametric = Parametrikus
.annotate = Feliratozás
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Plugin
ribbon-tab =
.block-editor = Editor blocchi
.draw = Disegno
.parametric = Parametrico
.annotate = Annotazione
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = プラグイン
ribbon-tab =
.block-editor = ブロック エディタ
.draw = 作図
.parametric = パラメトリック
.annotate = 注釈
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = 플러그인
ribbon-tab =
.block-editor = 블록 편집기
.draw = 그리기
.parametric = 파라메트릭
.annotate = 주석
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = ປລັກອິນ
ribbon-tab =
.block-editor = ຕົວແກ້ໄຂບລັອກ
.draw = ແຕ້ມ
.parametric = ພາຣາມີຕຣິກ
.annotate = ຄຳອະທິບາຍ
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Plug-ins
ribbon-tab =
.block-editor = Blokeditor
.draw = Tekenen
.parametric = Parametrisch
.annotate = Annoteren
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Wtyczki
ribbon-tab =
.block-editor = Edytor bloków
.draw = Rysowanie
.parametric = Parametryczne
.annotate = Opisy
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Plugins
ribbon-tab =
.block-editor = Editor de blocos
.draw = Desenhar
.parametric = Paramétrico
.annotate = Anotar
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Модули
ribbon-tab =
.block-editor = Редактор блоков
.draw = Начертить
.parametric = Параметрический
.annotate = Аннотации
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = Eklentiler
ribbon-tab =
.block-editor = Blok Düzenleyici
.draw = Çizim
.parametric = Parametrik
.annotate = Açıklama
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = 插件
ribbon-tab =
.block-editor = 块编辑器
.draw = 绘制
.parametric = 参数化
.annotate = 注释
+1
View File
@@ -29,6 +29,7 @@ action =
.plugins = 外掛程式
ribbon-tab =
.block-editor = 圖塊編輯器
.draw = 繪製
.parametric = 參數化
.annotate = 註解
+5
View File
@@ -339,6 +339,11 @@ impl OpenCADStudio {
self.sync_underlay_tab();
}
self.sync_frame_dropdown();
let block_editing = self
.tabs
.get(self.active_tab)
.is_some_and(|tab| tab.active_block_edit.is_some());
self.ribbon.set_block_editing(block_editing);
task
}
+1
View File
@@ -365,6 +365,7 @@ pub fn ribbon_module_title(id: &str, fallback: &str) -> String {
"layout" => crate::tr!("ribbon-tab", "layout"),
"manage" => crate::tr!("ribbon-tab", "manage"),
"view" => crate::tr!("ribbon-tab", "view"),
"block_editor" => crate::tr!("ribbon-tab", "block-editor"),
_ => fallback.to_string(),
}
}
+66
View File
@@ -0,0 +1,66 @@
//! Contextual "Block Editor" ribbon tab, shown only while a BEDIT session is
//! open: saving or discarding the block, attribute definitions, and the
//! constraint tools that drive parametric blocks.
use crate::modules::parametric::ParametricModule;
use crate::modules::{CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem, ToolDef};
pub struct BlockEditorModule;
impl CadModule for BlockEditorModule {
fn id(&self) -> &'static str {
"block_editor"
}
fn title(&self) -> &'static str {
"Block Editor"
}
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
let command = |id: &'static str, label: &'static str, icon: &'static [u8]| {
RibbonItem::LargeTool(ToolDef {
id,
label,
icon: IconKind::Svg(icon),
event: ModuleEvent::Command(id.to_string()),
})
};
let mut groups = vec![
RibbonGroup {
title: "Open and Save",
tools: vec![
command(
"BEDIT",
"Edit Block",
include_bytes!("../../../assets/icons/edit_block.svg"),
),
command(
"BEDIT_SAVE",
"Save Block",
include_bytes!("../../../assets/icons/mt_ok.svg"),
),
command(
"BEDIT_DISCARD",
"Discard Block Edit",
include_bytes!("../../../assets/icons/mt_cancel.svg"),
),
],
},
RibbonGroup {
title: "Attributes",
tools: vec![command(
"ATTDEF",
"Attribute Definition",
include_bytes!("../../../assets/icons/attdef.svg"),
)],
},
];
// Parametric blocks are driven by the same constraints and
// parameters as the Parametric tab.
groups.extend(ParametricModule.ribbon_groups().iter().cloned());
groups
})
}
}
+1
View File
@@ -25,6 +25,7 @@ pub use ocs_plugin_api::ribbon::{
// ── Module declarations ───────────────────────────────────────────────────
pub mod annotate;
pub mod block_editor;
pub mod draw;
pub mod insert;
pub mod layout;
+1
View File
@@ -23,6 +23,7 @@ pub fn all_modules() -> Vec<Box<dyn CadModule>> {
Box::new(super::view::ViewModule),
Box::new(super::manage::ManageModule),
Box::new(super::layout::LayoutModule),
Box::new(super::block_editor::BlockEditorModule),
]
}
+30 -2
View File
@@ -117,6 +117,9 @@ pub struct Ribbon {
xref_ctx: bool,
/// Only point clouds are selected: whether the first shows its crops.
point_cloud_ctx: Option<bool>,
/// A block editor session is open: the tab that was active before the
/// Block Editor tab took over.
block_editing: Option<usize>,
/// XDWGFADECTL as the Reference slide-out shows it (negative = off).
pub xref_fade: i32,
}
@@ -219,6 +222,7 @@ impl Ribbon {
underlay_ctx: None,
xref_ctx: false,
point_cloud_ctx: None,
block_editing: None,
xref_fade: 50,
}
}
@@ -293,6 +297,28 @@ impl Ribbon {
self.available_linetypes = items;
}
/// Show the Block Editor tab while a block is being edited, and return to
/// the previous tab when the editing ends.
pub fn set_block_editing(&mut self, editing: bool) {
let Some(index) = self.modules.iter().position(|module| module.id() == "block_editor")
else {
return;
};
match (editing, self.block_editing) {
(true, None) => {
self.block_editing = Some(self.active);
self.active = index;
}
(false, Some(previous)) => {
self.block_editing = None;
if self.active == index {
self.active = previous.min(self.modules.len().saturating_sub(1));
}
}
_ => {}
}
}
pub fn select(&mut self, index: usize) {
if index < self.modules.len() {
self.active = index;
@@ -550,12 +576,14 @@ impl Ribbon {
|mut acc, (i, module)| {
// The Layout module no longer has a ribbon tab — its paper-space
// tools live in the right-edge side toolbar (see ui::side_toolbar).
if module.id() == "layout" {
if module.id() == "layout"
|| (module.id() == "block_editor" && self.block_editing.is_none())
{
return acc;
}
let is_active = i == self.active;
let is_contextual = module.id() == "layout";
let is_contextual = matches!(module.id(), "layout" | "block_editor");
let holds_tool = holding_tab == Some(i);
let btn = container(
button(