Parcourir la source

fix(gtk): resolve window split crashes with proper widget lifecycle management

Key fixes to prevent crashes during window splits:

1. **Weak pointer references**: Use weak_ptr for Window objects in GTK callbacks
   to prevent dangling references when layout is rebuilt

2. **Safe Pango context**: Create Pango layout directly from Cairo context
   instead of relying on potentially invalid drawing_area_ pointer

3. **Clear drawing_area_ on rebuild**: Reset drawing_area_ pointer during
   rebuild_layout to prevent use of stale widget references

4. **Recursive redraw helper**: Add queue_redraw_all_windows to properly
   redraw all drawing areas after layout changes

These changes ensure GTK widgets are properly managed during the dynamic
layout reconstruction that happens when windows are split.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Bernardo Magri il y a 1 mois
Parent
commit
7fd79373a0
1 fichiers modifiés avec 57 ajouts et 30 suppressions
  1. 57 30
      src/gtk_editor.cpp

+ 57 - 30
src/gtk_editor.cpp

@@ -68,9 +68,9 @@ public:
             rebuild_layout();
         }
 
-        // Request redraw on most events
-        if (drawing_area_) {
-            drawing_area_->queue_draw();
+        // Request redraw on most events - recursively find all drawing areas
+        if (content_widget_) {
+            queue_redraw_all_windows(content_widget_);
         }
 
         // Handle mode switching events
@@ -117,6 +117,22 @@ public:
         core_ = core;
     }
 
+    // Helper to recursively find and redraw all drawing areas
+    void queue_redraw_all_windows(Gtk::Widget* widget) {
+        if (!widget) return;
+        
+        if (auto drawing_area = dynamic_cast<Gtk::DrawingArea*>(widget)) {
+            drawing_area->queue_draw();
+        } else if (auto paned = dynamic_cast<Gtk::Paned*>(widget)) {
+            if (auto start_child = paned->get_start_child()) {
+                queue_redraw_all_windows(start_child);
+            }
+            if (auto end_child = paned->get_end_child()) {
+                queue_redraw_all_windows(end_child);
+            }
+        }
+    }
+
 private:
     EditorCore* core_;
     Glib::RefPtr<Gtk::Application> app_;
@@ -336,6 +352,9 @@ protected:
         if (content_widget_) {
             window_->unset_child();
         }
+        
+        // Clear the drawing area reference since we're rebuilding
+        drawing_area_ = nullptr;
 
         // Create new layout based on the tree
         content_widget_ = create_widget_for_layout_node(root_layout);
@@ -353,9 +372,11 @@ protected:
             auto drawing_area = Gtk::make_managed<Gtk::DrawingArea>();
             
             // Set up drawing for this specific window
-            drawing_area->set_draw_func([this, node](const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
-                if (node && node->window) {
-                    draw_window(cr, width, height, node->window);
+            // Use a weak reference to the window to avoid crashes if the layout is rebuilt
+            std::weak_ptr<Window> weak_window = node->window;
+            drawing_area->set_draw_func([this, weak_window](const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
+                if (auto window = weak_window.lock()) {
+                    draw_window(cr, width, height, window);
                 }
             });
             
@@ -363,18 +384,22 @@ protected:
             
             // Add input handling  
             auto controller = Gtk::EventControllerKey::create();
-            controller->signal_key_pressed().connect([this, node](guint keyval, guint keycode, Gdk::ModifierType state) -> bool {
+            // Use weak reference to window for key handling
+            std::weak_ptr<Window> weak_window_key = node->window;
+            controller->signal_key_pressed().connect([this, weak_window_key](guint keyval, guint keycode, Gdk::ModifierType state) -> bool {
                 // Set this window as active when it receives input
-                if (node && node->window && core_) {
-                    // For now, find the right window by cycling through next_window until we find the matching one
-                    // This is a hack but works until we add proper window focusing API
-                    auto current = core_->active_window();
-                    if (current != node->window) {
-                        // Try to find the target window by calling next_window repeatedly
-                        for (int i = 0; i < 10; ++i) { // Prevent infinite loop
-                            core_->next_window();
-                            if (core_->active_window() == node->window) {
-                                break;
+                if (auto window = weak_window_key.lock()) {
+                    if (core_) {
+                        // For now, find the right window by cycling through next_window until we find the matching one
+                        // This is a hack but works until we add proper window focusing API
+                        auto current = core_->active_window();
+                        if (current != window) {
+                            // Try to find the target window by calling next_window repeatedly
+                            for (int i = 0; i < 10; ++i) { // Prevent infinite loop
+                                core_->next_window();
+                                if (core_->active_window() == window) {
+                                    break;
+                                }
                             }
                         }
                     }
@@ -385,16 +410,19 @@ protected:
             
             // Add focus handling to set active window
             auto focus_controller = Gtk::EventControllerFocus::create();
-            focus_controller->signal_enter().connect([this, node]() {
-                if (node && node->window && core_) {
-                    // For now, find the right window by cycling through next_window until we find the matching one
-                    auto current = core_->active_window();
-                    if (current != node->window) {
-                        // Try to find the target window by calling next_window repeatedly
-                        for (int i = 0; i < 10; ++i) { // Prevent infinite loop
-                            core_->next_window();
-                            if (core_->active_window() == node->window) {
-                                break;
+            std::weak_ptr<Window> weak_window_focus = node->window;
+            focus_controller->signal_enter().connect([this, weak_window_focus]() {
+                if (auto window = weak_window_focus.lock()) {
+                    if (core_) {
+                        // For now, find the right window by cycling through next_window until we find the matching one
+                        auto current = core_->active_window();
+                        if (current != window) {
+                            // Try to find the target window by calling next_window repeatedly
+                            for (int i = 0; i < 10; ++i) { // Prevent infinite loop
+                                core_->next_window();
+                                if (core_->active_window() == window) {
+                                    break;
+                                }
                             }
                         }
                     }
@@ -444,9 +472,8 @@ protected:
         cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
         cr->paint();
 
-        // Create Pango layout - need to get context from a drawing area
-        // For now, use the first drawing area's context
-        auto layout = Pango::Layout::create(drawing_area_->get_pango_context());
+        // Create Pango layout - create context directly from Cairo
+        auto layout = Pango::Layout::create(cr);
         
         // Font configuration
         Pango::FontDescription font_desc("Monospace 12");