add UI scaling support
This commit is contained in:
parent
d5c3ba0bb0
commit
2bf229575f
|
@ -27,9 +27,10 @@ fn main() {
|
||||||
title: "baseview".into(),
|
title: "baseview".into(),
|
||||||
width: 512,
|
width: 512,
|
||||||
height: 512,
|
height: 512,
|
||||||
|
scale: 1.0,
|
||||||
parent: baseview::Parent::None,
|
parent: baseview::Parent::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle = Window::open::<MyProgram>(window_open_options);
|
let (handle, _window_info) = Window::open::<MyProgram>(window_open_options).unwrap();
|
||||||
handle.app_run_blocking();
|
handle.app_run_blocking();
|
||||||
}
|
}
|
||||||
|
|
11
src/event.rs
11
src/event.rs
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::WindowInfo;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum KeyboardEvent {
|
pub enum KeyboardEvent {
|
||||||
KeyPressed(u32),
|
KeyPressed(u32),
|
||||||
|
@ -72,14 +74,7 @@ pub enum MouseEvent {
|
||||||
CursorLeft,
|
CursorLeft,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct WindowInfo {
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
pub scale: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum WindowEvent {
|
pub enum WindowEvent {
|
||||||
Resized(WindowInfo),
|
Resized(WindowInfo),
|
||||||
Focused,
|
Focused,
|
||||||
|
|
24
src/lib.rs
24
src/lib.rs
|
@ -18,9 +18,11 @@ pub use macos::*;
|
||||||
mod event;
|
mod event;
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod mouse_cursor;
|
mod mouse_cursor;
|
||||||
|
mod window_info;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
pub use keyboard::*;
|
pub use keyboard::*;
|
||||||
pub use mouse_cursor::MouseCursor;
|
pub use mouse_cursor::MouseCursor;
|
||||||
|
pub use window_info::WindowInfo;
|
||||||
|
|
||||||
pub enum Parent {
|
pub enum Parent {
|
||||||
None,
|
None,
|
||||||
|
@ -33,12 +35,30 @@ unsafe impl Send for Parent {}
|
||||||
pub struct WindowOpenOptions {
|
pub struct WindowOpenOptions {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|
||||||
pub width: usize,
|
/// The logical width of the window
|
||||||
pub height: usize,
|
pub width: u32,
|
||||||
|
/// The logical height of the window
|
||||||
|
pub height: u32,
|
||||||
|
|
||||||
|
/// The dpi scale factor. This will used in conjunction with the dpi scale
|
||||||
|
/// factor of the system.
|
||||||
|
pub scale: f64,
|
||||||
|
|
||||||
pub parent: Parent,
|
pub parent: Parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct WindowHandle {
|
||||||
|
thread: std::thread::JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowHandle {
|
||||||
|
pub fn app_run_blocking(self) {
|
||||||
|
let _ = self.thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WindowOpenResult = Result<WindowInfo, ()>;
|
||||||
|
|
||||||
pub trait WindowHandler {
|
pub trait WindowHandler {
|
||||||
type Message;
|
type Message;
|
||||||
|
|
||||||
|
|
76
src/window_info.rs
Normal file
76
src/window_info.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/// The info about the window
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct WindowInfo {
|
||||||
|
/// The physical the width of the window
|
||||||
|
physical_width: u32,
|
||||||
|
/// The physical height of the window
|
||||||
|
physical_height: u32,
|
||||||
|
/// The logical width of the window
|
||||||
|
logical_width: u32,
|
||||||
|
/// The logical height of the window
|
||||||
|
logical_height: u32,
|
||||||
|
/// The scale factor
|
||||||
|
scale: f64,
|
||||||
|
scale_recip: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowInfo {
|
||||||
|
pub fn from_logical_size(logical_width: u32, logical_height: u32, scale: f64) -> Self {
|
||||||
|
let scale_recip = 1.0 / scale;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
physical_width: (logical_width as f64 * scale).round() as u32,
|
||||||
|
physical_height: (logical_height as f64 * scale).round() as u32,
|
||||||
|
logical_width,
|
||||||
|
logical_height,
|
||||||
|
scale,
|
||||||
|
scale_recip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_physical_size(physical_width: u32, physical_height: u32, scale: f64) -> Self {
|
||||||
|
let scale_recip = 1.0 / scale;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
physical_width,
|
||||||
|
physical_height,
|
||||||
|
logical_width: (physical_width as f64 * scale_recip).round() as u32,
|
||||||
|
logical_height: (physical_height as f64 * scale_recip).round() as u32,
|
||||||
|
scale,
|
||||||
|
scale_recip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The physical width of the window
|
||||||
|
pub fn physical_width(&self) -> u32 {
|
||||||
|
self.physical_width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The physical height of the window
|
||||||
|
pub fn physical_height(&self) -> u32 {
|
||||||
|
self.physical_height
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The logical width of the window
|
||||||
|
pub fn logical_width(&self) -> u32 {
|
||||||
|
self.logical_width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The logical height of the window
|
||||||
|
pub fn logical_height(&self) -> u32 {
|
||||||
|
self.logical_height
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scale factor of the window
|
||||||
|
pub fn scale(&self) -> f64 {
|
||||||
|
self.scale
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert physical mouse coordinates to logical coordinates
|
||||||
|
pub fn physical_to_logical(&self, x: f64, y: f64) -> (f64, f64) {
|
||||||
|
(
|
||||||
|
x * self.scale_recip,
|
||||||
|
y * self.scale_recip
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ use raw_window_handle::{
|
||||||
use super::XcbConnection;
|
use super::XcbConnection;
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent,
|
Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent,
|
||||||
WindowHandler,WindowInfo, WindowOpenOptions,
|
WindowHandler,WindowInfo, WindowOpenOptions, WindowOpenResult, WindowHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
|
@ -24,24 +24,11 @@ pub struct Window {
|
||||||
frame_interval: Duration,
|
frame_interval: Duration,
|
||||||
event_loop_running: bool,
|
event_loop_running: bool,
|
||||||
|
|
||||||
new_size: Option<(u32, u32)>
|
new_physical_size: Option<(u32, u32)>
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: move to outer crate context
|
|
||||||
pub struct WindowHandle {
|
|
||||||
thread: std::thread::JoinHandle<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowHandle {
|
|
||||||
pub fn app_run_blocking(self) {
|
|
||||||
let _ = self.thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type WindowOpenResult = Result<(), ()>;
|
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> WindowHandle {
|
pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> Result<(WindowHandle, WindowInfo), ()> {
|
||||||
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
let thread = thread::spawn(move || {
|
||||||
|
@ -51,9 +38,9 @@ impl Window {
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME: placeholder types for returning errors in the future
|
// FIXME: placeholder types for returning errors in the future
|
||||||
let _ = rx.recv();
|
let window_info = rx.recv().unwrap().unwrap();
|
||||||
|
|
||||||
WindowHandle { thread }
|
Ok((WindowHandle { thread }, window_info))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_thread<H: WindowHandler>(
|
fn window_thread<H: WindowHandler>(
|
||||||
|
@ -92,6 +79,10 @@ impl Window {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let scaling = xcb_connection.get_scaling().unwrap_or(1.0) * options.scale;
|
||||||
|
|
||||||
|
let window_info = WindowInfo::from_logical_size(options.width, options.height, scaling);
|
||||||
|
|
||||||
let window_id = xcb_connection.conn.generate_id();
|
let window_id = xcb_connection.conn.generate_id();
|
||||||
xcb::create_window(
|
xcb::create_window(
|
||||||
&xcb_connection.conn,
|
&xcb_connection.conn,
|
||||||
|
@ -100,8 +91,8 @@ impl Window {
|
||||||
parent_id,
|
parent_id,
|
||||||
0, // x coordinate of the new window
|
0, // x coordinate of the new window
|
||||||
0, // y coordinate of the new window
|
0, // y coordinate of the new window
|
||||||
options.width as u16, // window width
|
window_info.physical_width() as u16, // window width
|
||||||
options.height as u16, // window height
|
window_info.physical_height() as u16, // window height
|
||||||
0, // window border
|
0, // window border
|
||||||
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
|
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
|
||||||
screen.root_visual(),
|
screen.root_visual(),
|
||||||
|
@ -144,14 +135,6 @@ impl Window {
|
||||||
|
|
||||||
xcb_connection.conn.flush();
|
xcb_connection.conn.flush();
|
||||||
|
|
||||||
let scaling = xcb_connection.get_scaling().unwrap_or(1.0);
|
|
||||||
|
|
||||||
let window_info = WindowInfo {
|
|
||||||
width: options.width as u32,
|
|
||||||
height: options.height as u32,
|
|
||||||
scale: scaling,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut window = Self {
|
let mut window = Self {
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
window_id,
|
window_id,
|
||||||
|
@ -161,15 +144,15 @@ impl Window {
|
||||||
frame_interval: Duration::from_millis(15),
|
frame_interval: Duration::from_millis(15),
|
||||||
event_loop_running: false,
|
event_loop_running: false,
|
||||||
|
|
||||||
new_size: None
|
new_physical_size: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut handler = H::build(&mut window);
|
let mut handler = H::build(&mut window);
|
||||||
|
|
||||||
let _ = tx.send(Ok(()));
|
let _ = tx.send(Ok(window_info));
|
||||||
|
|
||||||
window.run_event_loop(&mut handler);
|
window.run_event_loop(&mut handler);
|
||||||
Ok(())
|
Ok(window_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_info(&self) -> &WindowInfo {
|
pub fn window_info(&self) -> &WindowInfo {
|
||||||
|
@ -201,15 +184,18 @@ impl Window {
|
||||||
// the X server has a tendency to send spurious/extraneous configure notify events when a
|
// the X server has a tendency to send spurious/extraneous configure notify events when a
|
||||||
// window is resized, and we need to batch those together and just send one resize event
|
// window is resized, and we need to batch those together and just send one resize event
|
||||||
// when they've all been coalesced.
|
// when they've all been coalesced.
|
||||||
self.new_size = None;
|
self.new_physical_size = None;
|
||||||
|
|
||||||
while let Some(event) = self.xcb_connection.conn.poll_for_event() {
|
while let Some(event) = self.xcb_connection.conn.poll_for_event() {
|
||||||
self.handle_xcb_event(handler, event);
|
self.handle_xcb_event(handler, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((width, height)) = self.new_size.take() {
|
if let Some((width, height)) = self.new_physical_size.take() {
|
||||||
self.window_info.width = width;
|
self.window_info = WindowInfo::from_physical_size(
|
||||||
self.window_info.height = height;
|
width,
|
||||||
|
height,
|
||||||
|
self.window_info.scale()
|
||||||
|
);
|
||||||
|
|
||||||
handler.on_event(self, Event::Window(
|
handler.on_event(self, Event::Window(
|
||||||
WindowEvent::Resized(self.window_info)
|
WindowEvent::Resized(self.window_info)
|
||||||
|
@ -312,11 +298,11 @@ impl Window {
|
||||||
xcb::CONFIGURE_NOTIFY => {
|
xcb::CONFIGURE_NOTIFY => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ConfigureNotifyEvent>(&event) };
|
let event = unsafe { xcb::cast_event::<xcb::ConfigureNotifyEvent>(&event) };
|
||||||
|
|
||||||
let new_size = (event.width() as u32, event.height() as u32);
|
let new_physical_size = (event.width() as u32, event.height() as u32);
|
||||||
let cur_size = (self.window_info.width, self.window_info.height);
|
let cur_physical_size = (self.window_info.physical_width(), self.window_info.physical_height());
|
||||||
|
|
||||||
if self.new_size.is_some() || new_size != cur_size {
|
if self.new_physical_size.is_some() || new_physical_size != cur_physical_size {
|
||||||
self.new_size = Some(new_size);
|
self.new_physical_size = Some(new_physical_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,11 +314,16 @@ impl Window {
|
||||||
let detail = event.detail();
|
let detail = event.detail();
|
||||||
|
|
||||||
if detail != 4 && detail != 5 {
|
if detail != 4 && detail != 5 {
|
||||||
|
let (logical_x, logical_y) = self.window_info.physical_to_logical(
|
||||||
|
event.event_x() as f64,
|
||||||
|
event.event_y() as f64
|
||||||
|
);
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
self,
|
self,
|
||||||
Event::Mouse(MouseEvent::CursorMoved {
|
Event::Mouse(MouseEvent::CursorMoved {
|
||||||
x: event.event_x() as i32,
|
x: logical_x.round() as i32,
|
||||||
y: event.event_y() as i32,
|
y: logical_y.round() as i32,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue