refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
//! `dialog` arms and helpers, split out of the original `update.rs` (#mechanical decomposition).
|
|
|
|
|
|
|
|
|
|
#![allow(unused_imports)]
|
|
|
|
|
use super::util::*;
|
2026-06-27 11:50:11 +03:00
|
|
|
use super::{format_size, VIEWCUBE_HIT_SIZE};
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
use crate::app::helpers::{
|
|
|
|
|
ortho_constrain, parse_coord, polar_constrain_near, ucs_rotate_vec, ucs_to_wcs, ucs_z_axis,
|
|
|
|
|
CoordKind,
|
|
|
|
|
};
|
|
|
|
|
use crate::app::{Message, OpenCADStudio, POLY_START_DELAY_MS};
|
|
|
|
|
use crate::modules::ModuleEvent;
|
|
|
|
|
use crate::scene::pick::grip::{find_hit_grip, find_hit_grip_paper, find_hit_grip_rte, GripEdit};
|
|
|
|
|
use crate::scene::model::object::GripApply;
|
|
|
|
|
use crate::scene::{
|
2026-06-27 11:50:11 +03:00
|
|
|
self, hover_id, CubeRegion, Scene, VIEWCUBE_DRAW_PX, VIEWCUBE_PAD, VIEWCUBE_PX,
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
};
|
|
|
|
|
use crate::ui::PropertiesPanel;
|
|
|
|
|
use acadrust::types::Color as AcadColor;
|
|
|
|
|
use acadrust::{EntityType as AcadEntityType, Handle};
|
|
|
|
|
use iced::time::Instant;
|
|
|
|
|
use iced::{mouse, Point, Task};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl OpenCADStudio {
|
|
|
|
|
pub(in crate::app) fn open_save_dialog_window(&mut self, tab_idx: usize) -> Task<Message> {
|
2026-07-23 13:32:26 +03:00
|
|
|
// Default the format dropdown to the loaded file's own format — its
|
|
|
|
|
// DWG-vs-DXF kind (from the extension) and its version (from the parsed
|
|
|
|
|
// document) — so Save-As round-trips the format instead of silently
|
2026-07-27 22:37:47 +03:00
|
|
|
// re-targeting it. A new/unsaved drawing has no source format, so it
|
|
|
|
|
// uses the application-wide default chosen in Options (#529).
|
|
|
|
|
self.save_dialog_format = if let Some(path) = &self.tabs[tab_idx].current_path {
|
|
|
|
|
let document = &self.tabs[tab_idx].scene.document;
|
2026-08-03 12:47:48 +03:00
|
|
|
let is_dxf = crate::io::source_is_dxf(Some(path), document);
|
2026-07-27 22:37:47 +03:00
|
|
|
let version = if is_dxf {
|
|
|
|
|
document.version
|
|
|
|
|
} else {
|
|
|
|
|
document.dwg_source_version.unwrap_or(document.version)
|
|
|
|
|
};
|
|
|
|
|
crate::io::format_for_version(version, is_dxf)
|
|
|
|
|
} else {
|
|
|
|
|
self.default_save_format.clone()
|
|
|
|
|
};
|
2026-06-28 02:46:13 +03:00
|
|
|
|
2026-07-11 09:12:36 +03:00
|
|
|
// Pre-fill the default file name from the current path or the tab name;
|
|
|
|
|
// the destination folder comes from the native OS dialog that follows.
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
if let Some(p) = &self.tabs[tab_idx].current_path.clone() {
|
|
|
|
|
if let Some(name) = p.file_name() {
|
|
|
|
|
self.save_dialog_filename = name.to_string_lossy().into_owned();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let (ext, _) = crate::io::parse_save_format(&self.save_dialog_format);
|
|
|
|
|
self.save_dialog_filename = format!("{}.{ext}", self.tabs[tab_idx].tab_display_name());
|
|
|
|
|
}
|
2026-08-03 12:47:48 +03:00
|
|
|
if self.tabs[tab_idx].recovery_save_as_required {
|
|
|
|
|
let (ext, _) = crate::io::parse_save_format(&self.save_dialog_format);
|
|
|
|
|
let stem = std::path::Path::new(&self.save_dialog_filename)
|
|
|
|
|
.file_stem()
|
|
|
|
|
.map(|value| value.to_string_lossy().into_owned())
|
|
|
|
|
.unwrap_or_else(|| self.tabs[tab_idx].tab_display_name());
|
|
|
|
|
self.save_dialog_filename = format!("{stem}_recovered.{ext}");
|
|
|
|
|
}
|
2026-06-29 11:47:37 +03:00
|
|
|
self.aec_drop_acknowledged = false;
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
self.active_modal = Some(crate::app::ModalKind::SaveDialog);
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(in crate::app) fn close_save_dialog_window(&mut self) -> Task<Message> {
|
2026-06-29 11:47:37 +03:00
|
|
|
self.aec_drop_acknowledged = false;
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
if self.active_modal == Some(crate::app::ModalKind::SaveDialog) {
|
|
|
|
|
self.active_modal = None;
|
2026-08-01 18:24:05 +03:00
|
|
|
self.reset_modal_geometry();
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(in crate::app) fn open_unsaved_dialog_window(&mut self) -> Task<Message> {
|
|
|
|
|
self.active_modal = Some(crate::app::ModalKind::Unsaved);
|
|
|
|
|
// The unsaved-changes prompt renders inside the main window, so bring
|
|
|
|
|
// that window to the foreground — a close signal can arrive while the
|
|
|
|
|
// app is backgrounded, leaving the prompt unseen behind other windows.
|
|
|
|
|
// `gain_focus` alone is ignored by most Linux WMs (focus-stealing
|
|
|
|
|
// prevention), so pair it with an urgency hint so the window is at
|
|
|
|
|
// least flagged for attention when the compositor blocks the raise.
|
|
|
|
|
match self.main_window {
|
|
|
|
|
Some(id) => Task::batch([
|
|
|
|
|
iced::window::gain_focus(id),
|
|
|
|
|
iced::window::request_user_attention(
|
|
|
|
|
id,
|
|
|
|
|
Some(iced::window::UserAttention::Critical),
|
|
|
|
|
),
|
|
|
|
|
]),
|
|
|
|
|
None => Task::none(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(in crate::app) fn close_unsaved_dialog_window(&mut self) -> Task<Message> {
|
|
|
|
|
if self.active_modal == Some(crate::app::ModalKind::Unsaved) {
|
|
|
|
|
self.active_modal = None;
|
2026-08-01 18:24:05 +03:00
|
|
|
self.reset_modal_geometry();
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEvent) -> Task<Message> {
|
fix(start): let the welcome page's own buttons work (#388, #389)
Donate, Send Feedback, OCS Web and Manage > About all answered "No
drawing open — use New or Open first." and did nothing.
Two doors guarded the welcome tab and the outer one shadowed the inner.
`dispatch_command` already knew which commands stand alone — About,
Donate, Report and the links were all on its list — but
`on_ribbon_tool_click` refused every ribbon event before dispatch was
ever reached.
The welcome page's own buttons emit RibbonToolClick, so they are only
ever clickable while `is_start` holds: the refusal covered exactly the
case they exist in, and they had never worked. New Drawing / Open File /
Plugins send their own messages, which is why the page still looked
alive.
So the ribbon door now defers commands to dispatch and keeps refusing
only what touches the scene. The policy stays in one place instead of
being half-remembered in two. A drawing tool is still turned away on the
welcome tab (#299) — the wording just comes from dispatch now.
The same gap was swallowing tools the issues never mentioned: CUI and
ALIASEDIT configure the application, not a drawing, and were missing
from the list, so fixing only the ribbon door would have moved their
refusal one door down rather than removing it. HELP and CUILOAD /
CUIIMPORT join them.
The new test drives the real message path and fails on the old code with
the reported wording. It reads the allowlist from source rather than
dispatching DONATE / REPORT / WEBVERSION, which shell out to a browser —
that also fails the day someone adds a welcome-page button and forgets
the list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:29:42 +03:00
|
|
|
// On the Start page there is no drawing to act on, so a tool that
|
|
|
|
|
// touches the scene is inert — point the user at New / Open
|
|
|
|
|
// instead of running it into the empty welcome tab (#299).
|
|
|
|
|
//
|
|
|
|
|
// Commands are exempt: `dispatch_command` already decides which
|
|
|
|
|
// ones stand alone (About, Donate, Report, the web link…) and
|
|
|
|
|
// reports the rest. Refusing them here shadowed that list and
|
|
|
|
|
// killed the welcome page's own buttons, which by definition can
|
|
|
|
|
// only ever be clicked while `is_start` holds (#388, #389).
|
|
|
|
|
// Keep the policy in one place — this door must not second-guess
|
|
|
|
|
// it. Every other event below mutates the scene or its panels.
|
|
|
|
|
if self.tabs[self.active_tab].is_start && !matches!(event, ModuleEvent::Command(_)) {
|
2026-07-11 10:11:28 +03:00
|
|
|
self.ribbon.close_dropdown();
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
.push_info(crate::t!("No drawing open — use New or Open first.").as_ref());
|
2026-07-11 10:11:28 +03:00
|
|
|
return Task::none();
|
|
|
|
|
}
|
2026-07-05 18:04:55 +03:00
|
|
|
// Dismiss any open dropdown / collapsed-panel flyout on tool use,
|
|
|
|
|
// and remember this tool as its panel's last-used one.
|
|
|
|
|
self.ribbon.close_dropdown();
|
|
|
|
|
self.ribbon.note_panel_tool(&tool_id);
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
self.ribbon.activate_tool(&tool_id);
|
|
|
|
|
match event {
|
fix(ribbon): highlight follows each tool's mechanism; start-tab dimming (#355)
Tool highlights used to stick: every click set active_tool, but only
interactive commands and a hand-maintained per-modal list ever cleared
it, so one-shots (Cleanup/AUDIT, view actions) and unlisted dialogs
(Customization/ALIASEDIT, CUI, Plugin Manager…) stayed blue forever.
Now the rule is mechanical: state toggles light from state only (the
click highlight drops immediately), interactive commands stay lit
while running, dialog openers stay lit until the modal closes (generic
clear replaces the per-modal list), and one-shots never keep it — the
dispatch site turns the highlight off when nothing stayed running.
On the Start tab, tools whose command the start gate refuses now render
dimmed (grey icon + label) across all button styles, dropdowns and the
quick-access strip; start_allowed() is the single shared authority.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 00:13:48 +03:00
|
|
|
ModuleEvent::Command(cmd) => {
|
|
|
|
|
let task = self.dispatch_command(&cmd);
|
|
|
|
|
// One-shot tools (view changes, clipboard, toggles,
|
|
|
|
|
// audits…) leave nothing running: no interactive
|
|
|
|
|
// command and no dialog. Their highlight would stick
|
|
|
|
|
// forever — turn it off now. Interactive commands and
|
|
|
|
|
// dialog owners keep theirs; the command end / modal
|
|
|
|
|
// close clears those. (#355)
|
|
|
|
|
let i = self.active_tab;
|
2026-08-04 18:21:52 +03:00
|
|
|
if self.tabs[i].active_cmd.is_none()
|
|
|
|
|
&& self.active_modal.is_none()
|
|
|
|
|
&& !self.tabs[i].pan_mode
|
|
|
|
|
&& !self.tabs[i].orbit_mode
|
|
|
|
|
{
|
fix(ribbon): highlight follows each tool's mechanism; start-tab dimming (#355)
Tool highlights used to stick: every click set active_tool, but only
interactive commands and a hand-maintained per-modal list ever cleared
it, so one-shots (Cleanup/AUDIT, view actions) and unlisted dialogs
(Customization/ALIASEDIT, CUI, Plugin Manager…) stayed blue forever.
Now the rule is mechanical: state toggles light from state only (the
click highlight drops immediately), interactive commands stay lit
while running, dialog openers stay lit until the modal closes (generic
clear replaces the per-modal list), and one-shots never keep it — the
dispatch site turns the highlight off when nothing stayed running.
On the Start tab, tools whose command the start gate refuses now render
dimmed (grey icon + label) across all button styles, dropdowns and the
quick-access strip; start_allowed() is the single shared authority.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 00:13:48 +03:00
|
|
|
self.ribbon.deactivate_tool();
|
|
|
|
|
}
|
|
|
|
|
return task;
|
|
|
|
|
}
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
ModuleEvent::OpenFileDialog => {
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
.push_info(crate::t!("Open DWG/DXF: not yet implemented.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
ModuleEvent::ClearModels => {
|
|
|
|
|
let i = self.active_tab;
|
|
|
|
|
self.tabs[i].scene.clear();
|
|
|
|
|
self.tabs[i].properties = PropertiesPanel::empty();
|
2026-08-02 22:09:25 +03:00
|
|
|
self.command_line.push_output(crate::t!("Scene cleared.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
ModuleEvent::SetWireframe(w) => {
|
|
|
|
|
let i = self.active_tab;
|
|
|
|
|
self.tabs[i].wireframe = w;
|
|
|
|
|
self.ribbon.set_wireframe(w);
|
|
|
|
|
self.tabs[i].visual_style = if w {
|
|
|
|
|
"Wireframe".into()
|
|
|
|
|
} else {
|
|
|
|
|
"Shaded".into()
|
|
|
|
|
};
|
|
|
|
|
self.command_line.push_output(if w {
|
|
|
|
|
"Visual style: Wireframe"
|
|
|
|
|
} else {
|
|
|
|
|
"Visual style: Shaded"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
ModuleEvent::ToggleLayers => {
|
|
|
|
|
return Task::done(Message::ToggleLayers);
|
|
|
|
|
}
|
|
|
|
|
ModuleEvent::PluginFileDialog {
|
|
|
|
|
command,
|
|
|
|
|
title,
|
|
|
|
|
filter_name,
|
|
|
|
|
extensions,
|
|
|
|
|
} => {
|
|
|
|
|
return Task::perform(
|
|
|
|
|
async move {
|
|
|
|
|
let exts: Vec<&str> =
|
|
|
|
|
extensions.iter().map(|s| s.as_str()).collect();
|
2026-07-21 09:27:44 +03:00
|
|
|
let path = crate::sys::file_dialog()
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
.set_title(title)
|
|
|
|
|
.add_filter(filter_name, &exts)
|
|
|
|
|
.add_filter("All Files", &["*"])
|
|
|
|
|
.pick_file()
|
|
|
|
|
.await
|
|
|
|
|
.map(|h| crate::sys::handle_path(&h));
|
|
|
|
|
(command, path)
|
|
|
|
|
},
|
|
|
|
|
|(command, path)| Message::PluginFileDialogResult { command, path },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
fix(ribbon): highlight follows each tool's mechanism; start-tab dimming (#355)
Tool highlights used to stick: every click set active_tool, but only
interactive commands and a hand-maintained per-modal list ever cleared
it, so one-shots (Cleanup/AUDIT, view actions) and unlisted dialogs
(Customization/ALIASEDIT, CUI, Plugin Manager…) stayed blue forever.
Now the rule is mechanical: state toggles light from state only (the
click highlight drops immediately), interactive commands stay lit
while running, dialog openers stay lit until the modal closes (generic
clear replaces the per-modal list), and one-shots never keep it — the
dispatch site turns the highlight off when nothing stayed running.
On the Start tab, tools whose command the start gate refuses now render
dimmed (grey icon + label) across all button styles, dropdowns and the
quick-access strip; start_allowed() is the single shared authority.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 00:13:48 +03:00
|
|
|
// Every non-Command event above is a one-shot (state toggle,
|
|
|
|
|
// clear, dialog spawn) — nothing stays running to clear the
|
|
|
|
|
// highlight later, so turn it off here. (#355)
|
|
|
|
|
self.ribbon.deactivate_tool();
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn on_unsaved_dialog_discard(&mut self) -> Task<Message> {
|
|
|
|
|
match self.pending_close.take() {
|
|
|
|
|
Some(crate::app::PendingClose::Tab(idx)) => {
|
|
|
|
|
let close_win = self.close_unsaved_dialog_window();
|
2026-06-29 16:39:42 +03:00
|
|
|
// Discarded — drop this tab's autosave recovery copy.
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
|
let _ = std::fs::remove_file(self.autosave_target(idx));
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
if self.tabs.len() == 1 {
|
|
|
|
|
self.tab_counter += 1;
|
|
|
|
|
self.tabs[0] =
|
|
|
|
|
crate::app::document::DocumentTab::new_drawing(self.tab_counter);
|
|
|
|
|
self.active_tab = 0;
|
|
|
|
|
self.apply_bg_default(0);
|
|
|
|
|
} else {
|
|
|
|
|
self.tabs.remove(idx);
|
|
|
|
|
if self.active_tab >= self.tabs.len() {
|
|
|
|
|
self.active_tab = self.tabs.len() - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// The active tab is now a fresh blank or a
|
|
|
|
|
// different existing tab; sync ribbon chips so
|
|
|
|
|
// they don't keep showing the discarded tab's
|
|
|
|
|
// last selection. #21.
|
|
|
|
|
self.sync_ribbon_layers();
|
|
|
|
|
self.sync_ribbon_from_selection();
|
2026-07-27 12:02:50 +03:00
|
|
|
return Task::batch([close_win, self.continue_tab_close_queue()]);
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
Some(crate::app::PendingClose::Quit) => {
|
|
|
|
|
if let Some(idx) = self.tabs.iter().position(|t| t.dirty) {
|
|
|
|
|
self.tabs[idx].dirty = false;
|
|
|
|
|
}
|
|
|
|
|
if self.tabs.iter().any(|t| t.dirty) {
|
|
|
|
|
// More dirty tabs remain — keep window open.
|
|
|
|
|
self.pending_close = Some(crate::app::PendingClose::Quit);
|
|
|
|
|
} else {
|
|
|
|
|
let close_win = self.close_unsaved_dialog_window();
|
2026-06-29 13:34:16 +03:00
|
|
|
return Task::batch(vec![close_win, self.exit_app()]);
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => {}
|
|
|
|
|
}
|
|
|
|
|
Task::none()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn on_unsaved_dialog_save(&mut self) -> Task<Message> {
|
2026-07-25 02:58:46 +03:00
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
|
{
|
|
|
|
|
let Some(pending) = self.pending_close.clone() else {
|
|
|
|
|
return Task::none();
|
|
|
|
|
};
|
|
|
|
|
let (idx, continuation) = match pending {
|
|
|
|
|
crate::app::PendingClose::Tab(idx) => {
|
|
|
|
|
(idx, crate::app::SaveContinuation::CloseTab)
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
2026-07-25 02:58:46 +03:00
|
|
|
crate::app::PendingClose::Quit => {
|
|
|
|
|
let Some(idx) = self.tabs.iter().position(|tab| tab.dirty) else {
|
|
|
|
|
self.pending_close = None;
|
|
|
|
|
return Task::batch([
|
|
|
|
|
self.close_unsaved_dialog_window(),
|
|
|
|
|
self.exit_app(),
|
|
|
|
|
]);
|
|
|
|
|
};
|
|
|
|
|
(idx, crate::app::SaveContinuation::Quit)
|
|
|
|
|
}
|
|
|
|
|
};
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
2026-07-25 02:58:46 +03:00
|
|
|
if self.active_save_jobs.contains_key(&self.tabs[idx].id) {
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
.push_info(crate::t!("Save already running for this drawing.").as_ref());
|
2026-07-25 02:58:46 +03:00
|
|
|
return Task::none();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-03 12:47:48 +03:00
|
|
|
if !self.tabs[idx].recovery_save_as_required {
|
|
|
|
|
if let Some(path) = self.tabs[idx].current_path.clone() {
|
|
|
|
|
let version = self.tabs[idx].scene.document.version;
|
|
|
|
|
self.prepare_native_save(idx);
|
|
|
|
|
let close = self.close_unsaved_dialog_window();
|
|
|
|
|
let save = self.queue_native_save(
|
|
|
|
|
idx,
|
|
|
|
|
path,
|
|
|
|
|
version,
|
|
|
|
|
crate::app::SavePurpose::Manual,
|
|
|
|
|
continuation,
|
|
|
|
|
false,
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
return Task::batch([close, save]);
|
|
|
|
|
}
|
2026-07-25 02:58:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.active_tab = idx;
|
|
|
|
|
self.save_dialog_for_unsaved = true;
|
|
|
|
|
let close = self.close_unsaved_dialog_window();
|
2026-07-27 22:37:47 +03:00
|
|
|
let save = self.save_with_default_format(idx);
|
2026-07-25 02:58:46 +03:00
|
|
|
return Task::batch([close, save]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
|
|
|
{
|
|
|
|
|
match self.pending_close.take() {
|
|
|
|
|
Some(crate::app::PendingClose::Tab(idx)) => {
|
|
|
|
|
self.pending_close = Some(crate::app::PendingClose::Tab(idx));
|
|
|
|
|
self.save_dialog_for_unsaved = true;
|
|
|
|
|
let close = self.close_unsaved_dialog_window();
|
2026-07-27 22:37:47 +03:00
|
|
|
let save = self.save_with_default_format(idx);
|
2026-07-25 02:58:46 +03:00
|
|
|
Task::batch([close, save])
|
|
|
|
|
}
|
|
|
|
|
Some(crate::app::PendingClose::Quit) => {
|
|
|
|
|
if let Some(idx) = self.tabs.iter().position(|tab| tab.dirty) {
|
|
|
|
|
self.active_tab = idx;
|
|
|
|
|
self.pending_close = Some(crate::app::PendingClose::Quit);
|
|
|
|
|
self.save_dialog_for_unsaved = true;
|
|
|
|
|
let close = self.close_unsaved_dialog_window();
|
2026-07-27 22:37:47 +03:00
|
|
|
let save = self.save_with_default_format(idx);
|
2026-07-25 02:58:46 +03:00
|
|
|
Task::batch([close, save])
|
|
|
|
|
} else {
|
|
|
|
|
Task::batch([
|
|
|
|
|
self.close_unsaved_dialog_window(),
|
|
|
|
|
self.exit_app(),
|
|
|
|
|
])
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
|
|
|
|
}
|
2026-07-25 02:58:46 +03:00
|
|
|
None => Task::none(),
|
|
|
|
|
}
|
|
|
|
|
}
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|
2026-07-25 02:58:46 +03:00
|
|
|
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
}
|