summaryrefslogtreecommitdiff
path: root/DevTools/HackStudio
diff options
context:
space:
mode:
authorItamar <itamar8910@gmail.com>2020-04-24 23:48:25 +0300
committerAndreas Kling <kling@serenityos.org>2020-04-25 13:16:46 +0200
commit393560d8a2de4b507497786f3203e0c0cebb13c2 (patch)
tree207abdab49a27982d944358b714be64c66ccdd3a /DevTools/HackStudio
parent009b4ea3f418e3cd88a4b66e0d6531b871d270c7 (diff)
downloadserenity-393560d8a2de4b507497786f3203e0c0cebb13c2.zip
HackStudio: GUI support for setting breakpoints on source code lines
Diffstat (limited to 'DevTools/HackStudio')
-rw-r--r--DevTools/HackStudio/BreakpointCallback.h37
-rw-r--r--DevTools/HackStudio/Editor.cpp75
-rw-r--r--DevTools/HackStudio/Editor.h16
-rw-r--r--DevTools/HackStudio/EditorWrapper.cpp4
-rw-r--r--DevTools/HackStudio/EditorWrapper.h6
5 files changed, 133 insertions, 5 deletions
diff --git a/DevTools/HackStudio/BreakpointCallback.h b/DevTools/HackStudio/BreakpointCallback.h
new file mode 100644
index 0000000000..80d1f9495d
--- /dev/null
+++ b/DevTools/HackStudio/BreakpointCallback.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+#include <AK/Function.h>
+#include <AK/String.h>
+#include <AK/Types.h>
+
+enum class BreakpointChange {
+ Added,
+ Removed,
+};
+
+typedef Function<void(const String& file, size_t line, BreakpointChange)> BreakpointChangeCallback;
diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp
index b95d0e5a1f..2813750949 100644
--- a/DevTools/HackStudio/Editor.cpp
+++ b/DevTools/HackStudio/Editor.cpp
@@ -31,6 +31,7 @@
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <LibGUI/Application.h>
+#include <LibGUI/Label.h>
#include <LibGUI/Painter.h>
#include <LibGUI/ScrollBar.h>
#include <LibGUI/SyntaxHighlighter.h>
@@ -79,12 +80,20 @@ void Editor::focusout_event(Core::Event& event)
GUI::TextEditor::focusout_event(event);
}
+Gfx::Rect Editor::breakpoint_icon_rect(size_t line_number) const
+{
+ auto ruler_line_rect = ruler_content_rect(line_number);
+ auto center = ruler_line_rect.center().translated({ ruler_line_rect.width() - 10, -line_spacing() - 3 });
+ constexpr int size = 32;
+ return { center.x() - size / 2, center.y() - size / 2, size, size };
+}
+
void Editor::paint_event(GUI::PaintEvent& event)
{
GUI::TextEditor::paint_event(event);
+ GUI::Painter painter(*this);
if (is_focused()) {
- GUI::Painter painter(*this);
painter.add_clip_rect(event.rect());
auto rect = frame_inner_rect();
@@ -95,8 +104,26 @@ void Editor::paint_event(GUI::PaintEvent& event)
painter.draw_rect(rect, palette().selection());
}
- if (m_hovering_editor)
+ if (m_hovering_lines_ruler)
+ window()->set_override_cursor(GUI::StandardCursor::Arrow);
+ else if (m_hovering_editor)
window()->set_override_cursor(m_hovering_link && m_holding_ctrl ? GUI::StandardCursor::Hand : GUI::StandardCursor::IBeam);
+
+ if (ruler_visible()) {
+ size_t first_visible_line = text_position_at(event.rect().top_left()).line();
+ size_t last_visible_line = text_position_at(event.rect().bottom_right()).line();
+ for (size_t line : m_breakpoint_lines) {
+ if (line < first_visible_line || line > last_visible_line) {
+ continue;
+ }
+ const auto& icon = breakpoint_icon_bitmap();
+ painter.blit(breakpoint_icon_rect(line).center(), icon, icon.rect());
+ }
+ if (m_execution_position.has_value()) {
+ const auto& icon = current_position_icon_bitmap();
+ painter.blit(breakpoint_icon_rect(m_execution_position.value()).center(), icon, icon.rect());
+ }
+ }
}
static HashMap<String, String>& man_paths()
@@ -188,6 +215,9 @@ void Editor::mousemove_event(GUI::MouseEvent& event)
if (!highlighter)
return;
+ auto ruler_line_rect = ruler_content_rect(text_position.line());
+ m_hovering_lines_ruler = (event.position().x() < ruler_line_rect.width());
+
bool hide_tooltip = true;
bool is_over_link = false;
@@ -237,12 +267,23 @@ void Editor::mousedown_event(GUI::MouseEvent& event)
return;
}
+ auto text_position = text_position_at(event.position());
+ auto ruler_line_rect = ruler_content_rect(text_position.line());
+ if (event.position().x() < ruler_line_rect.width()) {
+ if (!m_breakpoint_lines.contains_slow(text_position.line())) {
+ m_breakpoint_lines.append(text_position.line());
+ on_breakpoint_change(wrapper().filename_label().text(), text_position.line(), BreakpointChange::Added);
+ } else {
+ m_breakpoint_lines.remove_first_matching([&](size_t line) { return line == text_position.line(); });
+ on_breakpoint_change(wrapper().filename_label().text(), text_position.line(), BreakpointChange::Removed);
+ }
+ }
+
if (!(event.modifiers() & Mod_Ctrl)) {
GUI::TextEditor::mousedown_event(event);
return;
}
- auto text_position = text_position_at(event.position());
if (!text_position.is_valid()) {
GUI::TextEditor::mousedown_event(event);
return;
@@ -338,3 +379,31 @@ void Editor::navigate_to_include_if_available(String path)
on_open(it->value);
}
+
+void Editor::set_execution_position(size_t line_number)
+{
+ m_execution_position = line_number;
+ update(breakpoint_icon_rect(line_number));
+}
+
+void Editor::clear_execution_position()
+{
+ if (!m_execution_position.has_value()) {
+ return;
+ }
+ size_t previous_position = m_execution_position.value();
+ m_execution_position = {};
+ update(breakpoint_icon_rect(previous_position));
+}
+
+const Gfx::Bitmap& Editor::breakpoint_icon_bitmap()
+{
+ static auto bitmap = Gfx::Bitmap::load_from_file("/res/icons/breakpoint.png");
+ return *bitmap;
+}
+
+const Gfx::Bitmap& Editor::current_position_icon_bitmap()
+{
+ static auto bitmap = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png");
+ return *bitmap;
+}
diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h
index d188e3655c..e4197da613 100644
--- a/DevTools/HackStudio/Editor.h
+++ b/DevTools/HackStudio/Editor.h
@@ -26,6 +26,8 @@
#pragma once
+#include "BreakpointCallback.h"
+#include <AK/Optional.h>
#include <LibGUI/TextEditor.h>
#include <LibWeb/Forward.h>
@@ -42,6 +44,12 @@ public:
EditorWrapper& wrapper();
const EditorWrapper& wrapper() const;
+ const Vector<size_t>& breakpoint_lines() const { return m_breakpoint_lines; }
+ void set_execution_position(size_t line_number);
+ void clear_execution_position();
+
+ BreakpointChangeCallback on_breakpoint_change;
+
private:
virtual void focusin_event(Core::Event&) override;
virtual void focusout_event(Core::Event&) override;
@@ -56,6 +64,10 @@ private:
void show_documentation_tooltip_if_available(const String&, const Gfx::Point& screen_location);
void navigate_to_include_if_available(String);
+ Gfx::Rect breakpoint_icon_rect(size_t line_number) const;
+ static const Gfx::Bitmap& breakpoint_icon_bitmap();
+ static const Gfx::Bitmap& current_position_icon_bitmap();
+
explicit Editor();
RefPtr<GUI::Window> m_documentation_tooltip_window;
@@ -65,4 +77,8 @@ private:
bool m_hovering_editor { false };
bool m_hovering_link { false };
bool m_holding_ctrl { false };
+ bool m_hovering_lines_ruler { false };
+
+ Vector<size_t> m_breakpoint_lines;
+ Optional<size_t> m_execution_position;
};
diff --git a/DevTools/HackStudio/EditorWrapper.cpp b/DevTools/HackStudio/EditorWrapper.cpp
index 5a638f98e3..1ee9eefb68 100644
--- a/DevTools/HackStudio/EditorWrapper.cpp
+++ b/DevTools/HackStudio/EditorWrapper.cpp
@@ -35,7 +35,7 @@
extern RefPtr<EditorWrapper> g_current_editor_wrapper;
extern Function<void(String)> g_open_file;
-EditorWrapper::EditorWrapper()
+EditorWrapper::EditorWrapper(BreakpointChangeCallback breakpoint_change_callback)
{
set_layout<GUI::VerticalBoxLayout>();
@@ -72,6 +72,8 @@ EditorWrapper::EditorWrapper()
m_editor->on_open = [this](String path) {
g_open_file(path);
};
+
+ m_editor->on_breakpoint_change = move(breakpoint_change_callback);
}
EditorWrapper::~EditorWrapper()
diff --git a/DevTools/HackStudio/EditorWrapper.h b/DevTools/HackStudio/EditorWrapper.h
index 9cf8947222..31aa679590 100644
--- a/DevTools/HackStudio/EditorWrapper.h
+++ b/DevTools/HackStudio/EditorWrapper.h
@@ -26,6 +26,9 @@
#pragma once
+#include "BreakpointCallback.h"
+#include <AK/Function.h>
+#include <AK/Vector.h>
#include <LibGUI/Widget.h>
#include <string.h>
@@ -40,11 +43,12 @@ public:
const Editor& editor() const { return *m_editor; }
GUI::Label& filename_label() { return *m_filename_label; }
+ const GUI::Label& filename_label() const { return *m_filename_label; }
void set_editor_has_focus(Badge<Editor>, bool);
private:
- explicit EditorWrapper();
+ explicit EditorWrapper(BreakpointChangeCallback);
RefPtr<GUI::Label> m_filename_label;
RefPtr<GUI::Label> m_cursor_label;