Fix void crash in kge.
Some checks failed
Release / Bump Homebrew formula (push) Has been cancelled
Release / Build Linux amd64 (push) Has been cancelled
Release / Build Linux arm64 (push) Has been cancelled
Release / Build macOS arm64 (.app) (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled

This commit is contained in:
2025-11-30 21:51:03 -08:00
parent e7eb35626c
commit 35e957b326
3 changed files with 79 additions and 73 deletions

9
.idea/workspace.xml generated
View File

@@ -34,8 +34,9 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Actually add the screenshot."> <list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Actually add the screenshot.">
<change beforePath="$PROJECT_DIR$/.github/workflows/release.yml" beforeDir="false" afterPath="$PROJECT_DIR$/.github/workflows/release.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/CMakeLists.txt" beforeDir="false" afterPath="$PROJECT_DIR$/CMakeLists.txt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/GUIRenderer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIRenderer.cc" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -128,9 +129,9 @@
</key> </key>
</component> </component>
<component name="RunManager" selected="CMake Application.kge"> <component name="RunManager" selected="CMake Application.kge">
<configuration default="true" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true"> <configuration default="true" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
<method v="2"> <method v="2">
<option name="CLION.EXTERNAL.BUILD" enabled="true" /> <option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method> </method>
</configuration> </configuration>
<configuration name="imgui" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="imgui" CONFIG_NAME="Debug"> <configuration name="imgui" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="kte" TARGET_NAME="imgui" CONFIG_NAME="Debug">
@@ -169,7 +170,7 @@
<workItem from="1764539556448" duration="156000" /> <workItem from="1764539556448" duration="156000" />
<workItem from="1764539725338" duration="1075000" /> <workItem from="1764539725338" duration="1075000" />
<workItem from="1764542392763" duration="3512000" /> <workItem from="1764542392763" duration="3512000" />
<workItem from="1764548345516" duration="9962000" /> <workItem from="1764548345516" duration="12773000" />
</task> </task>
<task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions."> <task id="LOCAL-00001" summary="Add undo/redo infrastructure and buffer management additions.">
<option name="closed" value="true" /> <option name="closed" value="true" />

View File

@@ -4,7 +4,7 @@ project(kte)
include(GNUInstallDirs) include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(KTE_VERSION "1.0.3") set(KTE_VERSION "1.0.4")
# Default to terminal-only build to avoid SDL/OpenGL dependency by default. # Default to terminal-only build to avoid SDL/OpenGL dependency by default.
# Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available. # Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available.
@@ -201,5 +201,5 @@ if (${BUILD_GUI})
endif () endif ()
# Install kge man page only when GUI is built # Install kge man page only when GUI is built
install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
install(FILES kge.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons) install(FILES kge.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons)
endif () endif ()

View File

@@ -151,14 +151,14 @@ GUIRenderer::Draw(Editor &ed)
} }
} }
} }
// Handle mouse click before rendering to avoid dependent on drawn items // Handle mouse click before rendering to avoid dependent on drawn items
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
ImVec2 mp = ImGui::GetIO().MousePos; ImVec2 mp = ImGui::GetIO().MousePos;
// Compute viewport-relative row so (0) is top row of the visible area // Compute viewport-relative row so (0) is top row of the visible area
float vy_f = (mp.y - list_origin.y - scroll_y) / row_h; float vy_f = (mp.y - list_origin.y - scroll_y) / row_h;
long vy = static_cast<long>(vy_f); long vy = static_cast<long>(vy_f);
if (vy < 0) if (vy < 0)
vy = 0; vy = 0;
// Clamp vy within visible content height to avoid huge jumps // Clamp vy within visible content height to avoid huge jumps
ImVec2 cr_min = ImGui::GetWindowContentRegionMin(); ImVec2 cr_min = ImGui::GetWindowContentRegionMin();
@@ -170,70 +170,75 @@ GUIRenderer::Draw(Editor &ed)
if (vy >= vis_rows) if (vy >= vis_rows)
vy = vis_rows - 1; vy = vis_rows - 1;
// Translate viewport row to buffer row using Buffer::Rowoffs // Translate viewport row to buffer row using Buffer::Rowoffs
std::size_t by = buf->Rowoffs() + static_cast<std::size_t>(vy); std::size_t by = buf->Rowoffs() + static_cast<std::size_t>(vy);
if (by >= lines.size()) { if (by >= lines.size()) {
if (!lines.empty()) if (!lines.empty())
by = lines.size() - 1; by = lines.size() - 1;
else else
by = 0; by = 0;
} }
// Compute desired pixel X inside the viewport content (subtract horizontal scroll) // Compute desired pixel X inside the viewport content (subtract horizontal scroll)
float px = (mp.x - list_origin.x - scroll_x); float px = (mp.x - list_origin.x - scroll_x);
if (px < 0.0f) if (px < 0.0f)
px = 0.0f; px = 0.0f;
// Convert pixel X to a render-column target including horizontal col offset // Empty buffer guard: if there are no lines yet, just move to 0:0
// Use our own tab expansion of width 8 to match command layer logic. if (lines.empty()) {
const std::string &line_clicked = lines[by]; Execute(ed, CommandId::MoveCursorTo, std::string("0:0"));
const std::size_t tabw = 8; } else {
// We iterate source columns computing absolute rendered column (rx_abs) from 0, // Convert pixel X to a render-column target including horizontal col offset
// then translate to viewport-space by subtracting Coloffs. // Use our own tab expansion of width 8 to match command layer logic.
std::size_t coloffs = buf->Coloffs(); const std::string &line_clicked = lines[by];
std::size_t rx_abs = 0; // absolute rendered column const std::size_t tabw = 8;
std::size_t i = 0; // source column iterator // We iterate source columns computing absolute rendered column (rx_abs) from 0,
// then translate to viewport-space by subtracting Coloffs.
std::size_t coloffs = buf->Coloffs();
std::size_t rx_abs = 0; // absolute rendered column
std::size_t i = 0; // source column iterator
// Fast-forward i until rx_abs >= coloffs to align with leftmost visible column // Fast-forward i until rx_abs >= coloffs to align with leftmost visible column
if (!line_clicked.empty() && coloffs > 0) { if (!line_clicked.empty() && coloffs > 0) {
while (i < line_clicked.size() && rx_abs < coloffs) { while (i < line_clicked.size() && rx_abs < coloffs) {
if (line_clicked[i] == '\t') { if (line_clicked[i] == '\t') {
rx_abs += (tabw - (rx_abs % tabw)); rx_abs += (tabw - (rx_abs % tabw));
} else { } else {
rx_abs += 1; rx_abs += 1;
} }
++i; ++i;
} }
} }
// Now search for closest source column to clicked px within/after viewport // Now search for closest source column to clicked px within/after viewport
std::size_t best_col = i; // default to first visible column std::size_t best_col = i; // default to first visible column
float best_dist = std::numeric_limits<float>::infinity(); float best_dist = std::numeric_limits<float>::infinity();
while (true) { while (true) {
// For i in [current..size], evaluate candidate including the implicit end position // For i in [current..size], evaluate candidate including the implicit end position
std::size_t rx_view = (rx_abs >= coloffs) ? (rx_abs - coloffs) : 0; std::size_t rx_view = (rx_abs >= coloffs) ? (rx_abs - coloffs) : 0;
float rx_px = static_cast<float>(rx_view) * space_w; float rx_px = static_cast<float>(rx_view) * space_w;
float dist = std::fabs(px - rx_px); float dist = std::fabs(px - rx_px);
if (dist <= best_dist) { if (dist <= best_dist) {
best_dist = dist; best_dist = dist;
best_col = i; best_col = i;
} }
if (i == line_clicked.size()) if (i == line_clicked.size())
break; break;
// advance to next source column // advance to next source column
if (line_clicked[i] == '\t') { if (line_clicked[i] == '\t') {
rx_abs += (tabw - (rx_abs % tabw)); rx_abs += (tabw - (rx_abs % tabw));
} else { } else {
rx_abs += 1; rx_abs += 1;
} }
++i; ++i;
} }
// Dispatch absolute buffer coordinates (row:col) // Dispatch absolute buffer coordinates (row:col)
char tmp[64]; char tmp[64];
std::snprintf(tmp, sizeof(tmp), "%zu:%zu", by, best_col); std::snprintf(tmp, sizeof(tmp), "%zu:%zu", by, best_col);
Execute(ed, CommandId::MoveCursorTo, std::string(tmp)); Execute(ed, CommandId::MoveCursorTo, std::string(tmp));
} }
}
// Cache current horizontal offset in rendered columns // Cache current horizontal offset in rendered columns
const std::size_t coloffs_now = buf->Coloffs(); const std::size_t coloffs_now = buf->Coloffs();
for (std::size_t i = rowoffs; i < lines.size(); ++i) { for (std::size_t i = rowoffs; i < lines.size(); ++i) {