diff --git a/src/app/commands.rs b/src/app/commands.rs index 4f69b5e57..4fd5cc098 100644 --- a/src/app/commands.rs +++ b/src/app/commands.rs @@ -2663,8 +2663,48 @@ impl OpenCADStudio { let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase(); let scene = &self.tabs[i].scene; if scene.current_layout == "Model" { - self.command_line - .push_error("VPORTS: switch to a paper space layout first."); + // Bare VPORTS → ask for the configuration interactively; + // the next command-line entry supplies it. + if sub.is_empty() { + self.awaiting_vports = true; + self.command_line + .push_info("VPORTS Configuration [SIngle/2H/2V/4]:"); + return self.focus_cmd_input(); + } + // Model space: split the tiled viewport layout. + use iced::Rectangle as R; + let full = R { x: 0.0, y: 0.0, width: 1.0, height: 1.0 }; + let rects: Option> = match sub.as_str() { + "SINGLE" | "SI" | "1" => Some(vec![full]), + "2H" | "2" => Some(vec![ + R { x: 0.0, y: 0.0, width: 1.0, height: 0.5 }, + R { x: 0.0, y: 0.5, width: 1.0, height: 0.5 }, + ]), + "2V" => Some(vec![ + R { x: 0.0, y: 0.0, width: 0.5, height: 1.0 }, + R { x: 0.5, y: 0.0, width: 0.5, height: 1.0 }, + ]), + "4" => Some(vec![ + R { x: 0.0, y: 0.0, width: 0.5, height: 0.5 }, + R { x: 0.5, y: 0.0, width: 0.5, height: 0.5 }, + R { x: 0.0, y: 0.5, width: 0.5, height: 0.5 }, + R { x: 0.5, y: 0.5, width: 0.5, height: 0.5 }, + ]), + _ => None, + }; + match rects { + Some(rects) => { + let n = rects.len(); + self.tabs[i].scene.set_model_tile_layout(rects); + self.tabs[i].scene.camera_generation += 1; + self.command_line + .push_output(&format!("VPORTS: {n} viewport(s).")); + } + None => { + self.command_line + .push_error("VPORTS: use SINGLE | 2H | 2V | 4."); + } + } } else if sub.is_empty() { // ── List existing viewports ────────────────────────── let layout_block = scene.current_layout_block_handle_pub(); diff --git a/src/app/mod.rs b/src/app/mod.rs index cea1ae7dc..3437e1b2a 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -70,6 +70,9 @@ pub(super) struct OpenCADStudio { show_grid: bool, /// Dynamic input overlay (F12): show coordinate tooltip near cursor. dyn_input: bool, + /// `true` after a bare `VPORTS` in model space — the next command-line + /// entry is treated as the tiled-config option (SIngle/2H/2V/4). + awaiting_vports: bool, /// Show the UCS icon in the bottom-left corner of model space (UCSICON). show_ucs_icon: bool, /// Whether the ViewCube 3D gizmo is visible in model space (NAVVCUBE). @@ -743,6 +746,7 @@ impl OpenCADStudio { polar_increment_deg: 45.0, show_grid: false, dyn_input: true, + awaiting_vports: false, show_ucs_icon: true, show_viewcube: true, show_properties: true, diff --git a/src/app/update.rs b/src/app/update.rs index 0b7fead21..45201a0e6 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -963,6 +963,15 @@ impl OpenCADStudio { // dropdown so the dispatched command's new prompt is // immediately visible on the overlay. self.command_line.close_history(); + // Interactive VPORTS: the entry after a bare `VPORTS` is the + // tiled configuration. Empty input defaults to SINGLE. + if self.awaiting_vports { + self.awaiting_vports = false; + let cfg = self.command_line.input.trim().to_string(); + self.command_line.input.clear(); + let cfg = if cfg.is_empty() { "SINGLE".to_string() } else { cfg }; + return self.dispatch_command(&format!("VPORTS {cfg}")); + } // If the user navigated the autocomplete list with the // arrow keys, Enter dispatches the highlighted command // rather than the partial text actually in the buffer. diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 6e7d9cc2f..017ebd4f0 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -4362,18 +4362,31 @@ impl Scene { true } - /// Reset the Model layout to a single full-window tile. - pub fn reset_model_tiles(&self) { + /// Replace the Model tiled layout with the given normalized rectangles + /// (each in 0..1). Every tile inherits the current camera; the first + /// tile becomes active. Used by VPORTS presets and `reset_model_tiles`. + pub fn set_model_tile_layout(&self, rects: Vec) { let cam_now = self.camera.borrow().clone(); - *self.model_tiles.borrow_mut() = vec![ModelTile { - rect: iced::Rectangle { - x: 0.0, - y: 0.0, - width: 1.0, - height: 1.0, - }, - camera: cam_now, - }]; + let tiles: Vec = rects + .into_iter() + .map(|rect| ModelTile { + rect, + camera: cam_now.clone(), + }) + .collect(); + *self.model_tiles.borrow_mut() = if tiles.is_empty() { + vec![ModelTile { + rect: iced::Rectangle { + x: 0.0, + y: 0.0, + width: 1.0, + height: 1.0, + }, + camera: cam_now, + }] + } else { + tiles + }; self.active_model_tile.set(0); }