forked from 1b-agent/opencadstudio-upstream
feat(viewport): reorder model controls, add close-vport button
Reorder the model-tile control chip to grid, grid snap, render mode, vertical split, horizontal split. Add a close button (shown only with more than one model tile) that merges the active pane into its longest-contact neighbour. Divider drags now clamp each side to the minimum tile size, so dragging a boundary to the screen edge no longer collapses a pane — closing is done with the button instead. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
9c03ae2b4d
commit
2baabdf4a0
4 files changed
+150
-138
No files matched your search
@@ -1078,6 +1078,9 @@ pub enum Message {
|
||||
/// Split the active Model viewport in two. `true` → horizontal divider
|
||||
/// (top / bottom); `false` → vertical divider (left / right).
|
||||
SplitModelViewport(bool),
|
||||
/// Close the active Model viewport, merging it into a neighbour.
|
||||
/// Only meaningful when more than one model tile exists.
|
||||
CloseModelViewport,
|
||||
/// Recall previous command in history (↑ arrow key).
|
||||
CommandHistoryPrev,
|
||||
/// Recall next command in history (↓ arrow key).
|
||||
|
||||
+13
-12
@@ -53,10 +53,9 @@ const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX;
|
||||
/// Pixel distance from a Model-tile inner divider that still registers as
|
||||
/// a resize grip on the press.
|
||||
const TILE_EDGE_HIT_PX: f32 = 4.0;
|
||||
/// Normalized minimum tile size before `collapse_small_model_tiles` merges
|
||||
/// it into a neighbour. Sized to comfortably contain the ViewCube + its
|
||||
/// padding so a tile that's still GPU-rendered always has room to show
|
||||
/// the gizmo.
|
||||
/// Normalized minimum tile size a divider drag clamps to. Sized to
|
||||
/// comfortably contain the ViewCube + its padding so every tile always
|
||||
/// has room to show the gizmo.
|
||||
fn tile_min_norm(canvas_w: f32, canvas_h: f32) -> (f32, f32) {
|
||||
let px = VIEWCUBE_DRAW_PX + 2.0 * VIEWCUBE_PAD + 16.0;
|
||||
(
|
||||
@@ -1562,6 +1561,13 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::CloseModelViewport => {
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].scene.close_active_model_tile();
|
||||
self.tabs[i].scene.camera_generation += 1;
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::CommandHistoryPrev => {
|
||||
// Grip popup wins first — arrow keys walk its items.
|
||||
if let Some(popup) = self.grip_popup.as_mut() {
|
||||
@@ -3472,15 +3478,10 @@ impl OpenCADStudio {
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
// End an in-flight tile-divider drag. Any tile that fell
|
||||
// below the minimum (viewcube fits comfortably) gets
|
||||
// absorbed into its longest-contact neighbour, so the
|
||||
// user can drag a divider all the way to one side to
|
||||
// remove a pane.
|
||||
// End an in-flight tile-divider drag. The move clamps
|
||||
// each side to its minimum, so panes never collapse from
|
||||
// dragging — use the close button to remove a pane.
|
||||
if self.tile_drag.take().is_some() {
|
||||
let (vw, vh) = self.tabs[i].scene.selection.borrow().vp_size;
|
||||
let (min_w, min_h) = tile_min_norm(vw, vh);
|
||||
self.tabs[i].scene.collapse_small_model_tiles(min_w, min_h);
|
||||
self.tabs[i].scene.camera_generation += 1;
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
+19
-10
@@ -436,6 +436,7 @@ impl OpenCADStudio {
|
||||
self.show_grid,
|
||||
self.snapper.grid_snap(),
|
||||
true,
|
||||
tab.scene.model_tiles.borrow().len(),
|
||||
);
|
||||
// Position the bar at the active model tile's top-left corner so
|
||||
// it follows the active panel in a tiled layout (full canvas when
|
||||
@@ -520,6 +521,7 @@ impl OpenCADStudio {
|
||||
self.show_grid,
|
||||
self.snapper.grid_snap(),
|
||||
false,
|
||||
0,
|
||||
)),
|
||||
],
|
||||
]
|
||||
@@ -4504,6 +4506,7 @@ fn viewport_controls<'a>(
|
||||
show_grid: bool,
|
||||
snap_on: bool,
|
||||
include_split: bool,
|
||||
tile_count: usize,
|
||||
) -> Element<'a, Message> {
|
||||
use acadrust::entities::ViewportRenderMode as M;
|
||||
let render_modes: Vec<RenderModeChoice> = vec![
|
||||
@@ -4586,19 +4589,25 @@ fn viewport_controls<'a>(
|
||||
let mut bar = row![]
|
||||
.spacing(3)
|
||||
.align_y(iced::alignment::Vertical::Center);
|
||||
if include_split {
|
||||
bar = bar
|
||||
.push(icon_btn(crate::ui::icons::SPLIT_H, false, Message::SplitModelViewport(true)))
|
||||
.push(sep())
|
||||
.push(icon_btn(crate::ui::icons::SPLIT_V, false, Message::SplitModelViewport(false)))
|
||||
.push(sep());
|
||||
}
|
||||
bar = bar
|
||||
.push(picker)
|
||||
.push(sep())
|
||||
.push(icon_btn(crate::ui::icons::GRID, show_grid, Message::ToggleGrid))
|
||||
.push(sep())
|
||||
.push(icon_btn(crate::ui::icons::SNAP, snap_on, Message::ToggleGridSnap));
|
||||
.push(icon_btn(crate::ui::icons::SNAP, snap_on, Message::ToggleGridSnap))
|
||||
.push(sep())
|
||||
.push(picker);
|
||||
if include_split {
|
||||
bar = bar
|
||||
.push(sep())
|
||||
.push(icon_btn(crate::ui::icons::SPLIT_V, false, Message::SplitModelViewport(false)))
|
||||
.push(sep())
|
||||
.push(icon_btn(crate::ui::icons::SPLIT_H, false, Message::SplitModelViewport(true)));
|
||||
// Close button: only meaningful with more than one model tile.
|
||||
if tile_count > 1 {
|
||||
bar = bar
|
||||
.push(sep())
|
||||
.push(icon_btn(crate::ui::icons::CLOSE, false, Message::CloseModelViewport));
|
||||
}
|
||||
}
|
||||
|
||||
container(bar)
|
||||
.padding(2)
|
||||
|
||||
Reference in new issue
Block a user