Skip to content

Commit 0328331

Browse files
committed
Command Palette enhancements
1 parent c68f8d4 commit 0328331

5 files changed

Lines changed: 210 additions & 24 deletions

File tree

ui/commandpalette.h

Lines changed: 197 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <QtWidgets/QLineEdit>
66
#include <QtWidgets/QFrame>
77
#include <QtCore/QPointer>
8+
#include <QtCore/QThread>
89
#include <QtWidgets/QStyledItemDelegate>
910
#include <vector>
1011
#include "action.h"
@@ -21,14 +22,132 @@
2122
*/
2223
struct BINARYNINJAUIAPI CommandListItem
2324
{
25+
enum CommandListItemType
26+
{
27+
// Index is serialized so only to add to the end
28+
Help,
29+
UIAction,
30+
OpenTab,
31+
NavigationHistory,
32+
RecentFile,
33+
RecentProject,
34+
ProjectFile,
35+
Expression,
36+
RecentExpression,
37+
Function,
38+
Symbol,
39+
Type,
40+
String,
41+
DerivedString,
42+
LastItemType
43+
};
44+
45+
CommandListItemType type;
2446
QString name;
47+
QString secondary;
2548
QString shortcut;
26-
QString action;
49+
QString extraSearchableText;
50+
QVariant action;
51+
Qt::TextElideMode secondaryElide = Qt::ElideRight;
52+
bool addToRecents = true;
53+
bool includeUnfiltered = true;
54+
int score = 0;
2755
};
2856

57+
58+
/*!
59+
60+
\ingroup commandpalette
61+
*/
62+
struct BINARYNINJAUIAPI CommandListItemSearchInfo
63+
{
64+
std::unordered_set<CommandListItem::CommandListItemType> types;
65+
QString name;
66+
QString searchName;
67+
std::string prefix;
68+
QKeySequence shortcut;
69+
70+
static std::vector<CommandListItemSearchInfo> GetSearchTypes();
71+
};
72+
73+
2974
class CommandPalette;
3075
class CommandListFilter;
3176

77+
class CommandListGenerateWorker : public QObject
78+
{
79+
Q_OBJECT
80+
81+
BinaryViewRef m_view;
82+
bool m_aborted;
83+
int m_request;
84+
std::shared_ptr<std::atomic_int> m_latestRequest;
85+
86+
bool m_pending;
87+
std::vector<CommandListItem> m_pendingItems;
88+
std::mutex m_pendingItemsMutex;
89+
90+
public:
91+
explicit CommandListGenerateWorker(QObject* parent, std::shared_ptr<std::atomic_int> request, const UIActionContext& context);
92+
virtual ~CommandListGenerateWorker();
93+
94+
Q_SIGNALS:
95+
void dataFetched(int request, const std::vector<CommandListItem>& items);
96+
void noMoreDataToFetch(int request);
97+
void workFinished(int request);
98+
99+
public Q_SLOTS:
100+
void fetchMore();
101+
void start();
102+
void abort();
103+
};
104+
105+
106+
class CommandListScoreWorker : public QObject
107+
{
108+
public:
109+
enum OrderStrategy
110+
{
111+
DefaultOrder,
112+
ScoreOrder
113+
};
114+
115+
private:
116+
Q_OBJECT
117+
118+
bool m_aborted;
119+
int m_request;
120+
std::shared_ptr<std::atomic_int> m_latestRequest;
121+
QString m_filter;
122+
std::shared_ptr<std::vector<CommandListItem>> m_items;
123+
std::vector<int> m_oldItemScores;
124+
OrderStrategy m_strategy;
125+
126+
bool m_pending;
127+
std::vector<std::pair<CommandListItem*, int>> m_pendingItems;
128+
std::mutex m_pendingItemsMutex;
129+
130+
public:
131+
explicit CommandListScoreWorker(
132+
QObject* parent,
133+
std::shared_ptr<std::atomic_int> request,
134+
QString filter,
135+
std::shared_ptr<std::vector<CommandListItem>> items,
136+
OrderStrategy orderStrategy
137+
);
138+
virtual ~CommandListScoreWorker();
139+
140+
Q_SIGNALS:
141+
void dataFetched(int request, const std::vector<std::pair<CommandListItem*, int>>& items);
142+
void noMoreDataToFetch(int request);
143+
void workFinished(int request);
144+
145+
public Q_SLOTS:
146+
void fetchMore();
147+
void start();
148+
void abort();
149+
};
150+
32151

