Merge pull request #136 from robbert-vdh/feature/resize
Support resizing windows after creation
This commit is contained in:
commit
7001c2521f
|
@ -11,6 +11,7 @@ use cocoa::appkit::{
|
||||||
NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView,
|
NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView,
|
||||||
};
|
};
|
||||||
use cocoa::base::{id, nil, YES};
|
use cocoa::base::{id, nil, YES};
|
||||||
|
use cocoa::foundation::NSSize;
|
||||||
|
|
||||||
use core_foundation::base::TCFType;
|
use core_foundation::base::TCFType;
|
||||||
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
|
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
|
||||||
|
@ -134,6 +135,14 @@ impl GlContext {
|
||||||
let () = msg_send![self.view, setNeedsDisplay: YES];
|
let () = msg_send![self.view, setNeedsDisplay: YES];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On macOS the `NSOpenGLView` needs to be resized separtely from our main view.
|
||||||
|
pub(crate) fn resize(&self, size: NSSize) {
|
||||||
|
unsafe { NSView::setFrameSize(self.view, size) };
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![self.view, setNeedsDisplay: YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for GlContext {
|
impl Drop for GlContext {
|
||||||
|
|
|
@ -106,4 +106,10 @@ impl GlContext {
|
||||||
pub fn swap_buffers(&self) {
|
pub fn swap_buffers(&self) {
|
||||||
self.context.swap_buffers();
|
self.context.swap_buffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// On macOS the `NSOpenGLView` needs to be resized separtely from our main view.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub(crate) fn resize(&self, size: cocoa::foundation::NSSize) {
|
||||||
|
self.context.resize(size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,18 +234,23 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel, _: id) {
|
||||||
let ns_window: *mut Object = msg_send![this, window];
|
let ns_window: *mut Object = msg_send![this, window];
|
||||||
|
|
||||||
let scale_factor: f64 =
|
let scale_factor: f64 =
|
||||||
if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) as f64 };
|
if ns_window.is_null() { 1.0 } else { NSWindow::backingScaleFactor(ns_window) };
|
||||||
|
|
||||||
let state: &mut WindowState = WindowState::from_field(this);
|
let state: &mut WindowState = WindowState::from_field(this);
|
||||||
|
|
||||||
let bounds: NSRect = msg_send![this, bounds];
|
let bounds: NSRect = msg_send![this, bounds];
|
||||||
|
|
||||||
let window_info = WindowInfo::from_logical_size(
|
let new_window_info = WindowInfo::from_logical_size(
|
||||||
Size::new(bounds.size.width, bounds.size.height),
|
Size::new(bounds.size.width, bounds.size.height),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
);
|
);
|
||||||
|
|
||||||
state.trigger_event(Event::Window(WindowEvent::Resized(window_info)));
|
// Only send the event when the window's size has actually changed to be in line with the
|
||||||
|
// other platform implementations
|
||||||
|
if new_window_info.physical_size() != state.window_info.physical_size() {
|
||||||
|
state.window_info = new_window_info;
|
||||||
|
state.trigger_event(Event::Window(WindowEvent::Resized(new_window_info)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,6 +369,6 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) {
|
||||||
|
|
||||||
state.trigger_event(Event::Mouse(MouseEvent::WheelScrolled {
|
state.trigger_event(Event::Mouse(MouseEvent::WheelScrolled {
|
||||||
delta,
|
delta,
|
||||||
modifiers: make_modifiers(modifiers)
|
modifiers: make_modifiers(modifiers),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use cocoa::appkit::{
|
use cocoa::appkit::{
|
||||||
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSWindow,
|
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSView,
|
||||||
NSWindowStyleMask,
|
NSWindow, NSWindowStyleMask,
|
||||||
};
|
};
|
||||||
use cocoa::base::{id, nil, NO};
|
use cocoa::base::{id, nil, NO, YES};
|
||||||
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
|
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
|
||||||
use core_foundation::runloop::{
|
use core_foundation::runloop::{
|
||||||
CFRunLoop, CFRunLoopTimer, CFRunLoopTimerContext, __CFRunLoopTimer, kCFRunLoopDefaultMode,
|
CFRunLoop, CFRunLoopTimer, CFRunLoopTimerContext, __CFRunLoopTimer, kCFRunLoopDefaultMode,
|
||||||
|
@ -20,7 +20,7 @@ use objc::{msg_send, runtime::Object, sel, sel_impl};
|
||||||
use raw_window_handle::{AppKitHandle, HasRawWindowHandle, RawWindowHandle};
|
use raw_window_handle::{AppKitHandle, HasRawWindowHandle, RawWindowHandle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, EventStatus, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
Event, EventStatus, Size, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
||||||
WindowScalePolicy,
|
WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -124,6 +124,13 @@ impl Window {
|
||||||
{
|
{
|
||||||
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
||||||
|
|
||||||
|
let scaling = match options.scale {
|
||||||
|
WindowScalePolicy::ScaleFactor(scale) => scale,
|
||||||
|
WindowScalePolicy::SystemScaleFactor => 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_info = WindowInfo::from_logical_size(options.size, scaling);
|
||||||
|
|
||||||
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() {
|
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() {
|
||||||
handle
|
handle
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,7 +151,7 @@ impl Window {
|
||||||
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
|
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let window_handle = Self::init(true, window, build);
|
let window_handle = Self::init(true, window, window_info, build);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view];
|
let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view];
|
||||||
|
@ -164,6 +171,13 @@ impl Window {
|
||||||
{
|
{
|
||||||
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
let pool = unsafe { NSAutoreleasePool::new(nil) };
|
||||||
|
|
||||||
|
let scaling = match options.scale {
|
||||||
|
WindowScalePolicy::ScaleFactor(scale) => scale,
|
||||||
|
WindowScalePolicy::SystemScaleFactor => 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let window_info = WindowInfo::from_logical_size(options.size, scaling);
|
||||||
|
|
||||||
let ns_view = unsafe { create_view(&options) };
|
let ns_view = unsafe { create_view(&options) };
|
||||||
|
|
||||||
let window = Window {
|
let window = Window {
|
||||||
|
@ -178,7 +192,7 @@ impl Window {
|
||||||
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
|
.map(|gl_config| Self::create_gl_context(None, ns_view, gl_config)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let window_handle = Self::init(true, window, build);
|
let window_handle = Self::init(true, window, window_info, build);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let () = msg_send![pool, drain];
|
let () = msg_send![pool, drain];
|
||||||
|
@ -215,10 +229,7 @@ impl Window {
|
||||||
|
|
||||||
let rect = NSRect::new(
|
let rect = NSRect::new(
|
||||||
NSPoint::new(0.0, 0.0),
|
NSPoint::new(0.0, 0.0),
|
||||||
NSSize::new(
|
NSSize::new(window_info.logical_size().width, window_info.logical_size().height),
|
||||||
window_info.logical_size().width as f64,
|
|
||||||
window_info.logical_size().height as f64,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let ns_window = unsafe {
|
let ns_window = unsafe {
|
||||||
|
@ -254,7 +265,7 @@ impl Window {
|
||||||
.map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)),
|
.map(|gl_config| Self::create_gl_context(Some(ns_window), ns_view, gl_config)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = Self::init(false, window, build);
|
let _ = Self::init(false, window, window_info, build);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ns_window.setContentView_(ns_view);
|
ns_window.setContentView_(ns_view);
|
||||||
|
@ -266,7 +277,9 @@ impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init<H, B>(parented: bool, mut window: Window, build: B) -> WindowHandle
|
fn init<H, B>(
|
||||||
|
parented: bool, mut window: Window, window_info: WindowInfo, build: B,
|
||||||
|
) -> WindowHandle
|
||||||
where
|
where
|
||||||
H: WindowHandler + 'static,
|
H: WindowHandler + 'static,
|
||||||
B: FnOnce(&mut crate::Window) -> H,
|
B: FnOnce(&mut crate::Window) -> H,
|
||||||
|
@ -285,6 +298,7 @@ impl Window {
|
||||||
keyboard_state: KeyboardState::new(),
|
keyboard_state: KeyboardState::new(),
|
||||||
frame_timer: None,
|
frame_timer: None,
|
||||||
retain_count_after_build,
|
retain_count_after_build,
|
||||||
|
window_info,
|
||||||
_parent_handle: parent_handle,
|
_parent_handle: parent_handle,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -302,6 +316,28 @@ impl Window {
|
||||||
self.close_requested = true;
|
self.close_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, size: Size) {
|
||||||
|
// NOTE: macOS gives you a personal rave if you pass in fractional pixels here. Even though
|
||||||
|
// the size is in fractional pixels.
|
||||||
|
let size = NSSize::new(size.width.round(), size.height.round());
|
||||||
|
|
||||||
|
unsafe { NSView::setFrameSize(self.ns_view, size) };
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![self.ns_view, setNeedsDisplay: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
// When using OpenGL the `NSOpenGLView` needs to be resized separately? Why? Because macOS.
|
||||||
|
#[cfg(feature = "opengl")]
|
||||||
|
if let Some(gl_context) = &self.gl_context {
|
||||||
|
gl_context.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a standalone window then we'll also need to resize the window itself
|
||||||
|
if let Some(ns_window) = self.ns_window {
|
||||||
|
unsafe { NSWindow::setContentSize_(ns_window, size) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
pub fn gl_context(&self) -> Option<&GlContext> {
|
pub fn gl_context(&self) -> Option<&GlContext> {
|
||||||
self.gl_context.as_ref()
|
self.gl_context.as_ref()
|
||||||
|
@ -325,6 +361,8 @@ pub(super) struct WindowState {
|
||||||
frame_timer: Option<CFRunLoopTimer>,
|
frame_timer: Option<CFRunLoopTimer>,
|
||||||
_parent_handle: Option<ParentHandle>,
|
_parent_handle: Option<ParentHandle>,
|
||||||
pub retain_count_after_build: usize,
|
pub retain_count_after_build: usize,
|
||||||
|
/// The last known window info for this window.
|
||||||
|
pub window_info: WindowInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
|
|
|
@ -16,21 +16,21 @@ use winapi::um::winuser::{
|
||||||
XBUTTON1, XBUTTON2,
|
XBUTTON1, XBUTTON2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::collections::VecDeque;
|
||||||
use std::ffi::{c_void, OsStr};
|
use std::ffi::{c_void, OsStr};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
|
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, Win32Handle};
|
||||||
|
|
||||||
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
const BV_WINDOW_MUST_CLOSE: UINT = WM_USER + 1;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent, WindowHandler,
|
Event, MouseButton, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
|
||||||
WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::keyboard::KeyboardState;
|
use super::keyboard::KeyboardState;
|
||||||
|
@ -61,7 +61,7 @@ const WIN_FRAME_TIMER: usize = 4242;
|
||||||
|
|
||||||
pub struct WindowHandle {
|
pub struct WindowHandle {
|
||||||
hwnd: Option<HWND>,
|
hwnd: Option<HWND>,
|
||||||
is_open: Arc<AtomicBool>,
|
is_open: Rc<Cell<bool>>,
|
||||||
|
|
||||||
// Ensure handle is !Send
|
// Ensure handle is !Send
|
||||||
_phantom: PhantomData<*mut ()>,
|
_phantom: PhantomData<*mut ()>,
|
||||||
|
@ -77,7 +77,7 @@ impl WindowHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.is_open.load(Ordering::Relaxed)
|
self.is_open.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,16 +95,16 @@ unsafe impl HasRawWindowHandle for WindowHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ParentHandle {
|
struct ParentHandle {
|
||||||
is_open: Arc<AtomicBool>,
|
is_open: Rc<Cell<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParentHandle {
|
impl ParentHandle {
|
||||||
pub fn new(hwnd: HWND) -> (Self, WindowHandle) {
|
pub fn new(hwnd: HWND) -> (Self, WindowHandle) {
|
||||||
let is_open = Arc::new(AtomicBool::new(true));
|
let is_open = Rc::new(Cell::new(true));
|
||||||
|
|
||||||
let handle = WindowHandle {
|
let handle = WindowHandle {
|
||||||
hwnd: Some(hwnd),
|
hwnd: Some(hwnd),
|
||||||
is_open: Arc::clone(&is_open),
|
is_open: Rc::clone(&is_open),
|
||||||
_phantom: PhantomData::default(),
|
_phantom: PhantomData::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ impl ParentHandle {
|
||||||
|
|
||||||
impl Drop for ParentHandle {
|
impl Drop for ParentHandle {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.is_open.store(false, Ordering::Relaxed);
|
self.is_open.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,31 +126,71 @@ unsafe extern "system" fn wnd_proc(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut RefCell<WindowState>;
|
let window_state_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut WindowState;
|
||||||
if !window_state_ptr.is_null() {
|
if !window_state_ptr.is_null() {
|
||||||
|
let result = wnd_proc_inner(hwnd, msg, wparam, lparam, &*window_state_ptr);
|
||||||
|
|
||||||
|
// If any of the above event handlers caused tasks to be pushed to the deferred tasks list,
|
||||||
|
// then we'll try to handle them now
|
||||||
|
loop {
|
||||||
|
// NOTE: This is written like this instead of using a `while let` loop to avoid exending
|
||||||
|
// the borrow of `window_state.deferred_tasks` into the call of
|
||||||
|
// `window_state.handle_deferred_task()` since that may also generate additional
|
||||||
|
// messages.
|
||||||
|
let task = match (*window_state_ptr).deferred_tasks.borrow_mut().pop_front() {
|
||||||
|
Some(task) => task,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
(*window_state_ptr).handle_deferred_task(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This is not handled in `wnd_proc_inner` because of the deferred task loop above
|
||||||
|
if msg == WM_NCDESTROY {
|
||||||
|
unregister_wnd_class((*window_state_ptr).window_class);
|
||||||
|
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
|
||||||
|
drop(Box::from_raw(window_state_ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual custom window proc has been moved to another function so we can always handle
|
||||||
|
// the deferred tasks regardless of whether the custom window proc returns early or not
|
||||||
|
if let Some(result) = result {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DefWindowProcW(hwnd, msg, wparam, lparam)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our custom `wnd_proc` handler. If the result contains a value, then this is returned after
|
||||||
|
/// handling any deferred tasks. otherwise the default window procedure is invoked.
|
||||||
|
unsafe fn wnd_proc_inner(
|
||||||
|
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM, window_state: &WindowState,
|
||||||
|
) -> Option<LRESULT> {
|
||||||
match msg {
|
match msg {
|
||||||
WM_MOUSEMOVE => {
|
WM_MOUSEMOVE => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let x = (lparam & 0xFFFF) as i16 as i32;
|
let x = (lparam & 0xFFFF) as i16 as i32;
|
||||||
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;
|
||||||
|
|
||||||
let physical_pos = PhyPoint { x, y };
|
let physical_pos = PhyPoint { x, y };
|
||||||
let logical_pos = physical_pos.to_logical(&window_state.window_info);
|
let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow());
|
||||||
let event = Event::Mouse(MouseEvent::CursorMoved {
|
let event = Event::Mouse(MouseEvent::CursorMoved {
|
||||||
position: logical_pos,
|
position: logical_pos,
|
||||||
modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam),
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
});
|
});
|
||||||
|
|
||||||
window_state.handler.on_event(&mut window, event);
|
window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
|
||||||
|
|
||||||
return 0;
|
Some(0)
|
||||||
}
|
}
|
||||||
WM_MOUSEWHEEL | WM_MOUSEHWHEEL => {
|
WM_MOUSEWHEEL | WM_MOUSEHWHEEL => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let value = (wparam >> 16) as i16;
|
let value = (wparam >> 16) as i16;
|
||||||
|
@ -163,20 +203,22 @@ unsafe extern "system" fn wnd_proc(
|
||||||
} else {
|
} else {
|
||||||
ScrollDelta::Lines { x: value, y: 0.0 }
|
ScrollDelta::Lines { x: value, y: 0.0 }
|
||||||
},
|
},
|
||||||
modifiers: window_state.keyboard_state.get_modifiers_from_mouse_wparam(wparam),
|
modifiers: window_state
|
||||||
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
});
|
});
|
||||||
|
|
||||||
window_state.handler.on_event(&mut window, event);
|
window_state.handler.borrow_mut().as_mut().unwrap().on_event(&mut window, event);
|
||||||
|
|
||||||
return 0;
|
Some(0)
|
||||||
}
|
}
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
WM_LBUTTONDOWN | WM_LBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_RBUTTONDOWN
|
||||||
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
|
| WM_RBUTTONUP | WM_XBUTTONDOWN | WM_XBUTTONUP => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let mut mouse_button_counter = window_state.mouse_button_counter;
|
let mut mouse_button_counter = window_state.mouse_button_counter.get();
|
||||||
|
|
||||||
let button = match msg {
|
let button = match msg {
|
||||||
WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left),
|
WM_LBUTTONDOWN | WM_LBUTTONUP => Some(MouseButton::Left),
|
||||||
|
@ -200,6 +242,7 @@ unsafe extern "system" fn wnd_proc(
|
||||||
button,
|
button,
|
||||||
modifiers: window_state
|
modifiers: window_state
|
||||||
.keyboard_state
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
.get_modifiers_from_mouse_wparam(wparam),
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +257,7 @@ unsafe extern "system" fn wnd_proc(
|
||||||
button,
|
button,
|
||||||
modifiers: window_state
|
modifiers: window_state
|
||||||
.keyboard_state
|
.keyboard_state
|
||||||
|
.borrow()
|
||||||
.get_modifiers_from_mouse_wparam(wparam),
|
.get_modifiers_from_mouse_wparam(wparam),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,94 +266,118 @@ unsafe extern "system" fn wnd_proc(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window_state.mouse_button_counter = mouse_button_counter;
|
window_state.mouse_button_counter.set(mouse_button_counter);
|
||||||
|
|
||||||
window_state.handler.on_event(&mut window, Event::Mouse(event));
|
window_state
|
||||||
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.on_event(&mut window, Event::Mouse(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
WM_TIMER => {
|
WM_TIMER => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
if wparam == WIN_FRAME_TIMER {
|
if wparam == WIN_FRAME_TIMER {
|
||||||
window_state.handler.on_frame(&mut window);
|
window_state.handler.borrow_mut().as_mut().unwrap().on_frame(&mut window);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
Some(0)
|
||||||
}
|
}
|
||||||
WM_CLOSE => {
|
WM_CLOSE => {
|
||||||
// Make sure to release the borrow before the DefWindowProc call
|
// Make sure to release the borrow before the DefWindowProc call
|
||||||
{
|
{
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
window_state
|
window_state
|
||||||
.handler
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
|
.on_event(&mut window, Event::Window(WindowEvent::WillClose));
|
||||||
}
|
}
|
||||||
|
|
||||||
// DestroyWindow(hwnd);
|
// DestroyWindow(hwnd);
|
||||||
// return 0;
|
// Some(0)
|
||||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
Some(DefWindowProcW(hwnd, msg, wparam, lparam))
|
||||||
}
|
}
|
||||||
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
|
WM_CHAR | WM_SYSCHAR | WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP
|
||||||
| WM_INPUTLANGCHANGE => {
|
| WM_INPUTLANGCHANGE => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let opt_event =
|
let opt_event =
|
||||||
window_state.keyboard_state.process_message(hwnd, msg, wparam, lparam);
|
window_state.keyboard_state.borrow_mut().process_message(hwnd, msg, wparam, lparam);
|
||||||
|
|
||||||
if let Some(event) = opt_event {
|
if let Some(event) = opt_event {
|
||||||
window_state.handler.on_event(&mut window, Event::Keyboard(event));
|
window_state
|
||||||
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.on_event(&mut window, Event::Keyboard(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != WM_SYSKEYDOWN {
|
if msg != WM_SYSKEYDOWN {
|
||||||
return 0;
|
Some(0)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WM_SIZE => {
|
WM_SIZE => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
let mut window = window_state.create_window();
|
||||||
let mut window = window_state.create_window(hwnd);
|
|
||||||
let mut window = crate::Window::new(&mut window);
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
let width = (lparam & 0xFFFF) as u16 as u32;
|
let width = (lparam & 0xFFFF) as u16 as u32;
|
||||||
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
|
let height = ((lparam >> 16) & 0xFFFF) as u16 as u32;
|
||||||
|
|
||||||
window_state.window_info = WindowInfo::from_physical_size(
|
let new_window_info = {
|
||||||
PhySize { width, height },
|
let mut window_info = window_state.window_info.borrow_mut();
|
||||||
window_state.window_info.scale(),
|
let new_window_info =
|
||||||
);
|
WindowInfo::from_physical_size(PhySize { width, height }, window_info.scale());
|
||||||
|
|
||||||
let window_info = window_state.window_info;
|
// Only send the event if anything changed
|
||||||
|
if window_info.physical_size() == new_window_info.physical_size() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
*window_info = new_window_info;
|
||||||
|
|
||||||
|
new_window_info
|
||||||
|
};
|
||||||
|
|
||||||
window_state
|
window_state
|
||||||
.handler
|
.handler
|
||||||
.on_event(&mut window, Event::Window(WindowEvent::Resized(window_info)));
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.on_event(&mut window, Event::Window(WindowEvent::Resized(new_window_info)));
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
WM_DPICHANGED => {
|
WM_DPICHANGED => {
|
||||||
let mut window_state = (*window_state_ptr).borrow_mut();
|
|
||||||
|
|
||||||
// To avoid weirdness with the realtime borrow checker.
|
// To avoid weirdness with the realtime borrow checker.
|
||||||
let new_rect = {
|
let new_rect = {
|
||||||
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
|
if let WindowScalePolicy::SystemScaleFactor = window_state.scale_policy {
|
||||||
let dpi = (wparam & 0xFFFF) as u16 as u32;
|
let dpi = (wparam & 0xFFFF) as u16 as u32;
|
||||||
let scale_factor = dpi as f64 / 96.0;
|
let scale_factor = dpi as f64 / 96.0;
|
||||||
|
|
||||||
window_state.window_info = WindowInfo::from_logical_size(
|
let mut window_info = window_state.window_info.borrow_mut();
|
||||||
window_state.window_info.logical_size(),
|
*window_info =
|
||||||
scale_factor,
|
WindowInfo::from_logical_size(window_info.logical_size(), scale_factor);
|
||||||
);
|
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
RECT {
|
RECT {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
// todo: check if usize fits into i32
|
// todo: check if usize fits into i32
|
||||||
right: window_state.window_info.physical_size().width as i32,
|
right: window_info.physical_size().width as i32,
|
||||||
bottom: window_state.window_info.physical_size().height as i32,
|
bottom: window_info.physical_size().height as i32,
|
||||||
},
|
},
|
||||||
window_state.dw_style,
|
window_state.dw_style,
|
||||||
))
|
))
|
||||||
|
@ -327,29 +395,24 @@ unsafe extern "system" fn wnd_proc(
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
hwnd,
|
hwnd,
|
||||||
hwnd,
|
hwnd,
|
||||||
new_rect.left as i32,
|
new_rect.left,
|
||||||
new_rect.top as i32,
|
new_rect.top,
|
||||||
new_rect.right - new_rect.left,
|
new_rect.right - new_rect.left,
|
||||||
new_rect.bottom - new_rect.top,
|
new_rect.bottom - new_rect.top,
|
||||||
SWP_NOZORDER | SWP_NOMOVE,
|
SWP_NOZORDER | SWP_NOMOVE,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
WM_NCDESTROY => {
|
|
||||||
let window_state = Box::from_raw(window_state_ptr);
|
|
||||||
unregister_wnd_class(window_state.borrow().window_class);
|
|
||||||
SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if msg == BV_WINDOW_MUST_CLOSE {
|
|
||||||
DestroyWindow(hwnd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefWindowProcW(hwnd, msg, wparam, lparam)
|
None
|
||||||
|
}
|
||||||
|
// NOTE: `WM_NCDESTROY` is handled in the outer function because this deallocates the window
|
||||||
|
// state
|
||||||
|
BV_WINDOW_MUST_CLOSE => {
|
||||||
|
DestroyWindow(hwnd);
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn register_wnd_class() -> ATOM {
|
unsafe fn register_wnd_class() -> ATOM {
|
||||||
|
@ -378,40 +441,93 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
|
||||||
UnregisterClassW(wnd_class as _, null_mut());
|
UnregisterClassW(wnd_class as _, null_mut());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All data associated with the window. This uses internal mutability so the outer struct doesn't
|
||||||
|
/// need to be mutably borrowed. Mutably borrowing the entire `WindowState` can be problematic
|
||||||
|
/// because of the Windows message loops' reentrant nature. Care still needs to be taken to prevent
|
||||||
|
/// `handler` from indirectly triggering other events that would also need to be handled using
|
||||||
|
/// `handler`.
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
|
/// The HWND belonging to this window. The window's actual state is stored in the `WindowState`
|
||||||
|
/// struct associated with this HWND through `unsafe { GetWindowLongPtrW(self.hwnd,
|
||||||
|
/// GWLP_USERDATA) } as *const WindowState`.
|
||||||
|
pub hwnd: HWND,
|
||||||
window_class: ATOM,
|
window_class: ATOM,
|
||||||
window_info: WindowInfo,
|
window_info: RefCell<WindowInfo>,
|
||||||
_parent_handle: Option<ParentHandle>,
|
_parent_handle: Option<ParentHandle>,
|
||||||
keyboard_state: KeyboardState,
|
keyboard_state: RefCell<KeyboardState>,
|
||||||
mouse_button_counter: usize,
|
mouse_button_counter: Cell<usize>,
|
||||||
handler: Box<dyn WindowHandler>,
|
// Initialized late so the `Window` can hold a reference to this `WindowState`
|
||||||
|
handler: RefCell<Option<Box<dyn WindowHandler>>>,
|
||||||
scale_policy: WindowScalePolicy,
|
scale_policy: WindowScalePolicy,
|
||||||
dw_style: u32,
|
dw_style: u32,
|
||||||
|
|
||||||
|
/// Tasks that should be executed at the end of `wnd_proc`. This is needed to avoid mutably
|
||||||
|
/// borrowing the fields from `WindowState` more than once. For instance, when the window
|
||||||
|
/// handler requests a resize in response to a keyboard event, the window state will already be
|
||||||
|
/// borrowed in `wnd_proc`. So the `resize()` function below cannot also mutably borrow that
|
||||||
|
/// window state at the same time.
|
||||||
|
pub deferred_tasks: RefCell<VecDeque<WindowTask>>,
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
gl_context: Arc<Option<GlContext>>,
|
pub gl_context: Option<GlContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
#[cfg(not(feature = "opengl"))]
|
fn create_window(&self) -> Window {
|
||||||
fn create_window(&self, hwnd: HWND) -> Window {
|
Window { state: self }
|
||||||
Window { hwnd }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
/// Handle a deferred task as described in [`Self::deferred_tasks
|
||||||
fn create_window(&self, hwnd: HWND) -> Window {
|
pub(self) fn handle_deferred_task(&self, task: WindowTask) {
|
||||||
Window { hwnd, gl_context: self.gl_context.clone() }
|
match task {
|
||||||
|
WindowTask::Resize(size) => {
|
||||||
|
let window_info = {
|
||||||
|
let mut window_info = self.window_info.borrow_mut();
|
||||||
|
let scaling = window_info.scale();
|
||||||
|
*window_info = WindowInfo::from_logical_size(size, scaling);
|
||||||
|
|
||||||
|
*window_info
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the window is a standalone window then the size needs to include the window
|
||||||
|
// decorations
|
||||||
|
let mut rect = RECT {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
right: window_info.physical_size().width as i32,
|
||||||
|
bottom: window_info.physical_size().height as i32,
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
AdjustWindowRectEx(&mut rect, self.dw_style, 0, 0);
|
||||||
|
SetWindowPos(
|
||||||
|
self.hwnd,
|
||||||
|
self.hwnd,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top,
|
||||||
|
SWP_NOZORDER | SWP_NOMOVE,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window {
|
/// Tasks that must be deferred until the end of [`wnd_proc()`] to avoid reentrant `WindowState`
|
||||||
hwnd: HWND,
|
/// borrows. See the docstring on [`WindowState::deferred_tasks`] for more information.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
#[cfg(feature = "opengl")]
|
enum WindowTask {
|
||||||
gl_context: Arc<Option<GlContext>>,
|
/// Resize the window to the given size. The size is in logical pixels. DPI scaling is applied
|
||||||
|
/// automatically.
|
||||||
|
Resize(Size),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
pub struct Window<'a> {
|
||||||
|
state: &'a WindowState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window<'_> {
|
||||||
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
|
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B) -> WindowHandle
|
||||||
where
|
where
|
||||||
P: HasRawWindowHandle,
|
P: HasRawWindowHandle,
|
||||||
|
@ -527,38 +643,43 @@ impl Window {
|
||||||
// todo: manage error ^
|
// todo: manage error ^
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
let gl_context: Arc<Option<GlContext>> = Arc::new(options.gl_config.map(|gl_config| {
|
let gl_context: Option<GlContext> = options.gl_config.map(|gl_config| {
|
||||||
let mut handle = Win32Handle::empty();
|
let mut handle = Win32Handle::empty();
|
||||||
handle.hwnd = hwnd as *mut c_void;
|
handle.hwnd = hwnd as *mut c_void;
|
||||||
let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) };
|
let handle = RawWindowHandleWrapper { handle: RawWindowHandle::Win32(handle) };
|
||||||
|
|
||||||
GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
|
GlContext::create(&handle, gl_config).expect("Could not create OpenGL context")
|
||||||
}));
|
});
|
||||||
|
|
||||||
#[cfg(not(feature = "opengl"))]
|
|
||||||
let handler = Box::new(build(&mut crate::Window::new(&mut Window { hwnd })));
|
|
||||||
#[cfg(feature = "opengl")]
|
|
||||||
let handler = Box::new(build(&mut crate::Window::new(&mut Window {
|
|
||||||
hwnd,
|
|
||||||
gl_context: gl_context.clone(),
|
|
||||||
})));
|
|
||||||
|
|
||||||
let (parent_handle, window_handle) = ParentHandle::new(hwnd);
|
let (parent_handle, window_handle) = ParentHandle::new(hwnd);
|
||||||
let parent_handle = if parented { Some(parent_handle) } else { None };
|
let parent_handle = if parented { Some(parent_handle) } else { None };
|
||||||
|
|
||||||
let mut window_state = Box::new(RefCell::new(WindowState {
|
let window_state = Box::new(WindowState {
|
||||||
|
hwnd,
|
||||||
window_class,
|
window_class,
|
||||||
window_info,
|
window_info: RefCell::new(window_info),
|
||||||
_parent_handle: parent_handle,
|
_parent_handle: parent_handle,
|
||||||
keyboard_state: KeyboardState::new(),
|
keyboard_state: RefCell::new(KeyboardState::new()),
|
||||||
mouse_button_counter: 0,
|
mouse_button_counter: Cell::new(0),
|
||||||
handler,
|
// The Window refers to this `WindowState`, so this `handler` needs to be
|
||||||
|
// initialized later
|
||||||
|
handler: RefCell::new(None),
|
||||||
scale_policy: options.scale,
|
scale_policy: options.scale,
|
||||||
dw_style: flags,
|
dw_style: flags,
|
||||||
|
|
||||||
|
deferred_tasks: RefCell::new(VecDeque::with_capacity(4)),
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
gl_context,
|
gl_context,
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
let handler = {
|
||||||
|
let mut window = window_state.create_window();
|
||||||
|
let mut window = crate::Window::new(&mut window);
|
||||||
|
|
||||||
|
build(&mut window)
|
||||||
|
};
|
||||||
|
*window_state.handler.borrow_mut() = Some(Box::new(handler));
|
||||||
|
|
||||||
// Only works on Windows 10 unfortunately.
|
// Only works on Windows 10 unfortunately.
|
||||||
SetProcessDpiAwarenessContext(
|
SetProcessDpiAwarenessContext(
|
||||||
|
@ -571,19 +692,17 @@ impl Window {
|
||||||
let dpi = GetDpiForWindow(hwnd);
|
let dpi = GetDpiForWindow(hwnd);
|
||||||
let scale_factor = dpi as f64 / 96.0;
|
let scale_factor = dpi as f64 / 96.0;
|
||||||
|
|
||||||
let mut window_state = window_state.get_mut();
|
let mut window_info = window_state.window_info.borrow_mut();
|
||||||
if window_state.window_info.scale() != scale_factor {
|
if window_info.scale() != scale_factor {
|
||||||
window_state.window_info = WindowInfo::from_logical_size(
|
*window_info =
|
||||||
window_state.window_info.logical_size(),
|
WindowInfo::from_logical_size(window_info.logical_size(), scale_factor);
|
||||||
scale_factor,
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(RECT {
|
Some(RECT {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
// todo: check if usize fits into i32
|
// todo: check if usize fits into i32
|
||||||
right: window_state.window_info.physical_size().width as i32,
|
right: window_info.physical_size().width as i32,
|
||||||
bottom: window_state.window_info.physical_size().height as i32,
|
bottom: window_info.physical_size().height as i32,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -605,8 +724,8 @@ impl Window {
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
hwnd,
|
hwnd,
|
||||||
hwnd,
|
hwnd,
|
||||||
new_rect.left as i32,
|
new_rect.left,
|
||||||
new_rect.top as i32,
|
new_rect.top,
|
||||||
new_rect.right - new_rect.left,
|
new_rect.right - new_rect.left,
|
||||||
new_rect.bottom - new_rect.top,
|
new_rect.bottom - new_rect.top,
|
||||||
SWP_NOZORDER | SWP_NOMOVE,
|
SWP_NOZORDER | SWP_NOMOVE,
|
||||||
|
@ -619,20 +738,27 @@ impl Window {
|
||||||
|
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
PostMessageW(self.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0);
|
PostMessageW(self.state.hwnd, BV_WINDOW_MUST_CLOSE, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, size: Size) {
|
||||||
|
// To avoid reentrant event handler calls we'll defer the actual resizing until after the
|
||||||
|
// event has been handled
|
||||||
|
let task = WindowTask::Resize(size);
|
||||||
|
self.state.deferred_tasks.borrow_mut().push_back(task);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
pub fn gl_context(&self) -> Option<&GlContext> {
|
pub fn gl_context(&self) -> Option<&GlContext> {
|
||||||
self.gl_context.as_ref().as_ref()
|
self.state.gl_context.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl HasRawWindowHandle for Window {
|
unsafe impl HasRawWindowHandle for Window<'_> {
|
||||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
let mut handle = Win32Handle::empty();
|
let mut handle = Win32Handle::empty();
|
||||||
handle.hwnd = self.hwnd as *mut c_void;
|
handle.hwnd = self.state.hwnd as *mut c_void;
|
||||||
|
|
||||||
RawWindowHandle::Win32(handle)
|
RawWindowHandle::Win32(handle)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
|
||||||
|
|
||||||
use crate::event::{Event, EventStatus};
|
use crate::event::{Event, EventStatus};
|
||||||
use crate::window_open_options::WindowOpenOptions;
|
use crate::window_open_options::WindowOpenOptions;
|
||||||
|
use crate::Size;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::macos as platform;
|
use crate::macos as platform;
|
||||||
|
@ -53,12 +54,22 @@ pub trait WindowHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window<'a> {
|
pub struct Window<'a> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
window: &'a mut platform::Window<'a>,
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
window: &'a mut platform::Window,
|
window: &'a mut platform::Window,
|
||||||
|
|
||||||
// so that Window is !Send on all platforms
|
// so that Window is !Send on all platforms
|
||||||
phantom: PhantomData<*mut ()>,
|
phantom: PhantomData<*mut ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Window<'a> {
|
impl<'a> Window<'a> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub(crate) fn new(window: &'a mut platform::Window<'a>) -> Window<'a> {
|
||||||
|
Window { window, phantom: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
pub(crate) fn new(window: &mut platform::Window) -> Window {
|
pub(crate) fn new(window: &mut platform::Window) -> Window {
|
||||||
Window { window, phantom: PhantomData }
|
Window { window, phantom: PhantomData }
|
||||||
}
|
}
|
||||||
|
@ -98,6 +109,12 @@ impl<'a> Window<'a> {
|
||||||
self.window.close();
|
self.window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resize the window to the given size. The size is always in logical pixels. DPI scaling will
|
||||||
|
/// automatically be accounted for.
|
||||||
|
pub fn resize(&mut self, size: Size) {
|
||||||
|
self.window.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
/// If provided, then an OpenGL context will be created for this window. You'll be able to
|
/// If provided, then an OpenGL context will be created for this window. You'll be able to
|
||||||
/// access this context through [crate::Window::gl_context].
|
/// access this context through [crate::Window::gl_context].
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
|
|
|
@ -12,7 +12,7 @@ use xcb::StructPtr;
|
||||||
|
|
||||||
use super::XcbConnection;
|
use super::XcbConnection;
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, WindowEvent,
|
Event, MouseButton, MouseCursor, MouseEvent, PhyPoint, PhySize, ScrollDelta, Size, WindowEvent,
|
||||||
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -389,6 +389,24 @@ impl Window {
|
||||||
self.close_requested = true;
|
self.close_requested = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, size: Size) {
|
||||||
|
let scaling = self.window_info.scale();
|
||||||
|
let new_window_info = WindowInfo::from_logical_size(size, scaling);
|
||||||
|
|
||||||
|
xcb::configure_window(
|
||||||
|
&self.xcb_connection.conn,
|
||||||
|
self.window_id,
|
||||||
|
&[
|
||||||
|
(xcb::CONFIG_WINDOW_WIDTH as u16, new_window_info.physical_size().width),
|
||||||
|
(xcb::CONFIG_WINDOW_HEIGHT as u16, new_window_info.physical_size().height),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
self.xcb_connection.conn.flush();
|
||||||
|
|
||||||
|
// This will trigger a `ConfigureNotify` event which will in turn change `self.window_info`
|
||||||
|
// and notify the window handler about it
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
|
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
|
||||||
self.gl_context.as_ref()
|
self.gl_context.as_ref()
|
||||||
|
|
Loading…
Reference in a new issue