mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Merge pull request #4540 from storytold/land/4292
Fix canvas pointer mapping under subpixel dock layouts
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! The document canvas: rendering, rulers, navigation, pointer routing to tools and on-canvas
|
||||
//! selection visuals (bounding box, anchors, handles, smart-guide style labels).
|
||||
|
||||
use egui::emath::GuiRounding as _;
|
||||
use egui::{Color32, CornerRadius, Pos2, Sense, Shape, Stroke, StrokeKind, Ui, Vec2, pos2, vec2};
|
||||
use serde_json::json;
|
||||
use vectorcraft_doc::text::TextKind;
|
||||
@@ -196,6 +197,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let bars = app.ui.screen_mode < 2;
|
||||
let full = if bars { egui::Rect::from_min_max(full.min, full.max - vec2(SCROLL_BAR, SCROLL_BAR)) } else { full };
|
||||
let rect = if app.ui.view.rulers && app.ui.screen_mode < 3 { egui::Rect::from_min_max(full.min + vec2(RULER, RULER), full.max) } else { full };
|
||||
// Dock layout can differ by fractions of a device pixel between fresh windows.
|
||||
// Use one pixel-aligned rectangle for drawing, inspection and pointer transforms.
|
||||
let rect = rect.round_to_pixels(ui.ctx().pixels_per_point());
|
||||
app.canvas_rect = Some(rect);
|
||||
let fitted = app.view().is_some_and(|v| v.fitted);
|
||||
if !fitted {
|
||||
|
||||
@@ -2,6 +2,32 @@ use super::*;
|
||||
use vectorcraft_engine::Session;
|
||||
use vectorcraft_geom::Shape as _;
|
||||
|
||||
#[test]
|
||||
fn subpixel_dock_layout_does_not_change_pointer_document_coordinates() {
|
||||
let mut observed = Vec::new();
|
||||
for width in [1324.0 + 29.0 / 32.0, 1324.875, 1325.875] {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 800, "height": 600, "created": 0})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
ctx.set_pixels_per_point(2.0);
|
||||
let raw = egui::RawInput {
|
||||
screen_rect: Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(1800.0, 900.0))),
|
||||
max_texture_side: Some(8192),
|
||||
..Default::default()
|
||||
};
|
||||
let mut out = ctx.run_ui(raw, |ui| {
|
||||
let area = egui::Rect::from_min_size(pos2(54.0, 72.0), vec2(width + SCROLL_BAR, 636.0 + SCROLL_BAR));
|
||||
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(area));
|
||||
show(&mut app, &mut child);
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
let rect = app.canvas_rect.unwrap();
|
||||
observed.push((rect, Xf::new(rect, app.view().unwrap()).to_doc(pos2(440.0, 330.0))));
|
||||
}
|
||||
assert_eq!(observed[0], observed[1], "the same physical canvas must map the same pointer identically");
|
||||
assert_ne!(observed[0], observed[2], "a real device-pixel resize must still change the mapping");
|
||||
}
|
||||
|
||||
/// A rendered raster becomes the image egui's own conversion makes of it, half-transparent
|
||||
/// edges and all; one whose bytes don't match its size becomes an empty image of that size.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user