|
@@ -68,7 +68,33 @@ public:
|
|
|
drawing_area_->queue_draw();
|
|
drawing_area_->queue_draw();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (event == EditorEvent::Quit) {
|
|
|
|
|
|
|
+ // Handle mode switching events
|
|
|
|
|
+ if (event == EditorEvent::CommandMode) {
|
|
|
|
|
+ mode_ = Mode::Command;
|
|
|
|
|
+ command_buffer_.clear();
|
|
|
|
|
+ message_line_.clear();
|
|
|
|
|
+ if (drawing_area_) drawing_area_->queue_draw();
|
|
|
|
|
+ } else if (event == EditorEvent::FindFileMode) {
|
|
|
|
|
+ mode_ = Mode::FindFile;
|
|
|
|
|
+ command_buffer_.clear();
|
|
|
|
|
+ message_line_.clear();
|
|
|
|
|
+ if (drawing_area_) drawing_area_->queue_draw();
|
|
|
|
|
+ } else if (event == EditorEvent::BufferSwitchMode) {
|
|
|
|
|
+ mode_ = Mode::BufferSwitch;
|
|
|
|
|
+ command_buffer_.clear();
|
|
|
|
|
+ message_line_.clear();
|
|
|
|
|
+ if (drawing_area_) drawing_area_->queue_draw();
|
|
|
|
|
+ } else if (event == EditorEvent::KillBufferMode) {
|
|
|
|
|
+ mode_ = Mode::KillBuffer;
|
|
|
|
|
+ command_buffer_.clear();
|
|
|
|
|
+ message_line_.clear();
|
|
|
|
|
+ if (drawing_area_) drawing_area_->queue_draw();
|
|
|
|
|
+ } else if (event == EditorEvent::ISearchMode) {
|
|
|
|
|
+ mode_ = Mode::ISearch;
|
|
|
|
|
+ command_buffer_.clear();
|
|
|
|
|
+ message_line_.clear();
|
|
|
|
|
+ if (drawing_area_) drawing_area_->queue_draw();
|
|
|
|
|
+ } else if (event == EditorEvent::Quit) {
|
|
|
// Disconnect timer before quitting to prevent segfault
|
|
// Disconnect timer before quitting to prevent segfault
|
|
|
if (cursor_timer_connection_.connected()) {
|
|
if (cursor_timer_connection_.connected()) {
|
|
|
cursor_timer_connection_.disconnect();
|
|
cursor_timer_connection_.disconnect();
|
|
@@ -258,6 +284,9 @@ protected:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Render Minibuffer at bottom of screen
|
|
|
|
|
+ render_minibuffer(cr, width, height, layout);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Cursor blinking callback
|
|
// Cursor blinking callback
|
|
@@ -282,6 +311,96 @@ protected:
|
|
|
return true; // Continue timer
|
|
return true; // Continue timer
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Render the minibuffer at bottom of screen
|
|
|
|
|
+ void render_minibuffer(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height,
|
|
|
|
|
+ const Glib::RefPtr<Pango::Layout>& layout) {
|
|
|
|
|
+ if (!core_) return;
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate minibuffer position (bottom line with padding)
|
|
|
|
|
+ double minibuffer_y = height - line_height_ - PADDING_BOTTOM;
|
|
|
|
|
+ double minibuffer_x = PADDING_LEFT;
|
|
|
|
|
+
|
|
|
|
|
+ // Get theme colors
|
|
|
|
|
+ auto theme = core_->active_theme();
|
|
|
|
|
+ Color bg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
|
|
|
|
|
+ Color fg = theme ? theme->get_fg_color(ThemeElement::Normal) : Color(255, 255, 255);
|
|
|
|
|
+
|
|
|
|
|
+ // Draw minibuffer background (slightly different shade)
|
|
|
|
|
+ cr->set_source_rgb(bg.r / 255.0 * 0.9, bg.g / 255.0 * 0.9, bg.b / 255.0 * 0.9);
|
|
|
|
|
+ cr->rectangle(0, minibuffer_y - 2, width, line_height_ + 4);
|
|
|
|
|
+ cr->fill();
|
|
|
|
|
+
|
|
|
|
|
+ // Draw separator line above minibuffer
|
|
|
|
|
+ cr->set_source_rgb(fg.r / 255.0 * 0.5, fg.g / 255.0 * 0.5, fg.b / 255.0 * 0.5);
|
|
|
|
|
+ cr->set_line_width(1.0);
|
|
|
|
|
+ cr->move_to(0, minibuffer_y - 2);
|
|
|
|
|
+ cr->line_to(width, minibuffer_y - 2);
|
|
|
|
|
+ cr->stroke();
|
|
|
|
|
+
|
|
|
|
|
+ // Prepare minibuffer text
|
|
|
|
|
+ std::string minibuffer_text;
|
|
|
|
|
+ if (mode_ != Mode::Normal) {
|
|
|
|
|
+ // Show appropriate prompt based on mode
|
|
|
|
|
+ switch (mode_) {
|
|
|
|
|
+ case Mode::Command:
|
|
|
|
|
+ minibuffer_text = "M-x " + command_buffer_;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case Mode::FindFile:
|
|
|
|
|
+ minibuffer_text = "Find file: " + command_buffer_;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case Mode::BufferSwitch:
|
|
|
|
|
+ minibuffer_text = "Switch to buffer: " + command_buffer_;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case Mode::KillBuffer:
|
|
|
|
|
+ minibuffer_text = "Kill buffer: " + command_buffer_;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case Mode::ISearch:
|
|
|
|
|
+ minibuffer_text = "I-search: " + command_buffer_;
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ minibuffer_text = command_buffer_;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (!message_line_.empty()) {
|
|
|
|
|
+ // Show message in minibuffer
|
|
|
|
|
+ minibuffer_text = message_line_;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Render minibuffer text
|
|
|
|
|
+ if (!minibuffer_text.empty()) {
|
|
|
|
|
+ cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
|
|
|
|
|
+ layout->set_text(minibuffer_text);
|
|
|
|
|
+ cr->move_to(minibuffer_x, minibuffer_y);
|
|
|
|
|
+ layout->show_in_cairo_context(cr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Render minibuffer cursor if in interactive mode
|
|
|
|
|
+ if (mode_ != Mode::Normal) {
|
|
|
|
|
+ // Calculate cursor position in minibuffer
|
|
|
|
|
+ std::string prompt_text;
|
|
|
|
|
+ switch (mode_) {
|
|
|
|
|
+ case Mode::Command: prompt_text = "M-x "; break;
|
|
|
|
|
+ case Mode::FindFile: prompt_text = "Find file: "; break;
|
|
|
|
|
+ case Mode::BufferSwitch: prompt_text = "Switch to buffer: "; break;
|
|
|
|
|
+ case Mode::KillBuffer: prompt_text = "Kill buffer: "; break;
|
|
|
|
|
+ case Mode::ISearch: prompt_text = "I-search: "; break;
|
|
|
|
|
+ default: break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Measure prompt + command buffer to position cursor
|
|
|
|
|
+ std::string text_to_cursor = prompt_text + command_buffer_;
|
|
|
|
|
+ layout->set_text(text_to_cursor);
|
|
|
|
|
+ Pango::Rectangle ink_rect, logical_rect;
|
|
|
|
|
+ layout->get_pixel_extents(ink_rect, logical_rect);
|
|
|
|
|
+ double cursor_x = minibuffer_x + logical_rect.get_width();
|
|
|
|
|
+
|
|
|
|
|
+ // Draw minibuffer cursor
|
|
|
|
|
+ cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
|
|
|
|
|
+ cr->rectangle(cursor_x, minibuffer_y, 2.0, line_height_);
|
|
|
|
|
+ cr->fill();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
std::string resolve_key(guint keyval, Gdk::ModifierType state) {
|
|
std::string resolve_key(guint keyval, Gdk::ModifierType state) {
|
|
|
// Handle modifier keys
|
|
// Handle modifier keys
|
|
|
unsigned int state_uint = static_cast<unsigned int>(state);
|
|
unsigned int state_uint = static_cast<unsigned int>(state);
|