Unicode improvements and version bump.
- Added full UTF-8 support for terminal rendering, including multi-width character handling. - Improved font handling in ImGui with expanded glyph support (Greek, Mathematical Operators). - Updated locale initialization to enable proper character rendering. - Bumped version to 1.5.8.
This commit is contained in:
@@ -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.7")
|
set(KTE_VERSION "1.5.8")
|
||||||
|
|
||||||
# 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.
|
||||||
|
|||||||
@@ -357,14 +357,43 @@ GUIFrontend::LoadGuiFont_(const char * /*path*/, const float size_px)
|
|||||||
{
|
{
|
||||||
const ImGuiIO &io = ImGui::GetIO();
|
const ImGuiIO &io = ImGui::GetIO();
|
||||||
io.Fonts->Clear();
|
io.Fonts->Clear();
|
||||||
const ImFont *font = io.Fonts->AddFontFromMemoryCompressedTTF(
|
|
||||||
|
ImFontConfig config;
|
||||||
|
config.MergeMode = false;
|
||||||
|
|
||||||
|
// Load Basic Latin + Latin Supplement
|
||||||
|
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
kte::Fonts::DefaultFontData,
|
kte::Fonts::DefaultFontData,
|
||||||
kte::Fonts::DefaultFontSize,
|
kte::Fonts::DefaultFontSize,
|
||||||
size_px);
|
size_px,
|
||||||
if (!font) {
|
&config,
|
||||||
font = io.Fonts->AddFontDefault();
|
io.Fonts->GetGlyphRangesDefault());
|
||||||
}
|
|
||||||
(void) font;
|
// Merge Greek and Coptic
|
||||||
|
config.MergeMode = true;
|
||||||
|
static const ImWchar greek_ranges[] = {
|
||||||
|
0x0370, 0x03FF, // Greek and Coptic
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
|
kte::Fonts::DefaultFontData,
|
||||||
|
kte::Fonts::DefaultFontSize,
|
||||||
|
size_px,
|
||||||
|
&config,
|
||||||
|
greek_ranges);
|
||||||
|
|
||||||
|
// Merge Mathematical Operators
|
||||||
|
static const ImWchar math_ranges[] = {
|
||||||
|
0x2200, 0x22FF, // Mathematical Operators
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
|
kte::Fonts::DefaultFontData,
|
||||||
|
kte::Fonts::DefaultFontSize,
|
||||||
|
size_px,
|
||||||
|
&config,
|
||||||
|
math_ranges);
|
||||||
|
|
||||||
io.Fonts->Build();
|
io.Fonts->Build();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#include <clocale>
|
||||||
|
#define _XOPEN_SOURCE_EXTENDED 1
|
||||||
|
#include <cwchar>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -177,15 +180,32 @@ TerminalRenderer::Draw(Editor &ed)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
while (written < cols) {
|
while (written < cols) {
|
||||||
char ch = ' ';
|
|
||||||
bool from_src = false;
|
bool from_src = false;
|
||||||
|
wchar_t wch = L' ';
|
||||||
|
int wch_len = 1;
|
||||||
|
int disp_w = 1;
|
||||||
|
|
||||||
if (src_i < line.size()) {
|
if (src_i < line.size()) {
|
||||||
unsigned char c = static_cast<unsigned char>(line[src_i]);
|
// Decode UTF-8
|
||||||
if (c == '\t') {
|
std::mbstate_t state = std::mbstate_t();
|
||||||
|
size_t res = std::mbrtowc(
|
||||||
|
&wch, &line[src_i], line.size() - src_i, &state);
|
||||||
|
if (res == (size_t) -1 || res == (size_t) -2) {
|
||||||
|
// Invalid or incomplete; treat as single byte
|
||||||
|
wch = static_cast<unsigned char>(line[src_i]);
|
||||||
|
wch_len = 1;
|
||||||
|
} else if (res == 0) {
|
||||||
|
wch = L'\0';
|
||||||
|
wch_len = 1;
|
||||||
|
} else {
|
||||||
|
wch_len = static_cast<int>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wch == L'\t') {
|
||||||
std::size_t next_tab = tabw - (render_col % tabw);
|
std::size_t next_tab = tabw - (render_col % tabw);
|
||||||
if (render_col + next_tab <= coloffs) {
|
if (render_col + next_tab <= coloffs) {
|
||||||
render_col += next_tab;
|
render_col += next_tab;
|
||||||
++src_i;
|
src_i += wch_len;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Emit spaces for tab
|
// Emit spaces for tab
|
||||||
@@ -233,23 +253,34 @@ TerminalRenderer::Draw(Editor &ed)
|
|||||||
++render_col;
|
++render_col;
|
||||||
--next_tab;
|
--next_tab;
|
||||||
}
|
}
|
||||||
++src_i;
|
src_i += wch_len;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// normal char
|
// normal char
|
||||||
|
disp_w = wcwidth(wch);
|
||||||
|
if (disp_w < 0)
|
||||||
|
disp_w = 1; // non-printable or similar
|
||||||
|
|
||||||
if (render_col < coloffs) {
|
if (render_col < coloffs) {
|
||||||
++render_col;
|
render_col += disp_w;
|
||||||
++src_i;
|
src_i += wch_len;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ch = static_cast<char>(c);
|
|
||||||
from_src = true;
|
from_src = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// beyond EOL, fill spaces
|
// beyond EOL, fill spaces
|
||||||
ch = ' ';
|
wch = L' ';
|
||||||
|
wch_len = 1;
|
||||||
|
disp_w = 1;
|
||||||
from_src = false;
|
from_src = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (written + disp_w > cols) {
|
||||||
|
// would overflow, just break
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
bool in_hl = search_mode && from_src && is_src_in_hl(src_i);
|
bool in_hl = search_mode && from_src && is_src_in_hl(src_i);
|
||||||
bool in_cur =
|
bool in_cur =
|
||||||
has_current && li == cur_my && from_src && src_i >= cur_mx && src_i <
|
has_current && li == cur_my && from_src && src_i >= cur_mx && src_i <
|
||||||
@@ -273,11 +304,20 @@ TerminalRenderer::Draw(Editor &ed)
|
|||||||
if (!in_hl && from_src) {
|
if (!in_hl && from_src) {
|
||||||
apply_token_attr(token_at(src_i));
|
apply_token_attr(token_at(src_i));
|
||||||
}
|
}
|
||||||
addch(static_cast<unsigned char>(ch));
|
|
||||||
++written;
|
if (from_src) {
|
||||||
++render_col;
|
cchar_t cch;
|
||||||
|
wchar_t warr[2] = {wch, L'\0'};
|
||||||
|
setcchar(&cch, warr, A_NORMAL, 0, nullptr);
|
||||||
|
add_wch(&cch);
|
||||||
|
} else {
|
||||||
|
addch(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
written += disp_w;
|
||||||
|
render_col += disp_w;
|
||||||
if (from_src)
|
if (from_src)
|
||||||
++src_i;
|
src_i += wch_len;
|
||||||
if (src_i >= line.size() && written >= cols)
|
if (src_i >= line.size() && written >= cols)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -306,14 +346,26 @@ TerminalRenderer::Draw(Editor &ed)
|
|||||||
std::size_t src_i_cur = 0;
|
std::size_t src_i_cur = 0;
|
||||||
std::size_t render_col_cur = 0;
|
std::size_t render_col_cur = 0;
|
||||||
while (src_i_cur < line_for_cursor.size() && src_i_cur < cx) {
|
while (src_i_cur < line_for_cursor.size() && src_i_cur < cx) {
|
||||||
unsigned char ccur = static_cast<unsigned char>(line_for_cursor[src_i_cur]);
|
std::mbstate_t state = std::mbstate_t();
|
||||||
if (ccur == '\t') {
|
wchar_t wch;
|
||||||
|
size_t res = std::mbrtowc(
|
||||||
|
&wch, &line_for_cursor[src_i_cur], line_for_cursor.size() - src_i_cur,
|
||||||
|
&state);
|
||||||
|
|
||||||
|
if (res == (size_t) -1 || res == (size_t) -2) {
|
||||||
|
render_col_cur += 1;
|
||||||
|
src_i_cur += 1;
|
||||||
|
} else if (res == 0) {
|
||||||
|
src_i_cur += 1;
|
||||||
|
} else {
|
||||||
|
if (wch == L'\t') {
|
||||||
std::size_t next_tab = tabw - (render_col_cur % tabw);
|
std::size_t next_tab = tabw - (render_col_cur % tabw);
|
||||||
render_col_cur += next_tab;
|
render_col_cur += next_tab;
|
||||||
++src_i_cur;
|
|
||||||
} else {
|
} else {
|
||||||
++render_col_cur;
|
int dw = wcwidth(wch);
|
||||||
++src_i_cur;
|
render_col_cur += (dw < 0) ? 1 : dw;
|
||||||
|
}
|
||||||
|
src_i_cur += res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rx_recomputed = render_col_cur;
|
rx_recomputed = render_col_cur;
|
||||||
|
|||||||
@@ -8,16 +8,43 @@ Font::Load(const float size) const
|
|||||||
{
|
{
|
||||||
const ImGuiIO &io = ImGui::GetIO();
|
const ImGuiIO &io = ImGui::GetIO();
|
||||||
io.Fonts->Clear();
|
io.Fonts->Clear();
|
||||||
const ImFont *font = io.Fonts->AddFontFromMemoryCompressedTTF(
|
|
||||||
|
ImFontConfig config;
|
||||||
|
config.MergeMode = false;
|
||||||
|
|
||||||
|
// Load Basic Latin + Latin Supplement
|
||||||
|
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
this->data_,
|
this->data_,
|
||||||
this->size_,
|
this->size_,
|
||||||
size);
|
size,
|
||||||
|
&config,
|
||||||
|
io.Fonts->GetGlyphRangesDefault());
|
||||||
|
|
||||||
if (!font) {
|
// Merge Greek and Coptic
|
||||||
font = io.Fonts->AddFontDefault();
|
config.MergeMode = true;
|
||||||
}
|
static const ImWchar greek_ranges[] = {
|
||||||
|
0x0370, 0x03FF, // Greek and Coptic
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
|
this->data_,
|
||||||
|
this->size_,
|
||||||
|
size,
|
||||||
|
&config,
|
||||||
|
greek_ranges);
|
||||||
|
|
||||||
|
// Merge Mathematical Operators
|
||||||
|
static const ImWchar math_ranges[] = {
|
||||||
|
0x2200, 0x22FF, // Mathematical Operators
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
io.Fonts->AddFontFromMemoryCompressedTTF(
|
||||||
|
this->data_,
|
||||||
|
this->size_,
|
||||||
|
size,
|
||||||
|
&config,
|
||||||
|
math_ranges);
|
||||||
|
|
||||||
(void) font;
|
|
||||||
io.Fonts->Build();
|
io.Fonts->Build();
|
||||||
}
|
}
|
||||||
} // namespace kte::Fonts
|
} // namespace kte::Fonts
|
||||||
3
main.cc
3
main.cc
@@ -1,3 +1,4 @@
|
|||||||
|
#include <clocale>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -113,6 +114,8 @@ RunStressHighlighter(unsigned seconds)
|
|||||||
int
|
int
|
||||||
main(int argc, const char *argv[])
|
main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
|
std::setlocale(LC_ALL, "");
|
||||||
|
|
||||||
Editor editor;
|
Editor editor;
|
||||||
|
|
||||||
// CLI parsing using getopt_long
|
// CLI parsing using getopt_long
|
||||||
|
|||||||
Reference in New Issue
Block a user