Fix double- and zero-wide characters
It's not perfect but seems to work well enough.
This commit is contained in:
parent
d3b966a93f
commit
1cc91a4b17
|
@ -17,6 +17,10 @@ set (project_VERSION "${project_VERSION_MAJOR}")
|
||||||
set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}")
|
set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}")
|
||||||
set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}")
|
set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}")
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
include (CheckFunctionExists)
|
||||||
|
CHECK_FUNCTION_EXISTS ("wcwidth" HAVE_WCWIDTH)
|
||||||
|
|
||||||
# Dependecies
|
# Dependecies
|
||||||
find_package (PkgConfig REQUIRED)
|
find_package (PkgConfig REQUIRED)
|
||||||
pkg_check_modules (dependencies REQUIRED ncursesw glib-2.0 gio-2.0 pango)
|
pkg_check_modules (dependencies REQUIRED ncursesw glib-2.0 gio-2.0 pango)
|
||||||
|
|
|
@ -8,5 +8,7 @@
|
||||||
#define GETTEXT_PACKAGE PROJECT_NAME
|
#define GETTEXT_PACKAGE PROJECT_NAME
|
||||||
#define GETTEXT_DIRNAME "${CMAKE_INSTALL_PREFIX}/share/locale"
|
#define GETTEXT_DIRNAME "${CMAKE_INSTALL_PREFIX}/share/locale"
|
||||||
|
|
||||||
|
#cmakedefine HAVE_WCWIDTH
|
||||||
|
|
||||||
#endif /* ! CONFIG_H */
|
#endif /* ! CONFIG_H */
|
||||||
|
|
||||||
|
|
85
src/sdtui.c
85
src/sdtui.c
|
@ -18,7 +18,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _XOPEN_SOURCE_EXTENDED /**< Yes, we want ncursesw. */
|
#define _XOPEN_SOURCE 500 //!< wcwidth
|
||||||
|
#define _XOPEN_SOURCE_EXTENDED //!< Yes, we want ncursesw.
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -98,6 +99,18 @@ struct curses_event
|
||||||
MEVENT mouse;
|
MEVENT mouse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
unichar_width (gunichar ch)
|
||||||
|
{
|
||||||
|
if (g_unichar_iszerowidth (ch))
|
||||||
|
return 0;
|
||||||
|
return 1 + g_unichar_iswide (ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_WCWIDTH
|
||||||
|
#define wcwidth(x) 1
|
||||||
|
#endif // ! HAVE_WCWIDTH
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
is_character_in_locale (wchar_t c)
|
is_character_in_locale (wchar_t c)
|
||||||
{
|
{
|
||||||
|
@ -353,31 +366,68 @@ app_add_utf8_string (Application *self, const gchar *str, int n)
|
||||||
ssize_t wide_len = wcslen (wide_str);
|
ssize_t wide_len = wcslen (wide_str);
|
||||||
wchar_t padding = L' ', error = L'?', ellipsis = L'…';
|
wchar_t padding = L' ', error = L'?', ellipsis = L'…';
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Compute how many wide characters fit in the limit
|
||||||
|
gint cols, i;
|
||||||
|
for (cols = i = 0; i < wide_len; i++)
|
||||||
|
{
|
||||||
|
if (!is_character_in_locale (wide_str[i]))
|
||||||
|
wide_str[i] = error;
|
||||||
|
|
||||||
|
gint width = wcwidth (wide_str[i]);
|
||||||
|
if (n >= 0 && cols + width > n)
|
||||||
|
break;
|
||||||
|
cols += width;
|
||||||
|
}
|
||||||
|
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
n = wide_len;
|
n = cols;
|
||||||
|
|
||||||
if (wide_len > n)
|
// Append ellipsis if the whole string didn't fit
|
||||||
|
gint len = i;
|
||||||
|
if (len != wide_len)
|
||||||
{
|
{
|
||||||
if (is_character_in_locale (ellipsis) && n > 0)
|
if (is_character_in_locale (ellipsis))
|
||||||
wide_str[n - 1] = ellipsis;
|
|
||||||
else if (n >= 3)
|
|
||||||
{
|
{
|
||||||
wide_str[n - 1] = L'.';
|
if (cols + wcwidth (ellipsis) > n)
|
||||||
wide_str[n - 2] = L'.';
|
cols -= wcwidth (wide_str[len - 1]);
|
||||||
wide_str[n - 3] = L'.';
|
else
|
||||||
|
len++;
|
||||||
|
|
||||||
|
wide_str[len - 1] = ellipsis;
|
||||||
|
cols += wcwidth (ellipsis);
|
||||||
|
}
|
||||||
|
else if (n >= 3 && len >= 3)
|
||||||
|
{
|
||||||
|
// With zero-width characters this overflows
|
||||||
|
// It's just a fallback anyway
|
||||||
|
cols -= wcwidth (wide_str[len - 1]);
|
||||||
|
cols -= wcwidth (wide_str[len - 2]);
|
||||||
|
cols -= wcwidth (wide_str[len - 3]);
|
||||||
|
cols += 3;
|
||||||
|
|
||||||
|
wide_str[len - 1] = L'.';
|
||||||
|
wide_str[len - 2] = L'.';
|
||||||
|
wide_str[len - 3] = L'.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gint i;
|
|
||||||
cchar_t cch;
|
cchar_t cch;
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (setcchar (&cch, (i < wide_len ? &wide_str[i] : &padding),
|
if (setcchar (&cch, &wide_str[i], A_NORMAL, 0, NULL) == OK)
|
||||||
A_NORMAL, 0, NULL) == ERR)
|
|
||||||
setcchar (&cch, &error, A_NORMAL, 0, NULL);
|
|
||||||
add_wch (&cch);
|
add_wch (&cch);
|
||||||
|
else
|
||||||
|
// This shouldn't happen
|
||||||
|
cols -= wcwidth (wide_str[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setcchar (&cch, &padding, A_NORMAL, 0, NULL);
|
||||||
|
while (cols++ < n)
|
||||||
|
add_wch (&cch);
|
||||||
|
|
||||||
g_free (wide_str);
|
g_free (wide_str);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -399,7 +449,12 @@ app_redraw_top (Application *self)
|
||||||
app_add_utf8_string (self, input_utf8, COLS - indent);
|
app_add_utf8_string (self, input_utf8, COLS - indent);
|
||||||
g_free (input_utf8);
|
g_free (input_utf8);
|
||||||
|
|
||||||
move (0, indent + self->input_pos);
|
guint offset, i;
|
||||||
|
for (offset = i = 0; i < self->input_pos; i++)
|
||||||
|
// This may be inconsistent with the output of app_add_utf8_string()
|
||||||
|
offset += unichar_width (g_array_index (self->input, gunichar, i));
|
||||||
|
|
||||||
|
move (0, indent + offset);
|
||||||
refresh ();
|
refresh ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue