diff --git a/README.md b/README.md
index 7ff3168a..258fdb6b 100644
--- a/README.md
+++ b/README.md
@@ -229,6 +229,7 @@ Clone dependency libraries to dlangui/deps directory
git clone https://github.com/DerelictOrg/DerelictSDL2.git
git clone https://github.com/gecko0307/dlib.git
git clone https://github.com/Dav1dde/gl3n.git
+ git clone https://github.com/nomad-software/x11.git
Open solution file with Mono-D
diff --git a/dlangui-monod-linux.dproj b/dlangui-monod-linux.dproj
index 85e014e9..af1cea46 100644
--- a/dlangui-monod-linux.dproj
+++ b/dlangui-monod-linux.dproj
@@ -139,6 +139,33 @@
-Jviews/res
-Jviews/res/i18n
-Jviews/res/mdpi
+-Jviews/res/hdpi
+ false
+ libdlangui-monod-linux
+ StaticLibrary
+ true
+ 0
+
+
+ bin\DebugX11
+
+
+ USE_X11
+ USE_FREETYPE
+ EmbedStandardResources
+
+
+ obj/DebugX11
+ false
+
+
+ X
+
+
+ -Jviews
+-Jviews/res
+-Jviews/res/i18n
+-Jviews/res/mdpi
-Jviews/res/hdpi
false
libdlangui-monod-linux
@@ -277,5 +304,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/dmledit/dmledit-monod-linux.dproj b/examples/dmledit/dmledit-monod-linux.dproj
index 216af14d..26668157 100644
--- a/examples/dmledit/dmledit-monod-linux.dproj
+++ b/examples/dmledit/dmledit-monod-linux.dproj
@@ -162,6 +162,22 @@
-Jviews/res/hdpi
-Jviews/res/mdpi
-Jviews/res/i18n
+
+ false
+ dmledit-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\DebugX11
+ obj/DebugX11
+ false
+ -Jviews
+-Jviews/res
+-Jviews/res/hdpi
+-Jviews/res/mdpi
+-Jviews/res/i18n
false
dmledit-monod-linux
diff --git a/examples/dmledit/src/dmledit.d b/examples/dmledit/src/dmledit.d
index be60ad80..f87c3254 100644
--- a/examples/dmledit/src/dmledit.d
+++ b/examples/dmledit/src/dmledit.d
@@ -146,17 +146,32 @@ class EditFrame : AppFrame {
}
}
+ void saveSourceFile(string filename) {
+ if (filename.length == 0)
+ filename = _filename;
+ import std.file;
+ _filename = filename;
+ window.windowCaption = toUTF32(filename);
+ _editor.save(filename);
+ }
+
bool onCanClose() {
// todo
return true;
}
- FileDialog createFileDialog(UIString caption) {
- FileDialog dlg = new FileDialog(caption, window, null);
+ FileDialog createFileDialog(UIString caption, bool fileMustExist = true) {
+ uint flags = DialogFlag.Modal | DialogFlag.Resizable;
+ if (fileMustExist)
+ flags |= FileDialogFlag.FileMustExist;
+ FileDialog dlg = new FileDialog(caption, window, null, flags);
dlg.filetypeIcons[".d"] = "text-dml";
return dlg;
}
+ void saveAs() {
+ }
+
/// override to handle specific actions
override bool handleAction(const Action a) {
if (a) {
@@ -169,7 +184,40 @@ class EditFrame : AppFrame {
window.showMessageBox(UIString("About DlangUI ML Editor"d),
UIString("DLangIDE\n(C) Vadim Lopatin, 2015\nhttp://github.com/buggins/dlangui\nSimple editor for DML code"d));
return true;
- case IDEActions.FileOpen:
+ case IDEActions.FileNew:
+ UIString caption;
+ caption = "Create new DML file"d;
+ FileDialog dlg = createFileDialog(caption, false);
+ dlg.addFilter(FileFilterEntry(UIString("DML files"d), "*.dml"));
+ dlg.addFilter(FileFilterEntry(UIString("All files"d), "*.*"));
+ dlg.onDialogResult = delegate(Dialog dlg, const Action result) {
+ if (result.id == ACTION_OPEN.id) {
+ string filename = result.stringParam;
+ _editor.text=""d;
+ saveSourceFile(filename);
+ }
+ };
+ dlg.show();
+ return true;
+ case IDEActions.FileSave:
+ if (_filename.length) {
+ saveSourceFile(_filename);
+ return true;
+ }
+ UIString caption;
+ caption = "Save DML File as"d;
+ FileDialog dlg = createFileDialog(caption, false);
+ dlg.addFilter(FileFilterEntry(UIString("DML files"d), "*.dml"));
+ dlg.addFilter(FileFilterEntry(UIString("All files"d), "*.*"));
+ dlg.onDialogResult = delegate(Dialog dlg, const Action result) {
+ if (result.id == ACTION_OPEN.id) {
+ string filename = result.stringParam;
+ saveSourceFile(filename);
+ }
+ };
+ dlg.show();
+ return true;
+ case IDEActions.FileOpen:
UIString caption;
caption = "Open DML File"d;
FileDialog dlg = createFileDialog(caption);
@@ -196,7 +244,23 @@ class EditFrame : AppFrame {
return false;
}
- void updatePreview() {
+ /// override to handle specific actions state (e.g. change enabled state for supported actions)
+ override bool handleActionStateRequest(const Action a) {
+ switch (a.id) {
+ case IDEActions.HelpAbout:
+ case IDEActions.FileNew:
+ case IDEActions.FileSave:
+ case IDEActions.FileOpen:
+ case IDEActions.DebugStart:
+ case IDEActions.EditPreferences:
+ a.state = ACTION_STATE_ENABLED;
+ return true;
+ default:
+ return super.handleActionStateRequest(a);
+ }
+ }
+
+ void updatePreview() {
dstring dsource = _editor.text;
string source = toUTF8(dsource);
try {
diff --git a/examples/example1/example1-monod-linux.dproj b/examples/example1/example1-monod-linux.dproj
index e3a5e646..d2281597 100644
--- a/examples/example1/example1-monod-linux.dproj
+++ b/examples/example1/example1-monod-linux.dproj
@@ -161,6 +161,22 @@
-Jviews/res/hdpi
-Jviews/res/mdpi
-Jviews/res/i18n
+
+ false
+ example1-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\DebugX11
+ obj/DebugX11
+ false
+ -Jviews
+-Jviews/res
+-Jviews/res/hdpi
+-Jviews/res/mdpi
+-Jviews/res/i18n
false
example1-monod-linux
diff --git a/examples/helloworld/helloworld-monod-linux.dproj b/examples/helloworld/helloworld-monod-linux.dproj
index 76660d6e..c4144341 100644
--- a/examples/helloworld/helloworld-monod-linux.dproj
+++ b/examples/helloworld/helloworld-monod-linux.dproj
@@ -129,6 +129,27 @@
true
0
+
+ bin\DebugX11
+
+
+ USE_X11
+ USE_FREETYPE
+
+
+ obj/DebugX11
+ true
+ false
+ helloworld-monod-linux
+ Executable
+ true
+ 0
+
+
+ -L-lX11
+
+
+
diff --git a/examples/tetris/tetris-monod-linux.dproj b/examples/tetris/tetris-monod-linux.dproj
index 14c6be1a..caefd800 100644
--- a/examples/tetris/tetris-monod-linux.dproj
+++ b/examples/tetris/tetris-monod-linux.dproj
@@ -161,6 +161,22 @@
-Jviews/res/hdpi
-Jviews/res/mdpi
-Jviews/res/i18n
+
+ false
+ tetris-monod-linux
+ Executable
+ true
+ 0
+
+
+ bin\DebugX11
+ obj/DebugX11
+ false
+ -Jviews
+-Jviews/res
+-Jviews/res/hdpi
+-Jviews/res/mdpi
+-Jviews/res/i18n
false
tetris-monod-linux
diff --git a/src/dlangui/dialogs/settingsdialog.d b/src/dlangui/dialogs/settingsdialog.d
index b7b403f0..775cfb8c 100644
--- a/src/dlangui/dialogs/settingsdialog.d
+++ b/src/dlangui/dialogs/settingsdialog.d
@@ -197,6 +197,28 @@ class NumberEditItem : SettingsItem {
}
}
+class StringEditItem : SettingsItem {
+ string _defaultValue;
+ this(string id, UIString label, string defaultValue) {
+ super(id, label);
+ _defaultValue = defaultValue;
+ }
+ /// create setting widget
+ override Widget[] createWidgets(Setting settings) {
+ TextWidget lbl = new TextWidget(_id ~ "-label", _label);
+ EditLine ed = new EditLine(_id ~ "-edit", _label);
+ Setting setting = settings.settingByPath(_id, SettingType.STRING);
+ string value = setting.strDef(_defaultValue);
+ setting.str = value;
+ ed.text = toUTF32(value);
+ ed.onContentChangeListener = delegate(EditableContent content) {
+ string value = toUTF8(content.text);
+ setting.str = value;
+ };
+ return [lbl, ed];
+ }
+}
+
/// settings page - item of settings tree, can edit several settings
class SettingsPage {
protected SettingsPage _parent;
@@ -261,7 +283,14 @@ class SettingsPage {
return res;
}
- StringComboBoxItem addStringComboBox(string id, UIString label, StringListValue[] items) {
+ /// add EditLine to edit string
+ StringEditItem addStringEdit(string id, UIString label, string defaultValue = "") {
+ StringEditItem res = new StringEditItem(id, label, defaultValue);
+ addItem(res);
+ return res;
+ }
+
+ StringComboBoxItem addStringComboBox(string id, UIString label, StringListValue[] items) {
StringComboBoxItem res = new StringComboBoxItem(id, label, items);
addItem(res);
return res;
diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d
index 399f342d..39ac1471 100644
--- a/src/dlangui/platforms/common/platform.d
+++ b/src/dlangui/platforms/common/platform.d
@@ -32,6 +32,10 @@ private import std.algorithm;
private import core.sync.mutex;
private import std.string;
+/// entry point - declare such function to use as main for dlangui app
+extern(C) int UIAppMain(string[] args);
+
+
// specify debug=DebugMouseEvents for logging mouse handling
// specify debug=DebugRedraw for logging drawing and layouts handling
// specify debug=DebugKeys for logging of key events
@@ -175,6 +179,10 @@ class Window {
if (_mainWidget !is null)
_mainWidget.window = this;
}
+
+ // Abstract methods : override in platform implementatino
+
+ /// show window
abstract void show();
/// returns window caption
abstract @property dstring windowCaption();
@@ -1370,6 +1378,9 @@ version (Windows) {
/// put "mixin APP_ENTRY_POINT;" to main module of your dlangui based app
mixin template APP_ENTRY_POINT() {
version (linux) {
+ version(USE_X11) {
+ pragma(lib, "X11");
+ }
version (USE_XCB) {
//pragma(lib, "png");
pragma(lib, "xcb");
@@ -1404,217 +1415,12 @@ mixin template APP_ENTRY_POINT() {
}
}
-version (Windows) {
-
- /// initialize font manager - default implementation
- /// On win32 - first it tries to init freetype, and falls back to win32 fonts.
- /// On linux/mac - tries to init freetype with some hardcoded font paths
- bool initFontManager() {
- import win32.windows;
- import std.utf;
- import dlangui.platforms.windows.win32fonts;
- try {
- /// testing freetype font manager
- version(USE_FREETYPE) {
- Log.v("Trying to init FreeType font manager");
-
- import dlangui.graphics.ftfonts;
- // trying to create font manager
- Log.v("Creating FreeTypeFontManager");
- FreeTypeFontManager ftfontMan = new FreeTypeFontManager();
-
- import win32.shlobj;
- string fontsPath = "c:\\Windows\\Fonts\\";
- static if (true) { // SHGetFolderPathW not found in shell32.lib
- WCHAR[MAX_PATH] szPath;
- static if (false) {
- const CSIDL_FLAG_NO_ALIAS = 0x1000;
- const CSIDL_FLAG_DONT_UNEXPAND = 0x2000;
- if(SUCCEEDED(SHGetFolderPathW(NULL,
- CSIDL_FONTS|CSIDL_FLAG_NO_ALIAS|CSIDL_FLAG_DONT_UNEXPAND,
- NULL,
- 0,
- szPath.ptr)))
- {
- fontsPath = toUTF8(fromWStringz(szPath));
- }
- } else {
- if (GetWindowsDirectory(szPath.ptr, MAX_PATH - 1)) {
- fontsPath = toUTF8(fromWStringz(szPath));
- Log.i("Windows directory: ", fontsPath);
- fontsPath ~= "\\Fonts\\";
- Log.i("Fonts directory: ", fontsPath);
- }
- }
- }
- Log.v("Registering fonts");
- ftfontMan.registerFont(fontsPath ~ "arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "arialbd.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "arialbi.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "ariali.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "cour.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "courbd.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "courbi.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "couri.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "times.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "timesbd.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "timesbi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "timesi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "consola.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "consolab.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "consolai.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "consolaz.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "verdana.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "verdanab.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Bold);
- ftfontMan.registerFont(fontsPath ~ "verdanai.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Normal);
- ftfontMan.registerFont(fontsPath ~ "verdanaz.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Bold);
- if (ftfontMan.registeredFontCount()) {
- FontManager.instance = ftfontMan;
- } else {
- Log.w("No fonts registered in FreeType font manager. Disabling FreeType.");
- destroy(ftfontMan);
- }
- }
- } catch (Exception e) {
- Log.e("Cannot create FreeTypeFontManager - falling back to win32");
- }
-
- // use Win32 font manager
- if (FontManager.instance is null) {
- FontManager.instance = new Win32FontManager();
- }
- return true;
- }
-
-} else {
- import dlangui.graphics.ftfonts;
- bool registerFonts(FreeTypeFontManager ft, string path) {
- import std.file;
- if (!exists(path) || !isDir(path))
- return false;
- ft.registerFont(path ~ "DejaVuSans.ttf", FontFamily.SansSerif, "DejaVuSans", false, FontWeight.Normal);
- ft.registerFont(path ~ "DejaVuSans-Bold.ttf", FontFamily.SansSerif, "DejaVuSans", false, FontWeight.Bold);
- ft.registerFont(path ~ "DejaVuSans-Oblique.ttf", FontFamily.SansSerif, "DejaVuSans", true, FontWeight.Normal);
- ft.registerFont(path ~ "DejaVuSans-BoldOblique.ttf", FontFamily.SansSerif, "DejaVuSans", true, FontWeight.Bold);
- ft.registerFont(path ~ "DejaVuSansMono.ttf", FontFamily.MonoSpace, "DejaVuSansMono", false, FontWeight.Normal);
- ft.registerFont(path ~ "DejaVuSansMono-Bold.ttf", FontFamily.MonoSpace, "DejaVuSansMono", false, FontWeight.Bold);
- ft.registerFont(path ~ "DejaVuSansMono-Oblique.ttf", FontFamily.MonoSpace, "DejaVuSansMono", true, FontWeight.Normal);
- ft.registerFont(path ~ "DejaVuSansMono-BoldOblique.ttf", FontFamily.MonoSpace, "DejaVuSansMono", true, FontWeight.Bold);
- return true;
- }
-
- /// initialize font manager - default implementation
- /// On win32 - first it tries to init freetype, and falls back to win32 fonts.
- /// On linux/mac - tries to init freetype with some hardcoded font paths
- bool initFontManager() {
- FreeTypeFontManager ft = new FreeTypeFontManager();
-
- if (!registerFontConfigFonts(ft)) {
- // TODO: use FontConfig
- Log.w("No fonts found using FontConfig. Trying hardcoded paths.");
- ft.registerFonts("/usr/share/fonts/truetype/dejavu/");
- ft.registerFonts("/usr/share/fonts/TTF/");
- ft.registerFonts("/usr/share/fonts/dejavu/");
- ft.registerFonts("/usr/share/fonts/truetype/ttf-dejavu/"); // let it compile on Debian Wheezy
- version(OSX) {
- ft.registerFont("/Library/Fonts/Arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Arial Narrow.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Arial Narrow Bold.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Arial Narrow Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Arial Narrow Bold Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Courier New.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Courier New Bold.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Courier New Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Courier New Bold Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Bold);
- ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Normal);
- ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Bold);
- }
- }
-
- if (!ft.registeredFontCount)
- return false;
-
- FontManager.instance = ft;
- return true;
- }
-}
-
-/// call this when all resources are supposed to be freed to report counts of non-freed resources by type
-void releaseResourcesOnAppExit() {
-
- //
- debug setAppShuttingDownFlag();
-
- debug {
- if (Widget.instanceCount() > 0) {
- Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount);
- }
- }
-
- currentTheme = null;
- drawableCache = null;
- imageCache = null;
- FontManager.instance = null;
-
- debug {
- if (DrawBuf.instanceCount > 0) {
- Log.e("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount);
- }
- if (Style.instanceCount > 0) {
- Log.e("Non-zero Style instance count when exiting: ", Style.instanceCount);
- }
- if (ImageDrawable.instanceCount > 0) {
- Log.e("Non-zero ImageDrawable instance count when exiting: ", ImageDrawable.instanceCount);
- }
- if (Drawable.instanceCount > 0) {
- Log.e("Non-zero Drawable instance count when exiting: ", Drawable.instanceCount);
- }
- version (USE_FREETYPE) {
- import dlangui.graphics.ftfonts;
- if (FreeTypeFontFile.instanceCount > 0) {
- Log.e("Non-zero FreeTypeFontFile instance count when exiting: ", FreeTypeFontFile.instanceCount);
- }
- if (FreeTypeFont.instanceCount > 0) {
- Log.e("Non-zero FreeTypeFont instance count when exiting: ", FreeTypeFont.instanceCount);
- }
- }
- }
-}
-
+/// initialize font manager on startup
+extern(C) bool initFontManager();
/// initialize logging (for win32 - to file ui.log, for other platforms - stderr; log level is TRACE for debug builds, and WARN for release builds)
-void initLogs() {
- version (Windows) {
- debug {
- Log.setFileLogger(new std.stdio.File("ui.log", "w"));
- } else {
- // no logging unless version ForceLogs is set
- version(ForceLogs) {
- Log.setFileLogger(new std.stdio.File("ui.log", "w"));
- Log.i("Logging to file ui.log");
- }
- }
- } else {
- Log.setStderrLogger();
- }
- debug {
- Log.setLogLevel(LogLevel.Trace);
- } else {
- version(ForceLogs) {
- Log.setLogLevel(LogLevel.Trace);
- Log.i("Log level: trace");
- } else {
- Log.setLogLevel(LogLevel.Warn);
- Log.i("Log level: warn");
- }
- }
- Log.i("Logger is initialized");
-}
+extern(C) void initLogs();
+/// 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/platforms/common/startup.d b/src/dlangui/platforms/common/startup.d
new file mode 100644
index 00000000..fd7f704d
--- /dev/null
+++ b/src/dlangui/platforms/common/startup.d
@@ -0,0 +1,226 @@
+module dlangui.platforms.common.startup;
+
+public import dlangui.core.events;
+public import dlangui.widgets.styles;
+public import dlangui.graphics.fonts;
+public import dlangui.graphics.resources;
+
+version(USE_FREETYPE) {
+ public import dlangui.graphics.ftfonts;
+}
+
+version (Windows) {
+
+ /// initialize font manager - default implementation
+ /// On win32 - first it tries to init freetype, and falls back to win32 fonts.
+ /// On linux/mac - tries to init freetype with some hardcoded font paths
+ extern(C) bool initFontManager() {
+ import win32.windows;
+ import std.utf;
+ import dlangui.platforms.windows.win32fonts;
+ try {
+ /// testing freetype font manager
+ version(USE_FREETYPE) {
+ Log.v("Trying to init FreeType font manager");
+
+ import dlangui.graphics.ftfonts;
+ // trying to create font manager
+ Log.v("Creating FreeTypeFontManager");
+ FreeTypeFontManager ftfontMan = new FreeTypeFontManager();
+
+ import win32.shlobj;
+ string fontsPath = "c:\\Windows\\Fonts\\";
+ static if (true) { // SHGetFolderPathW not found in shell32.lib
+ WCHAR[MAX_PATH] szPath;
+ static if (false) {
+ const CSIDL_FLAG_NO_ALIAS = 0x1000;
+ const CSIDL_FLAG_DONT_UNEXPAND = 0x2000;
+ if(SUCCEEDED(SHGetFolderPathW(NULL,
+ CSIDL_FONTS|CSIDL_FLAG_NO_ALIAS|CSIDL_FLAG_DONT_UNEXPAND,
+ NULL,
+ 0,
+ szPath.ptr)))
+ {
+ fontsPath = toUTF8(fromWStringz(szPath));
+ }
+ } else {
+ if (GetWindowsDirectory(szPath.ptr, MAX_PATH - 1)) {
+ fontsPath = toUTF8(fromWStringz(szPath));
+ Log.i("Windows directory: ", fontsPath);
+ fontsPath ~= "\\Fonts\\";
+ Log.i("Fonts directory: ", fontsPath);
+ }
+ }
+ }
+ Log.v("Registering fonts");
+ ftfontMan.registerFont(fontsPath ~ "arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "arialbd.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "arialbi.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "ariali.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "cour.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "courbd.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "courbi.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "couri.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "times.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "timesbd.ttf", FontFamily.Serif, "Times New Roman", false, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "timesbi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "timesi.ttf", FontFamily.Serif, "Times New Roman", true, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "consola.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "consolab.ttf", FontFamily.MonoSpace, "Consolas", false, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "consolai.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "consolaz.ttf", FontFamily.MonoSpace, "Consolas", true, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "verdana.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "verdanab.ttf", FontFamily.SansSerif, "Verdana", false, FontWeight.Bold);
+ ftfontMan.registerFont(fontsPath ~ "verdanai.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Normal);
+ ftfontMan.registerFont(fontsPath ~ "verdanaz.ttf", FontFamily.SansSerif, "Verdana", true, FontWeight.Bold);
+ if (ftfontMan.registeredFontCount()) {
+ FontManager.instance = ftfontMan;
+ } else {
+ Log.w("No fonts registered in FreeType font manager. Disabling FreeType.");
+ destroy(ftfontMan);
+ }
+ }
+ } catch (Exception e) {
+ Log.e("Cannot create FreeTypeFontManager - falling back to win32");
+ }
+
+ // use Win32 font manager
+ if (FontManager.instance is null) {
+ FontManager.instance = new Win32FontManager();
+ }
+ return true;
+ }
+
+} else {
+ import dlangui.graphics.ftfonts;
+ bool registerFonts(FreeTypeFontManager ft, string path) {
+ import std.file;
+ if (!exists(path) || !isDir(path))
+ return false;
+ ft.registerFont(path ~ "DejaVuSans.ttf", FontFamily.SansSerif, "DejaVuSans", false, FontWeight.Normal);
+ ft.registerFont(path ~ "DejaVuSans-Bold.ttf", FontFamily.SansSerif, "DejaVuSans", false, FontWeight.Bold);
+ ft.registerFont(path ~ "DejaVuSans-Oblique.ttf", FontFamily.SansSerif, "DejaVuSans", true, FontWeight.Normal);
+ ft.registerFont(path ~ "DejaVuSans-BoldOblique.ttf", FontFamily.SansSerif, "DejaVuSans", true, FontWeight.Bold);
+ ft.registerFont(path ~ "DejaVuSansMono.ttf", FontFamily.MonoSpace, "DejaVuSansMono", false, FontWeight.Normal);
+ ft.registerFont(path ~ "DejaVuSansMono-Bold.ttf", FontFamily.MonoSpace, "DejaVuSansMono", false, FontWeight.Bold);
+ ft.registerFont(path ~ "DejaVuSansMono-Oblique.ttf", FontFamily.MonoSpace, "DejaVuSansMono", true, FontWeight.Normal);
+ ft.registerFont(path ~ "DejaVuSansMono-BoldOblique.ttf", FontFamily.MonoSpace, "DejaVuSansMono", true, FontWeight.Bold);
+ return true;
+ }
+
+ /// initialize font manager - default implementation
+ /// On win32 - first it tries to init freetype, and falls back to win32 fonts.
+ /// On linux/mac - tries to init freetype with some hardcoded font paths
+ extern(C) bool initFontManager() {
+ FreeTypeFontManager ft = new FreeTypeFontManager();
+
+ if (!registerFontConfigFonts(ft)) {
+ // TODO: use FontConfig
+ Log.w("No fonts found using FontConfig. Trying hardcoded paths.");
+ ft.registerFonts("/usr/share/fonts/truetype/dejavu/");
+ ft.registerFonts("/usr/share/fonts/TTF/");
+ ft.registerFonts("/usr/share/fonts/dejavu/");
+ ft.registerFonts("/usr/share/fonts/truetype/ttf-dejavu/"); // let it compile on Debian Wheezy
+ version(OSX) {
+ ft.registerFont("/Library/Fonts/Arial.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Arial Narrow.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Arial Narrow Bold.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Arial Narrow Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Arial Narrow Bold Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Courier New.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Courier New Bold.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Courier New Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Courier New Bold Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Bold);
+ ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Normal);
+ ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Bold);
+ }
+ }
+
+ if (!ft.registeredFontCount)
+ return false;
+
+ FontManager.instance = ft;
+ return true;
+ }
+}
+
+
+/// initialize logging (for win32 - to file ui.log, for other platforms - stderr; log level is TRACE for debug builds, and WARN for release builds)
+extern (C) void initLogs() {
+ version (Windows) {
+ debug {
+ Log.setFileLogger(new std.stdio.File("ui.log", "w"));
+ } else {
+ // no logging unless version ForceLogs is set
+ version(ForceLogs) {
+ Log.setFileLogger(new std.stdio.File("ui.log", "w"));
+ Log.i("Logging to file ui.log");
+ }
+ }
+ } else {
+ Log.setStderrLogger();
+ }
+ debug {
+ Log.setLogLevel(LogLevel.Trace);
+ } else {
+ version(ForceLogs) {
+ Log.setLogLevel(LogLevel.Trace);
+ Log.i("Log level: trace");
+ } else {
+ Log.setLogLevel(LogLevel.Warn);
+ Log.i("Log level: warn");
+ }
+ }
+ Log.i("Logger is initialized");
+}
+
+/// call this when all resources are supposed to be freed to report counts of non-freed resources by type
+extern (C) void releaseResourcesOnAppExit() {
+
+ //
+ debug setAppShuttingDownFlag();
+
+ debug {
+ if (Widget.instanceCount() > 0) {
+ Log.e("Non-zero Widget instance count when exiting: ", Widget.instanceCount);
+ }
+ }
+
+ currentTheme = null;
+ drawableCache = null;
+ imageCache = null;
+ FontManager.instance = null;
+
+ debug {
+ if (DrawBuf.instanceCount > 0) {
+ Log.e("Non-zero DrawBuf instance count when exiting: ", DrawBuf.instanceCount);
+ }
+ if (Style.instanceCount > 0) {
+ Log.e("Non-zero Style instance count when exiting: ", Style.instanceCount);
+ }
+ if (ImageDrawable.instanceCount > 0) {
+ Log.e("Non-zero ImageDrawable instance count when exiting: ", ImageDrawable.instanceCount);
+ }
+ if (Drawable.instanceCount > 0) {
+ Log.e("Non-zero Drawable instance count when exiting: ", Drawable.instanceCount);
+ }
+ version (USE_FREETYPE) {
+ import dlangui.graphics.ftfonts;
+ if (FreeTypeFontFile.instanceCount > 0) {
+ Log.e("Non-zero FreeTypeFontFile instance count when exiting: ", FreeTypeFontFile.instanceCount);
+ }
+ if (FreeTypeFont.instanceCount > 0) {
+ Log.e("Non-zero FreeTypeFont instance count when exiting: ", FreeTypeFont.instanceCount);
+ }
+ }
+ }
+}
diff --git a/src/dlangui/platforms/sdl/sdlapp.d b/src/dlangui/platforms/sdl/sdlapp.d
index 91710b41..a8894125 100644
--- a/src/dlangui/platforms/sdl/sdlapp.d
+++ b/src/dlangui/platforms/sdl/sdlapp.d
@@ -1074,7 +1074,7 @@ class SDLPlatform : Platform {
case SDL_KEYDOWN:
SDLWindow w = getWindow(event.key.windowID);
if (w) {
- w.processKeyEvent(KeyAction.KeyDown, event.key.keysym.sym, event.key.keysym.mod);
+ w.processKeyEvent(KeyAction.KeyDown, event.key.keysym.sym, event.key.keysym.mod);
SDL_StartTextInput();
}
break;
@@ -1177,9 +1177,6 @@ class SDLPlatform : Platform {
protected SDLWindow[uint] _windowMap;
}
-// entry point
-extern(C) int UIAppMain(string[] args);
-
version (Windows) {
import win32.windows;
import dlangui.platforms.windows.win32fonts;
@@ -1266,9 +1263,6 @@ version (Windows) {
extern(C) int DLANGUImain(string[] args)
{
-
- initLogs();
-
return sdlmain(args);
}
}
@@ -1309,7 +1303,7 @@ int sdlmain(string[] args) {
}
SDL_DisplayMode displayMode;
- if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS) != 0) {
+ if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_EVENTS|SDL_INIT_NOPARACHUTE) != 0) {
Log.e("Cannot init SDL2");
return 2;
}
@@ -1336,7 +1330,8 @@ int sdlmain(string[] args) {
if (!sdl.connect()) {
return 1;
}
- Platform.setInstance(sdl);
+
+ Platform.setInstance(sdl);
int res = 0;
diff --git a/src/dlangui/platforms/x11/x11app.d b/src/dlangui/platforms/x11/x11app.d
new file mode 100644
index 00000000..2b99130a
--- /dev/null
+++ b/src/dlangui/platforms/x11/x11app.d
@@ -0,0 +1,252 @@
+module dlangui.platforms.x11.x11app;
+
+version (USE_X11):
+
+import dlangui.core.logger;
+import dlangui.core.events;
+import dlangui.core.files;
+import dlangui.graphics.drawbuf;
+import dlangui.graphics.fonts;
+import dlangui.graphics.ftfonts;
+import dlangui.graphics.resources;
+import dlangui.widgets.styles;
+import dlangui.widgets.widget;
+import dlangui.platforms.common.platform;
+
+import x11.Xlib;
+import x11.Xutil;
+import x11.Xtos;
+import x11.X;
+
+pragma(lib, "X11");
+
+private __gshared Display * x11display;
+private __gshared int x11screen;
+
+class X11Window : dlangui.platforms.common.platform.Window {
+ protected X11Platform _platform;
+ protected dstring _caption;
+ protected x11.Xlib.Window _win;
+ protected GC _gc;
+
+ this(X11Platform platform, dstring caption, dlangui.platforms.common.platform.Window parent, uint flags, uint width = 0, uint height = 0) {
+ _platform = platform;
+ _caption = caption;
+ debug Log.d("Creating SDL window");
+ _dx = width;
+ _dy = height;
+ //create(flags);
+
+ /* get the colors black and white (see section for details) */
+ ulong black, white;
+ black = BlackPixel(x11display, x11screen); /* get color black */
+ white = WhitePixel(x11display, x11screen); /* get color white */
+
+ /* once the display is initialized, create the window.
+ This window will be have be 200 pixels across and 300 down.
+ It will have the foreground white and background black
+ */
+ _win = XCreateSimpleWindow(x11display, DefaultRootWindow(x11display), 0, 0,
+ _dx, _dy, 5, white, black);
+
+ /* here is where some properties of the window can be set.
+ The third and fourth items indicate the name which appears
+ at the top of the window and the name of the minimized window
+ respectively.
+ */
+ XSetStandardProperties(x11display, _win, cast(char*)"My Window".ptr, cast(char*)"HI!".ptr, None, cast(char**)null, 0, cast(XSizeHints*)null);
+
+ /* this routine determines which types of input are allowed in
+ the input. see the appropriate section for details...
+ */
+ XSelectInput(x11display, _win, ExposureMask|ButtonPressMask|KeyPressMask);
+
+ /* create the Graphics Context */
+ _gc = XCreateGC(x11display, _win, 0, cast(XGCValues*)null);
+
+ /* here is another routine to set the foreground and background
+ colors _currently_ in use in the window.
+ */
+ XSetBackground(x11display, _gc, white);
+ XSetForeground(x11display, _gc, black);
+
+ /* clear the window and bring it on top of the other windows */
+ XClearWindow(x11display, _win);
+ }
+
+ ~this() {
+ if (_gc)
+ XFreeGC(x11display, _gc);
+ if (_win)
+ XDestroyWindow(x11display, _win);
+ }
+
+ /// show window
+ override void show() {
+ XMapRaised(x11display, _win);
+ }
+
+ override @property dstring windowCaption() {
+ return _caption;
+ }
+
+ override @property void windowCaption(dstring caption) {
+ _caption = caption;
+ //if (_win)
+ // SDL_SetWindowTitle(_win, toUTF8(_caption).toStringz);
+ }
+
+ /// sets window icon
+ override @property void windowIcon(DrawBufRef icon) {
+ }
+ /// request window redraw
+ override void invalidate() {
+ }
+
+ /// close window
+ override void close() {
+ }
+}
+
+class X11Platform : Platform {
+
+ this() {
+ }
+
+ /**
+ * create window
+ * Args:
+ * windowCaption = window caption text
+ * parent = parent Window, or null if no parent
+ * flags = WindowFlag bit set, combination of Resizable, Modal, Fullscreen
+ * width = window width
+ * height = window height
+ *
+ * Window w/o Resizable nor Fullscreen will be created with size based on measurement of its content widget
+ */
+ override dlangui.platforms.common.platform.Window createWindow(dstring windowCaption, dlangui.platforms.common.platform.Window parent, uint flags = WindowFlag.Resizable, uint width = 0, uint height = 0) {
+ int newwidth = width;
+ int newheight = height;
+ X11Window window = new X11Window(this, windowCaption, parent, flags, newwidth, newheight);
+ return window;
+ }
+
+ /**
+ * close window
+ *
+ * Closes window earlier created with createWindow()
+ */
+ override void closeWindow(dlangui.platforms.common.platform.Window w) {
+ }
+
+ /**
+ * Starts application message loop.
+ *
+ * When returned from this method, application is shutting down.
+ */
+ override int enterMessageLoop() {
+ XEvent event; /* the XEvent declaration !!! */
+ KeySym key; /* a dealie-bob to handle KeyPress Events */
+ char[255] text; /* a char buffer for KeyPress Events */
+
+ /* look for events forever... */
+ while(1) {
+ /* get the next event and stuff it into our event variable.
+ Note: only events we set the mask for are detected!
+ */
+ XNextEvent(x11display, &event);
+
+ if (event.type==Expose && event.xexpose.count==0) {
+ /* the window was exposed redraw it! */
+ //redraw();
+ }
+ if (event.type == KeyPress &&
+ XLookupString(&event.xkey, text.ptr, 255, &key, cast(XComposeStatus*)null) == 1) {
+ /* use the XLookupString routine to convert the invent
+ KeyPress data into regular text. Weird but necessary...
+ */
+ if (text[0]=='q') {
+ break;
+ //close_x();
+ }
+ Log.d("You pressed the key", text[0]);
+ }
+ if (event.type==ButtonPress) {
+ /* tell where the mouse Button was Pressed */
+ Log.d("You pressed a button at ",
+ event.xbutton.x, ", ", event.xbutton.y);
+ }
+ }
+ return 0;
+ }
+
+ /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux)
+ override dstring getClipboardText(bool mouseBuffer = false) {
+ return "";
+ }
+
+ /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux)
+ override void setClipboardText(dstring text, bool mouseBuffer = false) {
+ // todo
+ }
+
+ /// calls request layout for all windows
+ override void requestLayout() {
+ // todo
+ }
+}
+
+extern(C) int DLANGUImain(string[] args)
+{
+ initLogs();
+
+ if (!initFontManager()) {
+ Log.e("******************************************************************");
+ Log.e("No font files found!!!");
+ Log.e("Currently, only hardcoded font paths implemented.");
+ Log.e("Probably you can modify sdlapp.d to add some fonts for your system.");
+ Log.e("TODO: use fontconfig");
+ Log.e("******************************************************************");
+ assert(false);
+ }
+
+
+ /* use the information from the environment variable DISPLAY
+ to create the X connection:
+ */
+ x11display = XOpenDisplay(null);
+ if (!x11display) {
+ Log.e("Cannot open X11 display");
+ return 1;
+ }
+
+ x11screen = DefaultScreen(x11display);
+
+
+ currentTheme = createDefaultTheme();
+
+ X11Platform x11platform = new X11Platform();
+
+ Platform.setInstance(x11platform);
+
+ int res = 0;
+
+ version (unittest) {
+ } else {
+ res = UIAppMain(args);
+ }
+
+ //Log.e("Widget instance count after UIAppMain: ", Widget.instanceCount());
+
+ Log.d("Destroying X11 platform");
+ Platform.setInstance(null);
+
+ releaseResourcesOnAppExit();
+
+
+ XCloseDisplay(x11display);
+
+ Log.d("Exiting main width result=", res);
+
+ return res;
+}
diff --git a/src/dlangui/widgets/srcedit.d b/src/dlangui/widgets/srcedit.d
index 80cac927..d012a4de 100644
--- a/src/dlangui/widgets/srcedit.d
+++ b/src/dlangui/widgets/srcedit.d
@@ -54,4 +54,19 @@ class SourceEdit : EditBox {
_filename = null;
return false;
}
+
+ bool save(string fn) {
+ if (content.save(fn)) {
+ _filename = fn;
+ requestLayout();
+ window.update();
+ return true;
+ }
+ // failed
+ requestLayout();
+ window.update();
+ _filename = null;
+ return false;
+ }
+
}
diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d
index f1d77d33..9b11e522 100644
--- a/src/dlangui/widgets/widget.d
+++ b/src/dlangui/widgets/widget.d
@@ -1579,7 +1579,7 @@ class Widget {
alpha = cast(ushort)value;
return true;
}
- mixin(generatePropertySetters("minWidth", "maxWidth", "minHeight", "maxHeight", "layoutWidth", "layoutHeight", "textColor", "backgroundColor", "fontSize", "fontWeight"));
+ mixin(generatePropertySetters("minWidth", "maxWidth", "minHeight", "maxHeight", "layoutWidth", "layoutHeight", "layoutWeight", "textColor", "backgroundColor", "fontSize", "fontWeight"));
if (name.equal("margins")) { // use same value for all sides
margins = Rect(value, value, value, value);
return true;