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 <noreply@anthropic.com>
This commit is contained in:
parent
a41a05baa1
commit
eeb7df5fec
2 changed files with 112 additions and 38 deletions
|
|
@ -1570,11 +1570,26 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
// ── Cursor / viewport messages ─────────────────────────────────
|
// ── Cursor / viewport messages ─────────────────────────────────
|
||||||
Message::CursorMoved(p) => {
|
Message::CursorMoved(p) => {
|
||||||
let (vw, _vh) = self.tabs[self.active_tab].scene.selection.borrow().vp_size;
|
// `p` is relative to the ViewCube hit area's top-left. Map
|
||||||
self.cursor_pos = iced::Point::new(
|
// it back to full-canvas coordinates so ViewportClick's
|
||||||
vw - VIEWCUBE_PAD - VIEWCUBE_HIT_SIZE + p.x,
|
// hit-test lines up. The hit area sits in the top-right of
|
||||||
VIEWCUBE_PAD + p.y,
|
// 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()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2637,14 +2652,26 @@ impl OpenCADStudio {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let rot = self.tabs[i].scene.active_view_rotation_mat();
|
let rot = self.tabs[i].scene.active_view_rotation_mat();
|
||||||
let (vw, vh) = self.tabs[i].scene.selection.borrow().vp_size;
|
let (vw, vh) = self.tabs[i].scene.selection.borrow().vp_size;
|
||||||
if let Some(region) = scene::hit_test(
|
// The ViewCube draws in the top-right of whichever area
|
||||||
self.cursor_pos.x,
|
// owns it: the full canvas in model space, or the active
|
||||||
self.cursor_pos.y,
|
// viewport's screen rectangle in a paper layout. Map the
|
||||||
vw,
|
// cursor into that area before hit-testing so paper-space
|
||||||
vh,
|
// picks line up with the gizmo.
|
||||||
rot,
|
let (cx, cy, w, h) = match self
|
||||||
VIEWCUBE_PX,
|
.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));
|
return Task::done(Message::ViewCubeSnap(region));
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|
|
||||||
|
|
@ -471,21 +471,79 @@ impl OpenCADStudio {
|
||||||
.height(Fill),
|
.height(Fill),
|
||||||
selection_overlay,
|
selection_overlay,
|
||||||
viewport_mouse,
|
viewport_mouse,
|
||||||
// Toggle sits ABOVE the viewport mouse_area so clicks
|
]
|
||||||
// inside the toggle's bounds reach it instead of the
|
.width(Fill)
|
||||||
// shader behind it; `opaque` blocks the same clicks from
|
.height(Fill)
|
||||||
// bubbling further down. Outside the toggle's footprint
|
};
|
||||||
// the outer Fill container is transparent — viewport
|
|
||||||
// drawing / selection stays unaffected.
|
// 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))
|
container(iced::widget::opaque(info))
|
||||||
.width(Fill)
|
.width(Fill)
|
||||||
.height(Fill)
|
.height(Fill)
|
||||||
.align_x(iced::alignment::Horizontal::Left)
|
.align_x(iced::alignment::Horizontal::Left)
|
||||||
.align_y(iced::alignment::Vertical::Top),
|
.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)
|
.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 {
|
if self.show_viewcube && !is_paper && !tab.is_start {
|
||||||
let cube_click: Element<'_, Message> = container(
|
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)
|
.width(Fill)
|
||||||
.height(Fill);
|
.height(Fill);
|
||||||
|
|
||||||
// Render-mode picker in the active viewport's top-left corner,
|
// The render-mode picker and ViewCube hit area for the active
|
||||||
// mirroring the model-space overlay. Reads / writes the
|
// viewport are layered in the main view stack (above the
|
||||||
// viewport entity's own render mode via SetRenderMode.
|
// viewport mouse_area) so they're clickable — see
|
||||||
let vp_mode = scene
|
// `view()`. Here we only draw the sheet, the 3-D content, and
|
||||||
.active_viewport_render_mode()
|
// the highlight border.
|
||||||
.unwrap_or(acadrust::entities::ViewportRenderMode::Wireframe2D);
|
return stack![paper_sheet, positioned, border_layer]
|
||||||
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]
|
|
||||||
.width(Fill)
|
.width(Fill)
|
||||||
.height(Fill)
|
.height(Fill)
|
||||||
.into();
|
.into();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue