|
|
@@ -196,12 +196,19 @@ protected:
|
|
|
// Render visible lines
|
|
|
const auto& buffer = core_->buffer();
|
|
|
auto [start_line, end_line] = core_->active_window()->visible_line_range();
|
|
|
+ int horizontal_offset = core_->active_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);
|
|
|
|
|
|
- layout->set_text(line_text);
|
|
|
+ // 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 (Cairo expects top-left, not baseline)
|
|
|
double text_y = screen_y * line_height_;
|
|
|
@@ -211,29 +218,33 @@ protected:
|
|
|
|
|
|
// 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_;
|
|
|
+ // Account for horizontal scrolling
|
|
|
+ double cursor_screen_x = (static_cast<int>(cursor.column) - horizontal_offset) * char_width_;
|
|
|
int screen_y = cursor.line - start_line;
|
|
|
double cursor_y = screen_y * 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);
|
|
|
+ // Only render cursor if it's visible horizontally
|
|
|
+ if (cursor_screen_x >= 0 && cursor_screen_x < content_width_px) {
|
|
|
+ // 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] : ' ';
|
|
|
|
|
|
- layout->set_text(std::string(1, cursor_char));
|
|
|
- cr->move_to(cursor_screen_x, cursor_y);
|
|
|
- layout->show_in_cairo_context(cr);
|
|
|
+ // 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);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -279,6 +290,8 @@ protected:
|
|
|
case GDK_KEY_Right: key_name = "ArrowRight"; break;
|
|
|
case GDK_KEY_Home: key_name = "Home"; break;
|
|
|
case GDK_KEY_End: key_name = "End"; break;
|
|
|
+ case GDK_KEY_Page_Up: key_name = "PageUp"; break;
|
|
|
+ case GDK_KEY_Page_Down: key_name = "PageDown"; break;
|
|
|
case GDK_KEY_F3: key_name = "F3"; break;
|
|
|
case GDK_KEY_F4: key_name = "F4"; break;
|
|
|
default:
|
|
|
@@ -387,68 +400,76 @@ protected:
|
|
|
if (key_name == "Return") {
|
|
|
auto cursor = core_->cursor();
|
|
|
core_->buffer().insert_newline(cursor);
|
|
|
- core_->set_cursor({cursor.line + 1, 0});
|
|
|
+ core_->active_window()->set_cursor({cursor.line + 1, 0});
|
|
|
}
|
|
|
// Backspace - delete character
|
|
|
else if (key_name == "Backspace") {
|
|
|
auto cursor = core_->cursor();
|
|
|
core_->buffer().erase_char(cursor);
|
|
|
if (cursor.column > 0) {
|
|
|
- core_->set_cursor({cursor.line, cursor.column - 1});
|
|
|
+ core_->active_window()->set_cursor({cursor.line, cursor.column - 1});
|
|
|
} else if (cursor.line > 0) {
|
|
|
// Join with previous line
|
|
|
const auto& prev_line = core_->buffer().line(cursor.line - 1);
|
|
|
size_t prev_line_len = prev_line.length();
|
|
|
- core_->set_cursor({cursor.line - 1, prev_line_len});
|
|
|
+ core_->active_window()->set_cursor({cursor.line - 1, prev_line_len});
|
|
|
}
|
|
|
}
|
|
|
// Delete - delete character forward
|
|
|
else if (key_name == "Delete") {
|
|
|
auto cursor = core_->cursor();
|
|
|
core_->buffer().erase_char({cursor.line, cursor.column + 1});
|
|
|
+ // No cursor movement needed for forward delete
|
|
|
}
|
|
|
- // Arrow key navigation
|
|
|
+ // Arrow key navigation - use Window methods for proper scrolling
|
|
|
else if (key_name == "ArrowUp") {
|
|
|
- auto cursor = core_->cursor();
|
|
|
- if (cursor.line > 0) {
|
|
|
- const auto& target_line = core_->buffer().line(cursor.line - 1);
|
|
|
- size_t new_col = std::min(cursor.column, target_line.length());
|
|
|
- core_->set_cursor({cursor.line - 1, new_col});
|
|
|
- }
|
|
|
+ core_->active_window()->move_up();
|
|
|
}
|
|
|
else if (key_name == "ArrowDown") {
|
|
|
- auto cursor = core_->cursor();
|
|
|
- if (cursor.line < core_->buffer().line_count() - 1) {
|
|
|
- const auto& target_line = core_->buffer().line(cursor.line + 1);
|
|
|
- size_t new_col = std::min(cursor.column, target_line.length());
|
|
|
- core_->set_cursor({cursor.line + 1, new_col});
|
|
|
- }
|
|
|
+ core_->active_window()->move_down();
|
|
|
}
|
|
|
else if (key_name == "ArrowLeft") {
|
|
|
- auto cursor = core_->cursor();
|
|
|
- if (cursor.column > 0) {
|
|
|
- core_->set_cursor({cursor.line, cursor.column - 1});
|
|
|
- } else if (cursor.line > 0) {
|
|
|
- // Move to end of previous line
|
|
|
- const auto& prev_line = core_->buffer().line(cursor.line - 1);
|
|
|
- core_->set_cursor({cursor.line - 1, prev_line.length()});
|
|
|
- }
|
|
|
+ core_->active_window()->move_left();
|
|
|
}
|
|
|
else if (key_name == "ArrowRight") {
|
|
|
+ core_->active_window()->move_right();
|
|
|
+ }
|
|
|
+ // Page navigation - scroll multiple lines
|
|
|
+ else if (key_name == "PageUp") {
|
|
|
+ auto window = core_->active_window();
|
|
|
auto cursor = core_->cursor();
|
|
|
- const auto& current_line = core_->buffer().line(cursor.line);
|
|
|
- if (cursor.column < current_line.length()) {
|
|
|
- core_->set_cursor({cursor.line, cursor.column + 1});
|
|
|
- } else if (cursor.line < core_->buffer().line_count() - 1) {
|
|
|
- // Move to beginning of next line
|
|
|
- core_->set_cursor({cursor.line + 1, 0});
|
|
|
- }
|
|
|
+ int page_size = std::max(1, window->viewport().height - 2); // Leave 2 lines overlap
|
|
|
+
|
|
|
+ // Move cursor up by page size
|
|
|
+ size_t new_line = (cursor.line >= static_cast<size_t>(page_size))
|
|
|
+ ? cursor.line - page_size
|
|
|
+ : 0;
|
|
|
+
|
|
|
+ window->set_cursor({new_line, cursor.column});
|
|
|
+ }
|
|
|
+ else if (key_name == "PageDown") {
|
|
|
+ auto window = core_->active_window();
|
|
|
+ auto cursor = core_->cursor();
|
|
|
+ int page_size = std::max(1, window->viewport().height - 2); // Leave 2 lines overlap
|
|
|
+
|
|
|
+ // Move cursor down by page size
|
|
|
+ size_t max_line = core_->buffer().line_count() - 1;
|
|
|
+ size_t new_line = std::min(cursor.line + page_size, max_line);
|
|
|
+
|
|
|
+ window->set_cursor({new_line, cursor.column});
|
|
|
+ }
|
|
|
+ // Home/End navigation
|
|
|
+ else if (key_name == "Home") {
|
|
|
+ core_->active_window()->move_to_line_start();
|
|
|
+ }
|
|
|
+ else if (key_name == "End") {
|
|
|
+ core_->active_window()->move_to_line_end();
|
|
|
}
|
|
|
// Insert printable characters if unbound
|
|
|
else if (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});
|
|
|
+ core_->active_window()->set_cursor({cursor.line, cursor.column + 1});
|
|
|
|
|
|
// Debug cursor position
|
|
|
auto new_cursor = core_->cursor();
|