BWindow: Use a SplayTree to store shortcuts.

This makes for much faster lookups, and avoids the need
to allocate an array.

sizeof(BWindow) is unchanged (on x86_64, 504; on x86, 376)
so this should not break ABI/API.

Quick benchmark in Tracker:
501 lookups, before: ~488 usec
501 lookups, after: ~284 usec

Change-Id: Ica2c728f0c5957380add53e14a826a4aade52df0
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10720
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Augustin Cavalier
2026-04-13 19:24:34 +00:00
committed by waddlesplash
parent fd2113c61a
commit ad37e9a395
2 changed files with 96 additions and 46 deletions
+3 -1
View File
@@ -381,7 +381,9 @@ private:
uint32 _unused1;
BMenuBar* fKeyMenuBar;
BButton* fDefaultButton;
BList fShortcuts;
void* fShortcuts;
void* _unused2;
int32 _unused3[5];
int32 fTopViewToken;
bool fUpdateRequested;
bool fOffscreen;
+93 -45
View File
@@ -24,6 +24,7 @@
#include <Autolock.h>
#include <Bitmap.h>
#include <Button.h>
#include <Debug.h>
#include <Deskbar.h>
#include <DirectMessageTarget.h>
#include <FindDirectory.h>
@@ -54,6 +55,7 @@
#include <binary_compatibility/Interface.h>
#include <input_globals.h>
#include <tracker_private.h>
#include <util/SplayTree.h>
//#define DEBUG_WIN
@@ -89,36 +91,6 @@ struct BWindow::unpack_cookie {
};
class BWindow::Shortcut {
public:
Shortcut(uint32 key, uint32 modifiers,
BMenuItem* item);
Shortcut(uint32 key, uint32 modifiers,
BMessage* message, BHandler* target);
~Shortcut();
bool Matches(uint32 key, uint32 preparedModifiers) const;
uint32 Key() const { return fKey; };
uint32 Modifiers() const;
uint32 PreparedModifiers() const { return fPreparedModifiers; };
BMenuItem* MenuItem() const { return fMenuItem; }
BMessage* Message() const { return fMessage; }
BHandler* Target() const { return fTarget; }
static uint32 AllowedModifiers();
static uint32 PrepareKey(uint32 key);
static uint32 PrepareModifiers(uint32 modifiers);
private:
uint32 fKey;
uint32 fPreparedModifiers;
BMenuItem* fMenuItem;
BMessage* fMessage;
BHandler* fTarget;
};
using BPrivate::gDefaultTokens;
using BPrivate::MenuPrivate;
@@ -242,6 +214,73 @@ BWindow::unpack_cookie::unpack_cookie()
// #pragma mark - BWindow::Shortcut
class BWindow::Shortcut : public SplayTreeLink<BWindow::Shortcut> {
public:
struct TreeKey {
uint32 key;
uint32 prepared_modifiers;
TreeKey(uint32 key, uint32 preparedModifiers)
: key(key), prepared_modifiers(preparedModifiers) {}
};
struct SplayTreeDefinition {
typedef TreeKey KeyType;
typedef BWindow::Shortcut NodeType;
static KeyType GetKey(const NodeType* node)
{
return TreeKey(node->Key(), node->PreparedModifiers());
}
static SplayTreeLink<NodeType>* GetLink(NodeType* node)
{
return node;
}
static int Compare(const KeyType& key, const NodeType* node)
{
return node->Compare(key.key, key.prepared_modifiers);
}
};
typedef ::SplayTree<SplayTreeDefinition> SplayTree;
public:
static SplayTree* CastToTree(void** storage)
{
STATIC_ASSERT(sizeof(void*) == sizeof(SplayTree));
return reinterpret_cast<SplayTree*>(storage);
}
public:
Shortcut(uint32 key, uint32 modifiers,
BMenuItem* item);
Shortcut(uint32 key, uint32 modifiers,
BMessage* message, BHandler* target);
~Shortcut();
int Compare(uint32 key, uint32 preparedModifiers) const;
uint32 Key() const { return fKey; };
uint32 Modifiers() const;
uint32 PreparedModifiers() const { return fPreparedModifiers; };
BMenuItem* MenuItem() const { return fMenuItem; }
BMessage* Message() const { return fMessage; }
BHandler* Target() const { return fTarget; }
static uint32 AllowedModifiers();
static uint32 PrepareKey(uint32 key);
static uint32 PrepareModifiers(uint32 modifiers);
private:
uint32 fKey;
uint32 fPreparedModifiers;
BMenuItem* fMenuItem;
BMessage* fMessage;
BHandler* fTarget;
};
BWindow::Shortcut::Shortcut(uint32 key, uint32 modifiers, BMenuItem* item)
:
fKey(PrepareKey(key)),
@@ -272,10 +311,14 @@ BWindow::Shortcut::~Shortcut()
}
bool
BWindow::Shortcut::Matches(uint32 key, uint32 preparedModifiers) const
int
BWindow::Shortcut::Compare(uint32 key, uint32 preparedModifiers) const
{
return fKey == key && fPreparedModifiers == preparedModifiers;
if (fKey != key)
return fKey - key;
if (fPreparedModifiers != preparedModifiers)
return fPreparedModifiers - preparedModifiers;
return 0;
}
@@ -426,9 +469,12 @@ BWindow::~BWindow()
delete fTopView;
// remove all remaining shortcuts
int32 shortcutCount = fShortcuts.CountItems();
for (int32 i = 0; i < shortcutCount; i++)
delete (Shortcut*)fShortcuts.ItemAtFast(i);
Shortcut::SplayTree* shortcuts = Shortcut::CastToTree(&fShortcuts);
while (!shortcuts->IsEmpty()) {
Shortcut* shortcut = shortcuts->Root();
shortcuts->Remove(shortcut);
delete shortcut;
}
// TODO: release other dynamically-allocated objects
free(fTitle);
@@ -1709,7 +1755,7 @@ BWindow::_AddShortcut(uint32* _key, uint32* _modifiers, BMenuItem* item)
*_key = shortcut->Key();
*_modifiers = shortcut->Modifiers();
fShortcuts.AddItem(shortcut);
Shortcut::CastToTree(&fShortcuts)->Insert(shortcut);
}
@@ -1733,7 +1779,7 @@ BWindow::AddShortcut(uint32 key, uint32 modifiers, BMessage* message, BHandler*
// removes the shortcut if it already exists!
RemoveShortcut(shortcut->Key(), shortcut->Modifiers());
fShortcuts.AddItem(shortcut);
Shortcut::CastToTree(&fShortcuts)->Insert(shortcut);
}
@@ -1748,7 +1794,7 @@ void
BWindow::RemoveShortcut(uint32 key, uint32 modifiers)
{
Shortcut* shortcut = _FindShortcut(key, modifiers);
if (shortcut != NULL && fShortcuts.RemoveItem(shortcut))
if (shortcut != NULL && Shortcut::CastToTree(&fShortcuts)->Remove(shortcut))
delete shortcut;
else if (key == 'Q' && modifiers == B_COMMAND_KEY)
fNoQuitShortcut = true; // the quit shortcut is a fake shortcut
@@ -2770,6 +2816,7 @@ BWindow::_InitData(BRect frame, const char* title, window_look look,
fLastMouseMovedView = NULL;
fKeyMenuBar = NULL;
fDefaultButton = NULL;
fShortcuts = NULL;
// Shortcut 'Q' is handled in _HandleKeyDown() directly, as its message
// get sent to the application, and not one of our handlers.
@@ -3918,13 +3965,14 @@ BWindow::_FindShortcut(uint32 key, uint32 modifiers)
key = Shortcut::PrepareKey(key);
uint32 preparedModifiers = Shortcut::PrepareModifiers(modifiers);
int32 shortcutCount = fShortcuts.CountItems();
for (int32 index = 0; index < shortcutCount; index++) {
Shortcut* shortcut = (Shortcut*)fShortcuts.ItemAt(index);
if (shortcut != NULL && shortcut->Matches(key, preparedModifiers))
return shortcut;
}
Shortcut::SplayTree* shortcuts = Shortcut::CastToTree(&fShortcuts);
Shortcut* shortcut = shortcuts->FindClosest(Shortcut::TreeKey(key, preparedModifiers),
false, true);
if (shortcut == NULL)
return NULL;
if (shortcut->Compare(key, preparedModifiers) == 0)
return shortcut;
return NULL;
}