fix(paper): make active-viewport picker & ViewCube clickable

Picker ve ViewCube paper_canvas içinde gömülü olduğundan tümüyle
viewport mouse_area'nın altında kalıyor ve tıklamaları o yakalıyordu.
İkisi de ana view stack'ine, mouse_area'nın üstüne taşındı; aktif
viewport'un ekran dikdörtgeninin köşelerine konumlandırılıyor.

- model picker artık yalnızca model layout'ta gösteriliyor (paper'da
  viewport kendi picker'ını alıyor)
- ViewCube hit alanı aktif viewport rect'inin sağ üst köşesinde
- CursorMoved ve ViewportClick, ViewCube hit-test'ini full canvas yerine
  aktif viewport rect'ine göre eşliyor; böylece paper'da gizmo ile
  tıklama hizalı
- picker/viewcube dışı tıklamalar alttaki mouse_area'ya geçtiği için
  viewport içi seçim/çizim etkilenmiyor

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.7 committed 2026-05-27 18:17:10 +03:00
1 parent a41a05baa1
commit eeb7df5fec
2 files changed
+112 -38

No files matched your search

+40 -13
View File
@@ -1570,11 +1570,26 @@ impl OpenCADStudio {
// ── Cursor / viewport messages ─────────────────────────────────
Message::CursorMoved(p) => {
let (vw, _vh) = self.tabs[self.active_tab].scene.selection.borrow().vp_size;
self.cursor_pos = iced::Point::new(
vw - VIEWCUBE_PAD - VIEWCUBE_HIT_SIZE + p.x,
VIEWCUBE_PAD + p.y,
);
// `p` is relative to the ViewCube hit area's top-left. Map
// it back to full-canvas coordinates so ViewportClick's
// hit-test lines up. The hit area sits in the top-right of
// the full canvas in model space, or of the active
// viewport's screen rectangle in a paper layout.
let i = self.active_tab;
let (vw, _vh) = self.tabs[i].scene.selection.borrow().vp_size;
let (ox, oy) = match self
.tabs[i]
.scene
.active_viewport
.and_then(|h| self.tabs[i].scene.viewport_screen_rect(h, (vw, _vh)))
{
Some(rect) => (
rect.x + rect.width - VIEWCUBE_PAD - VIEWCUBE_HIT_SIZE,
rect.y + VIEWCUBE_PAD,
),
None => (vw - VIEWCUBE_PAD - VIEWCUBE_HIT_SIZE, VIEWCUBE_PAD),
};
self.cursor_pos = iced::Point::new(ox + p.x, oy + p.y);
Task::none()
}
@@ -2637,14 +2652,26 @@ impl OpenCADStudio {
let i = self.active_tab;
let rot = self.tabs[i].scene.active_view_rotation_mat();
let (vw, vh) = self.tabs[i].scene.selection.borrow().vp_size;
if let Some(region) = scene::hit_test(
self.cursor_pos.x,
self.cursor_pos.y,
vw,
vh,
rot,
VIEWCUBE_PX,
) {
// The ViewCube draws in the top-right of whichever area
// owns it: the full canvas in model space, or the active
// viewport's screen rectangle in a paper layout. Map the
// cursor into that area before hit-testing so paper-space
// picks line up with the gizmo.
let (cx, cy, w, h) = match self
.tabs[i]
.scene
.active_viewport
.and_then(|hndl| self.tabs[i].scene.viewport_screen_rect(hndl, (vw, vh)))
{
Some(rect) => (
self.cursor_pos.x - rect.x,
self.cursor_pos.y - rect.y,
rect.width,
rect.height,
),
None => (self.cursor_pos.x, self.cursor_pos.y, vw, vh),
};
if let Some(region) = scene::hit_test(cx, cy, w, h, rot, VIEWCUBE_PX) {
return Task::done(Message::ViewCubeSnap(region));
}
Task::none()
+72 -25
View File
@@ -471,21 +471,79 @@ impl OpenCADStudio {
.height(Fill),
selection_overlay,
viewport_mouse,
// Toggle sits ABOVE the viewport mouse_area so clicks
// inside the toggle's bounds reach it instead of the
// shader behind it; `opaque` blocks the same clicks from
// bubbling further down. Outside the toggle's footprint
// the outer Fill container is transparent — viewport
// drawing / selection stays unaffected.
]
.width(Fill)
.height(Fill)
};
// Model-space render-mode picker, top-left. Sits ABOVE the
// viewport mouse_area so clicks inside its bounds reach it
// instead of the shader behind it; `opaque` stops them bubbling
// further. Outside the chip the Fill container is transparent so
// viewport drawing / selection is unaffected. In a paper layout
// the active viewport gets its own picker (below) instead.
if !is_paper && !tab.is_start {
viewport_stack = viewport_stack.push(
container(iced::widget::opaque(info))
.width(Fill)
.height(Fill)
.align_x(iced::alignment::Horizontal::Left)
.align_y(iced::alignment::Vertical::Top),
);
}
// Active paper-space viewport overlays: a render-mode picker in
// its top-left corner and a ViewCube hit area in its top-right,
// both layered ABOVE the viewport mouse_area so they receive
// clicks (the paper canvas itself sits below it). Positioned with
// leading Spaces sized to the viewport's screen rectangle.
let active_vp_rect: Option<iced::Rectangle> = if is_paper && !tab.is_start {
tab.scene.active_viewport.and_then(|h| {
let (cw, ch) = tab.scene.selection.borrow().vp_size;
tab.scene.viewport_screen_rect(h, (cw, ch))
})
} else {
None
};
if let Some(rect) = active_vp_rect {
let x = rect.x.max(0.0);
let y = rect.y.max(0.0);
let vp_mode = tab
.scene
.active_viewport_render_mode()
.unwrap_or(acadrust::entities::ViewportRenderMode::Wireframe2D);
let picker_layer = column![
Space::new().height(iced::Length::Fixed(y + 4.0)),
row![
Space::new().width(iced::Length::Fixed(x + 4.0)),
iced::widget::opaque(render_mode_picker(vp_mode)),
],
]
.width(Fill)
.height(Fill)
};
.height(Fill);
viewport_stack = viewport_stack.push(picker_layer);
if self.show_viewcube {
let cube_x = (rect.x + rect.width - VIEWCUBE_HIT_SIZE - VIEWCUBE_PAD).max(0.0);
let cube_y = (rect.y + VIEWCUBE_PAD).max(0.0);
let cube_click = column![
Space::new().height(iced::Length::Fixed(cube_y)),
row![
Space::new().width(iced::Length::Fixed(cube_x)),
mouse_area(
iced::widget::Space::new()
.width(iced::Length::Fixed(VIEWCUBE_HIT_SIZE))
.height(iced::Length::Fixed(VIEWCUBE_HIT_SIZE)),
)
.on_move(Message::CursorMoved)
.on_press(Message::ViewportClick),
],
]
.width(Fill)
.height(Fill);
viewport_stack = viewport_stack.push(cube_click);
}
}
if self.show_viewcube && !is_paper && !tab.is_start {
let cube_click: Element<'_, Message> = container(
@@ -889,23 +947,12 @@ fn paper_canvas_view<'a>(tab: &'a super::document::DocumentTab) -> Element<'a, M
.width(Fill)
.height(Fill);
// Render-mode picker in the active viewport's top-left corner,
// mirroring the model-space overlay. Reads / writes the
// viewport entity's own render mode via SetRenderMode.
let vp_mode = scene
.active_viewport_render_mode()
.unwrap_or(acadrust::entities::ViewportRenderMode::Wireframe2D);
let picker_layer = column![
Space::new().height(iced::Length::Fixed(y + 4.0)),
row![
Space::new().width(iced::Length::Fixed(x + 4.0)),
iced::widget::opaque(render_mode_picker(vp_mode)),
],
]
.width(Fill)
.height(Fill);
return stack![paper_sheet, positioned, border_layer, picker_layer]
// The render-mode picker and ViewCube hit area for the active
// viewport are layered in the main view stack (above the
// viewport mouse_area) so they're clickable — see
// `view()`. Here we only draw the sheet, the 3-D content, and
// the highlight border.
return stack![paper_sheet, positioned, border_layer]
.width(Fill)
.height(Fill)
.into();