|
@@ -366,12 +366,42 @@ protected:
|
|
|
controller->signal_key_pressed().connect([this, node](guint keyval, guint keycode, Gdk::ModifierType state) -> bool {
|
|
controller->signal_key_pressed().connect([this, node](guint keyval, guint keycode, Gdk::ModifierType state) -> bool {
|
|
|
// Set this window as active when it receives input
|
|
// Set this window as active when it receives input
|
|
|
if (node && node->window && core_) {
|
|
if (node && node->window && core_) {
|
|
|
- // TODO: Set active window in 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
return on_key_pressed(keyval, keycode, state);
|
|
return on_key_pressed(keyval, keycode, state);
|
|
|
}, false);
|
|
}, false);
|
|
|
drawing_area->add_controller(controller);
|
|
drawing_area->add_controller(controller);
|
|
|
|
|
|
|
|
|
|
+ // 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;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ drawing_area->add_controller(focus_controller);
|
|
|
|
|
+
|
|
|
// Store reference for single-window compatibility
|
|
// Store reference for single-window compatibility
|
|
|
if (!drawing_area_) {
|
|
if (!drawing_area_) {
|
|
|
drawing_area_ = drawing_area;
|
|
drawing_area_ = drawing_area;
|
|
@@ -402,9 +432,11 @@ protected:
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Draw a specific window (factored out from on_draw)
|
|
// Draw a specific window (factored out from on_draw)
|
|
|
- void draw_window(const Cairo::RefPtr<Cairo::Context>& cr, int /*width*/, int /*height*/, std::shared_ptr<Window> window) {
|
|
|
|
|
|
|
+ void draw_window(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height, std::shared_ptr<Window> window) {
|
|
|
if (!core_ || !window) return;
|
|
if (!core_ || !window) return;
|
|
|
|
|
|
|
|
|
|
+ const auto cursor = window->cursor();
|
|
|
|
|
+
|
|
|
// Fill background
|
|
// Fill background
|
|
|
auto theme = core_->active_theme();
|
|
auto theme = core_->active_theme();
|
|
|
Color bg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
|
|
Color bg = theme ? theme->get_bg_color(ThemeElement::Background) : Color(0, 0, 0);
|
|
@@ -412,17 +444,139 @@ protected:
|
|
|
cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
|
|
cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
|
|
|
cr->paint();
|
|
cr->paint();
|
|
|
|
|
|
|
|
- // For now, just draw a simple indicator showing the buffer name
|
|
|
|
|
- auto layout = Pango::Layout::create(cr);
|
|
|
|
|
|
|
+ // 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());
|
|
|
|
|
+
|
|
|
|
|
+ // Font configuration
|
|
|
Pango::FontDescription font_desc("Monospace 12");
|
|
Pango::FontDescription font_desc("Monospace 12");
|
|
|
layout->set_font_description(font_desc);
|
|
layout->set_font_description(font_desc);
|
|
|
|
|
+
|
|
|
|
|
+ // Get font metrics
|
|
|
|
|
+ Pango::FontMetrics metrics = layout->get_context()->get_metrics(font_desc);
|
|
|
|
|
+ line_height_ = (double)metrics.get_height() / PANGO_SCALE;
|
|
|
|
|
+ ascent_ = (double)metrics.get_ascent() / PANGO_SCALE;
|
|
|
|
|
|
|
|
- std::string buffer_name = "Window: " + window->buffer().name();
|
|
|
|
|
- layout->set_text(buffer_name);
|
|
|
|
|
|
|
+ // Measure character width (for a single 'm' character)
|
|
|
|
|
+ layout->set_text("m");
|
|
|
|
|
+ Pango::Rectangle ink_rect, logical_rect;
|
|
|
|
|
+ layout->get_pixel_extents(ink_rect, logical_rect);
|
|
|
|
|
+ char_width_ = (double)logical_rect.get_width(); // Already in pixels
|
|
|
|
|
+
|
|
|
|
|
+ // Update window's viewport size based on actual font metrics and padding
|
|
|
|
|
+ int content_width_px = width - static_cast<int>(PADDING_LEFT + PADDING_RIGHT);
|
|
|
|
|
+ int content_height_px = height - static_cast<int>(PADDING_TOP + PADDING_BOTTOM);
|
|
|
|
|
+
|
|
|
|
|
+ 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 and minibuffer at bottom only for main window
|
|
|
|
|
+ bool is_main_window = (window == core_->active_window());
|
|
|
|
|
+ int editor_lines = is_main_window ? std::max(0, visible_lines - 2) : visible_lines;
|
|
|
|
|
+ window->set_viewport_size(visible_cols, editor_lines);
|
|
|
|
|
+
|
|
|
|
|
+ // Get default foreground color from theme
|
|
|
Color fg = theme ? theme->get_fg_color(ThemeElement::Normal) : Color(255, 255, 255);
|
|
Color fg = theme ? theme->get_fg_color(ThemeElement::Normal) : Color(255, 255, 255);
|
|
|
cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
|
|
cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
|
|
|
- cr->move_to(10, 10);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Render visible lines
|
|
|
|
|
+ const auto& buffer = window->buffer();
|
|
|
|
|
+ auto [start_line, end_line] = window->visible_line_range();
|
|
|
|
|
+ int horizontal_offset = window->viewport().horizontal_offset;
|
|
|
|
|
+
|
|
|
|
|
+ for (int screen_y = 0; screen_y < editor_lines && start_line + screen_y < end_line; ++screen_y) {
|
|
|
|
|
+ size_t buffer_line_idx = start_line + screen_y;
|
|
|
|
|
+ const auto& line_text = buffer.line(buffer_line_idx);
|
|
|
|
|
+
|
|
|
|
|
+ // Apply horizontal scrolling - show only the visible portion of the line
|
|
|
|
|
+ std::string visible_text;
|
|
|
|
|
+ if (horizontal_offset < static_cast<int>(line_text.length())) {
|
|
|
|
|
+ visible_text = line_text.substr(horizontal_offset);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ layout->set_text(visible_text);
|
|
|
|
|
+
|
|
|
|
|
+ // Render text at proper position
|
|
|
|
|
+ double text_x = PADDING_LEFT;
|
|
|
|
|
+ double text_y = PADDING_TOP + screen_y * line_height_;
|
|
|
|
|
+ cr->move_to(text_x, text_y);
|
|
|
|
|
+ layout->show_in_cairo_context(cr);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Render Cursor if this is the active window
|
|
|
|
|
+ if (window == core_->active_window() && cursor_visible_ &&
|
|
|
|
|
+ cursor.line >= static_cast<size_t>(start_line) &&
|
|
|
|
|
+ cursor.line < static_cast<size_t>(end_line)) {
|
|
|
|
|
+
|
|
|
|
|
+ int screen_y = cursor.line - start_line;
|
|
|
|
|
+ double cursor_y = PADDING_TOP + screen_y * line_height_;
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate the exact X position
|
|
|
|
|
+ double cursor_screen_x = PADDING_LEFT + (static_cast<int>(cursor.column) - horizontal_offset) * char_width_;
|
|
|
|
|
+
|
|
|
|
|
+ // Only render cursor if it's visible horizontally
|
|
|
|
|
+ if (cursor_screen_x >= PADDING_LEFT && cursor_screen_x < (width - PADDING_RIGHT)) {
|
|
|
|
|
+ // 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' && cursor_char != ' ') {
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Only render modeline and minibuffer for the main window
|
|
|
|
|
+ if (is_main_window) {
|
|
|
|
|
+ render_modeline_for_window(cr, width, height, layout, window);
|
|
|
|
|
+ render_minibuffer(cr, width, height, layout);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Render modeline for a specific window
|
|
|
|
|
+ void render_modeline_for_window(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height,
|
|
|
|
|
+ const Glib::RefPtr<Pango::Layout>& layout, std::shared_ptr<Window> window) {
|
|
|
|
|
+ if (!core_ || !window) return;
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate modeline position (second line from bottom)
|
|
|
|
|
+ double modeline_y = height - (2 * line_height_) - PADDING_BOTTOM;
|
|
|
|
|
+ double modeline_x = PADDING_LEFT;
|
|
|
|
|
+
|
|
|
|
|
+ // Get theme colors
|
|
|
|
|
+ auto theme = core_->active_theme();
|
|
|
|
|
+ Color bg = theme ? theme->get_bg_color(ThemeElement::StatusLine) : Color(40, 40, 40);
|
|
|
|
|
+ Color fg = theme ? theme->get_fg_color(ThemeElement::StatusLine) : Color(200, 200, 200);
|
|
|
|
|
+
|
|
|
|
|
+ // Draw modeline background
|
|
|
|
|
+ cr->set_source_rgb(bg.r / 255.0, bg.g / 255.0, bg.b / 255.0);
|
|
|
|
|
+ cr->rectangle(0, modeline_y, width, line_height_);
|
|
|
|
|
+ cr->fill();
|
|
|
|
|
+
|
|
|
|
|
+ // Build modeline content
|
|
|
|
|
+ auto cursor = window->cursor();
|
|
|
|
|
+ auto& buffer = window->buffer();
|
|
|
|
|
+ std::string modeline_text = " " + buffer.name() +
|
|
|
|
|
+ " Line " + std::to_string(cursor.line + 1) +
|
|
|
|
|
+ ":" + std::to_string(cursor.column + 1) +
|
|
|
|
|
+ " (" + (buffer.is_modified() ? "modified" : "saved") + ")";
|
|
|
|
|
+
|
|
|
|
|
+ // Render modeline text
|
|
|
|
|
+ cr->set_source_rgb(fg.r / 255.0, fg.g / 255.0, fg.b / 255.0);
|
|
|
|
|
+ layout->set_text(modeline_text);
|
|
|
|
|
+ cr->move_to(modeline_x, modeline_y);
|
|
|
layout->show_in_cairo_context(cr);
|
|
layout->show_in_cairo_context(cr);
|
|
|
}
|
|
}
|
|
|
|
|
|