4 Commits

Author SHA1 Message Date
cc0c187481 Improve macOS app build process and bundle handling.
- Updated `make-app-release` script to use `macdeployqt` with proper verbosity and bundle fixup.
- Introduced post-build fixup using CMake's `BundleUtilities` to internalize non-Qt dylibs.
- Enhanced macOS bundle RPATH settings for accurate Framework resolution.
- Added optional `kge_fixup_bundle` CMake target for post-build handling.
- Refined `default.nix` to load Nixpkgs in a default argument.
2025-12-09 18:49:16 -08:00
a8dcfbec58 Fix C-k c handling. 2025-12-08 15:28:45 -08:00
65705e3354 bump version 2025-12-07 15:25:50 -08:00
e1f9a9eb6a Preserve cursor position on buffer reload.
- Remember and restore the cursor's position after reloading a buffer, clamping if necessary.
- Improve user experience by maintaining editing context.
2025-12-07 15:25:40 -08:00
6 changed files with 94 additions and 50 deletions

View File

@@ -4,7 +4,7 @@ project(kte)
include(GNUInstallDirs) include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(KTE_VERSION "1.5.2") set(KTE_VERSION "1.5.3")
# 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.
@@ -379,12 +379,18 @@ if (${BUILD_GUI})
${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist ${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist
@ONLY) @ONLY)
# Ensure proper macOS bundle properties and RPATH so our bundled
# frameworks are preferred over system/Homebrew ones.
set_target_properties(kge PROPERTIES set_target_properties(kge PROPERTIES
MACOSX_BUNDLE TRUE MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID} MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID}
MACOSX_BUNDLE_BUNDLE_NAME "kge" MACOSX_BUNDLE_BUNDLE_NAME "kge"
MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE}
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist") MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist"
# Prefer the app's bundled frameworks at runtime
INSTALL_RPATH "@executable_path/../Frameworks"
BUILD_WITH_INSTALL_RPATH TRUE
)
add_dependencies(kge kte) add_dependencies(kge kte)
add_custom_command(TARGET kge POST_BUILD add_custom_command(TARGET kge POST_BUILD
@@ -408,4 +414,19 @@ if (${BUILD_GUI})
# 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)
# Optional post-build bundle fixup (can also be run from scripts).
# This provides a CMake target to run BundleUtilities' fixup_bundle on the
# built app, useful after macdeployqt to ensure non-Qt dylibs are internalized.
if (APPLE)
include(CMakeParseArguments)
add_custom_target(kge_fixup_bundle ALL
COMMAND ${CMAKE_COMMAND}
-DAPP_BUNDLE=$<TARGET_BUNDLE_DIR:kge>
-P ${CMAKE_CURRENT_LIST_DIR}/cmake/fix_bundle.cmake
BYPRODUCTS $<TARGET_BUNDLE_DIR:kge>/Contents/Frameworks
COMMENT "Running fixup_bundle on kge.app to internalize non-Qt dylibs"
VERBATIM)
add_dependencies(kge_fixup_bundle kge)
endif ()
endif () endif ()

View File

@@ -4224,22 +4224,38 @@ cmd_reflow_paragraph(CommandContext &ctx)
static bool static bool
cmd_reload_buffer(CommandContext &ctx) cmd_reload_buffer(CommandContext &ctx)
{ {
Buffer *buf = ctx.editor.CurrentBuffer(); Buffer *buf = ctx.editor.CurrentBuffer();
if (!buf) if (!buf)
return false; return false;
const std::string &filename = buf->Filename(); // Remember the current cursor position so we can attempt to restore it
if (filename.empty()) { const std::size_t old_x = buf->Curx();
ctx.editor.SetStatus("Cannot reload unnamed buffer"); const std::size_t old_y = buf->Cury();
return false; const std::string &filename = buf->Filename();
} if (filename.empty()) {
std::string err; ctx.editor.SetStatus("Cannot reload unnamed buffer");
if (!buf->OpenFromFile(filename, err)) { return false;
ctx.editor.SetStatus(std::string("Reload failed: ") + err); }
return false; std::string err;
} if (!buf->OpenFromFile(filename, err)) {
ctx.editor.SetStatus(std::string("Reloaded ") + filename); ctx.editor.SetStatus(std::string("Reload failed: ") + err);
ensure_cursor_visible(ctx.editor, *buf); return false;
return true; }
// Try to restore the cursor to its previous position if still valid; otherwise clamp
{
auto &rows = buf->Rows();
const std::size_t nrows = rows.size();
if (nrows == 0) {
buf->SetCursor(0, 0);
} else {
const std::size_t new_y = old_y < nrows ? old_y : (nrows - 1);
const std::size_t line_len = rows[new_y].size();
const std::size_t new_x = old_x < line_len ? old_x : line_len;
buf->SetCursor(new_x, new_y);
}
}
ctx.editor.SetStatus(std::string("Reloaded ") + filename);
ensure_cursor_visible(ctx.editor, *buf);
return true;
} }

View File

@@ -158,16 +158,17 @@ map_key(const SDL_Keycode key,
ascii_key = static_cast<int>(key); ascii_key = static_cast<int>(key);
} }
bool ctrl2 = (mod & KMOD_CTRL) != 0; bool ctrl2 = (mod & KMOD_CTRL) != 0;
// If user typed a literal 'C' (or '^') as a control qualifier, keep k-prefix active // If user typed a literal 'C' (uppercase) or '^' as a control qualifier, keep k-prefix active
if (ascii_key == 'C' || ascii_key == 'c' || ascii_key == '^') { // Do NOT treat lowercase 'c' as a qualifier; 'c' is a valid k-command (BufferClose).
k_ctrl_pending = true; if (ascii_key == 'C' || ascii_key == '^') {
// Keep waiting for the next suffix; show status and suppress ensuing TEXTINPUT k_ctrl_pending = true;
if (ed) // Keep waiting for the next suffix; show status and suppress ensuing TEXTINPUT
ed->SetStatus("C-k C _"); if (ed)
suppress_textinput_once = true; ed->SetStatus("C-k C _");
out.hasCommand = false; suppress_textinput_once = true;
return true; out.hasCommand = false;
} return true;
}
// Otherwise, consume the k-prefix now for the actual suffix // Otherwise, consume the k-prefix now for the actual suffix
k_prefix = false; k_prefix = false;
if (ascii_key != 0) { if (ascii_key != 0) {
@@ -472,16 +473,16 @@ ImGuiInputHandler::ProcessSDLEvent(const SDL_Event &e)
ascii_key = static_cast<int>(c0); ascii_key = static_cast<int>(c0);
} }
if (ascii_key != 0) { if (ascii_key != 0) {
// Qualifier via TEXTINPUT: 'C' or '^' // Qualifier via TEXTINPUT: uppercase 'C' or '^' only
if (ascii_key == 'C' || ascii_key == 'c' || ascii_key == '^') { if (ascii_key == 'C' || ascii_key == '^') {
k_ctrl_pending_ = true; k_ctrl_pending_ = true;
if (ed_) if (ed_)
ed_->SetStatus("C-k C _"); ed_->SetStatus("C-k C _");
// Keep k-prefix active; do not emit a command // Keep k-prefix active; do not emit a command
k_prefix_ = true; k_prefix_ = true;
produced = true; produced = true;
break; break;
} }
// Map via k-prefix table; do not pass Ctrl for TEXTINPUT case // Map via k-prefix table; do not pass Ctrl for TEXTINPUT case
CommandId id; CommandId id;
bool pass_ctrl = k_ctrl_pending_; bool pass_ctrl = k_ctrl_pending_;

View File

@@ -178,14 +178,15 @@ map_key_to_command(const int ch,
ctrl = true; ctrl = true;
ascii_key = 'a' + (ch - 1); ascii_key = 'a' + (ch - 1);
} }
// If user typed literal 'C'/'c' or '^' as a qualifier, keep k-prefix and set pending // If user typed literal 'C' or '^' as a qualifier, keep k-prefix and set pending
if (ascii_key == 'C' || ascii_key == 'c' || ascii_key == '^') { // Note: Do NOT treat lowercase 'c' as a qualifier, since 'c' is a valid C-k command (BufferClose).
k_ctrl_pending = true; if (ascii_key == 'C' || ascii_key == '^') {
if (ed) k_ctrl_pending = true;
ed->SetStatus("C-k C _"); if (ed)
out.hasCommand = false; ed->SetStatus("C-k C _");
return true; out.hasCommand = false;
} return true;
}
// For actual suffix, consume the k-prefix // For actual suffix, consume the k-prefix
k_prefix = false; k_prefix = false;
// Do NOT lowercase here; KLookupKCommand handles case-sensitive bindings // Do NOT lowercase here; KLookupKCommand handles case-sensitive bindings

View File

@@ -1,5 +1,6 @@
{ {
lib, pkgs ? import <nixpkgs> {},
lib ? pkgs.lib,
stdenv, stdenv,
cmake, cmake,
ncurses, ncurses,

View File

@@ -16,14 +16,18 @@ open .
cd .. cd ..
mkdir -p cmake-build-release-qt mkdir -p cmake-build-release-qt
cmake -S . -B cmake-build-release -DBUILD_GUI=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_ASAN=OFF cmake -S . -B cmake-build-release-qt -DBUILD_GUI=ON -DKTE_USE_QT=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_ASAN=OFF
cd cmake-build-release-qt cd cmake-build-release-qt
make clean make clean
rm -fr kge.app* kge-qt.app* rm -fr kge.app* kge-qt.app*
make make
mv kge.app kge-qt.app mv -f kge.app kge-qt.app
macdeployqt kge-qt.app -always-overwrite # Use the same Qt's macdeployqt as used for building; ensure it overwrites in-bundle paths
macdeployqt kge-qt.app -always-overwrite -verbose=3
# Run CMake BundleUtilities fixup to internalize non-Qt dylibs and rewrite install names
cmake -DAPP_BUNDLE="$(pwd)/kge-qt.app" -P "${PWD%/*}/cmake/fix_bundle.cmake"
zip -r kge-qt.app.zip kge-qt.app zip -r kge-qt.app.zip kge-qt.app
sha256sum kge-qt.app.zip sha256sum kge-qt.app.zip
open . open .