Render preview uses the shared camera navigation (#1462)

* Render preview uses the shared camera navigation

The Render workspace's product preview had its own hard-coded camera
controls. Camera input now lives in one component, navigation::handle,
which the modelling viewport and the Render preview both call (NavCaps:
orbit, pick). Render gets the navigation preset, Orbit around, zoom to the
cursor, the middle double-click and the axis view or navigation cube.
Flat pages can call it with orbit off. A test rejects pages that read the
wheel or move the camera themselves.

Fixes #1454

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Render panel hint says the camera moves as in Design

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-15 13:48:05 -04:00
1 parent e8e2ccc8a9
commit be985f136d
16 files changed
+969 -580

No files matched your search

+6 -14
View File
@@ -438,20 +438,11 @@ fn hash_surface(surface: Option<solidcraft_engine::render::surface::OpaqueSurfac
pub fn show(app: &mut SolveApp, ui: &mut egui::Ui) {
let rect = ui.max_rect();
app.viewport.rect = Some(rect);
let response = ui.allocate_rect(rect, Sense::click_and_drag());
let delta = ui.input(|i| i.pointer.delta());
if response.dragged_by(egui::PointerButton::Primary) {
app.cam.orbit(f64::from(delta.x), f64::from(delta.y));
}
if response.dragged_by(egui::PointerButton::Middle) {
app.cam.pan(f64::from(delta.x), f64::from(delta.y), f64::from(rect.height()));
}
if response.hovered() {
let scroll = ui.input(|i| i.smooth_scroll_delta.y);
if scroll != 0.0 {
app.cam.zoom_at((-f64::from(scroll) * 0.0015 * crate::prefs::zoom_sign(app)).exp(), None);
}
}
crate::viewport::refresh_layout(app, ui.ctx().pixels_per_point());
let response = ui.interact(rect, ui.id().with("render_preview"), Sense::click_and_drag());
// The modelling viewport's camera input (preset, Orbit around, zoom to the cursor, middle
// double-click), with nothing to pick or select here.
crate::navigation::handle(app, ui, rect, &response, crate::navigation::NavCaps::RENDER);
let bg = app.render_workspace.view.draft.background;
ui.painter().rect_filled(rect, 0.0, Color32::from_rgb(bg[0], bg[1], bg[2]));
let result = (|| -> Result<(), String> {
@@ -533,4 +524,5 @@ pub fn show(app: &mut SolveApp, ui: &mut egui::Ui) {
if let Err(error) = result {
ui.painter().text(rect.center(), egui::Align2::CENTER_CENTER, error, egui::FontId::proportional(14.0), Tokens::get().text);
}
crate::viewport::axis_view(app, ui, rect);
}