Add GUI initialization updates and improve navigation commands.
- Implement terminal detachment for GUI mode to enable terminal closure post-launch. - Add `+N` support for opening files at specific line numbers and refine cursor positioning. - Introduce `JumpToLine` command for direct navigation by line number. - Enhance mouse wheel handling for line-wise scrolling.
This commit is contained in:
@@ -45,7 +45,7 @@ GUIRenderer::Draw(Editor &ed)
|
||||
const auto &lines = buf->Rows();
|
||||
// Reserve space for status bar at bottom
|
||||
ImGui::BeginChild("scroll", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), false,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
// Detect click-to-move inside this scroll region
|
||||
ImVec2 list_origin = ImGui::GetCursorScreenPos();
|
||||
float scroll_y = ImGui::GetScrollY();
|
||||
@@ -56,24 +56,42 @@ GUIRenderer::Draw(Editor &ed)
|
||||
const float line_h = ImGui::GetTextLineHeight();
|
||||
const float row_h = ImGui::GetTextLineHeightWithSpacing();
|
||||
const float space_w = ImGui::CalcTextSize(" ").x;
|
||||
// If the command layer requested a specific top-of-screen (via Buffer::Rowoffs),
|
||||
// force the ImGui scroll to match so paging aligns the first visible row.
|
||||
// Two-way sync between Buffer::Rowoffs and ImGui scroll position:
|
||||
// - If command layer changed Buffer::Rowoffs since last frame, drive ImGui scroll from it.
|
||||
// - Otherwise, propagate ImGui scroll to Buffer::Rowoffs so command layer has an up-to-date view.
|
||||
// This prevents clicks/wheel from being immediately overridden by stale offsets.
|
||||
bool forced_scroll = false;
|
||||
{
|
||||
std::size_t desired_top = buf->Rowoffs();
|
||||
long current_top = static_cast<long>(scroll_y / row_h);
|
||||
if (static_cast<long>(desired_top) != current_top) {
|
||||
ImGui::SetScrollY(static_cast<float>(desired_top) * row_h);
|
||||
static long prev_buf_rowoffs = -1; // previous frame's Buffer::Rowoffs
|
||||
static float prev_scroll_y = -1.0f; // previous frame's ImGui scroll Y in pixels
|
||||
|
||||
const long buf_rowoffs = static_cast<long>(buf->Rowoffs());
|
||||
const long scroll_top = static_cast<long>(scroll_y / row_h);
|
||||
|
||||
// Detect programmatic change (e.g., keyboard navigation ensured visibility)
|
||||
if (prev_buf_rowoffs >= 0 && buf_rowoffs != prev_buf_rowoffs) {
|
||||
ImGui::SetScrollY(static_cast<float>(buf_rowoffs) * row_h);
|
||||
scroll_y = ImGui::GetScrollY();
|
||||
forced_scroll = true;
|
||||
} else {
|
||||
// If user scrolled (scroll_y changed), update buffer row offset accordingly
|
||||
if (prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
|
||||
if (Buffer *mbuf = const_cast<Buffer *>(buf)) {
|
||||
// Keep horizontal offset owned by GUI; only update vertical offset here
|
||||
mbuf->SetOffsets(static_cast<std::size_t>(std::max(0L, scroll_top)),
|
||||
mbuf->Coloffs());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update trackers for next frame
|
||||
prev_buf_rowoffs = static_cast<long>(buf->Rowoffs());
|
||||
prev_scroll_y = ImGui::GetScrollY();
|
||||
}
|
||||
// Synchronize cursor and scrolling.
|
||||
// A) When the user scrolls and the cursor goes off-screen, move the cursor to the nearest visible row.
|
||||
// B) When the cursor moves (via keyboard commands), scroll it back into view.
|
||||
// Ensure the cursor is visible even on the first frame or when it didn't move,
|
||||
// unless we already forced scrolling from Buffer::Rowoffs this frame.
|
||||
{
|
||||
static float prev_scroll_y = -1.0f;
|
||||
static long prev_cursor_y = -1;
|
||||
// Compute visible row range using the child window height
|
||||
float child_h = ImGui::GetWindowHeight();
|
||||
long first_row = static_cast<long>(scroll_y / row_h);
|
||||
@@ -82,37 +100,7 @@ GUIRenderer::Draw(Editor &ed)
|
||||
vis_rows = 1;
|
||||
long last_row = first_row + vis_rows - 1;
|
||||
|
||||
// A) If user scrolled (scroll_y changed), and cursor outside, move cursor to nearest visible row
|
||||
// Skip this when we just forced a scroll alignment this frame (programmatic change).
|
||||
if (!forced_scroll && prev_scroll_y >= 0.0f && scroll_y != prev_scroll_y) {
|
||||
long cyr = static_cast<long>(cy);
|
||||
if (cyr < first_row || cyr > last_row) {
|
||||
long new_row = (cyr < first_row) ? first_row : last_row;
|
||||
if (new_row < 0)
|
||||
new_row = 0;
|
||||
if (new_row >= static_cast<long>(lines.size()))
|
||||
new_row = static_cast<long>(lines.empty() ? 0 : (lines.size() - 1));
|
||||
// Clamp column to line length
|
||||
std::size_t new_col = 0;
|
||||
if (!lines.empty()) {
|
||||
const std::string &l = lines[static_cast<std::size_t>(new_row)];
|
||||
new_col = std::min<std::size_t>(cx, l.size());
|
||||
}
|
||||
char tmp2[64];
|
||||
std::snprintf(tmp2, sizeof(tmp2), "%ld:%zu", new_row, new_col);
|
||||
Execute(ed, CommandId::MoveCursorTo, std::string(tmp2));
|
||||
cy = buf->Cury();
|
||||
cx = buf->Curx();
|
||||
cyr = static_cast<long>(cy);
|
||||
// Update visible range again in case content changed
|
||||
first_row = static_cast<long>(ImGui::GetScrollY() / row_h);
|
||||
last_row = first_row + vis_rows - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// B) If cursor moved since last frame and is outside the visible region, scroll to reveal it
|
||||
// Skip this when we just forced a top-of-screen alignment this frame.
|
||||
if (!forced_scroll && prev_cursor_y >= 0 && static_cast<long>(cy) != prev_cursor_y) {
|
||||
if (!forced_scroll) {
|
||||
long cyr = static_cast<long>(cy);
|
||||
if (cyr < first_row || cyr > last_row) {
|
||||
float target = (static_cast<float>(cyr) - std::max(0L, vis_rows / 2)) * row_h;
|
||||
@@ -128,9 +116,6 @@ GUIRenderer::Draw(Editor &ed)
|
||||
last_row = first_row + vis_rows - 1;
|
||||
}
|
||||
}
|
||||
|
||||
prev_scroll_y = ImGui::GetScrollY();
|
||||
prev_cursor_y = static_cast<long>(cy);
|
||||
}
|
||||
// Handle mouse click before rendering to avoid dependent on drawn items
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
@@ -245,8 +230,16 @@ GUIRenderer::Draw(Editor &ed)
|
||||
std::snprintf(rbuf, sizeof(rbuf), "%d,%d | M: not set", row1, col1);
|
||||
std::string right = rbuf;
|
||||
|
||||
// Middle message
|
||||
const std::string &msg = ed.Status();
|
||||
// Middle message: if a prompt is active, show "Label: text"; otherwise show status
|
||||
std::string msg;
|
||||
if (ed.PromptActive()) {
|
||||
msg = ed.PromptLabel();
|
||||
if (!msg.empty())
|
||||
msg += ": ";
|
||||
msg += ed.PromptText();
|
||||
} else {
|
||||
msg = ed.Status();
|
||||
}
|
||||
|
||||
// Measurements
|
||||
ImVec2 left_sz = ImGui::CalcTextSize(left.c_str());
|
||||
|
||||
Reference in New Issue
Block a user