diff --git a/src/darwin/wayland/window.rs b/src/darwin/wayland/window.rs index c3868f0d4..e57833187 100644 --- a/src/darwin/wayland/window.rs +++ b/src/darwin/wayland/window.rs @@ -2073,7 +2073,10 @@ pub fn frame_for_surface(surface: *mut wl::WlSurface) -> Option<(f64, f64, f64)> for &p in list.iter() { let st = unsafe { (p as *mut WindowState).as_ref() }?; if st.surface == surface { - return Some((st.frame.origin.x, st.frame.origin.y, st.frame.size.height)); + // The same rule as window_for_surface: flip by what AppKit draws into. + let height = if st.draw_h > 0 { st.draw_h as f64 } else { st.frame.size.height }; + + return Some((st.frame.origin.x, st.frame.origin.y, height)); } } None @@ -2405,12 +2408,27 @@ pub fn margin_for_surface(surface: *mut wl::WlSurface) -> f64 { 0.0 } +/// The window behind a surface, with THE HEIGHT AN EVENT MUST BE FLIPPED BY. +/// +/// That height is the BITMAP height, not the buffer height, and the difference is a real defect +/// when they differ. A window whose application refuses to shrink below a size keeps drawing at +/// that size while the compositor is shown a smaller buffer taken from the TOP of it (see +/// ensure_backing and insist_w/insist_h). Wayland reports the pointer in surface coordinates from +/// the top, AppKit wants them from the bottom of ITS window, so flipping by the buffer height puts +/// every click short by exactly the amount the window overhangs. +/// +/// Measured on Swift Publisher 5, whose document window insists on 753 and is configured 684: a +/// click on the menu bar arrived 69 points low, landing on the toolbar or the document pane, and +/// the menu never opened. From outside that is indistinguishable from an application ignoring the +/// mouse. pub fn window_for_surface(surface: *mut wl::WlSurface) -> Option<(Object, Object, f64, i64)> { let list = WINDOWS.lock().ok()?; for &p in list.iter() { let st = unsafe { (p as *mut WindowState).as_ref() }?; if st.surface == surface { - return Some((st.owner, st.delegate, st.frame.size.height, st.number)); + let height = if st.draw_h > 0 { st.draw_h as f64 } else { st.frame.size.height }; + + return Some((st.owner, st.delegate, height, st.number)); } } None diff --git a/vendor/pins/cocotron/AppKit/NSMenu.subproj/NSMenu.m b/vendor/pins/cocotron/AppKit/NSMenu.subproj/NSMenu.m index 66b5e4d25..72b796468 100644 --- a/vendor/pins/cocotron/AppKit/NSMenu.subproj/NSMenu.m +++ b/vendor/pins/cocotron/AppKit/NSMenu.subproj/NSMenu.m @@ -476,6 +476,27 @@ BOOL itemIsEnabled(NSMenuItem *item) { to: [item target] from: nil]; + /* WHY AN ITEM IS GREY. A disabled menu item and a missing one look the same to a user and + * nearly the same in a capture, and the reason is always one of three: no target found in + * the responder chain, a target that does not answer the action, or a validate method that + * said no. This names which. */ + if (getenv("CIDER_TRACE_MENU") != NULL) { + fprintf(stderr, + "CIDER_MENUITEM %s action=%s itemtarget=%s bound(enabled)=%d bound(title)=%d " + "target=%s keyWindow=%s mainWindow=%s controller=%s\n", + [[item title] UTF8String] ?: "(none)", sel_getName([item action]), + [item target] ? object_getClassName([item target]) : "(nil)", + (int) ([item _binderForBinding: @"enabled" create: NO] != nil), + (int) ([item _binderForBinding: @"title" create: NO] != nil), + target ? object_getClassName(target) : "(nil)", + [[[NSApp keyWindow] title] UTF8String] ?: "(nil)", + [[[NSApp mainWindow] title] UTF8String] ?: "(nil)", + [[NSApp keyWindow] windowController] + ? object_getClassName([[NSApp keyWindow] windowController]) + : "(nil)"); + fflush(stderr); + } + if ((target == nil) || ![target respondsToSelector: [item action]]) { enabled = NO; } else if ([target respondsToSelector: @selector(validateMenuItem:)]) { @@ -497,6 +518,16 @@ BOOL itemIsEnabled(NSMenuItem *item) { [_delegate menuNeedsUpdate: self]; } + /* AUTOENABLING IS A PER MENU DECISION AND IT COMES FROM THE NIB. A menu that manages its own + * item states carries NSNoAutoenable, and getting that flag wrong greys out every item whose + * action is a placeholder, which is exactly what a bound menu item looks like. */ + if (getenv("CIDER_TRACE_MENU") != NULL) { + fprintf(stderr, "CIDER_MENUUPDATE %s items=%ld autoenables=%d\n", + [[self title] UTF8String] ?: "(none)", (long) [_itemArray count], + (int) _autoenablesItems); + fflush(stderr); + } + NSInteger i, count = [_itemArray count]; for (i = 0; i < count; i++) { diff --git a/vendor/pins/cocotron/AppKit/NSSplitView.m b/vendor/pins/cocotron/AppKit/NSSplitView.m index 0fd973e6c..49367dcaf 100644 --- a/vendor/pins/cocotron/AppKit/NSSplitView.m +++ b/vendor/pins/cocotron/AppKit/NSSplitView.m @@ -80,6 +80,16 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = } - (void) setDelegate: (id) delegate { + /* THE OBSERVER REGISTRATION LIVES IN THIS SETTER, so a delegate that arrives any other way + * (a nib connection that assigns the ivar, for instance) is a delegate that never hears the + * resize notifications. Naming every call tells the two apart. */ + if (getenv("CIDER_TRACE_SPLIT") != NULL) { + fprintf(stderr, "CIDER_SPLIT setDelegate %s <- %s responds=%d\n", + object_getClassName(self), delegate ? object_getClassName(delegate) : "(nil)", + (int) [delegate respondsToSelector: @selector(splitViewDidResizeSubviews:)]); + fflush(stderr); + } + if ([_delegate respondsToSelector: @selector(splitViewDidResizeSubviews:)]) [[NSNotificationCenter defaultCenter] removeObserver: _delegate @@ -276,12 +286,14 @@ NSString *const NSSplitViewWillResizeSubviewsNotification = fprintf(stderr, "CIDER_SPLIT adjustSubviews %s frame=%gx%g@%g,%g mask=%lu subviews=%lu " - "superview=%s %gx%g autoresizes=%d\n", + "superview=%s %gx%g autoresizes=%d delegate=%s responds=%d\n", object_getClassName(self), f.size.width, f.size.height, f.origin.x, f.origin.y, (unsigned long) [self autoresizingMask], (unsigned long) [_subviews count], sup ? object_getClassName(sup) : "(none)", sup ? [sup frame].size.width : 0.0, sup ? [sup frame].size.height : 0.0, - sup ? (int) [sup autoresizesSubviews] : -1); + sup ? (int) [sup autoresizesSubviews] : -1, + _delegate ? object_getClassName(_delegate) : "(nil)", + (int) [_delegate respondsToSelector: @selector(splitViewDidResizeSubviews:)]); fflush(stderr); }