Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ objc2-app-kit = { version = "0.3.2", default-features = false, features = [
"NSPasteboard",
"NSResponder",
"NSRunningApplication",
"NSScreen",
"NSTrackingArea",
"NSView",
"NSWindow",
Expand Down
19 changes: 18 additions & 1 deletion src/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use raw_window_handle::{
};

use crate::{
Event, EventStatus, MouseCursor, Size, WindowHandler, WindowInfo, WindowOpenOptions,
Event, EventStatus, MouseCursor, Point, Size, WindowHandler, WindowInfo, WindowOpenOptions,
WindowScalePolicy,
};

Expand Down Expand Up @@ -364,6 +364,23 @@ impl<'a> Window<'a> {
}
}

pub fn set_position(&mut self, position: Point) {
// NOTE: macOS uses a coordinate system where (0,0) is at the bottom-left of the screen.
// We need to convert from top-left coordinates to bottom-left coordinates.
if let Some(ns_window) = self.inner.ns_window.get() {
if let Some(screen) = ns_window.screen() {
let screen_frame = screen.frame();
let window_frame = ns_window.frame();

let y_bottom_left =
screen_frame.size.height - position.y.round() - window_frame.size.height;
let origin = NSPoint::new(position.x.round(), y_bottom_left);

ns_window.setFrameOrigin(origin);
}
}
}

pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) {
todo!()
}
Expand Down
84 changes: 65 additions & 19 deletions src/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ use windows_sys::Win32::{
},
WindowsAndMessaging::{
AdjustWindowRectEx, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW,
GetMessageW, GetWindowLongPtrW, LoadCursorW, PostMessageW, SetCursor, SetTimer,
SetWindowLongPtrW, SetWindowPos, TranslateMessage, GWLP_USERDATA, HTCLIENT, MSG,
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOZORDER, WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE,
WM_DPICHANGED, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP,
WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL,
WM_NCDESTROY, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE,
WM_SYSCHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN,
WM_XBUTTONUP, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX,
WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
GetMessageW, GetSystemMetrics, GetWindowLongPtrW, LoadCursorW, PostMessageW, SetCursor,
SetTimer, SetWindowLongPtrW, SetWindowPos, TranslateMessage, GWLP_USERDATA, HTCLIENT,
MSG, SM_CXSCREEN, SM_CYSCREEN, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER,
WHEEL_DELTA, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DPICHANGED, WM_INPUTLANGCHANGE,
WM_KEYDOWN, WM_KEYUP, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCDESTROY, WM_RBUTTONDOWN,
WM_RBUTTONUP, WM_SETCURSOR, WM_SHOWWINDOW, WM_SIZE, WM_SYSCHAR, WM_SYSKEYDOWN,
WM_SYSKEYUP, WM_TIMER, WM_USER, WM_XBUTTONDOWN, WM_XBUTTONUP, WS_CAPTION, WS_CHILD,
WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WS_SIZEBOX,
WS_VISIBLE,
},
},
};
Expand All @@ -45,8 +46,8 @@ const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1;

use crate::win::hook::{self, KeyboardHookHandle};
use crate::{
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, Point, ScrollDelta, Size,
WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
};

use super::cursor::cursor_to_lpcwstr;
Expand Down Expand Up @@ -562,6 +563,21 @@ impl WindowState {
)
};
}
WindowTask::SetPosition(position) => {
let window_info = self.window_info();
let physical_pos = position.to_physical(&window_info);
unsafe {
SetWindowPos(
self.hwnd,
self.hwnd,
physical_pos.x,
physical_pos.y,
0,
0,
SWP_NOZORDER | SWP_NOSIZE,
)
};
}
}
}
}
Expand All @@ -573,6 +589,9 @@ pub(super) enum WindowTask {
/// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied
/// automatically.
Resize(Size),
/// Set the position of the window. The position is in logical pixels. DPI scaling is applied
/// automatically.
SetPosition(Point),
}

pub struct Window<'a> {
Expand Down Expand Up @@ -668,13 +687,24 @@ impl Window<'_> {
AdjustWindowRectEx(&mut rect, flags, FALSE, 0);
}

let (x, y) = if !parented {
let screen_width = GetSystemMetrics(SM_CXSCREEN);
let screen_height = GetSystemMetrics(SM_CYSCREEN);
(
(screen_width - (rect.right - rect.left)) / 2,
(screen_height - (rect.bottom - rect.top)) / 2,
)
} else {
(0, 0)
};

