diff --git a/dlangui-msvc.visualdproj b/dlangui-msvc.visualdproj
index 6fc6c1e8..b8eed368 100644
--- a/dlangui-msvc.visualdproj
+++ b/dlangui-msvc.visualdproj
@@ -8,7 +8,7 @@
0
0
0
- 0
+ 1
0
0
0
@@ -317,7 +317,7 @@
0
0
0
- 0
+ 1
0
0
0
@@ -1276,6 +1276,7 @@
+
diff --git a/examples/dragon/dragon-monod-linux.dproj b/examples/dragon/dragon-monod-linux.dproj
new file mode 100644
index 00000000..c88ff90a
--- /dev/null
+++ b/examples/dragon/dragon-monod-linux.dproj
@@ -0,0 +1,218 @@
+
+
+
+ Debug
+ x64
+ {1E722D80-CF8D-4D98-BEAE-7BC9E6752AC4}
+ DMD2
+ true
+ true
+ true
+
+
+ {A38BEF21-AAFE-4115-A978-63B7C8C2FBD1}
+
+
+
+
+ ../../src
+ ../../3rdparty
+ ../../deps/DerelictSDL2/source
+ ../../deps/DerelictFT/source
+ ../../deps/DerelictGL3/source
+ ../../deps/DerelictUtil/source
+
+
+
+
+ true
+ bin\Debug
+
+
+ USE_SDL
+ USE_OPENGL
+ USE_FREETYPE
+
+
+ obj/Debug
+ true
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ true
+ bin\Unittest
+
+
+ USE_SDL
+ USE_OPENGL
+ USE_FREETYPE
+
+
+ obj/Unittest
+ true
+ true
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\Release
+
+
+ USE_SDL
+ USE_OPENGL
+ USE_FREETYPE
+
+
+ obj/Release
+ true
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\DebugMinimal
+
+
+ USE_SDL
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/DebugMinimal
+ true
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\ReleaseMinimal
+
+
+ USE_SDL
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/ReleaseMinimal
+ true
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\UnittestMinimal
+
+
+ USE_SDL
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/UnittestMinimal
+ true
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ true
+ bin\DebugX11
+
+
+ USE_X11
+ USE_OPENGL
+ USE_FREETYPE
+
+
+ obj/DebugX11
+ true
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ -L-lX11
+
+
+
+
+ bin\ReleaseX11
+
+
+ USE_X11
+ USE_OPENGL
+ USE_FREETYPE
+
+
+ obj/ReleaseX11
+ true
+ -lX11
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\UnittestX11
+
+
+ USE_X11
+ USE_OPENGL
+ USE_FREETYPE
+
+
+ obj/UnittestX11
+ true
+ -lX11
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\DebugConsole
+
+
+ USE_CONSOLE
+
+
+ obj/DebugConsole
+ false
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\DebugConsole
+ obj/DebugConsole
+ false
+ false
+ dragon-monod-linux
+ Executable
+ true
+ 0
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/dragon/dragon-monod-osx.dproj b/examples/dragon/dragon-monod-osx.dproj
new file mode 100644
index 00000000..692fb0fa
--- /dev/null
+++ b/examples/dragon/dragon-monod-osx.dproj
@@ -0,0 +1,95 @@
+
+
+
+ Debug
+ x64
+ {1B4E43FF-5E6A-4F4E-9A6C-F182EC258112}
+ DMD2
+ true
+ true
+ true
+
+
+ ../../src
+ ../../3rdparty
+ ../../deps/DerelictGL3/source
+ ../../deps/DerelictSDL2/source
+ ../../deps/DerelictFT/source
+ ../../deps/DerelictUtil/source
+
+
+
+
+ true
+ bin\Debug
+
+
+ USE_SDL
+ USE_OPENGL
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/Debug
+ true
+ -Jviews -Jviews/res -Jviews/res/i18n -Jviews/res/mdpi -Jviews/res/hdpi
+ false
+ dragon-monod-osx
+ Executable
+ true
+ 0
+
+
+ bin\Release
+
+
+ USE_SDL
+ USE_OPENGL
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/Release
+ true
+ -Jviews -Jviews/res -Jviews/res/i18n -Jviews/res/mdpi -Jviews/res/hdpi
+ false
+ dragon-monod-osx
+ Executable
+ true
+ 0
+
+
+ true
+ bin\Unittest
+
+
+ USE_SDL
+ USE_OPENGL
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/Unittest
+ true
+ -Jviews -Jviews/res -Jviews/res/i18n -Jviews/res/mdpi -Jviews/res/hdpi
+ true
+ dragon-monod-osx
+ Executable
+ true
+ 0
+
+
+ bin\DebugX11
+ obj/DebugX11
+ false
+ -Jviews -Jviews/res -Jviews/res/i18n -Jviews/res/mdpi -Jviews/res/hdpi
+ false
+ dragon-monod-osx
+ Executable
+ true
+ 0
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/dragon/dragon-monod-windows.dproj b/examples/dragon/dragon-monod-windows.dproj
new file mode 100644
index 00000000..64cbcde2
--- /dev/null
+++ b/examples/dragon/dragon-monod-windows.dproj
@@ -0,0 +1,92 @@
+
+
+
+ Debug
+ x64
+ {1EB161E7-0DB3-459F-86C5-32A84A3F4334}
+ DMD2
+ true
+ true
+ true
+
+
+ {45FB40CD-E99A-4C12-AC52-C13364412E09}
+
+
+
+
+ ../../src
+ ../../3rdparty
+ ../../deps/DerelictSDL2/source
+ ../../deps/DerelictFT/source
+ ../../deps/DerelictGL3/source
+ ../../deps/DerelictUtil/source
+
+
+
+
+ true
+ bin\Debug
+ Executable
+ dragon-monod-windows
+ false
+ true
+ obj\Debug
+
+
+ USE_FREETYPE
+ USE_OPENGL
+ EmbedStandardResources
+ Unicode
+ windows
+
+
+ 0
+ false
+
+
+ bin\Release
+ Executable
+ dragon-monod-windows
+ false
+ true
+ obj\Release
+
+
+ USE_FREETYPE
+ USE_OPENGL
+ EmbedStandardResources
+ Unicode
+ windows
+
+
+ 0
+ false
+
+
+ true
+ bin\Unittest
+ true
+ Executable
+ dragon-monod-windows
+ true
+ true
+ obj\Unittest
+
+
+ USE_FREETYPE
+ USE_OPENGL
+ EmbedStandardResources
+ Unicode
+ windows
+
+
+ 0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/dragon/dragon-msvc.visualdproj b/examples/dragon/dragon-msvc.visualdproj
new file mode 100644
index 00000000..82aa8d2f
--- /dev/null
+++ b/examples/dragon/dragon-msvc.visualdproj
@@ -0,0 +1,830 @@
+
+ {1F5147FF-DB71-4CEC-BF2F-803143E42BCD}
+
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ EmbedStandardResources USE_FREETYPE NO_OPENGL
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 1
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 2
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 1
+ 1
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2.043
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views views/res views/res/i18n views/res/mdpi views/res/hdpi
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ EmbedStandardResources USE_OPENGL USE_FREETYPE
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 0
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 1
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ EmbedStandardResources USE_FREETYPE NO_OPENGL
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 1
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 2
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2.043
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views views/res views/res/i18n views/res/mdpi views/res/hdpi
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ EmbedStandardResources
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 0
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 1
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views views/res views/res/i18n views/res/mdpi views/res/hdpi
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ USE_CONSOLE EmbedStandardResources ForceLogs
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 1
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 2
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views views/res views/res/i18n views/res/mdpi views/res/hdpi
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ USE_CONSOLE
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 1
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 2
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2.043
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views views/res views/res/i18n views/res/mdpi views/res/hdpi
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ EmbedStandardResources
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 1
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 2
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 2.043
+ 0
+ 0
+ 0
+ 0
+ 0
+ $(CC) -c
+ 1
+ 0
+ $(DMDInstallDir)windows\bin\dmd.exe
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
+ views views/res views/res/i18n views/res/mdpi views/res/hdpi
+ $(ConfigurationName)
+ $(OutDir)
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 1
+ $(IntDir)\$(TargetName).json
+ 0
+
+ 0
+ EmbedStandardResources
+ 0
+ 0
+ 0
+
+
+
+ 0
+
+ 1
+ $(VisualDInstallDir)cv2pdb\cv2pdb.exe
+ 0
+ 0
+ 0
+
+
+
+ ole32.lib kernel32.lib user32.lib comctl32.lib comdlg32.lib
+
+
+
+ $(OutDir)\$(ProjectName).exe
+ 1
+ 2
+ 0
+
+
+
+ *.obj;*.cmd;*.build;*.json;*.dep
+
+
+
+
+
diff --git a/examples/dragon/dub.json b/examples/dragon/dub.json
new file mode 100644
index 00000000..fd7833c5
--- /dev/null
+++ b/examples/dragon/dub.json
@@ -0,0 +1,34 @@
+{
+ "name": "dragon",
+ "description": "dlangui library example Dragon Curve",
+ "homepage": "https://github.com/buggins/dlangui",
+ "license": "Boost",
+ "authors": ["Vadim Lopatin"],
+
+ "targetPath": "bin",
+ "targetName": "dragon",
+ "targetType": "executable",
+
+ "sourceFiles-windows": ["$PACKAGE_DIR/src/win_app.def"],
+
+ "versions": ["EmbedStandardResources"],
+
+ "dependencies": {
+ "dlangui": {"path": "../../"}
+ },
+
+ "configurations" : [
+ {
+ "name" : "default",
+ "subConfigurations" : {
+ "dlangui" : "minimal"
+ }
+ },
+ {
+ "name" : "console",
+ "subConfigurations" : {
+ "dlangui" : "console"
+ }
+ }
+ ]
+}
diff --git a/examples/dragon/src/dragon.d b/examples/dragon/src/dragon.d
new file mode 100644
index 00000000..d06373ae
--- /dev/null
+++ b/examples/dragon/src/dragon.d
@@ -0,0 +1,357 @@
+module app;
+
+import dlangui;
+
+mixin APP_ENTRY_POINT;
+
+import dlangui.widgets.scroll;
+
+class DragonView : ScrollWidget {
+
+ int _scaleX;
+ int _scaleY;
+ int _middleX;
+ int _middleY;
+ int _dx;
+ int _dy;
+ int _x0;
+ int _y0;
+ int _length = 1000;
+ int _dir0 = 0; // either 0 or 1
+ int _straightLen = 10;
+ int _roundLen = 4;
+ uint _bgcolor = 0x101010;
+ uint _grid1color = 0x303030;
+ uint _grid2color = 0x202020;
+ uint _grid3color = 0x181818;
+ uint _curve1color = 0x4050FF;
+ uint _curve2color = 0xFF4040;
+ uint _curve3color = 0x30FF20;
+ uint _curve4color = 0xC000D0;
+ Point[8] _directionVectors = [
+ Point(4, 0),
+ Point(3, -3),
+ Point(0, -4),
+ Point(-3, -3),
+ Point(-4, 0),
+ Point(-3, 3),
+ Point(0, 4),
+ Point(3, 3),
+ ];
+
+ ColorDrawBuf _drawBuf;
+
+ this(string ID) {
+ super(ID);
+ fillParent();
+ //_fullScrollableArea.right = 2048;
+ //_fullScrollableArea.bottom = 2048;
+ setVectors();
+ resize(2048, 2048);
+ }
+
+ void resize(int dx, int dy) {
+ _dx = dx;
+ _dy = dy;
+ _fullScrollableArea.right = dx;
+ _fullScrollableArea.bottom = dy;
+ _visibleScrollableArea.left = dx / 2 - 300;
+ _visibleScrollableArea.top = dy / 2 - 300;
+ _visibleScrollableArea.right = _visibleScrollableArea.left + 400;
+ _visibleScrollableArea.bottom = _visibleScrollableArea.top + 400;
+
+ if (!_drawBuf) {
+ _drawBuf = new ColorDrawBuf(_fullScrollableArea.width, _fullScrollableArea.height);
+ } else {
+ _drawBuf.resize(dx, dy);
+ }
+ _middleX = _fullScrollableArea.width / 2;
+ _middleY = _fullScrollableArea.height / 2;
+ drawCurve();
+ }
+
+ void setVectors() {
+ setVectors(_straightLen, _roundLen);
+ }
+ void setVectors(int straightLen, int roundLen) {
+ if (!straightLen && !roundLen)
+ straightLen = 1;
+ setVectors([
+ Point(straightLen, 0),
+ Point(roundLen, -roundLen),
+ Point(0, -straightLen),
+ Point(-roundLen, -roundLen),
+ Point(-straightLen, 0),
+ Point(-roundLen, roundLen),
+ Point(0, straightLen),
+ Point(roundLen, roundLen),
+ ]);
+ }
+
+ void setVectors(Point[8] vectors) {
+ import std.math : abs;
+ _directionVectors = vectors;
+ int maxx1, maxx2, maxy1, maxy2;
+ for(int i = 0; i < 8; i += 2) {
+ if (maxx1 == 0 || maxx1 < abs(_directionVectors[i].x))
+ maxx1 = abs(_directionVectors[i].x);
+ if (maxy1 == 0 || maxy1 < abs(_directionVectors[i].y))
+ maxy1 = abs(_directionVectors[i].y);
+ if (maxx2 == 0 || maxx1 < abs(_directionVectors[i + 1].x))
+ maxx2 = abs(_directionVectors[i + 1].x);
+ if (maxy2 == 0 || maxy1 < abs(_directionVectors[i + 1].y))
+ maxy2 = abs(_directionVectors[i + 1].y);
+ }
+ if (_dir0 == 0) {
+ _scaleX = maxx1 + maxx2 * 2;
+ _scaleY = maxy1 + maxy2 * 2;
+ } else {
+ _scaleX = maxx1 + maxx2;
+ _scaleY = maxy1 + maxy2;
+ }
+ _x0 = vectors[1].x;
+ _y0 = vectors[1].y;
+ }
+
+ @property int straightLen() {
+ return _straightLen;
+ }
+ @property DragonView straightLen(int n) {
+ if (_straightLen != n) {
+ _straightLen = n;
+ setVectors();
+ drawCurve();
+ }
+ return this;
+ }
+ @property int roundLen() {
+ return _roundLen;
+ }
+ @property DragonView roundLen(int n) {
+ if (_roundLen != n) {
+ _roundLen = n;
+ setVectors();
+ drawCurve();
+ }
+ return this;
+ }
+ @property int length() {
+ return _length;
+ }
+ @property DragonView length(int n) {
+ if (_length != n) {
+ _length = n;
+ drawCurve();
+ }
+ return this;
+ }
+ @property int rotation() {
+ return _dir0;
+ }
+ @property DragonView rotation(int angle) {
+ if (_dir0 != (angle & 7)) {
+ _dir0 = angle & 7;
+ drawCurve();
+ }
+ return this;
+ }
+
+ void drawLine(Point pt1, Point pt2, uint color) {
+ pt1.x += _middleX;
+ pt2.x += _middleX;
+ pt1.y += _middleY;
+ pt2.y += _middleY;
+ _drawBuf.drawLine(pt1, pt2, color);
+ }
+
+ void drawBackground() {
+ _drawBuf.fill(_bgcolor);
+ int i = 0;
+ for (int x = 0; x < _middleX; x += _scaleX) {
+ uint color = _scaleX > 2 ? _grid3color : COLOR_TRANSPARENT;
+ if (i == 0)
+ color = _grid1color;
+ else if ((i & 15) == 0)
+ color = _grid2color;
+ if (color != COLOR_TRANSPARENT) {
+ drawLine(Point(x, -_middleY), Point(x, _middleY), color);
+ drawLine(Point(-x, -_middleY), Point(-x, _middleY), color);
+ if (x == 0) {
+ drawLine(Point(x - 1, -_middleY), Point(x - 1, _middleY), color);
+ drawLine(Point(x + 1, -_middleY), Point(x + 1, _middleY), color);
+ }
+ }
+ i++;
+ }
+ i = 0;
+ for (int y = 0; y < _middleY; y += _scaleY) {
+ uint color = _scaleY > 2 ? _grid3color : COLOR_TRANSPARENT;
+ if (i == 0)
+ color = _grid1color;
+ else if ((i & 15) == 0)
+ color = _grid2color;
+ if (color != COLOR_TRANSPARENT) {
+ drawLine(Point(-_middleX, y), Point(_middleX, y), color);
+ drawLine(Point(-_middleX, -y), Point(_middleX, -y), color);
+ if (y == 0) {
+ drawLine(Point(-_middleX, y - 1), Point(_middleX, y - 1), color);
+ drawLine(Point(-_middleX, y + 1), Point(_middleX, y + 1), color);
+ }
+ }
+ i++;
+ }
+ }
+
+ int getDirectionDelta(int n) {
+ if (n == 0)
+ return -1;
+ for (int i = 0; i < 30; i++) {
+ if (n & (1 << i)) {
+ return (n & (2 << i)) ? 1 : -1;
+ }
+ }
+ return 0;
+ }
+
+ void drawSegment(ref Point currentPoint, ref int currentDir, int n, uint color, int mirror) {
+ int delta = getDirectionDelta(n) * mirror;
+ Point nextPoint = currentPoint + _directionVectors[currentDir];
+ drawLine(currentPoint, nextPoint, color);
+ currentPoint = nextPoint;
+ currentDir = (currentDir + delta) & 7;
+ nextPoint = currentPoint + _directionVectors[currentDir];
+ drawLine(currentPoint, nextPoint, color);
+ currentPoint = nextPoint;
+ currentDir = (currentDir + delta) & 7;
+ }
+
+ void drawCurve() {
+ drawBackground();
+ // segment 1
+ int dir;
+ Point p0;
+ //Point p0 = Point(_directionVectors[_dir0].y, _directionVectors[_dir0 + 1].y );
+ if (_dir0 == 0)
+ p0 = Point(0, _directionVectors[_dir0 + 1].y);
+ else
+ p0 = Point(-_directionVectors[0].x / 2, _directionVectors[_dir0 + 1].y / 2);
+ //Point p0 = Point(-_directionVectors[0].x * 0, -_directionVectors[0].y / 2);
+ Point pt;
+ ///*
+ dir = 0 + _dir0;
+ //Point pt = Point(_directionVectors[dir + 1].x - _scaleX, _directionVectors[dir].y);
+ pt = p0 - (_directionVectors[dir] + _directionVectors[dir + 1]);
+ for(int i = 0; i < _length; i++)
+ drawSegment(pt, dir, i, _curve1color, 1);
+ // segment 2
+ ///*
+ dir = 4 + _dir0;
+ //pt = Point(-_directionVectors[dir + 1].x - _directionVectors[dir].x - _scaleX, _directionVectors[dir].y);
+ pt = p0 + _directionVectors[dir + 1];//_directionVectors[dir].y
+ for(int i = -1; i > -_length; i--)
+ drawSegment(pt, dir, i, _curve2color, -1);
+ //*/
+ ///*
+ // segment 3
+ dir = 4 + _dir0;
+ pt = p0 - (_directionVectors[dir - 1] + _directionVectors[dir]);
+ for(int i = 0; i < _length; i++)
+ drawSegment(pt, dir, i, _curve3color, 1);
+ // segment 4
+ dir = 0 + _dir0;
+ pt = p0 + _directionVectors[(dir - 1) & 7];
+ for(int i = -1; i > -_length; i--)
+ drawSegment(pt, dir, i, _curve4color, -1);
+ //*/
+ invalidate();
+ }
+
+ /// calculate full content size in pixels
+ override Point fullContentSize() {
+ Point sz = Point(_fullScrollableArea.width, _fullScrollableArea.height);
+ return sz;
+ }
+
+ override protected void drawClient(DrawBuf buf) {
+ Point sz = fullContentSize();
+ Point p = scrollPos;
+ //_contentWidget.layout(Rect(_clientRect.left - p.x, _clientRect.top - p.y, _clientRect.left + sz.x - p.x, _clientRect.top + sz.y - p.y));
+ //_contentWidget.onDraw(buf);
+ /// draw source buffer rectangle contents to destination buffer
+ buf.drawFragment(_clientRect.left, _clientRect.top, _drawBuf,
+ Rect(_visibleScrollableArea.left, _visibleScrollableArea.top,
+ _visibleScrollableArea.left + _clientRect.width, _visibleScrollableArea.top + _clientRect.height));
+ //Rect rc = _clientRect;
+ //rc.shrink(5, 5);
+ //buf.fillRect(rc, 0xFF8080);
+ }
+
+
+}
+
+/// entry point for dlangui based application
+extern (C) int UIAppMain(string[] args) {
+
+ // create window
+ Log.d("Creating window");
+ Window window = Platform.instance.createWindow("DlangUI example - Dragon Curve", null);
+ Log.d("Window created");
+
+ DragonView dragon = new DragonView("DRAGON_VIEW");
+
+ auto onScrollEvent = delegate(AbstractSlider source, ScrollEvent event) {
+ if (event.action == ScrollAction.SliderMoved) {
+ switch(source.id) {
+ case "straight":
+ dragon.straightLen = event.position;
+ break;
+ case "round":
+ dragon.roundLen = event.position;
+ break;
+ case "size":
+ dragon.length = event.position;
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+ };
+
+ auto content = new VerticalLayout().fillParent;
+ auto controls1 = new HorizontalLayout().fillHorizontal.padding(3.pointsToPixels).backgroundColor(0xD8D8D8);
+
+ controls1.addChild(new TextWidget(null," Straight"d));
+ auto sliderStraight = new SliderWidget("straight");
+ sliderStraight.setRange(0, 20).position(dragon.straightLen).layoutWeight(1).fillHorizontal;
+ sliderStraight.scrollEvent = onScrollEvent;
+ controls1.addChild(sliderStraight);
+
+ controls1.addChild(new TextWidget(null," Rounding"d));
+ auto sliderRound = new SliderWidget("round");
+ sliderRound.setRange(0, 20).position(dragon.roundLen).layoutWeight(1).fillHorizontal;
+ sliderRound.scrollEvent = onScrollEvent;
+ controls1.addChild(sliderRound);
+
+ auto cbRotate = new CheckBox(null, " Rotate 45`"d);
+ controls1.addChild(cbRotate).checked(dragon.rotation ? true : false);
+ cbRotate.checkChange = delegate(Widget w, bool check) {
+ dragon.rotation(check ? 1 : 0); return true;
+ };
+
+ controls1.addChild(new TextWidget(null," Size"d));
+ auto sliderSize = new SliderWidget("size");
+ sliderSize.setRange(2, 10000).position(dragon.length).layoutWeight(10).fillHorizontal;
+ sliderSize.scrollEvent = onScrollEvent;
+ controls1.addChild(sliderSize);
+
+ content.addChildren([controls1, dragon]);
+
+ window.mainWidget = content;
+
+ // show window
+ window.show();
+
+ // run message loop
+ return Platform.instance.enterMessageLoop();
+}
diff --git a/examples/dragon/src/win_app.def b/examples/dragon/src/win_app.def
new file mode 100644
index 00000000..40b796c7
--- /dev/null
+++ b/examples/dragon/src/win_app.def
@@ -0,0 +1,2 @@
+EXETYPE NT
+SUBSYSTEM WINDOWS
diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d
index 260f38cb..21187060 100644
--- a/src/dlangui/core/types.d
+++ b/src/dlangui/core/types.d
@@ -50,12 +50,18 @@ struct Point {
int x;
int y;
- Point opBinary(string op)(Point v) if (op == "+") {
+ Point opBinary(string op)(Point v) const if (op == "+") {
return Point(x + v.x, y + v.y);
}
- Point opBinary(string op)(Point v) if (op == "-") {
+ Point opBinary(string op)(int n) const if (op == "*") {
+ return Point(x * n, y * n);
+ }
+ Point opBinary(string op)(Point v) const if (op == "-") {
return Point(x - v.x, y - v.y);
}
+ Point opUnary(string op)() const if (op == "-") {
+ return Point(-x, -y);
+ }
int opCmp(ref const Point b) const {
if (x == b.x) return y - b.y;
return x - b.x;
diff --git a/src/dlangui/graphics/resources.d b/src/dlangui/graphics/resources.d
index b595aab1..d0b59601 100644
--- a/src/dlangui/graphics/resources.d
+++ b/src/dlangui/graphics/resources.d
@@ -125,11 +125,13 @@ struct EmbeddedResourceList {
void addResources(EmbeddedResource[] resources) {
list ~= resources;
}
+
void dumpEmbeddedResources() {
foreach(r; list) {
Log.d("EmbeddedResource: ", r.name);
}
}
+
/// find by exact file name
EmbeddedResource * find(string name) {
// search backwards to allow overriding standard resources (which are added first)
@@ -147,6 +149,7 @@ struct EmbeddedResourceList {
return &list[i];
return null;
}
+
/// find by name w/o extension
EmbeddedResource * findAutoExtension(string name) {
string xmlname = name ~ ".xml";
@@ -211,18 +214,6 @@ EmbeddedResource[] embedResources(string[] resourceNames)() {
return embedResources!(resourceNames[0 .. $/2])() ~ embedResources!(resourceNames[$/2 .. $])();
}
-/// split string into lines, autodetect line endings
-string[] splitLines(string s) {
- auto lines_crlf = split(s, "\r\n");
- auto lines_cr = split(s, "\r");
- auto lines_lf = split(s, "\n");
- if (lines_crlf.length >= lines_cr.length && lines_crlf.length >= lines_lf.length)
- return lines_crlf;
- if (lines_cr.length > lines_lf.length)
- return lines_cr;
- return lines_lf;
-}
-
/// embed all resources from list
EmbeddedResource[] embedResourcesFromList(string resourceList)() {
static if (BACKEND_CONSOLE) {
@@ -850,6 +841,7 @@ class ImageDrawable : Drawable {
debug _instanceCount--;
debug(resalloc) Log.d("Destroyed ImageDrawable, count=", _instanceCount);
}
+
@property override int width() {
if (_image.isNull)
return 0;
@@ -857,6 +849,7 @@ class ImageDrawable : Drawable {
return _image.width - 2;
return _image.width;
}
+
@property override int height() {
if (_image.isNull)
return 0;
@@ -864,11 +857,13 @@ class ImageDrawable : Drawable {
return _image.height - 2;
return _image.height;
}
+
@property override Rect padding() {
if (!_image.isNull && _image.hasNinePatch)
return _image.ninePatch.padding;
return Rect(0,0,0,0);
}
+
private static void correctFrameBounds(ref int n1, ref int n2, ref int n3, ref int n4) {
if (n1 > n2) {
//assert(n2 - n1 == n4 - n3);
@@ -877,6 +872,7 @@ class ImageDrawable : Drawable {
n3 = n4 = n3 + middledist;
}
}
+
override void drawTo(DrawBuf buf, Rect rc, uint state = 0, int tilex0 = 0, int tiley0 = 0) {
if (_image.isNull)
return;
@@ -1266,9 +1262,11 @@ class ImageCache {
bool _error; // flag to avoid loading of file if it has been failed once
bool _used;
+
this(string filename) {
_filename = filename;
}
+
/// get normal image
@property ref DrawBufRef get() {
if (!_drawbuf.isNull || _error) {
@@ -1301,6 +1299,7 @@ class ImageCache {
}
return _transformMap[transform];
}
+
/// remove from memory, will cause reload on next access
void compact() {
if (!_drawbuf.isNull)
@@ -1389,13 +1388,12 @@ class DrawableCache {
string _id;
string _filename;
bool _tiled;
- bool _error;
- bool _used;
DrawableRef _drawable;
DrawableRef[ColorTransform] _transformed;
- debug private static __gshared int _instanceCount;
- debug @property static int instanceCount() { return _instanceCount; }
+ bool _error; // flag to avoid loading of file if it has been failed once
+ bool _used;
+
this(string id, string filename, bool tiled) {
_id = id;
_filename = filename;
@@ -1404,6 +1402,9 @@ class DrawableCache {
debug ++_instanceCount;
debug(resalloc) Log.d("Created DrawableCacheItem, count=", _instanceCount);
}
+ debug private static __gshared int _instanceCount;
+ debug @property static int instanceCount() { return _instanceCount; }
+
~this() {
_drawable.clear();
foreach(ref t; _transformed)
@@ -1431,126 +1432,26 @@ class DrawableCache {
}
/// returns drawable (loads from file if necessary)
- @property ref DrawableRef drawable() {
+ @property ref DrawableRef drawable(in ColorTransform transform = ColorTransform()) {
_used = true;
- if (!_drawable.isNull || _error)
- return _drawable;
- if (_filename !is null) {
- // reload from file
- if (_filename.endsWith(".xml")) {
- // XML drawables support
- StateDrawable d = new StateDrawable();
- if (!d.load(_filename)) {
- destroy(d);
- _error = true;
- } else {
- _drawable = d;
- }
- } else if (_filename.endsWith(".tim")) {
- static if (BACKEND_CONSOLE) {
- try {
- // .tim (text image) drawables support
- string s = cast(string)loadResourceBytes(_filename);
- if (s.length) {
- TextDrawable d = new TextDrawable(s);
- if (d.width && d.height) {
- _drawable = d;
- }
- }
- } catch (Exception e) {
- // cannot find drawable file
- }
- }
- if (!_drawable)
- _error = true;
- } else if (_filename.startsWith("#")) {
- // color reference #AARRGGBB, e.g. #5599AA, or a gradient, or BorderDrawable description
- _drawable = createColorDrawable(_filename);
- } else if (_filename.startsWith("{")) {
- // json in {} with text drawable description
- static if (BACKEND_CONSOLE) {
- _drawable = createTextDrawable(_filename);
- }
- } else {
- static if (BACKEND_GUI) {
- // PNG/JPEG drawables support
- DrawBufRef image = imageCache.get(_filename);
- if (!image.isNull) {
- bool ninePatch = _filename.endsWith(".9.png");
- _drawable = new ImageDrawable(image, _tiled, ninePatch);
- } else
- _error = true;
- } else {
- _error = true;
- }
- }
- }
- return _drawable;
- }
- /// returns drawable (loads from file if necessary)
- @property ref DrawableRef drawable(ref ColorTransform transform) {
- if (transform.empty)
- return drawable();
- if (transform in _transformed)
+ if (!transform.empty && transform in _transformed)
return _transformed[transform];
- _used = true;
if (!_drawable.isNull || _error)
return _drawable;
- if (_filename !is null) {
- // reload from file
- if (_filename.endsWith(".xml") || _filename.endsWith(".XML")) {
- // XML drawables support
- StateDrawable d = new StateDrawable();
- if (!d.load(_filename)) {
- Log.e("failed to load .xml drawable from ", _filename);
- destroy(d);
- _error = true;
- } else {
- Log.d("loaded .xml drawable from ", _filename);
- _drawable = d;
- }
- } else if (_filename.endsWith(".tim") || _filename.endsWith(".TIM")) {
- static if (BACKEND_CONSOLE) {
- try {
- // .tim (text image) drawables support
- string s = cast(string)loadResourceBytes(_filename);
- if (s.length) {
- TextDrawable d = new TextDrawable(s);
- if (d.width && d.height) {
- _drawable = d;
- }
- }
- } catch (Exception e) {
- // cannot find drawable file
- }
- }
- if (!_drawable)
- _error = true;
- } else if (_filename.startsWith("{")) {
- // json in {} with text drawable description
- static if (BACKEND_CONSOLE) {
- _drawable = createTextDrawable(_filename);
- }
- } else {
- static if (BACKEND_GUI) {
- // PNG/JPEG drawables support
- DrawBufRef image = imageCache.get(_filename, transform);
- if (!image.isNull) {
- bool ninePatch = _filename.endsWith(".9.png") || _filename.endsWith(".9.PNG");
- _transformed[transform] = new ImageDrawable(image, _tiled, ninePatch);
- return _transformed[transform];
- } else {
- Log.e("failed to load image from ", _filename);
- _error = true;
- }
- } else {
- _error = true;
- }
- }
+
+ // not in cache - create it
+ Drawable dr = makeDrawableFromId(_filename, _tiled, transform);
+ _error = dr is null;
+ if (transform.empty) {
+ _drawable = dr;
+ return _drawable;
+ } else {
+ _transformed[transform] = dr;
+ return _transformed[transform];
}
- return _drawable;
}
}
+
void clear() {
Log.d("DrawableCache.clear()");
_idToFileMap.destroy();
@@ -1568,35 +1469,19 @@ class DrawableCache {
foreach (item; _idToDrawableMap)
item.cleanup();
}
+
string[] _resourcePaths;
string[string] _idToFileMap;
DrawableCacheItem[string] _idToDrawableMap;
DrawableRef _nullDrawable;
- ref DrawableRef get(string id) {
- while (id.length && (id[0] == ' ' || id[0] == '\t' || id[0] == '\r' || id[0] == '\n'))
- id = id[1 .. $];
- if (id.equal("@null"))
- return _nullDrawable;
- if (id in _idToDrawableMap)
- return _idToDrawableMap[id].drawable;
- string resourceId = id;
- bool tiled = false;
- if (id.endsWith(".tiled")) {
- resourceId = id[0..$-6]; // remove .tiled
- tiled = true;
- }
- string filename = findResource(resourceId);
- DrawableCacheItem item = new DrawableCacheItem(id, filename, tiled);
- _idToDrawableMap[id] = item;
- return item.drawable;
- }
- ref DrawableRef get(string id, ref ColorTransform transform) {
- if (transform.empty)
- return get(id);
+
+ ref DrawableRef get(string id, in ColorTransform transform = ColorTransform()) {
+ id = id.strip;
if (id.equal("@null"))
return _nullDrawable;
if (id in _idToDrawableMap)
return _idToDrawableMap[id].drawable(transform);
+ // not found - create it
string resourceId = id;
bool tiled = false;
if (id.endsWith(".tiled")) {
@@ -1604,10 +1489,11 @@ class DrawableCache {
tiled = true;
}
string filename = findResource(resourceId);
- DrawableCacheItem item = new DrawableCacheItem(id, filename, tiled);
+ auto item = new DrawableCacheItem(id, filename, tiled);
_idToDrawableMap[id] = item;
return item.drawable(transform);
}
+
@property string[] resourcePaths() {
return _resourcePaths;
}
@@ -1629,6 +1515,7 @@ class DrawableCache {
_resourcePaths = existingPaths;
clear();
}
+
/// concatenates path with resource id and extension, returns pathname if there is such file, null if file does not exist
private string checkFileName(string path, string id, string extension) {
char[] fn = path.dup;
@@ -1638,10 +1525,11 @@ class DrawableCache {
return fn.dup;
return null;
}
+
/// get resource file full pathname by resource id, null if not found
string findResource(string id) {
if (id.startsWith("#") || id.startsWith("{"))
- return id; // it's not a file name, just a color #AARRGGBB
+ return id; // it's not a file name
if (id in _idToFileMap)
return _idToFileMap[id];
EmbeddedResource * embedded = embeddedResourceList.findAutoExtension(id);
@@ -1697,12 +1585,63 @@ class DrawableCache {
}
-// load text resource
+/// This function takes an id and creates a drawable
+/// id may be a name of file, #directive, color or json
+private Drawable makeDrawableFromId(in string id, in bool tiled, ColorTransform transform = ColorTransform()) {
+ if (id !is null) {
+ if (id.endsWith(".xml") || id.endsWith(".XML")) {
+ // XML drawables support
+ auto d = new StateDrawable;
+ if (!d.load(id)) {
+ Log.e("failed to load .xml drawable from ", id);
+ destroy(d);
+ return null;
+ } else {
+ Log.d("loaded .xml drawable from ", id);
+ return d;
+ }
+ } else if (id.endsWith(".tim") || id.endsWith(".TIM")) {
+ static if (BACKEND_CONSOLE) {
+ try {
+ // .tim (text image) drawables support
+ string s = cast(string)loadResourceBytes(id);
+ if (s.length) {
+ auto d = new TextDrawable(s);
+ if (d.width && d.height) {
+ return d;
+ }
+ }
+ } catch (Exception e) {
+ // cannot find drawable file
+ }
+ }
+ } else if (id.startsWith("#")) {
+ // color reference #AARRGGBB, e.g. #5599AA, a gradient, border description, etc.
+ return createColorDrawable(id);
+ } else if (id.startsWith("{")) {
+ // json in {} with text drawable description
+ static if (BACKEND_CONSOLE) {
+ return createTextDrawable(id);
+ }
+ } else {
+ static if (BACKEND_GUI) {
+ // PNG/JPEG drawables support
+ DrawBufRef image = transform.empty ? imageCache.get(id) : imageCache.get(id, transform);
+ if (!image.isNull) {
+ bool ninePatch = id.endsWith(".9.png") || id.endsWith(".9.PNG");
+ return new ImageDrawable(image, tiled, ninePatch);
+ } else
+ Log.e("Failed to load image from ", id);
+ }
+ }
+ }
+ return null;
+}
+
+
+/// load text resource
string loadTextResource(string resourceId) {
- import dlangui.graphics.resources;
- import std.string : endsWith;
- string filename;
- filename = drawableCache.findResource(resourceId);
+ string filename = drawableCache.findResource(resourceId);
if (!filename) {
Log.e("Object resource file not found for resourceId ", resourceId);
assert(false);
diff --git a/src/dlangui/platforms/common/startup.d b/src/dlangui/platforms/common/startup.d
index ba145a56..70190234 100644
--- a/src/dlangui/platforms/common/startup.d
+++ b/src/dlangui/platforms/common/startup.d
@@ -416,41 +416,7 @@ extern (C) void initResourceManagers() {
Log.d("initResourceManagers() -- finished");
}
-/// register standard widgets to use in DML
-void registerStandardWidgets() {
- Log.d("Registering standard widgets for DML");
- import dlangui.widgets.metadata;
- import dlangui.widgets.widget;
- import dlangui.widgets.layouts;
- import dlangui.widgets.controls;
- import dlangui.widgets.scrollbar;
- import dlangui.widgets.lists;
- import dlangui.widgets.combobox;
- import dlangui.widgets.editors;
- import dlangui.widgets.grid;
- import dlangui.widgets.groupbox;
- import dlangui.widgets.progressbar;
- import dlangui.dialogs.filedlg;
- import dlangui.widgets.menu;
- import dlangui.widgets.tree;
- import dlangui.widgets.tabs;
- mixin(registerWidgets!(FileNameEditLine, DirEditLine, //dlangui.dialogs.filedlg
- ComboBox, ComboEdit, //dlangui.widgets.combobox
- Widget, TextWidget, MultilineTextWidget, Button, ImageWidget, ImageButton, ImageCheckButton, ImageTextButton,
- SwitchButton, RadioButton, CheckBox, HSpacer, VSpacer, CanvasWidget, // dlangui.widgets.controls
- ScrollBar, SliderWidget, // dlangui.widgets.scrollbar
- EditLine, EditBox, LogWidget,//dlangui.widgets.editors
- GroupBox, // dlangui.widgets.groupbox
- ProgressBarWidget, // dlangui.widgets.progressbar
- StringGridWidget, //dlangui.widgets.grid
- VerticalLayout, HorizontalLayout, TableLayout, FrameLayout, // dlangui.widgets.layouts
- ListWidget, StringListWidget,//dlangui.widgets.lists
- MainMenu, //dlangui.widgets.menu
- TreeWidget, // dlangui.widgets.tree
- TabWidget, // dlangui.widgets.tabs
- )("void registerWidgets"));
- registerWidgets();
-}
+
/// call this from shared static this()
extern (C) void initSharedResourceManagers() {
@@ -466,6 +432,9 @@ shared static this() {
//initSharedResourceManagers();
}
+/// register standard widgets to use in DML
+extern(C) void registerStandardWidgets();
+
/// call this when all resources are supposed to be freed to report counts of non-freed resources by type
extern (C) void releaseResourcesOnAppExit() {
diff --git a/src/dlangui/widgets/dmlwidgets.d b/src/dlangui/widgets/dmlwidgets.d
new file mode 100644
index 00000000..edff07ac
--- /dev/null
+++ b/src/dlangui/widgets/dmlwidgets.d
@@ -0,0 +1,117 @@
+module dlangui.widgets.dmlwidgets;
+
+/// register standard widgets to use in DML
+extern(C) void registerStandardWidgets() {
+ import dlangui.core.config;
+ import dlangui.core.logger;
+
+ Log.d("Registering standard widgets for DML");
+
+ import dlangui.widgets.metadata;
+ import dlangui.widgets.widget;
+
+ mixin(registerWidgetMetadataClass!Widget);
+
+ import dlangui.widgets.layouts;
+ mixin(registerWidgetMetadataClass!VerticalLayout);
+ mixin(registerWidgetMetadataClass!HorizontalLayout);
+ mixin(registerWidgetMetadataClass!TableLayout);
+ mixin(registerWidgetMetadataClass!FrameLayout); // dlangui.widgets.layouts
+
+ import dlangui.widgets.controls;
+
+ mixin(registerWidgetMetadataClass!TextWidget);
+ mixin(registerWidgetMetadataClass!MultilineTextWidget);
+ mixin(registerWidgetMetadataClass!Button);
+ mixin(registerWidgetMetadataClass!ImageWidget);
+ mixin(registerWidgetMetadataClass!ImageButton);
+ mixin(registerWidgetMetadataClass!ImageCheckButton);
+ mixin(registerWidgetMetadataClass!ImageTextButton);
+ mixin(registerWidgetMetadataClass!SwitchButton);
+ mixin(registerWidgetMetadataClass!RadioButton);
+ mixin(registerWidgetMetadataClass!CheckBox);
+ mixin(registerWidgetMetadataClass!HSpacer);
+ mixin(registerWidgetMetadataClass!VSpacer);
+ mixin(registerWidgetMetadataClass!CanvasWidget); // dlangui.widgets.controls
+
+ import dlangui.widgets.scrollbar;
+
+ mixin(registerWidgetMetadataClass!ScrollBar);
+ mixin(registerWidgetMetadataClass!SliderWidget); // dlangui.widgets.scrollbar
+
+ import dlangui.widgets.lists;
+
+ mixin(registerWidgetMetadataClass!ListWidget);
+ mixin(registerWidgetMetadataClass!StringListWidget);//dlangui.widgets.lists
+
+
+ import dlangui.widgets.editors;
+
+ mixin(registerWidgetMetadataClass!EditLine);
+ mixin(registerWidgetMetadataClass!EditBox);
+ mixin(registerWidgetMetadataClass!LogWidget);//dlangui.widgets.editors
+
+ import dlangui.widgets.combobox;
+ mixin(registerWidgetMetadataClass!ComboBox);
+ mixin(registerWidgetMetadataClass!ComboEdit); //dlangui.widgets.combobox
+
+ import dlangui.widgets.grid;
+
+ mixin(registerWidgetMetadataClass!StringGridWidget); //dlangui.widgets.grid
+
+ import dlangui.widgets.groupbox;
+
+ mixin(registerWidgetMetadataClass!GroupBox); // dlangui.widgets.groupbox
+
+ import dlangui.widgets.progressbar;
+
+ mixin(registerWidgetMetadataClass!ProgressBarWidget); // dlangui.widgets.progressbar
+
+ import dlangui.widgets.menu;
+
+ mixin(registerWidgetMetadataClass!MainMenu); //dlangui.widgets.menu
+
+ import dlangui.widgets.tree;
+
+ mixin(registerWidgetMetadataClass!TreeWidget); // dlangui.widgets.tree
+
+ import dlangui.widgets.tabs;
+
+ mixin(registerWidgetMetadataClass!TabWidget); // dlangui.widgets.tabs
+
+ import dlangui.dialogs.filedlg;
+
+ mixin(registerWidgetMetadataClass!FileNameEditLine);
+ mixin(registerWidgetMetadataClass!DirEditLine);
+
+ /*
+ mixin (registerWidgets!("void registerWidgets1",
+ FileNameEditLine, DirEditLine, //dlangui.dialogs.filedlg
+ ComboBox, ComboEdit, //dlangui.widgets.combobox
+ // )());
+ //mixin(registerWidgets!("void registerWidgets2",
+ Widget, TextWidget, MultilineTextWidget, Button, ImageWidget, ImageButton, ImageCheckButton, ImageTextButton,
+ ));
+ mixin(registerWidgets!("void registerWidgets3",
+ SwitchButton, RadioButton, CheckBox, HSpacer, VSpacer, CanvasWidget, // dlangui.widgets.controls
+ ScrollBar, SliderWidget, // dlangui.widgets.scrollbar
+ EditLine, EditBox, LogWidget,//dlangui.widgets.editors
+ ));
+ mixin(registerWidgets!("void registerWidgets4",
+ GroupBox, // dlangui.widgets.groupbox
+ ProgressBarWidget, // dlangui.widgets.progressbar
+ StringGridWidget, //dlangui.widgets.grid
+ VerticalLayout, HorizontalLayout, TableLayout, FrameLayout, // dlangui.widgets.layouts
+ MainMenu, //dlangui.widgets.menu
+ TreeWidget, // dlangui.widgets.tree
+ TabWidget, // dlangui.widgets.tabs
+ ));
+
+
+ registerWidgets1();
+ //registerWidgets2();
+ registerWidgets3();
+ registerWidgets4();
+ */
+}
+
diff --git a/src/dlangui/widgets/lists.d b/src/dlangui/widgets/lists.d
index e46ae548..e102a76f 100644
--- a/src/dlangui/widgets/lists.d
+++ b/src/dlangui/widgets/lists.d
@@ -1402,7 +1402,8 @@ class ListWidget : WidgetGroup, OnScrollHandler, OnAdapterChangeHandler {
class StringListWidget : ListWidget {
import std.conv : to;
- import std.datetime : dto = to, StopWatch;
+ import std.datetime.stopwatch : StopWatch;
+ import core.time : dur;
private dstring _searchString;
private StopWatch _stopWatch;
@@ -1479,9 +1480,10 @@ class StringListWidget : ListWidget {
if (event.action == KeyAction.Text) {
if ( !_stopWatch.running) { _stopWatch.start; }
- auto timePassed = _stopWatch.peek.dto!("seconds", float)(); // dtop is std.datetime.to
+ auto timePassed = _stopWatch.peek; //.to!("seconds", float)(); // dtop is std.datetime.to
- if (timePassed > 0.5) _searchString = ""d;
+ if (timePassed > dur!"msecs"(500))
+ _searchString = ""d;
_searchString ~= to!dchar(event.text.toUTF8);
_stopWatch.reset;
diff --git a/src/dlangui/widgets/metadata.d b/src/dlangui/widgets/metadata.d
index 86c630e8..793bd49e 100644
--- a/src/dlangui/widgets/metadata.d
+++ b/src/dlangui/widgets/metadata.d
@@ -83,53 +83,136 @@ template isMarkupType(T)
is(T==StringListValue[]);
}
-string generatePropertiesMetadata(alias T)() {
- version (GENERATE_PROPERTY_METADATA) {
- import std.algorithm.searching;
+private bool hasPropertyAnnotation(alias ti)() {
+ bool res = false;
+ foreach ( attr; __traits(getFunctionAttributes, ti)) {
+ static if (attr == "@property") {
+ res = true;
+ }
+ }
+ return res;
+}
+/*
+string markupPropertyGetterType(alias overload)() {
+ static if (__traits(getProtection, overload) == "public") {
import std.traits;
- import std.meta;
- char[] str;
- str ~= "[";
- foreach(m; __traits(allMembers, T)) {
- static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
- // skip non-public members, only functions that takes 0 or 1 arguments, add only types that parseable in markup
- static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
- static if (isFunction!(__traits(getMember, T, m))) {
- immutable int fnArity = arity!(__traits(getMember, T, m));
- static if (fnArity == 0 || fnArity == 1) {
- // TODO: filter out templates, signals and such
- // iterates class members and process @property functions (note: foreach {if})
- foreach ( attr; __traits(getFunctionAttributes, __traits(getMember, T, m))) if (attr == "@property") {
- alias ret = ReturnType!(__traits(getMember, T, m));
- alias params = Parameters!(__traits(getMember, T, m));
- string typestring;
- static if (fnArity == 0 && !__traits(isTemplate,ret) && isMarkupType!ret)
- typestring = ret.stringof;
- else static if (fnArity == 1 && !__traits(isTemplate,params[0]) && isMarkupType!(params[0]))
- typestring = params[0].stringof;
- if (typestring is null)
- continue;
- str ~= "WidgetPropertyMetadata( typeid(" ~ typestring ~ "), " ~ m.stringof ~ " ), ";
- }
+ static if (is(typeof(overload) == function) && hasPropertyAnnotation!overload) {
+ alias ret = ReturnType!overload;
+ //alias params = Parameters!overload;
+ alias params = ParameterTypeTuple!overload;
+ static if (params.length == 0 && isMarkupType!ret && !isTemplate!ret) {
+ return ret.stringof;
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
+
+string markupPropertySetterType(alias overload)() {
+ static if (__traits(getProtection, overload) == "public") {
+ import std.traits;
+ static if (is(typeof(overload) == function) && hasPropertyAnnotation!overload) {
+ //alias params = Parameters!overload;
+ alias params = ParameterTypeTuple!overload;
+ static if (params.length == 1 && isMarkupType!(params[0]) && !isTemplate!(params[0])) {
+ return params[0].stringof;
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+}
+*/
+
+private template isPublicPropertyFunction(alias overload) {
+ static if (__traits(getProtection, overload) == "public") {
+ static if (hasPropertyAnnotation!overload) {
+ enum isPublicPropertyFunction = true;
+ } else {
+ enum isPublicPropertyFunction = false;
+ }
+ } else {
+ enum isPublicPropertyFunction = false;
+ }
+ //pragma(msg, is(typeof(overload) == function).stringof);
+ //enum isPublicPropertyFunction = (__traits(getProtection, overload) == "public") && is(typeof(overload) == function);// && hasPropertyAnnotation!overload;
+}
+
+private template markupPropertyType(alias overload) {
+ import std.traits : ReturnType, ParameterTypeTuple;
+ alias ret = ReturnType!overload;
+ alias params = ParameterTypeTuple!overload;
+ static if (params.length == 0 && isMarkupType!ret /* && !isTemplate!ret*/) {
+ enum string markupPropertyType = ret.stringof;
+ } else static if (params.length == 1 && isMarkupType!(params[0]) /* && !isTemplate!(params[0])*/) {
+ enum string markupPropertyType = params[0].stringof;
+ } else {
+ enum string markupPropertyType = null;
+ }
+}
+
+private string[] generatePropertyTypeList(alias T)() {
+ import std.meta;
+ string[] properties;
+ properties ~= "[";
+ foreach(m; __traits(allMembers, T)) {
+ static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
+ //static if (is (typeof(__traits(getMember, T, m)) == function)) {
+ static if (__traits(isVirtualFunction, __traits(getMember, T, m))) {//
+ import std.traits : MemberFunctionsTuple;
+ alias overloads = typeof(__traits(getVirtualFunctions, T, m));
+ static if (overloads.length == 2) {
+ static if (isPublicPropertyFunction!(__traits(getVirtualFunctions, T, m)[0]) && isPublicPropertyFunction!(__traits(getVirtualFunctions, T, m)[1])) {
+ //pragma(msg, m ~ " isPublicPropertyFunction0=" ~ isPublicPropertyFunction!(__traits(getVirtualFunctions, T, m)[0]).stringof);
+ //pragma(msg, m ~ " isPublicPropertyFunction1=" ~ isPublicPropertyFunction!(__traits(getVirtualFunctions, T, m)[1]).stringof);
+ immutable getterType = markupPropertyType!(__traits(getVirtualFunctions, T, m)[0]);
+ immutable setterType = markupPropertyType!(__traits(getVirtualFunctions, T, m)[1]);
+ static if (getterType && setterType && getterType == setterType) {
+ //pragma(msg, "markup property found: " ~ getterType ~ " " ~ m.stringof);
+ properties ~= "WidgetPropertyMetadata( typeid(" ~ getterType ~ "), " ~ m.stringof ~ " ), ";
}
}
}
}
}
- str ~= "]";
- return cast(string)str;
+ }
+ properties ~= "]";
+ return properties;
+}
+
+string joinStrings(string[] lines) {
+ if (lines.length == 0)
+ return "";
+ if (lines.length == 1)
+ return lines[0];
+ else
+ return joinStrings(lines[0 .. $/2]) ~ joinStrings(lines[$/2 .. $]);
+}
+
+private string generatePropertiesMetadata(alias T)() if (is(T : Widget)) {
+ version (GENERATE_PROPERTY_METADATA) {
+ //import std.algorithm.searching;
+ //import std.traits : MemberFunctionsTuple;
+ //import std.meta;
+ auto properties = generatePropertyTypeList!T;
+ return joinStrings(properties);
} else {
return "[]";
}
}
-string generateMetadataClass(alias t)() {
+string generateMetadataClass(alias t)() if (is(t : Widget)) {
//pragma(msg, moduleName!t);
import std.traits;
//pragma(msg, getSignalList!t);
//pragma(msg, generatePropertiesMetadata!t);
immutable string metadataClassName = t.stringof ~ "Metadata";
- return "class " ~ metadataClassName ~ " : WidgetMetadataDef { \n" ~
+ return "static class " ~ metadataClassName ~ " : WidgetMetadataDef { \n" ~
" override Widget create() {\n" ~
" return new " ~ moduleName!t ~ "." ~ t.stringof ~ "();\n" ~
" }\n" ~
@@ -142,33 +225,73 @@ string generateMetadataClass(alias t)() {
" override string fullName() {\n" ~
" return \"" ~ moduleName!t ~ "." ~ t.stringof ~ "\";\n" ~
" }\n" ~
- " override WidgetPropertyMetadata[] properties() {" ~
+ " override WidgetPropertyMetadata[] properties() {\n" ~
" return " ~ generatePropertiesMetadata!t ~ ";\n" ~
" }\n" ~
"}\n";
}
-string generateRegisterMetadataClass(alias t)() {
+string generateRegisterMetadataClass(alias t)() if (is(t : Widget)) {
immutable string metadataClassName = t.stringof ~ "Metadata";
- return "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ metadataClassName ~ "());\n";
+ //pragma(msg, metadataClassName);
+ return "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n";
}
-string registerWidgets(T...)(string registerFunctionName = "__gshared static this") {
- string classDefs;
- string registerDefs;
+template registerWidgetMetadataClass(alias t) if (is(t : Widget)) {
+ //pragma(msg, t.stringof);
+ //pragma(msg, generateMetadataClass!t);
+ immutable string classDef = generateMetadataClass!t;
+ immutable string registerDef = "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n";
+ enum registerWidgetMetadataClass = classDef ~ registerDef;
+ //mixin(classDef);
+
+ //pragma(msg, "registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n");
+ //mixin("registerWidgetMetadata(\"" ~ t.stringof ~ "\", new " ~ t.stringof ~ "Metadata" ~ "());\n");
+}
+
+string registerWidgetsFunction(string registerFunctionName = "__gshared static this", T...)() {
+ pragma(msg, registerFunctionName);
+ pragma(msg, T);
+ string[] registerDefs;
foreach(t; T) {
- //pragma(msg, t.stringof);
+ pragma(msg, t.stringof);
//pragma(msg, moduleName!t);
//
- immutable string classdef = generateMetadataClass!t;
- //pragma(msg, classdef);
- immutable string registerdef = generateRegisterMetadataClass!t;
- //pragma(msg, registerdef);
- classDefs ~= classdef;
- registerDefs ~= registerdef;
+ static if (is(t : Widget)) {
+ //pragma(msg, classdef);
+ immutable string registerdef = generateRegisterMetadataClass!t;
+ pragma(msg, registerdef);
+ registerDefs ~= registerdef;
+ } else {
+ pragma(msg, "Skipping non-widget class: " ~ t.stringof);
+ }
//registerWidgetMetadata(T.stringof, new Metadata());
}
- return classDefs ~ "\n" ~ registerFunctionName ~ "() {\n" ~ registerDefs ~ "}";
+ return registerFunctionName ~ "() {\n" ~ joinStrings(registerDefs) ~ "}\n";
+}
+
+string registerWidgets(string registerFunctionName = "__gshared static this", T...)() {
+ pragma(msg, registerFunctionName);
+ pragma(msg, T);
+ string[] classDefs;
+ string[] registerDefs;
+ foreach(t; T) {
+ pragma(msg, t.stringof);
+ //pragma(msg, moduleName!t);
+ //
+ static if (is(t : Widget)) {
+ immutable string classdef = generateMetadataClass!t;
+ //pragma(msg, classdef);
+ immutable string registerdef = generateRegisterMetadataClass!t;
+ pragma(msg, registerdef);
+ classDefs ~= classdef;
+ registerDefs ~= registerdef;
+ } else {
+ pragma(msg, "Skipping non-widget class: " ~ t.stringof);
+ }
+ //registerWidgetMetadata(T.stringof, new Metadata());
+ }
+ return joinStrings(classDefs) ~ "\n" ~ registerFunctionName ~ "() {\n" ~ joinStrings(registerDefs) ~ "}\n";
}
/// returns true if passed name is identifier of registered widget class
diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d
index e769f82a..3ac0e600 100644
--- a/src/dlangui/widgets/widget.d
+++ b/src/dlangui/widgets/widget.d
@@ -637,6 +637,13 @@ public:
/// sets layout weight (while resizing to fill parent, widget will be resized proportionally to this value)
@property Widget layoutWeight(int value) { ownStyle.layoutWeight = value; return this; }
+ /// sets layoutWidth=FILL_PARENT and layoutHeight=FILL_PARENT
+ Widget fillParent() { return layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); }
+ /// sets layoutWidth=FILL_PARENT
+ Widget fillHorizontal() { return layoutWidth(FILL_PARENT); }
+ /// sets layoutHeight=FILL_PARENT
+ Widget fillVertical() { return layoutHeight(FILL_PARENT); }
+
/// returns widget visibility (Visible, Invisible, Gone)
@property Visibility visibility() { return _visibility; }
/// sets widget visibility (Visible, Invisible, Gone)
diff --git a/views/DLANGUI_VERSION b/views/DLANGUI_VERSION
index 705171d0..ce001651 100644
--- a/views/DLANGUI_VERSION
+++ b/views/DLANGUI_VERSION
@@ -1 +1 @@
-v0.9.166
\ No newline at end of file
+v0.9.167
\ No newline at end of file