From c1bdeb56c9ee4fc45639ef4eaa4306f7e41a00c0 Mon Sep 17 00:00:00 2001 From: Mirek Fidler Date: Wed, 14 Jan 2026 14:08:56 +0100 Subject: [PATCH] RichText: New 'no color emoji' flag M in QTF, Draw: fixed problems with NoColor fonts in X11/Gtk --- uppsrc/CtrlCore/DrawTextX11.cpp | 4 ++-- uppsrc/CtrlCore/GtkDrawImage.cpp | 2 +- uppsrc/CtrlCore/ImageX11.cpp | 2 +- uppsrc/CtrlCore/X11Wnd.cpp | 4 ++-- uppsrc/CtrlLib/RichText.h | 2 -- uppsrc/CtrlLib/RichTextView.cpp | 1 - uppsrc/Draw/Draw.h | 2 +- uppsrc/Draw/Font.cpp | 3 +++ uppsrc/Draw/FontCR.cpp | 4 ++++ uppsrc/Draw/FontFc.cpp | 2 +- uppsrc/Draw/src.tpp/Font_en-us.tpp | 3 ++- uppsrc/RichEdit/SelectSymbol.cpp | 5 ++--- uppsrc/RichText/EncodeQtf.cpp | 1 + uppsrc/RichText/Format.cpp | 6 ++++++ uppsrc/RichText/Para.h | 3 +++ uppsrc/RichText/ParaData.cpp | 17 ++++++++++++++++- uppsrc/RichText/ParaPaint.cpp | 2 -- uppsrc/RichText/ParseQtf.cpp | 1 + uppsrc/RichText/RichText.h | 1 - uppsrc/RichText/Txt.h | 1 + uppsrc/RichText/srcdoc.tpp/QTF_en-us.tpp | 2 ++ 21 files changed, 49 insertions(+), 19 deletions(-) diff --git a/uppsrc/CtrlCore/DrawTextX11.cpp b/uppsrc/CtrlCore/DrawTextX11.cpp index 0f57a8a8f..93b0ff1e0 100644 --- a/uppsrc/CtrlCore/DrawTextX11.cpp +++ b/uppsrc/CtrlCore/DrawTextX11.cpp @@ -193,10 +193,10 @@ void SystemDraw::DrawTextOp(int x, int y, int angle, const wchar *text, Font fon int xpos = 0; for(int i = 0; i < n; i++) { wchar h = text[i]; - XftDrawString16(xftdraw, &c, xftfont, + XftDrawString32(xftdraw, &c, xftfont, int(ox + xpos * cosa + offset.cx), int(oy - xpos * sina + offset.cy), - (FcChar16 *)&h, 1); + &h, 1); xpos += dx ? dx[i] : font[text[i]]; } if(font.IsUnderline() || font.IsStrikeout()) { diff --git a/uppsrc/CtrlCore/GtkDrawImage.cpp b/uppsrc/CtrlCore/GtkDrawImage.cpp index c197a77c8..6c75e11be 100644 --- a/uppsrc/CtrlCore/GtkDrawImage.cpp +++ b/uppsrc/CtrlCore/GtkDrawImage.cpp @@ -250,7 +250,7 @@ Image Image::SizeBottomLeft() FCURSOR_(GDK_BOTTOM_LEFT_CORNER) Image Image::SizeBottom() FCURSOR_(GDK_BOTTOM_SIDE) Image Image::SizeBottomRight() FCURSOR_(GDK_BOTTOM_RIGHT_CORNER) Image Image::Cross() FCURSOR_(GDK_CROSSHAIR) -Image Image::Hand() FCURSOR_(GDK_HAND2 ) +Image Image::Hand() FCURSOR_(GDK_HAND2) } diff --git a/uppsrc/CtrlCore/ImageX11.cpp b/uppsrc/CtrlCore/ImageX11.cpp index aed07bde7..a26bcc836 100644 --- a/uppsrc/CtrlCore/ImageX11.cpp +++ b/uppsrc/CtrlCore/ImageX11.cpp @@ -430,7 +430,7 @@ Image Image::SizeBottomLeft() FCURSOR_(XC_bottom_left_corner) Image Image::SizeBottom() FCURSOR_(XC_bottom_side) Image Image::SizeBottomRight() FCURSOR_(XC_bottom_right_corner) Image Image::Cross() FCURSOR_(XC_crosshair) -Image Image::Hand() FCURSOR_(XC_hand1) +Image Image::Hand() FCURSOR_(XC_hand2) void *CursorX11(const Image& img) { diff --git a/uppsrc/CtrlCore/X11Wnd.cpp b/uppsrc/CtrlCore/X11Wnd.cpp index 0b27f7d1b..b73a29dae 100644 --- a/uppsrc/CtrlCore/X11Wnd.cpp +++ b/uppsrc/CtrlCore/X11Wnd.cpp @@ -24,6 +24,7 @@ bool __X11_Grabbing = false; #define x_Event(x) { x, #x }, +#ifdef _DEBUG static struct XEventMap { int ID; @@ -33,6 +34,7 @@ sXevent[] = { #include "X11Event.i" { 0, NULL } }; +#endif ArrayMap& Ctrl::Xwindow() { @@ -48,8 +50,6 @@ Ptr Ctrl::popupWnd; Point Ctrl::mousePos; -static int s_starttime; - void Ctrl::DoPaint(const Vector& invalid) { GuiLock __; diff --git a/uppsrc/CtrlLib/RichText.h b/uppsrc/CtrlLib/RichText.h index 76ed0b1f6..0ccee115d 100644 --- a/uppsrc/CtrlLib/RichText.h +++ b/uppsrc/CtrlLib/RichText.h @@ -34,7 +34,6 @@ private: bool shrink_oversized_objects; bool icursor = true; bool copy_with_tabs = false; - bool mono_glyphs = false; void EndSizeTracking(); void SetSb(); @@ -121,7 +120,6 @@ public: RichTextView& ICursor(bool b = true) { icursor = b; return *this; } RichTextView& NoICursor() { return ICursor(false); } RichTextView& CopyWithTabs(bool b = true) { copy_with_tabs = b; return *this; } - RichTextView& MonoGlyphs(bool b = true) { mono_glyphs = b; return *this; } void operator=(const char *qtf) { SetQTF(qtf); } diff --git a/uppsrc/CtrlLib/RichTextView.cpp b/uppsrc/CtrlLib/RichTextView.cpp index 3e38ff0a7..f9ead002e 100644 --- a/uppsrc/CtrlLib/RichTextView.cpp +++ b/uppsrc/CtrlLib/RichTextView.cpp @@ -66,7 +66,6 @@ void RichTextView::Paint(Draw& w) pi.sizetracking = sizetracking; pi.shrink_oversized_objects = shrink_oversized_objects; pi.darktheme = IsDarkTheme(); - pi.mono_glyphs = mono_glyphs; Rect pg = GetPage(); pg.top = TopY(); text.Paint(pw, pg, pi); diff --git a/uppsrc/Draw/Draw.h b/uppsrc/Draw/Draw.h index 745235a25..01694bd2c 100644 --- a/uppsrc/Draw/Draw.h +++ b/uppsrc/Draw/Draw.h @@ -153,10 +153,10 @@ public: Font& Strikeout() { v.flags |= FONT_STRIKEOUT; return *this; } Font& NoStrikeout() { v.flags &= ~FONT_STRIKEOUT; return *this; } Font& Strikeout(bool b) { return b ? Strikeout() : NoStrikeout(); } + Font& NoColor(bool b = true) { if(b) v.flags |= FONT_NOCOLOR; else v.flags &= ~FONT_NOCOLOR; return *this; } Font& NonAntiAliased() { v.flags |= FONT_NON_ANTI_ALIASED; return *this; } Font& NoNonAntiAliased() { v.flags &= ~FONT_NON_ANTI_ALIASED; return *this; } // deprecated Font& NonAntiAliased(bool b) { return b ? NonAntiAliased() : NoNonAntiAliased(); } // deprecated - Font& NoColor() { v.flags |= FONT_NOCOLOR; return *this; } Font& TrueTypeOnly() { v.flags |= FONT_TRUE_TYPE_ONLY; return *this; } // deprecated Font& NoTrueTypeOnly() { v.flags &= ~FONT_TRUE_TYPE_ONLY; return *this; } // deprecated Font& TrueTypeOnly(bool b) { return b ? TrueTypeOnly() : NoTrueTypeOnly(); } // deprecated diff --git a/uppsrc/Draw/Font.cpp b/uppsrc/Draw/Font.cpp index 28db20693..5196be50c 100644 --- a/uppsrc/Draw/Font.cpp +++ b/uppsrc/Draw/Font.cpp @@ -469,6 +469,9 @@ CharEntry GetGlyphEntry(Font font, int chr, hash_t hash) { Mutex::Lock __(sFontLock); GlyphInfoMaker m; +#ifdef flagX11 + font.NoColor(); +#endif m.font = font; m.chr = chr; return MakeValue(m).To(); diff --git a/uppsrc/Draw/FontCR.cpp b/uppsrc/Draw/FontCR.cpp index 6ef167291..8c5b59ef9 100644 --- a/uppsrc/Draw/FontCR.cpp +++ b/uppsrc/Draw/FontCR.cpp @@ -383,6 +383,10 @@ bool Replace(Font fnt, int chr, Font& rfnt) fnt.Face(Font::SANSSERIF); // otherwise devangari font is used, which looks off #endif +#ifdef flagX11 // forces the legacy X11 backend (instead of GTK) + fnt.NoColor(); // no color emojis in pure X11 for now +#endif + bool prefer_color = PreferColorEmoji(chr) && !fnt.IsNoColor(); static VectorMap rface[2]; // face index to font info static Vector color[2]; // colorimg faces diff --git a/uppsrc/Draw/FontFc.cpp b/uppsrc/Draw/FontFc.cpp index 64d4e5b68..a6f2648fe 100644 --- a/uppsrc/Draw/FontFc.cpp +++ b/uppsrc/Draw/FontFc.cpp @@ -29,7 +29,7 @@ bool sInitFt(void) FcPattern *CreateFcPattern(Font font) { - LTIMING("CreateXftFont"); + LTIMING("CreateFcPattern"); int hg = abs(font.GetHeight()); if(hg == 0) hg = 10; String face = font.GetFaceName(); diff --git a/uppsrc/Draw/src.tpp/Font_en-us.tpp b/uppsrc/Draw/src.tpp/Font_en-us.tpp index 1da9f909d..d7b7461ff 100644 --- a/uppsrc/Draw/src.tpp/Font_en-us.tpp +++ b/uppsrc/Draw/src.tpp/Font_en-us.tpp @@ -168,7 +168,8 @@ normally returns), it is replaced with standard font height.&] [s2; Sets or unsets bold/italic/undeline/strikeout modes.&] [s3; &] [s4; &] -[s5;:Upp`:`:Font`:`:NoColor`(`): Font[@(0.0.255) `&] [* NoColor]()&] +[s5;:Upp`:`:Font`:`:NoColor`(bool`): Font[@(0.0.255) `&] [* NoColor]([@(0.0.255) bool] +[*@3 b] [@(0.0.255) `=] [@(0.0.255) true])&] [s2;%% If used, suppresses replacement of glyphs with color versions (e.g. for emoji).&] [s3; &] diff --git a/uppsrc/RichEdit/SelectSymbol.cpp b/uppsrc/RichEdit/SelectSymbol.cpp index 44b4feb1a..bf13bbb64 100644 --- a/uppsrc/RichEdit/SelectSymbol.cpp +++ b/uppsrc/RichEdit/SelectSymbol.cpp @@ -57,7 +57,6 @@ SelectSymbolDlg::SelectSymbolDlg(bool show_variants) search ^= group ^= [=] { Sync(); }; symbols.NoHyperlinkDecoration(); - symbols.MonoGlyphs(); if(show_variants) { symbols.WhenLink << [=](const String& s) { Variants(Atoi(s)); }; @@ -81,7 +80,7 @@ void SelectSymbolDlg::Sync() int g = ~group; String s = ToLower(~~search); - String qtf = "[A5 "; + String qtf = "[MA5 "; for(int i = 0; i < syms.GetCount(); i++) if(g == 0 || g == i + 1) for(Tuple h : syms[i]) @@ -92,7 +91,7 @@ void SelectSymbolDlg::Sync() void SelectSymbolDlg::Variants(int codepoint) { - String qtf = "[A5 "; + String qtf = "[MA5 "; Index h; svg.Clear(); for(int i = 0; i < Font::GetFaceCount(); i++) { diff --git a/uppsrc/RichText/EncodeQtf.cpp b/uppsrc/RichText/EncodeQtf.cpp index 1cdd9c5c4..0568b175a 100644 --- a/uppsrc/RichText/EncodeQtf.cpp +++ b/uppsrc/RichText/EncodeQtf.cpp @@ -44,6 +44,7 @@ void CharFmt(String& fmt, const RichPara::CharFormat& a, const RichPara::CharFor if(a.IsNonAntiAliased() != b.IsNonAntiAliased()) fmt.Cat('t'); if(a.capitals != b.capitals) fmt.Cat('c'); if(a.dashed != b.dashed) fmt.Cat('d'); + if(a.IsNoColor() != b.IsNoColor()) fmt.Cat('M'); if(a.sscript != b.sscript) fmt.Cat(b.sscript == 0 ? a.sscript == 1 ? '`' : ',' : b.sscript == 1 ? '`' : ','); diff --git a/uppsrc/RichText/Format.cpp b/uppsrc/RichText/Format.cpp index a6161df7b..a2495668b 100644 --- a/uppsrc/RichText/Format.cpp +++ b/uppsrc/RichText/Format.cpp @@ -44,6 +44,10 @@ void RichTxt::FormatInfo::Combine(const RichPara::CharFormat& fmt) charvalid &= ~DASHED; dashed = false; } + if(IsNoColor() != fmt.IsNoColor()) { + charvalid &= ~NO_COLOR; + dashed = false; + } if(sscript != fmt.sscript) { charvalid &= ~SSCRIPT; sscript = 0; @@ -156,6 +160,8 @@ void RichTxt::FormatInfo::ApplyTo(RichPara::CharFormat& fmt) const fmt.capitals = capitals; if(charvalid & DASHED) fmt.dashed = dashed; + if(charvalid & NO_COLOR) + fmt.NoColor(IsNoColor()); if(charvalid & SSCRIPT) fmt.sscript = sscript; if(charvalid & FACE) diff --git a/uppsrc/RichText/Para.h b/uppsrc/RichText/Para.h index dca2a95bb..76a1553ae 100644 --- a/uppsrc/RichText/Para.h +++ b/uppsrc/RichText/Para.h @@ -46,6 +46,9 @@ struct RichPara { NONAA0 = 1, NONAA1 = 2, NONAAS = 3, + NOCOLOR0 = 4, + NOCOLOR1 = 5, + NOCOLORS = 6, }; enum BULLET_STYLE { diff --git a/uppsrc/RichText/ParaData.cpp b/uppsrc/RichText/ParaData.cpp index 2032b22ec..2d540d664 100644 --- a/uppsrc/RichText/ParaData.cpp +++ b/uppsrc/RichText/ParaData.cpp @@ -61,7 +61,6 @@ PaintInfo::PaintInfo() showlabels = false; shrink_oversized_objects = false; textcolor = Null; - mono_glyphs = false; DrawSelection = [] (Draw& w, int x, int y, int cx, int cy) { w.DrawRect(x, y, cx, cy, InvertColor); }; @@ -195,6 +194,11 @@ void RichPara::Charformat(Stream& out, const RichPara::CharFormat& o, out.Put(n.IsNonAntiAliased() == s.IsNonAntiAliased() ? NONAAS : NONAA0 + n.IsNonAntiAliased()); } + if(o.IsNoColor() != n.IsNoColor()) { + out.Put(EXT); + out.Put(n.IsNoColor() == s.IsNoColor() ? NOCOLORS + : NOCOLOR0 + n.IsNoColor()); + } if(o.capitals != n.capitals) out.Put(n.capitals == s.capitals ? CAPITALSS : CAPITALS0 + n.capitals); @@ -512,6 +516,15 @@ void RichPara::UnpackParts(Stream& in, const RichPara::CharFormat& chrstyle, case NONAAS: format.NonAntiAliased(chrstyle.IsNonAntiAliased()); break; + case NOCOLOR0: + format.NoColor(false); + break; + case NOCOLOR1: + format.NoColor(true); + break; + case NOCOLORS: + format.NoColor(chrstyle.IsNoColor()); + break; } } while((c = in.Term()) < 31 && c != 9 && c != FIELD && c >= 0); @@ -747,6 +760,8 @@ void ApplyCharStyle(RichPara::CharFormat& format, const RichPara::CharFormat& f0 format.capitals = newstyle.capitals; if(format.dashed == f0.dashed) format.dashed = newstyle.dashed; + if(format.IsNoColor() == f0.IsNoColor()) + format.NoColor(newstyle.IsNoColor()); if(format.sscript == f0.sscript) format.sscript = newstyle.sscript; if(format.GetFace() == f0.GetFace()) diff --git a/uppsrc/RichText/ParaPaint.cpp b/uppsrc/RichText/ParaPaint.cpp index ba239d244..b0eb16747 100644 --- a/uppsrc/RichText/ParaPaint.cpp +++ b/uppsrc/RichText/ParaPaint.cpp @@ -45,8 +45,6 @@ void RichPara::Flush(Draw& draw, const PaintInfo& pi, wchar *text, if(!IsNull(f.paper) && !highlight && IsNull(pi.textcolor)) draw.DrawRect(zx0, z * y, width, z * (y + linecy) - z * y, pi.ResolvePaper(f.paper)); Font fnt = f; - if(pi.mono_glyphs) - fnt.NoColor(); int zht = z * tabs(f.GetHeight()); int ssa = 0; int ssd = 0; diff --git a/uppsrc/RichText/ParseQtf.cpp b/uppsrc/RichText/ParseQtf.cpp index 963a13f2d..d8a0bec53 100644 --- a/uppsrc/RichText/ParseQtf.cpp +++ b/uppsrc/RichText/ParseQtf.cpp @@ -622,6 +622,7 @@ void RichQtfParser::Parse(const char *qtf, int _accesskey) case '*': format.Bold(!format.IsBold()); break; case '_': format.Underline(!format.IsUnderline()); break; case 'T': format.NonAntiAliased(!format.IsNonAntiAliased()); break; + case 'M': format.NoColor(!format.IsNoColor()); break; case '-': format.Strikeout(!format.IsStrikeout()); break; case 'c': format.capitals = !format.capitals; break; case 'd': format.dashed = !format.dashed; break; diff --git a/uppsrc/RichText/RichText.h b/uppsrc/RichText/RichText.h index 22e03a7a6..025757fc2 100644 --- a/uppsrc/RichText/RichText.h +++ b/uppsrc/RichText/RichText.h @@ -287,7 +287,6 @@ struct PaintInfo { bool indexentrybg; bool usecache; bool sizetracking; - bool mono_glyphs; Color showcodes; Bits (*spellingchecker)(const RichPara& para); int highlightpara; diff --git a/uppsrc/RichText/Txt.h b/uppsrc/RichText/Txt.h index d3850d438..4e33d9932 100644 --- a/uppsrc/RichText/Txt.h +++ b/uppsrc/RichText/Txt.h @@ -17,6 +17,7 @@ public: INDEXENTRY = 0x00002000, DASHED = 0x00004000, NOAA = 0x00008000, + NO_COLOR = 0x00010000, }; enum { diff --git a/uppsrc/RichText/srcdoc.tpp/QTF_en-us.tpp b/uppsrc/RichText/srcdoc.tpp/QTF_en-us.tpp index ba9e33697..971e157a1 100644 --- a/uppsrc/RichText/srcdoc.tpp/QTF_en-us.tpp +++ b/uppsrc/RichText/srcdoc.tpp/QTF_en-us.tpp @@ -211,6 +211,8 @@ matching&] ::= [s0; Dashed underline.] ::^ [s0;%- [C@(128.0.255) T]] ::= [s0; Non anti aliased font.] +::^ [s0;%- [C@(128.0.255) M]] +::= [s0; Prevents color emoji variants for glyph replacements.] ::^ [s0;%- [C@(128.0.255) `^][/C@(0.0.255) text][C@(128.0.255) `^]] ::= [s0; Hyperlink.] ::^ [s0;%- [C@(128.0.255) I][/C@(0.0.255) text][C@(128.0.255) ;]]