cocoa: adding additional tracking for menus. If Modal window is called, all main menu tracking is being cancelled

(cherry picked from commit 3c3b14de01)
This commit is contained in:
Dmitry Boyarintsev 2021-10-09 22:28:43 -04:00 committed by Maxim Ganetsky
parent 927f2884d3
commit 30d615e33a
2 changed files with 76 additions and 4 deletions

View File

@ -831,6 +831,8 @@ begin
if not Assigned(Modals) then Modals := TList.Create;
MenuTrackCancelAll();
// If a modal menu has it's menu, then SetMainMenu has already been called
// (Show is called for modal windows prior to ShowModal. Show triggers Activate and Active is doing MainMenu)
if not hasMenu then begin

View File

@ -91,6 +91,7 @@ type
// of the menu. While LCL allows to modify the menu contents when the submenu
// is about to be activated.
procedure menuNeedsUpdate(AMenu: NSMenu); message 'menuNeedsUpdate:';
procedure menuDidClose(AMenu: NSMenu); message 'menuDidClose:';
//procedure menuDidClose(AMenu: NSMenu); message 'menuDidClose:';
function worksWhenModal: LCLObjCBoolean; message 'worksWhenModal';
end;
@ -171,18 +172,75 @@ function AllocCocoaMenu(const atitle: string = ''): TCocoaMenu;
function LCLMenuItemInit(item: NSMenuItem; const atitle: string; ashortCut: TShortCut): id;
function LCLMenuItemInit(item: NSMenuItem; const atitle: string; VKKey: Word = 0; State: TShiftState = []): id;
procedure MenuTrackStarted(mn: NSMenu);
procedure MenuTrackEnded(mn: NSMenu);
procedure MenuTrackCancelAll;
implementation
uses
CocoaInt;
var
isMenuEnabled : Boolean = true;
// menuTrack is needed due to the LCL architecture vs Cocoa menu handling.
//
// (below "Modal" refers to LCL "modal" state. Not verified with Cocoa modal)
//
// All Menu hangling in Cocoa is done within a "tracking" loop. (similar to Modal even loop)
// The major issue, is that WS code never knows that a menu was clicked,
// outside of the tracking loop. (The delegate is being notified already within the loop)
//
// If LCL handler is calling for any modal window at the time,
// the menu tracking should stop, and the model window is the only be processed
//
// In order to track "opened" menus, menuTrack list is used.
// If modal window is called MenuTrackCancelAll() will terminate all openned sub-menus
//
// The issue of the conflict between "Menu tracking" and "Modal tracking"
// is only affecting Node-Menus (menu items with submenus)
// because we call LM_ACTIVATE within tracking loop.
// The Leaf-menuitems (menu items w/o submenuis) are calling "itemSElected" (LM_ACTIVATE)
// when the tracking loop is over
//
// See topic: https://forum.lazarus.freepascal.org/index.php/topic,56419.0.html
menuTrack : TList = nil;
procedure MenuTrackStarted(mn: NSMenu);
begin
if not Assigned(menuTrack) then menuTrack:=TList.Create;
menuTrack.Add(mn);
end;
procedure MenuTrackEnded(mn: NSMenu);
var
i : integer;
begin
i := menuTrack.IndexOf(mn);
if i>=0 then
menuTrack.Delete(i);
end;
procedure MenuTrackCancelAll;
var
i : integer;
mn : NSMenu;
begin
if not Assigned(menuTrack) then Exit;
for i:=menuTrack.Count-1 downto 0 do begin
mn := NSMenu(menuTrack[i]);
if Assigned(mn) then
mn.cancelTracking;
end;
menuTrack.Clear;
end;
function LCLMenuItemInit(item: NSMenuItem; const atitle: string; ashortCut: TShortCut): id;
var
key : NSString;
mask : NSUInteger;
begin
ShortcutToKeyEquivalent(ashortCut, key, mask);
Result := item.initWithTitle_action_keyEquivalent(
ControlTitleToNSStr(Atitle),
objcselector('lclItemSelected:'), // Selector is Hard-coded, that's why it's LCLMenuItemInit
@ -406,6 +464,10 @@ end;
procedure TCocoaMenuItem.menuNeedsUpdate(AMenu: NSMenu);
begin
if not Assigned(menuItemCallback) then Exit;
if not isMenuEnabled then Exit;
MenuTrackStarted(AMenu);
if (menu.isKindOfClass(TCocoaMenu)) then
begin
// Issue #37789
@ -419,6 +481,11 @@ begin
menuItemCallback.ItemSelected;
end;
procedure TCocoaMenuItem.menuDidClose(AMenu: NSMenu);
begin
MenuTrackEnded(AMenu);
end;
function TCocoaMenuItem.worksWhenModal: LCLObjCBoolean;
begin
// refer to NSMenuItem.target (Apple) documentation
@ -968,9 +1035,6 @@ begin
end;
end;
var
isMenuEnabled : Boolean = true;
function ToggleAppMenu(ALogicalEnabled: Boolean): Boolean;
begin
Result := isMenuEnabled;
@ -978,4 +1042,10 @@ begin
isMenuEnabled := ALogicalEnabled;
end;
initialization
finalization
MenuTrackCancelAll;
if Assigned(menuTrack) then menuTrack.Free;
end.