macOS: simplify and improve method performKeyEquivalent: of class FLView and fix #1436
Build and Test / build-linux (push) Has been cancelled
Build and Test / build-wayland (push) Has been cancelled
Build and Test / build-macos (push) Has been cancelled
Build and Test / build-windows (push) Has been cancelled

Removal of support of very old macOS versions allows to simplify method performKeyEquivalent: of class FLView.
This in turn fixes issue #1436.
The new code has been tested OK from macOS 10.9 and above.
This commit is contained in:
ManoloFLTK
2026-05-18 17:18:49 +02:00
parent a47f1a7840
commit 0b7f6124b2
2 changed files with 33 additions and 37 deletions
+7 -4
View File
@@ -1,7 +1,7 @@
// //
// MacOS system menu bar widget for the Fast Light Tool Kit (FLTK). // MacOS system menu bar widget for the Fast Light Tool Kit (FLTK).
// //
// Copyright 1998-2021 by Bill Spitzak and others. // Copyright 1998-2026 by Bill Spitzak and others.
// //
// This library is free software. Distribution and use rights are outlined in // This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this // the file "COPYING" which should have been included with this file. If this
@@ -454,7 +454,10 @@ static int process_sys_menu_shortcuts(int event)
if (event != FL_SHORTCUT || !fl_sys_menu_bar || Fl::modal()) return 0; if (event != FL_SHORTCUT || !fl_sys_menu_bar || Fl::modal()) return 0;
// is the last event the shortcut of an item of the fl_sys_menu_bar menu ? // is the last event the shortcut of an item of the fl_sys_menu_bar menu ?
const Fl_Menu_Item *item = fl_sys_menu_bar->menu()->test_shortcut(); const Fl_Menu_Item *item = fl_sys_menu_bar->menu()->test_shortcut();
if (!item) return 0; if (!item) { // It's not a shortcut of the user-part of menubar. Is it of the application menu?
NSMenu *app_menu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; // the application menu
return (int)[app_menu performKeyEquivalent:[NSApp currentEvent]];
}
if (item->visible()) // have the system menu process the shortcut, highlighting the corresponding menu if (item->visible()) // have the system menu process the shortcut, highlighting the corresponding menu
[[NSApp mainMenu] performKeyEquivalent:[NSApp currentEvent]]; [[NSApp mainMenu] performKeyEquivalent:[NSApp currentEvent]];
else // have FLTK process the shortcut associated to an invisible Fl_Menu_Item else // have FLTK process the shortcut associated to an invisible Fl_Menu_Item
@@ -659,10 +662,10 @@ void Fl_MacOS_Sys_Menu_Bar_Driver::create_window_menu(void)
if (fl_mac_os_version >= 101200 && window_menu_style() != Fl_Sys_Menu_Bar::tabbing_mode_none) { if (fl_mac_os_version >= 101200 && window_menu_style() != Fl_Sys_Menu_Bar::tabbing_mode_none) {
window_menu_items[1].label("Show Previous Tab"); window_menu_items[1].label("Show Previous Tab");
window_menu_items[1].callback(previous_tab_cb); window_menu_items[1].callback(previous_tab_cb);
window_menu_items[1].shortcut(FL_SHIFT+FL_CTRL+0x9); window_menu_items[1].shortcut(FL_SHIFT+FL_CTRL+FL_Tab);
window_menu_items[2].label("Show Next Tab"); window_menu_items[2].label("Show Next Tab");
window_menu_items[2].callback(next_tab_cb); window_menu_items[2].callback(next_tab_cb);
window_menu_items[2].shortcut(FL_CTRL+0x9); window_menu_items[2].shortcut(FL_CTRL+FL_Tab);
window_menu_items[3].label("Move Tab To New Window"); window_menu_items[3].label("Move Tab To New Window");
window_menu_items[3].callback(move_tab_cb); window_menu_items[3].callback(move_tab_cb);
window_menu_items[4].label("Merge All Windows"); window_menu_items[4].label("Merge All Windows");
+26 -33
View File
@@ -1483,8 +1483,8 @@ static FLWindowDelegate *flwindowdelegate_instance = nil;
{ {
if (fl_mac_os_version >= 101300 && [NSApp isRunning]) [NSApp stop:nil]; if (fl_mac_os_version >= 101300 && [NSApp isRunning]) [NSApp stop:nil];
} }
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
{ static void attempt_close_all_windows() {
fl_lock_function(); fl_lock_function();
while ( Fl_X::first ) { while ( Fl_X::first ) {
Fl_Window *win = Fl::first_window(); Fl_Window *win = Fl::first_window();
@@ -1500,6 +1500,11 @@ static FLWindowDelegate *flwindowdelegate_instance = nil;
Fl::program_should_quit(1); Fl::program_should_quit(1);
Fl_Cocoa_Screen_Driver::breakMacEventLoop(); // necessary when called through menu and in Fl::wait() Fl_Cocoa_Screen_Driver::breakMacEventLoop(); // necessary when called through menu and in Fl::wait()
} }
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
{
attempt_close_all_windows();
return NSTerminateCancel; return NSTerminateCancel;
} }
- (void)applicationDidBecomeActive:(NSNotification *)notify - (void)applicationDidBecomeActive:(NSNotification *)notify
@@ -2087,14 +2092,12 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
Keyboard input sends keyDown: and performKeyEquivalent: messages to myview. The latter occurs for keys such as Keyboard input sends keyDown: and performKeyEquivalent: messages to myview. The latter occurs for keys such as
ForwardDelete, arrows and F1, and when the Ctrl or Cmd modifiers are used. Other key presses send keyDown: messages. ForwardDelete, arrows and F1, and when the Ctrl or Cmd modifiers are used. Other key presses send keyDown: messages.
The performKeyEquivalent: method directly calls Fl::handle(FL_KEYBOARD, focus-window) and returns always YES.
The keyDown: method calls [[myview inputContext] handleEvent:theEvent], and triggers system processing of keyboard events. The keyDown: method calls [[myview inputContext] handleEvent:theEvent], and triggers system processing of keyboard events.
The performKeyEquivalent: method directly calls Fl::handle(FL_KEYBOARD, focus-window) Three sorts of messages are then sent back by the system to myview: doCommandBySelector:, setMarkedText: and insertText:.
when the Ctrl or Cmd modifiers are used. If not, it also calls [[myview inputContext] handleEvent:theEvent]. All 3 messages eventually produce Fl::handle(FL_KEYBOARD, win) calls.
The performKeyEquivalent: method returns YES when the keystroke has been handled and NO otherwise, which allows The doCommandBySelector: message allows to process events such as new-line, backward delete, escape, tab.
shortcuts of the system menu to be processed. Three sorts of messages are then sent back by the system to myview: The message setMarkedText: is sent when marked text, that is, temporary text that gets replaced later
doCommandBySelector:, setMarkedText: and insertText:. All 3 messages eventually produce Fl::handle(FL_KEYBOARD, win) calls.
The doCommandBySelector: message allows to process events such as new-line, forward and backward delete, arrows,
escape, tab, F1. The message setMarkedText: is sent when marked text, that is, temporary text that gets replaced later
by some other text, is inserted. This happens when a dead key is pressed, and also by some other text, is inserted. This happens when a dead key is pressed, and also
when entering complex scripts (e.g., Chinese). Fl_Cocoa_Screen_Driver::next_marked_length gives the byte when entering complex scripts (e.g., Chinese). Fl_Cocoa_Screen_Driver::next_marked_length gives the byte
length of marked text before the FL_KEYBOARD event is processed. Fl::compose_state gives this length after this processing. length of marked text before the FL_KEYBOARD event is processed. Fl::compose_state gives this length after this processing.
@@ -2327,33 +2330,23 @@ static void cocoaKeyboardHandler(NSEvent *theEvent)
} }
fl_lock_function(); fl_lock_function();
cocoaKeyboardHandler(theEvent); cocoaKeyboardHandler(theEvent);
BOOL handled;
Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window];
if ( (mods & NSEventModifierFlagControl) || (mods & NSEventModifierFlagCommand) ) { NSString *s = [theEvent characters];
NSString *s = [theEvent characters]; if ( (mods & NSEventModifierFlagShift) && (mods & NSEventModifierFlagCommand) ) {
if ( (mods & NSEventModifierFlagShift) && (mods & NSEventModifierFlagCommand) ) { s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit
s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit
}
[FLView prepareEtext:s];
Fl::compose_state = 0;
handled = Fl::handle(FL_KEYBOARD, w);
if (!handled) {
// detect Ctrl+Command+Space to open character palette, if not used before as shortcut
if ( (mods & NSEventModifierFlagControl) && (mods & NSEventModifierFlagCommand) &&
!(mods & (NSEventModifierFlagShift|NSEventModifierFlagOption)) && [pure isEqualToString:@" "] ) {
[NSApp orderFrontCharacterPalette:self];
}
}
} }
else { [FLView prepareEtext:s];
in_key_event = YES; Fl::compose_state = 0;
need_handle = NO; BOOL handled = Fl::handle(FL_KEYBOARD, w);
handled = [[self inputContext] handleEvent:theEvent]; if (!handled) {
if (need_handle) handled = Fl::handle(FL_KEYBOARD, w); // detect Ctrl+Command+Space to open character palette, if not used before as shortcut
in_key_event = NO; if ( (mods & NSEventModifierFlagControl) && (mods & NSEventModifierFlagCommand) &&
!(mods & (NSEventModifierFlagShift|NSEventModifierFlagOption)) && [pure isEqualToString:@" "] ) {
[NSApp orderFrontCharacterPalette:self];
} }
}
fl_unlock_function(); fl_unlock_function();
return handled; return YES;
} }
- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent
{ {
@@ -3907,7 +3900,7 @@ static PrintWithTitlebarItem *print_with_titlebar_item = NULL;
} }
- (void)terminate:(id)sender - (void)terminate:(id)sender
{ {
[NSApp terminate:sender]; attempt_close_all_windows();
} }
@end @end