Improve handling of text containing context-dependent unicode points.
Build and Test / build-linux (push) Has been cancelled
Build and Test / build-wayland (push) Has been cancelled
Build and Test / build-macos (push) Has been cancelled
Build and Test / build-windows (push) Has been cancelled

This commit makes platforms Windows and macOS compute string widths
with the same mechanism as what is in place for platforms Wayland/X11:
- the width of a string containing a single codepoint is computed and
memorized in the table of character widths;
- the width of a string containing several codepoints is computed as
such rather than as the sum of the widths of its composing characters.

This commit also fixes how text input under Windows is processed
when a character needs encoding as a surrogate.

The result is that FLTK text widgets input and draw correctly also
complex emojis encoded with context-dependent codepoints.
This commit is contained in:
ManoloFLTK
2026-01-21 15:47:59 +01:00
parent d9cc1e555b
commit 211d42049d
3 changed files with 55 additions and 4 deletions
+17 -2
View File
@@ -1,7 +1,7 @@
//
// Windows-specific code for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2024 by Bill Spitzak and others.
// Copyright 1998-2026 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -1510,7 +1510,22 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
static char buffer[1024];
if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
wchar_t u = (wchar_t)wParam;
Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
// Windows emoji palette triggered with Windows + dot sends 2 or more WM_CHAR messages:
// the 2 components of a surrogate pair, or variation selectors, or zero-width joiner,
// or emoji modifiers FITZPATRICK or extra Unicode points.
if (u >= 0xD800 && u <= 0xDFFF) { // handle the 2 components of a surrogate pair
static wchar_t surrogate_pair[2];
if (IS_HIGH_SURROGATE(u)) {
surrogate_pair[0] = u; // memorize the 1st member of the pair
Fl::e_length = 0;
return 0; // and wait for next WM_CHAR message that will give the 2nd member
} else {
surrogate_pair[1] = u; // memorize the 2nd member of the pair
Fl::e_length = fl_utf8fromwc(buffer, 1024, surrogate_pair, 2); // transform to UTF-8
}
} else {
Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); // process regular Unicode point
}
buffer[Fl::e_length] = 0;
} else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) {
if (state & FL_NUM_LOCK) {
@@ -1,7 +1,7 @@
//
// Windows font utilities for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2025 by Bill Spitzak and others.
// Copyright 1998-2026 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -366,6 +366,27 @@ Fl_Fontsize Fl_GDI_Graphics_Driver::size_unscaled() {
}
double Fl_GDI_Graphics_Driver::width_unscaled(const char* c, int n) {
if (n == 0) return 0;
int len1 = fl_utf8len1(*c);
if (n > len1 && len1 > 0) { // a text with several codepoints: compute its typographical width
int wn = fl_utf8toUtf16(c, n, wstr, wstr_len);
if (wn >= wstr_len) {
wstr = (unsigned short*) realloc(wstr, sizeof(unsigned short) * (wn + 1));
wstr_len = wn + 1;
wn = fl_utf8toUtf16(c, n, wstr, wstr_len);
}
HDC gc2 = gc_;
HWND hWnd;
if (!gc2) {
hWnd = Fl::first_window() ? fl_xid(Fl::first_window()) : NULL;
gc2 = GetDC(hWnd);
}
SelectObject(gc2, ((Fl_GDI_Font_Descriptor*)font_descriptor())->fid);
SIZE s;
GetTextExtentPoint32W(gc2, (WCHAR*)wstr, wn, &s);
if (gc2 && gc2 != gc_) ReleaseDC(hWnd, gc2);
return (double)s.cx;
}
int i = 0;
if (!font_descriptor()) return -1.0;
double w = 0.0;
@@ -1,7 +1,7 @@
//
// MacOS font selection routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2018 by Bill Spitzak and others.
// Copyright 1998-2026 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -300,6 +300,21 @@ void Fl_Quartz_Graphics_Driver::rtl_draw(const char* c, int n, int x, int y) {
}
double Fl_Quartz_Graphics_Driver::width(const char* txt, int n) {
if (n == 0) return 0;
int len1 = fl_utf8len1(*txt);
if (len1 > 0 && n > len1) { // a text with several codepoints: compute its typographical width
CFStringRef str = CFStringCreateWithBytes(NULL, (const UInt8*)txt, n, kCFStringEncodingUTF8, false);
if (str) {
CFDictionarySetValue(attributes, kCTFontAttributeName, valid_font_descriptor()->fontref);
CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str, attributes);
CFRelease(str);
CTLineRef ctline = CTLineCreateWithAttributedString(mastr);
CFRelease(mastr);
double d = CTLineGetTypographicBounds(ctline, NULL, NULL, NULL);
CFRelease(ctline);
return d;
}
}
int wc_len = n;
UniChar *uniStr = mac_Utf8_to_Utf16(txt, n, &wc_len);
return width(uniStr, wc_len);