Переглянути джерело

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.
Andreas Kling 5 роки тому
батько
коміт
c16b1a515e
2 змінених файлів з 22 додано та 4 видалено
  1. 20 3
      Libraries/LibGUI/GTextEditor.cpp
  2. 2 1
      Libraries/LibGUI/GTextEditor.h

+ 20 - 3
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;
+}

+ 2 - 1
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 };