Serenity Operating System
at hosted 252 lines 9.8 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#pragma once 28 29#include <AK/Function.h> 30#include <AK/NonnullOwnPtrVector.h> 31#include <AK/NonnullRefPtrVector.h> 32#include <LibCore/ElapsedTimer.h> 33#include <LibGUI/ScrollableWidget.h> 34#include <LibGUI/TextDocument.h> 35#include <LibGUI/TextRange.h> 36#include <LibGfx/TextAlignment.h> 37 38namespace GUI { 39 40class TextEditor 41 : public ScrollableWidget 42 , public TextDocument::Client { 43 C_OBJECT(TextEditor) 44public: 45 enum Type { 46 MultiLine, 47 SingleLine 48 }; 49 virtual ~TextEditor() override; 50 51 const TextDocument& document() const { return *m_document; } 52 TextDocument& document() { return *m_document; } 53 54 void set_document(TextDocument&); 55 56 bool is_readonly() const { return m_readonly; } 57 void set_readonly(bool); 58 59 virtual bool is_automatic_indentation_enabled() const final { return m_automatic_indentation_enabled; } 60 void set_automatic_indentation_enabled(bool enabled) { m_automatic_indentation_enabled = enabled; } 61 62 virtual int soft_tab_width() const final { return m_soft_tab_width; } 63 64 bool is_line_wrapping_enabled() const { return m_line_wrapping_enabled; } 65 void set_line_wrapping_enabled(bool); 66 67 Gfx::TextAlignment text_alignment() const { return m_text_alignment; } 68 void set_text_alignment(Gfx::TextAlignment); 69 70 Type type() const { return m_type; } 71 bool is_single_line() const { return m_type == SingleLine; } 72 bool is_multi_line() const { return m_type == MultiLine; } 73 74 bool is_ruler_visible() const { return m_ruler_visible; } 75 void set_ruler_visible(bool b) { m_ruler_visible = b; } 76 77 Function<void()> on_cursor_change; 78 Function<void()> on_selection_change; 79 80 void set_text(const StringView&); 81 void scroll_cursor_into_view(); 82 void scroll_position_into_view(const TextPosition&); 83 size_t line_count() const { return document().line_count(); } 84 int line_spacing() const { return m_line_spacing; } 85 int line_height() const; 86 TextPosition cursor() const { return m_cursor; } 87 TextRange normalized_selection() const { return m_selection.normalized(); } 88 // FIXME: This should take glyph spacing into account, no? 89 int glyph_width() const; 90 91 void insert_at_cursor_or_replace_selection(const StringView&); 92 bool write_to_file(const StringView& path); 93 bool has_selection() const { return m_selection.is_valid(); } 94 String selected_text() const; 95 void set_selection(const TextRange&); 96 void clear_selection(); 97 bool can_undo() const { return document().can_undo(); } 98 bool can_redo() const { return document().can_redo(); } 99 100 String text() const; 101 102 void clear(); 103 104 void cut(); 105 void copy(); 106 void paste(); 107 void do_delete(); 108 void delete_current_line(); 109 void select_all(); 110 void undo() { document().undo(); } 111 void redo() { document().redo(); } 112 113 Function<void()> on_change; 114 Function<void()> on_return_pressed; 115 Function<void()> on_escape_pressed; 116 117 Action& undo_action() { return *m_undo_action; } 118 Action& redo_action() { return *m_redo_action; } 119 Action& cut_action() { return *m_cut_action; } 120 Action& copy_action() { return *m_copy_action; } 121 Action& paste_action() { return *m_paste_action; } 122 Action& delete_action() { return *m_delete_action; } 123 Action& go_to_line_action() { return *m_go_to_line_action; } 124 125 void add_custom_context_menu_action(Action&); 126 127 void set_cursor(size_t line, size_t column); 128 void set_cursor(const TextPosition&); 129 130 const SyntaxHighlighter* syntax_highlighter() const; 131 void set_syntax_highlighter(OwnPtr<SyntaxHighlighter>); 132 133protected: 134 explicit TextEditor(Type = Type::MultiLine); 135 136 virtual void did_change_font() override; 137 virtual void paint_event(PaintEvent&) override; 138 virtual void mousedown_event(MouseEvent&) override; 139 virtual void mouseup_event(MouseEvent&) override; 140 virtual void mousemove_event(MouseEvent&) override; 141 virtual void doubleclick_event(MouseEvent&) override; 142 virtual void keydown_event(KeyEvent&) override; 143 virtual void focusin_event(Core::Event&) override; 144 virtual void focusout_event(Core::Event&) override; 145 virtual void timer_event(Core::TimerEvent&) override; 146 virtual bool accepts_focus() const override { return true; } 147 virtual void enter_event(Core::Event&) override; 148 virtual void leave_event(Core::Event&) override; 149 virtual void context_menu_event(ContextMenuEvent&) override; 150 virtual void resize_event(ResizeEvent&) override; 151 virtual void theme_change_event(ThemeChangeEvent&) override; 152 virtual void cursor_did_change() {} 153 154 TextPosition text_position_at(const Gfx::Point&) const; 155 156private: 157 friend class TextDocumentLine; 158 159 // ^TextDocument::Client 160 virtual void document_did_append_line() override; 161 virtual void document_did_insert_line(size_t) override; 162 virtual void document_did_remove_line(size_t) override; 163 virtual void document_did_remove_all_lines() override; 164 virtual void document_did_change() override; 165 virtual void document_did_set_text() override; 166 virtual void document_did_set_cursor(const TextPosition&) override; 167 168 void create_actions(); 169 void paint_ruler(Painter&); 170 void update_content_size(); 171 void did_change(); 172 173 Gfx::Rect line_content_rect(size_t item_index) const; 174 Gfx::Rect line_widget_rect(size_t line_index) const; 175 Gfx::Rect cursor_content_rect() const; 176 Gfx::Rect content_rect_for_position(const TextPosition&) const; 177 void update_cursor(); 178 const NonnullOwnPtrVector<TextDocumentLine>& lines() const { return document().lines(); } 179 NonnullOwnPtrVector<TextDocumentLine>& lines() { return document().lines(); } 180 TextDocumentLine& line(size_t index) { return document().line(index); } 181 const TextDocumentLine& line(size_t index) const { return document().line(index); } 182 TextDocumentLine& current_line() { return line(m_cursor.line()); } 183 const TextDocumentLine& current_line() const { return line(m_cursor.line()); } 184 int ruler_width() const; 185 Gfx::Rect ruler_content_rect(size_t line) const; 186 void toggle_selection_if_needed_for_event(const KeyEvent&); 187 void delete_selection(); 188 void did_update_selection(); 189 int content_x_for_position(const TextPosition&) const; 190 Gfx::Rect ruler_rect_in_inner_coordinates() const; 191 Gfx::Rect visible_text_rect_in_inner_coordinates() const; 192 void recompute_all_visual_lines(); 193 void ensure_cursor_is_valid(); 194 void flush_pending_change_notification_if_needed(); 195 void get_selection_line_boundaries(size_t& first_line, size_t& last_line); 196 void move_selected_lines_up(); 197 void move_selected_lines_down(); 198 void sort_selected_lines(); 199 200 size_t visual_line_containing(size_t line_index, size_t column) const; 201 void recompute_visual_lines(size_t line_index); 202 203 template<class T, class... Args> 204 inline void execute(Args&&... args) 205 { 206 auto command = make<T>(*m_document, forward<Args>(args)...); 207 command->execute_from(*this); 208 m_document->add_to_undo_stack(move(command)); 209 } 210 211 Type m_type { MultiLine }; 212 213 TextPosition m_cursor; 214 Gfx::TextAlignment m_text_alignment { Gfx::TextAlignment::CenterLeft }; 215 bool m_cursor_state { true }; 216 bool m_in_drag_select { false }; 217 bool m_ruler_visible { false }; 218 bool m_has_pending_change_notification { false }; 219 bool m_automatic_indentation_enabled { false }; 220 bool m_line_wrapping_enabled { false }; 221 bool m_readonly { false }; 222 int m_line_spacing { 4 }; 223 size_t m_soft_tab_width { 4 }; 224 int m_horizontal_content_padding { 2 }; 225 TextRange m_selection; 226 RefPtr<Menu> m_context_menu; 227 RefPtr<Action> m_undo_action; 228 RefPtr<Action> m_redo_action; 229 RefPtr<Action> m_cut_action; 230 RefPtr<Action> m_copy_action; 231 RefPtr<Action> m_paste_action; 232 RefPtr<Action> m_delete_action; 233 RefPtr<Action> m_go_to_line_action; 234 Core::ElapsedTimer m_triple_click_timer; 235 NonnullRefPtrVector<Action> m_custom_context_menu_actions; 236 237 RefPtr<TextDocument> m_document; 238 239 template<typename Callback> 240 void for_each_visual_line(size_t line_index, Callback) const; 241 242 struct LineVisualData { 243 Vector<size_t, 1> visual_line_breaks; 244 Gfx::Rect visual_rect; 245 }; 246 247 NonnullOwnPtrVector<LineVisualData> m_line_visual_data; 248 249 OwnPtr<SyntaxHighlighter> m_highlighter; 250}; 251 252}