|
|
@@ -25,6 +25,10 @@ class GtkEditor : public IEditorView {
|
|
|
public:
|
|
|
GtkEditor() : core_(nullptr) {}
|
|
|
~GtkEditor() override {
|
|
|
+ // Disconnect cursor timer first to prevent callbacks during destruction
|
|
|
+ if (cursor_timer_connection_.connected()) {
|
|
|
+ cursor_timer_connection_.disconnect();
|
|
|
+ }
|
|
|
// Clear core pointer to prevent any callbacks during GTK cleanup
|
|
|
core_ = nullptr;
|
|
|
// Clear widget pointers - GTK manages their lifetime
|
|
|
@@ -54,6 +58,10 @@ public:
|
|
|
}
|
|
|
|
|
|
if (event == EditorEvent::Quit) {
|
|
|
+ // Disconnect timer before quitting to prevent segfault
|
|
|
+ if (cursor_timer_connection_.connected()) {
|
|
|
+ cursor_timer_connection_.disconnect();
|
|
|
+ }
|
|
|
app_->quit(); // Quit the application gracefully
|
|
|
}
|
|
|
}
|
|
|
@@ -85,6 +93,10 @@ private:
|
|
|
double char_width_ = 0;
|
|
|
double line_height_ = 0;
|
|
|
double ascent_ = 0;
|
|
|
+
|
|
|
+ // Cursor blinking
|
|
|
+ bool cursor_visible_ = true;
|
|
|
+ sigc::connection cursor_timer_connection_;
|
|
|
|
|
|
protected:
|
|
|
void on_activate() {
|
|
|
@@ -110,6 +122,11 @@ protected:
|
|
|
// Show window
|
|
|
window_->present();
|
|
|
drawing_area_->grab_focus();
|
|
|
+
|
|
|
+ // Set up cursor blinking timer (500ms intervals like Emacs)
|
|
|
+ cursor_timer_connection_ = Glib::signal_timeout().connect(
|
|
|
+ sigc::mem_fun(*this, &GtkEditor::on_cursor_blink), 500
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
// Rendering
|
|
|
@@ -176,19 +193,47 @@ protected:
|
|
|
layout->show_in_cairo_context(cr);
|
|
|
}
|
|
|
|
|
|
- // Render Cursor - Draw at same position as text
|
|
|
- if (cursor.line >= static_cast<size_t>(start_line) && cursor.line < static_cast<size_t>(end_line)) {
|
|
|
+ // Render Cursor - Emacs-style blinking block cursor with color inversion
|
|
|
+ if (cursor_visible_ && cursor.line >= static_cast<size_t>(start_line) && cursor.line < static_cast<size_t>(end_line)) {
|
|
|
double cursor_screen_x = cursor.column * char_width_;
|
|
|
int screen_y = cursor.line - start_line;
|
|
|
double cursor_y = screen_y * line_height_;
|
|
|
-
|
|
|
- // Draw bright green vertical bar aligned with text
|
|
|
- cr->set_source_rgb(0.0, 1.0, 0.0); // Bright green
|
|
|
- cr->rectangle(cursor_screen_x, cursor_y, 2.0, line_height_);
|
|
|
+
|
|
|
+ // Get the character under cursor for rendering with inverted colors
|
|
|
+ size_t buffer_line_idx = cursor.line;
|
|
|
+ const auto& cursor_line_text = buffer.line(buffer_line_idx);
|
|
|
+ char cursor_char = (cursor.column < cursor_line_text.length()) ? cursor_line_text[cursor.column] : ' ';
|
|
|
+
|
|
|
+ // Draw block cursor background (inverted background color)
|
|
|
+ Color cursor_bg = theme ? theme->get_fg_color(ThemeElement::Normal) : Color(255, 255, 255);
|
|
|
+ cr->set_source_rgb(cursor_bg.r / 255.0, cursor_bg.g / 255.0, cursor_bg.b / 255.0);
|
|
|
+ cr->rectangle(cursor_screen_x, cursor_y, char_width_, line_height_);
|
|
|
cr->fill();
|
|
|
+
|
|
|
+ // Draw the character with inverted color (background color as foreground)
|
|
|
+ if (cursor_char != '\0') {
|
|
|
+ Color cursor_fg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
|
|
|
+ cr->set_source_rgb(cursor_fg.r / 255.0, cursor_fg.g / 255.0, cursor_fg.b / 255.0);
|
|
|
+
|
|
|
+ layout->set_text(std::string(1, cursor_char));
|
|
|
+ cr->move_to(cursor_screen_x, cursor_y);
|
|
|
+ layout->show_in_cairo_context(cr);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Cursor blinking callback
|
|
|
+ bool on_cursor_blink() {
|
|
|
+ // Safety check - don't blink if core is destroyed or no drawing area
|
|
|
+ if (!core_ || !drawing_area_) {
|
|
|
+ return false; // Stop the timer
|
|
|
+ }
|
|
|
+
|
|
|
+ cursor_visible_ = !cursor_visible_;
|
|
|
+ drawing_area_->queue_draw();
|
|
|
+ return true; // Continue timer
|
|
|
+ }
|
|
|
+
|
|
|
std::string resolve_key(guint keyval, Gdk::ModifierType state) {
|
|
|
// Handle modifier keys
|
|
|
unsigned int state_uint = static_cast<unsigned int>(state);
|
|
|
@@ -234,6 +279,9 @@ protected:
|
|
|
// Safety check - don't process keys if core is destroyed
|
|
|
if (!core_) return false;
|
|
|
|
|
|
+ // Make cursor visible immediately when typing
|
|
|
+ cursor_visible_ = true;
|
|
|
+
|
|
|
// 1. Resolve the base key name
|
|
|
std::string key_name = resolve_key(keyval, state);
|
|
|
if (key_name.empty()) return false;
|