ソースを参照

fix(gtk): resolve modeline visibility and split crash

Fixed modeline not rendering in GTK by explicitly calling render_modeline_for_window in the widget draw function. Fixed segmentation fault on window split by updating GtkRenderer to use the persistent main window widget for Pango context creation instead of transient DrawingAreas.
Bernardo Magri 1 ヶ月 前
コミット
f77a196c6f
4 ファイル変更24 行追加20 行削除
  1. 2 0
      documentation/PLAN.md
  2. 2 2
      include/lumacs/gtk_renderer.hpp
  3. 5 3
      src/gtk_editor.cpp
  4. 15 15
      src/gtk_renderer.cpp

+ 2 - 0
documentation/PLAN.md

@@ -153,6 +153,8 @@ Lumacs/
         - **Theme system fully functional**: All 6 themes (default, dracula, everforest-dark, gruvbox-light, nord, solarized-dark) load correctly, theme switching works via `M-x set-theme`, `C-x t` keybindings work (including `C-x t l` for listing), and theme cycling is operational.
         - **Theme Cache Invalidation**: ✅ Fixed. Implemented `EditorEvent::ThemeChanged` and `GtkRenderer::invalidate_cache()` to ensure UI updates immediately when a new theme is applied.
 - ✅ **Phase 15 Polishing**: Successfully addressed GTK Cleanup and Modeline Refactor.
+    - [x] **GTK Modeline**: Fixed modeline not showing in GTK frontend and ensured per-window rendering.
+    - [x] **GTK Split Crash**: Fixed segmentation fault when splitting windows by using robust context widget management in `GtkRenderer`.
 - ✅ **Plugin Management**: Implemented dynamic loading and lifecycle management of Lua plugins.
 - ✅ **Lua Debugging**: Integrated basic remote debugging support for Lua scripts via MobDebug.
 - ✅ **Command Aliases**: Implemented support for user-defined command aliases.

+ 2 - 2
include/lumacs/gtk_renderer.hpp

