2 Commits

Author SHA1 Message Date
fb5976f123 Add buffer position display and documentation improvements.
Some checks failed
Release / Bump Homebrew formula (push) Has been cancelled
- Display buffer position prefix "[x/N]" in GUI and terminal renderers.
- Improve `kte` and `kge` man pages with frontend usage details and project homepage.
- Update README with GUI invocation instructions.
- Bump version to 1.0.0.
2025-11-30 18:40:44 -08:00
e4cd4877cc Introduce file picker and GUI configuration with enhancements.
- Add visual file picker for GUI with toggle support.
- Introduce `GUIConfig` class for loading GUI settings from configuration file.
- Refactor window initialization to support dynamic sizing based on configuration.
- Add macOS-specific handling for fullscreen behavior.
- Improve header inclusion order and minor code cleanup.
2025-11-30 18:35:12 -08:00
54 changed files with 621 additions and 106 deletions

48
.idea/workspace.xml generated
View File

@@ -33,12 +33,10 @@
</configurations> </configurations>
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Refactor code for consistency and enhanced functionality.&#10;&#10;- Normalize path handling for buffer operations, supporting tilde expansion and absolute paths.&#10;- Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes.&#10;- Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`.&#10;- Refine keybindings and enhance existing commands for improved command flow.&#10;- Adjust GUI and terminal renderers to display total line counts alongside filenames.&#10;- Update coding style to align with project guidelines."> <list default="true" id="e1fe3ab0-3650-4fca-8664-a247d5dfa457" name="Changes" comment="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup.">
<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$/Buffer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/Buffer.cc" afterDir="false" /> <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/GUIRenderer.cc" beforeDir="false" afterPath="$PROJECT_DIR$/GUIRenderer.cc" afterDir="false" /> <change beforePath="$PROJECT_DIR$/docs/kte.1" beforeDir="false" afterPath="$PROJECT_DIR$/docs/kte.1" afterDir="false" />
<change beforePath="$PROJECT_DIR$/KKeymap.cc" beforeDir="false" afterPath="$PROJECT_DIR$/KKeymap.cc" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.cc" beforeDir="false" afterPath="$PROJECT_DIR$/main.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" />
@@ -59,15 +57,6 @@
<component name="HighlightingSettingsPerFile"> <component name="HighlightingSettingsPerFile">
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" /> <setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" /> <setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///AIAssistantSnippet.." root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
</component> </component>
<component name="OptimizeOnSaveOptions"> <component name="OptimizeOnSaveOptions">
<option name="myRunOnSave" value="true" /> <option name="myRunOnSave" value="true" />
@@ -134,6 +123,11 @@
</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">
<method v="2">
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
</method>
</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">
<method v="2"> <method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" /> <option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
@@ -170,7 +164,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="3453000" /> <workItem from="1764548345516" duration="8203000" />
</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" />
@@ -252,7 +246,23 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1764550164829</updated> <updated>1764550164829</updated>
</task> </task>
<option name="localTasksCounter" value="11" /> <task id="LOCAL-00011" summary="Add horizontal scrolling support and refactor mouse click handling in GUI.&#10;&#10;- Introduce horizontal scrolling with column offset synchronization in GUI.&#10;- Refactor mouse click handling for improved accuracy and viewport alignment.&#10;- Enhance tab expansion and cursor rendering logic for better user experience.&#10;- Replace redundant variable declarations in `Buffer` for cleaner code.">
<option name="closed" value="true" />
<created>1764551986561</created>
<option name="number" value="00011" />
<option name="presentableId" value="LOCAL-00011" />
<option name="project" value="LOCAL" />
<updated>1764551986561</updated>
</task>
<task id="LOCAL-00012" summary="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup.">
<option name="closed" value="true" />
<created>1764556512864</created>
<option name="number" value="00012" />
<option name="presentableId" value="LOCAL-00012" />
<option name="project" value="LOCAL" />
<updated>1764556512864</updated>
</task>
<option name="localTasksCounter" value="13" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -276,7 +286,9 @@
<MESSAGE value="Add man pages for `kge` and `kte` with installation targets in CMake.&#10;&#10;- Introduce `docs/kge.1` and `docs/kte.1` man pages covering usage, options, keybindings, and examples.&#10;- Update `CMakeLists.txt` to install man pages under `${CMAKE_INSTALL_MANDIR}/man1`.&#10;- Ensure `kge` man page installation is conditional on GUI being built." /> <MESSAGE value="Add man pages for `kge` and `kte` with installation targets in CMake.&#10;&#10;- Introduce `docs/kge.1` and `docs/kte.1` man pages covering usage, options, keybindings, and examples.&#10;- Update `CMakeLists.txt` to install man pages under `${CMAKE_INSTALL_MANDIR}/man1`.&#10;- Ensure `kge` man page installation is conditional on GUI being built." />
<MESSAGE value="Add GUI initialization updates and improve navigation commands.&#10;&#10;- Implement terminal detachment for GUI mode to enable terminal closure post-launch.&#10;- Add `+N` support for opening files at specific line numbers and refine cursor positioning.&#10;- Introduce `JumpToLine` command for direct navigation by line number.&#10;- Enhance mouse wheel handling for line-wise scrolling." /> <MESSAGE value="Add GUI initialization updates and improve navigation commands.&#10;&#10;- Implement terminal detachment for GUI mode to enable terminal closure post-launch.&#10;- Add `+N` support for opening files at specific line numbers and refine cursor positioning.&#10;- Introduce `JumpToLine` command for direct navigation by line number.&#10;- Enhance mouse wheel handling for line-wise scrolling." />
<MESSAGE value="Refactor code for consistency and enhanced functionality.&#10;&#10;- Normalize path handling for buffer operations, supporting tilde expansion and absolute paths.&#10;- Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes.&#10;- Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`.&#10;- Refine keybindings and enhance existing commands for improved command flow.&#10;- Adjust GUI and terminal renderers to display total line counts alongside filenames.&#10;- Update coding style to align with project guidelines." /> <MESSAGE value="Refactor code for consistency and enhanced functionality.&#10;&#10;- Normalize path handling for buffer operations, supporting tilde expansion and absolute paths.&#10;- Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes.&#10;- Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`.&#10;- Refine keybindings and enhance existing commands for improved command flow.&#10;- Adjust GUI and terminal renderers to display total line counts alongside filenames.&#10;- Update coding style to align with project guidelines." />
<option name="LAST_COMMIT_MESSAGE" value="Refactor code for consistency and enhanced functionality.&#10;&#10;- Normalize path handling for buffer operations, supporting tilde expansion and absolute paths.&#10;- Introduce `DisplayNameFor` to uniquely resolve buffer display names, minimizing filename clashes.&#10;- Add new commands: `ShowWorkingDirectory` and `ChangeWorkingDirectory`.&#10;- Refine keybindings and enhance existing commands for improved command flow.&#10;- Adjust GUI and terminal renderers to display total line counts alongside filenames.&#10;- Update coding style to align with project guidelines." /> <MESSAGE value="Add horizontal scrolling support and refactor mouse click handling in GUI.&#10;&#10;- Introduce horizontal scrolling with column offset synchronization in GUI.&#10;- Refactor mouse click handling for improved accuracy and viewport alignment.&#10;- Enhance tab expansion and cursor rendering logic for better user experience.&#10;- Replace redundant variable declarations in `Buffer` for cleaner code." />
<MESSAGE value="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup." />
<option name="LAST_COMMIT_MESSAGE" value="Introduce file picker and GUI configuration with enhancements.&#10;&#10;- Add visual file picker for GUI with toggle support.&#10;- Introduce `GUIConfig` class for loading GUI settings from configuration file.&#10;- Refactor window initialization to support dynamic sizing based on configuration.&#10;- Add macOS-specific handling for fullscreen behavior.&#10;- Improve header inclusion order and minor code cleanup." />
</component> </component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />

View File

@@ -1,12 +1,12 @@
#include "Buffer.h"
#include "UndoSystem.h"
#include "UndoTree.h"
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <filesystem> #include <filesystem>
#include <cstdlib> #include <cstdlib>
#include "Buffer.h"
#include "UndoSystem.h"
#include "UndoTree.h"
Buffer::Buffer() Buffer::Buffer()
{ {

View File

@@ -12,6 +12,7 @@
#include "AppendBuffer.h" #include "AppendBuffer.h"
#include "UndoSystem.h" #include "UndoSystem.h"
class Buffer { class Buffer {
public: public:
Buffer(); Buffer();

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 "0.9.2") set(KTE_VERSION "1.0.0")
# 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.
@@ -128,6 +128,8 @@ endif ()
if (${BUILD_GUI}) if (${BUILD_GUI})
target_sources(kte PRIVATE target_sources(kte PRIVATE
Font.h Font.h
GUIConfig.cc
GUIConfig.h
GUIRenderer.cc GUIRenderer.cc
GUIRenderer.h GUIRenderer.h
GUIInputHandler.cc GUIInputHandler.cc
@@ -142,6 +144,8 @@ if (${BUILD_GUI})
main.cc main.cc
${COMMON_SOURCES} ${COMMON_SOURCES}
${COMMON_HEADERS} ${COMMON_HEADERS}
GUIConfig.cc
GUIConfig.h
GUIRenderer.cc GUIRenderer.cc
GUIRenderer.h GUIRenderer.h
GUIInputHandler.cc GUIInputHandler.cc
@@ -153,6 +157,15 @@ if (${BUILD_GUI})
# On macOS, build kge as a proper .app bundle # On macOS, build kge as a proper .app bundle
if (APPLE) if (APPLE)
# Define the icon file
set(MACOSX_BUNDLE_ICON_FILE kge.icns)
set(kge_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}")
# Add icon to the target sources and mark it as a resource
target_sources(kge PRIVATE ${kge_ICON})
set_source_files_properties(${kge_ICON} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
# Configure Info.plist with version and identifiers # Configure Info.plist with version and identifiers
set(KGE_BUNDLE_ID "dev.wntrmute.kge") set(KGE_BUNDLE_ID "dev.wntrmute.kge")
configure_file( configure_file(
@@ -164,6 +177,7 @@ if (${BUILD_GUI})
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_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist") MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist")
install(TARGETS kge install(TARGETS kge

View File

@@ -6,14 +6,13 @@
#include "Editor.h" #include "Editor.h"
#include "Buffer.h" #include "Buffer.h"
#include "UndoSystem.h" #include "UndoSystem.h"
// Note: Command layer must remain UI-agnostic. Do not include frontend/IO headers here.
// Keep buffer viewport offsets so that the cursor stays within the visible // Keep buffer viewport offsets so that the cursor stays within the visible
// window based on the editor's current dimensions. The bottom row is reserved // window based on the editor's current dimensions. The bottom row is reserved
// for the status line. // for the status line.
static std::size_t static std::size_t
compute_render_x(const std::string &line, std::size_t curx, std::size_t tabw) compute_render_x(const std::string &line, const std::size_t curx, const std::size_t tabw)
{ {
std::size_t rx = 0; std::size_t rx = 0;
for (std::size_t i = 0; i < curx && i < line.size(); ++i) { for (std::size_t i = 0; i < curx && i < line.size(); ++i) {
@@ -474,7 +473,7 @@ cmd_change_working_directory_start(CommandContext &ctx)
{ {
std::string initial; std::string initial;
try { try {
initial = std::filesystem::current_path().string(); initial = std::filesystem::current_path().string() + "/";
} catch (...) { } catch (...) {
initial.clear(); initial.clear();
} }
@@ -677,6 +676,30 @@ cmd_open_file_start(CommandContext &ctx)
} }
// GUI: toggle visual file picker (no-op in terminal; renderer will consume flag)
static bool
cmd_visual_file_picker_toggle(CommandContext &ctx)
{
// Toggle visibility
bool show = !ctx.editor.FilePickerVisible();
ctx.editor.SetFilePickerVisible(show);
if (show) {
// Initialize directory to current working directory if empty
if (ctx.editor.FilePickerDir().empty()) {
try {
ctx.editor.SetFilePickerDir(std::filesystem::current_path().string());
} catch (...) {
ctx.editor.SetFilePickerDir(".");
}
}
ctx.editor.SetStatus("Open File (visual)");
} else {
ctx.editor.SetStatus("Closed file picker");
}
return true;
}
static bool static bool
cmd_jump_to_line_start(CommandContext &ctx) cmd_jump_to_line_start(CommandContext &ctx)
{ {
@@ -2630,6 +2653,11 @@ InstallDefaultCommands()
CommandId::MarkAllAndJumpEnd, "mark-all-jump-end", "Set mark at beginning and jump to end", CommandId::MarkAllAndJumpEnd, "mark-all-jump-end", "Set mark at beginning and jump to end",
cmd_mark_all_and_jump_end cmd_mark_all_and_jump_end
}); });
// GUI
CommandRegistry::Register({
CommandId::VisualFilePickerToggle, "file-picker-toggle", "Toggle visual file picker",
cmd_visual_file_picker_toggle
});
// Working directory // Working directory
CommandRegistry::Register({ CommandRegistry::Register({
CommandId::ShowWorkingDirectory, "show-working-directory", "Show current working directory", CommandId::ShowWorkingDirectory, "show-working-directory", "Show current working directory",

View File

@@ -24,6 +24,8 @@ enum class CommandId {
KPrefix, // show "C-k _" prompt in status when entering k-command KPrefix, // show "C-k _" prompt in status when entering k-command
FindStart, // begin incremental search (placeholder) FindStart, // begin incremental search (placeholder)
OpenFileStart, // begin open-file prompt OpenFileStart, // begin open-file prompt
// GUI: visual file picker
VisualFilePickerToggle,
// Buffers // Buffers
BufferSwitchStart, // begin buffer switch prompt BufferSwitchStart, // begin buffer switch prompt
BufferClose, BufferClose,

View File

@@ -1,9 +1,9 @@
#include "Editor.h"
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <filesystem> #include <filesystem>
#include "Editor.h"
Editor::Editor() = default; Editor::Editor() = default;

View File

@@ -441,6 +441,31 @@ public:
return buffers_; return buffers_;
} }
// --- GUI: Visual File Picker state ---
void SetFilePickerVisible(bool on)
{
file_picker_visible_ = on;
}
[[nodiscard]] bool FilePickerVisible() const
{
return file_picker_visible_;
}
void SetFilePickerDir(const std::string &path)
{
file_picker_dir_ = path;
}
[[nodiscard]] const std::string &FilePickerDir() const
{
return file_picker_dir_;
}
private: private:
std::size_t rows_ = 0, cols_ = 0; std::size_t rows_ = 0, cols_ = 0;
int mode_ = 0; int mode_ = 0;
@@ -478,6 +503,10 @@ private:
std::string prompt_label_; std::string prompt_label_;
std::string prompt_text_; std::string prompt_text_;
std::string pending_overwrite_path_; std::string pending_overwrite_path_;
// GUI-only state (safe no-op in terminal builds)
bool file_picker_visible_ = false;
std::string file_picker_dir_;
}; };
#endif // KTE_EDITOR_H #endif // KTE_EDITOR_H

View File

@@ -4,8 +4,6 @@
#ifndef KTE_FRONTEND_H #ifndef KTE_FRONTEND_H
#define KTE_FRONTEND_H #define KTE_FRONTEND_H
#include <memory>
class Editor; class Editor;
class InputHandler; class InputHandler;

107
GUIConfig.cc Normal file
View File

@@ -0,0 +1,107 @@
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <algorithm>
#include "GUIConfig.h"
static void
trim(std::string &s)
{
auto not_space = [](int ch) {
return !std::isspace(ch);
};
s.erase(s.begin(), std::find_if(s.begin(), s.end(), not_space));
s.erase(std::find_if(s.rbegin(), s.rend(), not_space).base(), s.end());
}
static std::string
default_config_path()
{
const char *home = std::getenv("HOME");
if (!home || !*home)
return std::string();
std::string path(home);
path += "/.config/kte/kge.ini";
return path;
}
GUIConfig
GUIConfig::Load()
{
GUIConfig cfg; // defaults already set
std::string path = default_config_path();
if (!path.empty()) {
cfg.LoadFromFile(path);
}
return cfg;
}
bool
GUIConfig::LoadFromFile(const std::string &path)
{
std::ifstream in(path);
if (!in.good())
return false;
std::string line;
while (std::getline(in, line)) {
// Remove comments starting with '#' or ';'
auto hash = line.find('#');
if (hash != std::string::npos)
line.erase(hash);
auto sc = line.find("//");
if (sc != std::string::npos)
line.erase(sc);
// Basic key=value
auto eq = line.find('=');
if (eq == std::string::npos)
continue;
std::string key = line.substr(0, eq);
std::string val = line.substr(eq + 1);
trim(key);
trim(val);
// Strip trailing semicolon
if (!val.empty() && val.back() == ';') {
val.pop_back();
trim(val);
}
// Lowercase key
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) {
return (char) std::tolower(c);
});
if (key == "fullscreen") {
fullscreen = (val == "1" || val == "true" || val == "on" || val == "yes");
} else if (key == "columns" || key == "cols") {
int v = columns;
try {
v = std::stoi(val);
} catch (...) {}
if (v > 0)
columns = v;
} else if (key == "rows") {
int v = rows;
try {
v = std::stoi(val);
} catch (...) {}
if (v > 0)
rows = v;
} else if (key == "font_size" || key == "fontsize") {
float v = font_size;
try {
v = std::stof(val);
} catch (...) {}
if (v > 0.0f)
font_size = v;
}
}
return true;
}

27
GUIConfig.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* GUIConfig - loads simple GUI configuration from $HOME/.config/kte/kge.ini
*/
#ifndef KTE_GUI_CONFIG_H
#define KTE_GUI_CONFIG_H
#include <string>
#ifndef KTE_FONT_SIZE
#define KTE_FONT_SIZE 16.0f
#endif
class GUIConfig {
public:
bool fullscreen = false;
int columns = 80;
int rows = 42;
float font_size = (float) KTE_FONT_SIZE;
// Load from default path: $HOME/.config/kte/kge.ini
static GUIConfig Load();
// Load from explicit path. Returns true if file existed and was parsed.
bool LoadFromFile(const std::string &path);
};
#endif // KTE_GUI_CONFIG_H

View File

@@ -1,18 +1,25 @@
#include <SDL.h>
#include <SDL_opengl.h>
#include <imgui.h>
#include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_opengl3.h>
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <algorithm>
#include <SDL.h>
#include <SDL_opengl.h>
#include <imgui.h>
#include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_opengl3.h>
#include "Editor.h" #include "Editor.h"
#include "Command.h" #include "Command.h"
#include "GUIFrontend.h" #include "GUIFrontend.h"
#include "Font.h" // embedded default font (DefaultFontRegular) #include "Font.h" // embedded default font (DefaultFontRegular)
#include "GUIConfig.h"
#ifndef KTE_FONT_SIZE
#define KTE_FONT_SIZE 16.0f
#endif
static const char *kGlslVersion = "#version 150"; // GL 3.2 core (macOS compatible) static const char *kGlslVersion = "#version 150"; // GL 3.2 core (macOS compatible)
@@ -24,6 +31,9 @@ GUIFrontend::Init(Editor &ed)
return false; return false;
} }
// Load GUI configuration (fullscreen, columns/rows, font size)
const auto [fullscreen, columns, rows, font_size] = GUIConfig::Load();
// GL attributes for core profile // GL attributes for core profile
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
@@ -33,14 +43,56 @@ GUIFrontend::Init(Editor &ed)
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
// Compute desired window size from config
Uint32 win_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
if (fullscreen) {
// "Fullscreen": fill the usable bounds of the primary display.
// On macOS, do NOT use true fullscreen so the menu/status bar remains visible.
SDL_Rect usable{};
if (SDL_GetDisplayUsableBounds(0, &usable) == 0) {
width_ = usable.w;
height_ = usable.h;
}
#if !defined(__APPLE__)
// Non-macOS: desktop fullscreen uses the current display resolution.
win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
#endif
} else {
// Windowed: width = columns * font_size, height = (rows * 2) * font_size
int w = static_cast<int>(columns * font_size);
int h = static_cast<int>((rows * 2) * font_size);
// As a safety, clamp to display usable bounds if retrievable
SDL_Rect usable{};
if (SDL_GetDisplayUsableBounds(0, &usable) == 0) {
w = std::min(w, usable.w);
h = std::min(h, usable.h);
}
width_ = std::max(320, w);
height_ = std::max(200, h);
}
window_ = SDL_CreateWindow( window_ = SDL_CreateWindow(
"kte", "kge - kyle's text editor " KTE_VERSION_STR,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width_, height_, width_, height_,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); win_flags);
if (!window_) if (!window_)
return false; return false;
#if defined(__APPLE__)
// macOS: when "fullscreen" is requested, position the window at the
// top-left of the usable display area to mimic fullscreen while keeping
// the system menu bar visible.
if (fullscreen) {
SDL_Rect usable{};
if (SDL_GetDisplayUsableBounds(0, &usable) == 0) {
SDL_SetWindowPosition(window_, usable.x, usable.y);
}
}
#endif
gl_ctx_ = SDL_GL_CreateContext(window_); gl_ctx_ = SDL_GL_CreateContext(window_);
if (!gl_ctx_) if (!gl_ctx_)
return false; return false;
@@ -64,11 +116,23 @@ GUIFrontend::Init(Editor &ed)
width_ = w; width_ = w;
height_ = h; height_ = h;
// Initialize GUI font from embedded default #if defined(__APPLE__)
#ifndef KTE_FONT_SIZE // Workaround: On macOS Retina when starting maximized, we sometimes get a
#define KTE_FONT_SIZE 16.0f // subtle input vs draw alignment mismatch until the first manual resize.
// Nudge the window size by 1px and back to trigger a proper internal
// recomputation, without visible impact.
if (w > 1 && h > 1) {
SDL_SetWindowSize(window_, w - 1, h - 1);
SDL_SetWindowSize(window_, w, h);
// Update cached size in case backend reports immediately
SDL_GetWindowSize(window_, &w, &h);
width_ = w;
height_ = h;
}
#endif #endif
LoadGuiFont_(nullptr, (float) KTE_FONT_SIZE);
// Initialize GUI font from embedded default (use configured size or compiled default)
LoadGuiFont_(nullptr, (float) font_size);
return true; return true;
} }

View File

@@ -8,6 +8,7 @@
#include "GUIInputHandler.h" #include "GUIInputHandler.h"
#include "GUIRenderer.h" #include "GUIRenderer.h"
struct SDL_Window; struct SDL_Window;
typedef void *SDL_GLContext; typedef void *SDL_GLContext;

View File

@@ -1,7 +1,8 @@
#include <SDL.h>
#include <cstdio> #include <cstdio>
#include <ncurses.h> #include <ncurses.h>
#include <SDL.h>
#include "GUIInputHandler.h" #include "GUIInputHandler.h"
#include "KKeymap.h" #include "KKeymap.h"

View File

@@ -4,11 +4,12 @@
#ifndef KTE_GUI_INPUT_HANDLER_H #ifndef KTE_GUI_INPUT_HANDLER_H
#define KTE_GUI_INPUT_HANDLER_H #define KTE_GUI_INPUT_HANDLER_H
#include <queue>
#include <mutex> #include <mutex>
#include <queue>
#include "InputHandler.h" #include "InputHandler.h"
union SDL_Event; // fwd decl to avoid including SDL here (SDL defines SDL_Event as a union) union SDL_Event; // fwd decl to avoid including SDL here (SDL defines SDL_Event as a union)
class GUIInputHandler final : public InputHandler { class GUIInputHandler final : public InputHandler {

View File

@@ -1,29 +1,44 @@
#include "GUIRenderer.h" #include <cmath>
#include <cstdio>
#include "Editor.h" #include <filesystem>
#include "Buffer.h" #include <limits>
#include "Command.h" #include <string>
#include <imgui.h> #include <imgui.h>
#include <cstdio>
#include <string> #include "GUIRenderer.h"
#include <filesystem> #include "Buffer.h"
#include <cmath> #include "Command.h"
#include <limits> #include "Editor.h"
// Version string expected to be provided by build system as KTE_VERSION_STR // Version string expected to be provided by build system as KTE_VERSION_STR
#ifndef KTE_VERSION_STR #ifndef KTE_VERSION_STR
# define KTE_VERSION_STR "dev" # define KTE_VERSION_STR "dev"
#endif #endif
// ImGui compatibility: some bundled ImGui versions (or builds without docking)
// don't define ImGuiWindowFlags_NoDocking. Treat it as 0 in that case.
#ifndef ImGuiWindowFlags_NoDocking
# define ImGuiWindowFlags_NoDocking 0
#endif
void void
GUIRenderer::Draw(Editor &ed) GUIRenderer::Draw(Editor &ed)
{ {
// Make the editor window occupy the entire GUI container/viewport // Make the editor window occupy the entire GUI container/viewport
ImGuiViewport *vp = ImGui::GetMainViewport(); ImGuiViewport *vp = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(vp->Pos); // On HiDPI/Retina, snap to integer pixels to prevent any draw vs hit-test
ImGui::SetNextWindowSize(vp->Size); // mismatches that can appear on the very first maximized frame.
ImVec2 main_pos = vp->Pos;
ImVec2 main_sz = vp->Size;
main_pos.x = std::floor(main_pos.x + 0.5f);
main_pos.y = std::floor(main_pos.y + 0.5f);
main_sz.x = std::floor(main_sz.x + 0.5f);
main_sz.y = std::floor(main_sz.y + 0.5f);
ImGui::SetNextWindowPos(main_pos);
ImGui::SetNextWindowSize(main_sz);
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoResize
@@ -297,6 +312,18 @@ GUIRenderer::Draw(Editor &ed)
} catch (...) {} } catch (...) {}
} }
left += " "; left += " ";
// Insert buffer position prefix "[x/N] " before filename
{
std::size_t total = ed.BufferCount();
if (total > 0) {
std::size_t idx1 = ed.CurrentBufferIndex() + 1; // 1-based for display
left += "[";
left += std::to_string(static_cast<unsigned long long>(idx1));
left += "/";
left += std::to_string(static_cast<unsigned long long>(total));
left += "] ";
}
}
left += fname; left += fname;
if (buf->Dirty()) if (buf->Dirty())
left += " *"; left += " *";
@@ -379,4 +406,153 @@ GUIRenderer::Draw(Editor &ed)
ImGui::End(); ImGui::End();
ImGui::PopStyleVar(3); ImGui::PopStyleVar(3);
// --- Visual File Picker overlay (GUI only) ---
if (ed.FilePickerVisible()) {
// Centered popup-style window that always fits within the current viewport
ImGuiViewport *vp2 = ImGui::GetMainViewport();
// Desired size, min size, and margins
const ImVec2 want(800.0f, 500.0f);
const ImVec2 min_sz(240.0f, 160.0f);
const float margin = 20.0f; // space from viewport edges
// Compute the maximum allowed size (viewport minus margins) and make sure it's not negative
ImVec2 max_sz(std::max(32.0f, vp2->Size.x - 2.0f * margin),
std::max(32.0f, vp2->Size.y - 2.0f * margin));
// Clamp desired size to [min_sz, max_sz]
ImVec2 size(std::min(want.x, max_sz.x), std::min(want.y, max_sz.y));
size.x = std::max(size.x, std::min(min_sz.x, max_sz.x));
size.y = std::max(size.y, std::min(min_sz.y, max_sz.y));
// Center within the viewport using the final size
ImVec2 pos(vp2->Pos.x + std::max(margin, (vp2->Size.x - size.x) * 0.5f),
vp2->Pos.y + std::max(margin, (vp2->Size.y - size.y) * 0.5f));
// On HiDPI displays (macOS Retina), ensure integer pixel alignment to avoid
// potential hit-test vs draw mismatches from sub-pixel positions.
pos.x = std::floor(pos.x + 0.5f);
pos.y = std::floor(pos.y + 0.5f);
size.x = std::floor(size.x + 0.5f);
size.y = std::floor(size.y + 0.5f);
ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
ImGui::SetNextWindowSize(size, ImGuiCond_Always);
ImGuiWindowFlags wflags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoDocking;
bool open = true;
if (ImGui::Begin("File Picker", &open, wflags)) {
// Current directory
std::string curdir = ed.FilePickerDir();
if (curdir.empty()) {
try {
curdir = std::filesystem::current_path().string();
} catch (...) {
curdir = ".";
}
ed.SetFilePickerDir(curdir);
}
ImGui::TextUnformatted(curdir.c_str());
ImGui::SameLine();
if (ImGui::Button("Up")) {
try {
std::filesystem::path p(curdir);
if (p.has_parent_path()) {
ed.SetFilePickerDir(p.parent_path().string());
}
} catch (...) {}
}
ImGui::SameLine();
if (ImGui::Button("Close")) {
ed.SetFilePickerVisible(false);
}
ImGui::Separator();
// Header
ImGui::TextUnformatted("Name");
ImGui::Separator();
// Scrollable list
ImGui::BeginChild("picker-list", ImVec2(0, 0), true);
// Build entries: directories first then files, alphabetical
struct Entry {
std::string name;
std::filesystem::path path;
bool is_dir;
};
std::vector<Entry> entries;
entries.reserve(256);
// Optional parent entry
try {
std::filesystem::path base(curdir);
std::error_code ec;
for (auto it = std::filesystem::directory_iterator(base, ec);
!ec && it != std::filesystem::directory_iterator(); it.increment(ec)) {
const auto &p = it->path();
std::string nm;
try {
nm = p.filename().string();
} catch (...) {
continue;
}
if (nm == "." || nm == "..")
continue;
bool is_dir = false;
std::error_code ec2;
is_dir = it->is_directory(ec2);
entries.push_back({nm, p, is_dir});
}
} catch (...) {
// ignore listing errors; show empty
}
std::sort(entries.begin(), entries.end(), [](const Entry &a, const Entry &b) {
if (a.is_dir != b.is_dir)
return a.is_dir && !b.is_dir;
return a.name < b.name;
});
// Draw rows
int idx = 0;
for (const auto &e: entries) {
ImGui::PushID(idx++); // ensure unique/stable IDs even if names repeat
std::string label;
label.reserve(e.name.size() + 4);
if (e.is_dir)
label += "[";
label += e.name;
if (e.is_dir)
label += "]";
// Render selectable row
ImGui::Selectable(label.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick);
// Activate based strictly on hover + mouse, to avoid any off-by-one due to click routing
if (ImGui::IsItemHovered()) {
if (e.is_dir && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
// Enter directory on double-click
ed.SetFilePickerDir(e.path.string());
} else if (!e.is_dir && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
// Open file on single click
std::string err;
if (!ed.OpenFile(e.path.string(), err)) {
ed.SetStatus(std::string("open: ") + err);
} else {
ed.SetStatus(std::string("Opened: ") + e.name);
}
ed.SetFilePickerVisible(false);
}
}
ImGui::PopID();
}
ImGui::EndChild();
}
ImGui::End();
if (!open) {
ed.SetFilePickerVisible(false);
}
}
} }

View File

@@ -6,7 +6,7 @@
#include "Renderer.h" #include "Renderer.h"
class GUIRenderer : public Renderer { class GUIRenderer final : public Renderer {
public: public:
GUIRenderer() = default; GUIRenderer() = default;

View File

@@ -1,9 +1,10 @@
#include "GapBuffer.h"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include "GapBuffer.h"
GapBuffer::GapBuffer() = default; GapBuffer::GapBuffer() = default;

View File

@@ -6,6 +6,7 @@
#include <cstddef> #include <cstddef>
class GapBuffer { class GapBuffer {
public: public:
GapBuffer(); GapBuffer();

View File

@@ -8,6 +8,7 @@
#include "Command.h" #include "Command.h"
// Result of translating raw input into an editor command. // Result of translating raw input into an editor command.
struct MappedInput { struct MappedInput {
bool hasCommand = false; bool hasCommand = false;

View File

@@ -1,9 +1,9 @@
#include "KKeymap.h"
#include <iostream> #include <iostream>
#include <ncurses.h> #include <ncurses.h>
#include <ostream> #include <ostream>
#include "KKeymap.h"
auto auto
KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
@@ -83,6 +83,9 @@ KLookupKCommand(const int ascii_key, const bool ctrl, CommandId &out) -> bool
case 'u': case 'u':
out = CommandId::Undo; out = CommandId::Undo;
return true; return true;
case 'v':
out = CommandId::VisualFilePickerToggle;
return true;
case 'w': case 'w':
out = CommandId::ShowWorkingDirectory; out = CommandId::ShowWorkingDirectory;
return true; return true;

View File

@@ -6,6 +6,7 @@
#include "Command.h" #include "Command.h"
// Lookup the command to execute after a C-k prefix. // Lookup the command to execute after a C-k prefix.
// Parameters: // Parameters:
// - ascii_key: ASCII code of the key, preferably lowercased if it's a letter. // - ascii_key: ASCII code of the key, preferably lowercased if it's a letter.

View File

@@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
class PieceTable { class PieceTable {
public: public:
PieceTable(); PieceTable();

View File

@@ -2,8 +2,8 @@ kte — Kyle's Text Editor
Vision Vision
------- -------
kte will be a small, fast, and understandable text editor with a kte is a small, fast, and understandable text editor with a
terminal<EFBFBD>first UX and an optional ImGui GUI. It modernizes the terminal-first UX and an optional ImGui GUI. It modernizes the
original ke editor while preserving its familiar WordStar/VDEstyle original ke editor while preserving its familiar WordStar/VDEstyle
command model and Emacsinfluenced ergonomics. The focus is on command model and Emacsinfluenced ergonomics. The focus is on
simplicity of design, excellent latency, and pragmatic features you simplicity of design, excellent latency, and pragmatic features you
@@ -80,6 +80,15 @@ Interfaces
- GUI: an optional ImGuibased frontend that embeds the same editor - GUI: an optional ImGuibased frontend that embeds the same editor
core. core.
Man pages
---------
- Terminal editor: `docs/kte.1` (view locally with `man -l docs/kte.1`)
- GUI frontend: `docs/kge.1` (view locally with `man -l docs/kge.1`)
The `ke` keybinding reference remains the canonical source for
commands while kte evolves: see `docs/ke.md`.
Architecture (intended) Architecture (intended)
----------------------- -----------------------
@@ -180,6 +189,15 @@ Run:
./cmake-build-debug/kte [files] ./cmake-build-debug/kte [files]
``` ```
If you configured the GUI, you can also run the GUI-first target (when
built as `kge`) or request the GUI from `kte`:
```
./cmake-build-debug/kte --gui [files]
# or if built/installed as a separate GUI target
./cmake-build-debug/kge [files]
```
GUI build example GUI build example
----------------- -----------------

View File

@@ -1,10 +1,10 @@
#include <unistd.h>
#include <termios.h>
#include <ncurses.h> #include <ncurses.h>
#include <termios.h>
#include <unistd.h>
#include "Editor.h"
#include "Command.h"
#include "TerminalFrontend.h" #include "TerminalFrontend.h"
#include "Command.h"
#include "Editor.h"
bool bool

View File

@@ -4,10 +4,11 @@
#ifndef KTE_TERMINAL_FRONTEND_H #ifndef KTE_TERMINAL_FRONTEND_H
#define KTE_TERMINAL_FRONTEND_H #define KTE_TERMINAL_FRONTEND_H
#include <termios.h>
#include "Frontend.h" #include "Frontend.h"
#include "TerminalInputHandler.h" #include "TerminalInputHandler.h"
#include "TerminalRenderer.h" #include "TerminalRenderer.h"
#include <termios.h>
class TerminalFrontend final : public Frontend { class TerminalFrontend final : public Frontend {

View File

@@ -1,8 +1,8 @@
#include <ncurses.h>
#include <cstdio> #include <cstdio>
#include <ncurses.h>
#include "KKeymap.h"
#include "TerminalInputHandler.h" #include "TerminalInputHandler.h"
#include "KKeymap.h"
namespace { namespace {
constexpr int constexpr int

View File

@@ -4,8 +4,6 @@
#ifndef KTE_TERMINAL_INPUT_HANDLER_H #ifndef KTE_TERMINAL_INPUT_HANDLER_H
#define KTE_TERMINAL_INPUT_HANDLER_H #define KTE_TERMINAL_INPUT_HANDLER_H
#include <cstdint>
#include "InputHandler.h" #include "InputHandler.h"

View File

@@ -1,19 +1,19 @@
#include "TerminalRenderer.h"
#include <ncurses.h>
#include <cstdio>
#include <string>
#include <algorithm> #include <algorithm>
#include <cstdio>
#include <filesystem> #include <filesystem>
#include <ncurses.h>
#include <string>
#include "Editor.h" #include "TerminalRenderer.h"
#include "Buffer.h" #include "Buffer.h"
#include "Editor.h"
// Version string expected to be provided by build system as KTE_VERSION_STR // Version string expected to be provided by build system as KTE_VERSION_STR
#ifndef KTE_VERSION_STR #ifndef KTE_VERSION_STR
# define KTE_VERSION_STR "dev" # define KTE_VERSION_STR "dev"
#endif #endif
TerminalRenderer::TerminalRenderer() = default; TerminalRenderer::TerminalRenderer() = default;
TerminalRenderer::~TerminalRenderer() = default; TerminalRenderer::~TerminalRenderer() = default;
@@ -180,6 +180,18 @@ TerminalRenderer::Draw(Editor &ed)
fname = "[no name]"; fname = "[no name]";
} }
left += " "; left += " ";
// Insert buffer position prefix "[x/N] " before filename
{
std::size_t total = ed.BufferCount();
if (total > 0) {
std::size_t idx1 = ed.CurrentBufferIndex() + 1; // human-friendly 1-based
left += "[";
left += std::to_string(static_cast<unsigned long long>(idx1));
left += "/";
left += std::to_string(static_cast<unsigned long long>(total));
left += "] ";
}
}
left += fname; left += fname;
if (b && b->Dirty()) if (b && b->Dirty())
left += " *"; left += " *";

View File

@@ -1,7 +1,6 @@
#include "TestFrontend.h" #include "TestFrontend.h"
#include "Editor.h"
#include "Command.h" #include "Command.h"
#include <iostream> #include "Editor.h"
bool bool

View File

@@ -4,11 +4,12 @@
#ifndef KTE_TEST_INPUT_HANDLER_H #ifndef KTE_TEST_INPUT_HANDLER_H
#define KTE_TEST_INPUT_HANDLER_H #define KTE_TEST_INPUT_HANDLER_H
#include "InputHandler.h"
#include <queue> #include <queue>
#include "InputHandler.h"
class TestInputHandler : public InputHandler {
class TestInputHandler final : public InputHandler {
public: public:
TestInputHandler() = default; TestInputHandler() = default;
@@ -21,7 +22,7 @@ public:
void QueueText(const std::string &text); void QueueText(const std::string &text);
bool IsEmpty() const [[nodiscard]] bool IsEmpty() const
{ {
return queue_.empty(); return queue_.empty();
} }

View File

@@ -1,5 +1,4 @@
#include "TestRenderer.h" #include "TestRenderer.h"
#include "Editor.h"
void void

View File

@@ -4,11 +4,12 @@
#ifndef KTE_TEST_RENDERER_H #ifndef KTE_TEST_RENDERER_H
#define KTE_TEST_RENDERER_H #define KTE_TEST_RENDERER_H
#include "Renderer.h"
#include <cstddef> #include <cstddef>
#include "Renderer.h"
class TestRenderer : public Renderer {
class TestRenderer final : public Renderer {
public: public:
TestRenderer() = default; TestRenderer() = default;
@@ -17,7 +18,7 @@ public:
void Draw(Editor &ed) override; void Draw(Editor &ed) override;
std::size_t GetDrawCount() const [[nodiscard]] std::size_t GetDrawCount() const
{ {
return draw_count_; return draw_count_;
} }

View File

@@ -1,8 +1,6 @@
#ifndef KTE_UNDONODE_H #ifndef KTE_UNDONODE_H
#define KTE_UNDONODE_H #define KTE_UNDONODE_H
#include <cstddef>
#include <cstdint>
#include <string> #include <string>

View File

@@ -16,7 +16,7 @@ UndoSystem::Begin(UndoType type)
if (type == UndoType::Delete) { if (type == UndoType::Delete) {
// Support batching both forward deletes (DeleteChar) and backspace (prepend case) // Support batching both forward deletes (DeleteChar) and backspace (prepend case)
// Forward delete: cursor stays at anchor col; expected == col // Forward delete: cursor stays at anchor col; expected == col
std::size_t anchor = static_cast<std::size_t>(tree_.pending->col); const auto anchor = static_cast<std::size_t>(tree_.pending->col);
if (anchor + tree_.pending->text.size() == static_cast<std::size_t>(col)) { if (anchor + tree_.pending->text.size() == static_cast<std::size_t>(col)) {
pending_prepend_ = false; pending_prepend_ = false;
return; // keep batching forward delete return; // keep batching forward delete

View File

@@ -2,8 +2,10 @@
#define KTE_UNDOSYSTEM_H #define KTE_UNDOSYSTEM_H
#include <string_view> #include <string_view>
#include "UndoTree.h" #include "UndoTree.h"
class Buffer; class Buffer;
class UndoSystem { class UndoSystem {
@@ -39,7 +41,6 @@ private:
void update_dirty_flag(); void update_dirty_flag();
private:
Buffer *buf_; Buffer *buf_;
UndoTree &tree_; UndoTree &tree_;
// Internal hint for Delete batching: whether next Append() should prepend // Internal hint for Delete batching: whether next Append() should prepend

View File

@@ -2,7 +2,7 @@
#define KTE_UNDOTREE_H #define KTE_UNDOTREE_H
#include "UndoNode.h" #include "UndoNode.h"
#include <memory>
struct UndoTree { struct UndoTree {
UndoNode *root = nullptr; // first edit ever UndoNode *root = nullptr; // first edit ever

View File

@@ -6,6 +6,8 @@
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>kge</string> <string>kge</string>
<key>CFBundleIconFile</key>
<string>@MACOSX_BUNDLE_ICON_FILE@</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>@KGE_BUNDLE_ID@</string> <string>@KGE_BUNDLE_ID@</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
@@ -23,4 +25,4 @@
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
<true/> <true/>
</dict> </dict>
</plist> </plist>

View File

@@ -17,8 +17,11 @@ kge \- Kyle's Graphical Editor (GUI-first)
is the GUI-first build target of Kyle's Text Editor. It shares the same is the GUI-first build target of Kyle's Text Editor. It shares the same
editor core and command model as editor core and command model as
.BR kte (1), .BR kte (1),
but defaults to the graphical ImGui frontend when available. A terminal and defaults to the graphical ImGui frontend when available. A terminal
(ncurses) frontend is also available and can be requested explicitly. (ncurses) frontend is also available and can be requested explicitly with
.B --term
or by invoking
.BR kte (1).
If one or more If one or more
.I files .I files
@@ -199,6 +202,8 @@ Open using the terminal frontend from kge:
.BR kte (1), .BR kte (1),
.I docs/ke.md .I docs/ke.md
(project keybinding manual) (project keybinding manual)
.br
Project homepage: https://github.com/wntrmute/kte
.SH BUGS .SH BUGS
Report issues on the project tracker. Some behaviors are inherited from Report issues on the project tracker. Some behaviors are inherited from
ke and may evolve over time; see the manual for notes. ke and may evolve over time; see the manual for notes.

View File

@@ -16,8 +16,15 @@ kte \- Kyle's Text Editor (terminal-first)
.B kte .B kte
is a small, fast, and understandable text editor with a terminal-first is a small, fast, and understandable text editor with a terminal-first
experience. It preserves ke's WordStar/VDE-style command model with experience. It preserves ke's WordStar/VDE-style command model with
Emacs-influenced ergonomics. The core uses ncurses in the terminal and can Emacs-influenced ergonomics. The core uses ncurses in the terminal.
optionally run with a GUI frontend if built.
By default, .B kte
runs in the terminal (ncurses) frontend. If the binary was built with GUI
support, the same editor core can be shown with an ImGui-based GUI by passing
.B --gui
or by invoking the GUI-first target
.BR kge (1)
when available.
If one or more If one or more
.I files .I files
@@ -194,6 +201,8 @@ Force GUI frontend (if available):
.BR kge (1), .BR kge (1),
.I docs/ke.md .I docs/ke.md
(project keybinding manual) (project keybinding manual)
.br
Project homepage: https://github.com/wntrmute/kte
.SH BUGS .SH BUGS
Incremental search currently restarts from the top on each invocation; see Incremental search currently restarts from the top on each invocation; see
\(lqKnown behavior\(rq in the ke manual. Report issues on the project tracker. \(lqKnown behavior\(rq in the ke manual. Report issues on the project tracker.

BIN
kge.icns Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
kge.iconset/icon_16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

BIN
kge.iconset/icon_32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
kge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

13
main.cc
View File

@@ -1,15 +1,15 @@
#include <cctype>
#include <cstdio>
#include <getopt.h>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string>
#include <cctype>
#include <unistd.h>
#include <getopt.h>
#include <signal.h> #include <signal.h>
#include <cstdio> #include <string>
#include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "Editor.h"
#include "Command.h" #include "Command.h"
#include "Editor.h"
#include "Frontend.h" #include "Frontend.h"
#include "TerminalFrontend.h" #include "TerminalFrontend.h"
@@ -22,6 +22,7 @@
# define KTE_VERSION_STR "devel" # define KTE_VERSION_STR "devel"
#endif #endif
static void static void
PrintUsage(const char *prog) PrintUsage(const char *prog)
{ {

View File

@@ -1,10 +1,11 @@
#include <iostream>
#include <cassert> #include <cassert>
#include <fstream> #include <fstream>
#include <iostream>
#include "Buffer.h"
#include "Command.h"
#include "Editor.h" #include "Editor.h"
#include "TestFrontend.h" #include "TestFrontend.h"
#include "Command.h"
#include "Buffer.h"
int int