mirror of
https://github.com/rust-windowing/winit.git
synced 2026-08-29 12:50:04 -04:00
macOS: add WindowEvent::PointerButton::is_macos_activation_click
Apps need per-click decisions to follow the macOS convention of accepting first mouse for low-risk actions (selection, scrolling) but rejecting it for buttons and destructive actions. Motivated by slint-ui/slint#10451. Always return `true` from `acceptsFirstMouse:`, and tag the resulting `PointerButton` events with `is_macos_activation_click: true` (on both the activating left press and its matching release) so the app can short-circuit the whole gesture with a single check: WindowEvent::PointerButton { is_macos_activation_click: true, .. } => return, Replaces an earlier callback-based design (`accepts_first_mouse` on `ApplicationHandlerExtMacOS`) that mapped more closely to AppKit but didn't fit winit's event-driven model and required re-entrancy handling. Also removes `WindowAttributesMacOS::with_accepts_first_mouse`, which is now redundant — apps that want to reject activation clicks check the flag on the per-event instead.
This commit is contained in:
committed by
Simon Hausmann
parent
92b643c3f1
commit
6a8a334426
@@ -367,7 +367,6 @@ pub struct WindowAttributesMacOS {
|
||||
pub(crate) fullsize_content_view: bool,
|
||||
pub(crate) disallow_hidpi: bool,
|
||||
pub(crate) has_shadow: bool,
|
||||
pub(crate) accepts_first_mouse: bool,
|
||||
pub(crate) tabbing_identifier: Option<String>,
|
||||
pub(crate) option_as_alt: OptionAsAlt,
|
||||
pub(crate) borderless_game: bool,
|
||||
@@ -431,13 +430,6 @@ impl WindowAttributesMacOS {
|
||||
self
|
||||
}
|
||||
|
||||
/// Window accepts click-through mouse events.
|
||||
#[inline]
|
||||
pub fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self {
|
||||
self.accepts_first_mouse = accepts_first_mouse;
|
||||
self
|
||||
}
|
||||
|
||||
/// Defines the window tabbing identifier.
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier>
|
||||
@@ -506,7 +498,6 @@ impl Default for WindowAttributesMacOS {
|
||||
fullsize_content_view: false,
|
||||
disallow_hidpi: false,
|
||||
has_shadow: true,
|
||||
accepts_first_mouse: true,
|
||||
tabbing_identifier: None,
|
||||
option_as_alt: Default::default(),
|
||||
borderless_game: false,
|
||||
|
||||
@@ -138,7 +138,10 @@ pub struct ViewState {
|
||||
/// to the application, even during IME
|
||||
forward_key_to_app: Cell<bool>,
|
||||
marked_text: RefCell<Retained<NSMutableAttributedString>>,
|
||||
accepts_first_mouse: bool,
|
||||
|
||||
/// Set by `acceptsFirstMouse:` so the next `mouseDown:` can tag its
|
||||
/// [`WindowEvent::PointerButton`] with `is_macos_activation_click: true`.
|
||||
next_click_is_activation: Cell<bool>,
|
||||
|
||||
/// The state of the `Option` as `Alt`.
|
||||
option_as_alt: Cell<OptionAsAlt>,
|
||||
@@ -780,9 +783,18 @@ define_class!(
|
||||
}
|
||||
|
||||
#[unsafe(method(acceptsFirstMouse:))]
|
||||
fn accepts_first_mouse(&self, _event: &NSEvent) -> bool {
|
||||
fn accepts_first_mouse(&self, event: Option<&NSEvent>) -> bool {
|
||||
let _entered = debug_span!("acceptsFirstMouse:").entered();
|
||||
self.ivars().accepts_first_mouse
|
||||
// Always accept the click. The following `mouseDown:` is tagged with
|
||||
// `is_macos_activation_click: true` so the application can decide per click
|
||||
// whether to act on it.
|
||||
//
|
||||
// AppKit may pass nil here for synthetic queries that are not tied to a
|
||||
// specific click; only set the flag when we actually have an event.
|
||||
if event.is_some() {
|
||||
self.ivars().next_click_is_activation.set(true);
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -790,7 +802,6 @@ define_class!(
|
||||
impl WinitView {
|
||||
pub(super) fn new(
|
||||
app_state: &Rc<AppState>,
|
||||
accepts_first_mouse: bool,
|
||||
option_as_alt: OptionAsAlt,
|
||||
mtm: MainThreadMarker,
|
||||
) -> Retained<Self> {
|
||||
@@ -808,7 +819,7 @@ impl WinitView {
|
||||
ime_capabilities: Default::default(),
|
||||
forward_key_to_app: Default::default(),
|
||||
marked_text: Default::default(),
|
||||
accepts_first_mouse,
|
||||
next_click_is_activation: Default::default(),
|
||||
option_as_alt: Cell::new(option_as_alt),
|
||||
});
|
||||
let this: Retained<Self> = unsafe { msg_send![super(this), init] };
|
||||
@@ -1155,12 +1166,23 @@ impl WinitView {
|
||||
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
// `acceptsFirstMouse:` fires only for the left button, so the activation flag is
|
||||
// applied to the left press and held until the matching left release. This lets
|
||||
// apps short-circuit both events with a single `if is_macos_activation_click` check
|
||||
// without having to track gesture state themselves.
|
||||
let is_macos_activation_click = matches!(button, MouseButton::Left)
|
||||
&& match button_state {
|
||||
ElementState::Pressed => self.ivars().next_click_is_activation.get(),
|
||||
ElementState::Released => self.ivars().next_click_is_activation.replace(false),
|
||||
};
|
||||
|
||||
self.queue_event(WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state: button_state,
|
||||
position,
|
||||
button: button.into(),
|
||||
is_macos_activation_click,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -842,12 +842,7 @@ fn new_window(
|
||||
window.center();
|
||||
}
|
||||
|
||||
let view = WinitView::new(
|
||||
app_state,
|
||||
macos_attrs.accepts_first_mouse,
|
||||
macos_attrs.option_as_alt,
|
||||
mtm,
|
||||
);
|
||||
let view = WinitView::new(app_state, macos_attrs.option_as_alt, mtm);
|
||||
|
||||
// The default value of `setWantsBestResolutionOpenGLSurface:` was `false` until
|
||||
// macos 10.14 and `true` after 10.15, we should set it to `YES` or `NO` to avoid
|
||||
|
||||
Reference in New Issue
Block a user