feat(paper): render-mode picker + ViewCube on active viewport
Paper-space'de bir viewport'a çift tıklanıp aktif hale gelince, model space'deki gibi sol üst köşede render-mode dropdown'ı ve sağ üst köşede ViewCube gösteriliyor. - render_mode_picker helper'a çıkarıldı; model space ve aktif paper viewport ortak kullanıyor (kod tekrarı yok) - SetRenderMode artık viewport-aware: aktif viewport varsa o viewport entity'sinin render_mode'unu, yoksa model-layout tab stilini günceller (Scene::active_viewport_render_mode getter/setter) - build_viewport_primitive'e show_viewcube parametresi; aktif viewport primitive'i ViewCube gizmo'sunu çiziyor - PaperViewportPane shader::Program artık Message'a özelleşti ve sol tık ViewCube hit-test'ini bounds-relative yapıp ViewCubeSnap üretiyor Bilinen sorun: picker/viewcube doğru konumda görünüyor ancak tıklamalar üstteki viewport_mouse mouse_area katmanına gidiyor; katman sırası bir sonraki adımda düzeltilecek. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
fda0f2dbb9
commit
a41a05baa1
5 changed files with 149 additions and 57 deletions
|
|
@ -2199,6 +2199,36 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
/// Render mode of the active paper-space viewport, or `None` when no
|
||||
/// viewport is active (PSPACE / model layout).
|
||||
pub fn active_viewport_render_mode(
|
||||
&self,
|
||||
) -> Option<acadrust::entities::ViewportRenderMode> {
|
||||
let h = self.active_viewport?;
|
||||
match self.document.get_entity(h) {
|
||||
Some(acadrust::EntityType::Viewport(vp)) => Some(vp.render_mode),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the active paper-space viewport's render mode. Returns `true`
|
||||
/// when a viewport was active and updated; `false` (no-op) otherwise,
|
||||
/// so the caller can fall back to the model-layout render mode.
|
||||
pub fn set_active_viewport_render_mode(
|
||||
&mut self,
|
||||
mode: acadrust::entities::ViewportRenderMode,
|
||||
) -> bool {
|
||||
let Some(h) = self.active_viewport else {
|
||||
return false;
|
||||
};
|
||||
if let Some(acadrust::EntityType::Viewport(vp)) = self.document.get_entity_mut(h) {
|
||||
vp.render_mode = mode;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// View-rotation matrix for the active viewport (MSPACE), or the
|
||||
/// paper-space camera's matrix when not in MSPACE.
|
||||
/// Used by ViewCube hit-testing so clicks map to the correct camera.
|
||||
|
|
|
|||
|
|
@ -512,6 +512,7 @@ impl Scene {
|
|||
vp_handle: Handle,
|
||||
hover_region: Option<usize>,
|
||||
bounds: Rectangle,
|
||||
show_viewcube: bool,
|
||||
) -> Primitive {
|
||||
let render_mode = match self.document.get_entity(vp_handle) {
|
||||
Some(EntityType::Viewport(vp)) => vp.render_mode,
|
||||
|
|
@ -551,7 +552,7 @@ impl Scene {
|
|||
cam_rotation: cam.view_rotation_mat(),
|
||||
hover_region,
|
||||
bg_color: self.bg_color,
|
||||
show_viewcube: false,
|
||||
show_viewcube,
|
||||
fill_mode: self.document.header.fill_mode,
|
||||
view_wireframe,
|
||||
mesh_fill: flags.mesh_fill,
|
||||
|
|
@ -570,8 +571,10 @@ impl Scene {
|
|||
hover_region: Option<usize>,
|
||||
bounds: Rectangle,
|
||||
) -> PaperViewportPrimitive {
|
||||
// The active (double-clicked) viewport shows the ViewCube gizmo,
|
||||
// mirroring the model-space view.
|
||||
PaperViewportPrimitive(
|
||||
self.build_viewport_primitive(vp_handle, hover_region, bounds),
|
||||
self.build_viewport_primitive(vp_handle, hover_region, bounds, true),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use super::render::{CameraState, PaperViewportPrimitive, Primitive};
|
||||
use super::Scene;
|
||||
use super::{Scene, VIEWCUBE_PX};
|
||||
use crate::app::Message;
|
||||
use acadrust::Handle;
|
||||
use iced::widget::shader;
|
||||
use iced::{mouse, Event, Rectangle};
|
||||
|
|
@ -70,7 +71,7 @@ impl<'a> PaperViewportPane<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, Msg: std::fmt::Debug + Clone> shader::Program<Msg> for PaperViewportPane<'a> {
|
||||
impl<'a> shader::Program<Message> for PaperViewportPane<'a> {
|
||||
type State = CameraState;
|
||||
type Primitive = PaperViewportPrimitive;
|
||||
|
||||
|
|
@ -90,9 +91,30 @@ impl<'a, Msg: std::fmt::Debug + Clone> shader::Program<Msg> for PaperViewportPan
|
|||
event: &Event,
|
||||
bounds: Rectangle,
|
||||
cursor: mouse::Cursor,
|
||||
) -> Option<iced::widget::Action<Msg>> {
|
||||
) -> Option<iced::widget::Action<Message>> {
|
||||
self.scene.update_viewcube_state(state, bounds, cursor);
|
||||
let _ = event;
|
||||
// A left click on the ViewCube snaps the viewport's view. The
|
||||
// hit-test runs in bounds-relative coordinates so it matches the
|
||||
// gizmo drawn in this viewport's top-right corner. Clicks outside
|
||||
// the cube return None, so they fall through to the paper sheet
|
||||
// for normal selection / drawing.
|
||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) = event {
|
||||
if let Some(pos) = cursor.position_in(bounds) {
|
||||
let rot = self.scene.active_view_rotation_mat();
|
||||
if let Some(region) = super::hit_test(
|
||||
pos.x,
|
||||
pos.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
rot,
|
||||
VIEWCUBE_PX,
|
||||
) {
|
||||
return Some(iced::widget::Action::publish(Message::ViewCubeSnap(
|
||||
region,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +149,7 @@ impl<'a, Msg: std::fmt::Debug + Clone> shader::Program<Msg> for ViewportPane<'a>
|
|||
),
|
||||
ViewportPaneMode::Paper { handle } => {
|
||||
self.scene
|
||||
.build_viewport_primitive(*handle, state.hover_region, bounds)
|
||||
.build_viewport_primitive(*handle, state.hover_region, bounds, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue