|
|
@@ -1,6 +1,7 @@
|
|
|
#include "lumacs/gtk_editor.hpp"
|
|
|
#include "lumacs/editor_core.hpp"
|
|
|
#include "lumacs/lua_api.hpp"
|
|
|
+#include "lumacs/keybinding.hpp"
|
|
|
#include <iostream>
|
|
|
|
|
|
// Check if GTK is enabled in build
|
|
|
@@ -23,7 +24,14 @@ public:
|
|
|
class GtkEditor : public IEditorView {
|
|
|
public:
|
|
|
GtkEditor() : core_(nullptr) {}
|
|
|
- ~GtkEditor() override = default;
|
|
|
+ ~GtkEditor() override {
|
|
|
+ // Clear core pointer to prevent any callbacks during GTK cleanup
|
|
|
+ core_ = nullptr;
|
|
|
+ // Clear widget pointers - GTK manages their lifetime
|
|
|
+ drawing_area_ = nullptr;
|
|
|
+ window_ = nullptr;
|
|
|
+ // Let app_ RefPtr be destroyed naturally
|
|
|
+ }
|
|
|
|
|
|
void init() override {
|
|
|
// Initialize GTK application
|
|
|
@@ -37,11 +45,14 @@ public:
|
|
|
}
|
|
|
|
|
|
void handle_editor_event(EditorEvent event) override {
|
|
|
+ // Safety check during destruction
|
|
|
+ if (!core_ || !app_) return;
|
|
|
+
|
|
|
// Request redraw on most events
|
|
|
if (drawing_area_) {
|
|
|
drawing_area_->queue_draw();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (event == EditorEvent::Quit) {
|
|
|
app_->quit(); // Quit the application gracefully
|
|
|
}
|
|
|
@@ -54,6 +65,7 @@ public:
|
|
|
private:
|
|
|
EditorCore* core_;
|
|
|
Glib::RefPtr<Gtk::Application> app_;
|
|
|
+ Gtk::Window* window_ = nullptr; // Store window pointer for widget access only (not lifetime management)
|
|
|
// Input Mode State
|
|
|
enum class Mode {
|
|
|
Normal,
|
|
|
@@ -77,16 +89,18 @@ private:
|
|
|
protected:
|
|
|
void on_activate() {
|
|
|
// Create main window and associate with the application
|
|
|
- // Gtk::ApplicationWindow constructor adds it to the application, which manages lifetime.
|
|
|
- auto* window = new LumacsWindow(app_);
|
|
|
-
|
|
|
+ // Note: The window is owned by the application through GObject reference counting
|
|
|
+ // We just keep a raw pointer for access, but don't manage its lifetime
|
|
|
+ window_ = new LumacsWindow(app_);
|
|
|
+
|
|
|
// Create drawing area for text rendering
|
|
|
+ // Gtk::make_managed ties the widget's lifetime to its parent container
|
|
|
drawing_area_ = Gtk::make_managed<Gtk::DrawingArea>();
|
|
|
drawing_area_->set_draw_func(sigc::mem_fun(*this, &GtkEditor::on_draw));
|
|
|
drawing_area_->set_focusable(true);
|
|
|
-
|
|
|
- // Add to window
|
|
|
- window->set_child(*drawing_area_);
|
|
|
+
|
|
|
+ // Add to window - window becomes the parent and will manage drawing_area lifetime
|
|
|
+ window_->set_child(*drawing_area_);
|
|
|
|
|
|
// Input handling
|
|
|
auto controller = Gtk::EventControllerKey::create();
|
|
|
@@ -94,12 +108,15 @@ protected:
|
|
|
drawing_area_->add_controller(controller);
|
|
|
|
|
|
// Show window
|
|
|
- window->present();
|
|
|
+ window_->present();
|
|
|
drawing_area_->grab_focus();
|
|
|
}
|
|
|
|
|
|
// Rendering
|
|
|
void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height) {
|
|
|
+ // Safety check - don't draw if core is null (during destruction)
|
|
|
+ if (!core_) return;
|
|
|
+
|
|
|
// Fill background
|
|
|
auto theme = core_->active_theme();
|
|
|
Color bg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
|
|
|
@@ -159,17 +176,31 @@ protected:
|
|
|
|
|
|
// Render Cursor
|
|
|
const auto cursor = core_->cursor();
|
|
|
- if (cursor.line >= start_line && cursor.line < end_line) {
|
|
|
+ if (cursor.line >= static_cast<size_t>(start_line) && cursor.line < static_cast<size_t>(end_line)) {
|
|
|
int cursor_screen_x = static_cast<int>(cursor.column * char_width_);
|
|
|
int cursor_screen_y = static_cast<int>((cursor.line - start_line) * line_height_);
|
|
|
|
|
|
- // Get cursor color from theme
|
|
|
- Color cursor_color = theme ? theme->get_fg_color(ThemeElement::Cursor) : Color(255, 255, 255);
|
|
|
+ // Get cursor color from theme (fallback to a visible color)
|
|
|
+ Color cursor_color = theme ? theme->get_fg_color(ThemeElement::Cursor) : Color(255, 255, 0); // Yellow fallback
|
|
|
+
|
|
|
+ // Draw cursor background (filled rectangle)
|
|
|
cr->set_source_rgb(cursor_color.r / 255.0, cursor_color.g / 255.0, cursor_color.b / 255.0);
|
|
|
-
|
|
|
- // Draw a block cursor
|
|
|
cr->rectangle(cursor_screen_x, cursor_screen_y, char_width_, line_height_);
|
|
|
cr->fill();
|
|
|
+
|
|
|
+ // Draw the character at cursor position in inverted color
|
|
|
+ if (cursor.line < buffer.line_count()) {
|
|
|
+ const auto& line_text = buffer.line(cursor.line);
|
|
|
+ if (cursor.column < line_text.length()) {
|
|
|
+ std::string char_at_cursor(1, line_text[cursor.column]);
|
|
|
+ layout->set_text(char_at_cursor);
|
|
|
+
|
|
|
+ // Use background color for text to create inversion effect
|
|
|
+ cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
|
|
|
+ cr->move_to(cursor_screen_x, cursor_screen_y + ascent_);
|
|
|
+ layout->show_in_cairo_context(cr);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -285,9 +316,22 @@ protected:
|
|
|
// 4. Normal Mode Processing (Pass to Lua)
|
|
|
if (is_control && key_name.length() == 1) key_name = "C-" + key_name;
|
|
|
if (is_lumacs_meta) key_name = "M-" + key_name; // Use combined meta/alt
|
|
|
-
|
|
|
+
|
|
|
// std::cout << "Key: " << key_name << std::endl;
|
|
|
- core_->lua_api()->process_key(key_name);
|
|
|
+ KeyResult result = core_->lua_api()->process_key(key_name);
|
|
|
+
|
|
|
+ // Fallback: Insert printable characters if unbound
|
|
|
+ if (result == KeyResult::Unbound && key_name.size() == 1 &&
|
|
|
+ key_name[0] >= 32 && key_name[0] <= 126) {
|
|
|
+ auto cursor = core_->cursor();
|
|
|
+ core_->buffer().insert_char(cursor, key_name[0]);
|
|
|
+ core_->set_cursor({cursor.line, cursor.column + 1});
|
|
|
+ }
|
|
|
+
|
|
|
+ // Request redraw after processing input
|
|
|
+ if (drawing_area_) {
|
|
|
+ drawing_area_->queue_draw();
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
};
|