forked from 1b-agent/opencadstudio-upstream
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>
This commit is contained in:
1 parent
2baabdf4a0
commit
bddc050b3e
39 files changed
+26674
-26113
No files matched your search
@@ -0,0 +1,139 @@
|
||||
use super::*;
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_fileops(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
"NEW" => return Some(Task::done(Message::TabNew)),
|
||||
"OPEN" => return Some(Task::done(Message::OpenFile)),
|
||||
"SAVE" | "QSAVE" => return Some(Task::done(Message::SaveFile)),
|
||||
"SAVEAS" => return Some(Task::done(Message::SaveAs)),
|
||||
"UNDO" | "U" => return Some(Task::done(Message::Undo)),
|
||||
"REDO" => return Some(Task::done(Message::Redo)),
|
||||
"CLEAR" | "CLR" => return Some(Task::done(Message::ClearScene)),
|
||||
"WIREFRAME" | "VW" => return Some(Task::done(Message::SetWireframe(true))),
|
||||
"SOLID" | "VS" => return Some(Task::done(Message::SetWireframe(false))),
|
||||
"EXIT" | "QUIT" => {
|
||||
// Funnel through the OS close path so the unsaved-changes
|
||||
// dialog runs before `iced::exit()`. Falls back to a hard
|
||||
// exit if there's no main window registered yet.
|
||||
if let Some(id) = self.main_window {
|
||||
return Some(Task::done(Message::WindowCloseRequested(id)));
|
||||
}
|
||||
return Some(iced::exit());
|
||||
}
|
||||
|
||||
// ── Frame-budget HUD (Phase 5.3) ───────────────────────────────
|
||||
// Toggle the per-rebuild wire-tessellation readout overlay.
|
||||
"PERF" => {
|
||||
self.perf_hud = !self.perf_hud;
|
||||
self.command_line.push_info(if self.perf_hud {
|
||||
"PERF HUD on — shows last wire re-tessellation cost"
|
||||
} else {
|
||||
"PERF HUD off"
|
||||
});
|
||||
return Some(Task::none());
|
||||
}
|
||||
|
||||
// ── Background color ───────────────────────────────────────────
|
||||
// Usage: BACKGROUND <r> <g> <b> (0–255 each)
|
||||
// BACKGROUND WHITE|BLACK|GRAY|DARKGRAY|LTGRAY (preset)
|
||||
// BACKGROUND RESET (restore default)
|
||||
// The chosen colour is also stored as the persisted default
|
||||
// (`default_bg_color` / `default_paper_bg_color`) so it survives
|
||||
// restarts and applies to new drawings (#188).
|
||||
cmd if cmd == "BACKGROUND" || cmd.starts_with("BACKGROUND ") => {
|
||||
let args = cmd.split_whitespace().skip(1).collect::<Vec<_>>();
|
||||
let is_paper = self.tabs[i].scene.current_layout != "Model";
|
||||
if args
|
||||
.first()
|
||||
.map(|s| s.eq_ignore_ascii_case("RESET"))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if is_paper {
|
||||
self.tabs[i].paper_bg_color = None;
|
||||
self.tabs[i].scene.paper_bg_color = [1.0, 1.0, 1.0, 1.0];
|
||||
self.default_paper_bg_color = None;
|
||||
} else {
|
||||
self.tabs[i].bg_color = None;
|
||||
self.tabs[i].scene.bg_color = [0.11, 0.11, 0.11, 1.0];
|
||||
self.default_bg_color = None;
|
||||
}
|
||||
// Wire colour adaptation (`adapt_to_bg`) reads the bg
|
||||
// at tessellation time, so the cached wires need to
|
||||
// refresh — otherwise a light→dark bg flip leaves
|
||||
// black lines invisible against the new bg. Meshes
|
||||
// bake colour into per-vertex GPU buffers at upload
|
||||
// time; `recolor_meshes` rewrites the CPU side so
|
||||
// the next epoch-driven re-upload picks up the new
|
||||
// colour.
|
||||
self.tabs[i].scene.recolor_meshes();
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.command_line
|
||||
.push_output("Background reset to default.");
|
||||
} else if let Some(rgba) = parse_background_color(&args) {
|
||||
if is_paper {
|
||||
self.tabs[i].paper_bg_color = Some(rgba);
|
||||
self.tabs[i].scene.paper_bg_color = rgba;
|
||||
self.default_paper_bg_color = Some(rgba);
|
||||
} else {
|
||||
self.tabs[i].bg_color = Some(rgba);
|
||||
self.tabs[i].scene.bg_color = rgba;
|
||||
self.default_bg_color = Some(rgba);
|
||||
}
|
||||
self.tabs[i].scene.recolor_meshes();
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
let [r, g, b, _] = rgba;
|
||||
self.command_line.push_output(&format!(
|
||||
"Background: rgb({}, {}, {})",
|
||||
(r * 255.0).round() as u8,
|
||||
(g * 255.0).round() as u8,
|
||||
(b * 255.0).round() as u8
|
||||
));
|
||||
// Persisted centrally after this message via
|
||||
// `persist_settings_if_changed()`.
|
||||
} else {
|
||||
self.command_line.push_info(
|
||||
"Usage: BACKGROUND <r> <g> <b> (0–255) | WHITE|BLACK|GRAY|DARKGRAY|LTGRAY | RESET",
|
||||
);
|
||||
}
|
||||
}
|
||||
"ORTHO" => return Some(Task::done(Message::SetProjection(true))),
|
||||
"PERSP" => return Some(Task::done(Message::SetProjection(false))),
|
||||
"LAYERS" | "LA" => return Some(Task::done(Message::ToggleLayers)),
|
||||
|
||||
_ => return None,
|
||||
}
|
||||
Some(self.finish_dispatch(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the argument list of the `BACKGROUND` command into an `[r,g,b,a]`
|
||||
/// colour (channels 0.0–1.0, `a` always 1.0). Accepts:
|
||||
/// * three whitespace-separated 0–255 values: `255 255 255`
|
||||
/// * a named preset: WHITE / BLACK / GRAY|GREY / DARKGRAY|DARKGREY / LTGRAY
|
||||
/// Returns `None` if the arguments don't match either form.
|
||||
fn parse_background_color(args: &[&str]) -> Option<[f32; 4]> {
|
||||
let to_rgba = |[r, g, b]: [u8; 3]| {
|
||||
[r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0]
|
||||
};
|
||||
// Single token: a named preset.
|
||||
if args.len() == 1 {
|
||||
let preset = match args[0].to_ascii_uppercase().as_str() {
|
||||
"WHITE" => [255, 255, 255],
|
||||
"BLACK" => [0, 0, 0],
|
||||
"GRAY" | "GREY" => [128, 128, 128],
|
||||
"DARKGRAY" | "DARKGREY" | "DKGRAY" => [64, 64, 64],
|
||||
"LTGRAY" | "LIGHTGRAY" | "LIGHTGREY" => [192, 192, 192],
|
||||
_ => return None,
|
||||
};
|
||||
return Some(to_rgba(preset));
|
||||
}
|
||||
// Three separate tokens: `r g b`.
|
||||
if args.len() >= 3 {
|
||||
let r = args[0].parse::<u8>().ok()?;
|
||||
let g = args[1].parse::<u8>().ok()?;
|
||||
let b = args[2].parse::<u8>().ok()?;
|
||||
return Some(to_rgba([r, g, b]));
|
||||
}
|
||||
None
|
||||
}
|
||||
Reference in new issue
Block a user