Skip to content

Commit b4de5ac

Browse files
committed
HackStudio: Start working on a "Locator", much like Qt Creator has
Pressing Ctrl+K will now open the little locator command line at the bottom of the window. Right now it can only be used to jump quickly to a file.
1 parent f0da3ab commit b4de5ac

File tree

4 files changed

+202
-0
lines changed

4 files changed

+202
-0
lines changed

DevTools/HackStudio/Locator.cpp

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include "Locator.h"
2+
#include "Project.h"
3+
#include <LibGUI/GBoxLayout.h>
4+
#include <LibGUI/GTableView.h>
5+
#include <LibGUI/GTextBox.h>
6+
#include <LibGUI/GWindow.h>
7+
8+
extern RefPtr<Project> g_project;
9+
extern void open_file(const String&);
10+
static RefPtr<GraphicsBitmap> s_file_icon;
11+
12+
class LocatorSuggestionModel final : public GModel {
13+
public:
14+
explicit LocatorSuggestionModel(Vector<String>&& suggestions)
15+
: m_suggestions(move(suggestions))
16+
{
17+
}
18+
19+
enum Column {
20+
Icon,
21+
Name,
22+
__Column_Count,
23+
};
24+
virtual int row_count(const GModelIndex& = GModelIndex()) const override { return m_suggestions.size(); }
25+
virtual int column_count(const GModelIndex& = GModelIndex()) const override { return Column::__Column_Count; }
26+
virtual GVariant data(const GModelIndex& index, Role role = Role::Display) const override
27+
{
28+
auto& suggestion = m_suggestions.at(index.row());
29+
if (role == Role::Display) {
30+
if (index.column() == Column::Name)
31+
return suggestion;
32+
if (index.column() == Column::Icon) {
33+
if (!s_file_icon) {
34+
s_file_icon = GraphicsBitmap::load_from_file("/res/icons/16x16/filetype-unknown.png");
35+
}
36+
return *s_file_icon;
37+
}
38+
}
39+
return {};
40+
}
41+
virtual void update() override {};
42+
43+
private:
44+
Vector<String> m_suggestions;
45+
};
46+
47+
class LocatorTextBox final : public GTextBox {
48+
C_OBJECT(LocatorTextBox)
49+
public:
50+
virtual ~LocatorTextBox() override {}
51+
52+
Function<void()> on_up;
53+
Function<void()> on_down;
54+
55+
virtual void keydown_event(GKeyEvent& event) override
56+
{
57+
if (event.key() == Key_Up)
58+
on_up();
59+
else if (event.key() == Key_Down)
60+
on_down();
61+
62+
GTextBox::keydown_event(event);
63+
}
64+
65+
private:
66+
LocatorTextBox(GWidget* parent)
67+
: GTextBox(parent)
68+
{
69+
}
70+
};
71+
72+
Locator::Locator(GWidget* parent)
73+
: GWidget(parent)
74+
{
75+
set_layout(make<GBoxLayout>(Orientation::Vertical));
76+
set_size_policy(SizePolicy::Fill, SizePolicy::Fixed);
77+
set_preferred_size(0, 20);
78+
m_textbox = LocatorTextBox::construct(this);
79+
m_textbox->on_change = [this] {
80+
update_suggestions();
81+
};
82+
m_textbox->on_escape_pressed = [this] {
83+
m_popup_window->hide();
84+
};
85+
m_textbox->on_up = [this] {
86+
GModelIndex new_index = m_suggestion_view->selection().first();
87+
if (new_index.is_valid())
88+
new_index = m_suggestion_view->model()->index(new_index.row() - 1);
89+
else
90+
new_index = m_suggestion_view->model()->index(0);
91+
92+
if (new_index.is_valid())
93+
m_suggestion_view->selection().set(new_index);
94+
};
95+
m_textbox->on_down = [this] {
96+
GModelIndex new_index = m_suggestion_view->selection().first();
97+
if (new_index.is_valid())
98+
new_index = m_suggestion_view->model()->index(new_index.row() + 1);
99+
else
100+
new_index = m_suggestion_view->model()->index(0);
101+
102+
if (new_index.is_valid())
103+
m_suggestion_view->selection().set(new_index);
104+
};
105+
m_textbox->on_return_pressed = [this] {
106+
auto selected_index = m_suggestion_view->selection().first();
107+
if (!selected_index.is_valid())
108+
return;
109+
auto filename_index = m_suggestion_view->model()->index(selected_index.row(), LocatorSuggestionModel::Column::Name);
110+
auto filename = m_suggestion_view->model()->data(filename_index, GModel::Role::Display).to_string();
111+
open_file(filename);
112+
close();
113+
};
114+
115+
m_popup_window = GWindow::construct();
116+
// FIXME: This is obviously not a tooltip window, but it's the closest thing to what we want atm.
117+
m_popup_window->set_window_type(GWindowType::Tooltip);
118+
m_popup_window->set_rect(0, 0, 500, 200);
119+
120+
m_suggestion_view = GTableView::construct(nullptr);
121+
m_suggestion_view->set_size_columns_to_fit_content(true);
122+
m_suggestion_view->set_headers_visible(false);
123+
m_popup_window->set_main_widget(m_suggestion_view);
124+
}
125+
126+
Locator::~Locator()
127+
{
128+
}
129+
130+
void Locator::open()
131+
{
132+
m_textbox->set_focus(true);
133+
if (!m_textbox->text().is_empty()) {
134+
m_textbox->select_all();
135+
m_popup_window->show();
136+
}
137+
}
138+
139+
void Locator::close()
140+
{
141+
m_popup_window->hide();
142+
}
143+
144+
void Locator::keydown_event(GKeyEvent& event)
145+
{
146+
GWidget::keydown_event(event);
147+
}
148+
149+
void Locator::update_suggestions()
150+
{
151+
auto typed_text = m_textbox->text();
152+
Vector<String> suggestions;
153+
g_project->for_each_text_file([&](auto& file) {
154+
if (file.name().contains(typed_text))
155+
suggestions.append(file.name());
156+
});
157+
dbg() << "I have " << suggestions.size() << " suggestion(s):";
158+
for (auto& s : suggestions) {
159+
dbg() << " " << s;
160+
}
161+
162+
m_suggestion_view->set_model(adopt(*new LocatorSuggestionModel(move(suggestions))));
163+
164+
m_popup_window->move_to(screen_relative_rect().top_left().translated(0, -m_popup_window->height()));
165+
dbg() << "Popup rect: " << m_popup_window->rect();
166+
m_popup_window->show();
167+
}

