Tracker: Live updating shiftable menu items

Live updating menu classes listen for B_MODIFIERS_CHANGED
messages and update the item label and shortcut on Shift
or Control.

-- File --
Create link => Create relative link
Move to Trash => Delete
Cut => Cut more
Copy => Copy more
Paste => Paste links
Identify => Force identify

-- Window --
Close => Close all
Open parent enable/disable on Control to open root window

-- Window => Arrange by/Desktop --
Clean up => Clean up all

Create TLiveMenu and TLivePopUpMenu classes that inherit from
BMenu and BPopUpMenu respectively.

Create TLiveFileMenu, TLivePosePopUpMenu, TLiveWindowMenu and
TLiveWindowPopUpMenu subclasses for the File and Window menu
and context menus respectively.

Create TLiveMixin to share methods between menus. Pass in parent
window and use window shortcuts to enable/disable "Open parent".

Change-Id: I5dfbd4d468fad02894f1f31aa08d1abf630a4b5d
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2293
Reviewed-by: John Scipione <jscipione@gmail.com>
This commit is contained in:
John Scipione 2020-11-28 21:09:06 -05:00
parent 5b1284db40
commit 683100d673
5 changed files with 445 additions and 12 deletions

View File

@ -79,6 +79,7 @@ All rights reserved.
#include "FavoritesMenu.h" #include "FavoritesMenu.h"
#include "FindPanel.h" #include "FindPanel.h"
#include "IconMenuItem.h" #include "IconMenuItem.h"
#include "LiveMenu.h"
#include "MimeTypes.h" #include "MimeTypes.h"
#include "Model.h" #include "Model.h"
#include "MountMenu.h" #include "MountMenu.h"
@ -634,13 +635,13 @@ void
BContainerWindow::AddContextMenus() BContainerWindow::AddContextMenus()
{ {
// create context sensitive menus // create context sensitive menus
fPoseContextMenu = new BPopUpMenu("PoseContext", false, false); fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false);
AddPoseContextMenu(fPoseContextMenu); AddPoseContextMenu(fPoseContextMenu);
fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false); fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false);
AddVolumeContextMenu(fVolumeContextMenu); AddVolumeContextMenu(fVolumeContextMenu);
fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false);
AddWindowContextMenu(fWindowContextMenu); AddWindowContextMenu(fWindowContextMenu);
fDropContextMenu = new BPopUpMenu("DropContext", false, false); fDropContextMenu = new BPopUpMenu("DropContext", false, false);
@ -714,11 +715,11 @@ BContainerWindow::RepopulateMenus()
} }
delete fPoseContextMenu; delete fPoseContextMenu;
fPoseContextMenu = new BPopUpMenu("PoseContext", false, false); fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false);
AddPoseContextMenu(fPoseContextMenu); AddPoseContextMenu(fPoseContextMenu);
delete fWindowContextMenu; delete fWindowContextMenu;
fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false);
AddWindowContextMenu(fWindowContextMenu); AddWindowContextMenu(fWindowContextMenu);
} }
@ -1695,11 +1696,11 @@ BContainerWindow::IsShowing(const entry_ref* entry) const
void void
BContainerWindow::AddMenus() BContainerWindow::AddMenus()
{ {
fFileMenu = new BMenu(B_TRANSLATE("File")); fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this);
AddFileMenu(fFileMenu); AddFileMenu(fFileMenu);
fMenuBar->AddItem(fFileMenu); fMenuBar->AddItem(fFileMenu);
fWindowMenu = new BMenu(B_TRANSLATE("Window")); fWindowMenu = new TLiveWindowMenu(B_TRANSLATE("Window"), this);
fMenuBar->AddItem(fWindowMenu); fMenuBar->AddItem(fWindowMenu);
AddWindowMenu(fWindowMenu); AddWindowMenu(fWindowMenu);
@ -3458,7 +3459,7 @@ BContainerWindow::NewArrangeByMenu()
ASSERT(fAttrMenu); ASSERT(fAttrMenu);
// create a new "Arrange by >" menu // create a new "Arrange by >" menu
BMenu* menu = new BMenu(Shortcuts()->ArrangeByLabel()); TLiveArrangeByMenu* menu = new TLiveArrangeByMenu(Shortcuts()->ArrangeByLabel(), this);
// add Attributes items to "Arrange by >" // add Attributes items to "Arrange by >"
BMenuItem* item; BMenuItem* item;

View File

@ -75,6 +75,7 @@ All rights reserved.
#include "FSUtils.h" #include "FSUtils.h"
#include "FavoritesMenu.h" #include "FavoritesMenu.h"
#include "IconMenuItem.h" #include "IconMenuItem.h"
#include "LiveMenu.h"
#include "MimeTypes.h" #include "MimeTypes.h"
#include "NavMenu.h" #include "NavMenu.h"
#include "Shortcuts.h" #include "Shortcuts.h"
@ -851,7 +852,7 @@ TFilePanel::AddMenus()
{ {
// File // File
fFileMenu = new BMenu(B_TRANSLATE("File")); fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this);
AddFileMenu(fFileMenu); AddFileMenu(fFileMenu);
fMenuBar->AddItem(fFileMenu); fMenuBar->AddItem(fFileMenu);
@ -1124,19 +1125,19 @@ TFilePanel::RepopulateMenus()
fMenuBar->RemoveItem(fFileMenu); fMenuBar->RemoveItem(fFileMenu);
delete fFileMenu; delete fFileMenu;
if (ShouldAddMenus()) { if (ShouldAddMenus()) {
fFileMenu = new BMenu(B_TRANSLATE("File")); fFileMenu = new TLiveFileMenu(B_TRANSLATE("File"), this);
TFilePanel::AddFileMenu(fFileMenu); AddFileMenu(fFileMenu);
fMenuBar->AddItem(fFileMenu, 0); fMenuBar->AddItem(fFileMenu, 0);
} }
} }
delete fPoseContextMenu; delete fPoseContextMenu;
fPoseContextMenu = new BPopUpMenu("PoseContext", false, false); fPoseContextMenu = new TLivePosePopUpMenu("PoseContext", this, false, false);
fPoseContextMenu->SetFont(be_plain_font); fPoseContextMenu->SetFont(be_plain_font);
TFilePanel::AddPoseContextMenu(fPoseContextMenu); TFilePanel::AddPoseContextMenu(fPoseContextMenu);
delete fWindowContextMenu; delete fWindowContextMenu;
fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); fWindowContextMenu = new TLiveWindowPopUpMenu("WindowContext", this, false, false);
fWindowContextMenu->SetFont(be_plain_font); fWindowContextMenu->SetFont(be_plain_font);
TFilePanel::AddWindowContextMenu(fWindowContextMenu); TFilePanel::AddWindowContextMenu(fWindowContextMenu);
} }

