mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Initial support for hidpi (#293)
* Start work on hidpi support * Introduce maplibre physical and logical size types
This commit is contained in:
parent
10b0e10ad3
commit
ad95f7665f
@ -59,11 +59,11 @@ impl InputController {
|
||||
|
||||
/// Process the given winit `[winit::event::WindowEvent]`.
|
||||
/// Returns true if the event has been processed and false otherwise.
|
||||
pub fn window_input(&mut self, event: &WindowEvent) -> bool {
|
||||
pub fn window_input(&mut self, event: &WindowEvent, scale_factor: f64) -> bool {
|
||||
match event {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let position: (f64, f64) = position.to_owned().into();
|
||||
let position = Vector2::from(position);
|
||||
let position = Vector2::from(position) / scale_factor;
|
||||
self.pan_handler.process_window_position(&position, false);
|
||||
self.query_handler.process_window_position(&position, false);
|
||||
self.zoom_handler.process_window_position(&position, false);
|
||||
@ -99,14 +99,11 @@ impl InputController {
|
||||
}
|
||||
TouchPhase::Moved => {
|
||||
let position: (f64, f64) = touch.location.to_owned().into();
|
||||
self.pan_handler
|
||||
.process_window_position(&Vector2::from(position), true);
|
||||
self.query_handler
|
||||
.process_window_position(&Vector2::from(position), true);
|
||||
self.zoom_handler
|
||||
.process_window_position(&Vector2::from(position), true);
|
||||
self.camera_handler
|
||||
.process_window_position(&Vector2::from(position), true);
|
||||
let position = Vector2::from(position) / scale_factor;
|
||||
self.pan_handler.process_window_position(&position, true);
|
||||
self.query_handler.process_window_position(&position, true);
|
||||
self.zoom_handler.process_window_position(&position, true);
|
||||
self.camera_handler.process_window_position(&position, true);
|
||||
true
|
||||
}
|
||||
TouchPhase::Cancelled => false,
|
||||
|
||||
@ -8,7 +8,7 @@ use maplibre::{
|
||||
event_loop::{EventLoop, EventLoopProxy, SendEventError},
|
||||
io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient},
|
||||
map::Map,
|
||||
window::{HeadedMapWindow, MapWindowConfig},
|
||||
window::{HeadedMapWindow, MapWindowConfig, PhysicalSize},
|
||||
};
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
@ -77,6 +77,7 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
|
||||
let mut current_frame: u64 = 0;
|
||||
|
||||
let mut input_controller = InputController::new(0.2, 100.0, 0.1);
|
||||
let mut scale_factor = 1.0;
|
||||
|
||||
self.event_loop
|
||||
.run(move |event, _window_target, control_flow| {
|
||||
@ -104,7 +105,7 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
|
||||
ref event,
|
||||
window_id,
|
||||
} if window_id == map.window().id().into() => {
|
||||
if !input_controller.window_input(event) {
|
||||
if !input_controller.window_input(event, scale_factor) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
@ -116,14 +117,17 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
|
||||
},
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
if let Ok(map_context) = map.context_mut() {
|
||||
map_context.resize(physical_size.width, physical_size.height);
|
||||
WindowEvent::Resized(winit::dpi::PhysicalSize { width, height}) => {
|
||||
if let Ok(map_context) = map.context_mut() {
|
||||
let size = PhysicalSize::new(*width, *height).expect("window values should not be zero");
|
||||
map_context.resize(size, scale_factor);
|
||||
}
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
WindowEvent::ScaleFactorChanged { new_inner_size: winit::dpi::PhysicalSize { width, height}, scale_factor: new_scale_factor } => {
|
||||
if let Ok(map_context) = map.context_mut() {
|
||||
map_context.resize(new_inner_size.width, new_inner_size.height);
|
||||
scale_factor = *new_scale_factor;
|
||||
let size = PhysicalSize::new(*width, *height).expect("window values should not be zero");
|
||||
map_context.resize(size, scale_factor);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -178,6 +182,7 @@ impl<ET: 'static + PartialEq + Debug> EventLoop<ET> for WinitEventLoop<ET> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WinitEventLoopProxy<ET: 'static> {
|
||||
proxy: RawEventLoopProxy<ET>,
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ use maplibre::{
|
||||
},
|
||||
render::{builder::RendererBuilder, settings::WgpuSettings, RenderPlugin},
|
||||
style::Style,
|
||||
window::{MapWindow, MapWindowConfig, WindowSize},
|
||||
window::{MapWindow, MapWindowConfig, PhysicalSize},
|
||||
};
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
@ -54,17 +54,17 @@ impl<ET> WinitMapWindowConfig<ET> {
|
||||
}
|
||||
|
||||
impl<ET> MapWindow for WinitMapWindow<ET> {
|
||||
fn size(&self) -> WindowSize {
|
||||
fn size(&self) -> PhysicalSize {
|
||||
let size = self.window.inner_size();
|
||||
#[cfg(target_os = "android")]
|
||||
// On android we can not get the dimensions of the window initially. Therefore, we use a
|
||||
// fallback until the window is ready to deliver its correct bounds.
|
||||
let window_size =
|
||||
WindowSize::new(size.width, size.height).unwrap_or(WindowSize::new(100, 100).unwrap());
|
||||
let window_size = PhysicalSize::new(size.width, size.height)
|
||||
.unwrap_or(PhysicalSize::new(100, 100).unwrap());
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let window_size =
|
||||
WindowSize::new(size.width, size.height).expect("failed to get window dimensions.");
|
||||
PhysicalSize::new(size.width, size.height).expect("failed to get window dimensions.");
|
||||
window_size
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use maplibre::window::{MapWindow, MapWindowConfig, WindowSize};
|
||||
use maplibre::window::{MapWindow, MapWindowConfig, PhysicalSize};
|
||||
use winit::{platform::web::WindowBuilderExtWebSys, window::WindowBuilder};
|
||||
|
||||
use super::WinitMapWindow;
|
||||
@ -44,10 +44,10 @@ impl<ET: 'static + Clone> MapWindowConfig for WinitMapWindowConfig<ET> {
|
||||
}
|
||||
|
||||
impl<ET: 'static> MapWindow for WinitMapWindow<ET> {
|
||||
fn size(&self) -> WindowSize {
|
||||
fn size(&self) -> PhysicalSize {
|
||||
let size = self.window.inner_size();
|
||||
|
||||
WindowSize::new(size.width, size.height).expect("failed to get window dimensions.")
|
||||
PhysicalSize::new(size.width, size.height).expect("failed to get window dimensions.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ use crate::{
|
||||
render::{view_state::ViewState, Renderer},
|
||||
style::Style,
|
||||
tcs::world::World,
|
||||
window::PhysicalSize,
|
||||
};
|
||||
|
||||
/// Stores the context of the map.
|
||||
@ -16,8 +17,8 @@ pub struct MapContext {
|
||||
}
|
||||
|
||||
impl MapContext {
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.view_state.resize(width, height);
|
||||
self.renderer.resize_surface(width, height)
|
||||
pub fn resize(&mut self, size: PhysicalSize, scale_factor: f64) {
|
||||
self.view_state.resize(size.to_logical(scale_factor));
|
||||
self.renderer.resize_surface(size)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ use crate::{
|
||||
},
|
||||
schedule::Schedule,
|
||||
tcs::{system::SystemContainer, world::World},
|
||||
window::{MapWindowConfig, WindowSize},
|
||||
window::{MapWindowConfig, PhysicalSize},
|
||||
};
|
||||
|
||||
mod graph_node;
|
||||
@ -34,7 +34,7 @@ pub async fn create_headless_renderer(
|
||||
let client = ReqwestHttpClient::new(cache_path);
|
||||
let mut kernel = KernelBuilder::new()
|
||||
.with_map_window_config(HeadlessMapWindowConfig::new(
|
||||
WindowSize::new(tile_size, tile_size).unwrap(),
|
||||
PhysicalSize::new(tile_size, tile_size).unwrap(),
|
||||
))
|
||||
.with_http_client(client.clone())
|
||||
.with_apc(SchedulerAsyncProcedureCall::new(TokioScheduler::new()))
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use crate::window::{MapWindow, MapWindowConfig, WindowSize};
|
||||
use crate::window::{MapWindow, MapWindowConfig, PhysicalSize};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HeadlessMapWindowConfig {
|
||||
size: WindowSize,
|
||||
size: PhysicalSize,
|
||||
}
|
||||
|
||||
impl HeadlessMapWindowConfig {
|
||||
pub fn new(size: WindowSize) -> Self {
|
||||
pub fn new(size: PhysicalSize) -> Self {
|
||||
Self { size }
|
||||
}
|
||||
}
|
||||
@ -20,11 +20,11 @@ impl MapWindowConfig for HeadlessMapWindowConfig {
|
||||
}
|
||||
|
||||
pub struct HeadlessMapWindow {
|
||||
size: WindowSize,
|
||||
size: PhysicalSize,
|
||||
}
|
||||
|
||||
impl MapWindow for HeadlessMapWindow {
|
||||
fn size(&self) -> WindowSize {
|
||||
fn size(&self) -> PhysicalSize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,10 +67,13 @@ pub mod view_state;
|
||||
|
||||
pub use shaders::ShaderVertex;
|
||||
|
||||
use crate::render::{
|
||||
render_phase::{LayerItem, RenderPhase, TileMaskItem},
|
||||
systems::{graph_runner_system::GraphRunnerSystem, upload_system::upload_system},
|
||||
tile_view_pattern::{ViewTileSources, WgpuTileViewPattern},
|
||||
use crate::{
|
||||
render::{
|
||||
render_phase::{LayerItem, RenderPhase, TileMaskItem},
|
||||
systems::{graph_runner_system::GraphRunnerSystem, upload_system::upload_system},
|
||||
tile_view_pattern::{ViewTileSources, WgpuTileViewPattern},
|
||||
},
|
||||
window::PhysicalSize,
|
||||
};
|
||||
|
||||
pub(crate) const INDEX_FORMAT: wgpu::IndexFormat = wgpu::IndexFormat::Uint32; // Must match IndexDataType
|
||||
@ -240,8 +243,8 @@ impl Renderer {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resize_surface(&mut self, width: u32, height: u32) {
|
||||
self.resources.surface.resize(width, height)
|
||||
pub fn resize_surface(&mut self, size: PhysicalSize) {
|
||||
self.resources.surface.resize(size)
|
||||
}
|
||||
|
||||
/// Requests a device
|
||||
@ -421,12 +424,12 @@ impl Renderer {
|
||||
mod tests {
|
||||
use crate::{
|
||||
tcs::world::World,
|
||||
window::{MapWindow, MapWindowConfig, WindowSize},
|
||||
window::{MapWindow, MapWindowConfig, PhysicalSize},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HeadlessMapWindowConfig {
|
||||
size: WindowSize,
|
||||
size: PhysicalSize,
|
||||
}
|
||||
|
||||
impl MapWindowConfig for HeadlessMapWindowConfig {
|
||||
@ -438,11 +441,11 @@ mod tests {
|
||||
}
|
||||
|
||||
pub struct HeadlessMapWindow {
|
||||
size: WindowSize,
|
||||
size: PhysicalSize,
|
||||
}
|
||||
|
||||
impl MapWindow for HeadlessMapWindow {
|
||||
fn size(&self) -> WindowSize {
|
||||
fn size(&self) -> PhysicalSize {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
@ -486,7 +489,7 @@ mod tests {
|
||||
let render_state = RenderResources::new(Surface::from_image(
|
||||
&device,
|
||||
&HeadlessMapWindow {
|
||||
size: WindowSize::new(100, 100).expect("invalid headless map size"),
|
||||
size: PhysicalSize::new(100, 100).expect("invalid headless map size"),
|
||||
},
|
||||
&RendererSettings::default(),
|
||||
));
|
||||
|
||||
@ -12,7 +12,7 @@ use crate::{
|
||||
resource::texture::TextureView,
|
||||
settings::{Msaa, RendererSettings},
|
||||
},
|
||||
window::{HeadedMapWindow, MapWindow, WindowSize},
|
||||
window::{HeadedMapWindow, MapWindow, PhysicalSize},
|
||||
};
|
||||
|
||||
pub struct BufferDimensions {
|
||||
@ -23,7 +23,7 @@ pub struct BufferDimensions {
|
||||
}
|
||||
|
||||
impl BufferDimensions {
|
||||
fn new(size: WindowSize) -> Self {
|
||||
fn new(size: PhysicalSize) -> Self {
|
||||
let bytes_per_pixel = size_of::<u32>() as u32;
|
||||
let unpadded_bytes_per_row = size.width() * bytes_per_pixel;
|
||||
|
||||
@ -41,7 +41,7 @@ impl BufferDimensions {
|
||||
|
||||
pub struct WindowHead {
|
||||
surface: wgpu::Surface,
|
||||
size: WindowSize,
|
||||
size: PhysicalSize,
|
||||
|
||||
texture_format: wgpu::TextureFormat,
|
||||
present_mode: wgpu::PresentMode,
|
||||
@ -50,7 +50,7 @@ pub struct WindowHead {
|
||||
|
||||
impl WindowHead {
|
||||
pub fn resize_and_configure(&mut self, width: u32, height: u32, device: &wgpu::Device) {
|
||||
self.size = WindowSize::new(width, height).unwrap();
|
||||
self.size = PhysicalSize::new(width, height).unwrap();
|
||||
self.configure(device);
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ pub enum Head {
|
||||
}
|
||||
|
||||
pub struct Surface {
|
||||
size: WindowSize,
|
||||
size: PhysicalSize,
|
||||
head: Head,
|
||||
}
|
||||
|
||||
@ -289,12 +289,12 @@ impl Surface {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> WindowSize {
|
||||
pub fn size(&self) -> PhysicalSize {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.size = WindowSize::new(width, height).expect("Invalid size for resizing the surface.");
|
||||
pub fn resize(&mut self, size: PhysicalSize) {
|
||||
self.size = size;
|
||||
}
|
||||
|
||||
pub fn reconfigure(&mut self, device: &wgpu::Device) {
|
||||
|
||||
@ -15,7 +15,7 @@ use crate::{
|
||||
math::{bounds_from_points, Aabb2, Aabb3, Plane},
|
||||
ChangeObserver,
|
||||
},
|
||||
window::WindowSize,
|
||||
window::{LogicalSize, PhysicalSize},
|
||||
};
|
||||
|
||||
const VIEW_REGION_PADDING: i32 = 1;
|
||||
@ -33,7 +33,7 @@ pub struct ViewState {
|
||||
|
||||
impl ViewState {
|
||||
pub fn new<F: Into<Rad<f64>>, P: Into<Deg<f64>>>(
|
||||
window_size: WindowSize,
|
||||
window_size: PhysicalSize,
|
||||
position: WorldCoords,
|
||||
zoom: Zoom,
|
||||
pitch: P,
|
||||
@ -65,9 +65,9 @@ impl ViewState {
|
||||
&self.edge_insets
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
self.width = width as f64;
|
||||
self.height = height as f64;
|
||||
pub fn resize(&mut self, size: LogicalSize) {
|
||||
self.width = size.width() as f64;
|
||||
self.height = size.height() as f64;
|
||||
}
|
||||
|
||||
pub fn create_view_region(&self, visible_level: ZoomLevel) -> Option<ViewRegion> {
|
||||
@ -469,14 +469,14 @@ mod tests {
|
||||
use crate::{
|
||||
coords::{WorldCoords, Zoom},
|
||||
render::view_state::ViewState,
|
||||
window::WindowSize,
|
||||
window::PhysicalSize,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn conform_transformation() {
|
||||
let fov = Deg(60.0);
|
||||
let mut state = ViewState::new(
|
||||
WindowSize::new(800, 600).unwrap(),
|
||||
PhysicalSize::new(800, 600).unwrap(),
|
||||
WorldCoords::at_ground(0.0, 0.0),
|
||||
Zoom::new(10.0),
|
||||
Deg(0.0),
|
||||
|
||||
@ -4,9 +4,9 @@ use std::num::NonZeroU32;
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
/// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one.
|
||||
/// Window of a certain [`PhysicalSize`]. This can either be a proper window or a headless one.
|
||||
pub trait MapWindow {
|
||||
fn size(&self) -> WindowSize;
|
||||
fn size(&self) -> PhysicalSize;
|
||||
}
|
||||
|
||||
/// Window which references a physical `RawWindow`. This is only implemented by headed windows and
|
||||
@ -31,13 +31,13 @@ pub trait MapWindowConfig: 'static + Clone {
|
||||
}
|
||||
|
||||
/// Window size with a width and an height in pixels.
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct WindowSize {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct PhysicalSize {
|
||||
width: NonZeroU32,
|
||||
height: NonZeroU32,
|
||||
}
|
||||
|
||||
impl WindowSize {
|
||||
impl PhysicalSize {
|
||||
pub fn new(width: u32, height: u32) -> Option<Self> {
|
||||
Some(Self {
|
||||
width: NonZeroU32::new(width)?,
|
||||
@ -61,3 +61,45 @@ impl WindowSize {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct LogicalSize {
|
||||
width: NonZeroU32,
|
||||
height: NonZeroU32,
|
||||
}
|
||||
|
||||
impl LogicalSize {
|
||||
pub fn new(width: u32, height: u32) -> Option<Self> {
|
||||
Some(Self {
|
||||
width: NonZeroU32::new(width)?,
|
||||
height: NonZeroU32::new(height)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn width(&self) -> u32 {
|
||||
self.width.get()
|
||||
}
|
||||
|
||||
pub fn width_non_zero(&self) -> NonZeroU32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
self.height.get()
|
||||
}
|
||||
|
||||
pub fn height_non_zero(&self) -> NonZeroU32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalSize {
|
||||
pub fn to_logical(&self, scale_factor: f64) -> LogicalSize {
|
||||
let width = self.width.get() as f64 / scale_factor;
|
||||
let height = self.height.get() as f64 / scale_factor;
|
||||
LogicalSize {
|
||||
width: NonZeroU32::new(width as u32).expect("impossible to reach"),
|
||||
height: NonZeroU32::new(height as u32).expect("impossible to reach"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user