mirror of https://github.com/buggins/dlangui.git
rework resources destroy
This commit is contained in:
parent
5b358236f2
commit
e76394c5da
|
|
@ -43,7 +43,10 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
LinearLayout layout = new LinearLayout();
|
LinearLayout layout = new LinearLayout();
|
||||||
layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
|
layout.addChild((new TextWidget()).textColor(0x00802000).text("Text widget 0"));
|
||||||
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
|
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
|
||||||
layout.addChild((new Button()).text("Button1")); //.textColor(0x40FF4000)
|
layout.addChild((new Button("BTN1")).text("Button1")); //.textColor(0x40FF4000)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LinearLayout hlayout = new HorizontalLayout();
|
LinearLayout hlayout = new HorizontalLayout();
|
||||||
//hlayout.addChild((new Button()).text("<<")); //.textColor(0x40FF4000)
|
//hlayout.addChild((new Button()).text("<<")); //.textColor(0x40FF4000)
|
||||||
|
|
@ -62,13 +65,16 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40FFFF00));
|
vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40FFFF00));
|
||||||
layout.addChild(vlayout);
|
layout.addChild(vlayout);
|
||||||
|
|
||||||
layout.addChild((new Button()).textColor(0x000000FF).text("Button2"));
|
layout.addChild((new Button("BTN2")).textColor(0x000000FF).text("Button2"));
|
||||||
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
|
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
|
||||||
layout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)));
|
layout.addChild((new ImageWidget()).drawableId("exit").padding(Rect(5,5,5,5)));
|
||||||
layout.addChild((new TextWidget()).textColor(0xFF4000).text("Text widget2").padding(Rect(5,5,5,5)).margins(Rect(5,5,5,5)).backgroundColor(0xA0A0A0));
|
layout.addChild((new TextWidget()).textColor(0xFF4000).text("Text widget2").padding(Rect(5,5,5,5)).margins(Rect(5,5,5,5)).backgroundColor(0xA0A0A0));
|
||||||
layout.addChild((new Button()).textColor(0x000000FF).text("Button3").layoutHeight(FILL_PARENT));
|
layout.addChild((new Button("BTN3")).textColor(0x000000FF).text("Button3").layoutHeight(FILL_PARENT));
|
||||||
layout.addChild((new TextWidget()).textColor(0x004000).text("Text widget3 with very long text"));
|
layout.addChild((new TextWidget()).textColor(0x004000).text("Text widget3 with very long text"));
|
||||||
|
|
||||||
|
layout.childById("BTN1").onClickListener(delegate (Widget w) { Log.d("onClick ", w.id); return true; });
|
||||||
|
layout.childById("BTN2").onClickListener(delegate (Widget w) { Log.d("onClick ", w.id); return true; });
|
||||||
|
layout.childById("BTN3").onClickListener(delegate (Widget w) { Log.d("onClick ", w.id); return true; });
|
||||||
|
|
||||||
layout.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT);
|
layout.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -670,6 +670,13 @@ class ColorDrawBuf : ColorDrawBufBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Drawable : RefCountedObject {
|
class Drawable : RefCountedObject {
|
||||||
|
private static int _instanceCount;
|
||||||
|
this() {
|
||||||
|
Log.d("Created drawable, count=", ++_instanceCount);
|
||||||
|
}
|
||||||
|
~this() {
|
||||||
|
Log.d("Destroyed drawable, count=", --_instanceCount);
|
||||||
|
}
|
||||||
abstract void drawTo(DrawBuf buf, Rect rc, int tilex0 = 0, int tiley0 = 0);
|
abstract void drawTo(DrawBuf buf, Rect rc, int tilex0 = 0, int tiley0 = 0);
|
||||||
@property abstract int width();
|
@property abstract int width();
|
||||||
@property abstract int height();
|
@property abstract int height();
|
||||||
|
|
|
||||||
|
|
@ -146,9 +146,19 @@ struct FontList {
|
||||||
FontRef[] _list;
|
FontRef[] _list;
|
||||||
uint _len;
|
uint _len;
|
||||||
~this() {
|
~this() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@property uint length() {
|
||||||
|
return _len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
for (uint i = 0; i < _len; i++) {
|
for (uint i = 0; i < _len; i++) {
|
||||||
_list[i].clear();
|
_list[i].clear();
|
||||||
|
_list[i] = null;
|
||||||
}
|
}
|
||||||
|
_len = 0;
|
||||||
}
|
}
|
||||||
// returns item by index
|
// returns item by index
|
||||||
ref FontRef get(int index) {
|
ref FontRef get(int index) {
|
||||||
|
|
@ -216,6 +226,8 @@ class FontManager {
|
||||||
static __gshared FontManager _instance;
|
static __gshared FontManager _instance;
|
||||||
/// sets new font manager singleton instance
|
/// sets new font manager singleton instance
|
||||||
static @property void instance(FontManager manager) {
|
static @property void instance(FontManager manager) {
|
||||||
|
if (_instance !is null)
|
||||||
|
destroy(_instance);
|
||||||
_instance = manager;
|
_instance = manager;
|
||||||
}
|
}
|
||||||
/// returns font manager singleton instance
|
/// returns font manager singleton instance
|
||||||
|
|
@ -232,5 +244,7 @@ class FontManager {
|
||||||
/// removes entries not used after last call of checkpoint() or cleanup()
|
/// removes entries not used after last call of checkpoint() or cleanup()
|
||||||
abstract void cleanup();
|
abstract void cleanup();
|
||||||
|
|
||||||
~this() {}
|
~this() {
|
||||||
|
Log.d("Destroying font manager");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ private class FreeTypeFontFile {
|
||||||
@property int weight() { return _weight; }
|
@property int weight() { return _weight; }
|
||||||
@property bool italic() { return _italic; }
|
@property bool italic() { return _italic; }
|
||||||
|
|
||||||
|
private static int _instanceCount;
|
||||||
this(FT_Library library, string filename) {
|
this(FT_Library library, string filename) {
|
||||||
_library = library;
|
_library = library;
|
||||||
_filename = filename;
|
_filename = filename;
|
||||||
|
|
@ -106,6 +107,12 @@ private class FreeTypeFontFile {
|
||||||
_matrix.yy = 0x10000;
|
_matrix.yy = 0x10000;
|
||||||
_matrix.xy = 0;
|
_matrix.xy = 0;
|
||||||
_matrix.yx = 0;
|
_matrix.yx = 0;
|
||||||
|
Log.d("Created FreeTypeFontFile, count=", ++_instanceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
~this() {
|
||||||
|
clear();
|
||||||
|
Log.d("Destroyed FreeTypeFontFile, count=", --_instanceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string familyName(FT_Face face)
|
private static string familyName(FT_Face face)
|
||||||
|
|
@ -273,9 +280,6 @@ private class FreeTypeFontFile {
|
||||||
_face = null;
|
_face = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
~this() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -285,22 +289,26 @@ class FreeTypeFont : Font {
|
||||||
private FontFileItem _fontItem;
|
private FontFileItem _fontItem;
|
||||||
private FreeTypeFontFile[] _files;
|
private FreeTypeFontFile[] _files;
|
||||||
|
|
||||||
|
static int _instanceCount;
|
||||||
/// need to call create() after construction to initialize font
|
/// need to call create() after construction to initialize font
|
||||||
this(FontFileItem item, int size) {
|
this(FontFileItem item, int size) {
|
||||||
_fontItem = item;
|
_fontItem = item;
|
||||||
_size = size;
|
_size = size;
|
||||||
_height = size;
|
_height = size;
|
||||||
|
Log.d("Created font, count=", ++_instanceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// do cleanup
|
||||||
|
~this() {
|
||||||
|
clear();
|
||||||
|
Log.d("Destroyed font, count=", --_instanceCount);
|
||||||
|
}
|
||||||
|
|
||||||
private int _size;
|
private int _size;
|
||||||
private int _height;
|
private int _height;
|
||||||
|
|
||||||
private GlyphCache _glyphCache;
|
private GlyphCache _glyphCache;
|
||||||
|
|
||||||
/// do cleanup
|
|
||||||
~this() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// cleanup resources
|
/// cleanup resources
|
||||||
override void clear() {
|
override void clear() {
|
||||||
|
|
@ -482,6 +490,13 @@ class FreeTypeFontManager : FontManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~this() {
|
~this() {
|
||||||
|
Log.d("FreeTypeFontManager ~this() active fonts: ", _activeFonts.length);
|
||||||
|
_activeFonts.clear();
|
||||||
|
foreach(ref FontFileItem item; _fontFiles) {
|
||||||
|
destroy(item);
|
||||||
|
item = null;
|
||||||
|
}
|
||||||
|
_fontFiles.length = 0;
|
||||||
// uninit library
|
// uninit library
|
||||||
if (_library)
|
if (_library)
|
||||||
FT_Done_FreeType(_library);
|
FT_Done_FreeType(_library);
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,61 @@
|
||||||
module dlangui.graphics.images;
|
module dlangui.graphics.images;
|
||||||
|
|
||||||
import dlangui.core.logger;
|
import dlangui.core.logger;
|
||||||
import dlangui.core.types;
|
import dlangui.core.types;
|
||||||
import dlangui.graphics.drawbuf;
|
import dlangui.graphics.drawbuf;
|
||||||
import std.stream;
|
import std.stream;
|
||||||
import std.file;
|
import std.file;
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
import libpng.png;
|
import libpng.png;
|
||||||
|
|
||||||
/// decoded image cache
|
/// decoded image cache
|
||||||
class ImageCache {
|
class ImageCache {
|
||||||
|
|
||||||
static class ImageCacheItem {
|
static class ImageCacheItem {
|
||||||
string _filename;
|
string _filename;
|
||||||
DrawBufRef _drawbuf;
|
DrawBufRef _drawbuf;
|
||||||
bool _error; // flag to avoid loading of file if it has been failed once
|
bool _error; // flag to avoid loading of file if it has been failed once
|
||||||
bool _used;
|
bool _used;
|
||||||
this(string filename) {
|
this(string filename) {
|
||||||
_filename = filename;
|
_filename = filename;
|
||||||
}
|
}
|
||||||
@property ref DrawBufRef get() {
|
@property ref DrawBufRef get() {
|
||||||
if (!_drawbuf.isNull || _error) {
|
if (!_drawbuf.isNull || _error) {
|
||||||
_used = true;
|
_used = true;
|
||||||
return _drawbuf;
|
return _drawbuf;
|
||||||
}
|
}
|
||||||
_drawbuf = loadImage(_filename);
|
_drawbuf = loadImage(_filename);
|
||||||
_used = true;
|
_used = true;
|
||||||
if (_drawbuf.isNull)
|
if (_drawbuf.isNull)
|
||||||
_error = true;
|
_error = true;
|
||||||
return _drawbuf;
|
return _drawbuf;
|
||||||
}
|
}
|
||||||
/// remove from memory, will cause reload on next access
|
/// remove from memory, will cause reload on next access
|
||||||
void compact() {
|
void compact() {
|
||||||
if (!_drawbuf.isNull)
|
if (!_drawbuf.isNull)
|
||||||
_drawbuf.clear();
|
_drawbuf.clear();
|
||||||
}
|
}
|
||||||
/// mark as not used
|
/// mark as not used
|
||||||
void checkpoint() {
|
void checkpoint() {
|
||||||
_used = false;
|
_used = false;
|
||||||
}
|
}
|
||||||
/// cleanup if unused since last checkpoint
|
/// cleanup if unused since last checkpoint
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
if (!_used)
|
if (!_used)
|
||||||
compact();
|
compact();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImageCacheItem[string] _map;
|
ImageCacheItem[string] _map;
|
||||||
|
|
||||||
/// get and cache image
|
/// get and cache image
|
||||||
ref DrawBufRef get(string filename) {
|
ref DrawBufRef get(string filename) {
|
||||||
if (filename in _map) {
|
if (filename in _map) {
|
||||||
return _map[filename].get;
|
return _map[filename].get;
|
||||||
}
|
}
|
||||||
ImageCacheItem item = new ImageCacheItem(filename);
|
ImageCacheItem item = new ImageCacheItem(filename);
|
||||||
_map[filename] = item;
|
_map[filename] = item;
|
||||||
return item.get;
|
return item.get;
|
||||||
}
|
}
|
||||||
// clear usage flags for all entries
|
// clear usage flags for all entries
|
||||||
void checkpoint() {
|
void checkpoint() {
|
||||||
foreach (item; _map)
|
foreach (item; _map)
|
||||||
|
|
@ -73,71 +73,87 @@ class ImageCache {
|
||||||
~this() {
|
~this() {
|
||||||
Log.i("Destroying ImageCache");
|
Log.i("Destroying ImageCache");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__gshared ImageCache _imageCache;
|
__gshared ImageCache _imageCache;
|
||||||
/// image cache singleton
|
/// image cache singleton
|
||||||
@property ImageCache imageCache() { return _imageCache; }
|
@property ImageCache imageCache() { return _imageCache; }
|
||||||
|
/// image cache singleton
|
||||||
__gshared DrawableCache _drawableCache;
|
@property void imageCache(ImageCache cache) {
|
||||||
/// drawable cache singleton
|
if (_imageCache !is null)
|
||||||
@property DrawableCache drawableCache() { return _drawableCache; }
|
destroy(_imageCache);
|
||||||
|
_imageCache = cache;
|
||||||
shared static this() {
|
}
|
||||||
_imageCache = new ImageCache();
|
|
||||||
_drawableCache = new DrawableCache();
|
__gshared DrawableCache _drawableCache;
|
||||||
}
|
/// drawable cache singleton
|
||||||
|
@property DrawableCache drawableCache() { return _drawableCache; }
|
||||||
class DrawableCache {
|
/// drawable cache singleton
|
||||||
static class DrawableCacheItem {
|
@property void drawableCache(DrawableCache cache) {
|
||||||
string _id;
|
if (_drawableCache !is null)
|
||||||
string _filename;
|
destroy(_drawableCache);
|
||||||
bool _tiled;
|
_drawableCache = cache;
|
||||||
bool _error;
|
}
|
||||||
bool _used;
|
|
||||||
DrawableRef _drawable;
|
shared static this() {
|
||||||
this(string id, string filename, bool tiled) {
|
_imageCache = new ImageCache();
|
||||||
_id = id;
|
_drawableCache = new DrawableCache();
|
||||||
_filename = filename;
|
}
|
||||||
_tiled = tiled;
|
|
||||||
_error = filename is null;
|
class DrawableCache {
|
||||||
}
|
static class DrawableCacheItem {
|
||||||
/// remove from memory, will cause reload on next access
|
string _id;
|
||||||
void compact() {
|
string _filename;
|
||||||
if (!_drawable.isNull)
|
bool _tiled;
|
||||||
_drawable.clear();
|
bool _error;
|
||||||
}
|
bool _used;
|
||||||
/// mark as not used
|
DrawableRef _drawable;
|
||||||
void checkpoint() {
|
this(string id, string filename, bool tiled) {
|
||||||
_used = false;
|
_id = id;
|
||||||
}
|
_filename = filename;
|
||||||
/// cleanup if unused since last checkpoint
|
_tiled = tiled;
|
||||||
|
_error = filename is null;
|
||||||
|
}
|
||||||
|
~this() {
|
||||||
|
_drawable.clear();
|
||||||
|
}
|
||||||
|
/// remove from memory, will cause reload on next access
|
||||||
|
void compact() {
|
||||||
|
if (!_drawable.isNull)
|
||||||
|
_drawable.clear();
|
||||||
|
}
|
||||||
|
/// mark as not used
|
||||||
|
void checkpoint() {
|
||||||
|
_used = false;
|
||||||
|
}
|
||||||
|
/// cleanup if unused since last checkpoint
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
if (!_used)
|
if (!_used)
|
||||||
compact();
|
compact();
|
||||||
}
|
}
|
||||||
@property ref DrawableRef drawable() {
|
@property ref DrawableRef drawable() {
|
||||||
_used = true;
|
_used = true;
|
||||||
if (!_drawable.isNull || _error)
|
if (!_drawable.isNull || _error)
|
||||||
return _drawable;
|
return _drawable;
|
||||||
if (_filename !is null) {
|
if (_filename !is null) {
|
||||||
// reload from file
|
// reload from file
|
||||||
DrawBufRef image = imageCache.get(_filename);
|
DrawBufRef image = imageCache.get(_filename);
|
||||||
if (!image.isNull) {
|
if (!image.isNull) {
|
||||||
bool ninePatch = _filename.endsWith(".9.png");
|
bool ninePatch = _filename.endsWith(".9.png");
|
||||||
_drawable = new ImageDrawable(image, _tiled, ninePatch);
|
_drawable = new ImageDrawable(image, _tiled, ninePatch);
|
||||||
} else
|
} else
|
||||||
_error = true;
|
_error = true;
|
||||||
}
|
}
|
||||||
return _drawable;
|
return _drawable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void clear() {
|
void clear() {
|
||||||
_idToFileMap.clear();
|
Log.d("DrawableCache.clear()");
|
||||||
foreach(DrawableCacheItem item; _idToDrawableMap)
|
_idToFileMap.clear();
|
||||||
item.drawable.clear();
|
foreach(DrawableCacheItem item; _idToDrawableMap)
|
||||||
_idToDrawableMap.clear();
|
item.drawable.clear();
|
||||||
}
|
_idToDrawableMap.clear();
|
||||||
|
}
|
||||||
// clear usage flags for all entries
|
// clear usage flags for all entries
|
||||||
void checkpoint() {
|
void checkpoint() {
|
||||||
foreach (item; _idToDrawableMap)
|
foreach (item; _idToDrawableMap)
|
||||||
|
|
@ -148,286 +164,286 @@ class DrawableCache {
|
||||||
foreach (item; _idToDrawableMap)
|
foreach (item; _idToDrawableMap)
|
||||||
item.cleanup();
|
item.cleanup();
|
||||||
}
|
}
|
||||||
string[] _resourcePaths;
|
string[] _resourcePaths;
|
||||||
string[string] _idToFileMap;
|
string[string] _idToFileMap;
|
||||||
DrawableCacheItem[string] _idToDrawableMap;
|
DrawableCacheItem[string] _idToDrawableMap;
|
||||||
ref DrawableRef get(string id) {
|
ref DrawableRef get(string id) {
|
||||||
if (id in _idToDrawableMap)
|
if (id in _idToDrawableMap)
|
||||||
return _idToDrawableMap[id].drawable;
|
return _idToDrawableMap[id].drawable;
|
||||||
string resourceId = id;
|
string resourceId = id;
|
||||||
bool tiled = false;
|
bool tiled = false;
|
||||||
if (id.endsWith(".tiled")) {
|
if (id.endsWith(".tiled")) {
|
||||||
resourceId = id[0..$-6]; // remove .tiled
|
resourceId = id[0..$-6]; // remove .tiled
|
||||||
tiled = true;
|
tiled = true;
|
||||||
}
|
}
|
||||||
string filename = findResource(resourceId);
|
string filename = findResource(resourceId);
|
||||||
DrawableCacheItem item = new DrawableCacheItem(id, filename, tiled);
|
DrawableCacheItem item = new DrawableCacheItem(id, filename, tiled);
|
||||||
_idToDrawableMap[id] = item;
|
_idToDrawableMap[id] = item;
|
||||||
return item.drawable;
|
return item.drawable;
|
||||||
}
|
}
|
||||||
@property string[] resourcePaths() {
|
@property string[] resourcePaths() {
|
||||||
return _resourcePaths;
|
return _resourcePaths;
|
||||||
}
|
}
|
||||||
@property void resourcePaths(string[] paths) {
|
@property void resourcePaths(string[] paths) {
|
||||||
_resourcePaths = paths;
|
_resourcePaths = paths;
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
string findResource(string id) {
|
string findResource(string id) {
|
||||||
if (id in _idToFileMap)
|
if (id in _idToFileMap)
|
||||||
return _idToFileMap[id];
|
return _idToFileMap[id];
|
||||||
foreach(string path; _resourcePaths) {
|
foreach(string path; _resourcePaths) {
|
||||||
char[] name = path.dup;
|
char[] name = path.dup;
|
||||||
name ~= id;
|
name ~= id;
|
||||||
name ~= ".png";
|
name ~= ".png";
|
||||||
if (!exists(name)) {
|
if (!exists(name)) {
|
||||||
name = path.dup;
|
name = path.dup;
|
||||||
name ~= id;
|
name ~= id;
|
||||||
name ~= ".9.png";
|
name ~= ".9.png";
|
||||||
}
|
}
|
||||||
if (exists(name) && isFile(name)) {
|
if (exists(name) && isFile(name)) {
|
||||||
string filename = name.dup;
|
string filename = name.dup;
|
||||||
_idToFileMap[id] = filename;
|
_idToFileMap[id] = filename;
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this() {
|
this() {
|
||||||
Log.i("Creating DrawableCache");
|
Log.i("Creating DrawableCache");
|
||||||
}
|
}
|
||||||
~this() {
|
~this() {
|
||||||
Log.i("Destroying DrawableCache");
|
Log.i("Destroying DrawableCache");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed
|
/// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed
|
||||||
ColorDrawBuf loadImage(string filename) {
|
ColorDrawBuf loadImage(string filename) {
|
||||||
Log.d("Loading image from file " ~ filename);
|
Log.d("Loading image from file " ~ filename);
|
||||||
try {
|
try {
|
||||||
std.stream.File f = new std.stream.File(filename);
|
std.stream.File f = new std.stream.File(filename);
|
||||||
scope(exit) { f.close(); }
|
scope(exit) { f.close(); }
|
||||||
return loadImage(f);
|
return loadImage(f);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// load and decode image from stream to ColorDrawBuf, returns null if loading or decoding is failed
|
/// load and decode image from stream to ColorDrawBuf, returns null if loading or decoding is failed
|
||||||
ColorDrawBuf loadImage(InputStream stream) {
|
ColorDrawBuf loadImage(InputStream stream) {
|
||||||
if (stream is null || !stream.isOpen)
|
if (stream is null || !stream.isOpen)
|
||||||
return null;
|
return null;
|
||||||
static if (USE_FREEIMAGE) {
|
static if (USE_FREEIMAGE) {
|
||||||
return loadFreeImage(stream);
|
return loadFreeImage(stream);
|
||||||
} else static if (USE_LIBPNG) {
|
} else static if (USE_LIBPNG) {
|
||||||
return loadPngImage(stream);
|
return loadPngImage(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageDecodingException : Exception {
|
class ImageDecodingException : Exception {
|
||||||
this(string msg) {
|
this(string msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
immutable bool USE_LIBPNG = false;
|
immutable bool USE_LIBPNG = false;
|
||||||
immutable bool USE_FREEIMAGE = true;
|
immutable bool USE_FREEIMAGE = true;
|
||||||
|
|
||||||
shared static this() {
|
shared static this() {
|
||||||
//import derelict.freeimage.freeimage;
|
//import derelict.freeimage.freeimage;
|
||||||
//DerelictFI.load();
|
//DerelictFI.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
static if (USE_FREEIMAGE) {
|
static if (USE_FREEIMAGE) {
|
||||||
ColorDrawBuf loadFreeImage(InputStream stream) {
|
ColorDrawBuf loadFreeImage(InputStream stream) {
|
||||||
import derelict.freeimage.freeimage;
|
import derelict.freeimage.freeimage;
|
||||||
|
|
||||||
static bool FREE_IMAGE_LOADED;
|
static bool FREE_IMAGE_LOADED;
|
||||||
if (!FREE_IMAGE_LOADED) {
|
if (!FREE_IMAGE_LOADED) {
|
||||||
DerelictFI.load();
|
DerelictFI.load();
|
||||||
FREE_IMAGE_LOADED = true;
|
FREE_IMAGE_LOADED = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ubyte imagebuf[];
|
ubyte imagebuf[];
|
||||||
ubyte readbuf[4096];
|
ubyte readbuf[4096];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
size_t bytesRead = stream.read(readbuf);
|
size_t bytesRead = stream.read(readbuf);
|
||||||
if (!bytesRead)
|
if (!bytesRead)
|
||||||
break;
|
break;
|
||||||
imagebuf ~= readbuf[0..bytesRead];
|
imagebuf ~= readbuf[0..bytesRead];
|
||||||
}
|
}
|
||||||
//pointer to the image, once loaded
|
//pointer to the image, once loaded
|
||||||
FIBITMAP *dib = null; //image format
|
FIBITMAP *dib = null; //image format
|
||||||
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
|
FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
|
||||||
// attach the binary data to a memory stream
|
// attach the binary data to a memory stream
|
||||||
FIMEMORY *hmem = FreeImage_OpenMemory(imagebuf.ptr, imagebuf.length);
|
FIMEMORY *hmem = FreeImage_OpenMemory(imagebuf.ptr, imagebuf.length);
|
||||||
fif = FreeImage_GetFileTypeFromMemory(hmem);
|
fif = FreeImage_GetFileTypeFromMemory(hmem);
|
||||||
//check that the plugin has reading capabilities and load the file
|
//check that the plugin has reading capabilities and load the file
|
||||||
if(!FreeImage_FIFSupportsReading(fif)) {
|
if(!FreeImage_FIFSupportsReading(fif)) {
|
||||||
FreeImage_CloseMemory(hmem);
|
FreeImage_CloseMemory(hmem);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// load an image from the memory stream
|
// load an image from the memory stream
|
||||||
dib = FreeImage_LoadFromMemory(fif, hmem, 0);
|
dib = FreeImage_LoadFromMemory(fif, hmem, 0);
|
||||||
|
|
||||||
//if the image failed to load, return failure
|
//if the image failed to load, return failure
|
||||||
if (!dib) {
|
if (!dib) {
|
||||||
Log.e("Failed to decode image");
|
Log.e("Failed to decode image");
|
||||||
FreeImage_CloseMemory(hmem);
|
FreeImage_CloseMemory(hmem);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//retrieve the image data
|
//retrieve the image data
|
||||||
ubyte * data = cast(ubyte*)FreeImage_GetBits(dib);
|
ubyte * data = cast(ubyte*)FreeImage_GetBits(dib);
|
||||||
//get the image width and height, and size per pixel
|
//get the image width and height, and size per pixel
|
||||||
int width = FreeImage_GetWidth(dib);
|
int width = FreeImage_GetWidth(dib);
|
||||||
int height = FreeImage_GetHeight(dib);
|
int height = FreeImage_GetHeight(dib);
|
||||||
int pixelSize = FreeImage_GetBPP(dib)/8;
|
int pixelSize = FreeImage_GetBPP(dib)/8;
|
||||||
int size = width*height*pixelSize;
|
int size = width*height*pixelSize;
|
||||||
|
|
||||||
ColorDrawBuf res = new ColorDrawBuf(width, height);
|
ColorDrawBuf res = new ColorDrawBuf(width, height);
|
||||||
|
|
||||||
//swap R and B and invert image while copying
|
//swap R and B and invert image while copying
|
||||||
ubyte* src;
|
ubyte* src;
|
||||||
uint* dst;
|
uint* dst;
|
||||||
uint r, g, b, a;
|
uint r, g, b, a;
|
||||||
for( int i = 0, ii = height-1; i < height ; ++i, --ii ) {
|
for( int i = 0, ii = height-1; i < height ; ++i, --ii ) {
|
||||||
dst = res.scanLine(i);
|
dst = res.scanLine(i);
|
||||||
src = data + (ii * width) * pixelSize;
|
src = data + (ii * width) * pixelSize;
|
||||||
for( int j = 0; j < width; ++j, ++dst, src += pixelSize ) {
|
for( int j = 0; j < width; ++j, ++dst, src += pixelSize ) {
|
||||||
a = 0;
|
a = 0;
|
||||||
switch (pixelSize) {
|
switch (pixelSize) {
|
||||||
case 4:
|
case 4:
|
||||||
a = src[3] ^ 255;
|
a = src[3] ^ 255;
|
||||||
case 3:
|
case 3:
|
||||||
r = src[2];
|
r = src[2];
|
||||||
g = src[1];
|
g = src[1];
|
||||||
b = src[0];
|
b = src[0];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// todo: do something better
|
// todo: do something better
|
||||||
r = g = src[1];
|
r = g = src[1];
|
||||||
b = src[0];
|
b = src[0];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 1:
|
case 1:
|
||||||
r = g = b = src[0];
|
r = g = b = src[0];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dst[0] = (a << 24) | (r << 16) | (g << 8) | b;
|
dst[0] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FreeImage_CloseMemory(hmem);
|
FreeImage_CloseMemory(hmem);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static if (USE_LIBPNG) {
|
static if (USE_LIBPNG) {
|
||||||
|
|
||||||
extern (C) void lvpng_error_func (png_structp png, png_const_charp msg)
|
extern (C) void lvpng_error_func (png_structp png, png_const_charp msg)
|
||||||
{
|
{
|
||||||
string s = fromStringz(msg);
|
string s = fromStringz(msg);
|
||||||
Log.d("Error while reading PNG image: ", s);
|
Log.d("Error while reading PNG image: ", s);
|
||||||
// todo: exceptions do not work inside C function
|
// todo: exceptions do not work inside C function
|
||||||
throw new ImageDecodingException("Error while decoding PNG image");
|
throw new ImageDecodingException("Error while decoding PNG image");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern (C) void lvpng_warning_func (png_structp png, png_const_charp msg)
|
extern (C) void lvpng_warning_func (png_structp png, png_const_charp msg)
|
||||||
{
|
{
|
||||||
string s = fromStringz(msg);
|
string s = fromStringz(msg);
|
||||||
Log.d("Warn while reading PNG image: ", s);
|
Log.d("Warn while reading PNG image: ", s);
|
||||||
// todo: exceptions do not work inside C function
|
// todo: exceptions do not work inside C function
|
||||||
throw new ImageDecodingException("Error while decoding PNG image");
|
throw new ImageDecodingException("Error while decoding PNG image");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern (C) void lvpng_read_func(png_structp png, png_bytep buf, png_size_t len)
|
extern (C) void lvpng_read_func(png_structp png, png_bytep buf, png_size_t len)
|
||||||
{
|
{
|
||||||
InputStream stream = cast(InputStream)png_get_io_ptr(png);
|
InputStream stream = cast(InputStream)png_get_io_ptr(png);
|
||||||
ubyte[] localbuf = new ubyte[len];
|
ubyte[] localbuf = new ubyte[len];
|
||||||
if (stream.read(localbuf) != len)
|
if (stream.read(localbuf) != len)
|
||||||
throw new ImageDecodingException("Error while reading PNG image");
|
throw new ImageDecodingException("Error while reading PNG image");
|
||||||
for (uint i = 0; i < len; i++)
|
for (uint i = 0; i < len; i++)
|
||||||
buf[i] = localbuf[i];
|
buf[i] = localbuf[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// load and decode PNG image
|
/// load and decode PNG image
|
||||||
ColorDrawBuf loadPngImage(InputStream stream)
|
ColorDrawBuf loadPngImage(InputStream stream)
|
||||||
{
|
{
|
||||||
png_structp png_ptr = null;
|
png_structp png_ptr = null;
|
||||||
png_infop info_ptr = null;
|
png_infop info_ptr = null;
|
||||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||||
cast(png_voidp)stream, &lvpng_error_func, &lvpng_warning_func);
|
cast(png_voidp)stream, &lvpng_error_func, &lvpng_warning_func);
|
||||||
if ( !png_ptr )
|
if ( !png_ptr )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//
|
//
|
||||||
info_ptr = png_create_info_struct(png_ptr);
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
if (!info_ptr)
|
if (!info_ptr)
|
||||||
lvpng_error_func(png_ptr, "cannot create png info struct");
|
lvpng_error_func(png_ptr, "cannot create png info struct");
|
||||||
png_set_read_fn(png_ptr,
|
png_set_read_fn(png_ptr,
|
||||||
cast(void*)stream, &lvpng_read_func);
|
cast(void*)stream, &lvpng_read_func);
|
||||||
png_read_info( png_ptr, info_ptr );
|
png_read_info( png_ptr, info_ptr );
|
||||||
|
|
||||||
|
|
||||||
png_uint_32 width, height;
|
png_uint_32 width, height;
|
||||||
int bit_depth, color_type, interlace_type;
|
int bit_depth, color_type, interlace_type;
|
||||||
png_get_IHDR(png_ptr, info_ptr, &width, &height,
|
png_get_IHDR(png_ptr, info_ptr, &width, &height,
|
||||||
&bit_depth, &color_type, &interlace_type,
|
&bit_depth, &color_type, &interlace_type,
|
||||||
null, null);
|
null, null);
|
||||||
ColorDrawBuf drawbuf = new ColorDrawBuf(width, height);
|
ColorDrawBuf drawbuf = new ColorDrawBuf(width, height);
|
||||||
|
|
||||||
if (color_type & PNG_COLOR_MASK_PALETTE)
|
if (color_type & PNG_COLOR_MASK_PALETTE)
|
||||||
png_set_palette_to_rgb(png_ptr);
|
png_set_palette_to_rgb(png_ptr);
|
||||||
|
|
||||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||||
|
|
||||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
||||||
png_set_tRNS_to_alpha(png_ptr);
|
png_set_tRNS_to_alpha(png_ptr);
|
||||||
|
|
||||||
if (bit_depth == 16)
|
if (bit_depth == 16)
|
||||||
png_set_strip_16(png_ptr);
|
png_set_strip_16(png_ptr);
|
||||||
|
|
||||||
png_set_invert_alpha(png_ptr);
|
png_set_invert_alpha(png_ptr);
|
||||||
|
|
||||||
if (bit_depth < 8)
|
if (bit_depth < 8)
|
||||||
png_set_packing(png_ptr);
|
png_set_packing(png_ptr);
|
||||||
|
|
||||||
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
|
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
|
||||||
|
|
||||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
png_set_gray_to_rgb(png_ptr);
|
png_set_gray_to_rgb(png_ptr);
|
||||||
|
|
||||||
int number_passes = png_set_interlace_handling(png_ptr);
|
int number_passes = png_set_interlace_handling(png_ptr);
|
||||||
png_set_bgr(png_ptr);
|
png_set_bgr(png_ptr);
|
||||||
|
|
||||||
for (int pass = 0; pass < number_passes; pass++)
|
for (int pass = 0; pass < number_passes; pass++)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
uint * row = drawbuf.scanLine(y);
|
uint * row = drawbuf.scanLine(y);
|
||||||
png_read_rows(png_ptr, cast(ubyte **)&row, null, 1);
|
png_read_rows(png_ptr, cast(ubyte **)&row, null, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
png_read_end(png_ptr, info_ptr);
|
png_read_end(png_ptr, info_ptr);
|
||||||
|
|
||||||
png_destroy_read_struct(&png_ptr, &info_ptr, null);
|
png_destroy_read_struct(&png_ptr, &info_ptr, null);
|
||||||
|
|
||||||
return drawbuf;
|
return drawbuf;
|
||||||
} catch (ImageDecodingException e) {
|
} catch (ImageDecodingException e) {
|
||||||
if (png_ptr)
|
if (png_ptr)
|
||||||
{
|
{
|
||||||
png_destroy_read_struct(&png_ptr, &info_ptr, null);
|
png_destroy_read_struct(&png_ptr, &info_ptr, null);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//bool LVPngImageSource::CheckPattern( const lUInt8 * buf, int )
|
//bool LVPngImageSource::CheckPattern( const lUInt8 * buf, int )
|
||||||
//{
|
//{
|
||||||
//return( !png_sig_cmp((unsigned char *)buf, (png_size_t)0, 4) );
|
//return( !png_sig_cmp((unsigned char *)buf, (png_size_t)0, 4) );
|
||||||
//}
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +42,12 @@ class Window {
|
||||||
this() {
|
this() {
|
||||||
_backgroundColor = 0xFFFFFF;
|
_backgroundColor = 0xFFFFFF;
|
||||||
}
|
}
|
||||||
|
~this() {
|
||||||
|
if (_mainWidget !is null) {
|
||||||
|
destroy(_mainWidget);
|
||||||
|
_mainWidget = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void animate(Widget root, long interval) {
|
private void animate(Widget root, long interval) {
|
||||||
if (root.visibility != Visibility.Visible)
|
if (root.visibility != Visibility.Visible)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ version(linux) {
|
||||||
import dlangui.graphics.drawbuf;
|
import dlangui.graphics.drawbuf;
|
||||||
import dlangui.graphics.fonts;
|
import dlangui.graphics.fonts;
|
||||||
import dlangui.graphics.ftfonts;
|
import dlangui.graphics.ftfonts;
|
||||||
|
import dlangui.graphics.images;
|
||||||
|
import dlangui.widgets.styles;
|
||||||
import dlangui.platforms.common.platform;
|
import dlangui.platforms.common.platform;
|
||||||
|
|
||||||
class XCBWindow : Window {
|
class XCBWindow : Window {
|
||||||
|
|
@ -301,6 +303,11 @@ version(linux) {
|
||||||
this() {
|
this() {
|
||||||
}
|
}
|
||||||
~this() {
|
~this() {
|
||||||
|
foreach(ref XCBWindow wnd; _windowMap) {
|
||||||
|
destroy(wnd);
|
||||||
|
wnd = null;
|
||||||
|
}
|
||||||
|
_windowMap.clear();
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
void disconnect() {
|
void disconnect() {
|
||||||
|
|
@ -522,7 +529,9 @@ version(linux) {
|
||||||
FreeTypeFontManager ft = new FreeTypeFontManager();
|
FreeTypeFontManager ft = new FreeTypeFontManager();
|
||||||
ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal);
|
ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal);
|
||||||
FontManager.instance = ft;
|
FontManager.instance = ft;
|
||||||
|
|
||||||
|
currentTheme = createDefaultTheme();
|
||||||
|
|
||||||
XCBPlatform xcb = new XCBPlatform();
|
XCBPlatform xcb = new XCBPlatform();
|
||||||
if (!xcb.connect()) {
|
if (!xcb.connect()) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -541,8 +550,15 @@ version(linux) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.setInstance(null);
|
Platform.setInstance(null);
|
||||||
|
Log.d("Destroying XCB platform");
|
||||||
destroy(xcb);
|
destroy(xcb);
|
||||||
|
|
||||||
|
currentTheme = null;
|
||||||
|
drawableCache = null;
|
||||||
|
imageCache = null;
|
||||||
|
FontManager.instance = null;
|
||||||
|
|
||||||
|
Log.d("Exiting main");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ module dlangui.widgets.controls;
|
||||||
|
|
||||||
import dlangui.widgets.widget;
|
import dlangui.widgets.widget;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// static text widget
|
/// static text widget
|
||||||
class TextWidget : Widget {
|
class TextWidget : Widget {
|
||||||
this(string ID = null) {
|
this(string ID = null) {
|
||||||
|
|
@ -121,7 +123,8 @@ class Button : Widget {
|
||||||
Point sz = font.textSize(text);
|
Point sz = font.textSize(text);
|
||||||
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
|
||||||
}
|
}
|
||||||
override void onDraw(DrawBuf buf) {
|
|
||||||
|
override void onDraw(DrawBuf buf) {
|
||||||
super.onDraw(buf);
|
super.onDraw(buf);
|
||||||
Rect rc = _pos;
|
Rect rc = _pos;
|
||||||
applyMargins(rc);
|
applyMargins(rc);
|
||||||
|
|
@ -134,26 +137,4 @@ class Button : Widget {
|
||||||
font.drawText(buf, rc.left, rc.top, text, textColor);
|
font.drawText(buf, rc.left, rc.top, text, textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
override bool onMouseEvent(MouseEvent event) {
|
|
||||||
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
|
|
||||||
setState(State.Pressed);
|
|
||||||
Log.d("Button state: ", state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
|
|
||||||
resetState(State.Pressed);
|
|
||||||
Log.d("Button state: ", state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
|
|
||||||
resetState(State.Pressed);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (event.action == MouseAction.FocusIn) {
|
|
||||||
setState(State.Pressed);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -386,10 +386,29 @@ class Style {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int _instanceCount;
|
||||||
this(Theme theme, string id) {
|
this(Theme theme, string id) {
|
||||||
_theme = theme;
|
_theme = theme;
|
||||||
_parentStyle = theme;
|
_parentStyle = theme;
|
||||||
_id = id;
|
_id = id;
|
||||||
|
Log.d("Created style ", _id, ", count=", ++_instanceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
~this() {
|
||||||
|
foreach(ref Style item; _substates) {
|
||||||
|
Log.d("Destroying substate");
|
||||||
|
destroy(item);
|
||||||
|
item = null;
|
||||||
|
}
|
||||||
|
_substates.clear();
|
||||||
|
foreach(ref Style item; _children) {
|
||||||
|
destroy(item);
|
||||||
|
item = null;
|
||||||
|
}
|
||||||
|
_children.clear();
|
||||||
|
_backgroundDrawable.clear();
|
||||||
|
_font.clear();
|
||||||
|
Log.d("Destroyed style ", _id, ", parentId=", _parentId, ", state=", _stateMask, ", count=", --_instanceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// create named substyle of this style
|
/// create named substyle of this style
|
||||||
|
|
@ -403,7 +422,9 @@ class Style {
|
||||||
/// create state substyle for this style
|
/// create state substyle for this style
|
||||||
Style createState(uint stateMask = 0, uint stateValue = 0) {
|
Style createState(uint stateMask = 0, uint stateValue = 0) {
|
||||||
assert(stateMask != 0);
|
assert(stateMask != 0);
|
||||||
Style child = createSubstyle(null);
|
Log.d("Creating substate ", stateMask);
|
||||||
|
Style child = (_theme !is null ? _theme : currentTheme).createSubstyle(null);
|
||||||
|
child._parentStyle = this;
|
||||||
child._stateMask = stateMask;
|
child._stateMask = stateMask;
|
||||||
child._stateValue = stateValue;
|
child._stateValue = stateValue;
|
||||||
child._backgroundColor = COLOR_UNSPECIFIED;
|
child._backgroundColor = COLOR_UNSPECIFIED;
|
||||||
|
|
@ -424,6 +445,7 @@ class Style {
|
||||||
}
|
}
|
||||||
return this; // fallback to current style
|
return this; // fallback to current style
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Theme - root for style hierarhy.
|
/// Theme - root for style hierarhy.
|
||||||
|
|
@ -447,6 +469,10 @@ class Theme : Style {
|
||||||
_layoutHeight = WRAP_CONTENT;
|
_layoutHeight = WRAP_CONTENT;
|
||||||
_layoutWeight = 1;
|
_layoutWeight = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~this() {
|
||||||
|
Log.d("Theme destructor");
|
||||||
|
}
|
||||||
|
|
||||||
/// create wrapper style which will have currentTheme.get(id) as parent instead of fixed parent - to modify some base style properties in widget
|
/// create wrapper style which will have currentTheme.get(id) as parent instead of fixed parent - to modify some base style properties in widget
|
||||||
Style modifyStyle(string id) {
|
Style modifyStyle(string id) {
|
||||||
|
|
@ -499,13 +525,26 @@ class Theme : Style {
|
||||||
/// to access current theme
|
/// to access current theme
|
||||||
private __gshared Theme _currentTheme;
|
private __gshared Theme _currentTheme;
|
||||||
@property Theme currentTheme() { return _currentTheme; }
|
@property Theme currentTheme() { return _currentTheme; }
|
||||||
|
@property void currentTheme(Theme theme) {
|
||||||
|
if (_currentTheme !is null) {
|
||||||
|
destroy(_currentTheme);
|
||||||
|
}
|
||||||
|
_currentTheme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
static this() {
|
|
||||||
_currentTheme = new Theme("default");
|
Theme createDefaultTheme() {
|
||||||
Style button = _currentTheme.createSubstyle("BUTTON").backgroundImageId("btn_default_small_normal").alignment(Align.Center);
|
Log.d("Creating default theme");
|
||||||
Style text = _currentTheme.createSubstyle("TEXT").margins(Rect(3,3,3,3)).padding(Rect(3,3,3,3));
|
Theme res = new Theme("default");
|
||||||
|
Style button = res.createSubstyle("BUTTON").backgroundImageId("btn_default_small_normal").alignment(Align.Center);
|
||||||
|
Style text = res.createSubstyle("TEXT").margins(Rect(3,3,3,3)).padding(Rect(3,3,3,3));
|
||||||
button.createState(State.Disabled | State.Focused, State.Disabled | State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
|
button.createState(State.Disabled | State.Focused, State.Disabled | State.Focused).backgroundImageId("btn_default_small_normal_disable_focused");
|
||||||
button.createState(State.Disabled, State.Disabled).backgroundImageId("btn_default_small_normal_disable");
|
button.createState(State.Disabled, State.Disabled).backgroundImageId("btn_default_small_normal_disable");
|
||||||
button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed");
|
button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed");
|
||||||
button.createState(State.Focused, State.Focused).backgroundImageId("btn_default_small_selected");
|
button.createState(State.Focused, State.Focused).backgroundImageId("btn_default_small_selected");
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shared static ~this() {
|
||||||
|
currentTheme = null;
|
||||||
|
}
|
||||||
|
|
@ -7,10 +7,14 @@ public import dlangui.graphics.drawbuf;
|
||||||
public import dlangui.graphics.images;
|
public import dlangui.graphics.images;
|
||||||
public import dlangui.graphics.fonts;
|
public import dlangui.graphics.fonts;
|
||||||
|
|
||||||
|
public import std.signals;
|
||||||
|
|
||||||
import dlangui.platforms.common.platform;
|
import dlangui.platforms.common.platform;
|
||||||
|
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
|
|
||||||
|
alias onClick_t = bool delegate(Widget);
|
||||||
|
|
||||||
|
|
||||||
/// Visibility (see Android View Visibility)
|
/// Visibility (see Android View Visibility)
|
||||||
enum Visibility : ubyte {
|
enum Visibility : ubyte {
|
||||||
|
|
@ -50,9 +54,18 @@ class Widget {
|
||||||
/// window (to be used for top level widgets only!)
|
/// window (to be used for top level widgets only!)
|
||||||
protected Window _window;
|
protected Window _window;
|
||||||
|
|
||||||
|
private static int _instanceCount = 0;
|
||||||
|
/// create widget, with optional id
|
||||||
this(string ID = null) {
|
this(string ID = null) {
|
||||||
_id = id;
|
_id = ID;
|
||||||
|
Log.d("Created widget, count = ", ++_instanceCount);
|
||||||
}
|
}
|
||||||
|
~this() {
|
||||||
|
if (_ownStyle !is null)
|
||||||
|
destroy(_ownStyle);
|
||||||
|
_ownStyle = null;
|
||||||
|
Log.d("Destroyed widget, count = ", --_instanceCount);
|
||||||
|
}
|
||||||
|
|
||||||
/// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used).
|
/// accessor to style - by lookup in theme by styleId (if style id is not set, theme base style will be used).
|
||||||
protected @property const (Style) style() const {
|
protected @property const (Style) style() const {
|
||||||
|
|
@ -90,7 +103,7 @@ class Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns widget id, null if not set
|
/// returns widget id, null if not set
|
||||||
@property string id() const { return _styleId; }
|
@property string id() const { return _id; }
|
||||||
/// set widget id
|
/// set widget id
|
||||||
@property void id(string id) { _id = id; }
|
@property void id(string id) { _id = id; }
|
||||||
/// compare widget id with specified value, returs true if matches
|
/// compare widget id with specified value, returs true if matches
|
||||||
|
|
@ -266,9 +279,35 @@ class Widget {
|
||||||
|
|
||||||
/// process mouse event; return true if event is processed by widget.
|
/// process mouse event; return true if event is processed by widget.
|
||||||
bool onMouseEvent(MouseEvent event) {
|
bool onMouseEvent(MouseEvent event) {
|
||||||
return false;
|
// support onClick
|
||||||
|
if (_onClickListener !is null) {
|
||||||
|
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
|
||||||
|
setState(State.Pressed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
|
||||||
|
resetState(State.Pressed);
|
||||||
|
_onClickListener(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
|
||||||
|
resetState(State.Pressed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (event.action == MouseAction.FocusIn) {
|
||||||
|
setState(State.Pressed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onClick_t _onClickListener;
|
||||||
|
/// on click event listener (bool delegate(Widget))
|
||||||
|
@property onClick_t onClickListener() { return _onClickListener; }
|
||||||
|
/// set on click event listener (bool delegate(Widget))
|
||||||
|
@property Widget onClickListener(onClick_t listener) { _onClickListener = listener; return this; }
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
// Layout and measurement methods
|
// Layout and measurement methods
|
||||||
|
|
||||||
|
|
@ -455,6 +494,7 @@ class Widget {
|
||||||
/// sets window (to be used for top level widget from Window implementation). TODO: hide it from API?
|
/// sets window (to be used for top level widget from Window implementation). TODO: hide it from API?
|
||||||
@property void window(Window window) { _window = window; }
|
@property void window(Window window) { _window = window; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// widget list holder
|
/// widget list holder
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue