diff --git a/include/lgi/common/DisplayString.h b/include/lgi/common/DisplayString.h --- a/include/lgi/common/DisplayString.h +++ b/include/lgi/common/DisplayString.h @@ -1,303 +1,307 @@ #ifndef _GDISPLAY_STRING_H_ #define _GDISPLAY_STRING_H_ #include "lgi/common/Font.h" #if defined(LGI_SDL) #elif defined(LINUX) namespace Pango { #include "pango/pango.h" #include "pango/pangocairo.h" } #endif #if !defined(_MSC_VER) || defined(__GTK_H__) #define LGI_DSP_STR_CACHE 1 #endif /// \brief Cache for text measuring, glyph substitution and painting /// /// To paint text onto the screen several stages need to be implemented to /// properly support unicode on multiple platforms. This class addresses all /// of those needs and then allows you to cache the results to reduce text /// related workload. /// /// The first stage is converting text into the native format for the /// OS's API. This usually involved converting the text to wide characters for /// Linux or Windows, or Utf-8 for Haiku. Then the text is converted into runs of /// characters that can be rendered in the same font. If glyph substitution is /// required to render the characters a separate run is used with a different /// font ID. Finally you can measure or paint these runs of text. Also tab characters /// are expanded to the current tab size setting. class LgiClass LDisplayString { protected: LSurface *pDC; LFont *Font; uint32_t x, y; int xf, yf; int DrawOffsetF; // Flags uint8_t LaidOut : 1; uint8_t AppendDots : 1; uint8_t VisibleTab : 1; /* String data: Str Wide Windows: utf-16 utf-16 MacCarbon: utf-16 utf-32 MacCocoa: utf-16 utf-32 Gtk3: utf-8 utf-32 + Haiku: utf-8 utf-32 */ OsChar *Str; ssize_t StrWords; #if LGI_DSP_STR_CACHE wchar_t *Wide; ssize_t WideWords; #endif #if defined(LGI_SDL) LAutoPtr Img; #elif defined(__GTK_H__) struct GDisplayStringPriv *d; friend struct GDisplayStringPriv; #elif defined(MAC) #if USE_CORETEXT CTLineRef Hnd; CFAttributedStringRef AttrStr; void CreateAttrStr(); #else ATSUTextLayout Hnd; ATSUTextMeasurement fAscent; ATSUTextMeasurement fDescent; #endif #elif defined(WINNATIVE) || defined(HAIKU) class CharInfo { public: OsChar *Str; int Len; int X; uint8_t FontId; int8 SizeDelta; CharInfo() { Str = 0; Len = 0; X = 0; FontId = 0; SizeDelta = 0; } uint32_t First() { auto s = (const uint16*)Str; ssize_t l = Len * sizeof(*Str); return LgiUtf16To32(s, l); } }; LArray Info; #endif void Layout(bool Debug = false); void DrawWhiteSpace(LSurface *pDC, char Ch, LRect &r); public: + // Turn on debug logging... + bool _debug = false; + /// Constructor LDisplayString ( /// The base font. Must not be destroyed during the lifetime of this object. LFont *f, /// Utf-8 input string const char *s, /// Number of bytes in the input string or -1 for NULL terminated. ssize_t l = -1, LSurface *pdc = 0 ); /// Constructor LDisplayString ( /// The base font. Must not be destroyed during the lifetime of this object. LFont *f, /// A wide character input string const char16 *s, /// The number of characters in the input string (NOT the number of bytes) or -1 for NULL terminated ssize_t l = -1, LSurface *pdc = 0 ); #ifdef _MSC_VER /// Constructor LDisplayString ( /// The base font. Must not be destroyed during the lifetime of this object. LFont *f, /// A wide character input string const uint32_t *s, /// The number of characters in the input string (NOT the number of bytes) or -1 for NULL terminated ssize_t l = -1, LSurface *pdc = 0 ); #endif virtual ~LDisplayString(); LDisplayString &operator=(const LDisplayString &s) { LAssert(0); return *this; } /// \returns the draw offset used to calculate tab spacing (in Px) int GetDrawOffset(); /// Sets the draw offset used to calculate tab spacing (in Px) void SetDrawOffset(int Px); /// Returns the ShowVisibleTab setting. /// Treats Unicode-2192 (left arrow) as a tab char bool ShowVisibleTab(); /// Sets the ShowVisibleTab setting. /// Treats Unicode-2192 (left arrow) as a tab char void ShowVisibleTab(bool i); /// \returns the font used to create the layout LFont *GetFont() { return Font; }; /// Fits string into 'width' pixels, truncating with '...' if it's not going to fit void TruncateWithDots ( /// The number of pixels the string has to fit int Width ); /// Returns true if the string is trucated bool IsTruncated(); /// Returns the words (not chars) in the OsChar string ssize_t Length(); /* /// Sets the number of words in the OsChar string void Length(int NewLen); */ /// Returns the pointer to the native string operator const char16*() { #if LGI_DSP_STR_CACHE return Wide; #else return Str; #endif } /// \returns the number of words in the wide string ssize_t WideLength() { #if LGI_DSP_STR_CACHE return WideWords; #else return StrWords; #endif } // API that use full pixel sizes: /// Returns the width of the whole string int X(); /// Returns the height of the whole string int Y(); /// Returns the width and height of the whole string LPoint Size(); /// Returns the number of characters that fit in 'x' pixels. ssize_t CharAt(int x, LPxToIndexType Type = LgiTruncate); /// Draws the string onto a device surface void Draw ( /// The output device LSurface *pDC, /// The x coordinate of the top left corner of the output box int x, /// The y coordinate of the top left corner of the output box int y, /// An optional clipping rectangle. If the font is not transparent this rectangle will be filled with the background colour. LRect *r = NULL, /// Turn on debug logging bool Debug = false ); /// Draws the string onto a device surface void DrawCenter ( /// The output device LSurface *pDC, /// An rectangle to center in. If the font is not transparent this rectangle will be filled with the background colour. LRect *r ) { if (r) Draw(pDC, r->x1 + ((r->X() - X()) >> 1), r->y1 + ((r->Y() - Y()) >> 1), r); } // API that uses fractional pixel sizes. enum { #if defined LGI_SDL FShift = 6, FScale = 1 << FShift, #elif defined __GTK_H__ /// This is the value for 1 pixel. FScale = PANGO_SCALE, /// This is bitwise shift to convert between integer and fractional FShift = 10 #elif defined MAC /// This is the value for 1 pixel. FScale = 0x10000, /// This is bitwise shift to convert between integer and fractional FShift = 16 #else /// This is the value for 1 pixel. FScale = 1, /// This is bitwise shift to convert between integer and fractional FShift = 0 #endif }; /// \returns the draw offset used to calculate tab spacing (in fractional units) int GetDrawOffsetF(); /// Sets the draw offset used to calculate tab spacing (in fractional units) void SetDrawOffsetF(int Fpx); /// \returns the fractional width of the string int FX(); /// \returns the fractional height of the string int FY(); /// \returns both the fractional width and height of the string LPoint FSize(); /// Draws the string using fractional co-ordinates. void FDraw ( /// The output device LSurface *pDC, /// The fractional x coordinate of the top left corner of the output box int fx, /// The fractional y coordinate of the top left corner of the output box int fy, /// [Optional] fractional clipping rectangle. If the font is not transparent /// this rectangle will be filled with the background colour. LRect *frc = NULL, /// [Optional] print debug info bool Debug = false ); }; #endif diff --git a/src/common/Gdc2/Font/DisplayString.cpp b/src/common/Gdc2/Font/DisplayString.cpp --- a/src/common/Gdc2/Font/DisplayString.cpp +++ b/src/common/Gdc2/Font/DisplayString.cpp @@ -1,2269 +1,2293 @@ ////////////////////////////////////////////////////////////////////// // Includes #include #include #include #include +#ifdef HAIKU +#include +#endif + #include "lgi/common/Lgi.h" #include "lgi/common/Variant.h" #include "lgi/common/FontSelect.h" #include "lgi/common/GdiLeak.h" #include "lgi/common/DisplayString.h" #include "lgi/common/PixelRops.h" #include "lgi/common/UnicodeString.h" #ifdef FontChange #undef FontChange #endif #ifdef LGI_SDL #include "ftsynth.h" #endif #if WINNATIVE static OsChar GDisplayStringDots[] = {'.', '.', '.', 0}; #endif //345678123456781234567812345678 // 2nd #define DEBUG_CHAR_AT 0 #define DEBUG_BOUNDS 0 #if defined(__GTK_H__) || (defined(MAC) && !defined(LGI_SDL)) #define DISPLAY_STRING_FRACTIONAL_NATIVE 1 #else #define DISPLAY_STRING_FRACTIONAL_NATIVE 0 #endif #if defined(__GTK_H__) struct Block : public LRect { /// This points to somewhere in Ds->Str OsChar *Str = NULL; /// Bytes in this block int Bytes = 0; /// Utf-8 characters in this block int Chars = 0; /// Alternative font to get characters from (NULL if using the display string's font) LFont *Fnt = NULL; /// Layout for this block. Shouldn't ever be NULL. But shouldn't crash otherwise. Gtk::PangoLayout *Hnd = NULL; ~Block() { if (Hnd) g_object_unref(Hnd); } }; struct GDisplayStringPriv { LDisplayString *Ds; LArray Blocks; bool Debug; int LastTabOffset; GDisplayStringPriv(LDisplayString *str) : Ds(str) { #if 0 Debug = Stristr(Ds->Str, "(Jumping).wma") != 0; #else Debug = false; #endif LastTabOffset = -1; } ~GDisplayStringPriv() { } void Create(Gtk::GtkPrintContext *PrintCtx) { auto *Fs = LFontSystem::Inst(); auto *Fnt = Ds->Font; auto Tbl = Fnt->GetGlyphMap(); int Chars = 0; LUtf8Ptr p(Ds->Str); auto *Start = p.GetPtr(); if (Tbl) { int32 w; Block *b = NULL; auto DisplayCtx = LFontSystem::Inst()->GetContext(); while ((w = (int32)p)) { LFont *f; if (w >= 0x80 && !_HasUnicodeGlyph(Tbl, w)) f = Fs->GetGlyph(w, Ds->Font); else f = Ds->Font; if (!b || (f != NULL && f != Fnt)) { // Finish old block if (b) { b->Bytes = p.GetPtr() - Start; b->Chars = Chars; Chars = 0; } Start = p.GetPtr(); if (f) { // Start new block... b = &Blocks.New(); b->Str = (char*)Start; b->Bytes = -1; // unknown at this point if (f != Ds->Font) // External font b->Fnt = f; // Create a pango layout if (PrintCtx) b->Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx); else b->Hnd = Gtk::pango_layout_new(DisplayCtx); } // else no font supports glyph Fnt = f; } // else no change in font p++; Chars++; } if (b) { // Finish the last block b->Bytes = p.GetPtr() - Start; b->Chars = Chars; } if (Debug) { // Print the block array for (size_t i=0; iStrWords) { p++; Chars++; } auto &b = Blocks.New(); b.Str = (char*)Start; b.Bytes = p.GetPtr() - Start; b.Chars = Chars; if (PrintCtx) b.Hnd = Gtk::gtk_print_context_create_pango_layout(PrintCtx); else b.Hnd = Gtk::pango_layout_new(LFontSystem::Inst()->GetContext()); } /* This could get stuck in an infinite loop. Leaving out for the moment. for (auto &b: Blocks) { if (b.Hnd == NULL && b.Fnt == NULL) { Blocks.Length(0); goto Start; } } */ } void UpdateTabs(int Offset, int Size, bool Debug = false) { if (Ds->Font && Ds->Font->TabSize()) { int Len = 16; LastTabOffset = Offset; Gtk::PangoTabArray *t = Gtk::pango_tab_array_new(Len, true); if (t) { for (int i=0; i bool StringConvert(Out *&out, ssize_t &OutWords, const In *in, ssize_t InLen) { if (!in) { out = 0; OutWords = 0; return false; } auto InSz = sizeof(In); auto OutSz = sizeof(Out); // Work out input size ssize_t InWords; if (InLen >= 0) InWords = InLen; else for (InWords = 0; in[InWords]; InWords++) ; if (InSz == OutSz) { // No conversion optimization out = (Out*)Strdup(in, InWords); OutWords = out ? InWords : 0; return out != 0; } else { // Convert the string to new word size static const char *Cp[] = { NULL, "utf-8", "utf-16", NULL, "utf-32"}; LAssert(OutSz <= 4 && InSz <= 4 && Cp[OutSz] && Cp[InSz]); out = (Out*) LNewConvertCp(Cp[OutSz], in, Cp[InSz], InWords*sizeof(In)); OutWords = Strlen(out); return out != NULL; } return false; } ////////////////////////////////////////////////////////////////////// #define SubtractPtr(a, b) ( ((a)-(b)) / sizeof(*a) ) #define VisibleTabChar 0x2192 #define IsTabChar(c) (c == '\t') // || (c == VisibleTabChar && VisibleTab)) #if USE_CORETEXT #include void LDisplayString::CreateAttrStr() { if (!Wide) return; if (AttrStr) { CFRelease(AttrStr); AttrStr = NULL; } wchar_t *w = Wide; CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)w, StrlenW(w) * sizeof(*w), kCFStringEncodingUTF32LE, false); if (string) { CFDictionaryRef attributes = Font->GetAttributes(); if (attributes) AttrStr = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); // else LAssert(0); CFRelease(string); } } #endif LDisplayString::LDisplayString(LFont *f, const char *s, ssize_t l, LSurface *pdc) { pDC = pdc; Font = f; #if LGI_DSP_STR_CACHE StringConvert(Wide, WideWords, s, l); #endif StringConvert(Str, StrWords, s, l); x = y = 0; xf = 0; yf = 0; DrawOffsetF = 0; LaidOut = 0; AppendDots = 0; VisibleTab = 0; #if defined __GTK_H__ d = new GDisplayStringPriv(this); if (Font && Str) { LAssert(StrWords >= 0); if (StrWords > 0) d->Create(pDC ? pDC->GetPrintContext() : NULL); } #elif defined MAC && !defined(LGI_SDL) Hnd = 0; #if USE_CORETEXT AttrStr = NULL; #endif if (Font && Str && StrWords > 0) { #if USE_CORETEXT CreateAttrStr(); #else ATSUCreateTextLayout(&Hnd); #endif } #endif } LDisplayString::LDisplayString(LFont *f, const char16 *s, ssize_t l, LSurface *pdc) { pDC = pdc; Font = f; #if LGI_DSP_STR_CACHE StringConvert(Wide, WideWords, s, l); #endif StringConvert(Str, StrWords, s, l); x = y = 0; xf = 0; yf = 0; DrawOffsetF = 0; LaidOut = 0; AppendDots = 0; VisibleTab = 0; #if defined __GTK_H__ d = new GDisplayStringPriv(this); if (Font && Str && StrWords > 0) d->Create(pDC ? pDC->GetPrintContext() : NULL); #elif defined MAC && !defined(LGI_SDL) Hnd = NULL; #if USE_CORETEXT AttrStr = NULL; #endif if (Font && Str && StrWords > 0) { #if USE_CORETEXT CreateAttrStr(); #else OSStatus e = ATSUCreateTextLayout(&Hnd); if (e) printf("%s:%i - ATSUCreateTextLayout failed with %i.\n", _FL, (int)e); #endif } #endif } #ifdef _MSC_VER LDisplayString::LDisplayString(LFont *f, const uint32_t *s, ssize_t l, LSurface *pdc) { pDC = pdc; Font = f; x = y = 0; xf = 0; yf = 0; DrawOffsetF = 0; LaidOut = 0; AppendDots = 0; VisibleTab = 0; #if LGI_DSP_STR_CACHE StringConvert(Wide, WideWords, s, l); #endif StringConvert(Str, StrWords, s, l); #if defined __GTK_H__ d = new GDisplayStringPriv(this); if (Font && Str && StrWords > 0) d->Create(pDC ? pDC->GetPrintContext() : NULL); #endif } #endif LDisplayString::~LDisplayString() { #if defined(LGI_SDL) Img.Reset(); #elif defined __GTK_H__ DeleteObj(d); #elif defined MAC #if USE_CORETEXT if (Hnd) { CFRelease(Hnd); Hnd = NULL; } if (AttrStr) { CFRelease(AttrStr); AttrStr = NULL; } #else if (Hnd) ATSUDisposeTextLayout(Hnd); #endif #endif DeleteArray(Str); #if LGI_DSP_STR_CACHE DeleteArray(Wide); #endif } void LDisplayString::DrawWhiteSpace(LSurface *pDC, char Ch, LRect &r) { if (Ch == '\t') { r.Inset(3, 3); if (r.Y()/2 == 0) r.y2++; int Cy = (r.Y() >> 1); pDC->Line(r.x1, r.y1+Cy, r.x2, r.y1+Cy); pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y1); pDC->Line(r.x2, r.y1+Cy, r.x2-Cy, r.y2); } else // Space { int x = r.x1 + (r.X()>>1) - 1; int Cy = r.y1 + (int)ceil(Font->Ascent()) - 2; pDC->Rectangle(x, Cy, x+1, Cy+1); } } void LDisplayString::Layout(bool Debug) { if (LaidOut || !Font) return; LaidOut = 1; #if defined(LGI_SDL) FT_Face Fnt = Font->Handle(); FT_Error error; if (!Fnt || !Str) return; // Create an array of glyph indexes LArray Glyphs; for (OsChar *s = Str; *s; s++) { FT_UInt index = FT_Get_Char_Index(Fnt, *s); if (index) Glyphs.Add(index); } // Measure the string... LPoint Sz; int FontHt = Font->GetHeight(); int AscentF = (int) (Font->Ascent() * FScale); int LoadMode = FT_LOAD_FORCE_AUTOHINT; for (unsigned i=0; iglyph->metrics.horiBearingY; Sz.x += Fnt->glyph->metrics.horiAdvance; Sz.y = MAX(Sz.y, PyF + Fnt->glyph->metrics.height); } } // Create the memory context to draw into xf = Sz.x; x = ((Sz.x + FScale - 1) >> FShift) + 1; yf = FontHt << FShift; y = FontHt; // ((Sz.y + FScale - 1) >> FShift) + 1; if (Img.Reset(new LMemDC(x, y, CsIndex8))) { // Clear the context to black Img->Colour(0); Img->Rectangle(); bool IsItalic = Font->Italic(); int CurX = 0; int FBaseline = Fnt->size->metrics.ascender; for (unsigned i=0; iglyph); error = FT_Render_Glyph(Fnt->glyph, FT_RENDER_MODE_NORMAL); if (error == 0) { FT_Bitmap &bmp = Fnt->glyph->bitmap; if (bmp.buffer) { // Copy rendered glyph into our image memory int Px = (CurX + (FScale >> 1)) >> FShift; int PyF = AscentF - Fnt->glyph->metrics.horiBearingY; int Py = PyF >> FShift; int Skip = 0; if (Fnt->glyph->format == FT_GLYPH_FORMAT_BITMAP) { Px += Fnt->glyph->bitmap_left; Skip = Fnt->glyph->bitmap_left < 0 ? -Fnt->glyph->bitmap_left : 0; Py = (AscentF >> FShift) - Fnt->glyph->bitmap_top; } LAssert(Px + bmp.width <= Img->X()); for (int y=0; y= 0); out += Px+Skip; memcpy(out, in+Skip, bmp.width-Skip); } /* else { LAssert(!"No scanline?"); break; } */ } } if (i < Glyphs.Length() - 1) { FT_Vector kerning; FT_Get_Kerning(Fnt, Glyphs[i], Glyphs[i+1], FT_KERNING_DEFAULT, &kerning); CurX += Fnt->glyph->metrics.horiAdvance + kerning.x; } else { CurX += Fnt->glyph->metrics.horiAdvance; } } } } } else LgiTrace("::Layout Create MemDC failed\n"); #elif defined(__GTK_H__) y = Font->GetHeight(); yf = y * PANGO_SCALE; if (!d->Blocks.Length() || !Font->Handle()) return; LUtf8Ptr Utf(Str); int32 Wide; while (*Utf.GetPtr()) { Wide = Utf; if (!Wide) { LgiTrace("%s:%i - Not utf8\n", _FL); return; } Utf++; } LFontSystem *FSys = LFontSystem::Inst(); Gtk::pango_context_set_font_description(FSys->GetContext(), Font->Handle()); int TabSizePx = Font->TabSize(); int TabSizeF = TabSizePx * FScale; int TabOffsetF = DrawOffsetF % TabSizeF; int OffsetF = TabOffsetF ? TabSizeF - TabOffsetF : 0; d->UpdateTabs(OffsetF / FScale, Font->TabSize()); if (Font->Underline()) { Gtk::PangoAttrList *attrs = Gtk::pango_attr_list_new(); Gtk::PangoAttribute *attr = Gtk::pango_attr_underline_new(Gtk::PANGO_UNDERLINE_SINGLE); Gtk::pango_attr_list_insert(attrs, attr); for (auto &b: d->Blocks) Gtk::pango_layout_set_attributes(b.Hnd, attrs); Gtk::pango_attr_list_unref(attrs); } int Fx = 0; for (auto &b: d->Blocks) { int bx = 0, by = 0; if (b.Hnd) { if (!LIsUtf8(b.Str, b.Bytes)) { LgiTrace("Invalid UTF8: '%.*S'\n", (int)b.Bytes, b.Str); } else { Gtk::pango_layout_set_text(b.Hnd, b.Str, b.Bytes); } Gtk::pango_layout_get_size(b.Hnd, &bx, &by); } else if (b.Fnt) { b.Fnt->_Measure(bx, by, b.Str, b.Bytes); bx <<= FShift; by <<= FShift; } b.ZOff(bx-1, by-1); b.Offset(Fx, 0); xf += bx; yf = MAX(yf, by); } x = (xf + PANGO_SCALE - 1) / PANGO_SCALE; #if 1 y = Font->GetHeight(); #else y = (yf + PANGO_SCALE - 1) / PANGO_SCALE; #endif if (y > Font->GetHeight()) { printf("%s:%i - Height error: %i > %i\n", _FL, y, Font->GetHeight()); } #elif defined MAC && !defined(LGI_SDL) #if USE_CORETEXT int height = Font->GetHeight(); y = height; if (AttrStr) { LAssert(!Hnd); Hnd = CTLineCreateWithAttributedString(AttrStr); if (Hnd) { CGFloat ascent = 0.0; CGFloat descent = 0.0; CGFloat leading = 0.0; double width = CTLineGetTypographicBounds(Hnd, &ascent, &descent, &leading); x = ceil(width); xf = width * FScale; yf = height * FScale; } } #else if (!Hnd || !Str) return; OSStatus e = ATSUSetTextPointerLocation(Hnd, Str, 0, len, len); if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUSetTextPointerLocation failed with errorcode %i (%s)\n", _FL, (int)e, a); DeleteArray(a); return; } e = ATSUSetRunStyle(Hnd, Font->Handle(), 0, len); if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUSetRunStyle failed with errorcode %i (%s)\n", _FL, (int)e, a); DeleteArray(a); return; } ATSUTextMeasurement fTextBefore; ATSUTextMeasurement fTextAfter; if (pDC) { OsPainter dc = pDC->Handle(); ATSUAttributeTag Tags[1] = {kATSUCGContextTag}; ByteCount Sizes[1] = {sizeof(CGContextRef)}; ATSUAttributeValuePtr Values[1] = {&dc}; e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values); if (e) printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e); } ATSUTab Tabs[32]; for (int i=0; iTabSize()) << 16; Tabs[i].tabType = kATSULeftTab; } e = ATSUSetTabArray(Hnd, Tabs, CountOf(Tabs)); if (e) printf("%s:%i - ATSUSetTabArray failed (e=%i)\n", _FL, (int)e); e = ATSUGetUnjustifiedBounds( Hnd, kATSUFromTextBeginning, kATSUToTextEnd, &fTextBefore, &fTextAfter, &fAscent, &fDescent); if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUGetUnjustifiedBounds failed with errorcode %i (%s)\n", _FL, (int)e, a); DeleteArray(a); return; } xf = fTextAfter - fTextBefore; yf = fAscent + fDescent; x = (xf + 0xffff) >> 16; y = (yf + 0xffff) >> 16; ATSUSetTransientFontMatching(Hnd, true); #endif #elif defined(HAIKU) if (!Font) { LgiTrace("%s:%i - Missing pointer: %p\n", _FL, Font); return; } BFont *fnt = Font->Handle(); if (!fnt) { LgiTrace("%s:%i - Missing handle. %p/%p\n", _FL, fnt); return; } int tabSize = Font->TabSize() ? Font->TabSize() : 32; font_height height = {0}; fnt->GetHeight(&height); yf = y = height.ascent + height.descent + height.leading; if (!Str) return; LUtf8Ptr start(Str); int isTab = -1; for (LUtf8Ptr p(Str); true; p++) { int32_t ch = p; if (isTab < 0) { isTab = IsTabChar(ch); } else if (!ch || IsTabChar(ch) ^ isTab) { auto &l = Info.New(); l.Str = start.GetPtr(); l.Len = p.GetPtr() - start.GetPtr(); const char *strArr[] = { l.Str }; const int32 strLen[] = { l.Len }; float width[1] = { 0 }; fnt->GetStringWidths(strArr, strLen, 1, width); if (isTab) { // Handle tab(s) for (int t=0; tHandle()) Font->Create(); y = Font->GetHeight(); LFontSystem *Sys = LFontSystem::Inst(); if (Sys && Str) { LFont *f = Font; bool GlyphSub = Font->SubGlyphs(); Info[i].Str = Str; int TabSize = Font->TabSize() ? Font->TabSize() : 32; bool WasTab = IsTabChar(*Str); f = GlyphSub ? Sys->GetGlyph(*Str, Font) : Font; if (f && f != Font) { f->Size(Font->Size()); f->SetWeight(Font->GetWeight()); if (!f->Handle()) f->Create(); } bool Debug = WasTab; uint32_t u32; for (LUnicodeString u(Str, StrWords); true; u++) { u32 = *u; LFont *n = GlyphSub ? Sys->GetGlyph(u32, Font) : Font; bool Change = n != f || // The font changed (IsTabChar(u32) ^ WasTab) || // Entering/leaving a run of tabs !u32 || // Hit a NULL character (u.Get() - Info[i].Str) >= 1000; // This is to stop very long segments not rendering if (Change) { // End last segment if (n && n != Font) { n->Size(Font->Size()); n->SetWeight(Font->GetWeight()); if (!n->Handle()) n->Create(); } Info[i].Len = (int) (u.Get() - Info[i].Str); if (Info[i].Len) { if (WasTab) { // Handle tab(s) for (int t=0; tGetHeight() > Font->GetHeight())) { Info[i].SizeDelta = -1; f->PointSize(Font->PointSize() + Info[i].SizeDelta); f->Create(); } #endif if (!f) { // no font, so ? out the chars... as they aren't available anyway // printf("Font Cache Miss, Len=%i\n\t", Info[i].Len); m = Font; for (int n=0; n_Measure(sx, sy, Info[i].Str, Info[i].Len); x += Info[i].X = sx > 0xffff ? 0xffff : sx; } auto Ch = Info[i].First(); Info[i].FontId = !f || Font == f ? 0 : Sys->Lut[Ch]; i++; } f = n; // Start next segment WasTab = IsTabChar(u32); Info[i].Str = u.Get(); } if (!u32) break; } if (Info.Length() > 0 && Info.Last().Len == 0) { Info.Length(Info.Length()-1); } } xf = x; yf = y; #endif } int LDisplayString::GetDrawOffset() { return DrawOffsetF >> FShift; } void LDisplayString::SetDrawOffset(int Px) { if (LaidOut) LAssert(!"No point setting TabOrigin after string is laid out.\n"); DrawOffsetF = Px << FShift; } bool LDisplayString::ShowVisibleTab() { return VisibleTab; } void LDisplayString::ShowVisibleTab(bool i) { VisibleTab = i; } bool LDisplayString::IsTruncated() { return AppendDots; } void LDisplayString::TruncateWithDots(int Width) { Layout(); #if defined __GTK_H__ int Fx = 0; int Fwid = Width << FShift; for (auto &b: d->Blocks) { if (Fwid < Fx + b.X()) { if (b.Hnd) { Gtk::pango_layout_set_ellipsize(b.Hnd, Gtk::PANGO_ELLIPSIZE_END); Gtk::pango_layout_set_width(b.Hnd, Fwid - Fx); Gtk::pango_layout_set_single_paragraph_mode(b.Hnd, true); } else if (b.Fnt) { } break; } Fx += b.X(); } #elif WINNATIVE if (Width < X() + 8) { ssize_t c = CharAt(Width); if (c >= 0 && c < StrWords) { if (c > 0) c--; // fudge room for dots if (c > 0) c--; AppendDots = 1; if (Info.Length()) { int Width = 0; int Pos = 0; for (int i=0; i= Pos && c < Pos + Info[i].Len) { Info[i].Len = (int) (c - Pos); Info[i].Str[Info[i].Len] = 0; LFont *f = Font; if (Info[i].FontId) { LFontSystem *Sys = LFontSystem::Inst(); f = Sys->Font[Info[i].FontId]; f->PointSize(Font->PointSize() + Info[i].SizeDelta); if (!f->Handle()) { f->Create(); } } else { f = Font; } if (f) { int Sx, Sy; f->_Measure(Sx, Sy, Info[i].Str, Info[i].Len); Info[i].X = Sx; Width += Info[i].X; } Info.Length(i + 1); break; } Pos += Info[i].Len; Width += Info[i].X; } int DotsX, DotsY; Font->_Measure(DotsX, DotsY, GDisplayStringDots, 3); x = Width + DotsX; } } } #elif defined(LGI_SDL) #elif defined(MAC) #if USE_CORETEXT if (Hnd) { /* CFAttributedStringRef truncationString = CFAttributedStringCreate(NULL, CFSTR("\u2026"), Font->GetAttributes()); if (truncationString) { CTLineRef truncationToken = CTLineCreateWithAttributedString(truncationString); CFRelease(truncationString); if (truncationToken) { CTLineRef TruncatedLine = CTLineCreateTruncatedLine(Hnd, Width, kCTLineTruncationEnd, truncationToken); CFRelease(truncationToken); if (TruncatedLine) { CFRelease(Hnd); Hnd = TruncatedLine; } } } */ } #endif #endif } ssize_t LDisplayString::CharAt(int Px, LPxToIndexType Type) { Layout(); if (Px < 0) { return 0; } else if (Px >= (int)x) { #if defined __GTK_H__ if (Str) { LUtf8Str u(Str); return u.GetChars(); } return 0; #else #if LGI_DSP_STR_CACHE return WideWords; #else return StrWords; #endif #endif } int Status = -1; #if defined(__GTK_H__) int Fx = 0; int Fpos = Px << FShift; Status = 0; for (auto &b: d->Blocks) { int Index = 0, Trailing = 0; int Foffset = Fpos - Fx; if (b.Hnd && Gtk::pango_layout_xy_to_index(b.Hnd, Foffset, 0, &Index, &Trailing)) { if (d->Debug) printf("CharAt(%g) x=%g Status=%i Foffset=%g index=%i trailing=%i\n", (double)Fpos/FScale, (double)b.X()/FScale, Status, (double)Foffset/FScale, Index, Trailing); LUtf8Str u(Str); while ((OsChar*)u.GetPtr() < Str + Index + Trailing) { u++; Status++; } return Status; } else { if (d->Debug) printf("CharAt(%g) x=%g Status=%i Chars=%i\n", (double)Fpos/FScale, (double)b.X()/FScale, Status, b.Chars); Status += b.Chars; } Fx += b.X(); } #elif defined(LGI_SDL) LAssert(!"Impl me"); #elif defined(MAC) if (Hnd && Str) { #if USE_CORETEXT CGPoint pos = { (CGFloat)Px, 1.0f }; CFIndex utf16 = CTLineGetStringIndexForPosition(Hnd, pos); // 'utf16' is in UTF-16, and the API needs to return a UTF-32 index... // So convert between the 2 here... LAssert(Str != NULL); int utf32 = 0; for (int i=0; Str[i] && iTabSize() ? Font->TabSize() : 32; int Cx = 0; int Char = 0; #if DEBUG_CHAR_AT printf("CharAt(%i) Str='%s'\n", Px, Str); #endif for (int i=0; i= Cx && Px < Cx + Info[i].X) { // The position is in this block of characters if (IsTabChar(Info[i].Str[0])) { // Search through tab block for (int t=0; t= Cx && Px < Cx + TabX) { Status = Char; #if DEBUG_CHAR_AT printf("\tIn tab block %i\n", i); #endif break; } Cx += TabX; Char++; } } else { // Find the pos in this block LFont *f = Font; #if defined(WIN32) if (Info[i].FontId) { f = Sys->Font[Info[i].FontId]; f->Colour(Font->Fore(), Font->Back()); f->Size(Font->Size()); if (!f->Handle()) { f->Create(); } } #endif int Fit = f->_CharAt(Px - Cx, Info[i].Str, Info[i].Len, Type); #if DEBUG_CHAR_AT printf("\tNon tab block %i, Fit=%i, Px-Cx=%i-%i=%i, Str='%.5s'\n", i, Fit, Px, Cx, Px-Cx, Info[i].Str); #endif if (Fit >= 0) Status = Char + Fit; else Status = -1; break; } } else { // Not in this block, skip the whole lot Cx += Info[i].X; Char += Info[i].Len; } } if (Status < 0) { Status = Char; } } #endif return Status; } ssize_t LDisplayString::Length() { return StrWords; } int LDisplayString::X() { Layout(); return x; } int LDisplayString::Y() { Layout(); return y; } LPoint LDisplayString::Size() { Layout(); return LPoint(x, y); } #if defined LGI_SDL template bool CompositeText8Alpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip) { OutPx map[256]; if (!Out || !In || !Font) return false; // FIXME, do blt clipping here... // Create colour map of the foreground/background colours uint8_t *Div255 = Div255Lut; LColour fore = Font->Fore(); LRgb24 fore_px; fore_px.r = fore.r(); fore_px.g = fore.g(); fore_px.b = fore.b(); if (Font->Transparent()) { for (int a=0; a<256; a++) { map[a].r = Div255[a * fore_px.r]; map[a].g = Div255[a * fore_px.g]; map[a].b = Div255[a * fore_px.b]; map[a].a = a; } } else { LColour back = Font->Back(); LRgb24 back_px; back_px.r = back.r(); back_px.g = back.g(); back_px.b = back.b(); for (int a=0; a<256; a++) { int oma = 255 - a; map[a].r = (oma * back_px.r) + (a * fore_px.r) / 255; map[a].g = (oma * back_px.g) + (a * fore_px.g) / 255; map[a].b = (oma * back_px.b) + (a * fore_px.b) / 255; map[a].a = 255; } } uint8_t *StartOfBuffer = (*Out)[0]; uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y()); for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++) { OutPx *d = ((OutPx*) (*Out)[py + y]) + Clip.DstClip.x1; uint8_t *i = (*In)[y]; if (!i) return false; uint8_t *e = i + Clip.DstClip.X(); LAssert((uint8_t*)d >= StartOfBuffer); if (Font->Transparent()) { uint8_t a, o; OutPx *s; while (i < e) { // Alpha blend map and output pixel a = *i++; switch (a) { case 0: break; case 255: // Copy *d = map[a]; break; default: // Blend o = 255 - a; s = map + a; #define NonPreMulOver32NoAlpha(c) d->c = ((s->c * a) + (Div255[d->c * 255] * o)) / 255 NonPreMulOver32NoAlpha(r); NonPreMulOver32NoAlpha(g); NonPreMulOver32NoAlpha(b); break; } d++; } } else { while (i < e) { // Copy rop *d++ = map[*i++]; } } LAssert((uint8_t*)d <= EndOfBuffer); } return true; } template bool CompositeText8NoAlpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip) { LRgba32 map[256]; if (!Out || !In || !Font) return false; // FIXME, do blt clipping here... // Create colour map of the foreground/background colours uint8_t *DivLut = Div255Lut; LColour fore = Font->Fore(); LRgb24 fore_px; fore_px.r = fore.r(); fore_px.g = fore.g(); fore_px.b = fore.b(); if (Font->Transparent()) { for (int a=0; a<256; a++) { map[a].r = DivLut[a * fore_px.r]; map[a].g = DivLut[a * fore_px.g]; map[a].b = DivLut[a * fore_px.b]; map[a].a = a; } } else { LColour back = Font->Back(); LRgb24 back_px; back_px.r = back.r(); back_px.g = back.g(); back_px.b = back.b(); for (int a=0; a<256; a++) { int oma = 255 - a; map[a].r = DivLut[(oma * back_px.r) + (a * fore_px.r)]; map[a].g = DivLut[(oma * back_px.g) + (a * fore_px.g)]; map[a].b = DivLut[(oma * back_px.b) + (a * fore_px.b)]; map[a].a = 255; } } uint8_t *StartOfBuffer = (*Out)[0]; uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y()); for (int y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++) { OutPx *dst = (OutPx*) (*Out)[py + y]; if (!dst) continue; dst += Clip.DstClip.x1; if ((uint8_t*)dst < StartOfBuffer) continue; uint8_t *i = (*In)[y]; if (!i) return false; uint8_t *e = i + Clip.DstClip.X(); LRgba32 *src; LAssert((uint8_t*)dst >= StartOfBuffer); if (Font->Transparent()) { uint8_t a, oma; while (i < e) { // Alpha blend map and output pixel a = *i++; src = map + a; switch (a) { case 0: break; case 255: // Copy dst->r = src->r; dst->g = src->g; dst->b = src->b; break; default: // Blend OverPm32toPm24(src, dst); break; } dst++; } } else { while (i < e) { // Copy rop src = map + *i++; dst->r = src->r; dst->g = src->g; dst->b = src->b; dst++; } } LAssert((uint8_t*)dst <= EndOfBuffer); } return true; } template bool CompositeText5NoAlpha(LSurface *Out, LSurface *In, LFont *Font, int px, int py, LBlitRegions &Clip) { OutPx map[256]; if (!Out || !In || !Font) return false; // FIXME, do blt clipping here... #define MASK_5BIT 0x1f #define MASK_6BIT 0x3f // Create colour map of the foreground/background colours uint8_t *Div255 = Div255Lut; LColour fore = Font->Fore(); LRgb24 fore_px; fore_px.r = fore.r(); fore_px.g = fore.g(); fore_px.b = fore.b(); if (Font->Transparent()) { for (int a=0; a<256; a++) { map[a].r = (int)Div255[a * fore_px.r] >> 3; map[a].g = (int)Div255[a * fore_px.g] >> 2; map[a].b = (int)Div255[a * fore_px.b] >> 3; } } else { LColour back = Font->Back(); LRgb24 back_px; back_px.r = back.r(); back_px.g = back.g(); back_px.b = back.b(); for (int a=0; a<256; a++) { int oma = 255 - a; map[a].r = Div255[(oma * back_px.r) + (a * fore_px.r)] >> 3; map[a].g = Div255[(oma * back_px.g) + (a * fore_px.g)] >> 2; map[a].b = Div255[(oma * back_px.b) + (a * fore_px.b)] >> 3; } } uint8_t *StartOfBuffer = (*Out)[0]; uint8_t *EndOfBuffer = StartOfBuffer + (Out->GetRowStep() * Out->Y()); for (unsigned y=Clip.SrcClip.y1; y<=Clip.SrcClip.y2; y++) { OutPx *dst = ((OutPx*) (*Out)[py + y]); if (!dst) continue; dst += Clip.DstClip.x1; uint8_t *i = (*In)[y]; if (!i) return false; uint8_t *e = i + Clip.DstClip.X(); LAssert((uint8_t*)dst >= StartOfBuffer); if (Font->Transparent()) { uint8_t a; OutPx *src; while (i < e) { // Alpha blend map and output pixel a = *i++; switch (a) { case 0: break; case 255: // Copy *dst = map[a]; break; default: { // Blend #if 0 uint8_t oma = 255 - a; src = map + a; LRgb24 d = { G5bitTo8bit(dst->r), G6bitTo8bit(dst->g), G5bitTo8bit(dst->b)}; LRgb24 s = { G5bitTo8bit(src->r), G6bitTo8bit(src->g), G5bitTo8bit(src->b)}; dst->r = Div255[(oma * d.r) + (a * s.r)] >> 3; dst->g = Div255[(oma * d.g) + (a * s.g)] >> 2; dst->b = Div255[(oma * d.b) + (a * s.b)] >> 3; #else uint8_t a5 = a >> 3; uint8_t a6 = a >> 2; uint8_t oma5 = MASK_5BIT - a5; uint8_t oma6 = MASK_6BIT - a6; src = map + a; dst->r = ((oma5 * (uint8_t)dst->r) + (a5 * (uint8_t)src->r)) / MASK_5BIT; dst->g = ((oma6 * (uint8_t)dst->g) + (a6 * (uint8_t)src->g)) / MASK_6BIT; dst->b = ((oma5 * (uint8_t)dst->b) + (a5 * (uint8_t)src->b)) / MASK_5BIT; #endif break; } } dst++; } } else { while (i < e) { // Copy rop *dst++ = map[*i++]; } } LAssert((uint8_t*)dst <= EndOfBuffer); } return true; } #endif void LDisplayString::Draw(LSurface *pDC, int px, int py, LRect *r, bool Debug) { Layout(); #if DISPLAY_STRING_FRACTIONAL_NATIVE // GTK / Mac use fractional pixels, so call the fractional version: LRect rc; if (r) { rc = *r; rc.x1 <<= FShift; rc.y1 <<= FShift; #if defined(MAC) && !defined(__GTK_H__) rc.x2 <<= FShift; rc.y2 <<= FShift; #else rc.x2 = (rc.x2 + 1) << FShift; rc.y2 = (rc.y2 + 1) << FShift; #endif } FDraw(pDC, px << FShift, py << FShift, r ? &rc : NULL, Debug); #elif defined(HAIKU) if (!Font || !pDC) { LgiTrace("%s:%i - No ptr: %p/%p.\n", _FL, Font, pDC); return; } BFont *fnt = Font->Handle(); BView *view = pDC->Handle(); if (!fnt || !view) { LgiTrace("%s:%i - No handle: %p/%p(%s).\n", _FL, fnt, view, pDC->GetClass()); return; } font_height height = {0}; fnt->GetHeight(&height); + /* + if (_debug) + printf(" trans=%i height=%g,%g,%g\n", + Font->Transparent(), + height.ascent, height.descent, height.leading); + */ + if (!Font->Transparent()) { view->SetHighColor(Font->Back()); if (r) view->FillRect(*r); else view->FillRect(BRect(px, py, px+x, py+y)); } + auto locked = view->LockLooper(); view->SetFont(fnt); view->SetHighColor(Font->Fore()); int cx = px; for (auto &i: Info) { + /* + if (_debug) + printf(" info=%.*s c=%i,%i\n", i.Len, i.Str, cx, py); + */ + view->DrawString(i.Str, i.Len, BPoint(cx, py + height.ascent)); + cx += i.X; } + + BRegion region; + view->GetClippingRegion(®ion); + + if (locked) + view->UnlockLooper(); #elif defined(LGI_SDL) if (Img && pDC && pDC->Y() > 0 && (*pDC)[0]) { int Ox = 0, Oy = 0; pDC->GetOrigin(Ox, Oy); LBlitRegions Clip(pDC, px-Ox, py-Oy, Img, r); LColourSpace DstCs = pDC->GetColourSpace(); switch (DstCs) { #define DspStrCase(px_fmt, comp) \ case Cs##px_fmt: \ CompositeText##comp(pDC, Img, Font, px-Ox, py-Oy, Clip); \ break; DspStrCase(Rgb16, 5NoAlpha) DspStrCase(Bgr16, 5NoAlpha) DspStrCase(Rgb24, 8NoAlpha) DspStrCase(Bgr24, 8NoAlpha) DspStrCase(Rgbx32, 8NoAlpha) DspStrCase(Bgrx32, 8NoAlpha) DspStrCase(Xrgb32, 8NoAlpha) DspStrCase(Xbgr32, 8NoAlpha) DspStrCase(Rgba32, 8Alpha) DspStrCase(Bgra32, 8Alpha) DspStrCase(Argb32, 8Alpha) DspStrCase(Abgr32, 8Alpha) default: LgiTrace("%s:%i - LDisplayString::Draw Unsupported colour space.\n", _FL); // LAssert(!"Unsupported colour space."); break; #undef DspStrCase } } else LgiTrace("::Draw argument error.\n"); #elif defined WINNATIVE if (Info.Length() && pDC && Font) { LFontSystem *Sys = LFontSystem::Inst(); COLOUR Old = pDC->Colour(); int TabSize = Font->TabSize() ? Font->TabSize() : 32; int Ox = px; LColour cFore = Font->Fore(); LColour cBack = Font->Back(); LColour cWhitespace; if (VisibleTab) { cWhitespace = Font->WhitespaceColour(); LAssert(cWhitespace.IsValid()); } for (int i=0; iFont[Info[i].FontId]; f->Colour(cFore, cBack); auto Sz = Font->Size(); Sz.Value += Info[i].SizeDelta; f->Size(Sz); f->Transparent(Font->Transparent()); f->Underline(Font->Underline()); if (!f->Handle()) { f->Create(); } } else { f = Font; } if (f) { LRect b; if (r) { b.x1 = i ? px : r->x1; b.y1 = r->y1; b.x2 = i < Info.Length() - 1 ? px + Info[i].X - 1 : r->x2; b.y2 = r->y2; } else { b.x1 = px; b.y1 = py; b.x2 = px + Info[i].X - 1; b.y2 = py + Y() - 1; } if (b.Valid()) { if (IsTabChar(*Info[i].Str)) { // Invisible tab... draw blank space if (!Font->Transparent()) { pDC->Colour(cBack); pDC->Rectangle(&b); } if (VisibleTab) { int X = px; for (int n=0; nColour(cWhitespace); DrawWhiteSpace(pDC, '\t', r); X += Dx; } } } else { // Draw the character(s) LColour Fg = f->Fore(); LAssert(Fg.IsValid()); f->_Draw(pDC, px, py, Info[i].Str, Info[i].Len, &b, Fg); if (VisibleTab) { OsChar *start = Info[i].Str; OsChar *s = start; OsChar *e = s + Info[i].Len; int Sp = -1; while (s < e) { if (*s == ' ') { int Sx, Sy; if (Sp < 0) f->_Measure(Sp, Sy, s, 1); f->_Measure(Sx, Sy, start, (int)(s - start)); LRect r(0, 0, Sp-1, Sy-1); r.Offset(px + Sx, py); pDC->Colour(cWhitespace); DrawWhiteSpace(pDC, ' ', r); } s++; } } } } } // Inc my position px += Info[i].X; } if (AppendDots) { int Sx, Sy; Font->_Measure(Sx, Sy, GDisplayStringDots, 3); LRect b; if (r) { b.x1 = px; b.y1 = r->y1; b.x2 = min(px + Sx - 1, r->x2); b.y2 = r->y2; } else { b.x1 = px; b.y1 = py; b.x2 = px + Sx - 1; b.y2 = py + Y() - 1; } LColour Fg = Font->Fore(); Font->_Draw(pDC, px, py, GDisplayStringDots, 3, &b, Fg); } pDC->Colour(Old); } else if (r && Font && !Font->Transparent()) { pDC->Colour(Font->Back()); pDC->Rectangle(r); } #endif } int LDisplayString::GetDrawOffsetF() { return DrawOffsetF; } void LDisplayString::SetDrawOffsetF(int Fpx) { if (LaidOut) LAssert(!"No point setting TabOrigin after string is laid out.\n"); DrawOffsetF = Fpx; } int LDisplayString::FX() { Layout(); return xf; } int LDisplayString::FY() { Layout(); return yf; } LPoint LDisplayString::FSize() { Layout(); return LPoint(xf, yf); } void LDisplayString::FDraw(LSurface *pDC, int fx, int fy, LRect *frc, bool Debug) { Layout(Debug); #if !DISPLAY_STRING_FRACTIONAL_NATIVE // Windows doesn't use fractional pixels, so call the integer version: LRect rc; if (frc) { rc = *frc; rc.x1 >>= FShift; rc.y1 >>= FShift; rc.x2 >>= FShift; rc.y2 >>= FShift; } Draw(pDC, fx >> FShift, fy >> FShift, frc ? &rc : NULL, Debug); #elif defined __GTK_H__ Gtk::cairo_t *cr = pDC->Handle(); if (!cr) { LAssert(!"Can't get cairo."); return; } pango_context_set_font_description(LFontSystem::Inst()->GetContext(), Font->Handle()); cairo_save(cr); LColour b = Font->Back(); double Dx = ((double)fx / FScale); double Dy = ((double)fy / FScale); double rx, ry, rw, rh; if (!Font->Transparent()) { // Background fill cairo_set_source_rgb(cr, (double)b.r()/255.0, (double)b.g()/255.0, (double)b.b()/255.0); cairo_new_path(cr); if (frc) { rx = ((double)frc->x1 / FScale); ry = ((double)frc->y1 / FScale); rw = (double)frc->X() / FScale; rh = (double)frc->Y() / FScale; } else { rx = Dx; ry = Dy; rw = x; rh = y; } cairo_rectangle(cr, rx, ry, rw, rh); cairo_fill(cr); if (frc) { cairo_rectangle(cr, rx, ry, rw, rh); cairo_clip(cr); } } else if (frc) { rx = ((double)frc->x1 / FScale); ry = ((double)frc->y1 / FScale); rw = (double)frc->X() / FScale; rh = (double)frc->Y() / FScale; cairo_rectangle(cr, rx, ry, rw, rh); cairo_clip(cr); } cairo_translate(cr, Dx, Dy); LColour f = Font->Fore(); for (auto &b: d->Blocks) { double Bx = ((double)b.X()) / FScale; #if DEBUG_BOUNDS double By = Font->GetHeight(); cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); cairo_rectangle(cr, 0, 0, Bx, By); cairo_rectangle(cr, 1, 1, Bx - 2.0, By - 2.0); cairo_set_fill_rule(cr, Gtk::CAIRO_FILL_RULE_EVEN_ODD); cairo_fill(cr); #endif if (b.Hnd) { cairo_set_source_rgb( cr, (double)f.r()/255.0, (double)f.g()/255.0, (double)f.b()/255.0); pango_cairo_show_layout(cr, b.Hnd); if (VisibleTab && Str) { LUtf8Str Ptr(Str); auto Ws = Font->WhitespaceColour(); pDC->Colour(Ws); for (int32 u, Idx = 0 ; (u = Ptr); Idx++) { if (IsTabChar(u) || u == ' ') { Gtk::PangoRectangle pos; Gtk::pango_layout_index_to_pos(b.Hnd, Idx, &pos); LRect r(0, 0, pos.width / FScale, pos.height / FScale); r.Offset(pos.x / FScale, pos.y / FScale); DrawWhiteSpace(pDC, u, r); } Ptr++; } } } else if (b.Fnt) { b.Fnt->Transparent(Font->Transparent()); b.Fnt->Back(Font->Back()); b.Fnt->_Draw(pDC, 0, 0, b.Str, b.Bytes, NULL, f); } else LAssert(0); cairo_translate(cr, Bx, 0); } cairo_restore(cr); #elif defined MAC && !defined(LGI_SDL) int Ox = 0, Oy = 0; int px = fx >> FShift; int py = fy >> FShift; LRect rc; if (frc) rc.Set( frc->x1 >> FShift, frc->y1 >> FShift, frc->x2 >> FShift, frc->y2 >> FShift); if (pDC && !pDC->IsScreen()) pDC->GetOrigin(Ox, Oy); if (pDC && !Font->Transparent()) { LColour Old = pDC->Colour(Font->Back()); if (frc) { pDC->Rectangle(&rc); } else { LRect a(px, py, px + x - 1, py + y - 1); pDC->Rectangle(&a); } pDC->Colour(Old); } if (Hnd && pDC && StrWords > 0) { OsPainter dc = pDC->Handle(); #if USE_CORETEXT int y = (pDC->Y() - py + Oy); CGContextSaveGState(dc); pDC->Colour(Font->Fore()); if (pDC->IsScreen()) { if (frc) { CGRect rect = rc; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } CGContextTranslateCTM(dc, 0, pDC->Y()-1); CGContextScaleCTM(dc, 1.0, -1.0); } else { if (frc) { CGContextSaveGState(dc); CGRect rect = rc; rect.origin.x -= Ox; rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } } CGFloat Tx = (CGFloat)fx / FScale - Ox; CGFloat Ty = (CGFloat)y - Font->Ascent(); CGContextSetTextPosition(dc, Tx, Ty); CTLineDraw(Hnd, dc); CGContextRestoreGState(dc); #else ATSUAttributeTag Tags[1] = {kATSUCGContextTag}; ByteCount Sizes[1] = {sizeof(CGContextRef)}; ATSUAttributeValuePtr Values[1] = {&dc}; e = ATSUSetLayoutControls(Hnd, 1, Tags, Sizes, Values); if (e) { printf("%s:%i - ATSUSetLayoutControls failed (e=%i)\n", _FL, (int)e); } else { // Set style attr ATSURGBAlphaColor c; LColour Fore = Font->Fore(); c.red = (double) Fore.r() / 255.0; c.green = (double) Fore.g() / 255.0; c.blue = (double) Fore.b() / 255.0; c.alpha = 1.0; ATSUAttributeTag Tags[] = {kATSURGBAlphaColorTag}; ATSUAttributeValuePtr Values[] = {&c}; ByteCount Lengths[] = {sizeof(c)}; e = ATSUSetAttributes( Font->Handle(), CountOf(Tags), Tags, Lengths, Values); if (e) { printf("%s:%i - Error setting font attr (e=%i)\n", _FL, (int)e); } else { int y = (pDC->Y() - py + Oy); if (pDC->IsScreen()) { CGContextSaveGState(dc); if (frc) { CGRect rect = rc; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } CGContextTranslateCTM(dc, 0, pDC->Y()-1); CGContextScaleCTM(dc, 1.0, -1.0); e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, fx - Long2Fix(Ox), Long2Fix(y) - fAscent); CGContextRestoreGState(dc); } else { if (frc) { CGContextSaveGState(dc); CGRect rect = rc; rect.origin.x -= Ox; rect.origin.y = pDC->Y() - rect.origin.y + Oy - rect.size.height; rect.size.width += 1.0; rect.size.height += 1.0; CGContextClipToRect(dc, rect); } e = ATSUDrawText(Hnd, kATSUFromTextBeginning, kATSUToTextEnd, Long2Fix(px - Ox), Long2Fix(y) - fAscent); if (frc) CGContextRestoreGState(dc); } if (e) { char *a = 0; StringConvert(a, NULL, Str, len); printf("%s:%i - ATSUDrawText failed with %i, len=%i, str=%.20s\n", _FL, (int)e, len, a); DeleteArray(a); } } } #endif } #endif } diff --git a/src/common/Widgets/ItemContainer.cpp b/src/common/Widgets/ItemContainer.cpp --- a/src/common/Widgets/ItemContainer.cpp +++ b/src/common/Widgets/ItemContainer.cpp @@ -1,1312 +1,1314 @@ #include "lgi/common/Lgi.h" #include "lgi/common/ItemContainer.h" #include "lgi/common/DisplayString.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/Edit.h" #include "lgi/common/CssTools.h" // Colours #if defined(__GTK_H__) #define DOUBLE_BUFFER_COLUMN_DRAWING 1 #else #define DOUBLE_BUFFER_COLUMN_DRAWING 0 #endif #if defined(WIN32) #if !defined(WS_EX_LAYERED) #define WS_EX_LAYERED 0x80000 #endif #if !defined(LWA_ALPHA) #define LWA_ALPHA 2 #endif typedef BOOL (__stdcall *_SetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags); #endif class LItemColumnPrivate { public: LRect Pos; bool Down; bool Drag; LItemContainer *Parent; char *cName; LDisplayString *Txt; int cWidth; int cType; LSurface *cIcon; int cImage; int cMark; bool OwnIcon; bool CanResize; LItemColumnPrivate(LItemContainer *parent) { Parent = parent; Txt = 0; cName = 0; cWidth = 0; cIcon = 0; cType = GIC_ASK_TEXT; cImage = -1; cMark = GLI_MARK_NONE; Down = false; OwnIcon = false; CanResize = true; Drag = false; } ~LItemColumnPrivate() { DeleteArray(cName); if (OwnIcon) { DeleteObj(cIcon); } DeleteObj(Txt); } }; static LColour cActiveCol(0x86, 0xba, 0xe9); static void FillStops(LArray &Stops, LRect &r, bool Active) { if (Active) { Stops[0].Set(0.0f, LColour(0xd0, 0xe2, 0xf5)); Stops[1].Set(2.0f / (r.Y() - 2), LColour(0x98, 0xc1, 0xe9)); Stops[2].Set(0.5f, LColour(0x86, 0xba, 0xe9)); Stops[3].Set(0.51f, LColour(0x68, 0xaf, 0xea)); Stops[4].Set(1.0f, LColour(0xbb, 0xfc, 0xff)); } else { LColour cMed(L_MED), cWs(L_WORKSPACE); if (cWs.GetGray() < 96) { cMed = cMed.Mix(LColour::White, 0.25f); cWs = cWs.Mix(LColour::White, 0.25f); } Stops[0].Set(0.0f, cWs); Stops[1].Set(0.5f, cMed.Mix(cWs)); Stops[2].Set(0.51f, cMed); Stops[3].Set(1.0f, cWs); } } ////////////////////////////////////////////////////////////////////////////////////// LItemContainer::LItemContainer() { Flags = 0; DragMode = 0; DragCol = NULL; ColClick = -1; ColumnHeaders = true; ColumnHeader.ZOff(-1, -1); Columns.SetFixedLength(true); ItemEdit = NULL; } LItemContainer::~LItemContainer() { DeleteObj(ItemEdit); DeleteObj(DragCol); Columns.DeleteObjects(); } void LItemContainer::PaintColumnHeadings(LSurface *pDC) { // Draw column headings if (!ColumnHeaders || !ColumnHeader.Valid()) return; LSurface *ColDC = pDC; LRect cr; #if DOUBLE_BUFFER_COLUMN_DRAWING LMemDC Bmp; if (!pDC->SupportsAlphaCompositing() && Bmp.Create(ColumnHeader.X(), ColumnHeader.Y(), System32BitColourSpace)) { ColDC = &Bmp; Bmp.Op(GDC_ALPHA); cr = ColumnHeader.ZeroTranslate(); } else #endif { cr = ColumnHeader; pDC->ClipRgn(&cr); } // Draw columns int cx = cr.x1; if (IconCol) { cr.x1 = cx; cr.x2 = cr.x1 + IconCol->Width() - 1; IconCol->SetPos(cr); IconCol->OnPaint(ColDC, cr); cx += IconCol->Width(); } // Draw other columns for (int i=0; iWidth() - 1; c->SetPos(cr); c->OnPaint(ColDC, cr); cx += c->Width(); } else LAssert(0); } // Draw ending piece cr.x1 = cx; cr.x2 = ColumnHeader.x2 + 2; if (cr.Valid()) { // Draw end section where there are no columns #ifdef MAC LArray Stops; LRect j(cr.x1, cr.y1, cr.x2-1, cr.y2-1); FillStops(Stops, j, false); LFillGradient(ColDC, j, true, Stops); ColDC->Colour(L_LOW); ColDC->Line(cr.x1, cr.y2, cr.x2, cr.y2); #else if (LApp::SkinEngine) { LSkinState State; State.pScreen = ColDC; State.Rect = cr; State.Enabled = Enabled(); State.View = this; LApp::SkinEngine->OnPaint_ListColumn(0, 0, &State); } else { LWideBorder(ColDC, cr, DefaultRaisedEdge); ColDC->Colour(LColour(L_MED)); ColDC->Rectangle(&cr); } #endif } #if DOUBLE_BUFFER_COLUMN_DRAWING if (!pDC->SupportsAlphaCompositing()) pDC->Blt(ColumnHeader.x1, ColumnHeader.y1, &Bmp); else #endif pDC->ClipRgn(0); } LItemColumn *LItemContainer::AddColumn(const char *Name, int Width, int Where) { LItemColumn *c = 0; if (Lock(_FL)) { c = new LItemColumn(this, Name, Width); if (c) { Columns.SetFixedLength(false); Columns.AddAt(Where, c); Columns.SetFixedLength(true); UpdateAllItems(); SendNotify(LNotifyItemColumnsChanged); } Unlock(); } return c; } bool LItemContainer::AddColumn(LItemColumn *Col, int Where) { bool Status = false; if (Col && Lock(_FL)) { Columns.SetFixedLength(false); Status = Columns.AddAt(Where, Col); Columns.SetFixedLength(true); if (Status) { UpdateAllItems(); SendNotify(LNotifyItemColumnsChanged); } Unlock(); } return Status; } void LItemContainer::DragColumn(int Index) { DeleteObj(DragCol); if (Index >= 0) { DragCol = new LDragColumn(this, Index); if (DragCol) { Capture(true); DragMode = DRAG_COLUMN; } } } int LItemContainer::ColumnAtX(int x, LItemColumn **Col, int *Offset) { LItemColumn *Column = 0; if (!Col) Col = &Column; int Cx = GetImageList() ? 16 : 0; int c; for (c=0; c= Cx && x < Cx + (*Col)->Width()) { if (Offset) *Offset = Cx; return c; } Cx += (*Col)->Width(); } return -1; } void LItemContainer::EmptyColumns() { Columns.DeleteObjects(); Invalidate(&ColumnHeader); SendNotify(LNotifyItemColumnsChanged); } int LItemContainer::HitColumn(int x, int y, LItemColumn *&Resize, LItemColumn *&Over) { int Index = -1; Resize = 0; Over = 0; if (ColumnHeaders && ColumnHeader.Overlap(x, y)) { // Clicked on a column heading int cx = ColumnHeader.x1 + ((IconCol) ? IconCol->Width() : 0); for (int n = 0; n < Columns.Length(); n++) { LItemColumn *c = Columns[n]; cx += c->Width(); if (abs(x-cx) < 5) { if (c->Resizable()) { Resize = c; Index = n; break; } } else if (c->d->Pos.Overlap(x, y)) { Over = c; Index = n; break; } } } return Index; } void LItemContainer::OnColumnClick(int Col, LMouse &m) { ColClick = Col; ColMouse = m; LNotification n(LNotifyItemColumnClicked); n.Int[0] = Col; SendNotify(n); } bool LItemContainer::GetColumnClickInfo(int &Col, LMouse &m) { if (ColClick >= 0) { Col = ColClick; m = ColMouse; return true; } return false; } void LItemContainer::GetColumnSizes(ColSizes &cs) { // Read in the current sizes cs.FixedPx = 0; cs.ResizePx = 0; for (int i=0; iResizable()) { ColInfo &Inf = cs.Info.New(); Inf.Col = c; Inf.Idx = i; Inf.ContentPx = c->GetContentSize(); Inf.WidthPx = c->Width(); cs.ResizePx += Inf.ContentPx; } else { cs.FixedPx += c->Width(); } } } LMessage::Result LItemContainer::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_RESIZE_TO_CONTENT: { ResizeColumnsToContent((int)Msg->A()); break; } default: break; } return LLayout::OnEvent(Msg); } void LItemContainer::ResizeColumnsToContent(int Border) { if (!InThread()) { PostEvent(M_RESIZE_TO_CONTENT, Border); return; } if (Lock(_FL)) { // Read in the current sizes ColSizes Sizes; GetColumnSizes(Sizes); // Allocate space int AvailablePx = GetClient().X() - 5; if (VScroll) AvailablePx -= VScroll->X(); int ExpandPx = AvailablePx - Sizes.FixedPx; Sizes.Info.Sort([](auto a, auto b) { int AGrowPx = a->GrowPx(); int BGrowPx = b->GrowPx(); return AGrowPx - BGrowPx; }); for (int i=0; iResizable()) { if (ExpandPx > Sizes.ResizePx) { // Everything fits... Inf.Col->Width(Inf.ContentPx + Border); } else { int Cx = GetClient().X(); double Ratio = Cx ? (double)Inf.ContentPx / Cx : 1.0; if (Ratio < 0.25) { Inf.Col->Width(Inf.ContentPx + Border); } else { // Need to scale to fit... int Px = Inf.ContentPx * ExpandPx / Sizes.ResizePx; Inf.Col->Width(Px + Border); } } ClearDs(Inf.Idx); } } Unlock(); } Invalidate(); } ////////////////////////////////////////////////////////////////////////////// LDragColumn::LDragColumn(LItemContainer *list, int col) { List = list; Index = col; Offset = 0; #ifdef LINUX Back = 0; #endif Col = List->ColumnAt(Index); if (Col) { Col->d->Down = false; Col->d->Drag = true; LRect r = Col->d->Pos; r.y1 = 0; r.y2 = List->Y()-1; List->Invalidate(&r, true); #if WINNATIVE LArray Ver; bool Layered = ( LGetOs(&Ver) == LGI_OS_WIN32 || LGetOs(&Ver) == LGI_OS_WIN64 ) && Ver[0] >= 5; SetStyle(WS_POPUP); SetExStyle(GetExStyle() | WS_EX_TOOLWINDOW); if (Layered) { SetExStyle(GetExStyle() | WS_EX_LAYERED | WS_EX_TRANSPARENT); } #endif Attach(0); #if WINNATIVE if (Layered) { SetWindowLong(Handle(), GWL_EXSTYLE, GetWindowLong(Handle(), GWL_EXSTYLE) | WS_EX_LAYERED); LLibrary User32("User32"); _SetLayeredWindowAttributes SetLayeredWindowAttributes = (_SetLayeredWindowAttributes)User32.GetAddress("SetLayeredWindowAttributes"); if (SetLayeredWindowAttributes) { if (!SetLayeredWindowAttributes(Handle(), 0, DRAG_COL_ALPHA, LWA_ALPHA)) { DWORD Err = GetLastError(); } } } #elif defined(__GTK_H__) Gtk::GtkWindow *w = WindowHandle(); if (w) { gtk_window_set_decorated(w, FALSE); gtk_widget_set_opacity(GtkCast(w, gtk_widget, GtkWidget), DRAG_COL_ALPHA / 255.0); } #endif LMouse m; List->GetMouse(m); Offset = m.x - r.x1; List->PointToScreen(ListScrPos); r.Offset(ListScrPos.x, ListScrPos.y); SetPos(r); Visible(true); } } LDragColumn::~LDragColumn() { Visible(false); if (Col) { Col->d->Drag = false; } List->Invalidate(); } #if LINUX_TRANS_COL void LDragColumn::OnPosChange() { Invalidate(); } #endif void LDragColumn::OnPaint(LSurface *pScreen) { #if LINUX_TRANS_COL LSurface *Buf = new LMemDC(X(), Y(), GdcD->GetBits()); LSurface *pDC = new LMemDC(X(), Y(), GdcD->GetBits()); #else LSurface *pDC = pScreen; #endif pDC->SetOrigin(Col->d->Pos.x1, 0); if (Col) Col->d->Drag = false; List->OnPaint(pDC); if (Col) Col->d->Drag = true; pDC->SetOrigin(0, 0); #if LINUX_TRANS_COL if (Buf && pDC) { LRect p = GetPos(); // Fill the buffer with the background Buf->Blt(ListScrPos.x - p.x1, 0, Back); // Draw painted column over the back with alpha Buf->Op(GDC_ALPHA); LApplicator *App = Buf->Applicator(); if (App) { App->SetVar(GAPP_ALPHA_A, DRAG_COL_ALPHA); } Buf->Blt(0, 0, pDC); // Put result on the screen pScreen->Blt(0, 0, Buf); } DeleteObj(Buf); DeleteObj(pDC); #endif } /////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// // List column LItemColumn::LItemColumn(LItemContainer *parent, const char *name, int width) : ResObject(Res_Column) { d = new LItemColumnPrivate(parent); d->cWidth = width; if (name) Name(name); } LItemColumn::~LItemColumn() { if (d->Drag) { d->Parent->DragColumn(-1); } DeleteObj(d); } LItemContainer *LItemColumn::GetList() { return d->Parent; } void LItemColumn::Image(int i) { d->cImage = i; } int LItemColumn::Image() { return d->cImage; } bool LItemColumn::Resizable() { return d->CanResize; } void LItemColumn::Resizable(bool i) { d->CanResize = i; } bool LItemColumn::InDrag() { return d->Drag; } LRect LItemColumn::GetPos() { return d->Pos; } void LItemColumn::SetPos(LRect &r) { d->Pos = r; } void LItemColumn::Name(const char *n) { DeleteArray(d->cName); DeleteObj(d->Txt); d->cName = NewStr(n); LFont *f = d->Parent && d->Parent->GetFont() && d->Parent->GetFont()->Handle() ? d->Parent->GetFont() : LSysFont; d->Txt = new LDisplayString(f, (char*)n); if (d->Parent) { d->Parent->Invalidate(&d->Parent->ColumnHeader); } } char *LItemColumn::Name() { return d->cName; } int LItemColumn::GetIndex() { if (d->Parent) { return (int)d->Parent->Columns.IndexOf(this); } return -1; } int LItemColumn::GetContentSize() { return d->Parent->GetContentSize(GetIndex()); } void LItemColumn::Width(int i) { if (d->cWidth != i) { d->cWidth = i; // If we are attached to a list... if (d->Parent) { /* FIXME int MyIndex = GetIndex(); // Clear all the cached strings for this column for (List::I it=d->Parent->Items.Start(); it.In(); it++) { DeleteObj((*it)->d->Display[MyIndex]); } if (d->Parent->IsAttached()) { // Update the screen from this column across LRect Up = d->Parent->GetClient(); Up.x1 = d->Pos.x1; d->Parent->Invalidate(&Up); } */ } // Notify listener auto p = d->Parent; if (p) p->SendNotify(LNotifyItemColumnsResized); } } int LItemColumn::Width() { return d->cWidth; } void LItemColumn::Mark(int i) { d->cMark = i; if (d->Parent) { d->Parent->Invalidate(&d->Parent->ColumnHeader); } } int LItemColumn::Mark() { return d->cMark; } void LItemColumn::Type(int i) { d->cType = i; if (d->Parent) { d->Parent->Invalidate(&d->Parent->ColumnHeader); } } int LItemColumn::Type() { return d->cType; } void LItemColumn::Icon(LSurface *i, bool Own) { if (d->OwnIcon) { DeleteObj(d->cIcon); } d->cIcon = i; d->OwnIcon = Own; if (d->Parent) { d->Parent->Invalidate(&d->Parent->ColumnHeader); } } LSurface *LItemColumn::Icon() { return d->cIcon; } bool LItemColumn::Value() { return d->Down; } void LItemColumn::Value(bool i) { d->Down = i; } void LItemColumn::OnPaint_Content(LSurface *pDC, LRect &r, bool FillBackground) { - if (!d->Drag) + if (d->Drag) + return; + + LCssTools Tools(d->Parent); + auto Fore = Tools.GetFore(); + auto cMed = LColour(L_MED); + int Off = d->Down ? 1 : 0; + int Mx = r.x1 + 8, My = r.y1 + ((r.Y() - 8) / 2); + if (d->cIcon) { - LCssTools Tools(d->Parent); - auto Fore = Tools.GetFore(); - auto cMed = LColour(L_MED); - int Off = d->Down ? 1 : 0; - int Mx = r.x1 + 8, My = r.y1 + ((r.Y() - 8) / 2); - if (d->cIcon) + if (FillBackground) { - if (FillBackground) - { - pDC->Colour(cMed); - pDC->Rectangle(&r); - } + pDC->Colour(cMed); + pDC->Rectangle(&r); + } - int x = (r.X()-d->cIcon->X()) / 2; - - pDC->Blt( r.x1 + x + Off, - r.y1 + ((r.Y()-d->cIcon->Y())/2) + Off, - d->cIcon); + int x = (r.X()-d->cIcon->X()) / 2; + + pDC->Blt( r.x1 + x + Off, + r.y1 + ((r.Y()-d->cIcon->Y())/2) + Off, + d->cIcon); - if (d->cMark) - { - Mx += x + d->cIcon->X() + 4; - } + if (d->cMark) + { + Mx += x + d->cIcon->X() + 4; } - else if (d->cImage >= 0 && d->Parent) + } + else if (d->cImage >= 0 && d->Parent) + { + LColour Background = cMed; + if (FillBackground) { - LColour Background = cMed; - if (FillBackground) - { - pDC->Colour(Background); - pDC->Rectangle(&r); - } - - if (d->Parent->GetImageList()) + pDC->Colour(Background); + pDC->Rectangle(&r); + } + + if (d->Parent->GetImageList()) + { + LRect *b = d->Parent->GetImageList()->GetBounds(); + int x = r.x1; + int y = r.y1; + if (b) { - LRect *b = d->Parent->GetImageList()->GetBounds(); - int x = r.x1; - int y = r.y1; - if (b) - { - b += d->cImage; - x = r.x1 + ((r.X()-b->X()) / 2) - b->x1; - y = r.y1 + ((r.Y()-b->Y()) / 2) - b->y1; - } - - d->Parent->GetImageList()->Draw(pDC, - x + Off, - y + Off, - d->cImage, - Background); - } - - if (d->cMark) - { - Mx += d->Parent->GetImageList()->TileX() + 4; - } - } - else if (ValidStr(d->cName) && d->Txt) - { - LFont *f = d->Txt->GetFont(); - if (!f) - { - LAssert(0); - return; - } - - LColour cText = Fore; - #ifdef MAC - // Contrast check - if (d->cMark && (cText - cActiveCol) < 64) - cText = cText.Invert(); - #endif - - f->Transparent(!FillBackground); - f->Colour(cText, cMed); - int ty = d->Txt->Y(); - int ry = r.Y(); - int y = r.y1 + ((ry - ty) >> 1); - d->Txt->Draw(pDC, r.x1 + Off + 3, y + Off, &r); - - if (d->cMark) - { - Mx += d->Txt->X(); - } - } - else - { - if (FillBackground) - { - pDC->Colour(cMed); - pDC->Rectangle(&r); - } + b += d->cImage; + x = r.x1 + ((r.X()-b->X()) / 2) - b->x1; + y = r.y1 + ((r.Y()-b->Y()) / 2) - b->y1; + } + + d->Parent->GetImageList()->Draw(pDC, + x + Off, + y + Off, + d->cImage, + Background); } - #define ARROW_SIZE 9 - pDC->Colour(Fore); - Mx += Off; - My += Off - 1; + if (d->cMark) + { + Mx += d->Parent->GetImageList()->TileX() + 4; + } + } + else if (ValidStr(d->cName) && d->Txt) + { + LFont *f = d->Txt->GetFont(); + if (!f) + { + LAssert(0); + return; + } - switch (d->cMark) + LColour cText = Fore; + #ifdef MAC + // Contrast check + if (d->cMark && (cText - cActiveCol) < 64) + cText = cText.Invert(); + #endif + + f->Transparent(!FillBackground); + f->Colour(cText, cMed); + int ty = d->Txt->Y(); + int ry = r.Y(); + int y = r.y1 + ((ry - ty) >> 1); + + // d->Txt->_debug = true; + d->Txt->Draw(pDC, r.x1 + Off + 3, y + Off, &r); + + if (d->cMark) { - case GLI_MARK_UP_ARROW: - { - pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1); - pDC->Line(Mx, My + 2, Mx + 2, My); - pDC->Line(Mx + 2, My, Mx + 4, My + 2); - break; - } - case GLI_MARK_DOWN_ARROW: - { - pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1); - pDC->Line( Mx, - My + ARROW_SIZE - 3, - Mx + 2, - My + ARROW_SIZE - 1); - pDC->Line( Mx + 2, - My + ARROW_SIZE - 1, - Mx + 4, - My + ARROW_SIZE - 3); - break; - } + Mx += d->Txt->X(); + } + } + else + { + if (FillBackground) + { + pDC->Colour(cMed); + pDC->Rectangle(&r); + } + } + + #define ARROW_SIZE 9 + pDC->Colour(Fore); + Mx += Off; + My += Off - 1; + + switch (d->cMark) + { + case GLI_MARK_UP_ARROW: + { + pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1); + pDC->Line(Mx, My + 2, Mx + 2, My); + pDC->Line(Mx + 2, My, Mx + 4, My + 2); + break; + } + case GLI_MARK_DOWN_ARROW: + { + pDC->Line(Mx + 2, My, Mx + 2, My + ARROW_SIZE - 1); + pDC->Line( Mx, + My + ARROW_SIZE - 3, + Mx + 2, + My + ARROW_SIZE - 1); + pDC->Line( Mx + 2, + My + ARROW_SIZE - 1, + Mx + 4, + My + ARROW_SIZE - 3); + break; } } } void ColumnPaint(void *UserData, LSurface *pDC, LRect &r, bool FillBackground) { ((LItemColumn*)UserData)->OnPaint_Content(pDC, r, FillBackground); } void LItemColumn::OnPaint(LSurface *pDC, LRect &Rgn) { LRect r = Rgn; if (d->Drag) { pDC->Colour(DragColumnColour); pDC->Rectangle(&r); } else { #ifdef MAC LArray Stops; LRect j(r.x1, r.y1, r.x2-1, r.y2-1); FillStops(Stops, j, d->cMark != 0); LFillGradient(pDC, j, true, Stops); if (d->cMark) pDC->Colour(Rgb24(0x66, 0x93, 0xc0), 24); else pDC->Colour(Rgb24(178, 178, 178), 24); pDC->Line(r.x1, r.y2, r.x2, r.y2); pDC->Line(r.x2, r.y1, r.x2, r.y2); LRect n = r; n.Inset(2, 2); OnPaint_Content(pDC, n, false); #else if (LApp::SkinEngine) { LSkinState State; State.pScreen = pDC; State.ptrText = &d->Txt; State.Rect = Rgn; State.Value = Value(); State.Enabled = GetList()->Enabled(); State.View = d->Parent; LApp::SkinEngine->OnPaint_ListColumn(ColumnPaint, this, &State); } else { if (d->Down) { LThinBorder(pDC, r, DefaultSunkenEdge); LFlatBorder(pDC, r, 1); } else { LWideBorder(pDC, r, DefaultRaisedEdge); } OnPaint_Content(pDC, r, true); } #endif } } /////////////////////////////////////////////////////////////////////////////////////////// LItem::LItem() { SelectionStart = SelectionEnd = -1; } LItem::~LItem() { } LView *LItem::EditLabel(int Col) { LItemContainer *c = GetContainer(); if (!c) return NULL; c->Capture(false); if (!c->ItemEdit) { c->ItemEdit = new LItemEdit(c, this, Col, SelectionStart, SelectionEnd); SelectionStart = SelectionEnd = -1; } return c->ItemEdit; } void LItem::OnEditLabelEnd() { LItemContainer *c = GetContainer(); if (c) c->ItemEdit = NULL; } void LItem::SetEditLabelSelection(int SelStart, int SelEnd) { SelectionStart = SelStart; SelectionEnd = SelEnd; } //////////////////////////////////////////////////////////////////////////////////////////// #define M_END_POPUP (M_USER+0x1500) #define M_LOSING_FOCUS (M_USER+0x1501) class LItemEditBox : public LEdit { LItemEdit *ItemEdit; public: LItemEditBox(LItemEdit *i, int x, int y, const char *s) : LEdit(100, 1, 1, x-3, y-3, s) { ItemEdit = i; Sunken(false); MultiLine(false); #ifndef LINUX SetPos(GetPos()); #endif } const char *GetClass() { return "LItemEditBox"; } void OnCreate() { LEdit::OnCreate(); Focus(true); } void OnFocus(bool f) { if (!f && GetParent()) { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEditBox posting M_LOSING_FOCUS\n", _FL); #endif GetParent()->PostEvent(M_LOSING_FOCUS); } LEdit::OnFocus(f); } bool OnKey(LKey &k) { /* This should be handled by LEdit::OnKey now. Which will send a LNotifyEscapeKey or LNotifyReturnKey up to the ItemEdit OnNotify handler. switch (k.vkey) { case LK_RETURN: case LK_ESCAPE: { if (k.Down()) ItemEdit->OnNotify(this, k.c16); return true; } } */ return LEdit::OnKey(k); } bool SetScrollBars(bool x, bool y) { return false; } }; ////////////////////////////////////////////////////////////////////////////////////////// class LItemEditPrivate { public: LItem *Item; LEdit *Edit; int Index; bool Esc; LItemEditPrivate() { Esc = false; Item = 0; Index = 0; } }; LItemEdit::LItemEdit(LView *parent, LItem *item, int index, int SelStart, int SelEnd) : LPopup(parent) { d = new LItemEditPrivate; d->Item = item; d->Index = index; _BorderSize = 0; Sunken(false); Raised(false); #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit(%p/%s, %i, %i, %i)\n", _FL, parent, parent?parent->GetClass():0, index, SelStart, SelEnd); #endif LPoint p; SetParent(parent); GetParent()->PointToScreen(p); LRect r = d->Item->GetPos(d->Index); int MinY = 6 + LSysFont->GetHeight(); if (r.Y() < MinY) r.y2 = r.y1 + MinY - 1; r.Offset(p.x, p.y); SetPos(r); if (Attach(parent)) { d->Edit = new LItemEditBox(this, r.X(), r.Y(), d->Item->GetText(d->Index)); if (d->Edit) { d->Edit->Attach(this); d->Edit->Focus(true); if (SelStart >= 0) { d->Edit->Select(SelStart, SelEnd-SelStart+1); } } Visible(true); } } LItemEdit::~LItemEdit() { if (d->Item) { if (d->Edit && !d->Esc) { auto Str = d->Edit->Name(); #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - ~LItemEdit, updating item(%i) with '%s'\n", _FL, d->Index, Str); #endif LItemContainer *c = d->Item->GetContainer(); if (d->Item->SetText(Str, d->Index)) { d->Item->Update(); } else { // Item is deleting itself... // Make sure there is no dangling ptr on the container.. if (c) c->ItemEdit = NULL; // And we don't touch the no longer existant item.. d->Item = NULL; } } #if DEBUG_EDIT_LABEL else LgiTrace("%s:%i - Edit=%p Esc=%i\n", _FL, d->Edit, d->Esc); #endif if (d->Item) d->Item->OnEditLabelEnd(); } #if DEBUG_EDIT_LABEL else LgiTrace("%s:%i - Error: No item?\n", _FL); #endif DeleteObj(d); } LItem *LItemEdit::GetItem() { return d->Item; } void LItemEdit::OnPaint(LSurface *pDC) { pDC->Colour(L_BLACK); pDC->Rectangle(); } int LItemEdit::OnNotify(LViewI *v, LNotification n) { switch (v->GetId()) { case 100: { if (n.Type == LNotifyEscapeKey) { d->Esc = true; #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit got escape\n", _FL); #endif } if (n.Type == LNotifyEscapeKey || n.Type == LNotifyReturnKey) { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit hiding on esc/enter\n", _FL); #endif d->Edit->KeyProcessed(); Visible(false); } break; } } return 0; } void LItemEdit::Visible(bool i) { LPopup::Visible(i); if (!i) { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit posting M_END_POPUP\n", _FL); #endif PostEvent(M_END_POPUP); } } bool LItemEdit::OnKey(LKey &k) { if (d->Edit) return d->Edit->OnKey(k); return false; } void LItemEdit::OnFocus(bool f) { if (f && d->Edit) d->Edit->Focus(true); } LMessage::Result LItemEdit::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_LOSING_FOCUS: { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit get M_LOSING_FOCUS\n", _FL); #endif // One of us has to retain focus... don't care which control. if (Focus() || d->Edit->Focus()) break; // else fall thru to end the popup #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit falling thru to M_END_POPUP\n", _FL); #endif } case M_END_POPUP: { #if DEBUG_EDIT_LABEL LgiTrace("%s:%i - LItemEdit got M_END_POPUP, quiting\n", _FL); #endif if (d->Item && d->Item->GetContainer()) { d->Item->GetContainer()->Focus(true); } Quit(); return 0; } } return LPopup::OnEvent(Msg); } diff --git a/src/haiku/MemDC.cpp b/src/haiku/MemDC.cpp --- a/src/haiku/MemDC.cpp +++ b/src/haiku/MemDC.cpp @@ -1,349 +1,351 @@ /*hdr ** FILE: MemDC.cpp ** AUTHOR: Matthew Allen ** DATE: 14/10/2000 ** DESCRIPTION: GDC v2.xx header ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include + #include "Screen.h" +#include "Region.h" #include "lgi/common/Gdc2.h" #include "lgi/common/LgiString.h" #include ///////////////////////////////////////////////////////////////////////////////////////////////////// #define ROUND_UP(bits) (((bits) + 7) / 8) class LMemDCPrivate { public: ::LArray Client; LColourSpace CreateCs = CsNone; BBitmap *Bmp = NULL; BView *View = NULL; LMemDCPrivate() { } ~LMemDCPrivate() { Empty(); } void Empty() { } }; LMemDC::LMemDC(int x, int y, LColourSpace cs, int flags) { d = new LMemDCPrivate; if (cs != CsNone) Create(x, y, cs, flags); } LMemDC::LMemDC(LSurface *pDC) { d = new LMemDCPrivate; if (pDC && Create(pDC->X(), pDC->Y(), pDC->GetColourSpace())) { Blt(0, 0, pDC); } } LMemDC::~LMemDC() { Empty(); DeleteObj(d); } OsBitmap LMemDC::GetBitmap() { return d->Bmp; } OsPainter LMemDC::Handle() { if (!d->View && d->Bmp) { d->View = new BView(d->Bmp->Bounds(), "BBitmapView", B_FOLLOW_NONE, B_WILL_DRAW); if (d->View) d->Bmp->AddChild(d->View); } return d->View; } void LMemDC::SetClient(LRect *c) { if (c) { Handle(); LRect Doc; if (d->Client.Length()) Doc = d->Client.Last(); else Doc = Bounds(); LRect r = *c; r.Bound(&Doc); d->Client.Add(r); Clip = r; OriginX = -r.x1; OriginY = -r.y1; } else { if (d->Client.Length()) d->Client.PopLast(); if (d->Client.Length()) { auto &r = d->Client.Last(); OriginX = -r.x1; OriginY = -r.y1; Clip = r; } else { OriginX = 0; OriginY = 0; Clip.ZOff(pMem->x-1, pMem->y-1); } } } void LMemDC::Empty() { d->Empty(); DeleteObj(pMem); } bool LMemDC::Lock() { return false; } bool LMemDC::Unlock() { return false; } bool LMemDC::Create(int x, int y, LColourSpace Cs, int Flags) { - BRect b(0, 0, x, y); - d->Bmp = new BBitmap(b, B_RGB32, false, true); + BRect b(0, 0, x-1, y-1); + d->Bmp = new BBitmap(b, B_RGB32, true, true); if (!d->Bmp || d->Bmp->InitCheck() != B_OK) { DeleteObj(d->Bmp); LgiTrace("%s:%i - Failed to create memDC(%i,%i)\n", _FL, x, y); return false; } - + pMem = new LBmpMem; if (!pMem) return false; - pMem->x = d->Bmp->Bounds().Width(); - pMem->y = d->Bmp->Bounds().Height(); - ColourSpace = pMem->Cs = System32BitColourSpace; - pMem->Line = d->Bmp->BytesPerRow(); - pMem->Base = (uchar*)d->Bmp->Bits(); + pMem->x = x; + pMem->y = y; + pMem->Cs = ColourSpace = System32BitColourSpace; + pMem->Line = d->Bmp->BytesPerRow(); + pMem->Base = (uchar*)d->Bmp->Bits(); int NewOp = (pApp) ? Op() : GDC_SET; if ((Flags & GDC_OWN_APPLICATOR) && !(Flags & GDC_CACHED_APPLICATOR)) { DeleteObj(pApp); } for (int i=0; ix, pMem->y, LColourSpaceToString(pMem->Cs), pMem->Line, pMem->Base); return true; } void LMemDC::Blt(int x, int y, LSurface *Src, LRect *a) { if (!Src) return; bool Status = false; LBlitRegions br(this, x, y, Src, a); if (!br.Valid()) return; LScreenDC *Screen; if ((Screen = Src->IsScreen())) { BScreen scr; BBitmap *bitmap = NULL; BRect src = br.SrcClip; auto r = scr.GetBitmap(&bitmap, TestFlag(Flags, GDC_CAPTURE_CURSOR), &src); if (r == B_OK) { bitmap->LockBits(); int dstPx = GetBits() / 8; size_t srcPx = 4, row = 0, chunk = 0; get_pixel_size_for(bitmap->ColorSpace(), &srcPx, &row, &chunk); for (int y=0; yBits()) + (bitmap->BytesPerRow() * (br.SrcClip.y1 + y)) + (br.SrcClip.x1 * srcPx); LAssert(!"Impl pixel converter."); } bitmap->UnlockBits(); } else { Colour(Rgb24(255, 0, 255), 24); Rectangle(a); } delete bitmap; } else if ((*Src)[0]) { // Memory -> Memory (Source alpha used) LSurface::Blt(x, y, Src, a); } } void LMemDC::StretchBlt(LRect *d, LSurface *Src, LRect *s) { LAssert(!"Not implemented"); } void LMemDC::SetOrigin(int x, int y) { } bool LMemDC::SupportsAlphaCompositing() { return true; } void LMemDC::GetOrigin(int &x, int &y) { LSurface::GetOrigin(x, y); } LRect LMemDC::ClipRgn(LRect *Rgn) { LRect Old = Clip; if (Rgn) { LRect Dc(0, 0, X()-1, Y()-1); Clip = *Rgn; Clip.Offset(-OriginX, -OriginY); Clip.Bound(&Dc); } else { Clip.ZOff(X()-1, Y()-1); } return Old; } void LMemDC::HorzLine(int x1, int x2, int y, COLOUR a, COLOUR b) { if (x1 > x2) LSwap(x1, x2); if (x1 < Clip.x1) x1 = Clip.x1; if (x2 > Clip.x2) x2 = Clip.x2; if (x1 <= x2 && y >= Clip.y1 && y <= Clip.y2 && pApp) { COLOUR Prev = pApp->c; pApp->SetPtr(x1, y); for (; x1 <= x2; x1++) { if (x1 & 1) { pApp->c = a; } else { pApp->c = b; } pApp->Set(); pApp->IncX(); } pApp->c = Prev; } } void LMemDC::VertLine(int x, int y1, int y2, COLOUR a, COLOUR b) { if (y1 > y2) LSwap(y1, y2); if (y1 < Clip.y1) y1 = Clip.y1; if (y2 > Clip.y2) y2 = Clip.y2; if (y1 <= y2 && x >= Clip.x1 && x <= Clip.x2 && pApp) { COLOUR Prev = pApp->c; pApp->SetPtr(x, y1); for (; y1 <= y2; y1++) { if (y1 & 1) { pApp->c = a; } else { pApp->c = b; } pApp->Set(); pApp->IncY(); } pApp->c = Prev; } } diff --git a/src/haiku/ScreenDC.cpp b/src/haiku/ScreenDC.cpp --- a/src/haiku/ScreenDC.cpp +++ b/src/haiku/ScreenDC.cpp @@ -1,481 +1,481 @@ /*hdr ** FILE: LScreenDC.cpp ** AUTHOR: Matthew Allen ** DATE: 29/11/2021 ** DESCRIPTION: Haiku screen DC ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include #include #define LOGGING 0 #define VIEW_CHECK(...) if (!d->v) return __VA_ARGS__; class LScreenPrivate { public: int x = 0, y = 0, Bits = 32; bool Own = false; LColour Col; LRect Client; LView *View = NULL; OsView v = NULL; LScreenPrivate() { Client.ZOff(-1, -1); } ~LScreenPrivate() { } }; // Translates are cumulative... so we undo the last one before resetting it. ///////////////////////////////////////////////////////////////////////////////////////////////////// LScreenDC::LScreenDC() { d = new LScreenPrivate; d->x = GdcD->X(); d->y = GdcD->Y(); d->Bits = GdcD->GetBits(); } LScreenDC::LScreenDC(LView *view, void *param) { d = new LScreenPrivate; if (d->View = view) d->v = view->Handle(); d->Bits = GdcD->GetBits(); /* if (d->v) d->Client = d->v->Frame(); else LgiTrace("%s:%i - LScreenDC::LScreenDC - No view?\n", _FL); */ } LScreenDC::~LScreenDC() { DeleteObj(d); } OsPainter LScreenDC::Handle() { return d->v; } ::LString LScreenDC::Dump() { ::LString s; s.Printf("LScreenDC size=%i,%i\n", d->x, d->y); return s; } bool LScreenDC::SupportsAlphaCompositing() { return true; } LPoint LScreenDC::GetDpi() { return LScreenDpi(); } bool LScreenDC::GetClient(LRect *c) { if (!c) return false; *c = d->Client; return true; } LString GetClip(BView *v) { BRegion r; v->GetClippingRegion(&r); LRect lr = r.Frame(); return lr.GetStr(); } void LScreenDC::GetOrigin(int &x, int &y) { x = OriginX; y = OriginY; #if LOGGING printf("%p.GetOrigin=%i+%i=%i, %i+%i=%i\n", this, OriginX, d->Client.x1, x, OriginY, d->Client.y1, y); #endif } void LScreenDC::SetOrigin(int x, int y) { VIEW_CHECK() if (d->Client.Valid()) { // The clipping region is relative to the offset. // Remove it here and reinstate it after setting the origin. d->v->ConstrainClippingRegion(NULL); } - OriginX = x; - OriginY = y; + OriginX = x - d->Client.x1; + OriginY = y - d->Client.y1; #if LOGGING printf("%p.SetOrigin=%i,%i (%i,%i) = %i,%i\n", this, x, y, d->Client.x1, d->Client.y1, d->Client.x1 - OriginX, d->Client.y1 - OriginY); #endif d->v->SetOrigin( d->Client.x1 - OriginX, d->Client.y1 - OriginY); if (d->Client.Valid()) { // Reset the clipping region related to the origin. auto clp = d->Client.ZeroTranslate(); clp.Offset(OriginX, OriginY); d->v->ClipToRect(clp); } } void LScreenDC::SetClient(LRect *c) { VIEW_CHECK() if (c) { d->Client = *c; OriginX += d->Client.x1; OriginY += d->Client.y1; #if LOGGING printf("SetClient(%s)\n", d->Client.GetStr()); #endif d->v->SetOrigin(OriginX, OriginY); auto clp = d->Client.ZeroTranslate(); // clp.Offset(OriginX, OriginY); d->v->ClipToRect(clp); /* BRegion r; d->v->GetClippingRegion(&r); LRect lr = r.Frame(); printf("SetClient %s = %s (%i,%i)\n", c->GetStr(), lr.GetStr(), OriginX, OriginY); */ } else { d->v->ConstrainClippingRegion(NULL); d->v->SetOrigin(OriginX = 0, OriginX = 0); d->Client.ZOff(-1, -1); #if LOGGING printf("SetClient()\n"); #endif } } LRect *LScreenDC::GetClient() { return &d->Client; } uint LScreenDC::LineStyle() { return LSurface::LineStyle(); } uint LScreenDC::LineStyle(uint32_t Bits, uint32_t Reset) { return LSurface::LineStyle(Bits); } int LScreenDC::GetFlags() { return 0; } LRect LScreenDC::ClipRgn() { return Clip; } LRect LScreenDC::ClipRgn(LRect *c) { LRect Prev = Clip; if (c) { Clip = *c; d->v->ClipToRect(Clip); } else { Clip.ZOff(-1, -1); d->v->ConstrainClippingRegion(NULL); } return Prev; } COLOUR LScreenDC::Colour() { return d->Col.Get(GetBits()); } COLOUR LScreenDC::Colour(COLOUR c, int Bits) { auto b = Bits ? Bits : GetBits(); d->Col.Set(c, b); return Colour(d->Col).Get(b); } LColour LScreenDC::Colour(LColour c) { LColour Prev = d->Col; d->Col = c; if (d->v) { d->v->SetLowColor(c); d->v->SetHighColor(c); } else LgiTrace("%s:%i - No view.\n", _FL); return Prev; } int LScreenDC::Op(int ROP, NativeInt Param) { return GDC_SET; // not supported.. } int LScreenDC::Op() { return GDC_SET; // not supported.. } int LScreenDC::X() { return d->Client.Valid() ? d->Client.X() : d->x; } int LScreenDC::Y() { return d->Client.Valid() ? d->Client.Y() : d->y; } int LScreenDC::GetBits() { return d->Bits; } LPalette *LScreenDC::Palette() { return NULL; // not supported. } void LScreenDC::Palette(LPalette *pPal, bool bOwnIt) { } void LScreenDC::Set(int x, int y) { VIEW_CHECK() d->v->StrokeLine(BPoint(x, y), BPoint(x, y)); } COLOUR LScreenDC::Get(int x, int y) { return 0; } void LScreenDC::HLine(int x1, int x2, int y) { VIEW_CHECK() d->v->StrokeLine(BPoint(x1, y), BPoint(x2, y)); } void LScreenDC::VLine(int x, int y1, int y2) { VIEW_CHECK() d->v->StrokeLine(BPoint(x, y1), BPoint(x, y2)); } void LScreenDC::Line(int x1, int y1, int x2, int y2) { VIEW_CHECK() d->v->StrokeLine(BPoint(x1, y1), BPoint(x2, y2)); } void LScreenDC::Circle(double cx, double cy, double radius) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), radius, radius, 0, 360); } void LScreenDC::FilledCircle(double cx, double cy, double radius) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), radius, radius, 0, 360); } void LScreenDC::Arc(double cx, double cy, double radius, double start, double end) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), radius, radius, start, end); } void LScreenDC::FilledArc(double cx, double cy, double radius, double start, double end) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), radius, radius, start, end); } void LScreenDC::Ellipse(double cx, double cy, double x, double y) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), x, y, 0, 360); } void LScreenDC::FilledEllipse(double cx, double cy, double x, double y) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), x, y, 0, 360); } void LScreenDC::Box(int x1, int y1, int x2, int y2) { VIEW_CHECK() d->v->StrokeRect(LRect(x1, y1, x2, y2)); } void LScreenDC::Box(LRect *a) { VIEW_CHECK() if (a) d->v->StrokeRect(*a); else Box(0, 0, X()-1, Y()-1); } void LScreenDC::Rectangle(int x1, int y1, int x2, int y2) { VIEW_CHECK() if (x2 >= x1 && y2 >= y1) { // auto o = d->v->Origin(); // printf("Rect %i,%i,%i,%i %g,%g\n", x1, y1, x2, y2, o.x, o.y); d->v->FillRect(LRect(x1, y1, x2, y2)); } } void LScreenDC::Rectangle(LRect *a) { VIEW_CHECK() LRect r = a ? *a : Bounds(); BRect br(r.x1, r.y1, r.x2, r.y2); d->v->FillRect(br); } void LScreenDC::Polygon(int Points, LPoint *Data) { VIEW_CHECK() if (!Data || Points <= 0) return; } void LScreenDC::Blt(int x, int y, LSurface *Src, LRect *a) { VIEW_CHECK() if (!Src) { LgiTrace("%s:%i - No source.\n", _FL); return; } if (Src->IsScreen()) { LgiTrace("%s:%i - Can't do screen->screen blt.\n", _FL); return; } BBitmap *bmp = Src->GetBitmap(); if (!bmp) { LAssert(!"no bmp."); LgiTrace("%s:%i - No bitmap.\n", _FL); return; } d->v->SetDrawingMode(B_OP_ALPHA); d->v->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); if (a) { BRect bitmapRect = *a; BRect viewRect(x, y, x + a->X() - 1, y + a->Y() - 1); d->v->DrawBitmap(bmp, bitmapRect, viewRect); #if 0 printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n", bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(), viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(), a->X(), a->Y()); #endif } else { BRect bitmapRect = bmp->Bounds(); BRect viewRect(x, y, x + Src->X() - 1, y + Src->Y() - 1); d->v->DrawBitmap(bmp, bitmapRect, viewRect); #if 0 printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n", bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(), viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(), x, y); #endif } d->v->SetDrawingMode(B_OP_COPY); } void LScreenDC::StretchBlt(LRect *Dest, LSurface *Src, LRect *s) { VIEW_CHECK() LAssert(0); } void LScreenDC::Bezier(int Threshold, LPoint *Pt) { VIEW_CHECK() LAssert(0); } void LScreenDC::FloodFill(int x, int y, int Mode, COLOUR Border, LRect *Bounds) { VIEW_CHECK() LAssert(0); }