From 05f5d0dda3e27dcbbb5ad4060052859b9c821cf8 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 2 Jan 2021 20:31:45 +0100 Subject: [PATCH] LibGfx: Add Gfx::TextAttributes (and use it in GUI::TextDocumentSpan) --- .../Spreadsheet/CellSyntaxHighlighter.cpp | 32 ++++--- DevTools/HackStudio/Editor.cpp | 10 +-- Libraries/LibGUI/CppSyntaxHighlighter.cpp | 4 +- Libraries/LibGUI/GMLSyntaxHighlighter.cpp | 4 +- Libraries/LibGUI/INISyntaxHighlighter.cpp | 4 +- Libraries/LibGUI/JSSyntaxHighlighter.cpp | 4 +- Libraries/LibGUI/ShellSyntaxHighlighter.cpp | 86 +++++++++---------- Libraries/LibGUI/SyntaxHighlighter.cpp | 8 +- Libraries/LibGUI/TextDocument.h | 9 +- Libraries/LibGUI/TextEditor.cpp | 8 +- Libraries/LibGfx/TextAttributes.h | 41 +++++++++ 11 files changed, 127 insertions(+), 83 deletions(-) create mode 100644 Libraries/LibGfx/TextAttributes.h diff --git a/Applications/Spreadsheet/CellSyntaxHighlighter.cpp b/Applications/Spreadsheet/CellSyntaxHighlighter.cpp index f2202f1d85..c26d2aa238 100644 --- a/Applications/Spreadsheet/CellSyntaxHighlighter.cpp +++ b/Applications/Spreadsheet/CellSyntaxHighlighter.cpp @@ -47,23 +47,29 @@ void CellSyntaxHighlighter::rehighlight(Gfx::Palette palette) // Highlight the '=' m_editor->document().spans().empend( GUI::TextRange { { 0, 0 }, { 0, 1 } }, - palette.syntax_keyword(), - Optional {}, - false, - false, - false, - nullptr); + Gfx::TextAttributes { + palette.syntax_keyword(), + Optional {}, + false, + false, + }, + nullptr, + false); if (m_cell && m_cell->exception()) { auto range = m_cell->exception()->source_ranges().first(); GUI::TextRange text_range { { range.start.line - 1, range.start.column }, { range.end.line - 1, range.end.column - 1 } }; - m_editor->document().spans().prepend({ text_range, - Color::Black, - Color::Red, - false, - false, - false, - nullptr }); + m_editor->document().spans().prepend( + GUI::TextDocumentSpan { + text_range, + Gfx::TextAttributes { + Color::Black, + Color::Red, + false, + false, + }, + nullptr, + false }); } m_editor->update(); } diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp index ecfbb17e3a..2d9c3b03f6 100644 --- a/DevTools/HackStudio/Editor.cpp +++ b/DevTools/HackStudio/Editor.cpp @@ -226,8 +226,8 @@ void Editor::mousemove_event(GUI::MouseEvent& event) for (auto& span : document().spans()) { if (span.range.contains(m_previous_text_position) && !span.range.contains(text_position)) { - if (highlighter->is_navigatable(span.data) && span.is_underlined) { - span.is_underlined = false; + if (highlighter->is_navigatable(span.data) && span.attributes.underline) { + span.attributes.underline = false; wrapper().editor().update(); } } @@ -243,9 +243,9 @@ void Editor::mousemove_event(GUI::MouseEvent& event) if (highlighter->is_navigatable(span.data)) { is_over_link = true; - bool was_underlined = span.is_underlined; - span.is_underlined = event.modifiers() & Mod_Ctrl; - if (span.is_underlined != was_underlined) { + bool was_underlined = span.attributes.underline; + span.attributes.underline = event.modifiers() & Mod_Ctrl; + if (span.attributes.underline != was_underlined) { wrapper().editor().update(); } } diff --git a/Libraries/LibGUI/CppSyntaxHighlighter.cpp b/Libraries/LibGUI/CppSyntaxHighlighter.cpp index 03c2e6ff59..2a2572ef05 100644 --- a/Libraries/LibGUI/CppSyntaxHighlighter.cpp +++ b/Libraries/LibGUI/CppSyntaxHighlighter.cpp @@ -90,8 +90,8 @@ void CppSyntaxHighlighter::rehighlight(Gfx::Palette palette) span.range.set_start({ token.m_start.line, token.m_start.column }); span.range.set_end({ token.m_end.line, token.m_end.column }); auto style = style_for_token_type(palette, token.m_type); - span.color = style.color; - span.bold = style.bold; + span.attributes.color = style.color; + span.attributes.bold = style.bold; span.is_skippable = token.m_type == Cpp::Token::Type::Whitespace; span.data = reinterpret_cast(token.m_type); spans.append(span); diff --git a/Libraries/LibGUI/GMLSyntaxHighlighter.cpp b/Libraries/LibGUI/GMLSyntaxHighlighter.cpp index a7616b2456..01c4f96c36 100644 --- a/Libraries/LibGUI/GMLSyntaxHighlighter.cpp +++ b/Libraries/LibGUI/GMLSyntaxHighlighter.cpp @@ -72,8 +72,8 @@ void GMLSyntaxHighlighter::rehighlight(Gfx::Palette palette) span.range.set_start({ token.m_start.line, token.m_start.column }); span.range.set_end({ token.m_end.line, token.m_end.column }); auto style = style_for_token_type(palette, token.m_type); - span.color = style.color; - span.bold = style.bold; + span.attributes.color = style.color; + span.attributes.bold = style.bold; span.is_skippable = false; span.data = reinterpret_cast(token.m_type); spans.append(span); diff --git a/Libraries/LibGUI/INISyntaxHighlighter.cpp b/Libraries/LibGUI/INISyntaxHighlighter.cpp index e5aab5c49e..3b798ea1dd 100644 --- a/Libraries/LibGUI/INISyntaxHighlighter.cpp +++ b/Libraries/LibGUI/INISyntaxHighlighter.cpp @@ -71,8 +71,8 @@ void IniSyntaxHighlighter::rehighlight(Gfx::Palette palette) span.range.set_start({ token.m_start.line, token.m_start.column }); span.range.set_end({ token.m_end.line, token.m_end.column }); auto style = style_for_token_type(palette, token.m_type); - span.color = style.color; - span.bold = style.bold; + span.attributes.color = style.color; + span.attributes.bold = style.bold; span.is_skippable = token.m_type == IniToken::Type::Whitespace; span.data = reinterpret_cast(token.m_type); spans.append(span); diff --git a/Libraries/LibGUI/JSSyntaxHighlighter.cpp b/Libraries/LibGUI/JSSyntaxHighlighter.cpp index 16bec63b50..0ceb45535b 100644 --- a/Libraries/LibGUI/JSSyntaxHighlighter.cpp +++ b/Libraries/LibGUI/JSSyntaxHighlighter.cpp @@ -100,8 +100,8 @@ void JSSyntaxHighlighter::rehighlight(Gfx::Palette palette) span.range.set_end({ position.line(), position.column() }); auto type = is_trivia ? JS::TokenType::Invalid : token.type(); auto style = style_for_token_type(palette, type); - span.color = style.color; - span.bold = style.bold; + span.attributes.color = style.color; + span.attributes.bold = style.bold; span.is_skippable = is_trivia; span.data = reinterpret_cast(static_cast(type)); spans.append(span); diff --git a/Libraries/LibGUI/ShellSyntaxHighlighter.cpp b/Libraries/LibGUI/ShellSyntaxHighlighter.cpp index 42472468ca..8f3c27fa8e 100644 --- a/Libraries/LibGUI/ShellSyntaxHighlighter.cpp +++ b/Libraries/LibGUI/ShellSyntaxHighlighter.cpp @@ -104,8 +104,8 @@ private: { if (node->path()->is_bareword()) { auto& span = span_for_node(node->path()); - span.color = m_palette.link(); - span.is_underlined = true; + span.attributes.color = m_palette.link(); + span.attributes.underline = true; } else { NodeVisitor::visit(node); } @@ -124,8 +124,8 @@ private: auto& span = span_for_node(node); span.range.set_start({ node->and_position().start_line.line_number, node->and_position().start_line.line_column }); set_offset_range_end(span.range, node->and_position().end_line); - span.color = m_palette.syntax_punctuation(); - span.bold = true; + span.attributes.color = m_palette.syntax_punctuation(); + span.attributes.bold = true; } virtual void visit(const AST::ListConcatenate* node) override { @@ -137,8 +137,8 @@ private: auto& span = span_for_node(node); set_offset_range_start(span.range, node->position().end_line); - span.color = m_palette.syntax_punctuation(); - span.bold = true; + span.attributes.color = m_palette.syntax_punctuation(); + span.attributes.bold = true; } virtual void visit(const AST::BraceExpansion* node) override { @@ -150,11 +150,11 @@ private: auto& span = span_for_node(node); if (m_is_first_in_command) { - span.color = m_palette.syntax_keyword(); - span.bold = true; + span.attributes.color = m_palette.syntax_keyword(); + span.attributes.bold = true; m_is_first_in_command = false; } else if (node->text().starts_with("-")) { - span.color = m_palette.syntax_preprocessor_statement(); + span.attributes.color = m_palette.syntax_preprocessor_statement(); } } virtual void visit(const AST::CastToCommand* node) override @@ -166,12 +166,12 @@ private: NodeVisitor::visit(node); auto& start_span = span_for_node(node); - start_span.color = m_palette.syntax_punctuation(); + start_span.attributes.color = m_palette.syntax_punctuation(); start_span.range.set_end({ node->position().start_line.line_number, node->position().start_line.line_column + 1 }); start_span.data = (void*)static_cast(AugmentedTokenKind::OpenParen); auto& end_span = span_for_node(node); - end_span.color = m_palette.syntax_punctuation(); + end_span.attributes.color = m_palette.syntax_punctuation(); set_offset_range_start(end_span.range, node->position().end_line); end_span.data = (void*)static_cast(AugmentedTokenKind::CloseParen); } @@ -188,21 +188,21 @@ private: NodeVisitor::visit(node); auto& span = span_for_node(node); - span.color = m_palette.syntax_comment(); + span.attributes.color = m_palette.syntax_comment(); } virtual void visit(const AST::ContinuationControl* node) override { NodeVisitor::visit(node); auto& span = span_for_node(node); - span.color = m_palette.syntax_control_keyword(); + span.attributes.color = m_palette.syntax_control_keyword(); } virtual void visit(const AST::DynamicEvaluate* node) override { NodeVisitor::visit(node); auto& start_span = span_for_node(node); - start_span.color = m_palette.syntax_punctuation(); + start_span.attributes.color = m_palette.syntax_punctuation(); start_span.range.set_end({ node->position().start_line.line_number, node->position().start_line.line_column }); } virtual void visit(const AST::DoubleQuotedString* node) override @@ -210,18 +210,18 @@ private: NodeVisitor::visit(node); auto& start_span = span_for_node(node); - start_span.color = m_palette.syntax_string(); + start_span.attributes.color = m_palette.syntax_string(); set_offset_range_end(start_span.range, node->position().start_line, 0); start_span.is_skippable = true; auto& end_span = span_for_node(node); set_offset_range_start(end_span.range, node->position().end_line); - end_span.color = m_palette.syntax_string(); + end_span.attributes.color = m_palette.syntax_string(); end_span.is_skippable = true; if (m_is_first_in_command) { - start_span.bold = true; - end_span.bold = true; + start_span.attributes.bold = true; + end_span.attributes.bold = true; } m_is_first_in_command = false; } @@ -237,14 +237,14 @@ private: auto& name_span = span_for_node(node); name_span.range.set_start({ node->name().position.start_line.line_number, node->name().position.start_line.line_column }); set_offset_range_end(name_span.range, node->name().position.end_line); - name_span.color = m_palette.syntax_identifier(); + name_span.attributes.color = m_palette.syntax_identifier(); // arguments for (auto& arg : node->arguments()) { auto& name_span = span_for_node(node); name_span.range.set_start({ arg.position.start_line.line_number, arg.position.start_line.line_column }); set_offset_range_end(name_span.range, arg.position.end_line); - name_span.color = m_palette.syntax_identifier(); + name_span.attributes.color = m_palette.syntax_identifier(); } } virtual void visit(const AST::ForLoop* node) override @@ -257,7 +257,7 @@ private: auto& for_span = span_for_node(node); // FIXME: "fo\\\nr" is valid too for_span.range.set_end({ node->position().start_line.line_number, node->position().start_line.line_column + 2 }); - for_span.color = m_palette.syntax_keyword(); + for_span.attributes.color = m_palette.syntax_keyword(); // "in" if (auto maybe_position = node->in_keyword_position(); maybe_position.has_value()) { @@ -266,7 +266,7 @@ private: auto& in_span = span_for_node(node); in_span.range.set_start({ position.start_line.line_number, position.start_line.line_column }); set_offset_range_end(in_span.range, position.end_line); - in_span.color = m_palette.syntax_keyword(); + in_span.attributes.color = m_palette.syntax_keyword(); } } virtual void visit(const AST::Glob* node) override @@ -274,7 +274,7 @@ private: NodeVisitor::visit(node); auto& span = span_for_node(node); - span.color = m_palette.syntax_preprocessor_value(); + span.attributes.color = m_palette.syntax_preprocessor_value(); } virtual void visit(const AST::Execute* node) override { @@ -283,12 +283,12 @@ private: if (node->does_capture_stdout()) { auto& start_span = span_for_node(node); - start_span.color = m_palette.syntax_punctuation(); + start_span.attributes.color = m_palette.syntax_punctuation(); start_span.range.set_end({ node->position().start_line.line_number, node->position().start_line.line_column + 1 }); start_span.data = (void*)static_cast(AugmentedTokenKind::OpenParen); auto& end_span = span_for_node(node); - end_span.color = m_palette.syntax_punctuation(); + end_span.attributes.color = m_palette.syntax_punctuation(); set_offset_range_start(end_span.range, node->position().end_line); end_span.data = (void*)static_cast(AugmentedTokenKind::CloseParen); } @@ -302,7 +302,7 @@ private: auto& if_span = span_for_node(node); // FIXME: "i\\\nf" is valid too if_span.range.set_end({ node->position().start_line.line_number, node->position().start_line.line_column + 1 }); - if_span.color = m_palette.syntax_keyword(); + if_span.attributes.color = m_palette.syntax_keyword(); // "else" if (auto maybe_position = node->else_position(); maybe_position.has_value()) { @@ -311,7 +311,7 @@ private: auto& else_span = span_for_node(node); else_span.range.set_start({ position.start_line.line_number, position.start_line.line_column }); set_offset_range_end(else_span.range, node->position().end_line); - else_span.color = m_palette.syntax_keyword(); + else_span.attributes.color = m_palette.syntax_keyword(); } } virtual void visit(const AST::Join* node) override @@ -328,7 +328,7 @@ private: auto& match_expr = span_for_node(node); // FIXME: "mat\\\nch" is valid too match_expr.range.set_end({ node->position().start_line.line_number, node->position().start_line.line_column + 4 }); - match_expr.color = m_palette.syntax_keyword(); + match_expr.attributes.color = m_palette.syntax_keyword(); // "as" if (auto maybe_position = node->as_position(); maybe_position.has_value()) { @@ -337,7 +337,7 @@ private: auto& as_span = span_for_node(node); as_span.range.set_start({ position.start_line.line_number, position.start_line.line_column }); as_span.range.set_end({ position.end_line.line_number, position.end_line.line_column }); - as_span.color = m_palette.syntax_keyword(); + as_span.attributes.color = m_palette.syntax_keyword(); } } virtual void visit(const AST::Or* node) override @@ -354,8 +354,8 @@ private: auto& span = span_for_node(node); span.range.set_start({ node->or_position().start_line.line_number, node->or_position().start_line.line_column }); set_offset_range_end(span.range, node->or_position().end_line); - span.color = m_palette.syntax_punctuation(); - span.bold = true; + span.attributes.color = m_palette.syntax_punctuation(); + span.attributes.bold = true; } virtual void visit(const AST::Pipe* node) override { @@ -368,7 +368,7 @@ private: auto& span = span_for_node(node->start()); span.range.set_start(span.range.end()); set_offset_range_end(span.range, node->start()->position().end_line, 2); - span.color = m_palette.syntax_punctuation(); + span.attributes.color = m_palette.syntax_punctuation(); } virtual void visit(const AST::ReadRedirection* node) override { @@ -392,8 +392,8 @@ private: auto& span = span_for_node(node); span.range.set_start({ node->separator_position().start_line.line_number, node->separator_position().start_line.line_column }); set_offset_range_end(span.range, node->separator_position().end_line); - span.color = m_palette.syntax_punctuation(); - span.bold = true; + span.attributes.color = m_palette.syntax_punctuation(); + span.attributes.bold = true; span.is_skippable = true; } virtual void visit(const AST::Subshell* node) override @@ -405,14 +405,14 @@ private: NodeVisitor::visit(node); auto& span = span_for_node(node); - span.color = m_palette.syntax_identifier(); + span.attributes.color = m_palette.syntax_identifier(); } virtual void visit(const AST::SpecialVariable* node) override { NodeVisitor::visit(node); auto& span = span_for_node(node); - span.color = m_palette.syntax_identifier(); + span.attributes.color = m_palette.syntax_identifier(); } virtual void visit(const AST::Juxtaposition* node) override { @@ -426,9 +426,9 @@ private: return; auto& span = span_for_node(node); - span.color = m_palette.syntax_string(); + span.attributes.color = m_palette.syntax_string(); if (m_is_first_in_command) - span.bold = true; + span.attributes.bold = true; m_is_first_in_command = false; } virtual void visit(const AST::StringPartCompose* node) override @@ -440,22 +440,22 @@ private: NodeVisitor::visit(node); auto& span = span_for_node(node); - span.is_underlined = true; - span.background_color = Color(Color::NamedColor::MidRed).lightened(1.3f).with_alpha(128); + span.attributes.underline = true; + span.attributes.background_color = Color(Color::NamedColor::MidRed).lightened(1.3f).with_alpha(128); } virtual void visit(const AST::Tilde* node) override { NodeVisitor::visit(node); auto& span = span_for_node(node); - span.color = m_palette.link(); + span.attributes.color = m_palette.link(); } virtual void visit(const AST::VariableDeclarations* node) override { TemporaryChange first_in_command { m_is_first_in_command, false }; for (auto& decl : node->variables()) { auto& name_span = span_for_node(decl.name); - name_span.color = m_palette.syntax_identifier(); + name_span.attributes.color = m_palette.syntax_identifier(); decl.name->visit(*this); decl.value->visit(*this); @@ -463,7 +463,7 @@ private: auto& start_span = span_for_node(decl.name); start_span.range.set_start({ decl.name->position().end_line.line_number, decl.name->position().end_line.line_column }); start_span.range.set_end({ decl.value->position().start_line.line_number, decl.value->position().start_line.line_column }); - start_span.color = m_palette.syntax_punctuation(); + start_span.attributes.color = m_palette.syntax_punctuation(); start_span.data = (void*)static_cast(AugmentedTokenKind::OpenParen); } } diff --git a/Libraries/LibGUI/SyntaxHighlighter.cpp b/Libraries/LibGUI/SyntaxHighlighter.cpp index 5286758419..032adc65d7 100644 --- a/Libraries/LibGUI/SyntaxHighlighter.cpp +++ b/Libraries/LibGUI/SyntaxHighlighter.cpp @@ -89,10 +89,10 @@ void SyntaxHighlighter::highlight_matching_token_pair() m_brace_buddies[1].index = index1; m_brace_buddies[0].span_backup = buddy0; m_brace_buddies[1].span_backup = buddy1; - buddy0.background_color = Color::DarkCyan; - buddy1.background_color = Color::DarkCyan; - buddy0.color = Color::White; - buddy1.color = Color::White; + buddy0.attributes.background_color = Color::DarkCyan; + buddy1.attributes.background_color = Color::DarkCyan; + buddy0.attributes.color = Color::White; + buddy1.attributes.color = Color::White; m_editor->update(); }; diff --git a/Libraries/LibGUI/TextDocument.h b/Libraries/LibGUI/TextDocument.h index ba78c1677f..eb20cbdb77 100644 --- a/Libraries/LibGUI/TextDocument.h +++ b/Libraries/LibGUI/TextDocument.h @@ -38,19 +38,16 @@ #include #include #include -#include +#include #include namespace GUI { struct TextDocumentSpan { TextRange range; - Color color; - Optional background_color; - bool is_skippable { false }; - bool is_underlined { false }; - bool bold { false }; + Gfx::TextAttributes attributes; void* data { nullptr }; + bool is_skippable { false }; }; class TextDocument : public RefCounted { diff --git a/Libraries/LibGUI/TextEditor.cpp b/Libraries/LibGUI/TextEditor.cpp index 114800f800..0e44875c7c 100644 --- a/Libraries/LibGUI/TextEditor.cpp +++ b/Libraries/LibGUI/TextEditor.cpp @@ -496,13 +496,13 @@ void TextEditor::paint_event(PaintEvent& event) for (auto& span : document().spans()) { if (!span.range.contains(physical_position)) continue; - color = span.color; - if (span.bold) { + color = span.attributes.color; + if (span.attributes.bold) { if (auto bold_font = Gfx::FontDatabase::the().get(font->family(), font->presentation_size(), 700)) font = bold_font; } - background_color = span.background_color; - underline = span.is_underlined; + background_color = span.attributes.background_color; + underline = span.attributes.underline; break; } character_rect.set_width(font->glyph_width(code_point) + font->glyph_spacing()); diff --git a/Libraries/LibGfx/TextAttributes.h b/Libraries/LibGfx/TextAttributes.h new file mode 100644 index 0000000000..1de9e5c01d --- /dev/null +++ b/Libraries/LibGfx/TextAttributes.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021, Andreas Kling + * 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 +#include + +namespace Gfx { + +struct TextAttributes { + Color color; + Optional background_color; + bool underline { false }; + bool bold { false }; +}; + +}