From 1f92636c6de2e3b39983dc5ca3d1f7696b150ae0 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 7 Mar 2019 01:05:35 +0100 Subject: [PATCH] GTextEditor: Make the cursor blink while the editor is focused. --- LibGUI/GTextEditor.cpp | 45 ++++++++++++++++++++++++++++++++++-------- LibGUI/GTextEditor.h | 8 ++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index 9231261759..d6e6c37455 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -62,7 +62,7 @@ void GTextEditor::resize_event(GResizeEvent& event) void GTextEditor::update_scrollbar_ranges() { int available_height = height() - m_horizontal_scrollbar->height(); - int excess_height = max(0, (line_count() * font().glyph_height()) - available_height); + int excess_height = max(0, (line_count() * line_height()) - available_height); m_vertical_scrollbar->set_range(0, excess_height); int available_width = width() - m_vertical_scrollbar->width(); @@ -97,10 +97,11 @@ void GTextEditor::paint_event(GPaintEvent& event) for (int i = 0; i < line_count(); ++i) { auto& line = m_lines[i]; auto line_rect = line_content_rect(i); - painter.draw_text(line_rect, line.text(), TextAlignment::TopLeft, Color::Black); + painter.draw_text(line_rect, line.text(), TextAlignment::CenterLeft, Color::Black); } - painter.fill_rect(cursor_content_rect(), Color::Red); + if (is_focused() && m_cursor_state) + painter.fill_rect(cursor_content_rect(), Color::Red); painter.translate(-padding(), -padding()); painter.translate(m_horizontal_scrollbar->value(), m_vertical_scrollbar->value()); @@ -159,14 +160,23 @@ Rect GTextEditor::visible_content_rect() const Rect GTextEditor::cursor_content_rect() const { - ASSERT(m_cursor.is_valid()); + if (!m_cursor.is_valid()) + return { }; ASSERT(!m_lines.is_empty()); auto& line = m_lines[m_cursor.line()]; ASSERT(m_cursor.column() <= (line.text().length() + 1)); int x = 0; for (int i = 0; i < m_cursor.column(); ++i) x += font().glyph_width(line.text()[i]); - return { x, m_cursor.line() * font().glyph_height(), 1, font().glyph_height() }; + return { x, m_cursor.line() * line_height(), 1, line_height() }; +} + +Rect GTextEditor::cursor_widget_rect() const +{ + ASSERT(m_horizontal_scrollbar); + ASSERT(m_vertical_scrollbar); + auto rect = cursor_content_rect(); + return rect.translated(-(m_horizontal_scrollbar->value() - padding()), -(m_vertical_scrollbar->value() - padding())); } void GTextEditor::scroll_into_view(const GTextPosition& position, Orientation orientation) @@ -195,15 +205,15 @@ Rect GTextEditor::line_content_rect(int line_index) const auto& line = m_lines[line_index]; return { 0, - line_index * font().glyph_height(), + line_index * line_height(), line.width(font()), - font().glyph_height() + line_height() }; } void GTextEditor::update_cursor() { - update(); + update(cursor_widget_rect()); } void GTextEditor::set_cursor(int line, int column) @@ -212,11 +222,30 @@ void GTextEditor::set_cursor(int line, int column) return; update_cursor(); m_cursor = GTextPosition(line, column); + m_cursor_state = true; update_cursor(); if (on_cursor_change) on_cursor_change(*this); } +void GTextEditor::focusin_event(GEvent&) +{ + update_cursor(); + start_timer(500); +} + +void GTextEditor::focusout_event(GEvent&) +{ + stop_timer(); +} + +void GTextEditor::timer_event(GTimerEvent&) +{ + m_cursor_state = !m_cursor_state; + if (is_focused()) + update_cursor(); +} + void GTextEditor::Line::set_text(const String& text) { if (text == m_text) diff --git a/LibGUI/GTextEditor.h b/LibGUI/GTextEditor.h index 85f8b4a9a0..8bd38d4666 100644 --- a/LibGUI/GTextEditor.h +++ b/LibGUI/GTextEditor.h @@ -40,6 +40,8 @@ public: Rect visible_content_rect() const; void scroll_into_view(const GTextPosition&, Orientation); int line_count() const { return m_lines.size(); } + int line_spacing() const { return m_line_spacing; } + int line_height() const { return font().glyph_height() + m_line_spacing; } int padding() const { return 2; } GTextPosition cursor() const { return m_cursor; } @@ -48,11 +50,15 @@ private: virtual void resize_event(GResizeEvent&) override; virtual void mousedown_event(GMouseEvent&) override; virtual void keydown_event(GKeyEvent&) override; + virtual void focusin_event(GEvent&) override; + virtual void focusout_event(GEvent&) override; + virtual void timer_event(GTimerEvent&) override; virtual bool accepts_focus() const override { return true; } void update_scrollbar_ranges(); Rect line_content_rect(int item_index) const; Rect cursor_content_rect() const; + Rect cursor_widget_rect() const; void update_cursor(); void set_cursor(int line, int column); @@ -74,4 +80,6 @@ private: }; Vector m_lines; GTextPosition m_cursor; + bool m_cursor_state { true }; + int m_line_spacing { 2 }; };