RichText: New 'no color emoji' flag M in QTF, Draw: fixed problems with NoColor fonts in X11/Gtk

This commit is contained in:
Mirek Fidler 2026-01-14 14:08:56 +01:00
parent fef58bbcb8
commit c1bdeb56c9
21 changed files with 49 additions and 19 deletions

View file

@ -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()) {

View file

@ -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)
}

View file

@ -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)
{

View file

@ -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<Window, Ctrl::XWindow>& Ctrl::Xwindow()
{
@ -48,8 +50,6 @@ Ptr<Ctrl> Ctrl::popupWnd;
Point Ctrl::mousePos;
static int s_starttime;
void Ctrl::DoPaint(const Vector<Rect>& invalid)
{
GuiLock __;

View file

@ -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); }

View file

@ -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);

View file

@ -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

View file

@ -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<CharEntry>();

View file

@ -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<int, sRFace *> rface[2]; // face index to font info
static Vector<int> color[2]; // colorimg faces

View file

@ -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();

View file

@ -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; &]

View file

@ -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<int, String> h : syms[i])
@ -92,7 +91,7 @@ void SelectSymbolDlg::Sync()
void SelectSymbolDlg::Variants(int codepoint)
{
String qtf = "[A5 ";
String qtf = "[MA5 ";
Index<Image> h;
svg.Clear();
for(int i = 0; i < Font::GetFaceCount(); i++) {

View file

@ -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 ? '`' : ',');

View file

@ -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)

View file

@ -46,6 +46,9 @@ struct RichPara {
NONAA0 = 1,
NONAA1 = 2,
NONAAS = 3,
NOCOLOR0 = 4,
NOCOLOR1 = 5,
NOCOLORS = 6,
};
enum BULLET_STYLE {

View file

@ -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())

View file

@ -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;

View file

@ -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;

View file

@ -287,7 +287,6 @@ struct PaintInfo {
bool indexentrybg;
bool usecache;
bool sizetracking;
bool mono_glyphs;
Color showcodes;
Bits (*spellingchecker)(const RichPara& para);
int highlightpara;

View file

@ -17,6 +17,7 @@ public:
INDEXENTRY = 0x00002000,
DASHED = 0x00004000,
NOAA = 0x00008000,
NO_COLOR = 0x00010000,
};
enum {

View file

@ -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) ;]]