Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Window::has_focus #2531

Merged
merged 1 commit into from
Jan 17, 2023
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- Add `Window::has_focus`.
- On Windows, fix `Window::set_minimized(false)` not working for windows minimized by `Win + D` hotkey.
- **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`.
- **Breaking:** Removed platform specific `WindowBuilder::with_parent` API in favor of `WindowBuilder::with_parent_window`.
Expand Down
11 changes: 10 additions & 1 deletion src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
hash::Hash,
sync::{
atomic::{AtomicBool, Ordering},
mpsc, Arc,
mpsc, Arc, RwLock,
},
time::{Duration, Instant},
};
Expand All @@ -14,6 +14,7 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
};
use once_cell::sync::Lazy;
use raw_window_handle::{
AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
};
Expand All @@ -27,6 +28,8 @@ use crate::{
window::{self, CursorGrabMode, ResizeDirection, Theme, WindowButtons, WindowLevel},
};

static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));

fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> {
match keycode {
Keycode::A => Some(VirtualKeyCode::A),
Expand Down Expand Up @@ -394,6 +397,7 @@ impl<T: 'static> EventLoop<T> {
warn!("TODO: find a way to notify application of content rect change");
}
MainEvent::GainedFocus => {
*HAS_FOCUS.write().unwrap() = true;
sticky_exit_callback(
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
Expand All @@ -405,6 +409,7 @@ impl<T: 'static> EventLoop<T> {
);
}
MainEvent::LostFocus => {
*HAS_FOCUS.write().unwrap() = false;
sticky_exit_callback(
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
Expand Down Expand Up @@ -1064,6 +1069,10 @@ impl Window {
None
}

pub fn has_focus(&self) -> bool {
*HAS_FOCUS.read().unwrap()
}

pub fn title(&self) -> String {
String::new()
}
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/ios/uikit/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@ extern_methods!(

#[sel(makeKeyAndVisible)]
pub fn makeKeyAndVisible(&self);

#[sel(isKeyWindow)]
pub fn isKeyWindow(&self) -> bool;
}
);
4 changes: 4 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ impl Inner {
None
}

pub fn has_focus(&self) -> bool {
self.window.isKeyWindow()
}

#[inline]
pub fn set_theme(&self, _theme: Option<Theme>) {
warn!("`Window::set_theme` is ignored on iOS");
Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,10 @@ impl Window {
}

#[inline]
pub fn has_focus(&self) -> bool {
x11_or_wayland!(match self; Window(window) => window.has_focus())
}

pub fn title(&self) -> String {
x11_or_wayland!(match self; Window(window) => window.title())
}
Expand Down
8 changes: 8 additions & 0 deletions src/platform_impl/linux/wayland/seat/keyboard/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Handling of various keyboard events.

use std::sync::atomic::Ordering;

use sctk::reexports::client::protocol::wl_keyboard::KeyState;

use sctk::seat::keyboard::Event as KeyboardEvent;
Expand All @@ -22,6 +24,9 @@ pub(super) fn handle_keyboard(
KeyboardEvent::Enter { surface, .. } => {
let window_id = wayland::make_wid(&surface);

let window_handle = winit_state.window_map.get_mut(&window_id).unwrap();
window_handle.has_focus.store(true, Ordering::Relaxed);

// Window gained focus.
event_sink.push_window_event(WindowEvent::Focused(true), window_id);

Expand All @@ -44,6 +49,9 @@ pub(super) fn handle_keyboard(
);
}

let window_handle = winit_state.window_map.get_mut(&window_id).unwrap();
window_handle.has_focus.store(false, Ordering::Relaxed);

// Window lost focus.
event_sink.push_window_event(WindowEvent::Focused(false), window_id);

Expand Down
10 changes: 10 additions & 0 deletions src/platform_impl/linux/wayland/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ pub struct Window {

/// Grabbing mode.
cursor_grab_mode: Mutex<CursorGrabMode>,

/// Whether the window has keyboard focus.
has_focus: Arc<AtomicBool>,
}

impl Window {
Expand Down Expand Up @@ -244,6 +247,7 @@ impl Window {
window.surface().commit();

let size = Arc::new(Mutex::new(LogicalSize::new(width, height)));
let has_focus = Arc::new(AtomicBool::new(true));

// We should trigger redraw and commit the surface for the newly created window.
let mut window_user_request = WindowUserRequest::new();
Expand All @@ -258,6 +262,7 @@ impl Window {
&event_loop_window_target.env,
window,
size.clone(),
has_focus.clone(),
window_requests.clone(),
);

Expand Down Expand Up @@ -318,6 +323,7 @@ impl Window {
resizeable: AtomicBool::new(attributes.resizable),
decorated: AtomicBool::new(attributes.decorations),
cursor_grab_mode: Mutex::new(CursorGrabMode::None),
has_focus,
};

Ok(window)
Expand Down Expand Up @@ -650,6 +656,10 @@ impl Window {
}

#[inline]
pub fn has_focus(&self) -> bool {
self.has_focus.load(Ordering::Relaxed)
}

pub fn title(&self) -> String {
String::new()
}
Expand Down
6 changes: 6 additions & 0 deletions src/platform_impl/linux/wayland/window/shim.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::cell::Cell;
use std::mem::ManuallyDrop;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};

use sctk::reexports::client::protocol::wl_compositor::WlCompositor;
Expand Down Expand Up @@ -154,6 +155,9 @@ pub struct WindowHandle {
/// Whether the window is resizable.
pub is_resizable: Cell<bool>,

/// Whether the window has keyboard focus.
pub has_focus: Arc<AtomicBool>,

/// Allow IME events for that window.
pub ime_allowed: Cell<bool>,

Expand Down Expand Up @@ -187,6 +191,7 @@ impl WindowHandle {
env: &Environment<WinitEnv>,
window: Window<WinitFrame>,
size: Arc<Mutex<LogicalSize<u32>>>,
has_focus: Arc<AtomicBool>,
pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
) -> Self {
let xdg_activation = env.get_global::<XdgActivationV1>();
Expand All @@ -209,6 +214,7 @@ impl WindowHandle {
attention_requested: Cell::new(false),
compositor,
ime_allowed: Cell::new(false),
has_focus,
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/platform_impl/linux/x11/event_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,10 @@ impl<T: 'static> EventProcessor<T> {
let window_id = mkwid(xev.event);
let position = PhysicalPosition::new(xev.event_x, xev.event_y);

if let Some(window) = self.with_window(xev.event, Arc::clone) {
window.shared_state_lock().has_focus = true;
}

callback(Event::WindowEvent {
window_id,
event: Focused(true),
Expand Down Expand Up @@ -1002,6 +1006,10 @@ impl<T: 'static> EventProcessor<T> {
event: WindowEvent::ModifiersChanged(ModifiersState::empty()),
});

if let Some(window) = self.with_window(xev.event, Arc::clone) {
window.shared_state_lock().has_focus = false;
}

callback(Event::WindowEvent {
window_id,
event: Focused(false),
Expand Down
6 changes: 6 additions & 0 deletions src/platform_impl/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub struct SharedState {
pub resize_increments: Option<Size>,
pub base_size: Option<Size>,
pub visibility: Visibility,
pub has_focus: bool,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -94,6 +95,7 @@ impl SharedState {
max_inner_size: None,
resize_increments: None,
base_size: None,
has_focus: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't the window start initially with keyboard focus?

Copy link
Member

@madsmtm madsmtm Nov 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On macOS, the window is initially not focused (and window.has_focus() returns false), but it receives a WindowEvent::Focused shortly after launching (which also makes window.has_focus() return true)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only affects x11, on macOS we query the OS directly. The reason I made x11 start with true is because Windows also starts with true and I think x11 is very similar to Windows in this matter, but I haven't had the chance to test x11 (I may test it later today).

})
}
}
Expand Down Expand Up @@ -1602,6 +1604,10 @@ impl UnownedWindow {
}

#[inline]
pub fn has_focus(&self) -> bool {
self.shared_state_lock().has_focus
}

pub fn title(&self) -> String {
String::new()
}
Expand Down
3 changes: 3 additions & 0 deletions src/platform_impl/macos/appkit/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ extern_methods!(
#[sel(isVisible)]
pub fn isVisible(&self) -> bool;

#[sel(isKeyWindow)]
pub fn isKeyWindow(&self) -> bool;

#[sel(isZoomed)]
pub fn isZoomed(&self) -> bool;

Expand Down
4 changes: 4 additions & 0 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,10 @@ impl WinitWindow {
}

#[inline]
pub fn has_focus(&self) -> bool {
self.isKeyWindow()
}

pub fn set_theme(&self, theme: Option<Theme>) {
set_ns_theme(theme);
self.lock_shared_state("set_theme").current_theme = theme.or_else(|| Some(get_ns_theme()));
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/orbital/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ impl Window {
None
}

#[inline]
pub fn has_focus(&self) -> bool {
false
}

#[inline]
pub fn set_theme(&self, _theme: Option<window::Theme>) {}
}
Expand Down
7 changes: 7 additions & 0 deletions src/platform_impl/web/event_loop/window_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl<T> EventLoopWindowTarget<T> {
canvas: &Rc<RefCell<backend::Canvas>>,
id: WindowId,
prevent_default: bool,
has_focus: Rc<RefCell<bool>>,
) {
self.runner.add_canvas(RootWindowId(id), canvas);
let mut canvas = canvas.borrow_mut();
Expand All @@ -65,15 +66,19 @@ impl<T> EventLoopWindowTarget<T> {
canvas.on_touch_end(prevent_default);

let runner = self.runner.clone();
let has_focus_clone = has_focus.clone();
canvas.on_blur(move || {
*has_focus_clone.borrow_mut() = false;
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Focused(false),
});
});

let runner = self.runner.clone();
let has_focus_clone = has_focus.clone();
canvas.on_focus(move || {
*has_focus_clone.borrow_mut() = true;
runner.send_event(Event::WindowEvent {
window_id: RootWindowId(id),
event: WindowEvent::Focused(true),
Expand Down Expand Up @@ -191,6 +196,8 @@ impl<T> EventLoopWindowTarget<T> {
let runner_touch = self.runner.clone();
canvas.on_mouse_press(
move |pointer_id, position, button, modifiers| {
*has_focus.borrow_mut() = true;

// A mouse down event may come in without any prior CursorMoved events,
// therefore we should send a CursorMoved event to make sure that the
// user code has the correct cursor position.
Expand Down
9 changes: 8 additions & 1 deletion src/platform_impl/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct Window {
register_redraw_request: Box<dyn Fn()>,
resize_notify_fn: Box<dyn Fn(PhysicalSize<u32>)>,
destroy_fn: Option<Box<dyn FnOnce()>>,
has_focus: Rc<RefCell<bool>>,
}

impl Window {
Expand All @@ -42,7 +43,8 @@ impl Window {

let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));

target.register(&canvas, id, prevent_default);
let has_focus = Rc::new(RefCell::new(false));
target.register(&canvas, id, prevent_default, has_focus.clone());

let runner = target.runner.clone();
let resize_notify_fn = Box::new(move |new_size| {
Expand All @@ -62,6 +64,7 @@ impl Window {
register_redraw_request,
resize_notify_fn,
destroy_fn: Some(destroy_fn),
has_focus,
};

backend::set_canvas_size(
Expand Down Expand Up @@ -399,6 +402,10 @@ impl Window {
}

#[inline]
pub fn has_focus(&self) -> bool {
*self.has_focus.borrow()
}

pub fn title(&self) -> String {
String::new()
}
Expand Down
5 changes: 5 additions & 0 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,11 @@ impl Window {
}

#[inline]
pub fn has_focus(&self) -> bool {
let window_state = self.window_state.lock().unwrap();
window_state.has_active_focus()
}

pub fn title(&self) -> String {
let len = unsafe { GetWindowTextLengthW(self.window.0) } + 1;
let mut buf = vec![0; len as usize];
Expand Down
10 changes: 10 additions & 0 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,16 @@ impl Window {
self.window.focus_window()
}

/// Gets whether the window has keyboard focus.
///
amrbashir marked this conversation as resolved.
Show resolved Hide resolved
/// This queries the same state information as [`WindowEvent::Focused`].
///
/// [`WindowEvent::Focused`]: crate::event::WindowEvent::Focused
#[inline]
pub fn has_focus(&self) -> bool {
self.window.has_focus()
}

/// Requests user attention to the window, this has no effect if the application
/// is already focused. How requesting for user attention manifests is platform dependent,
/// see [`UserAttentionType`] for details.
Expand Down