forked from 1b-agent/opencadstudio-upstream
REFEDIT picks an INSERT, extracts the block entities into model space with the INSERT transform applied (translate + rotate + uniform scale), and enters an editing session. Any normal modify command works on the temporary entities. REFCLOSE SAVE applies the inverse transform, replaces the block definition, and rebuilds all derived caches (hatch/image/mesh), so every INSERT of that block reflects the edits immediately. REFCLOSE DISCARD removes the temp entities without changing the block. Non-uniform-scale inserts are rejected with an error message. RefEditSession is stored on DocumentTab; undo snapshots bracket the begin and close operations. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
101 lines
3.4 KiB
Rust
101 lines
3.4 KiB
Rust
use crate::scene::Scene;
|
|
use crate::ui::{LayerPanel, PropertiesPanel};
|
|
use crate::command::CadCommand;
|
|
use crate::snap::SnapResult;
|
|
use crate::scene::grip::GripEdit;
|
|
use crate::scene::GripDef;
|
|
use crate::modules::home::modify::refedit::RefEditSession;
|
|
use acadrust::{CadDocument, Handle};
|
|
use acadrust::tables::Ucs;
|
|
use crate::linetypes;
|
|
use std::path::PathBuf;
|
|
use iced;
|
|
|
|
// ── Per-document tab state ─────────────────────────────────────────────────
|
|
|
|
pub(super) struct DocumentTab {
|
|
pub(super) scene: Scene,
|
|
pub(super) current_path: Option<PathBuf>,
|
|
pub(super) dirty: bool,
|
|
pub(super) tab_title: String,
|
|
pub(super) properties: PropertiesPanel,
|
|
pub(super) layers: LayerPanel,
|
|
pub(super) active_cmd: Option<Box<dyn CadCommand>>,
|
|
pub(super) last_cmd: Option<String>,
|
|
pub(super) snap_result: Option<SnapResult>,
|
|
pub(super) active_grip: Option<GripEdit>,
|
|
pub(super) selected_grips: Vec<GripDef>,
|
|
pub(super) selected_handle: Option<Handle>,
|
|
pub(super) wireframe: bool,
|
|
pub(super) visual_style: String,
|
|
pub(super) last_cursor_world: glam::Vec3,
|
|
pub(super) last_cursor_screen: iced::Point,
|
|
pub(super) history: HistoryState,
|
|
pub(super) active_layer: String,
|
|
/// Currently active UCS. `None` means WCS (identity transform).
|
|
pub(super) active_ucs: Option<Ucs>,
|
|
/// Custom model-space background color. `None` = default dark grey.
|
|
pub(super) bg_color: Option<[f32; 4]>,
|
|
/// Custom paper-space background color. `None` = default off-white grey.
|
|
pub(super) paper_bg_color: Option<[f32; 4]>,
|
|
/// Active REFEDIT session, if any.
|
|
pub(super) refedit_session: Option<RefEditSession>,
|
|
}
|
|
|
|
impl DocumentTab {
|
|
pub(super) fn new_drawing(n: usize) -> Self {
|
|
let mut scene = Scene::new();
|
|
linetypes::populate_document(&mut scene.document);
|
|
Self {
|
|
scene,
|
|
current_path: None,
|
|
dirty: false,
|
|
tab_title: format!("Drawing{}", n),
|
|
properties: PropertiesPanel::empty(),
|
|
layers: LayerPanel::default(),
|
|
active_cmd: None,
|
|
last_cmd: None,
|
|
snap_result: None,
|
|
active_grip: None,
|
|
selected_grips: vec![],
|
|
selected_handle: None,
|
|
wireframe: false,
|
|
visual_style: "Shaded".into(),
|
|
last_cursor_world: glam::Vec3::ZERO,
|
|
last_cursor_screen: iced::Point::ORIGIN,
|
|
history: HistoryState::default(),
|
|
active_layer: "0".to_string(),
|
|
active_ucs: None,
|
|
bg_color: None,
|
|
paper_bg_color: None,
|
|
refedit_session: None,
|
|
}
|
|
}
|
|
|
|
pub(super) fn tab_display_name(&self) -> String {
|
|
match &self.current_path {
|
|
Some(p) => p
|
|
.file_name()
|
|
.unwrap_or_default()
|
|
.to_string_lossy()
|
|
.to_string(),
|
|
None => self.tab_title.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(super) struct HistorySnapshot {
|
|
pub(super) document: CadDocument,
|
|
pub(super) current_layout: String,
|
|
pub(super) selected: Vec<Handle>,
|
|
pub(super) dirty: bool,
|
|
pub(super) label: String,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub(super) struct HistoryState {
|
|
pub(super) undo_stack: Vec<HistorySnapshot>,
|
|
pub(super) redo_stack: Vec<HistorySnapshot>,
|
|
}
|