diff --git a/include/lgi/common/LiteHtmlView.h b/include/lgi/common/LiteHtmlView.h
--- a/include/lgi/common/LiteHtmlView.h
+++ b/include/lgi/common/LiteHtmlView.h
@@ -1,22 +1,25 @@
#pragma once
class LiteHtmlView :
public LLayout
{
protected:
struct LiteHtmlViewPriv *d;
public:
LiteHtmlView(int id);
~LiteHtmlView();
bool SetUrl(LString url);
+ // Events:
+ virtual void OnNavigate(LString url);
+
// LLayout impl
void OnAttach() override;
LCursor GetCursor(int x, int y) override;
void OnPaint(LSurface *pDC) override;
int OnNotify(LViewI *c, LNotification n) override;
void OnMouseClick(LMouse &m) override;
void OnMouseMove(LMouse &m) override;
};
diff --git a/src/common/Gdc2/Font/FontSystem.cpp b/src/common/Gdc2/Font/FontSystem.cpp
--- a/src/common/Gdc2/Font/FontSystem.cpp
+++ b/src/common/Gdc2/Font/FontSystem.cpp
@@ -1,767 +1,767 @@
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Library.h"
#include "lgi/common/LibraryUtils.h"
#if defined(LGI_STATIC)
#undef HAS_ICONV
#endif
#define DEBUG_ICONV_LOG 0
#if HAS_ICONV
//
// Get 'iconv.h' from http://www.gnu.org/software/libiconv
// Current download at time of writing:
// http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz
//
// Then add whatever include path to your project or development
// settings. Otherwise you can build without extended charset
// support by changing the HAS_ICONV define in Lgi.h to '0'
//
// Linux should always build with iconv, on windows you may or
// may not want to bother depending on what sort of app your
// writing.
//
#include "iconv.h"
#if defined(WIN32)
typedef const char IconvChar;
#else
typedef char IconvChar;
#endif
#endif
/////////////////////////////////////////////////////////////////////
// Private growable class for binary compatability
class LFontSystemPrivate : public LLibrary
{
public:
bool DefaultGlyphSub;
int Used, Refs;
bool FontTableLoaded;
bool SubSupport;
bool CheckedConfig;
bool LibCheck;
#ifdef __GTK_H__
Gtk::PangoFontMap *Map;
Gtk::PangoContext *Ctx;
LFontSystemPrivate()
{
Map = Gtk::pango_cairo_font_map_get_default();
if (!Map)
LAssert(!"pango_cairo_font_map_get_default failed.\n");
Ctx = Gtk::pango_cairo_font_map_create_context((Gtk::PangoCairoFontMap*)Map);
if (!Ctx)
LAssert(!"pango_cairo_font_map_create_context failed.\n");
}
#endif
#if HAS_ICONV
#ifdef WIN32
DynFunc2(iconv_t, libiconv_open, const char*, tocode, const char*, fromcode);
DynFunc5(size_t, libiconv, iconv_t, cd, IconvChar**, inbuf, size_t*, inbytesleft, char**, outbuf, size_t*, outbytesleft);
DynFunc1(int, libiconv_close, iconv_t, cd);
#elif !defined(MAC)
// Use glibc I guess
iconv_t libiconv_open(const char *tocode, const char *fromcode)
{
return ::iconv_open(tocode, fromcode);
}
size_t libiconv(iconv_t cd, IconvChar** inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
{
return ::iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft);
}
int libiconv_close(iconv_t cd)
{
return ::iconv_close(cd);
}
bool IsLoaded()
{
return true;
}
#endif
#endif // HAS_ICONV
};
/////////////////////////////////////////////////////////////////////
static bool FontSystemDone = false;
LFontSystem *LFontSystem::Me = 0;
LFontSystem *LFontSystem::Inst()
{
if (!Me && !FontSystemDone)
new LFontSystem;
return Me;
}
LFontSystem::LFontSystem()
{
Me = this;
d = new LFontSystemPrivate;
// Glyph sub setup
int Os = LGetOs();
d->SubSupport = (Os == LGI_OS_LINUX) ||
(Os == LGI_OS_WIN64) ||
(Os == LGI_OS_WIN32)
#ifdef __GTK_H__
|| (Os == LGI_OS_MAC_OS_X)
#endif
; // && Rev == 0); // WinXP does it's own glyph substitution
d->DefaultGlyphSub = d->SubSupport;
d->CheckedConfig = false;
d->FontTableLoaded = false;
d->Used = 1; // the '0th' font spot isn't used
// because Lut[Char] == 0 means no font
// available
d->Refs = 0;
d->LibCheck = false;
ZeroObj(Lut); // Clear the table to 'no font'
ZeroObj(Font); // Initialize the list of fonts to empty
}
LFontSystem::~LFontSystem()
{
// Clean up all our resources
for (int i=0; iUsed; i++)
{
DeleteObj(Font[i]);
}
DeleteObj(d);
Me = 0;
FontSystemDone = true;
}
#ifdef __GTK_H__
Gtk::PangoFontMap *LFontSystem::GetFontMap()
{
return d->Map;
}
Gtk::PangoContext *LFontSystem::GetContext()
{
return d->Ctx;
}
#endif
bool LFontSystem::GetGlyphSubSupport()
{
return d->SubSupport;
}
bool LFontSystem::GetDefaultGlyphSub()
{
if (!d->CheckedConfig && LAppInst)
{
auto GlyphSub = LAppInst->GetConfig(LApp::CfgFontsGlyphSub);
if (GlyphSub)
d->DefaultGlyphSub = atoi(GlyphSub) != 0;
d->CheckedConfig = true;
}
return d->DefaultGlyphSub;
}
void LFontSystem::SetDefaultGlyphSub(bool i)
{
if (d->SubSupport)
d->DefaultGlyphSub = i;
}
#ifdef WINNATIVE
int CALLBACK _EnumFonts(ENUMLOGFONT FAR *lpelf,
NEWTEXTMETRIC FAR *lpntm,
int FontType,
LPARAM lParam)
{
LString::Array *p = (LString::Array*) lParam;
if (p)
p->New() = lpelf->elfLogFont.lfFaceName;
return true;
}
#endif
int StringSort(LString *a, LString *b)
{
if (a && b) return stricmp(*a, *b);
return 0;
}
bool LFontSystem::EnumerateFonts(LString::Array &Fonts)
{
Fonts.SetFixedLength(false);
if (!AllFonts.Length())
{
#if defined WINNATIVE
HDC hDC = CreateCompatibleDC(NULL);
if (hDC)
{
EnumFontFamilies( hDC,
NULL,
(FONTENUMPROC) _EnumFonts,
(LPARAM) &AllFonts);
DeleteDC(hDC);
}
#elif defined __GTK_H__
Gtk::PangoFontFamily **families;
int n_families;
Gtk::PangoFontMap * fontmap;
fontmap = Gtk::pango_cairo_font_map_get_default();
Gtk::pango_font_map_list_families (fontmap, & families, & n_families);
for (int i = 0; i < n_families; i++)
{
Gtk::PangoFontFamily * family = families[i];
const char * family_name;
family_name = Gtk::pango_font_family_get_name (family);
AllFonts.New() = family_name;
}
Gtk::g_free (families);
#elif LGI_COCOA
auto avail = [[NSFontManager sharedFontManager] availableFontFamilies];
for (NSString *s in avail)
{
AllFonts.New() = [s UTF8String];
}
#elif LGI_CARBON
CFArrayRef fontFamilies = CTFontManagerCopyAvailableFontFamilyNames();
if (fontFamilies)
{
for(CFIndex i = 0; i < CFArrayGetCount(fontFamilies); i++)
{
CFStringRef fontName = (CFStringRef) CFArrayGetValueAtIndex(fontFamilies, i);
if (fontName)
AllFonts.New() = fontName;
}
CFRelease(fontFamilies);
}
#elif HAIKU
int32 families = count_font_families();
for (int32 i=0; iLibCheck = false;
}
bool LFontSystem::HasIconv(bool Quiet)
{
if (d->IsLoaded())
return true;
bool Status = false;
if (!d->LibCheck)
{
d->LibCheck = true;
#ifdef WINDOWS
auto LibName = "iconv-2." LGI_LIBRARY_EXT;
#else
auto LibName = "libiconv." LGI_LIBRARY_EXT;
#endif
Status = d->Load(LibName);
if (!Status && !Quiet)
{
if (!NeedsCapability("libiconv"))
{
static bool Warn = true;
if (Warn)
{
Warn = false;
LAssert(!"Iconv is not available");
}
}
}
}
return Status;
}
// This converts a normal charset to an Apple encoding ID
#if defined LGI_CARBON
static CFStringEncoding CharsetToEncoding(const char *cs)
{
CFStringRef InputCs = CFStringCreateWithCString(0, cs, kCFStringEncodingUTF8);
if (!InputCs)
return kCFStringEncodingUTF8; // Um what to do here?
CFStringEncoding enc = CFStringConvertIANACharSetNameToEncoding(InputCs);
CFRelease(InputCs);
return enc;
}
#elif defined LGI_COCOA
static CFStringEncoding CharsetToEncoding(const char *cs)
{
LString s = cs;
auto r = s.CreateStringRef();
auto e = CFStringConvertIANACharSetNameToEncoding(r);
CFRelease(r);
return e;
}
#endif
ssize_t LFontSystem::IconvConvert(const char *OutCs, LStreamI *Out, const char *InCs, const char *&In, ssize_t InLen)
{
LAssert(InLen > 0);
if (!Out || !In)
return 0;
#if defined(MAC)
char Buf[2 << 10];
CFStringEncoding InEnc = CharsetToEncoding(InCs);
CFStringEncoding OutEnc = CharsetToEncoding(OutCs);
if (InEnc != kCFStringEncodingInvalidId &&
OutEnc != kCFStringEncodingInvalidId)
{
CFStringRef r = CFStringCreateWithBytes(0, (const UInt8 *)In, InLen, InEnc, false);
if (r)
{
CFRange g = { 0, CFStringGetLength(r) };
CFIndex used = 0;
CFIndex ret;
while ((ret = CFStringGetBytes(r, g, OutEnc, '?', false, (UInt8*)Buf, sizeof(Buf), &used)) > 0 && g.length > 0)
{
// char16 *b = (char16*)Buf;
Out->Write(Buf, used);
g.location += ret;
g.length -= ret;
}
CFRelease(r);
}
else return 0;
}
else return 0;
#elif HAS_ICONV
if (!HasIconv(false))
{
return 0;
}
char Buf[2 << 10] = {0};
iconv_t Conv;
if ((NativeInt)(Conv = d->libiconv_open(OutCs, InCs)) >= 0)
{
char *i = (char*)In;
LAssert((NativeInt)Conv != 0xffffffff);
while (InLen)
{
char *o = (char*)Buf;
size_t OutLen = sizeof(Buf);
ssize_t OldInLen = InLen;
size_t InSz = InLen;
#if DEBUG_ICONV_LOG
printf("iconv %s,%p,%i->%s,%p,%i",
InCs, In, (int)InSz,
OutCs, Out, (int)sizeof(Buf));
#endif
ssize_t s = d->libiconv(Conv, (IconvChar**)&i, &InSz, &o, &OutLen);
#if DEBUG_ICONV_LOG
printf(" = %i\n", (int)s);
#endif
InLen = InSz;
Out->Write((uchar*)Buf, sizeof(Buf) - OutLen);
if (OldInLen == InLen) break;
}
d->libiconv_close(Conv);
}
else
{
LgiTrace("Iconv won't load.\n");
return 0;
}
#endif
return 1;
}
ssize_t LFontSystem::IconvConvert(const char *OutCs, char *Out, ssize_t OutLen, const char *InCs, const char *&In, ssize_t InLen)
{
ssize_t Status = 0;
if (!Out || !In || !HasIconv(false))
return 0;
#if defined(MAC)
CFStringEncoding InEnc = CharsetToEncoding(InCs);
CFStringEncoding OutEnc = CharsetToEncoding(OutCs);
if (InEnc != kCFStringEncodingInvalidId &&
OutEnc != kCFStringEncodingInvalidId)
{
CFStringRef r = CFStringCreateWithBytes(0, (const UInt8 *)In, InLen, InEnc, false);
if (r)
{
CFRange g = { 0, CFStringGetLength(r) };
CFIndex ret = CFStringGetBytes(r, g, OutEnc, '?', false, (UInt8*)Out, OutLen, 0);
CFRelease(r);
return ret;
}
}
#elif HAS_ICONV
// Set locale yet?
static bool sl = false;
if (!sl)
{
sl = true;
setlocale(LC_ALL, "");
}
// Iconv conversion
iconv_t Conv;
if ((Conv = d->libiconv_open(OutCs, InCs)) != NULL)
{
size_t InSz = InLen;
size_t OutSz = OutLen;
char *o = Out;
char *i = (char*)In;
// Convert
char *Start = o;
#if DEBUG_ICONV_LOG
printf("iconv %s,%p,%i->%s,%p,%i",
InCs, In, (int)InSz,
OutCs, Out, (int)OutSz);
#endif
ssize_t s = d->libiconv(Conv, (IconvChar**)&i, &InSz, &o, &OutSz);
#if DEBUG_ICONV_LOG
printf(" = %i\n", (int)s);
#endif
InLen = InSz;
OutLen = OutSz;
d->libiconv_close(Conv);
In = (const char*)i;
Status = o - Out;
}
else
{
LgiTrace("Iconv not present/won't load.\n");
}
#endif
return Status;
}
LFont *LFontSystem::GetBestFont(char *Str)
{
LFont *MatchingFont = 0;
if (d->SubSupport)
{
char16 *s = Utf8ToWide(Str);
if (s)
{
// Make list of possible fonts
List Possibles;
char16 *i;
for (i = s; *i; i++)
{
LFont *Font = GetGlyph(*i, LSysFont);
if (Font)
{
bool Has = false;
for (auto h: Possibles)
{
if (h == Font)
{
Has = true;
break;
}
}
if (!Has)
{
Possibles.Insert(Font);
}
}
}
// Choose best match amongst possibles
int MatchingChars = 0;
for (auto h: Possibles)
{
int Chars = 0;
for (i = s; *i; i++)
{
if (h->GetGlyphMap() &&
_HasUnicodeGlyph(h->GetGlyphMap(), *i))
{
Chars++;
}
}
if (!MatchingFont ||
Chars > MatchingChars)
{
MatchingFont = h;
}
}
DeleteArray(s);
}
}
return MatchingFont;
}
typedef LHashTbl,int> FontMap;
bool LFontSystem::AddFont(LAutoPtr Fnt)
{
if (!Fnt)
return false;
if (d->Used >= CountOf(Font))
return false;
Fnt->Create();
auto *Map = Fnt->GetGlyphMap();
if (Map)
{
uint8_t Used = d->Used;
// Insert all the characters of this font into the LUT
// so that we can map from a character back to the font
for (int k=0; k<=MAX_UNICODE; k += 8)
{
// unroll the loop for maximum speed..
uint8_t m = Map[k >> 3];
#define TestLut(i) \
if (!Lut[k+i] && (m & (1 << i))) \
Lut[k+i] = Used;
TestLut(0);
TestLut(1);
TestLut(2);
TestLut(3);
TestLut(4);
TestLut(5);
TestLut(6);
TestLut(7);
}
}
Font[d->Used++] = Fnt.Release();
return true;
}
LFont *LFontSystem::GetGlyph(uint32_t u, LFont *UserFont)
{
if (u > MAX_UNICODE || !UserFont)
{
LAssert(!"Invalid character");
return 0;
}
// Check app font
if (!d->SubSupport ||
(UserFont->GetGlyphMap() &&
_HasUnicodeGlyph(UserFont->GetGlyphMap(), u)))
{
return UserFont;
}
// Check LUT
LFont *Has = 0;
if (Lut[u])
{
Has = Font[Lut[u]];
LAssert(Has != NULL);
if (!Has)
{
LgiTrace("%s:%i - Font table missing pointer. u=%i Lut[u]=%i\n", _FL, u, Lut[u]);
Has = UserFont;
}
}
else if (d->Used < 255 && !d->FontTableLoaded)
{
// Add fonts to Lut...
if (!SubFonts.Length())
{
#if LGI_EXCEPTIONS
try
{
#endif
FontMap Pref(0, 0);
if (LFontSystem::Inst()->EnumerateFonts(SubFonts))
{
// Reorder font list to prefer certain known as good fonts or
// avoid certain bad fonts.
if (LGetOs() == LGI_OS_WIN32 ||
LGetOs() == LGI_OS_WIN64)
{
Pref.Add("Microsoft Sans Serif", 1);
Pref.Add("Arial Unicode MS", 1);
Pref.Add("Verdana", 1);
Pref.Add("Tahoma", 1);
Pref.Add("Bookworm", -1);
Pref.Add("Christmas Tree", -1);
Pref.Add("MingLiU", -1);
}
if (LGetOs() == LGI_OS_LINUX)
{
// Windows fonts are much better than anything Linux
// has to offer.
Pref.Add("Verdana", 1);
Pref.Add("Tahoma", 1);
Pref.Add("Arial Unicode MS", 1);
// Most linux fonts suck... and the rest aren't much
// good either
Pref.Add("AR PL *", -1);
Pref.Add("Baekmuk *", -1);
Pref.Add("console8*", -1);
Pref.Add("Courier*", -1);
Pref.Add("Fangsong*", -1);
Pref.Add("Kochi*", -1);
Pref.Add("MiscFixed", -1);
Pref.Add("Serto*", -1);
Pref.Add("Standard Symbols*", -1);
Pref.Add("Nimbus*", -1);
}
// Prefer these fonts...
SubFonts.Sort([Pref](auto a, auto b)
{
int ap = Pref.Find(*a);
int bp = Pref.Find(*b);
return bp - ap;
});
// Delete fonts prefixed with '@' to the end, as they are for
// vertical rendering... and aren't suitable for what LGI uses
// fonts for.
for (unsigned i=0; iUsed;
while (SubFonts.Length() > 0 && (LCurrentTime() - Start) < 10)
{
LString f = SubFonts[0];
SubFonts.DeleteAt(0, true);
if (d->Used >= CountOf(Font))
{
// No more space
SubFonts.Empty();
break;
}
LAutoPtr Fnt(new LFont);
if (Fnt)
{
*Fnt.Get() = *UserFont;
Fnt->Face(f);
if (AddFont(Fnt))
{
LFont *Prev = Font[d->Used - 1];
auto PrevMap = Prev->GetGlyphMap();
if (PrevMap && _HasUnicodeGlyph(Prev->GetGlyphMap(), u))
{
Has = Prev;
LAssert(Has != NULL);
break;
}
}
}
}
- LgiTrace("Loaded %i fonts for glyph sub.\n", d->Used - Used);
+ // LgiTrace("Loaded %i fonts for glyph sub.\n", d->Used - Used);
#if LGI_EXCEPTIONS
}
catch (...)
{
LgiTrace("%s:%i - Glyph search crashed.\n", _FL);
}
#endif
if (!SubFonts.Length())
{
d->FontTableLoaded = true;
}
}
return Has;
}
diff --git a/src/common/LiteHtml/LiteHtmlView.cpp b/src/common/LiteHtml/LiteHtmlView.cpp
--- a/src/common/LiteHtml/LiteHtmlView.cpp
+++ b/src/common/LiteHtml/LiteHtmlView.cpp
@@ -1,416 +1,566 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/Box.h"
#include "lgi/common/Edit.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/Layout.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/Menu.h"
#include "lgi/common/LiteHtmlView.h"
+#include "lgi/common/Thread.h"
+#include "lgi/common/Http.h"
#undef min
#undef max
#include "litehtml/html.h"
#define NOT_IMPL \
- printf("%s:%i - %s not impl.\n", _FL, __func__); \
+ LgiTrace("%s:%i - %s not impl.\n", _FL, __func__); \
LAssert(!"not impl");
+struct LiteHtmlViewPriv :
+ public litehtml::document_container,
+ public LCancel
+{
+ struct NetworkThread :
+ public LThread,
+ public LCancel
+ {
+ using TCallback = std::function;
+
+ LView *view = NULL;
+ LString url;
+ TCallback callback;
-struct LiteHtmlViewPriv :
- public litehtml::document_container
-{
+ NetworkThread(LView *View, const char *Url, TCallback cb) :
+ LThread("LiteHtmlViewPriv.NetworkThread")
+ {
+ view = View;
+ url = Url;
+ callback = std::move(cb);
+ Run();
+ }
+
+ ~NetworkThread()
+ {
+ Cancel();
+ WaitForExit();
+ }
+
+ int Main()
+ {
+ LStringPipe p;
+ LString err;
+
+ auto result = LgiGetUri(this, &p, &err, url);
+ // LgiTrace("net(%s) = %i\n", url.Get(), result);
+ view->RunCallback([result, data=p.NewLStr(), this]()
+ {
+ callback(result, data);
+ });
+
+ return 0;
+ }
+ };
+
+ struct Image
+ {
+ int status = -1;
+ LString uri;
+ LAutoPtr img;
+ };
+
+ LArray threads;
LiteHtmlView *view = NULL;
LWindow *wnd = NULL;
litehtml::document::ptr doc;
LRect client;
LString cursorName;
+ LString currentUrl;
LHashTbl, LFont*> fontMap;
+ LHashTbl, Image*> imageCache;
LiteHtmlViewPriv(LiteHtmlView *v) : view(v)
{
-
}
~LiteHtmlViewPriv()
{
+ Cancel();
+ threads.DeleteObjects();
+
// Do this before releasing other owned objects, like the fontMap.
doc.reset();
+
+ // Clean up caches
+ imageCache.DeleteObjects();
+ fontMap.DeleteObjects();
}
LColour Convert(const litehtml::web_color &c)
{
return LColour(c.red, c.green, c.blue, c.alpha);
}
LRect Convert(const litehtml::position &p)
{
LRect r(p.x, p.y, p.x + p.width - 1, p.y + p.height - 1);
return r;
}
void UpdateScreen(litehtml::position::vector &redraw)
{
if (redraw.size() > 0)
{
// FIXME: should invalidate just the dirty regions...
view->Invalidate();
}
}
-
litehtml::uint_ptr create_font( const char* faceName,
int size,
int weight,
litehtml::font_style italic,
unsigned int decoration,
litehtml::font_metrics *fm)
{
litehtml::uint_ptr hnd;
do
{
hnd = LRand(10000);
}
while (fontMap.Find(hnd) != NULL);
LFont *fnt = new LFont;
bool status = fnt->Create(faceName, LCss::Len(LCss::LenPt, size) );
if (!status)
LgiTrace("%s:%i - failed to create font(%s,%i)\n", _FL, faceName, size);
fontMap.Add(hnd, fnt);
if (fm)
{
LDisplayString ds(fnt, "x");
fm->height = fnt->GetHeight();
fm->ascent = fnt->Ascent();
fm->descent = fnt->Descent();
fm->x_height = ds.Y();
fm->draw_spaces = false;
}
return hnd;
}
void delete_font(litehtml::uint_ptr hFont)
{
auto fnt = fontMap.Find(hFont);
if (fnt)
{
delete fnt;
fontMap.Delete(hFont);
}
}
int text_width(const char* text, litehtml::uint_ptr hFont)
{
auto fnt = fontMap.Find(hFont);
if (!fnt)
return 0;
LDisplayString ds(fnt, text);
return ds.X();
}
void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos)
{
auto pDC = (LSurface*)hdc;
auto Fnt = fontMap.Find(hFont);
if (!pDC || !Fnt)
return;
LDisplayString ds(Fnt, text);
Fnt->Fore(Convert(color));
Fnt->Transparent(true);
ds.Draw(pDC, pos.x, pos.y);
}
int pt_to_px(int pt) const
{
auto dpi = wnd->GetDpi();
int px = pt * dpi.x / 72;
return px;
}
int get_default_font_size() const
{
return 13; // LSysFont->PointSize();
}
const char *get_default_font_name() const
{
return LSysFont->Face();
}
void draw_list_marker(litehtml::uint_ptr hdc, const litehtml::list_marker &marker)
{
auto pDC = (LSurface*)hdc;
auto Fnt = fontMap.Find(marker.font);
if (!pDC)
return;
pDC->Colour(Convert(marker.color));
switch (marker.marker_type)
{
case litehtml::list_style_type_disc:
{
pDC->FilledCircle(marker.pos.x, marker.pos.y, 3);
break;
}
default:
{
- NOT_IMPL;
+ LgiTrace("%s:%i - draw_list_marker %i not impl\n", marker.marker_type);
break;
}
}
}
void load_image(const char *src, const char *baseurl, bool redraw_on_ready)
{
- NOT_IMPL
+ if (!imageCache.Find(src))
+ {
+ LgiTrace("load_image(%s,%s)\n", src, baseurl);
+ if (auto i = new Image)
+ {
+ i->uri = src;
+ imageCache.Add(src, i);
+ threads.Add(new NetworkThread(view, src, [i, this](auto status, auto data)
+ {
+ auto leaf = i->uri.SplitDelimit("/").Last();
+ if (status)
+ {
+ LMemStream s(data.Get(), data.Length(), false);
+ i->status = i->img.Reset(GdcD->Load(&s, leaf));
+ LgiTrace("load_image(%s) load status=%i\n", i->uri.Get(), i->status);
+ }
+ else
+ {
+ i->status = false;
+ LgiTrace("load_image(%s) network status=%i\n", i->uri.Get(), i->status);
+ }
+
+ view->Invalidate();
+ }));
+ }
+ }
}
void get_image_size(const char *src, const char *baseurl, litehtml::size &sz)
{
- NOT_IMPL
+ if (auto i = imageCache.Find(src))
+ {
+ if (i->img)
+ {
+ sz.width = i->img->X();
+ sz.height = i->img->Y();
+ }
+ }
}
void draw_background(litehtml::uint_ptr hdc, const std::vector &background)
{
auto pDC = (LSurface*)hdc;
for (auto b: background)
{
- pDC->Colour(Convert(b.color));
auto rc = Convert(b.border_box);
- pDC->Rectangle(&rc);
+ if (!b.image.empty())
+ {
+ if (auto i = imageCache.Find(b.image.c_str() ))
+ {
+ if (i->img)
+ {
+ pDC->Blt(b.position_x, b.position_y, i->img);
+ }
+ else LgiTrace("%s:%i - draw_background(img=%s) img no surface\n", _FL, b.image.c_str());
+ }
+ else LgiTrace("%s:%i - draw_background(img=%s) img not found\n", _FL, b.image.c_str());
+ }
+ else
+ {
+ pDC->Colour(Convert(b.color));
+ pDC->Rectangle(&rc);
+ }
}
}
void draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
{
auto pDC = (LSurface*)hdc;
auto drawEdge = [&](const litehtml::border &b, int x, int y, int dx, int dy, int ix, int iy)
{
pDC->Colour(Convert(b.color));
for (int i=0; iLine(x, y, x+dx, y+dy);
x += ix;
y += iy;
}
};
int x2 = draw_pos.width - 1;
int y2 = draw_pos.height - 1;
drawEdge(borders.left, draw_pos.x, draw_pos.y, 0, y2, 1, 0);
drawEdge(borders.top, draw_pos.x, draw_pos.y, x2, 0, 0, 1);
drawEdge(borders.right, draw_pos.x+x2, draw_pos.y, 0, y2, -1, 0);
drawEdge(borders.bottom, draw_pos.x, draw_pos.y+y2, x2, 0, 0, -1);
}
void set_caption(const char* caption)
{
wnd->Name(caption);
}
void set_base_url(const char* base_url)
{
NOT_IMPL
}
void link(const std::shared_ptr& doc, const litehtml::element::ptr& el)
{
NOT_IMPL
}
void on_anchor_click(const char* url, const litehtml::element::ptr& el)
{
- NOT_IMPL
+ if (!url)
+ return;
+
+ threads.Add(new NetworkThread(view, url, [this, url=LString(url)](auto status, auto data)
+ {
+ currentUrl = url;
+ doc = litehtml::document::createFromString(data, this);
+ view->OnNavigate(url);
+ view->Invalidate();
+ }));
}
void set_cursor(const char* cursor)
{
cursorName = cursor;
}
void transform_text(litehtml::string& text, litehtml::text_transform tt)
{
NOT_IMPL
}
void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl)
{
- NOT_IMPL
+ LUri cur(currentUrl);
+ LUri u(url.c_str());
+ LString newUri;
+
+ if (u.sProtocol && u.sHost)
+ {
+ // Absolute URI
+ newUri = url.c_str();
+ }
+ else if (char *s = u.sPath.Get())
+ {
+ // Relative URI
+ if (*s == '/')
+ cur.sPath.Empty();
+ cur.sPath = u.sPath;
+ newUri = cur.ToString();
+ }
+
+ if (newUri)
+ {
+ LStringPipe out;
+ LString err;
+ if (LgiGetUri(this, &out, &err, newUri))
+ {
+ text = out.NewLStr().Get();
+ }
+ else LgiTrace("%s:%i - error: LgiGetUri(%s)=%s (currentUrl=%s)\n",
+ _FL, newUri.Get(), err.Get(), currentUrl.Get());
+ }
+ else LgiTrace("%s:%i - error: no uri for loading css.\n", _FL);
}
void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius)
{
- NOT_IMPL
+ // NOT_IMPL
}
void del_clip()
{
- NOT_IMPL
+ // NOT_IMPL
}
void get_client_rect(litehtml::position &out) const
{
out = litehtml::position(client.x1, client.y1, client.X(), client.Y());
}
litehtml::element::ptr create_element( const char* tag_name,
const litehtml::string_map& attributes,
const std::shared_ptr& doc)
{
return NULL;
}
void get_media_features(litehtml::media_features& media) const
{
// NOT_IMPL
}
void get_language(litehtml::string& language, litehtml::string& culture) const
{
NOT_IMPL
}
/*
litehtml::string resolve_color(const litehtml::string &color) const
{
NOT_IMPL
return litehtml::string();
}
*/
};
/////////////////////////////////////////////////////////////////
LiteHtmlView::LiteHtmlView(int id)
{
d = new LiteHtmlViewPriv(this);
SetId(id);
}
LiteHtmlView::~LiteHtmlView()
{
delete d;
}
void LiteHtmlView::OnAttach()
{
d->wnd = GetWindow();
}
LCursor LiteHtmlView::GetCursor(int x, int y)
{
if (d->cursorName == "pointer")
return LCUR_PointingHand;
return LCUR_Normal;
}
+void LiteHtmlView::OnNavigate(LString url)
+{
+ d->currentUrl = url;
+ Invalidate();
+}
+
bool LiteHtmlView::SetUrl(LString url)
{
if (LFileExists(url))
{
auto html_text = LReadFile(url);
if (html_text)
{
+ d->currentUrl = url;
d->doc = litehtml::document::createFromString(html_text, d);
- Invalidate();
+ OnNavigate(url);
return d->doc != NULL;
}
}
return false;
}
void LiteHtmlView::OnPaint(LSurface *pDC)
{
#ifdef WINDOWS
LDoubleBuffer buf(pDC);
#endif
d->client = GetClient();
if (d->doc)
{
pDC->Colour(L_WORKSPACE);
pDC->Rectangle();
auto width = pDC->X();
int r = d->doc->render(width);
if (r)
{
auto width = d->doc->content_width();
auto height = d->doc->content_height();
if (height > Y())
{
SetScrollBars(false, true);
if (VScroll)
{
VScroll->SetRange(height);
VScroll->SetPage(Y());
}
}
litehtml::position clip(0, 0, pDC->X(), pDC->Y());
d->doc->draw((litehtml::uint_ptr)pDC, 0, VScroll?-VScroll->Value():0, &clip);
}
}
else
{
LLayout::OnPaint(pDC);
}
}
int LiteHtmlView::OnNotify(LViewI *c, LNotification n)
{
- // printf("OnNotify %i=%i, %i=%i\n", c->GetId(), IDC_VSCROLL, n.Type, LNotifyValueChanged);
+ // LgiTrace("OnNotify %i=%i, %i=%i\n", c->GetId(), IDC_VSCROLL, n.Type, LNotifyValueChanged);
if (c->GetId() == IDC_VSCROLL &&
n.Type == LNotifyValueChanged)
{
- // printf("Inval\n");
+ // LgiTrace("Inval\n");
Invalidate();
}
return LLayout::OnNotify(c, n);
}
void LiteHtmlView::OnMouseClick(LMouse &m)
{
if (!d->doc)
return;
int64_t sx, sy;
GetScrollPos(sx, sy);
litehtml::position::vector redraw_boxes;
if (m.IsContextMenu())
{
LSubMenu sub;
sub.AppendItem("notImpl: submenu", -1, false);
sub.Float(this, m);
}
else if (m.Left())
{
if (m.Down())
d->doc->on_lbutton_down(m.x+sx, m.y+sy, m.x, m.y, redraw_boxes);
else
d->doc->on_lbutton_up(m.x+sx, m.y+sy, m.x, m.y, redraw_boxes);
}
d->UpdateScreen(redraw_boxes);
}
void LiteHtmlView::OnMouseMove(LMouse &m)
{
if (!d->doc)
return;
int64_t sx, sy;
GetScrollPos(sx, sy);
litehtml::position::vector redraw_boxes;
d->doc->on_mouse_over(m.x+sx, m.y+sy, m.x, m.y, redraw_boxes);
d->UpdateScreen(redraw_boxes);
}
diff --git a/test/LiteHtml/CMakeLists.txt b/test/LiteHtml/CMakeLists.txt
--- a/test/LiteHtml/CMakeLists.txt
+++ b/test/LiteHtml/CMakeLists.txt
@@ -1,21 +1,23 @@
cmake_minimum_required(VERSION 3.18)
project(LgiLiteHtml)
set(LGI_LIB_ONLY TRUE)
add_subdirectory(../.. lgi)
add_subdirectory(../../../../../codelib/litehtml litehtml)
set(SRC
src/main.cpp
../../src/common/LiteHtml/LiteHtmlView.cpp
+ ../../src/common/Net/Http.cpp
+ ../../src/common/Net/OpenSSLSocket.cpp
../../src/common/Lgi/LgiMain.cpp)
if(WIN32)
set(APP_TYPE WIN32)
else()
set(APP_TYPE)
endif()
add_executable(LgiLiteHtml ${APP_TYPE} ${SRC})
target_link_libraries(LgiLiteHtml PUBLIC lgi litehtml)
\ No newline at end of file
diff --git a/test/LiteHtml/src/main.cpp b/test/LiteHtml/src/main.cpp
--- a/test/LiteHtml/src/main.cpp
+++ b/test/LiteHtml/src/main.cpp
@@ -1,69 +1,84 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/Box.h"
#include "lgi/common/Edit.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/Layout.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/Menu.h"
#include "lgi/common/LiteHtmlView.h"
const char *AppName = "LgiLiteHtml";
enum Ctrls
{
IDC_BOX = 100,
IDC_LOCATION,
IDC_BROWSER,
};
+class AppLiteHtmlView : public LiteHtmlView
+{
+public:
+ AppLiteHtmlView(int id) : LiteHtmlView(id)
+ {
+
+ }
+
+ void OnNavigate(LString url) override
+ {
+ GetWindow()->SetCtrlName(IDC_LOCATION, url);
+ LiteHtmlView::OnNavigate(url);
+ }
+};
+
class App : public LWindow
{
LBox *box = NULL;
LEdit *location = NULL;
LiteHtmlView *browser = NULL;
public:
App()
{
LRect r(200, 200, 1400, 1000);
SetPos(r);
Name(AppName);
MoveToCenter();
SetQuitOnClose(true);
if (Attach(0))
{
AddView(box = new LBox(IDC_BOX, true));
box->AddView(location = new LEdit(IDC_LOCATION, 0, 0, 100, 20));
- box->AddView(browser = new LiteHtmlView(IDC_BROWSER));
+ box->AddView(browser = new AppLiteHtmlView(IDC_BROWSER));
box->Value(LSysFont->GetHeight() + 8);
AttachChildren();
location->Focus(true);
Visible(true);
}
}
void SetUrl(LString s)
{
if (location)
location->Name(s);
if (browser)
browser->SetUrl(s);
}
void OnReceiveFiles(LArray &Files)
{
SetUrl(Files[0]);
}
};
int LgiMain(OsAppArguments &AppArgs)
{
LApp app(AppArgs, "application/x-lgi-litehtml");
if (app.IsOk())
{
app.AppWnd = new App;
app.Run();
}
return 0;
}