BMenu: Inform parent menu when modifier keys change

BMenuWindow: Move submenus over recursively if layout changes.

Fixes #15782

Change-Id: I70e67c3514a3b850f5b5893170b9a34a9176363e
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2324
Haiku-Format: Haiku-format Bot <[email protected]>
Reviewed-by: John Scipione <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
John Scipione
2025-01-26 18:29:22 +00:00
parent bc61d0aef5
commit 5b1284db40
2 changed files with 98 additions and 0 deletions
+7
View File
@@ -499,6 +499,13 @@ BMenu::MessageReceived(BMessage* message)
break;
}
case B_MODIFIERS_CHANGED:
if (fSuper != NULL && fSuper->fState != MENU_STATE_CLOSED) {
// inform parent to update its modifier keys and relayout
BMessenger(fSuper).SendMessage(Window()->CurrentMessage());
}
break;
default:
BView::MessageReceived(message);
break;
+91
View File
@@ -15,6 +15,7 @@
#include <Debug.h>
#include <Menu.h>
#include <MenuItem.h>
#include <Screen.h>
#include <MenuPrivate.h>
#include <WindowPrivate.h>
@@ -41,6 +42,11 @@ public:
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void Draw(BRect updateRect);
virtual void LayoutChanged();
void MoveSubmenusOver(BMenu* menu,
BRect menuFrame,
BRect screenFrame);
private:
friend class BMenuWindow;
@@ -228,6 +234,91 @@ BMenuFrame::Draw(BRect updateRect)
}
void
BMenuFrame::LayoutChanged()
{
if (fMenu == NULL || Window() == NULL)
return BView::LayoutChanged();
// shift child menus over recursively
MoveSubmenusOver(fMenu, fMenu->ConvertToScreen(fMenu->Frame()),
(BScreen(fMenu->Window())).Frame());
BView::LayoutChanged();
}
void
BMenuFrame::MoveSubmenusOver(BMenu* menu, BRect menuFrame, BRect screenFrame)
{
if (menu == NULL)
return;
BMenu* submenu;
BMenuWindow* submenuWindow;
BPoint submenuLoc;
BRect submenuFrame;
int32 itemCount = menu->CountItems();
for (int32 index = 0; index < itemCount; index++) {
submenu = menu->SubmenuAt(index);
if (submenu == NULL || submenu->Window() == NULL)
continue; // not an open submenu, next
submenuWindow = dynamic_cast<BMenuWindow*>(submenu->Window());
if (submenuWindow == NULL)
break; // submenu window was not a BMenuWindow, strange if true
// found an open submenu, get submenu frame
if (submenu->LockLooper()) {
// need to lock looper because we're in a different thread
submenuFrame = submenu->Frame();
submenu->ConvertToScreen(&submenuFrame);
submenu->UnlockLooper();
} else {
// give up
break;
}
// get submenu loc and convert it to screen coords using menu
if (menu->LockLooper()) {
// check if submenu should be displayed right or left of menu
BRect superFrame = submenu->Superitem()->Frame();
if (submenuFrame.right < menuFrame.right)
submenuLoc = superFrame.LeftTop() - BPoint(submenuFrame.Width() + 1, -1);
else
submenuLoc = superFrame.RightTop() + BPoint(1, 1);
menu->ConvertToScreen(&submenuLoc);
submenuFrame.OffsetTo(submenuLoc);
menu->UnlockLooper();
} else {
// give up
break;
}
// move submenu frame into screen bounds vertically
if (submenuFrame.Height() < screenFrame.Height()) {
if (submenuFrame.bottom >= screenFrame.bottom)
submenuLoc.y -= (submenuFrame.bottom - screenFrame.bottom);
else if (submenuFrame.top <= screenFrame.top)
submenuLoc.y += (screenFrame.top - submenuFrame.top);
} else {
// put menu at top of screen, turn on the scroll arrows
submenuLoc.y = 0;
}
// move submenu window into place
submenuWindow->MoveTo(submenuLoc);
// recurse through submenu's submenus
MoveSubmenusOver(submenu, submenuFrame, screenFrame);
// we're done with this menu
break;
}
}
// #pragma mark -