From c16b1a515e9261f958fe9d151b175c74f6adaf4a Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 8 Nov 2019 19:49:08 +0100 Subject: [PATCH] GTextEditor: Add a way to flush any pending on_change notifications Since on_change handlers can alter the text document we're working on, we have to make sure they've been run before we try looking at spans. This fixes some flakiness when a paint happened before HackStudio had a chance to re-highlight some C++ while editing it. The design where clients of GTextEditor perform syntax highlighting in the "arbitrary code execution" on_change callback is not very good. We should find a way to move highlighting closer to the editor. --- Libraries/LibGUI/GTextEditor.cpp | 23 ++++++++++++++++++++--- Libraries/LibGUI/GTextEditor.h | 3 ++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Libraries/LibGUI/GTextEditor.cpp b/Libraries/LibGUI/GTextEditor.cpp index c64e073a99..d332a4a10f 100644 --- a/Libraries/LibGUI/GTextEditor.cpp +++ b/Libraries/LibGUI/GTextEditor.cpp @@ -146,6 +146,9 @@ void GTextEditor::doubleclick_event(GMouseEvent& event) if (event.button() != GMouseButton::Left) return; + // NOTE: This ensures that spans are updated before we look at them. + flush_pending_change_notification_if_needed(); + m_triple_click_timer.start(); m_in_drag_select = false; @@ -291,6 +294,9 @@ Rect GTextEditor::visible_text_rect_in_inner_coordinates() const void GTextEditor::paint_event(GPaintEvent& event) { + // NOTE: This ensures that spans are updated before we look at them. + flush_pending_change_notification_if_needed(); + GFrame::paint_event(event); GPainter painter(*this); @@ -1221,12 +1227,14 @@ void GTextEditor::did_change() ASSERT(!is_readonly()); update_content_size(); recompute_all_visual_lines(); - if (!m_have_pending_change_notification) { - m_have_pending_change_notification = true; + if (!m_has_pending_change_notification) { + m_has_pending_change_notification = true; deferred_invoke([this](auto&) { + if (!m_has_pending_change_notification) + return; if (on_change) on_change(); - m_have_pending_change_notification = false; + m_has_pending_change_notification = false; }); } } @@ -1597,3 +1605,12 @@ void GTextEditor::CreateLineCommand::redo() for (int i = 0; i < m_line_content.size(); i++) m_text_editor.document().lines()[m_text_position.line() + 1].insert(m_text_editor.document(), i, m_line_content[i]); } + +void GTextEditor::flush_pending_change_notification_if_needed() +{ + if (!m_has_pending_change_notification) + return; + if (on_change) + on_change(); + m_has_pending_change_notification = false; +} diff --git a/Libraries/LibGUI/GTextEditor.h b/Libraries/LibGUI/GTextEditor.h index 2a67c94c48..61957d6794 100644 --- a/Libraries/LibGUI/GTextEditor.h +++ b/Libraries/LibGUI/GTextEditor.h @@ -160,6 +160,7 @@ private: Rect visible_text_rect_in_inner_coordinates() const; void recompute_all_visual_lines(); void ensure_cursor_is_valid(); + void flush_pending_change_notification_if_needed(); class UndoCommand { @@ -233,7 +234,7 @@ private: bool m_cursor_state { true }; bool m_in_drag_select { false }; bool m_ruler_visible { false }; - bool m_have_pending_change_notification { false }; + bool m_has_pending_change_notification { false }; bool m_automatic_indentation_enabled { false }; bool m_line_wrapping_enabled { false }; bool m_readonly { false };