Skip to content

Commit e814da5

Browse files
committed
Code review and make scaling ratio dynamic
1 parent 0a3b5d2 commit e814da5

File tree

5 files changed

+63
-19
lines changed

5 files changed

+63
-19
lines changed

editor/src/messages/portfolio/document/overlays/overlays_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::messages::prelude::*;
55
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
66
pub enum OverlaysMessage {
77
Draw,
8-
8+
SetDevicePixelRatio { ratio: f64 },
99
// Serde functionality isn't used but is required by the message system macros
1010
AddProvider(#[serde(skip, default = "empty_provider")] OverlayProvider),
1111
RemoveProvider(#[serde(skip, default = "empty_provider")] OverlayProvider),

editor/src/messages/portfolio/document/overlays/overlays_message_handler.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ pub struct OverlaysMessageHandler {
1111
pub overlay_providers: HashSet<OverlayProvider>,
1212
canvas: Option<web_sys::HtmlCanvasElement>,
1313
context: Option<web_sys::CanvasRenderingContext2d>,
14-
#[allow(dead_code)]
1514
device_pixel_ratio: Option<f64>,
1615
}
1716

@@ -42,12 +41,12 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
4241

4342
let size = ipp.viewport_bounds.size().as_uvec2();
4443

45-
let device_pixel_ratio = *self.device_pixel_ratio.get_or_insert_with(|| web_sys::window().map(|w| w.device_pixel_ratio()).unwrap_or(1.0));
44+
let device_pixel_ratio = self.device_pixel_ratio.unwrap_or(1.);
4645

4746
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(device_pixel_ratio)).to_cols_array();
48-
context.set_transform(a, b, c, d, e, f).expect("scaling is necessary to support HiDPI displays");
47+
let _ = context.set_transform(a, b, c, d, e, f);
4948
context.clear_rect(0., 0., ipp.viewport_bounds.size().x, ipp.viewport_bounds.size().y);
50-
context.reset_transform().expect("scaling is necessary to support HiDPI displays");
49+
let _ = context.reset_transform();
5150

5251
if overlays_visible {
5352
responses.add(DocumentMessage::GridOverlays(OverlayContext {
@@ -71,6 +70,10 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
7170
self.canvas, self.context
7271
);
7372
}
73+
OverlaysMessage::SetDevicePixelRatio { ratio } => {
74+
self.device_pixel_ratio = Some(ratio);
75+
responses.add(OverlaysMessage::Draw);
76+
}
7477
OverlaysMessage::AddProvider(message) => {
7578
self.overlay_providers.insert(message);
7679
}

editor/src/messages/portfolio/document/overlays/utility_types.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,8 @@ pub struct OverlayContext {
2626
#[specta(skip)]
2727
pub render_context: web_sys::CanvasRenderingContext2d,
2828
pub size: DVec2,
29-
// The device pixel ratio is a property provided
30-
// by the browser window and is the css pixel size
31-
// divided by the physical pixel size. It allows
32-
// better pixel density of visualizations on
33-
// high dpi displays (such as Retina displays).
29+
// The device pixel ratio is a property provided by the browser window and is the CSS pixel size divided by the physical monitor's pixel size.
30+
// It allows better pixel density of visualizations on high-DPI displays where the OS display scaling is not 100%, or where the browser is zoomed.
3431
pub device_pixel_ratio: f64,
3532
}
3633
// Message hashing isn't used but is required by the message system macros
@@ -44,8 +41,9 @@ impl OverlayContext {
4441
}
4542

4643
pub fn dashed_quad(&mut self, quad: Quad, color_fill: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
47-
// Set the dash pattern
4844
self.start_dpi_aware_transform();
45+
46+
// Set the dash pattern
4947
if let Some(dash_width) = dash_width {
5048
let dash_gap_width = dash_gap_width.unwrap_or(1.);
5149
let array = js_sys::Array::new();
@@ -89,6 +87,7 @@ impl OverlayContext {
8987
if dash_offset.is_some() && dash_offset != Some(0.) {
9088
self.render_context.set_line_dash_offset(0.);
9189
}
90+
9291
self.end_dpi_aware_transform();
9392
}
9493

@@ -97,8 +96,9 @@ impl OverlayContext {
9796
}
9897

9998
pub fn dashed_line(&mut self, start: DVec2, end: DVec2, color: Option<&str>, dash_width: Option<f64>, dash_gap_width: Option<f64>, dash_offset: Option<f64>) {
100-
// Set the dash pattern
10199
self.start_dpi_aware_transform();
100+
101+
// Set the dash pattern
102102
if let Some(dash_width) = dash_width {
103103
let dash_gap_width = dash_gap_width.unwrap_or(1.);
104104
let array = js_sys::Array::new();
@@ -136,11 +136,13 @@ impl OverlayContext {
136136
if dash_offset.is_some() && dash_offset != Some(0.) {
137137
self.render_context.set_line_dash_offset(0.);
138138
}
139+
139140
self.end_dpi_aware_transform();
140141
}
141142

142143
pub fn manipulator_handle(&mut self, position: DVec2, selected: bool, color: Option<&str>) {
143144
self.start_dpi_aware_transform();
145+
144146
let position = position.round() - DVec2::splat(0.5);
145147

146148
self.render_context.begin_path();
@@ -153,6 +155,7 @@ impl OverlayContext {
153155
self.render_context.set_stroke_style_str(color.unwrap_or(COLOR_OVERLAY_BLUE));
154156
self.render_context.fill();
155157
self.render_context.stroke();
158+
156159
self.end_dpi_aware_transform();
157160
}
158161

@@ -162,21 +165,19 @@ impl OverlayContext {
162165
self.square(position, None, Some(color_fill), Some(color_stroke));
163166
}
164167

165-
/// Transforms the Canvas Context To Adjust for DPI
168+
/// Transforms the canvas context to adjust for DPI scaling
166169
///
167-
/// Overwrites all existing tranforms.
168-
/// This operation can be reversed with [`Self::reset_transform`].
170+
/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].
169171
fn start_dpi_aware_transform(&self) {
170172
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.device_pixel_ratio)).to_cols_array();
171173
self.render_context
172174
.set_transform(a, b, c, d, e, f)
173175
.expect("transform should be able to be set to be able to account for DPI");
174176
}
175177

176-
/// Untransforms the Canvas Context To Adjust for DPI
178+
/// Un-transforms the Canvas context to adjust for DPI scaling
177179
///
178-
/// Warning: this function doesn't only reset the
179-
/// DPI adjustment, it resets the entire transform.
180+
/// Warning: this function doesn't only reset the DPI scaling adjustment, it resets the entire transform.
180181
fn end_dpi_aware_transform(&self) {
181182
self.render_context.reset_transform().expect("transform should be able to be reset to be able to account for DPI");
182183
}
@@ -190,12 +191,14 @@ impl OverlayContext {
190191
let corner = position - DVec2::splat(size) / 2.;
191192

192193
self.start_dpi_aware_transform();
194+
193195
self.render_context.begin_path();
194196
self.render_context.rect(corner.x, corner.y, size, size);
195197
self.render_context.set_fill_style_str(color_fill);
196198
self.render_context.set_stroke_style_str(color_stroke);
197199
self.render_context.fill();
198200
self.render_context.stroke();
201+
199202
self.end_dpi_aware_transform();
200203
}
201204

@@ -207,10 +210,12 @@ impl OverlayContext {
207210
let corner = position - DVec2::splat(size) / 2.;
208211

209212
self.start_dpi_aware_transform();
213+
210214
self.render_context.begin_path();
211215
self.render_context.rect(corner.x, corner.y, size, size);
212216
self.render_context.set_fill_style_str(color_fill);
213217
self.render_context.fill();
218+
214219
self.end_dpi_aware_transform();
215220
}
216221

@@ -220,12 +225,14 @@ impl OverlayContext {
220225
let position = position.round();
221226

222227
self.start_dpi_aware_transform();
228+
223229
self.render_context.begin_path();
224230
self.render_context.arc(position.x, position.y, radius, 0., TAU).expect("Failed to draw the circle");
225231
self.render_context.set_fill_style_str(color_fill);
226232
self.render_context.set_stroke_style_str(color_stroke);
227233
self.render_context.fill();
228234
self.render_context.stroke();
235+
229236
self.end_dpi_aware_transform();
230237
}
231238

@@ -291,6 +298,7 @@ impl OverlayContext {
291298
let (x, y) = (position.round() - DVec2::splat(0.5)).into();
292299

293300
self.start_dpi_aware_transform();
301+
294302
// Circle
295303

296304
self.render_context.begin_path();
@@ -315,11 +323,15 @@ impl OverlayContext {
315323
self.render_context.move_to(x, y - crosshair_radius);
316324
self.render_context.line_to(x, y + crosshair_radius);
317325
self.render_context.stroke();
326+
327+
self.render_context.set_line_cap("butt");
328+
318329
self.end_dpi_aware_transform();
319330
}
320331

321332
pub fn outline_vector(&mut self, vector_data: &VectorData, transform: DAffine2) {
322333
self.start_dpi_aware_transform();
334+
323335
self.render_context.begin_path();
324336
let mut last_point = None;
325337
for (_, bezier, start_id, end_id) in vector_data.segment_bezier_iter() {
@@ -331,20 +343,24 @@ impl OverlayContext {
331343

332344
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
333345
self.render_context.stroke();
346+
334347
self.end_dpi_aware_transform();
335348
}
336349

337350
pub fn outline_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
338351
self.start_dpi_aware_transform();
352+
339353
self.render_context.begin_path();
340354
self.bezier_command(bezier, transform, true);
341355
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
342356
self.render_context.stroke();
357+
343358
self.end_dpi_aware_transform();
344359
}
345360

346361
fn bezier_command(&self, bezier: Bezier, transform: DAffine2, move_to: bool) {
347362
self.start_dpi_aware_transform();
363+
348364
let Bezier { start, end, handles } = bezier.apply_transformation(|point| transform.transform_point2(point));
349365
if move_to {
350366
self.render_context.move_to(start.x, start.y);
@@ -355,11 +371,13 @@ impl OverlayContext {
355371
bezier_rs::BezierHandles::Quadratic { handle } => self.render_context.quadratic_curve_to(handle.x, handle.y, end.x, end.y),
356372
bezier_rs::BezierHandles::Cubic { handle_start, handle_end } => self.render_context.bezier_curve_to(handle_start.x, handle_start.y, handle_end.x, handle_end.y, end.x, end.y),
357373
}
374+
358375
self.end_dpi_aware_transform();
359376
}
360377

361378
pub fn outline(&mut self, subpaths: impl Iterator<Item = impl Borrow<Subpath<PointId>>>, transform: DAffine2) {
362379
self.start_dpi_aware_transform();
380+
363381
self.render_context.begin_path();
364382
for subpath in subpaths {
365383
let subpath = subpath.borrow();
@@ -406,6 +424,7 @@ impl OverlayContext {
406424

407425
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE);
408426
self.render_context.stroke();
427+
409428
self.end_dpi_aware_transform();
410429
}
411430

frontend/src/components/panels/Document.svelte

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,22 @@
370370
}
371371
372372
onMount(() => {
373-
devicePixelRatio = window.devicePixelRatio;
373+
// Not compatible with Safari:
374+
// <https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#browser_compatibility>
375+
// <https://bugs.webkit.org/show_bug.cgi?id=124862>
376+
let removeUpdatePixelRatio: (() => void) | undefined = undefined;
377+
const updatePixelRatio = () => {
378+
removeUpdatePixelRatio?.();
379+
const mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
380+
// The event is one-time use, so we have to set up a new listener and remove the old one every time
381+
mediaQueryList.addEventListener("change", updatePixelRatio);
382+
removeUpdatePixelRatio = () => mediaQueryList.removeEventListener("change", updatePixelRatio);
383+
384+
devicePixelRatio = window.devicePixelRatio;
385+
editor.handle.setDevicePixelRatio(devicePixelRatio);
386+
};
387+
updatePixelRatio();
388+
374389
// Update rendered SVGs
375390
editor.subscriptions.subscribeJsMessage(UpdateDocumentArtwork, async (data) => {
376391
await tick();

frontend/wasm/src/editor_api.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,13 @@ impl EditorHandle {
352352
self.dispatch(message);
353353
}
354354

355+
/// Inform the overlays system of the current device pixel ratio
356+
#[wasm_bindgen(js_name = setDevicePixelRatio)]
357+
pub fn set_device_pixel_ratio(&self, ratio: f64) {
358+
let message = OverlaysMessage::SetDevicePixelRatio { ratio };
359+
self.dispatch(message);
360+
}
361+
355362
/// Mouse movement within the screenspace bounds of the viewport
356363
#[wasm_bindgen(js_name = onMouseMove)]
357364
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {

0 commit comments

Comments
 (0)