View File

@ -52,6 +52,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
IconCache.cpp IconCache.cpp
IconMenuItem.cpp IconMenuItem.cpp
InfoWindow.cpp InfoWindow.cpp
LiveMenu.cpp
MimeTypeList.cpp MimeTypeList.cpp
MiniMenuField.cpp MiniMenuField.cpp
Model.cpp Model.cpp

View File

@ -0,0 +1,309 @@
/*
* Copyright 2020-2024 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
*/
#include "LiveMenu.h"
#include <string.h>
#include <Application.h>
#include <Locale.h>
#include <MenuItem.h>
#include <Window.h>
#include "Commands.h"
#include "TrackerSettings.h"
// #pragma mark - TLiveMixin
TLiveMixin::TLiveMixin(const BContainerWindow* window)
{
fWindow = window;
}
void
TLiveMixin::UpdateFileMenu(BMenu* menu)
{
if (menu == NULL)
return;
if (menu->Window()->LockLooper()) {
int32 itemCount = menu->CountItems();
for (int32 index = 0; index < itemCount; index++) {
BMenuItem* item = menu->ItemAt(index);
if (item == NULL || item->Message() == NULL)
continue;
switch (item->Message()->what) {
// Move to Trash/Delete
case kMoveSelectionToTrash:
case kDeleteSelection:
if (!fWindow->Shortcuts()->IsTrash())
fWindow->Shortcuts()->UpdateMoveToTrashItem(item);
break;
// Create link/Create relative link
case kCreateLink:
case kCreateRelativeLink:
fWindow->Shortcuts()->UpdateCreateLinkItem(item);
break;
// Cut/Cut more
case B_CUT:
case kCutMoreSelectionToClipboard:
fWindow->Shortcuts()->UpdateCutItem(item);
break;
// Copy/Copy more
case B_COPY:
case kCopyMoreSelectionToClipboard:
fWindow->Shortcuts()->UpdateCopyItem(item);
break;
// Paste/Paste links
case B_PASTE:
case kPasteLinksFromClipboard:
fWindow->Shortcuts()->UpdatePasteItem(item);
break;
// Identify/Force identify
case kIdentifyEntry:
fWindow->Shortcuts()->UpdateIdentifyItem(item);
break;
}
}
menu->Window()->UnlockLooper();
}
}
void
TLiveMixin::UpdateWindowMenu(BMenu* menu)
{
if (menu == NULL)
return;
// update using the window version of TShortcuts class
if (menu->Window()->LockLooper()) {
int32 itemCount = menu->CountItems();
for (int32 index = 0; index < itemCount; index++) {
BMenuItem* item = menu->ItemAt(index);
if (item == NULL || item->Message() == NULL)
continue;
switch (item->Message()->what) {
// Clean up/Clean up all
case kCleanup:
case kCleanupAll:
fWindow->Shortcuts()->UpdateCleanupItem(item);
break;
// Open parent
case kOpenParentDir:
fWindow->Shortcuts()->UpdateOpenParentItem(item);
break;
// Close/Close all
case B_QUIT_REQUESTED:
case kCloseAllWindows:
fWindow->Shortcuts()->UpdateCloseItem(item);
break;
}
}
menu->Window()->UnlockLooper();
}
}
// #pragma mark - TLiveMenu
TLiveMenu::TLiveMenu(const char* label)
:
BMenu(label)
{
}
TLiveMenu::~TLiveMenu()
{
}
void
TLiveMenu::MessageReceived(BMessage* message)
{
if (message != NULL && message->what == B_MODIFIERS_CHANGED)
Update();
else
BMenu::MessageReceived(message);
}
void
TLiveMenu::Update()
{
// hook method
}
// #pragma mark - TLivePopUpMenu
TLivePopUpMenu::TLivePopUpMenu(const char* label, bool radioMode, bool labelFromMarked,
menu_layout layout)
:
BPopUpMenu(label, radioMode, labelFromMarked, layout)
{
}
TLivePopUpMenu::~TLivePopUpMenu()
{
}
void
TLivePopUpMenu::MessageReceived(BMessage* message)
{
if (message != NULL && message->what == B_MODIFIERS_CHANGED)
Update();
else
BMenu::MessageReceived(message);
}
void
TLivePopUpMenu::Update()
{
// hook method
}
// #pragma mark - TLiveArrangeByMenu
TLiveArrangeByMenu::TLiveArrangeByMenu(const char* label, const BContainerWindow* window)
:
TLiveMenu(label),
TLiveMixin(window)
{
}
TLiveArrangeByMenu::~TLiveArrangeByMenu()
{
}
void
TLiveArrangeByMenu::Update()
{
// Clean up/Clean up all
TShortcuts().UpdateCleanupItem(TShortcuts().FindItem(this, kCleanup, kCleanupAll));
}
// #pragma mark - TLiveFileMenu
TLiveFileMenu::TLiveFileMenu(const char* label, const BContainerWindow* window)
:
TLiveMenu(label),
TLiveMixin(window)
{
}
TLiveFileMenu::~TLiveFileMenu()
{
}
void
TLiveFileMenu::Update()
{
UpdateFileMenu(this);
}
// #pragma mark - TLivePosePopUpMenu
TLivePosePopUpMenu::TLivePosePopUpMenu(const char* label, const BContainerWindow* window,
bool radioMode, bool labelFromMarked, menu_layout layout)
:
TLivePopUpMenu(label, radioMode, labelFromMarked, layout),
TLiveMixin(window)
{
}
TLivePosePopUpMenu::~TLivePosePopUpMenu()
{
}
void
TLivePosePopUpMenu::Update()
{
UpdateFileMenu(this);
}
// #pragma mark - TLiveWindowMenu
TLiveWindowMenu::TLiveWindowMenu(const char* label, const BContainerWindow* window)
:
TLiveMenu(label),
TLiveMixin(window)
{
}
TLiveWindowMenu::~TLiveWindowMenu()
{
}
void
TLiveWindowMenu::Update()
{
UpdateWindowMenu(this);
}
// #pragma mark - TLiveWindowPopUpMenu
TLiveWindowPopUpMenu::TLiveWindowPopUpMenu(const char* label, const BContainerWindow* window,
bool radioMode, bool labelFromMarked, menu_layout layout)
:
TLivePopUpMenu(label, radioMode, labelFromMarked, layout),
TLiveMixin(window)
{
}
TLiveWindowPopUpMenu::~TLiveWindowPopUpMenu()
{
}
void
TLiveWindowPopUpMenu::Update()
{
UpdateWindowMenu(this);
}