33152
/*!
34153
@@ -54,17 +173,40 @@ class BINARYNINJAUIAPI CommandListModel : public QAbstractItemModel
54173
{
55174
Q_OBJECT
56175

57-
std::vector<CommandListItem> m_items;
58-
std::vector<CommandListItem> m_allItems;
176+
UIActionHandler* m_handler;
177+
UIActionContext m_context;
59178

60-
std::vector<QString> m_recentItems;
61-
size_t m_maxRecentItems;
179+
std::shared_ptr<std::vector<CommandListItem>> m_allItems;
180+
std::vector<CommandListItem*> m_displayItems; // pointers into m_allItems
181+
QString m_filterText;
182+
bool m_updatesPaused;
62183

63-
bool isFilterMatch(const QString& name, const QString& filter);
64-
int getFilterMatchScore(const QString& name, const QString& filter);
184+
std::vector<CommandListItem> m_recentItems;
65185

66-
public:
67-
CommandListModel(QWidget* parent, const std::vector<CommandListItem>& items);
186+
QThread m_generateWorkerThread;
187+
CommandListGenerateWorker* m_generateWorker;
188+
std::shared_ptr<std::atomic_int> m_generateWorkerRequest;
189+
QTimer m_generateFetchTimer;
190+
bool m_generateMoreToFetch;
191+
192+
QTimer m_scoreTimer;
193+
QMetaObject::Connection m_scoreTimerConnection;
194+
195+
QThread m_scoreWorkerThread;
196+
CommandListScoreWorker* m_scoreWorker;
197+
std::shared_ptr<std::atomic_int> m_scoreWorkerRequest;
198+
QTimer m_scoreFetchTimer;
199+
bool m_scoreMoreToFetch;
200+
201+
void loadRecentItems();
202+
std::vector<CommandListItem> generateFastCommandList();
203+
void sortCommandList(std::vector<CommandListItem>& list);
204+
void mergeCommandList(std::vector<CommandListItem>& output, std::vector<CommandListItem>&& input);
205+
void startScoreListThread();
206+
207+
public:
208+
CommandListModel(QWidget* parent);
209+
~CommandListModel();
68210

69211
virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override;
70212
virtual QModelIndex parent(const QModelIndex& i) const override;
@@ -73,10 +215,33 @@ class BINARYNINJAUIAPI CommandListModel : public QAbstractItemModel
73215
virtual int columnCount(const QModelIndex& parent) const override;
74216
virtual QVariant data(const QModelIndex& i, int role) const override;
75217

76-
QString getActionForItem(int row);
218+
CommandListItem getItem(int row);
77219
void setFilterText(const QString& text);
78-
size_t getRecentPosition(const QString& name) const;
79-
void addRecentItem(const QString& name);
220+
size_t getRecentPosition(const CommandListItem& item) const;
221+
void addRecentItem(const CommandListItem& item);
222+
223+
void clearCommandList();
224+
void generateCommandList(UIActionHandler* handler, const UIActionContext& context);
225+
void cancelGenerateCommandList();
226+
227+
void scoreCommandList(CommandListScoreWorker::OrderStrategy strategy);
228+
void cancelScoreCommandList();
229+
230+
void pauseCommandListUpdates();
231+
void unpauseCommandListUpdates();
232+
233+
void updateDisplayedItems();
234+
235+
private Q_SLOTS:
236+
void generateFetch();
237+
void itemsGenerated(int request, const std::vector<CommandListItem>& items);
238+
void itemsFinishedGenerating(int request);
239+
void generateWorkFinished(int request);
240+
241+
void scoreFetch();
242+
void itemsScored(int request, const std::vector<std::pair<CommandListItem*, int>>& items);
243+
void itemsFinishedScoring(int request);
244+
void scoreWorkFinished(int request);
80245
};
81246

82247
/*!
@@ -92,14 +257,21 @@ class BINARYNINJAUIAPI CommandList : public QListView
92257
CommandListFilter* m_filter;
93258

94259
public:
95-
CommandList(CommandPalette* parent, const std::vector<CommandListItem>& items);
260+
CommandList(CommandPalette* parent);
96261
void setFilter(CommandListFilter* filter) { m_filter = filter; }
97262
void setFilterText(const QString& text);
98263

99-
QString getActionForItem(int row);
264+
CommandListItem getItem(int row);
100265

101266
QModelIndex index(int row, int col, const QModelIndex& parent = QModelIndex()) const;
102-
void addRecentItem(const QString& name);
267+
void addRecentItem(const CommandListItem& item);
268+
269+
void clearCommandList();
270+
void generateCommandList(UIActionHandler* handler, const UIActionContext& context);
271+
void cancelGenerateCommandList();
272+
273+
void pauseCommandListUpdates();
274+
void unpauseCommandListUpdates();
103275

104276
protected:
105277
virtual void keyPressEvent(QKeyEvent* event) override;
@@ -126,7 +298,9 @@ class BINARYNINJAUIAPI CommandListFilter : public QLineEdit
126298
protected:
127299
bool event(QEvent* event) override;
128300
virtual void keyPressEvent(QKeyEvent* event) override;
301+
virtual void focusInEvent(QFocusEvent* event) override;
129302
virtual void focusOutEvent(QFocusEvent* event) override;
303+
virtual void paintEvent(QPaintEvent* event) override;
130304
};
131305

132306
/*!
@@ -139,26 +313,31 @@ class BINARYNINJAUIAPI CommandPalette : public QFrame
139313

140314
UIActionHandler* m_handler;
141315
UIActionContext m_context;
316+
142317
QPointer<QWidget> m_previousWidget;
143318

144319
CommandListFilter* m_filter;
145320
CommandList* m_list;
321+
std::optional<CommandListItem> m_savedTop;
146322

147323
bool m_executing;
148324

149-
std::vector<CommandListItem> getCommandList();
150325
void init();
151326

152327
public:
153-
CommandPalette(QWidget* parent, UIActionHandler* handler);
154-
CommandPalette(QWidget* parent, UIActionHandler* handler, const UIActionContext& context);
328+
CommandPalette(QWidget* parent);
155329

330+
void openWithInput(const QString& text);
156331
void focusInput();
157332

333+
void clearCommandList();
334+
void generateCommandList(UIActionHandler* handler, const UIActionContext& context);
335+
158336
//! Activate the focused item, or topmost item if there is no selection.
159337
void activateFocusedItem();
160338
void selectFirstItem();
161339
void close(bool restoreFocus = true);
340+
void activateItem(const CommandListItem& item);
162341

163342
private Q_SLOTS:
164343
void itemClicked(const QModelIndex& idx);

ui/flowgraphwidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class BINARYNINJAUIAPI FlowGraphHistoryEntry : public HistoryEntry
6363
void setCurrentAddress(uint64_t a) { m_addr = a; }
6464
void setHighlightTokenState(const HighlightTokenState& state) { m_highlight = state; }
6565

66+
virtual QString getDescription() const override;
6667
virtual Json::Value serialize() const override;
6768
virtual bool deserialize(const Json::Value& value) override;
6869
};

ui/linearview.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class BINARYNINJAUIAPI LinearViewHistoryEntry : public HistoryEntry
111111
void setInFunction(bool inFunc) { m_inFunc = inFunc; }
112112
void setHighlightTokenState(const HighlightTokenState& state) { m_highlight = state; }
113113

114+
virtual QString getDescription() const override;
114115
virtual Json::Value serialize() const override;
115116
virtual bool deserialize(const Json::Value& value) override;
116117
};

ui/tokenizedtextview.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class BINARYNINJAUIAPI TokenizedTextViewHistoryEntry : public HistoryEntry
3333
void setCursorLine(size_t line) { m_cursorLine = line; }
3434
void setHighlightTokenState(const HighlightTokenState& state) { m_highlight = state; }
3535

36+
virtual QString getDescription() const override;
3637
virtual Json::Value serialize() const override;
3738
virtual bool deserialize(const Json::Value& value) override;
3839
};

ui/viewframe.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class BINARYNINJAUIAPI HistoryEntry : public BinaryNinja::RefCountObject
8585
QString getViewType() const { return m_viewType; }
8686
void setViewType(const QString& type) { m_viewType = type; }
8787

88+
virtual QString getDescription() const;
89+
8890
/*!
8991
Serialize to json representation
9092
\return Json representation of history entry. In the Python api, this must be a dict.
@@ -392,12 +394,6 @@ class BINARYNINJAUIAPI ViewFrame : public QWidget
392394
bool gestureEvent(QGestureEvent* event);
393395

394396
void setView(QWidget* view);
395-
/*!
396-
Load one history entry from json representation
397-
\param json Json rep of history entry
398-
\return Entry, if successful, else nullptr
399-
*/
400-
BinaryNinja::Ref<HistoryEntry> deserializeHistoryEntry(const Json::Value& json);
401397

402398
bool tryMainSymbolsNavigation();
403399

@@ -464,6 +460,14 @@ class BINARYNINJAUIAPI ViewFrame : public QWidget
464460

465461
void updateFonts();
466462
void updateTheme();
463+
std::vector<BinaryNinja::Ref<HistoryEntry>> getBackHistory();
464+
std::vector<BinaryNinja::Ref<HistoryEntry>> getForwardHistory();
465+
/*!
466+
Load one history entry from json representation
467+
\param json Json rep of history entry
468+
\return Entry, if successful, else nullptr
469+
*/
470+
BinaryNinja::Ref<HistoryEntry> deserializeHistoryEntry(const Json::Value& json);
467471
void addHistoryEntry();
468472
/*!
469473
Parse history entries from the raw data associated with a BinaryView, loading them into the back/forward

0 commit comments

Comments
 (0)