mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
HackStudio: GUI support for setting breakpoints on source code lines
This commit is contained in:
parent
009b4ea3f4
commit
393560d8a2
Notes:
sideshowbarker
2024-07-19 07:19:04 +09:00
Author: https://github.com/itamar8910 Commit: https://github.com/SerenityOS/serenity/commit/393560d8a2d Pull-request: https://github.com/SerenityOS/serenity/pull/1946 Reviewed-by: https://github.com/awesomekling
BIN
Base/res/icons/breakpoint.png
Normal file
BIN
Base/res/icons/breakpoint.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 305 B |
37
DevTools/HackStudio/BreakpointCallback.h
Normal file
37
DevTools/HackStudio/BreakpointCallback.h
Normal file
@ -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;
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -151,8 +151,10 @@ protected:
|
||||
virtual void resize_event(ResizeEvent&) override;
|
||||
virtual void theme_change_event(ThemeChangeEvent&) override;
|
||||
virtual void cursor_did_change() {}
|
||||
Gfx::Rect ruler_content_rect(size_t line) const;
|
||||
|
||||
TextPosition text_position_at(const Gfx::Point&) const;
|
||||
bool ruler_visible() const { return m_ruler_visible; }
|
||||
|
||||
private:
|
||||
friend class TextDocumentLine;
|
||||
@ -183,7 +185,6 @@ private:
|
||||
TextDocumentLine& current_line() { return line(m_cursor.line()); }
|
||||
const TextDocumentLine& current_line() const { return line(m_cursor.line()); }
|
||||
int ruler_width() const;
|
||||
Gfx::Rect ruler_content_rect(size_t line) const;
|
||||
void toggle_selection_if_needed_for_event(const KeyEvent&);
|
||||
void delete_selection();
|
||||
void did_update_selection();
|
||||
|
Loading…
Reference in New Issue
Block a user