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:
Hakan Seven 2026-05-27 18:05:09 +03:00
commit a41a05baa1
5 changed files with 149 additions and 57 deletions

View file

@ -669,12 +669,6 @@ impl OpenCADStudio {
Message::SetRenderMode(mode) => {
use acadrust::entities::ViewportRenderMode as M;
let i = self.active_tab;
self.tabs[i].render_mode = mode;
// Keep the legacy `wireframe` bool synced — both wireframe
// modes set it, everything else clears it.
let wf = matches!(mode, M::Wireframe2D | M::Wireframe3D);
self.tabs[i].wireframe = wf;
self.ribbon.set_wireframe(wf);
let label = match mode {
M::Wireframe2D => "Wireframe 2D",
M::Wireframe3D => "Wireframe 3D",
@ -684,6 +678,21 @@ impl OpenCADStudio {
M::FlatShadedWithEdges => "Flat Shaded + Edges",
M::GouraudShadedWithEdges => "Gouraud Shaded + Edges",
};
// In a paper layout with an active (double-clicked)
// viewport, the picker drives that viewport entity's own
// render mode; the model-layout tab style is untouched.
if self.tabs[i].scene.set_active_viewport_render_mode(mode) {
self.tabs[i].scene.bump_geometry();
self.command_line
.push_output(&format!("Viewport visual style: {label}"));
return Task::none();
}
self.tabs[i].render_mode = mode;
// Keep the legacy `wireframe` bool synced — both wireframe
// modes set it, everything else clears it.
let wf = matches!(mode, M::Wireframe2D | M::Wireframe3D);
self.tabs[i].wireframe = wf;
self.ribbon.set_wireframe(wf);
self.tabs[i].visual_style = label.into();
// Re-upload face3d fills on the next frame — the render
// pipeline keys its upload cache off `geometry_epoch`.