diff --git a/src/dlangui/core/settings.d b/src/dlangui/core/settings.d index f3a2fd66..381b7116 100644 --- a/src/dlangui/core/settings.d +++ b/src/dlangui/core/settings.d @@ -57,6 +57,12 @@ class SettingsFile { protected bool _loaded; @property Setting setting() { return _setting; } + @property Setting copySettings() { + return _setting.clone(); + } + @property void applySettings(Setting settings) { + _setting.apply(settings); + } alias setting this; /// create settings file object; if filename is provided, attempts to load settings from file @@ -241,6 +247,13 @@ final class Setting { @property int length() { return cast(int)list.length; } + /// deep copy + void copyFrom(ref SettingArray v) { + list.length = v.list.length; + for(int i = 0; i < v.list.length; i++) { + list[i] = v.list[i].clone(); + } + } } /// ordered map @@ -315,6 +328,16 @@ final class Setting { @property int length() { return cast(int)list.length; } + /// deep copy + void copyFrom(SettingMap * v) { + list.length = v.list.length; + for(int i = 0; i < v.list.length; i++) { + list[i] = v.list[i].clone(); + } + destroy(map); + foreach(key, value; v.map) + map[key] = value; + } } @@ -374,6 +397,45 @@ final class Setting { } } + void apply(Setting settings) { + } + + /// deep copy of settings + Setting clone() { + Setting res = new Setting(); + res.clear(_type); + final switch(_type) with(SettingType) { + case STRING: + res._store.str = _store.str; + break; + case ARRAY: + res._store.array.copyFrom(_store.array); + break; + case OBJECT: + if (_store.map) { + res._store.map = new SettingMap(); + res._store.map.copyFrom(_store.map); + } + break; + case INTEGER: + res._store.integer = _store.integer; + break; + case UINTEGER: + res._store.uinteger = _store.uinteger; + break; + case FLOAT: + res._store.floating = _store.floating; + break; + case TRUE: + case FALSE: + case NULL: + break; + } + res._changed = false; + return res; + } + + /// read as string value @property inout(string) str() inout { final switch(_type) with(SettingType) { diff --git a/src/dlangui/core/signals.d b/src/dlangui/core/signals.d index db97d396..35bc9c30 100644 --- a/src/dlangui/core/signals.d +++ b/src/dlangui/core/signals.d @@ -1,105 +1,105 @@ -// Written in the D programming language. - -/** - -This module contains definition of signals / listeners. - -Similar to std.signals. - -Unlike std.signals, supports any types of delegates, and as well interfaces with single method. - -Unlike std.signals, can support return types for slots. - -Caution: unlike std.signals, does not disconnect signal from slots belonging to destroyed objects. - -Listener here stand for holder of single delegate (slot). - -Signal is the same but supports multiple slots. - -Listener has smaller memory footprint, but allows only single slot. - - -Can be declared either using list of result value and argument types, or by interface name with single method. - - -Synopsis: - ----- -import dlangui.core.signals; - -interface SomeInterface { - bool someMethod(string s, int n); -} -class Foo : SomeInterface { - override bool someMethod(string s, int n) { - writeln("someMethod called ", s, ", ", n); - return n > 10; // can return value - } -} - -// Listener! can hold arbitrary number of connected slots - -// declare using list of return value and parameter types -Listener!(bool, string, n) signal1; - -Foo f = new Foo(); -// when signal is defined as type list, you can use delegate -signal1 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } -// or method reference -signal1 = &f.someMethod; - -// declare using interface with single method -Listener!SomeInterface signal2; -// you still can use any delegate -signal2 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } -// but for class method which overrides interface method, you can use simple syntax -signal2 = f; // it will automatically take &f.someMethod - - -// call listener(s) either by opcall or explicit emit -signal1("text", 1); -signal1.emit("text", 2); -signal2.emit("text", 3); - -// check if any slit is connected -if (signal1.assigned) - writeln("has listeners"); - -// Signal! can hold arbitrary number of connected slots - -// declare using list of return value and parameter types -Signal!(bool, string, n) signal3; - -// add listeners via connect call -signal3.connect(bool delegate(string, int) { return false; }); -// or via ~= operator -signal3 ~= bool delegate(string, int) { return false; }; - -// declare using interface with single method -Signal!SomeInterface signal4; - -// you can connect several slots to signal -signal4 ~= f; -signal4 ~= bool delegate(string, int) { return true; } - -// calling of listeners of Signal! is similar to Listener! -// using opCall -bool res = signal4("blah", 5); -// call listeners using emit -bool res = signal4.emit("blah", 5); - -// you can disconnect individual slots -// using disconnect() -signal4.disconnect(f); -// or -= operator -signal4 -= f; - ----- - -Copyright: Vadim Lopatin, 2014 -License: Boost License 1.0 -Authors: Vadim Lopatin, coolreader.org@gmail.com -*/ +// Written in the D programming language. + +/** + +This module contains definition of signals / listeners. + +Similar to std.signals. + +Unlike std.signals, supports any types of delegates, and as well interfaces with single method. + +Unlike std.signals, can support return types for slots. + +Caution: unlike std.signals, does not disconnect signal from slots belonging to destroyed objects. + +Listener here stand for holder of single delegate (slot). + +Signal is the same but supports multiple slots. + +Listener has smaller memory footprint, but allows only single slot. + + +Can be declared either using list of result value and argument types, or by interface name with single method. + + +Synopsis: + +---- +import dlangui.core.signals; + +interface SomeInterface { + bool someMethod(string s, int n); +} +class Foo : SomeInterface { + override bool someMethod(string s, int n) { + writeln("someMethod called ", s, ", ", n); + return n > 10; // can return value + } +} + +// Listener! can hold arbitrary number of connected slots + +// declare using list of return value and parameter types +Listener!(bool, string, n) signal1; + +Foo f = new Foo(); +// when signal is defined as type list, you can use delegate +signal1 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } +// or method reference +signal1 = &f.someMethod; + +// declare using interface with single method +Listener!SomeInterface signal2; +// you still can use any delegate +signal2 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } +// but for class method which overrides interface method, you can use simple syntax +signal2 = f; // it will automatically take &f.someMethod + + +// call listener(s) either by opcall or explicit emit +signal1("text", 1); +signal1.emit("text", 2); +signal2.emit("text", 3); + +// check if any slit is connected +if (signal1.assigned) + writeln("has listeners"); + +// Signal! can hold arbitrary number of connected slots + +// declare using list of return value and parameter types +Signal!(bool, string, n) signal3; + +// add listeners via connect call +signal3.connect(bool delegate(string, int) { return false; }); +// or via ~= operator +signal3 ~= bool delegate(string, int) { return false; }; + +// declare using interface with single method +Signal!SomeInterface signal4; + +// you can connect several slots to signal +signal4 ~= f; +signal4 ~= bool delegate(string, int) { return true; } + +// calling of listeners of Signal! is similar to Listener! +// using opCall +bool res = signal4("blah", 5); +// call listeners using emit +bool res = signal4.emit("blah", 5); + +// you can disconnect individual slots +// using disconnect() +signal4.disconnect(f); +// or -= operator +signal4 -= f; + +---- + +Copyright: Vadim Lopatin, 2014 +License: Boost License 1.0 +Authors: Vadim Lopatin, coolreader.org@gmail.com +*/ module dlangui.core.signals; import std.traits; @@ -172,13 +172,13 @@ struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0])); alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0])); alias slot_t = return_t delegate(params_t); - private Collection!slot_t _listeners; - - this(ref Signal!T1 v) { - _listeners.addAll(v._listeners); - } + private Collection!slot_t _listeners; - /// returns true if listener is assigned + this(ref Signal!T1 v) { + _listeners.addAll(v._listeners); + } + + /// returns true if listener is assigned final bool assigned() { return _listeners.length > 0; } diff --git a/src/dlangui/dialogs/settingsdialog.d b/src/dlangui/dialogs/settingsdialog.d index 9a568b86..97851dc9 100644 --- a/src/dlangui/dialogs/settingsdialog.d +++ b/src/dlangui/dialogs/settingsdialog.d @@ -4,7 +4,7 @@ import dlangui.core.events; import dlangui.core.i18n; import dlangui.core.stdaction; import dlangui.core.files; -import dlangui.core.settings; +public import dlangui.core.settings; import dlangui.widgets.controls; import dlangui.widgets.lists; import dlangui.widgets.layouts; @@ -22,6 +22,7 @@ private import std.utf; private import std.conv : to; private import std.array : split; +/// item on settings page class SettingsItem { protected string _id; protected UIString _label; @@ -40,20 +41,27 @@ class SettingsItem { } } -/// items shows checkbox +/// checkbox setting class CheckboxItem : SettingsItem { - this(string id, UIString label) { + private bool _inverse; + this(string id, UIString label, bool inverse = false) { super(id, label); + _inverse = inverse; } /// create setting widget override Widget createWidget(Setting settings) { CheckBox res = new CheckBox(_id, _label); Setting setting = settings.settingByPath(_id, SettingType.FALSE); - res.checked = setting.boolean; + res.checked = setting.boolean ^ _inverse; + res.onCheckChangeListener = delegate(Widget source, bool checked) { + setting.boolean = checked ^ _inverse; + return true; + }; return res; } } +/// settings page - item of settings tree, can edit several settings class SettingsPage { protected SettingsPage _parent; protected ObjectList!SettingsPage _children; @@ -78,9 +86,14 @@ class SettingsPage { return _children[index]; } - void addChild(SettingsPage item) { + SettingsPage addChild(SettingsPage item) { _children.add(item); item._parent = this; + return item; + } + + SettingsPage addChild(string id, UIString label) { + return addChild(new SettingsPage(id, label)); } @property int itemCount() { @@ -92,9 +105,17 @@ class SettingsPage { return _items[index]; } - void addItem(SettingsItem item) { + SettingsItem addItem(SettingsItem item) { _items.add(item); item._page = this; + return item; + } + + /// add checkbox (boolean value) for setting + CheckboxItem addCheckbox(string id, UIString label, bool inverse = false) { + CheckboxItem res = new CheckboxItem(id, label, inverse); + addItem(res); + return res; } /// create page widget (default implementation creates empty page) @@ -110,7 +131,7 @@ class SettingsPage { } TreeItem createTreeItem() { - return null; + return new TreeItem(_id, _label); } } @@ -118,10 +139,10 @@ class SettingsPage { class SettingsDialog : Dialog { protected TreeWidget _tree; protected FrameLayout _frame; - protected SettingsFile _settings; + protected Setting _settings; protected SettingsPage _layout; - this(UIString caption, Window parent, SettingsFile settings, SettingsPage layout) { + this(UIString caption, Window parent, Setting settings, SettingsPage layout) { super(caption, parent, DialogFlag.Modal | DialogFlag.Resizable | DialogFlag.Popup); _settings = settings; _layout = layout; @@ -152,10 +173,18 @@ class SettingsDialog : Dialog { minWidth(600).minHeight(400); _tree = new TreeWidget("prop_tree"); _tree.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT); + _tree.minHeight(200).minWidth(100); _tree.selectionListener = &onTreeItemSelected; _tree.fontSize = 16; _frame = new FrameLayout("prop_pages"); + _frame.minHeight(200).minWidth(100); createControls(_layout, _tree.items); + HorizontalLayout content = new HorizontalLayout("settings_dlg_content"); + content.addChild(_tree); + content.addChild(_frame); + content.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT); + addChild(content); + addChild(createButtonsPanel([ACTION_APPLY, ACTION_CANCEL], 0, 0)); } } diff --git a/views/res/i18n/std_en.ini b/views/res/i18n/std_en.ini index f209c1f6..e9bc09f8 100644 --- a/views/res/i18n/std_en.ini +++ b/views/res/i18n/std_en.ini @@ -3,6 +3,7 @@ ACTION_OK=Ok ACTION_CANCEL=Cancel ACTION_YES=Yes +ACTION_APPLY=Apply ACTION_NO=No ACTION_CLOSE=Close ACTION_ABORT=Abort