121
src/kits/tracker/LiveMenu.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright 2020-2024 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
*/
#ifndef _LIVE_MENU_H
#define _LIVE_MENU_H
#include <Menu.h>
#include <PopUpMenu.h>
#include "ContainerWindow.h"
#include "Shortcuts.h"
namespace BPrivate {
// mixin class for sharing virtual methods
struct TLiveMixin {
TLiveMixin(const BContainerWindow* window);
virtual void UpdateFileMenu(BMenu* menu);
virtual void UpdateWindowMenu(BMenu* menu);
private:
const BContainerWindow* fWindow;
};
class TLiveMenu : public BMenu {
public:
TLiveMenu(const char* label);
virtual ~TLiveMenu();
virtual void MessageReceived(BMessage* message);
protected:
virtual void Update();
};
class TLivePopUpMenu : public BPopUpMenu {
public:
TLivePopUpMenu(const char* label,
bool radioMode = true,
bool labelFromMarked = true,
menu_layout layout = B_ITEMS_IN_COLUMN);
virtual ~TLivePopUpMenu();
virtual void MessageReceived(BMessage* message);
protected:
virtual void Update();
private:
const BContainerWindow* fWindow;
};
class TLiveArrangeByMenu: public TLiveMenu, public TLiveMixin {
public:
TLiveArrangeByMenu(const char* label, const BContainerWindow* window);
virtual ~TLiveArrangeByMenu();
protected:
virtual void Update();
};
class TLiveFileMenu : public TLiveMenu, public TLiveMixin {
public:
TLiveFileMenu(const char* label, const BContainerWindow* window);
virtual ~TLiveFileMenu();
protected:
virtual void Update();
};
class TLivePosePopUpMenu : public TLivePopUpMenu, public TLiveMixin {
public:
TLivePosePopUpMenu(const char* label, const BContainerWindow* window,
bool radioMode = true, bool labelFromMarked = true,
menu_layout layout = B_ITEMS_IN_COLUMN);
virtual ~TLivePosePopUpMenu();
protected:
virtual void Update();
};
class TLiveWindowMenu : public TLiveMenu, public TLiveMixin {
public:
TLiveWindowMenu(const char* label, const BContainerWindow* window);
virtual ~TLiveWindowMenu();
protected:
virtual void Update();
};
class TLiveWindowPopUpMenu : public TLivePopUpMenu, public TLiveMixin {
public:
TLiveWindowPopUpMenu(const char* label, const BContainerWindow* window,
bool radioMode = true, bool labelFromMarked = true,
menu_layout layout = B_ITEMS_IN_COLUMN);
virtual ~TLiveWindowPopUpMenu();
protected:
virtual void Update();
};
} // namespace BPrivate
using namespace BPrivate;
#endif // _LIVE_MENU_H