forked from 1b-agent/opencadstudio-upstream
Refactor src/main.rs into modular src/app/ structure
Split the 4500-line main.rs into focused submodules under src/app/: - mod.rs: H7CAD struct, Message enum, boot/new/run - document.rs: DocumentTab, HistorySnapshot, HistoryState - helpers.rs: pure helper functions - history.rs: undo/redo logic - properties.rs: properties panel logic - layers.rs: layer sync - commands.rs: dispatch_command - cmd_result.rs: apply_cmd_result - view.rs: view, subscription, tab bar - update.rs: update message handler Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
854f07517e
commit
ed9cbc2092
19 files changed
+4150
-4519
No files matched your search
+284
@@ -0,0 +1,284 @@
|
||||
mod document;
|
||||
mod helpers;
|
||||
mod history;
|
||||
mod properties;
|
||||
mod layers;
|
||||
mod commands;
|
||||
mod cmd_result;
|
||||
mod view;
|
||||
mod update;
|
||||
|
||||
use document::DocumentTab;
|
||||
|
||||
use acadrust::types::{Color as AcadColor, LineWeight};
|
||||
use acadrust::CadDocument;
|
||||
use crate::modules::ModuleEvent;
|
||||
use crate::scene::CubeRegion;
|
||||
use crate::snap::Snapper;
|
||||
use crate::ui::{AppMenu, CommandLine, Ribbon, StatusBar};
|
||||
|
||||
use iced::time::Instant;
|
||||
use iced::window;
|
||||
use iced::{mouse, Point, Task, Theme};
|
||||
|
||||
pub(super) const POLY_START_DELAY_MS: u128 = 150;
|
||||
pub(super) const VARIES_LABEL: &str = "*VARIES*";
|
||||
|
||||
// ── Application state ──────────────────────────────────────────────────────
|
||||
|
||||
pub(super) struct H7CAD {
|
||||
start: Instant,
|
||||
tabs: Vec<DocumentTab>,
|
||||
active_tab: usize,
|
||||
tab_counter: usize,
|
||||
ribbon: Ribbon,
|
||||
app_menu: AppMenu,
|
||||
command_line: CommandLine,
|
||||
status_bar: StatusBar,
|
||||
cursor_pos: Point,
|
||||
vp_size: (f32, f32),
|
||||
snapper: Snapper,
|
||||
snap_popup_open: bool,
|
||||
/// Whether Tangent snap was enabled before a tangent-pick command started.
|
||||
pre_cmd_tangent: Option<bool>,
|
||||
/// Orthogonal drawing constraint (F8): constrains picks to 0°/90°/180°/270°.
|
||||
ortho_mode: bool,
|
||||
/// Polar tracking (F10): constrains picks to 45° angle increments.
|
||||
polar_mode: bool,
|
||||
/// Show grid lines in the viewport (F7).
|
||||
show_grid: bool,
|
||||
/// Last point committed by a drawing command — used as ortho/polar base.
|
||||
last_point: Option<glam::Vec3>,
|
||||
/// OS window Id for the floating Layer Properties Manager (None when closed).
|
||||
layer_window: Option<window::Id>,
|
||||
/// OS window Id of the primary application window.
|
||||
main_window: Option<window::Id>,
|
||||
/// In-memory clipboard: cloned entities waiting to be pasted.
|
||||
clipboard: Vec<acadrust::EntityType>,
|
||||
/// Centroid of the clipboard entities (XZ plane, Y-up).
|
||||
clipboard_centroid: glam::Vec3,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Tick(Instant),
|
||||
OpenFile,
|
||||
FileOpened(Result<(String, PathBuf, CadDocument), String>),
|
||||
SaveFile,
|
||||
SaveAs,
|
||||
PickedSavePath(Option<PathBuf>),
|
||||
ClearScene,
|
||||
SetWireframe(bool),
|
||||
/// Switch camera projection: true = Orthographic, false = Perspective.
|
||||
SetProjection(bool),
|
||||
/// Select a ribbon module tab by index.
|
||||
RibbonSelectTab(usize),
|
||||
/// A ribbon tool button was clicked — highlights the tool and dispatches its event.
|
||||
RibbonToolClick {
|
||||
tool_id: String,
|
||||
event: ModuleEvent,
|
||||
},
|
||||
// ── Application menu ──────────────────────────────────────────────────
|
||||
ToggleAppMenu,
|
||||
CloseAppMenu,
|
||||
/// Close the menu and immediately dispatch a CAD command.
|
||||
CloseAppMenuAndRun(String),
|
||||
AppMenuSearch(String),
|
||||
// ── Document tabs ──────────────────────────────────────────────────────
|
||||
/// Create a new empty document tab.
|
||||
TabNew,
|
||||
/// Switch to the given tab index.
|
||||
TabSwitch(usize),
|
||||
/// Close the given tab index.
|
||||
TabClose(usize),
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
CommandInput(String),
|
||||
CommandSubmit,
|
||||
Command(String),
|
||||
ToggleLayers,
|
||||
LayerToggleVisible(usize),
|
||||
LayerToggleLock(usize),
|
||||
LayerToggleFreeze(usize),
|
||||
LayerNew,
|
||||
LayerDelete,
|
||||
LayerSetCurrent,
|
||||
LayerSelect(usize),
|
||||
LayerRenameStart(usize),
|
||||
LayerRenameEdit(String),
|
||||
LayerColorPickerToggle(usize),
|
||||
LayerColorMorePalette,
|
||||
LayerColorSet(u8),
|
||||
LayerLinetypeSet(String),
|
||||
LayerLineweightSet(LineWeight),
|
||||
LayerTransparencyEdit(usize, String),
|
||||
LayerRenameCommit,
|
||||
CursorMoved(Point),
|
||||
ViewportClick,
|
||||
ViewportMove(Point),
|
||||
ViewportLeftPress,
|
||||
ViewportLeftRelease,
|
||||
ViewportRightPress,
|
||||
ViewportRightRelease,
|
||||
ViewportMiddlePress,
|
||||
ViewportMiddleRelease,
|
||||
ViewportScroll(mouse::ScrollDelta),
|
||||
ViewportExit,
|
||||
ViewCubeSnap(CubeRegion),
|
||||
WindowResized(f32, f32),
|
||||
/// Enter pressed globally — finalises the active command (no text-input involvement).
|
||||
CommandFinalize,
|
||||
/// Escape pressed globally — cancels the active command.
|
||||
CommandEscape,
|
||||
/// Toggle the global snap on/off (OSNAP button body click).
|
||||
ToggleSnapEnabled,
|
||||
/// Toggle grid-snap on/off — F9 / SNAP status-bar button.
|
||||
ToggleGridSnap,
|
||||
/// Toggle grid display in the viewport — F7 / GRID status-bar button.
|
||||
ToggleGrid,
|
||||
/// Toggle orthogonal drawing constraint — F8 / ORTHO status-bar button.
|
||||
ToggleOrtho,
|
||||
/// Toggle polar-angle constraint (45° increments) — F10 / POLAR status-bar button.
|
||||
TogglePolar,
|
||||
/// Toggle an individual snap mode (from popup row click).
|
||||
ToggleSnap(crate::snap::SnapType),
|
||||
/// Open / close the OSNAP popup (▾ arrow click).
|
||||
ToggleSnapPopup,
|
||||
/// Close the OSNAP popup (click-catcher outside the panel).
|
||||
CloseSnapPopup,
|
||||
/// Enable all snap modes.
|
||||
SnapSelectAll,
|
||||
/// Disable all snap modes.
|
||||
SnapClearAll,
|
||||
/// Toggle a ribbon dropdown open/closed.
|
||||
ToggleRibbonDropdown(String),
|
||||
/// Close any open ribbon dropdown (click-catcher outside the panel).
|
||||
CloseRibbonDropdown,
|
||||
/// User selected a specific item from a ribbon dropdown.
|
||||
DropdownSelectItem {
|
||||
dropdown_id: &'static str,
|
||||
cmd: &'static str,
|
||||
},
|
||||
/// Delete key — erase all currently selected entities.
|
||||
DeleteSelected,
|
||||
Undo,
|
||||
Redo,
|
||||
UndoMany(usize),
|
||||
RedoMany(usize),
|
||||
// ── Ribbon ────────────────────────────────────────────────────────────
|
||||
/// User selected a layer from the layer combobox in the ribbon.
|
||||
RibbonLayerChanged(String),
|
||||
/// User changed the active color in the Properties toolbar.
|
||||
RibbonColorChanged(AcadColor),
|
||||
/// Toggle the full ACI palette inside the ribbon color picker.
|
||||
RibbonColorPaletteToggle,
|
||||
/// User changed the active linetype in the Properties toolbar.
|
||||
RibbonLinetypeChanged(String),
|
||||
/// User changed the active lineweight in the Properties toolbar.
|
||||
RibbonLineweightChanged(LineWeight),
|
||||
|
||||
// ── Properties panel ──────────────────────────────────────────────────
|
||||
/// User selected a layer from the layer pick_list in the Properties panel.
|
||||
PropLayerChanged(String),
|
||||
PropSelectionGroupChanged(crate::ui::properties::SelectionGroup),
|
||||
/// User picked a color from the Properties color picker.
|
||||
PropColorChanged(AcadColor),
|
||||
/// User selected a lineweight from the Properties pick_list.
|
||||
PropLwChanged(LineWeight),
|
||||
/// User selected a linetype from the linetype pick_list.
|
||||
PropLinetypeChanged(String),
|
||||
/// User toggled a boolean property (e.g. Invisible).
|
||||
PropBoolToggle(&'static str),
|
||||
/// User selected a hatch pattern from the pattern pick_list in Properties.
|
||||
PropHatchPatternChanged(String),
|
||||
/// User selected a generic choice field in the Properties panel.
|
||||
PropGeomChoiceChanged {
|
||||
field: &'static str,
|
||||
value: String,
|
||||
},
|
||||
/// User is typing in an editable geometry field (live buffer update).
|
||||
PropGeomInput {
|
||||
field: &'static str,
|
||||
value: String,
|
||||
},
|
||||
/// User committed a geometry/common field edit (Enter pressed).
|
||||
PropGeomCommit(&'static str),
|
||||
/// Toggle the inline color picker dropdown open/closed.
|
||||
PropColorPickerToggle,
|
||||
/// Toggle the full ACI colour palette expansion.
|
||||
PropColorPaletteToggle,
|
||||
/// Switch to a named layout ("Model" or paper space layout name).
|
||||
LayoutSwitch(String),
|
||||
/// Create a new paper space layout.
|
||||
LayoutCreate,
|
||||
/// A window was closed by the OS (e.g. the user clicked the title-bar ✕).
|
||||
OsWindowClosed(window::Id),
|
||||
/// No-op — used as a fallback when a TabEvent has no host mapping.
|
||||
Noop,
|
||||
}
|
||||
|
||||
impl H7CAD {
|
||||
fn new() -> Self {
|
||||
let first_tab = DocumentTab::new_drawing(1);
|
||||
let mut app = Self {
|
||||
start: Instant::now(),
|
||||
tabs: vec![first_tab],
|
||||
active_tab: 0,
|
||||
tab_counter: 1,
|
||||
ribbon: Ribbon::new(),
|
||||
app_menu: AppMenu::new(),
|
||||
command_line: CommandLine::new(),
|
||||
status_bar: StatusBar::new(),
|
||||
cursor_pos: Point::ORIGIN,
|
||||
vp_size: (1280.0, 720.0),
|
||||
snapper: Snapper::default(),
|
||||
snap_popup_open: false,
|
||||
pre_cmd_tangent: None,
|
||||
ortho_mode: false,
|
||||
polar_mode: false,
|
||||
show_grid: false,
|
||||
last_point: None,
|
||||
layer_window: None,
|
||||
main_window: None,
|
||||
clipboard: Vec::new(),
|
||||
clipboard_centroid: glam::Vec3::ZERO,
|
||||
};
|
||||
app.sync_ribbon_layers();
|
||||
app
|
||||
}
|
||||
|
||||
/// Boot function for `iced::daemon`: returns initial state plus a task that
|
||||
/// opens the primary application window.
|
||||
fn boot() -> (Self, Task<Message>) {
|
||||
use helpers::build_window_icon;
|
||||
let state = Self::new();
|
||||
let (id, open_task) = window::open(window::Settings {
|
||||
maximized: true,
|
||||
icon: window::icon::from_rgba(build_window_icon(), 32, 32).ok(),
|
||||
..Default::default()
|
||||
});
|
||||
let mut s = state;
|
||||
s.main_window = Some(id);
|
||||
let task = open_task.map(|_| Message::Noop);
|
||||
(s, task)
|
||||
}
|
||||
}
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn run() -> iced::Result {
|
||||
iced::daemon(H7CAD::boot, H7CAD::update, H7CAD::view)
|
||||
.subscription(H7CAD::subscription)
|
||||
.title(|state: &H7CAD, window_id: window::Id| {
|
||||
if Some(window_id) == state.layer_window {
|
||||
"Layer Properties Manager".to_string()
|
||||
} else if let Some(tab) = state.tabs.get(state.active_tab) {
|
||||
let dot = if tab.dirty { "● " } else { "" };
|
||||
let name = tab.tab_display_name();
|
||||
format!("{}H7CAD — {}", dot, name)
|
||||
} else {
|
||||
"H7CAD".to_string()
|
||||
}
|
||||
})
|
||||
.theme(Theme::Dark)
|
||||
.run()
|
||||
}
|
||||
Reference in new issue
Block a user