mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibGUI+HackStudio: Make gutter a first class element
Gutter -- a space left of the text, before the ruler -- is not a part of the ruler, nor should it be treated as such. This commit implements gutter handling in LibGUI::TextEditor as part of mild cleaning up of the gutter handling (breakpoint icons) in HackStudio's Editor. This commit also enables separate theming of the gutter.
This commit is contained in:
parent
920b4d730e
commit
8501617fcb
Notes:
sideshowbarker
2024-07-18 12:19:15 +09:00
Author: https://github.com/dmitrii-ubskii Commit: https://github.com/SerenityOS/serenity/commit/8501617fcb7 Pull-request: https://github.com/SerenityOS/serenity/pull/8016
19 changed files with 103 additions and 25 deletions
|
@ -47,6 +47,8 @@ Link=#88c
|
|||
ActiveLink=#c88
|
||||
VisitedLink=#c8c
|
||||
PlaceholderText=#171717
|
||||
Gutter=#0f0f0f
|
||||
GutterBorder=#2f2f2f
|
||||
Ruler=#0f0f0f
|
||||
RulerBorder=#2f2f2f
|
||||
RulerActiveText=white
|
||||
|
|
|
@ -43,6 +43,8 @@ RubberBandBorder=#594fbf
|
|||
Link=#0000ff
|
||||
ActiveLink=#ee0000
|
||||
VisitedLink=#551a8b
|
||||
Gutter=#aeb2c3
|
||||
GutterBorder=#5d6069
|
||||
Ruler=#aeb2c3
|
||||
RulerBorder=#5d6069
|
||||
RulerActiveText=#5d6069
|
||||
|
|
|
@ -39,6 +39,8 @@ Link=#88c
|
|||
ActiveLink=#c88
|
||||
VisitedLink=#c8c
|
||||
PlaceholderText=#2e2f30
|
||||
Gutter=#505050
|
||||
GutterBorder=#666666
|
||||
Ruler=#505050
|
||||
RulerBorder=#666666
|
||||
RulerActiveText=white
|
||||
|
|
|
@ -47,6 +47,8 @@ RubberBandBorder=#6e2209
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d4d0c8
|
||||
GutterBorder=#404040
|
||||
Ruler=#d4d0c8
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -47,6 +47,8 @@ RubberBandBorder=#6e2209
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d5ccbb
|
||||
GutterBorder=#404040
|
||||
Ruler=#d5ccbb
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -39,6 +39,8 @@ RubberBandBorder=black
|
|||
Link=#0000b0
|
||||
ActiveLink=#2020d0
|
||||
VisitedLink=#2000b0
|
||||
Gutter=#808080
|
||||
GutterBorder=black
|
||||
Ruler=#808080
|
||||
RulerBorder=black
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -47,6 +47,8 @@ RubberBandBorder=#598dc6
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d4d0c8
|
||||
GutterBorder=#404040
|
||||
Ruler=#d4d0c8
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -39,6 +39,8 @@ RubberBandBorder=#4c566a
|
|||
Link=#7e9dbc
|
||||
ActiveLink=#95adc5
|
||||
VisitedLink=#3b4e68
|
||||
Gutter=#434c5e
|
||||
GutterBorder=#3b4252
|
||||
Ruler=#434c5e
|
||||
RulerBorder=#3b4252
|
||||
RulerActiveText=#95adc5
|
||||
|
|
|
@ -47,6 +47,8 @@ RubberBandBorder=#50096e
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d4d0c8
|
||||
GutterBorder=#404040
|
||||
Ruler=#d4d0c8
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -43,6 +43,8 @@ RubberBandBorder=#09226e
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d4d0c8
|
||||
GutterBorder=#404040
|
||||
Ruler=#d4d0c8
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -43,6 +43,8 @@ RubberBandBorder=black
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d4d0c8
|
||||
GutterBorder=#404040
|
||||
Ruler=#d4d0c8
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -39,6 +39,8 @@ RubberBandBorder=#6e2209
|
|||
Link=blue
|
||||
ActiveLink=red
|
||||
VisitedLink=magenta
|
||||
Gutter=#d4d0c8
|
||||
GutterBorder=#404040
|
||||
Ruler=#d4d0c8
|
||||
RulerBorder=#404040
|
||||
RulerActiveText=#404040
|
||||
|
|
|
@ -43,6 +43,8 @@ RubberBandBorder=#007f7f
|
|||
Link=#88c
|
||||
ActiveLink=#c88
|
||||
VisitedLink=#c8c
|
||||
Gutter=#aeb2c3
|
||||
GutterBorder=#5d6069
|
||||
Ruler=#aeb2c3
|
||||
RulerBorder=#5d6069
|
||||
RulerActiveText=#5d6069
|
||||
|
|
|
@ -66,6 +66,8 @@ Editor::Editor()
|
|||
});
|
||||
add_custom_context_menu_action(*m_evaluate_expression_action);
|
||||
add_custom_context_menu_action(*m_move_execution_to_line_action);
|
||||
|
||||
set_gutter_visible(true);
|
||||
}
|
||||
|
||||
Editor::~Editor()
|
||||
|
@ -95,15 +97,9 @@ void Editor::focusout_event(GUI::FocusEvent& event)
|
|||
GUI::TextEditor::focusout_event(event);
|
||||
}
|
||||
|
||||
Gfx::IntRect Editor::breakpoint_icon_rect(size_t line_number) const
|
||||
Gfx::IntRect Editor::gutter_icon_rect(size_t line_number) const
|
||||
{
|
||||
auto ruler_line_rect = ruler_content_rect(line_number);
|
||||
|
||||
auto scroll_value = vertical_scrollbar().value();
|
||||
ruler_line_rect = ruler_line_rect.translated({ 0, -scroll_value });
|
||||
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 };
|
||||
return gutter_content_rect(line_number).translated(ruler_width() + gutter_width() + frame_thickness(), -vertical_scrollbar().value());
|
||||
}
|
||||
|
||||
void Editor::paint_event(GUI::PaintEvent& event)
|
||||
|
@ -122,7 +118,7 @@ void Editor::paint_event(GUI::PaintEvent& event)
|
|||
painter.draw_rect(rect, palette().selection());
|
||||
}
|
||||
|
||||
if (ruler_visible()) {
|
||||
if (gutter_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 : breakpoint_lines()) {
|
||||
|
@ -130,11 +126,11 @@ void Editor::paint_event(GUI::PaintEvent& event)
|
|||
continue;
|
||||
}
|
||||
const auto& icon = breakpoint_icon_bitmap();
|
||||
painter.blit(breakpoint_icon_rect(line).center(), icon, icon.rect());
|
||||
painter.blit(gutter_icon_rect(line).top_left(), icon, icon.rect());
|
||||
}
|
||||
if (execution_position().has_value()) {
|
||||
const auto& icon = current_position_icon_bitmap();
|
||||
painter.blit(breakpoint_icon_rect(execution_position().value()).center(), icon, icon.rect());
|
||||
painter.blit(gutter_icon_rect(execution_position().value()).top_left(), icon, icon.rect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +375,7 @@ void Editor::set_execution_position(size_t line_number)
|
|||
{
|
||||
code_document().set_execution_position(line_number);
|
||||
scroll_position_into_view({ line_number, 0 });
|
||||
update(breakpoint_icon_rect(line_number));
|
||||
update(gutter_icon_rect(line_number));
|
||||
}
|
||||
|
||||
void Editor::clear_execution_position()
|
||||
|
@ -389,7 +385,7 @@ void Editor::clear_execution_position()
|
|||
}
|
||||
size_t previous_position = execution_position().value();
|
||||
code_document().clear_execution_position();
|
||||
update(breakpoint_icon_rect(previous_position));
|
||||
update(gutter_icon_rect(previous_position));
|
||||
}
|
||||
|
||||
const Gfx::Bitmap& Editor::breakpoint_icon_bitmap()
|
||||
|
|
|
@ -68,7 +68,7 @@ private:
|
|||
void on_navigatable_link_click(const GUI::TextDocumentSpan&);
|
||||
void on_identifier_click(const GUI::TextDocumentSpan&);
|
||||
|
||||
Gfx::IntRect breakpoint_icon_rect(size_t line_number) const;
|
||||
Gfx::IntRect gutter_icon_rect(size_t line_number) const;
|
||||
static const Gfx::Bitmap& breakpoint_icon_bitmap();
|
||||
static const Gfx::Bitmap& current_position_icon_bitmap();
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ void TextEditor::update_content_size()
|
|||
content_width = max(frame_inner_rect().width(), content_width);
|
||||
|
||||
set_content_size({ content_width, content_height });
|
||||
set_size_occupied_by_fixed_elements({ ruler_width(), 0 });
|
||||
set_size_occupied_by_fixed_elements({ ruler_width() + gutter_width(), 0 });
|
||||
}
|
||||
|
||||
TextPosition TextEditor::text_position_at_content_position(const Gfx::IntPoint& content_position) const
|
||||
|
@ -195,7 +195,7 @@ TextPosition TextEditor::text_position_at(const Gfx::IntPoint& widget_position)
|
|||
{
|
||||
auto content_position = widget_position;
|
||||
content_position.translate_by(horizontal_scrollbar().value(), vertical_scrollbar().value());
|
||||
content_position.translate_by(-(m_horizontal_content_padding + ruler_width()), 0);
|
||||
content_position.translate_by(-(m_horizontal_content_padding + ruler_width() + gutter_width()), 0);
|
||||
content_position.translate_by(-frame_thickness(), -frame_thickness());
|
||||
return text_position_at_content_position(content_position);
|
||||
}
|
||||
|
@ -339,10 +339,17 @@ int TextEditor::ruler_width() const
|
|||
if (!m_ruler_visible)
|
||||
return 0;
|
||||
int line_count_digits = static_cast<int>(log10(line_count())) + 1;
|
||||
constexpr size_t padding = 20;
|
||||
constexpr size_t padding = 5;
|
||||
return line_count() < 10 ? (line_count_digits + 1) * font().glyph_width('x') + padding : line_count_digits * font().glyph_width('x') + padding;
|
||||
}
|
||||
|
||||
int TextEditor::gutter_width() const
|
||||
{
|
||||
if (!m_gutter_visible)
|
||||
return 0;
|
||||
return line_height(); // square gutter
|
||||
}
|
||||
|
||||
Gfx::IntRect TextEditor::ruler_content_rect(size_t line_index) const
|
||||
{
|
||||
if (!m_ruler_visible)
|
||||
|
@ -355,9 +362,26 @@ Gfx::IntRect TextEditor::ruler_content_rect(size_t line_index) const
|
|||
};
|
||||
}
|
||||
|
||||
Gfx::IntRect TextEditor::gutter_content_rect(size_t line_index) const
|
||||
{
|
||||
if (!m_gutter_visible)
|
||||
return {};
|
||||
return {
|
||||
0 - ruler_width() - gutter_width() + horizontal_scrollbar().value(),
|
||||
line_content_rect(line_index).y(),
|
||||
gutter_width(),
|
||||
line_content_rect(line_index).height()
|
||||
};
|
||||
}
|
||||
|
||||
Gfx::IntRect TextEditor::ruler_rect_in_inner_coordinates() const
|
||||
{
|
||||
return { 0, 0, ruler_width(), height() - height_occupied_by_horizontal_scrollbar() };
|
||||
return { gutter_width(), 0, ruler_width(), height() - height_occupied_by_horizontal_scrollbar() };
|
||||
}
|
||||
|
||||
Gfx::IntRect TextEditor::gutter_rect_in_inner_coordinates() const
|
||||
{
|
||||
return { 0, 0, gutter_width(), height() - height_occupied_by_horizontal_scrollbar() };
|
||||
}
|
||||
|
||||
Gfx::IntRect TextEditor::visible_text_rect_in_inner_coordinates() const
|
||||
|
@ -398,16 +422,22 @@ void TextEditor::paint_event(PaintEvent& event)
|
|||
|
||||
painter.translate(frame_thickness(), frame_thickness());
|
||||
|
||||
auto ruler_rect = ruler_rect_in_inner_coordinates();
|
||||
if (m_gutter_visible) {
|
||||
auto gutter_rect = gutter_rect_in_inner_coordinates();
|
||||
painter.fill_rect(gutter_rect, palette().gutter());
|
||||
if (!m_ruler_visible)
|
||||
painter.draw_line(gutter_rect.top_right(), gutter_rect.bottom_right(), palette().gutter_border());
|
||||
}
|
||||
|
||||
if (m_ruler_visible) {
|
||||
auto ruler_rect = ruler_rect_in_inner_coordinates();
|
||||
painter.fill_rect(ruler_rect, palette().ruler());
|
||||
painter.draw_line(ruler_rect.top_right(), ruler_rect.bottom_right(), palette().ruler_border());
|
||||
}
|
||||
|
||||
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
|
||||
if (m_ruler_visible)
|
||||
painter.translate(ruler_width(), 0);
|
||||
painter.translate(gutter_width(), 0);
|
||||
painter.translate(ruler_width(), 0);
|
||||
|
||||
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();
|
||||
|
@ -428,14 +458,19 @@ void TextEditor::paint_event(PaintEvent& event)
|
|||
}
|
||||
}
|
||||
|
||||
auto text_left = 0;
|
||||
if (m_ruler_visible)
|
||||
text_left = ruler_rect_in_inner_coordinates().right() + 1;
|
||||
else if (m_gutter_visible)
|
||||
text_left = gutter_rect_in_inner_coordinates().right() + 1;
|
||||
text_left += frame_thickness();
|
||||
|
||||
Gfx::IntRect text_clip_rect {
|
||||
(m_ruler_visible ? (ruler_rect_in_inner_coordinates().right() + frame_thickness() + 1) : frame_thickness()),
|
||||
0,
|
||||
frame_thickness(),
|
||||
width() - width_occupied_by_vertical_scrollbar() - ruler_width(),
|
||||
width() - width_occupied_by_vertical_scrollbar() - text_left,
|
||||
height() - height_occupied_by_horizontal_scrollbar()
|
||||
};
|
||||
if (m_ruler_visible)
|
||||
text_clip_rect.translate_by(-ruler_width(), 0);
|
||||
text_clip_rect.translate_by(horizontal_scrollbar().value(), vertical_scrollbar().value());
|
||||
painter.add_clip_rect(text_clip_rect);
|
||||
|
||||
|
@ -1860,6 +1895,15 @@ void TextEditor::set_ruler_visible(bool visible)
|
|||
update();
|
||||
}
|
||||
|
||||
void TextEditor::set_gutter_visible(bool visible)
|
||||
{
|
||||
if (m_gutter_visible == visible)
|
||||
return;
|
||||
m_gutter_visible = visible;
|
||||
recompute_all_visual_lines();
|
||||
update();
|
||||
}
|
||||
|
||||
void TextEditor::undo()
|
||||
{
|
||||
clear_selection();
|
||||
|
|
|
@ -90,6 +90,9 @@ public:
|
|||
bool is_ruler_visible() const { return m_ruler_visible; }
|
||||
void set_ruler_visible(bool);
|
||||
|
||||
bool is_gutter_visible() const { return m_gutter_visible; }
|
||||
void set_gutter_visible(bool);
|
||||
|
||||
void set_icon(const Gfx::Bitmap*);
|
||||
const Gfx::Bitmap* icon() const { return m_icon; }
|
||||
|
||||
|
@ -209,11 +212,14 @@ protected:
|
|||
virtual void theme_change_event(ThemeChangeEvent&) override;
|
||||
virtual void cursor_did_change() { }
|
||||
Gfx::IntRect ruler_content_rect(size_t line) const;
|
||||
Gfx::IntRect gutter_content_rect(size_t line) const;
|
||||
|
||||
TextPosition text_position_at(const Gfx::IntPoint&) const;
|
||||
bool ruler_visible() const { return m_ruler_visible; }
|
||||
bool gutter_visible() const { return m_gutter_visible; }
|
||||
Gfx::IntRect content_rect_for_position(const TextPosition&) const;
|
||||
int ruler_width() const;
|
||||
int gutter_width() const;
|
||||
|
||||
private:
|
||||
friend class TextDocumentLine;
|
||||
|
@ -272,6 +278,7 @@ private:
|
|||
void delete_selection();
|
||||
int content_x_for_position(const TextPosition&) const;
|
||||
Gfx::IntRect ruler_rect_in_inner_coordinates() const;
|
||||
Gfx::IntRect gutter_rect_in_inner_coordinates() const;
|
||||
Gfx::IntRect visible_text_rect_in_inner_coordinates() const;
|
||||
void recompute_all_visual_lines();
|
||||
void ensure_cursor_is_valid();
|
||||
|
@ -302,6 +309,7 @@ private:
|
|||
bool m_cursor_state { true };
|
||||
bool m_in_drag_select { false };
|
||||
bool m_ruler_visible { false };
|
||||
bool m_gutter_visible { false };
|
||||
bool m_has_pending_change_notification { false };
|
||||
bool m_automatic_indentation_enabled { false };
|
||||
WrappingMode m_wrapping_mode { WrappingMode::NoWrap };
|
||||
|
|
|
@ -92,6 +92,8 @@ public:
|
|||
Color hover_highlight() const { return color(ColorRole::HoverHighlight); }
|
||||
Color rubber_band_fill() const { return color(ColorRole::RubberBandFill); }
|
||||
Color rubber_band_border() const { return color(ColorRole::RubberBandBorder); }
|
||||
Color gutter() const { return color(ColorRole::Gutter); }
|
||||
Color gutter_border() const { return color(ColorRole::Gutter); }
|
||||
Color ruler() const { return color(ColorRole::Ruler); }
|
||||
Color ruler_border() const { return color(ColorRole::RulerBorder); }
|
||||
Color ruler_active_text() const { return color(ColorRole::RulerActiveText); }
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace Gfx {
|
|||
C(ButtonText) \
|
||||
C(DesktopBackground) \
|
||||
C(FocusOutline) \
|
||||
C(Gutter) \
|
||||
C(GutterBorder) \
|
||||
C(HighlightWindowBorder1) \
|
||||
C(HighlightWindowBorder2) \
|
||||
C(HighlightWindowTitle) \
|
||||
|
|
Loading…
Reference in a new issue