DevTools/HackStudio/Locator.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#pragma once
2+
3+
#include <LibGUI/GWidget.h>
4+
5+
class LocatorTextBox;
6+
class GTableView;
7+
8+
class Locator final : public GWidget {
9+
C_OBJECT(Locator)
10+
public:
11+
virtual ~Locator() override;
12+
13+
void open();
14+
void close();
15+
16+
private:
17+
virtual void keydown_event(GKeyEvent&) override;
18+
19+
void update_suggestions();
20+
21+
explicit Locator(GWidget* parent);
22+
23+
RefPtr<LocatorTextBox> m_textbox;
24+
RefPtr<GWindow> m_popup_window;
25+
RefPtr<GTableView> m_suggestion_view;
26+
};

DevTools/HackStudio/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ OBJS = \
99
CppLexer.o \
1010
Editor.o \
1111
EditorWrapper.o \
12+
Locator.o \
1213
main.o
1314

1415
APP = HackStudio

DevTools/HackStudio/main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "Editor.h"
33
#include "EditorWrapper.h"
44
#include "FindInFilesWidget.h"
5+
#include "Locator.h"
56
#include "Project.h"
67
#include "TerminalWrapper.h"
78
#include <LibCore/CFile.h>
@@ -184,6 +185,12 @@ int main(int argc, char** argv)
184185
auto terminal_wrapper = TerminalWrapper::construct(nullptr);
185186
tab_widget->add_widget("Console", terminal_wrapper);
186187

188+
auto locator = Locator::construct(widget);
189+
190+
auto open_locator_action = GAction::create("Open Locator...", { Mod_Ctrl, Key_K }, [&](auto&) {
191+
locator->open();
192+
});
193+
187194
auto menubar = make<GMenuBar>();
188195
auto app_menu = make<GMenu>("HackStudio");
189196
app_menu->add_action(save_action);
@@ -223,6 +230,7 @@ int main(int argc, char** argv)
223230

224231
auto view_menu = make<GMenu>("View");
225232
view_menu->add_action(hide_action_tabs_action);
233+
view_menu->add_action(open_locator_action);
226234
menubar->add_menu(move(view_menu));
227235

228236
auto small_icon = GraphicsBitmap::load_from_file("/res/icons/16x16/app-hack-studio.png");

0 commit comments

Comments
 (0)