diff --git a/CHANGELOG.md b/CHANGELOG.md index ac26f345..0b025fcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre # Unreleased +- On Windows and macOS, add `WindowBuilder::with_active`. - Add `Window::is_minimized`. - On X11, fix errors handled during `register_xlib_error_hook` invocation bleeding into winit. - Add `Window::has_focus`. diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 2e81cb54..3056055b 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -511,7 +511,24 @@ impl EventProcessor { window.invalidate_cached_frame_extents(); }); } + ffi::MapNotify => { + let xev: &ffi::XMapEvent = xev.as_ref(); + let window = xev.window; + let window_id = mkwid(window); + // XXX re-issue the focus state when mapping the window. + // + // The purpose of it is to deliver initial focused state of the newly created + // window, given that we can't rely on `CreateNotify`, due to it being not + // sent. + let focus = self + .with_window(window, |window| window.has_focus()) + .unwrap_or_default(); + callback(Event::WindowEvent { + window_id, + event: WindowEvent::Focused(focus), + }); + } ffi::DestroyNotify => { let xev: &ffi::XDestroyWindowEvent = xev.as_ref(); diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 49036b19..60d7ce37 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -95,7 +95,7 @@ impl SharedState { max_inner_size: None, resize_increments: None, base_size: None, - has_focus: true, + has_focus: false, }) } } diff --git a/src/platform_impl/macos/appkit/window.rs b/src/platform_impl/macos/appkit/window.rs index 9b74a65e..8562e3f0 100644 --- a/src/platform_impl/macos/appkit/window.rs +++ b/src/platform_impl/macos/appkit/window.rs @@ -114,6 +114,9 @@ extern_methods!( #[sel(makeKeyAndOrderFront:)] pub fn makeKeyAndOrderFront(&self, sender: Option<&Object>); + #[sel(orderFront:)] + pub fn orderFront(&self, sender: Option<&Object>); + #[sel(miniaturize:)] pub fn miniaturize(&self, sender: Option<&Object>); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 7ceaa823..4b05d7c2 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -19,6 +19,7 @@ use crate::{ LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size, Size::Logical, }, error::{ExternalError, NotSupportedError, OsError as RootOsError}, + event::WindowEvent, icon::Icon, platform::macos::WindowExtMacOS, platform_impl::platform::{ @@ -464,14 +465,20 @@ impl WinitWindow { // state, since otherwise we'll briefly see the window at normal size // before it transitions. if attrs.visible { - // Tightly linked with `app_state::window_activation_hack` - this.makeKeyAndOrderFront(None); + if attrs.active { + // Tightly linked with `app_state::window_activation_hack` + this.makeKeyAndOrderFront(None); + } else { + this.orderFront(None); + } } if attrs.maximized { this.set_maximized(attrs.maximized); } + delegate.queue_event(WindowEvent::Focused(false)); + Ok((this, delegate)) } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index e44e5c2b..6a8f2f13 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -406,7 +406,7 @@ impl WinitWindowDelegate { } } - fn queue_event(&self, event: WindowEvent<'static>) { + pub(crate) fn queue_event(&self, event: WindowEvent<'static>) { let event = Event::WindowEvent { window_id: WindowId(self.window.id()), event, diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index f6c6c927..b0d30e21 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -1076,6 +1076,7 @@ where WindowFlags::NO_BACK_BUFFER, pl_attribs.no_redirection_bitmap, ); + window_flags.set(WindowFlags::MARKER_ACTIVATE, attributes.active); window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent); // WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured. window_flags.set(WindowFlags::RESIZABLE, attributes.resizable); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 60c0320a..9cf127f5 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -116,6 +116,8 @@ bitflags! { /// Drop shadow for undecorated windows. const MARKER_UNDECORATED_SHADOW = 1 << 20; + const MARKER_ACTIVATE = 1 << 18; + const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; } } @@ -306,8 +308,14 @@ impl WindowFlags { } if new.contains(WindowFlags::VISIBLE) { + let flag = !if self.contains(WindowFlags::MARKER_ACTIVATE) { + self.set(WindowFlags::MARKER_ACTIVATE, true); + SWP_NOACTIVATE + } else { + SW_SHOW + }; unsafe { - ShowWindow(window, SW_SHOW); + ShowWindow(window, flag); } } diff --git a/src/window.rs b/src/window.rs index ab431ccc..b114d0b6 100644 --- a/src/window.rs +++ b/src/window.rs @@ -139,6 +139,7 @@ pub(crate) struct WindowAttributes { pub content_protected: bool, pub window_level: WindowLevel, pub parent_window: Option, + pub active: bool, } impl Default for WindowAttributes { @@ -163,6 +164,7 @@ impl Default for WindowAttributes { resize_increments: None, content_protected: false, parent_window: None, + active: true, } } } @@ -405,6 +407,22 @@ impl WindowBuilder { self } + /// Whether the window will be initially focused or not. + /// + /// The window should be assumed as not focused by default + /// following by the [`WindowEvent::Focused`]. + /// + /// ## Platform-specific: + /// + /// **Android / iOS / X11 / Wayland / Orbital:** Unsupported. + /// + /// [`WindowEvent::Focused`]: crate::event::WindowEvent::Focused. + #[inline] + pub fn with_active(mut self, active: bool) -> WindowBuilder { + self.window.active = active; + self + } + /// Build window with parent window. /// /// The default is `None`.