Detailed changes
@@ -25,10 +25,11 @@ mod test;
mod windows;
use crate::{
- Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels,
+ point, Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels,
DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, Keymap,
LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams,
RenderSvgParams, Scene, SharedString, Size, Task, TaskLabel, WindowContext,
+ DEFAULT_WINDOW_SIZE,
};
use anyhow::Result;
use async_task::Runnable;
@@ -167,6 +168,14 @@ pub trait PlatformDisplay: Send + Sync + Debug {
/// Get the bounds for this display
fn bounds(&self) -> Bounds<DevicePixels>;
+
+ /// Get the default bounds for this display to place a window
+ fn default_bounds(&self) -> Bounds<DevicePixels> {
+ let center = self.bounds().center();
+ let offset = DEFAULT_WINDOW_SIZE / 2;
+ let origin = point(center.x - offset.width, center.y - offset.height);
+ Bounds::new(origin, DEFAULT_WINDOW_SIZE)
+ }
}
/// An opaque identifier for a hardware display
@@ -108,6 +108,22 @@ impl WindowsDisplay {
Some(WindowsDisplay::new_with_handle(monitor))
}
+ /// Check if the center point of given bounds is inside this monitor
+ pub fn check_given_bounds(&self, bounds: Bounds<DevicePixels>) -> bool {
+ let center = bounds.center();
+ let center = POINT {
+ x: center.x.0,
+ y: center.y.0,
+ };
+ let monitor = unsafe { MonitorFromPoint(center, MONITOR_DEFAULTTONULL) };
+ if monitor.is_invalid() {
+ false
+ } else {
+ let display = WindowsDisplay::new_with_handle(monitor);
+ display.uuid == self.uuid
+ }
+ }
+
pub fn displays() -> Vec<Rc<dyn PlatformDisplay>> {
available_monitors()
.into_iter()
@@ -135,6 +151,11 @@ impl WindowsDisplay {
.then(|| devmode.dmDisplayFrequency)
})
}
+
+ /// Check if this monitor is still online
+ pub fn is_connected(hmonitor: HMONITOR) -> bool {
+ available_monitors().iter().contains(&hmonitor)
+ }
}
impl PlatformDisplay for WindowsDisplay {
@@ -39,6 +39,7 @@ pub(crate) fn handle_msg(
WM_TIMER => handle_timer_msg(handle, wparam, state_ptr),
WM_NCCALCSIZE => handle_calc_client_size(handle, wparam, lparam, state_ptr),
WM_DPICHANGED => handle_dpi_changed_msg(handle, wparam, lparam, state_ptr),
+ WM_DISPLAYCHANGE => handle_display_change_msg(handle, state_ptr),
WM_NCHITTEST => handle_hit_test_msg(handle, msg, wparam, lparam, state_ptr),
WM_PAINT => handle_paint_msg(handle, state_ptr),
WM_CLOSE => handle_close_msg(state_ptr),
@@ -112,6 +113,8 @@ fn handle_move_msg(
{
// center of the window may have moved to another monitor
let monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
+ // minimize the window can trigger this event too, in this case,
+ // monitor is invalid, we do nothing.
if !monitor.is_invalid() && lock.display.handle != monitor {
// we will get the same monitor if we only have one
lock.display = WindowsDisplay::new_with_handle(monitor);
@@ -775,6 +778,41 @@ fn handle_dpi_changed_msg(
Some(0)
}
+/// The following conditions will trigger this event:
+/// 1. The monitor on which the window is located goes offline or changes resolution.
+/// 2. Another monitor goes offline, is plugged in, or changes resolution.
+///
+/// In either case, the window will only receive information from the monitor on which
+/// it is located.
+///
+/// For example, in the case of condition 2, where the monitor on which the window is
+/// located has actually changed nothing, it will still receive this event.
+fn handle_display_change_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
+ // NOTE:
+ // Even the `lParam` holds the resolution of the screen, we just ignore it.
+ // Because WM_DPICHANGED, WM_MOVE, WM_SIEZ will come first, window reposition and resize
+ // are handled there.
+ // So we only care about if monitor is disconnected.
+ let previous_monitor = state_ptr.as_ref().state.borrow().display;
+ if WindowsDisplay::is_connected(previous_monitor.handle) {
+ // we are fine, other display changed
+ return None;
+ }
+ // display disconnected
+ // in this case, the OS will move our window to another monitor, and minimize it.
+ // we deminimize the window and query the monitor after moving
+ unsafe { ShowWindow(handle, SW_SHOWNORMAL) };
+ let new_monitor = unsafe { MonitorFromWindow(handle, MONITOR_DEFAULTTONULL) };
+ // all monitors disconnected
+ if new_monitor.is_invalid() {
+ log::error!("No monitor detected!");
+ return None;
+ }
+ let new_display = WindowsDisplay::new_with_handle(new_monitor);
+ state_ptr.as_ref().state.borrow_mut().display = new_display;
+ Some(0)
+}
+
fn handle_hit_test_msg(
handle: HWND,
msg: u32,
@@ -258,13 +258,17 @@ impl WindowsWindow {
);
let dwstyle = WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX;
let hinstance = get_module_handle();
+ let display = if let Some(display_id) = params.display_id {
+ // if we obtain a display_id, then this ID must be valid.
+ WindowsDisplay::new(display_id).unwrap()
+ } else {
+ WindowsDisplay::primary_monitor().unwrap()
+ };
let mut context = WindowCreateContext {
inner: None,
handle,
hide_title_bar,
- // todo(windows) move window to target monitor
- // options.display_id
- display: WindowsDisplay::primary_monitor().unwrap(),
+ display,
transparent: params.window_background != WindowBackgroundAppearance::Opaque,
executor,
mouse_wheel_settings,
@@ -297,10 +301,16 @@ impl WindowsWindow {
..Default::default()
};
GetWindowPlacement(raw_hwnd, &mut placement).log_err();
- placement.rcNormalPosition.left = params.bounds.left().0;
- placement.rcNormalPosition.right = params.bounds.right().0;
- placement.rcNormalPosition.top = params.bounds.top().0;
- placement.rcNormalPosition.bottom = params.bounds.bottom().0;
+ // the bounds may be not inside the display
+ let bounds = if display.check_given_bounds(params.bounds) {
+ params.bounds
+ } else {
+ display.default_bounds()
+ };
+ placement.rcNormalPosition.left = bounds.left().0;
+ placement.rcNormalPosition.right = bounds.right().0;
+ placement.rcNormalPosition.top = bounds.top().0;
+ placement.rcNormalPosition.bottom = bounds.bottom().0;
SetWindowPlacement(raw_hwnd, &placement).log_err();
}
unsafe { ShowWindow(raw_hwnd, SW_SHOW).ok().log_err() };
@@ -51,6 +51,9 @@ mod prompts;
pub use prompts::*;
+pub(crate) const DEFAULT_WINDOW_SIZE: Size<DevicePixels> =
+ size(DevicePixels(1024), DevicePixels(700));
+
/// Represents the two different phases when dispatching events.
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)]
pub enum DispatchPhase {
@@ -573,7 +576,6 @@ pub(crate) struct ElementStateBox {
}
fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<DevicePixels> {
- const DEFAULT_WINDOW_SIZE: Size<DevicePixels> = size(DevicePixels(1024), DevicePixels(700));
const DEFAULT_WINDOW_OFFSET: Point<DevicePixels> = point(DevicePixels(0), DevicePixels(35));
cx.active_window()
@@ -585,12 +587,7 @@ fn default_bounds(display_id: Option<DisplayId>, cx: &mut AppContext) -> Bounds<
.unwrap_or_else(|| cx.primary_display());
display
- .map(|display| {
- let center = display.bounds().center();
- let offset = DEFAULT_WINDOW_SIZE / 2;
- let origin = point(center.x - offset.width, center.y - offset.height);
- Bounds::new(origin, DEFAULT_WINDOW_SIZE)
- })
+ .map(|display| display.default_bounds())
.unwrap_or_else(|| {
Bounds::new(point(DevicePixels(0), DevicePixels(0)), DEFAULT_WINDOW_SIZE)
})