feat: A4 landscape default paper, white paper area on dark desk

- paper_limits() falls back to A4 landscape (297×210mm) even when no
  Layout object exists in the document
- paper_bg_color default changed to white [1,1,1,1] (paper surface color)
- PaperCanvas::draw() fills canvas with dark desk color first, then draws
  the paper area rectangle in paper_bg_color so the sheet is clearly
  visible against the desk background
- view.rs container background uses the fixed desk color instead of
  paper_bg_color so it no longer conflicts with the paper-surface color
- BACKGROUND RESET for paper space now resets to white instead of dark gray
- Removed unused PaperSheet variant, paper_sheet() constructor,
  build_paper_sheet_primitive() and associated match arms (dead code
  after 2D canvas replacement)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-23 14:05:07 +03:00
commit 0a291c877b
6 changed files with 23 additions and 56 deletions

View file

@ -41,7 +41,7 @@ impl H7CAD {
if args.first().map(|s| s.eq_ignore_ascii_case("RESET")).unwrap_or(false) {
if is_paper {
self.tabs[i].paper_bg_color = None;
self.tabs[i].scene.paper_bg_color = [0.22, 0.24, 0.28, 1.0];
self.tabs[i].scene.paper_bg_color = [1.0, 1.0, 1.0, 1.0];
} else {
self.tabs[i].bg_color = None;
self.tabs[i].scene.bg_color = [0.11, 0.11, 0.11, 1.0];

View file

@ -232,9 +232,8 @@ impl H7CAD {
.height(Fill);
let bg_color = if is_paper {
tab.paper_bg_color
.map(|[r, g, b, a]| Color { r, g, b, a })
.unwrap_or(Color { r: 0.22, g: 0.24, b: 0.28, a: 1.0 })
// Desk color — matches the DESK constant in paper_canvas.rs.
Color { r: 0.22, g: 0.24, b: 0.28, a: 1.0 }
} else {
tab.bg_color
.map(|[r, g, b, a]| Color { r, g, b, a })

View file

@ -95,7 +95,7 @@ impl Scene {
images: HashMap::new(),
active_viewport: None,
bg_color: [0.11, 0.11, 0.11, 1.0],
paper_bg_color: [0.22, 0.24, 0.28, 1.0],
paper_bg_color: [1.0, 1.0, 1.0, 1.0],
}
}
@ -201,11 +201,9 @@ impl Scene {
if let ObjectType::Layout(l) = obj {
if l.name == self.current_layout {
let (min, max) = (l.min_limits, l.max_limits);
// Guard against degenerate limits.
let w = (max.0 - min.0).abs();
let h = (max.1 - min.1).abs();
if w < 1e-6 || h < 1e-6 {
// Default to A4 landscape (mm).
return Some(((0.0, 0.0), (297.0, 210.0)));
}
return Some((min, max));
@ -213,6 +211,8 @@ impl Scene {
}
None
})
// No Layout object found for the current layout — default to A4 landscape.
.or(Some(((0.0, 0.0), (297.0, 210.0))))
}
/// Scale of the first user viewport (id > 1) in the current paper layout,

View file

@ -64,9 +64,21 @@ impl<'a> canvas::Program<Message> for PaperCanvas<'a> {
let mut frame = canvas::Frame::new(renderer, bounds.size());
// ── Background ────────────────────────────────────────────────────────
let [r, g, b, a] = self.scene.paper_bg_color;
frame.fill_rectangle(Point::ORIGIN, bounds.size(), Color { r, g, b, a });
// ── Desk background (area outside the paper sheet) ────────────────────
const DESK: Color = Color { r: 0.22, g: 0.24, b: 0.28, a: 1.0 };
frame.fill_rectangle(Point::ORIGIN, bounds.size(), DESK);
// ── White paper area ──────────────────────────────────────────────────
if let Some(((px0, py0), (px1, py1))) = self.scene.paper_limits() {
let tl = to_px(px0 as f32, py1 as f32);
let br = to_px(px1 as f32, py0 as f32);
let pw = br.x - tl.x;
let ph = br.y - tl.y;
if pw > 0.0 && ph > 0.0 {
let [r, g, b, a] = self.scene.paper_bg_color;
frame.fill_rectangle(tl, iced::Size::new(pw, ph), Color { r, g, b, a });
}
}
// ── Wipeout fills (rendered before wires, cover background) ──────────
for hatch in &self.scene.paper_canvas_wipeouts() {

View file

@ -246,39 +246,6 @@ impl Scene {
}
}
/// Build a Primitive for the full paper canvas: paper-space entities
/// (title blocks, frames, borders) plus model-space content projected
/// through each viewport's view matrix into paper-space coordinates.
pub(super) fn build_paper_sheet_primitive(
&self,
hover_region: Option<usize>,
bounds: Rectangle,
) -> Primitive {
let cam = self.camera.borrow();
self.selection.borrow_mut().vp_size = (bounds.width, bounds.height);
let layout_block = self.current_layout_block_handle();
let mut wires = self.paper_sheet_wires();
// When MSPACE is active, exclude that viewport from the CPU projection —
// it is rendered in 3D by the separate PaperViewportPane widget.
wires.extend(self.viewport_content_wires(layout_block, None, self.active_viewport));
if let Some(iw) = &self.interim_wire {
wires.push(iw.clone());
}
wires.extend(self.preview_wires.iter().cloned());
Primitive {
wires,
hatches: self.synced_hatch_models(),
wipeout_hatches: self.wipeout_models(),
images: self.images.values().cloned().collect(),
meshes: self.meshes.values().cloned().collect(),
uniforms: Uniforms::new(&cam, bounds),
cam_rotation: cam.view_rotation_mat(),
hover_region,
bg_color: self.paper_bg_color,
}
}
/// Build a Primitive that renders model-space content through a specific
/// paper-space viewport's camera, applying its layer-freeze list.

View file

@ -9,9 +9,6 @@ use iced::{mouse, Event, Rectangle};
pub enum ViewportPaneMode {
/// Full model space — fills whatever bounds Iced assigns.
Model,
/// Paper-space entities plus model content projected through viewports.
/// Used for the full paper canvas (single widget, no per-viewport widgets).
PaperSheet,
/// Model-space content rendered through a specific viewport's 3-D camera.
///
/// NOTE: Currently unused because Iced 0.14 batches all shader `prepare()`
@ -35,11 +32,6 @@ impl<'a> ViewportPane<'a> {
Self { scene, mode: ViewportPaneMode::Model }
}
/// Paper-sheet layer: paper-space entities rendered with the paper camera.
pub fn paper_sheet(scene: &'a Scene) -> Self {
Self { scene, mode: ViewportPaneMode::PaperSheet }
}
/// One paper-space viewport: model content rendered through its own camera.
/// See [`ViewportPaneMode::Paper`] for why this is currently unused.
#[allow(dead_code)]
@ -116,9 +108,6 @@ impl<'a, Msg: std::fmt::Debug + Clone> shader::Program<Msg> for ViewportPane<'a>
ViewportPaneMode::Model => {
self.scene.build_primitive(state.hover_region, bounds)
}
ViewportPaneMode::PaperSheet => {
self.scene.build_paper_sheet_primitive(state.hover_region, bounds)
}
ViewportPaneMode::Paper { handle } => {
self.scene.build_viewport_primitive(*handle, state.hover_region, bounds)
}
@ -133,7 +122,7 @@ impl<'a, Msg: std::fmt::Debug + Clone> shader::Program<Msg> for ViewportPane<'a>
cursor: mouse::Cursor,
) -> Option<iced::widget::Action<Msg>> {
// ViewCube hover only makes sense in the full model-space view.
if matches!(self.mode, ViewportPaneMode::Model | ViewportPaneMode::PaperSheet) {
if matches!(self.mode, ViewportPaneMode::Model) {
self.scene.update_viewcube_state(state, bounds, cursor);
}
let _ = event;
@ -146,7 +135,7 @@ impl<'a, Msg: std::fmt::Debug + Clone> shader::Program<Msg> for ViewportPane<'a>
_b: Rectangle,
_c: mouse::Cursor,
) -> mouse::Interaction {
if matches!(self.mode, ViewportPaneMode::Model | ViewportPaneMode::PaperSheet) {
if matches!(self.mode, ViewportPaneMode::Model) {
self.scene.viewcube_mouse_interaction(state)
} else {
mouse::Interaction::default()