forked from 1b-agent/opencadstudio-upstream
feat(block): fade context, free editing and a Save/Discard toolbar in REFEDIT
In-place block edit now reads more like a dedicated editor: - Fade everything except the edited entities toward the background (wires, block instances and 3D solid meshes) so the block stands out while the surrounding drawing stays visible for context. - Don't leave a command active on enter — the user edits the block's geometry freely (move, grips, draw, erase) and finishes explicitly. - Show the right-edge vertical toolbar with clickable Save (REFCLOSE_SAVE) and Discard (REFCLOSE_DISCARD) buttons while a session is active, so the edit can be ended without typing. The fade clears on save/discard, restoring full brightness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
5052d2338c
commit
038052f5f9
4 files changed
+97
-9
No files matched your search
+16
-5
@@ -2192,6 +2192,13 @@ impl OpenCADStudio {
|
||||
}
|
||||
self.tabs[i].refedit_session.as_mut().unwrap().temp_handles = temp_handles.clone();
|
||||
|
||||
// Fade everything except the entities being edited, so the
|
||||
// surrounding drawing stays visible for context but the block's
|
||||
// geometry stands out. (#136)
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.set_refedit_keep(Some(temp_handles.iter().copied().collect()));
|
||||
|
||||
// Select the temp entities so user can see what they're editing.
|
||||
self.tabs[i].scene.deselect_all();
|
||||
for h in &temp_handles {
|
||||
@@ -2199,14 +2206,13 @@ impl OpenCADStudio {
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
|
||||
// No active command — the user edits the block's geometry freely
|
||||
// (move, grips, draw, erase…) and runs REFCLOSE when done. (#136)
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.command_line.push_info(&format!(
|
||||
"REFEDIT: Editing block \"{}\". Use REFCLOSE when done.",
|
||||
"REFEDIT: Editing block \"{}\". Run REFCLOSE to save, REFCLOSE_DISCARD to cancel.",
|
||||
insert.block_name
|
||||
));
|
||||
use crate::modules::draw::modify::refedit::RefCloseCommand;
|
||||
let cmd_obj = RefCloseCommand::new();
|
||||
self.command_line.push_info(&cmd_obj.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
||||
}
|
||||
|
||||
"REFCLOSE" => {
|
||||
@@ -2291,6 +2297,9 @@ impl OpenCADStudio {
|
||||
"REFCLOSE: Block \"{}\" saved. All references updated.",
|
||||
session.block_name
|
||||
));
|
||||
// End the edit fade before rebuilding, so the restored geometry
|
||||
// recolours bright. (#136)
|
||||
self.tabs[i].scene.set_refedit_keep(None);
|
||||
// Rebuild hatch/image/mesh caches since block content changed.
|
||||
self.tabs[i].scene.rebuild_derived_caches();
|
||||
}
|
||||
@@ -2307,6 +2316,8 @@ impl OpenCADStudio {
|
||||
// Remove temp entities without modifying the block.
|
||||
self.tabs[i].scene.erase_entities(&session.temp_handles);
|
||||
self.tabs[i].scene.deselect_all();
|
||||
// End the edit fade — restore the drawing to full brightness.
|
||||
self.tabs[i].scene.set_refedit_keep(None);
|
||||
self.command_line
|
||||
.push_output("REFCLOSE: Changes discarded.");
|
||||
}
|
||||
|
||||
@@ -687,6 +687,16 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
// In-place block edit (REFEDIT): right-edge toolbar with Save / Discard
|
||||
// so the edit can be finished by clicking. (#136)
|
||||
if tab.refedit_session.is_some() && !tab.is_start {
|
||||
if let Some(tb) = crate::ui::side_toolbar::view(
|
||||
&crate::modules::draw::modify::refedit::refedit_tools(),
|
||||
) {
|
||||
viewport_stack = viewport_stack.push(tb);
|
||||
}
|
||||
}
|
||||
|
||||
// Quick Properties: compact floating property panel on selection,
|
||||
// anchored at the canvas top-left so it doesn't track the cursor.
|
||||
if self.quick_properties && !tab.is_start {
|
||||
|
||||
@@ -17,6 +17,26 @@ use acadrust::{EntityType, Handle};
|
||||
use glam::Vec3;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
|
||||
/// Right-edge side-toolbar buttons shown while a REFEDIT session is active:
|
||||
/// save the in-place block edit, or discard it. (#136)
|
||||
pub fn refedit_tools() -> Vec<ToolDef> {
|
||||
vec![
|
||||
ToolDef {
|
||||
id: "REFCLOSE_SAVE",
|
||||
label: "Save Block Edit",
|
||||
icon: IconKind::Svg(include_bytes!("../../../../assets/icons/mt_ok.svg")),
|
||||
event: ModuleEvent::Command("REFCLOSE_SAVE".to_string()),
|
||||
},
|
||||
ToolDef {
|
||||
id: "REFCLOSE_DISCARD",
|
||||
label: "Discard Block Edit",
|
||||
icon: IconKind::Svg(include_bytes!("../../../../assets/icons/mt_cancel.svg")),
|
||||
event: ModuleEvent::Command("REFCLOSE_DISCARD".to_string()),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// ── Session state (held in DocumentTab) ───────────────────────────────────
|
||||
|
||||
|
||||
+51
-4
@@ -663,6 +663,11 @@ pub struct Scene {
|
||||
/// `tessellate_block`'s visibility test skips these, so they neither
|
||||
/// render nor hit-test until isolation ends.
|
||||
pub hidden: HashSet<Handle>,
|
||||
/// During in-place block edit (REFEDIT), the handles of the entities being
|
||||
/// edited. Everything else is rendered faded toward the background so the
|
||||
/// edited geometry stands out while the surrounding drawing stays visible
|
||||
/// for context. `None` = not editing. (#136)
|
||||
pub refedit_keep: Option<HashSet<Handle>>,
|
||||
/// Entity drawn with the selection-highlight colour without being part
|
||||
/// of the real selection — used to preview a row in the cycling list box.
|
||||
pub hover_highlight: Option<Handle>,
|
||||
@@ -883,6 +888,7 @@ impl Scene {
|
||||
document: CadDocument::new(),
|
||||
selected: HashSet::default(),
|
||||
hidden: HashSet::default(),
|
||||
refedit_keep: None,
|
||||
hover_highlight: None,
|
||||
transparency_display: true,
|
||||
selection_filter: HashSet::default(),
|
||||
@@ -1097,14 +1103,22 @@ impl Scene {
|
||||
// re-entrantly through `render_style` inside a `&mut self` loop.
|
||||
// Covers both top-level solid meshes and block-definition meshes
|
||||
// (instanced per INSERT), so a solid recolours wherever it lives.
|
||||
// During REFEDIT, solids outside the edited set render faded.
|
||||
let bg = self.bg_color;
|
||||
let colors: HashMap<Handle, [f32; 4]> = self
|
||||
.meshes
|
||||
.keys()
|
||||
.chain(self.block_meshes.keys())
|
||||
.filter_map(|&h| {
|
||||
self.document
|
||||
.get_entity(h)
|
||||
.map(|e| (h, self.render_style(e).0))
|
||||
self.document.get_entity(h).map(|e| {
|
||||
let mut c = self.render_style(e).0;
|
||||
if let Some(keep) = &self.refedit_keep {
|
||||
if !keep.contains(&h) {
|
||||
c = crate::scene::cache::block_cache::fade_toward_bg(c, bg);
|
||||
}
|
||||
}
|
||||
(h, c)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
for (h, set) in self.meshes.iter_mut().chain(self.block_meshes.iter_mut()) {
|
||||
@@ -1116,6 +1130,31 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter / leave the REFEDIT fade. `keep` holds the edited entities (left
|
||||
/// bright); everything else renders faded. Re-tessellates wires and
|
||||
/// recolours solids so the change shows immediately. (#136)
|
||||
pub fn set_refedit_keep(&mut self, keep: Option<HashSet<Handle>>) {
|
||||
self.refedit_keep = keep;
|
||||
self.recolor_meshes();
|
||||
self.bump_geometry();
|
||||
}
|
||||
|
||||
/// Fade the colours of wires that belong to entities outside the REFEDIT
|
||||
/// keep set (no-op when not editing). The geometry is untouched, so
|
||||
/// hit-testing still works on faded entities.
|
||||
fn apply_refedit_fade(&self, wires: &mut [WireModel], bg: [f32; 4]) {
|
||||
let Some(keep) = &self.refedit_keep else {
|
||||
return;
|
||||
};
|
||||
for w in wires.iter_mut() {
|
||||
let keep_bright =
|
||||
Self::handle_from_wire_name(&w.name).is_some_and(|h| keep.contains(&h));
|
||||
if !keep_bright {
|
||||
w.color = crate::scene::cache::block_cache::fade_toward_bg(w.color, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Switch the active layout. Bumps `geometry_epoch` so the wire cache
|
||||
/// re-tessellates — `render_style`'s `adapt_to_bg` picks the model or
|
||||
/// paper background depending on `current_layout`, so cached wires
|
||||
@@ -1779,7 +1818,9 @@ impl Scene {
|
||||
};
|
||||
let block = self.model_space_block_handle();
|
||||
let t_tess = iced::time::Instant::now();
|
||||
let arc = Arc::new(self.wires_for_block_culled(block, tess_region, wpp, None, None));
|
||||
let mut wires = self.wires_for_block_culled(block, tess_region, wpp, None, None);
|
||||
self.apply_refedit_fade(&mut wires, self.bg_color);
|
||||
let arc = Arc::new(wires);
|
||||
self.last_tess_ms.set(t_tess.elapsed().as_secs_f32() * 1000.0);
|
||||
self.last_tess_wires.set(arc.len());
|
||||
let stored_region = tess_region
|
||||
@@ -1818,6 +1859,12 @@ impl Scene {
|
||||
let sheet_name = sheet.value().to_string();
|
||||
wires.retain(|w| w.name != sheet_name);
|
||||
}
|
||||
let bg = if self.current_layout == "Model" {
|
||||
self.bg_color
|
||||
} else {
|
||||
self.paper_bg_color
|
||||
};
|
||||
self.apply_refedit_fade(&mut wires, bg);
|
||||
let arc = Arc::new(wires);
|
||||
*self.paper_sheet_cache.borrow_mut() = Some((key, Arc::clone(&arc)));
|
||||
arc
|
||||
|
||||
Reference in new issue
Block a user