@@ -23,7 +23,7 @@ static constexpr double PADDING_BOTTOM = 8.0;
 
 class GtkRenderer {
 public:
-    GtkRenderer(EditorCore& core, Gtk::DrawingArea& main_drawing_area);
+    GtkRenderer(EditorCore& core, Gtk::Widget& context_widget);
 
     void initialize_font_metrics();
     void apply_face_attributes(Pango::AttrList& attr_list, const FaceAttributes& face, int start_index, int end_index);
@@ -66,7 +66,7 @@ public:
         std::unordered_map<Window*, std::unordered_map<size_t, LineCacheEntry>> render_cache_;
     
         EditorCore& core_;
-        Gtk::DrawingArea& main_drawing_area_;
+        Gtk::Widget& context_widget_;
 
     Pango::FontDescription font_desc_;
     bool font_initialized_ = false;

+ 5 - 3
src/gtk_editor.cpp

@@ -413,9 +413,10 @@ void GtkEditor::on_activate() {
         drawing_area_->grab_focus();
     }
     
-    // Initialize GtkRenderer after drawing_area_ is set
-    if (core_ && drawing_area_) {
-        gtk_renderer_ = std::make_unique<GtkRenderer>(*core_, *drawing_area_);
+    // Initialize GtkRenderer using the main window as context widget
+    // This ensures the Pango context remains valid even if layout changes destroy specific DrawingAreas
+    if (core_ && window_) {
+        gtk_renderer_ = std::make_unique<GtkRenderer>(*core_, *window_);
     }
 
     // Set up cursor blinking timer (BLINK_INTERVAL like Emacs)
@@ -540,6 +541,7 @@ Gtk::Widget* GtkEditor::create_widget_for_layout_node(std::shared_ptr<LayoutNode
             if (auto window_ptr = weak_window.lock()) { // Correctly capture shared_ptr
                 if (gtk_renderer_) {
                     gtk_renderer_->draw_window(cr, width, height, window_ptr, drawing_area, cached_active_window_, cursor_visible_);
+                    gtk_renderer_->render_modeline_for_window(cr, width, height, window_ptr, cached_active_window_);
                 }
             }
         });

+ 15 - 15
src/gtk_renderer.cpp

@@ -6,8 +6,8 @@
 
 namespace lumacs {
 
-GtkRenderer::GtkRenderer(EditorCore& core, Gtk::DrawingArea& main_drawing_area)
-    : core_(core), main_drawing_area_(main_drawing_area) {
+GtkRenderer::GtkRenderer(EditorCore& core, Gtk::Widget& context_widget)
+    : core_(core), context_widget_(context_widget) {
     initialize_font_metrics(); // Initialize font metrics once during construction
 }
 
@@ -15,7 +15,7 @@ void GtkRenderer::initialize_font_metrics() {
     if (font_initialized_) return;
 
     // Use a minimal string for layout creation
-    auto layout = main_drawing_area_.create_pango_layout(" ");
+    auto layout = context_widget_.create_pango_layout(" ");
     font_desc_ = Pango::FontDescription("Monospace 12");
     layout->set_font_description(font_desc_);
     
@@ -133,7 +133,7 @@ void GtkRenderer::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, in
     draw_window(cr, width, height, active_window, &main_drawing_area_, active_window_cache, cursor_visible_state);
 
     // Use a temporary layout for modeline/minibuffer as they are dynamic
-    auto temp_layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
+    auto temp_layout = Pango::Layout::create(context_widget_.get_pango_context());
     temp_layout->set_font_description(font_desc_);
 
     // Render modeline
@@ -167,9 +167,8 @@ void GtkRenderer::draw_window(const Cairo::RefPtr<Cairo::Context>& cr, int width
     int visible_lines = static_cast<int>(content_height_px / line_height_);
     int visible_cols = static_cast<int>(content_width_px / char_width_);
 
-    // Reserve space for modeline (1 line). Minibuffer is global and rendered separately.
-    bool is_main_window = (window == active_window_cache);
-    int editor_lines = is_main_window ? std::max(0, visible_lines - 2) : std::max(0, visible_lines - 1);
+    // Reserve space for modeline (1 line). Minibuffer is now separate.
+    int editor_lines = std::max(0, visible_lines - 1);
     window->set_viewport_size(visible_cols, editor_lines);
 
     // Region/Mark Calculation
@@ -384,8 +383,9 @@ void GtkRenderer::render_modeline_for_window(const Cairo::RefPtr<Cairo::Context>
     // The logic for is_active needs to be passed from GtkEditor
     bool is_active = (window == active_window_cache);
     
-    // Calculate modeline position (second line from bottom)
-    double modeline_y = height - (2 * line_height_) - PADDING_BOTTOM; // Position above minibuffer
+    // Calculate modeline position (bottom of the drawing area)
+    // Minibuffer is separate, so modeline is at the very bottom
+    double modeline_y = height - line_height_ - PADDING_BOTTOM; 
     double modeline_x = PADDING_LEFT;
     
     // Get theme colors
@@ -415,7 +415,7 @@ void GtkRenderer::render_modeline_for_window(const Cairo::RefPtr<Cairo::Context>
         }
         
         // Use a temporary layout for modeline chunk
-        auto layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
+        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
         layout->set_font_description(font_desc_);
         layout->set_text(chunk.text);
         
@@ -486,7 +486,7 @@ void GtkRenderer::render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int
     // Render minibuffer text
     if (!minibuffer_text.empty()) {
         cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
-        auto layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
+        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
         layout->set_font_description(font_desc_);
         layout->set_text(minibuffer_text);
         
@@ -507,7 +507,7 @@ void GtkRenderer::render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int
     bool cursor_visible_ = true; // Placeholder
     if (core_.minibuffer_manager().is_active() && cursor_visible_) {
         // Calculate cursor position in minibuffer
-        auto layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
+        auto layout = Pango::Layout::create(context_widget_.get_pango_context());
         layout->set_font_description(font_desc_);
         layout->set_text(minibuffer_text); // Measure full text
         Pango::Rectangle ink_rect, logical_rect;
@@ -531,7 +531,7 @@ void GtkRenderer::render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int
                     layout->get_pixel_extents(ink_rect, logical_rect);
                     double text_width = logical_rect.get_width();
 
-                    auto completion_layout = Pango::Layout::create(main_drawing_area_.get_pango_context());
+                    auto completion_layout = Pango::Layout::create(context_widget_.get_pango_context());
                     completion_layout->set_font_description(font_desc_);
                     completion_layout->set_text(completion_suffix); // Only draw suffix
                     
@@ -563,10 +563,10 @@ void GtkRenderer::render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int
 bool GtkRenderer::get_minibuffer_coords(int& x, int& y, int& width, int& height) const {
     // These coordinates are relative to the main drawing area.
     // The minibuffer is always at the bottom of the main drawing area.
-    width = main_drawing_area_.get_width();
+    width = context_widget_.get_width();
     height = static_cast<int>(line_height_ + PADDING_TOP + PADDING_BOTTOM); // Approximate minibuffer height
     x = 0; // Starts at the left edge
-    y = main_drawing_area_.get_height() - height; // Position from the bottom
+    y = context_widget_.get_height() - height; // Position from the bottom
 
     return true;
 }