let hwnd = CreateWindowExW(
0,
window_class.as_atom_ptr(),
title.as_ptr(),
flags,
0,
0,
x,
y,
rect.right - rect.left,
rect.bottom - rect.top,
parent as *mut _,
Expand Down Expand Up @@ -771,20 +801,29 @@ impl Window<'_> {
SetTimer(hwnd, WIN_FRAME_TIMER, 15, None);

if let Some(mut new_rect) = new_rect {
// Convert this desired"client rectangle" size to the actual "window rectangle"
// size (Because of course you have to do that).
AdjustWindowRectEx(&mut new_rect, flags, 0, 0);
let (x, y) = if !parented {
let screen_width = GetSystemMetrics(SM_CXSCREEN);
let screen_height = GetSystemMetrics(SM_CYSCREEN);
AdjustWindowRectEx(&mut new_rect, flags, FALSE, 0);
(
(screen_width - (new_rect.right - new_rect.left)) / 2,
(screen_height - (new_rect.bottom - new_rect.top)) / 2,
)
} else {
AdjustWindowRectEx(&mut new_rect, flags, FALSE, 0);
(new_rect.left, new_rect.top)
};

// Windows makes us resize the window manually. This will trigger another `WM_SIZE` event,
// which we can then send the user the new scale factor.
SetWindowPos(
hwnd,
hwnd,
new_rect.left,
new_rect.top,
x,
y,
new_rect.right - new_rect.left,
new_rect.bottom - new_rect.top,
SWP_NOZORDER | SWP_NOMOVE,
SWP_NOZORDER,
);
}

Expand Down Expand Up @@ -816,6 +855,13 @@ impl Window<'_> {
self.state.deferred_tasks.borrow_mut().push_back(task);
}

pub fn set_position(&mut self, position: Point) {
// To avoid reentrant event handler calls we'll defer the actual positioning until after the
// event has been handled
let task = WindowTask::SetPosition(position);
self.state.deferred_tasks.borrow_mut().push_back(task);
}

pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
self.state.cursor_icon.set(mouse_cursor);
unsafe {
Expand Down
8 changes: 7 additions & 1 deletion src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use raw_window_handle::{

use crate::event::{Event, EventStatus};
use crate::window_open_options::WindowOpenOptions;
use crate::{MouseCursor, Size};
use crate::{MouseCursor, Point, Size};

#[cfg(target_os = "macos")]
use crate::macos as platform;
Expand Down Expand Up @@ -98,6 +98,12 @@ impl<'a> Window<'a> {
self.window.resize(size);
}

/// Set the position of the window. The position is always in logical pixels. DPI scaling will
/// automatically be accounted for.
pub fn set_position(&mut self, position: Point) {
self.window.set_position(position);
}

pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
self.window.set_mouse_cursor(cursor);
}
Expand Down
36 changes: 30 additions & 6 deletions src/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use x11rb::wrapper::ConnectionExt as _;

use super::XcbConnection;
use crate::{
Event, MouseCursor, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
Event, MouseCursor, Point, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
WindowScalePolicy,
};

Expand Down Expand Up @@ -195,6 +195,19 @@ impl<'a> Window<'a> {

let window_info = WindowInfo::from_logical_size(options.size, scaling);

let width = (options.size.width * scaling).round() as u32;
let height = (options.size.height * scaling).round() as u32;

let (x, y) = if parent.is_none() {
let screen_width = screen.width_in_pixels;
let screen_height = screen.height_in_pixels;
let x = (screen_width as i32 - width as i32) / 2;
let y = (screen_height as i32 - height as i32) / 2;
(x.max(0) as i16, y.max(0) as i16)
} else {
(0, 0)
};

#[cfg(feature = "opengl")]
let visual_info =
WindowVisualConfig::find_best_visual_config_for_gl(&xcb_connection, options.gl_config)?;
Expand All @@ -207,11 +220,11 @@ impl<'a> Window<'a> {
visual_info.visual_depth,
window_id,
parent_id,
0, // x coordinate of the new window
0, // y coordinate of the new window
window_info.physical_size().width as u16, // window width
window_info.physical_size().height as u16, // window height
0, // window border
x,
y,
width as u16,
height as u16,
0, // window border
WindowClass::INPUT_OUTPUT,
visual_info.visual_id,
&CreateWindowAux::new()
Expand Down Expand Up @@ -344,6 +357,17 @@ impl<'a> Window<'a> {
// and notify the window handler about it
}

pub fn set_position(&mut self, position: Point) {
let window_info = self.inner.window_info;
let physical_pos = position.to_physical(&window_info);

let _ = self.inner.xcb_connection.conn.configure_window(
self.inner.window_id,
&ConfigureWindowAux::new().x(physical_pos.x).y(physical_pos.y),
);
let _ = self.inner.xcb_connection.conn.flush();
}

#[cfg(feature = "opengl")]
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
self.inner.gl_context.as_ref()
Expand Down