Wire up scale factor (#245)

Generally prefer use of LogicalPosition to PhysicalPosition for layout,
events, etc. Use scale factor to convert to logical position. Apply
scale factor as final transform before Vello rendering.

This PR also makes physical_position available in PointerState.

There is a TODO for handling WindowEvent::Rescale, which remains and is
not addressed. It should be at some future point.
This commit is contained in:
Raph Levien 2024-05-03 07:11:45 -07:00 committed by GitHub
parent 3c371b7a84
commit 69b995b271
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 49 additions and 38 deletions

View File

@ -8,7 +8,7 @@ use std::time::Duration;
use parley::FontContext;
use tracing::{trace, warn};
use winit::dpi::PhysicalPosition;
use winit::dpi::LogicalPosition;
use winit::window::CursorIcon;
use crate::action::Action;
@ -630,9 +630,7 @@ impl LayoutCtx<'_> {
self.widget_state.local_paint_rect =
self.widget_state.local_paint_rect.union(child.paint_rect());
let mouse_pos = self
.mouse_pos
.map(|pos| PhysicalPosition::new(pos.x, pos.y));
let mouse_pos = self.mouse_pos.map(|pos| LogicalPosition::new(pos.x, pos.y));
// if the widget has moved, it may have moved under the mouse, in which
// case we need to handle that.
if WidgetPod::update_hot_state(

View File

@ -9,7 +9,7 @@ use crate::WidgetId;
use std::{collections::HashSet, path::PathBuf};
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use winit::event::{Ime, KeyEvent, Modifiers, MouseButton};
use winit::keyboard::ModifiersState;
@ -39,7 +39,7 @@ pub enum PointerEvent {
PointerMove(PointerState),
PointerEnter(PointerState),
PointerLeave(PointerState),
MouseWheel(PhysicalPosition<f64>, PointerState),
MouseWheel(LogicalPosition<f64>, PointerState),
HoverFile(PathBuf, PointerState),
DropFile(PathBuf, PointerState),
HoverFileCancel(PointerState),
@ -60,7 +60,8 @@ pub enum TextEvent {
pub struct PointerState {
// TODO
// pub device_id: DeviceId,
pub position: PhysicalPosition<f64>,
pub physical_position: PhysicalPosition<f64>,
pub position: LogicalPosition<f64>,
pub buttons: HashSet<MouseButton>,
pub mods: Modifiers,
pub count: u8,
@ -172,7 +173,7 @@ pub enum InternalLifeCycle {
/// The parents widget origin in window coordinate space has changed.
ParentWindowOrigin {
mouse_pos: Option<PhysicalPosition<f64>>,
mouse_pos: Option<LogicalPosition<f64>>,
},
}
@ -255,7 +256,8 @@ impl PointerState {
let device_id = unsafe { DeviceId::dummy() };
PointerState {
position: PhysicalPosition::new(0.0, 0.0),
physical_position: PhysicalPosition::new(0.0, 0.0),
position: LogicalPosition::new(0.0, 0.0),
buttons: Default::default(),
mods: Default::default(),
count: 0,

View File

@ -5,11 +5,12 @@ use std::num::NonZeroUsize;
use std::sync::Arc;
use tracing::warn;
use vello::kurbo::Affine;
use vello::util::{RenderContext, RenderSurface};
use vello::{peniko::Color, AaSupport, RenderParams, Renderer, RendererOptions, Scene};
use wgpu::PresentMode;
use winit::application::ApplicationHandler;
use winit::dpi::PhysicalPosition;
use winit::dpi::LogicalPosition;
use winit::event::WindowEvent as WinitWindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::window::{Window, WindowId};
@ -45,11 +46,12 @@ pub fn run(
PresentMode::AutoVsync,
))
.unwrap();
let scale_factor = window.scale_factor();
let mut main_state = MainState {
window,
render_cx,
surface,
render_root: RenderRoot::new(root_widget, WindowSizePolicy::User),
render_root: RenderRoot::new(root_widget, WindowSizePolicy::User, scale_factor),
renderer: None,
pointer_state: PointerState::empty(),
app_driver: Box::new(app_driver),
@ -78,7 +80,8 @@ impl ApplicationHandler for MainState<'_> {
.handle_text_event(TextEvent::ModifierChange(modifiers.state()));
}
WinitWindowEvent::CursorMoved { position, .. } => {
self.pointer_state.position = position;
self.pointer_state.physical_position = position;
self.pointer_state.position = position.to_logical(self.window.scale_factor());
self.render_root
.handle_pointer_event(PointerEvent::PointerMove(self.pointer_state.clone()));
}
@ -104,10 +107,13 @@ impl ApplicationHandler for MainState<'_> {
},
WinitWindowEvent::MouseWheel { delta, .. } => {
let delta = match delta {
winit::event::MouseScrollDelta::LineDelta(x, y) => (x as f64, y as f64),
winit::event::MouseScrollDelta::PixelDelta(delta) => (delta.x, delta.y),
winit::event::MouseScrollDelta::LineDelta(x, y) => {
LogicalPosition::new(x as f64, y as f64)
}
winit::event::MouseScrollDelta::PixelDelta(delta) => {
delta.to_logical(self.window.scale_factor())
}
};
let delta = PhysicalPosition::new(delta.0, delta.1);
self.render_root
.handle_pointer_event(PointerEvent::MouseWheel(
delta,
@ -175,7 +181,7 @@ impl ApplicationHandler for MainState<'_> {
impl MainState<'_> {
fn render(&mut self, scene: Scene) {
//let scale = self.window.scale_factor();
let scale = self.window.scale_factor();
let size = self.window.inner_size();
let width = size.width;
let height = size.height;
@ -185,12 +191,14 @@ impl MainState<'_> {
.resize_surface(&mut self.surface, width, height);
}
#[cfg(FALSE)]
let transform = if scale != 1.0 {
Some(Affine::scale(scale))
} else {
let transformed_scene = if scale == 1.0 {
None
} else {
let mut new_scene = Scene::new();
new_scene.append(&scene, Some(Affine::scale(scale)));
Some(new_scene)
};
let scene_ref = transformed_scene.as_ref().unwrap_or(&scene);
let Ok(surface_texture) = self.surface.surface.get_current_texture() else {
warn!("failed to acquire next swapchain texture");
@ -217,7 +225,7 @@ impl MainState<'_> {
};
self.renderer
.get_or_insert_with(|| Renderer::new(device, renderer_options).unwrap())
.render_to_surface(device, queue, &scene, &surface_texture, &render_params)
.render_to_surface(device, queue, scene_ref, &surface_texture, &render_params)
.expect("failed to render to surface");
surface_texture.present();
device.poll(wgpu::Maintain::Wait);

View File

@ -10,7 +10,7 @@ use parley::FontContext;
use tracing::{info_span, warn};
use vello::peniko::{Color, Fill};
use vello::Scene;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
use winit::keyboard::{KeyCode, PhysicalKey};
use winit::window::CursorIcon;
@ -33,7 +33,7 @@ pub struct RenderRoot {
pub(crate) scale_factor: f64,
/// Is `Some` if the most recently displayed frame was an animation frame.
pub(crate) last_anim: Option<Instant>,
pub(crate) last_mouse_pos: Option<PhysicalPosition<f64>>,
pub(crate) last_mouse_pos: Option<LogicalPosition<f64>>,
pub(crate) cursor_icon: CursorIcon,
pub(crate) state: RenderRootState,
}
@ -80,12 +80,12 @@ pub enum RenderRootSignal {
}
impl RenderRoot {
pub fn new(root_widget: impl Widget, size_policy: WindowSizePolicy) -> Self {
pub fn new(root_widget: impl Widget, size_policy: WindowSizePolicy, scale_factor: f64) -> Self {
let mut root = RenderRoot {
root: WidgetPod::new(root_widget).boxed(),
size_policy,
size: PhysicalSize::new(0, 0),
scale_factor: 1.0,
scale_factor,
last_anim: None,
last_mouse_pos: None,
cursor_icon: CursorIcon::Default,

View File

@ -13,7 +13,7 @@ use wgpu::{
BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Extent3d, ImageCopyBuffer,
TextureDescriptor, TextureFormat, TextureUsages,
};
use winit::dpi::{PhysicalPosition, PhysicalSize};
use winit::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
use winit::event::{Ime, MouseButton};
use super::screenshots::get_image_diff;
@ -174,7 +174,7 @@ impl TestHarness {
let window_size = PhysicalSize::new(window_size.width as _, window_size.height as _);
let mut harness = TestHarness {
render_root: RenderRoot::new(root_widget, WindowSizePolicy::User),
render_root: RenderRoot::new(root_widget, WindowSizePolicy::User, 1.0),
mouse_state,
window_size,
background_color,
@ -332,7 +332,10 @@ impl TestHarness {
// FIXME - Account for scaling
let pos = pos.into();
let pos = PhysicalPosition::new(pos.x, pos.y);
self.mouse_state.position = dbg!(pos);
self.mouse_state.physical_position = dbg!(pos);
// TODO: may want to support testing with non-unity scale factors.
let scale_factor = 1.0;
self.mouse_state.position = pos.to_logical(scale_factor);
self.process_pointer_event(PointerEvent::PointerMove(self.mouse_state.clone()));
}
@ -351,7 +354,7 @@ impl TestHarness {
/// Send a Wheel event to the window
pub fn mouse_wheel(&mut self, wheel_delta: Vec2) {
let pixel_delta = PhysicalPosition::new(wheel_delta.x, wheel_delta.y);
let pixel_delta = LogicalPosition::new(wheel_delta.x, wheel_delta.y);
self.process_pointer_event(PointerEvent::MouseWheel(
pixel_delta,
self.mouse_state.clone(),

View File

@ -6,7 +6,7 @@
use smallvec::{smallvec, SmallVec};
use tracing::{trace, trace_span, warn, Span};
use vello::Scene;
use winit::dpi::PhysicalPosition;
use winit::dpi::LogicalPosition;
use winit::event::MouseButton;
use winit::window::CursorIcon;
@ -195,7 +195,7 @@ impl Split {
}
/// Returns true if the provided mouse position is inside the splitter bar area.
fn bar_hit_test(&self, size: Size, mouse_pos: PhysicalPosition<f64>) -> bool {
fn bar_hit_test(&self, size: Size, mouse_pos: LogicalPosition<f64>) -> bool {
let (edge1, edge2) = self.bar_edges(size);
match self.split_axis {
Axis::Horizontal => mouse_pos.x >= edge1 && mouse_pos.x <= edge2,

View File

@ -3,7 +3,7 @@
use tracing::{info_span, trace, warn};
use vello::Scene;
use winit::dpi::PhysicalPosition;
use winit::dpi::LogicalPosition;
use crate::event::{PointerEvent, TextEvent};
use crate::kurbo::{Affine, Insets, Point, Rect, Shape, Size};
@ -231,7 +231,7 @@ impl<W: Widget> WidgetPod<W> {
inner: &mut W,
inner_state: &mut WidgetState,
global_state: &mut RenderRootState,
mouse_pos: Option<PhysicalPosition<f64>>,
mouse_pos: Option<LogicalPosition<f64>>,
) -> bool {
let rect = inner_state.layout_rect() + inner_state.parent_window_origin.to_vec2();
let had_hot = inner_state.is_hot;
@ -607,7 +607,7 @@ impl<W: Widget> WidgetPod<W> {
InternalLifeCycle::ParentWindowOrigin { mouse_pos } => {
self.state.parent_window_origin = parent_ctx.widget_state.window_origin();
self.state.needs_window_origin = false;
let mouse_pos = mouse_pos.map(|pos| PhysicalPosition::new(pos.x, pos.y));
let mouse_pos = mouse_pos.map(|pos| LogicalPosition::new(pos.x, pos.y));
WidgetPod::update_hot_state(
&mut self.inner,
&mut self.state,

View File

@ -12,7 +12,6 @@ use vello::{
use wgpu::PresentMode;
use winit::{
application::ApplicationHandler,
dpi::PhysicalPosition,
event::{ElementState, Modifiers, MouseButton, MouseScrollDelta, WindowEvent},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
window::{Window, WindowId},
@ -170,8 +169,9 @@ impl<'a, T: Send + 'static, V: View<T> + 'static> MainState<'a, T, V> {
MouseScrollDelta::LineDelta(x, y) => {
ScrollDelta::Lines(x.trunc() as isize, y.trunc() as isize)
}
MouseScrollDelta::PixelDelta(PhysicalPosition { x, y }) => {
ScrollDelta::Precise(Vec2::new(x, y) * (1.0 / self.window.scale_factor()))
MouseScrollDelta::PixelDelta(position) => {
let logical_pos = position.to_logical(self.window.scale_factor());
ScrollDelta::Precise(Vec2::new(logical_pos.x, logical_pos.y))
}
})));
self.window.request_redraw();

View File

@ -38,7 +38,7 @@ pub struct MouseEvent {
/// > Positive values indicate that the content that is being scrolled should move
/// > right and down (revealing more content left and up).
///
/// The choice to follow this has not been reasoned, but is based on expediance
/// The choice to follow this has not been reasoned, but is based on expedience.
pub enum ScrollDelta {
Precise(Vec2),
Lines(isize, isize),