diff --git a/.changes/window-builder-focused.md b/.changes/window-builder-focused.md new file mode 100644 index 000000000..5ffe4bde9 --- /dev/null +++ b/.changes/window-builder-focused.md @@ -0,0 +1,5 @@ +--- +"tao": "patch" +--- + +Add `WindowBuilder::with_focused` to specify whether to initially focus the window or not. \ No newline at end of file diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 8dfbe8ac9..6ddb5a1ca 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -6,7 +6,10 @@ use std::{ cell::RefCell, collections::VecDeque, rc::Rc, - sync::atomic::{AtomicBool, AtomicI32, Ordering}, + sync::{ + atomic::{AtomicBool, AtomicI32, Ordering}, + Arc, + }, }; use gdk::{WindowEdge, WindowState}; @@ -70,7 +73,10 @@ impl Window { ) -> Result { let app = &event_loop_window_target.app; let window_requests_tx = event_loop_window_target.window_requests_tx.clone(); - let window = gtk::ApplicationWindow::new(app); + let window = gtk::ApplicationWindow::builder() + .application(app) + .accept_focus(attributes.focused) + .build(); let window_id = WindowId(window.id()); event_loop_window_target .windows @@ -246,6 +252,21 @@ impl Window { window.set_transient_for(Some(&parent)); } + // restore accept-focus after the window has been drawn + // if the window was initially created without focus + if !attributes.focused { + let signal_id = Arc::new(RefCell::new(None)); + let signal_id_ = signal_id.clone(); + let id = window.connect_draw(move |window, _| { + if let Some(id) = signal_id_.take() { + window.set_accept_focus(true); + window.disconnect(id); + } + Inhibit(false) + }); + signal_id.borrow_mut().replace(id); + } + let w_pos = window.position(); let position: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_pos.0.into(), w_pos.1.into())); let position_clone = position.clone(); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 784a92486..b8d6f736e 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -488,6 +488,7 @@ impl UnownedWindow { let fullscreen = win_attribs.fullscreen.take(); let maximized = win_attribs.maximized; let visible = win_attribs.visible; + let focused = win_attribs.focused; let decorations = win_attribs.decorations; let inner_rect = win_attribs .inner_size @@ -526,8 +527,12 @@ impl UnownedWindow { // state, since otherwise we'll briefly see the window at normal size // before it transitions. if visible { - // Tightly linked with `app_state::window_activation_hack` - unsafe { window.ns_window.makeKeyAndOrderFront_(nil) }; + if focused { + // Tightly linked with `app_state::window_activation_hack` + unsafe { window.ns_window.makeKeyAndOrderFront_(nil) }; + } else { + unsafe { window.ns_window.orderFront_(nil) }; + } } if maximized { diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 272616c60..82ecee412 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -880,6 +880,8 @@ unsafe fn init( // WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured. window_flags.set(WindowFlags::RESIZABLE, attributes.resizable); + window_flags.set(WindowFlags::MARKER_DONT_FOCUS, !attributes.focused); + let parent = match pl_attribs.parent { Parent::ChildOf(parent) => { window_flags.set(WindowFlags::CHILD, true); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 80592dc41..8d962a6ab 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -79,26 +79,28 @@ bitflags! { const TRANSPARENT = 1 << 6; const CHILD = 1 << 7; const MAXIMIZED = 1 << 8; - const POPUP = 1 << 14; - const ALWAYS_ON_BOTTOM = 1 << 16; + const POPUP = 1 << 9; + const ALWAYS_ON_BOTTOM = 1 << 10; /// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is /// included here to make masking easier. - const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 9; - const MARKER_BORDERLESS_FULLSCREEN = 1 << 13; + const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 11; + const MARKER_BORDERLESS_FULLSCREEN = 1 << 12; /// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`. /// In most cases, it's okay to let those parameters change the state. However, when we're /// running the `WindowFlags::apply_diff` function, we *don't* want those parameters to /// effect our stored state, because the purpose of `apply_diff` is to update the actual /// window's state to match our stored state. This controls whether to accept those changes. - const MARKER_RETAIN_STATE_ON_SIZE = 1 << 10; + const MARKER_RETAIN_STATE_ON_SIZE = 1 << 13; - const MARKER_IN_SIZE_MOVE = 1 << 11; + const MARKER_IN_SIZE_MOVE = 1 << 14; - const MINIMIZED = 1 << 12; + const MARKER_DONT_FOCUS = 1 << 15; - const IGNORE_CURSOR_EVENT = 1 << 15; + const MINIMIZED = 1 << 16; + + const IGNORE_CURSOR_EVENT = 1 << 17; const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; } @@ -277,7 +279,15 @@ impl WindowFlags { if new.contains(WindowFlags::VISIBLE) { unsafe { - ShowWindow(window, SW_SHOW); + ShowWindow( + window, + if self.contains(WindowFlags::MARKER_DONT_FOCUS) { + self.set(WindowFlags::MARKER_DONT_FOCUS, false); + SW_SHOWNOACTIVATE + } else { + SW_SHOW + }, + ); } } diff --git a/src/window.rs b/src/window.rs index 1747019b0..fe9a9870b 100644 --- a/src/window.rs +++ b/src/window.rs @@ -208,6 +208,13 @@ pub struct WindowAttributes { pub window_menu: Option, pub preferred_theme: Option, + + /// Whether the window should be initially focused or not. + /// + /// ## Platform-specific: + /// + /// **Android / iOS:** Unsupported. + pub focused: bool, } impl Default for WindowAttributes { @@ -230,6 +237,7 @@ impl Default for WindowAttributes { window_icon: None, window_menu: None, preferred_theme: None, + focused: false, } } } @@ -411,6 +419,17 @@ impl WindowBuilder { self } + /// Whether the window will be initially focused or not. + /// + /// ## Platform-specific: + /// + /// **Android / iOS:** Unsupported. + #[inline] + pub fn with_focused(mut self, focused: bool) -> WindowBuilder { + self.window.focused = focused; + self + } + /// Builds the window. /// /// Possible causes of error include denied permission, incompatible system, and lack of memory.