settings dialog support

This commit is contained in:
Vadim Lopatin 2015-03-03 17:57:12 +03:00
parent 042c675431
commit e654035492
4 changed files with 209 additions and 117 deletions

View File

@ -57,6 +57,12 @@ class SettingsFile {
protected bool _loaded; protected bool _loaded;
@property Setting setting() { return _setting; } @property Setting setting() { return _setting; }
@property Setting copySettings() {
return _setting.clone();
}
@property void applySettings(Setting settings) {
_setting.apply(settings);
}
alias setting this; alias setting this;
/// create settings file object; if filename is provided, attempts to load settings from file /// create settings file object; if filename is provided, attempts to load settings from file
@ -241,6 +247,13 @@ final class Setting {
@property int length() { @property int length() {
return cast(int)list.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 /// ordered map
@ -315,6 +328,16 @@ final class Setting {
@property int length() { @property int length() {
return cast(int)list.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 /// read as string value
@property inout(string) str() inout { @property inout(string) str() inout {
final switch(_type) with(SettingType) { final switch(_type) with(SettingType) {

View File

@ -1,105 +1,105 @@
// Written in the D programming language. // Written in the D programming language.
/** /**
This module contains definition of signals / listeners. This module contains definition of signals / listeners.
Similar to std.signals. Similar to std.signals.
Unlike std.signals, supports any types of delegates, and as well interfaces with single method. Unlike std.signals, supports any types of delegates, and as well interfaces with single method.
Unlike std.signals, can support return types for slots. Unlike std.signals, can support return types for slots.
Caution: unlike std.signals, does not disconnect signal from slots belonging to destroyed objects. Caution: unlike std.signals, does not disconnect signal from slots belonging to destroyed objects.
Listener here stand for holder of single delegate (slot). Listener here stand for holder of single delegate (slot).
Signal is the same but supports multiple slots. Signal is the same but supports multiple slots.
Listener has smaller memory footprint, but allows only single slot. 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. Can be declared either using list of result value and argument types, or by interface name with single method.
Synopsis: Synopsis:
---- ----
import dlangui.core.signals; import dlangui.core.signals;
interface SomeInterface { interface SomeInterface {
bool someMethod(string s, int n); bool someMethod(string s, int n);
} }
class Foo : SomeInterface { class Foo : SomeInterface {
override bool someMethod(string s, int n) { override bool someMethod(string s, int n) {
writeln("someMethod called ", s, ", ", n); writeln("someMethod called ", s, ", ", n);
return n > 10; // can return value return n > 10; // can return value
} }
} }
// Listener! can hold arbitrary number of connected slots // Listener! can hold arbitrary number of connected slots
// declare using list of return value and parameter types // declare using list of return value and parameter types
Listener!(bool, string, n) signal1; Listener!(bool, string, n) signal1;
Foo f = new Foo(); Foo f = new Foo();
// when signal is defined as type list, you can use delegate // 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; } signal1 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; }
// or method reference // or method reference
signal1 = &f.someMethod; signal1 = &f.someMethod;
// declare using interface with single method // declare using interface with single method
Listener!SomeInterface signal2; Listener!SomeInterface signal2;
// you still can use any delegate // you still can use any delegate
signal2 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } 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 // but for class method which overrides interface method, you can use simple syntax
signal2 = f; // it will automatically take &f.someMethod signal2 = f; // it will automatically take &f.someMethod
// call listener(s) either by opcall or explicit emit // call listener(s) either by opcall or explicit emit
signal1("text", 1); signal1("text", 1);
signal1.emit("text", 2); signal1.emit("text", 2);
signal2.emit("text", 3); signal2.emit("text", 3);
// check if any slit is connected // check if any slit is connected
if (signal1.assigned) if (signal1.assigned)
writeln("has listeners"); writeln("has listeners");
// Signal! can hold arbitrary number of connected slots // Signal! can hold arbitrary number of connected slots
// declare using list of return value and parameter types // declare using list of return value and parameter types
Signal!(bool, string, n) signal3; Signal!(bool, string, n) signal3;
// add listeners via connect call // add listeners via connect call
signal3.connect(bool delegate(string, int) { return false; }); signal3.connect(bool delegate(string, int) { return false; });
// or via ~= operator // or via ~= operator
signal3 ~= bool delegate(string, int) { return false; }; signal3 ~= bool delegate(string, int) { return false; };
// declare using interface with single method // declare using interface with single method
Signal!SomeInterface signal4; Signal!SomeInterface signal4;
// you can connect several slots to signal // you can connect several slots to signal
signal4 ~= f; signal4 ~= f;
signal4 ~= bool delegate(string, int) { return true; } signal4 ~= bool delegate(string, int) { return true; }
// calling of listeners of Signal! is similar to Listener! // calling of listeners of Signal! is similar to Listener!
// using opCall // using opCall
bool res = signal4("blah", 5); bool res = signal4("blah", 5);
// call listeners using emit // call listeners using emit
bool res = signal4.emit("blah", 5); bool res = signal4.emit("blah", 5);
// you can disconnect individual slots // you can disconnect individual slots
// using disconnect() // using disconnect()
signal4.disconnect(f); signal4.disconnect(f);
// or -= operator // or -= operator
signal4 -= f; signal4 -= f;
---- ----
Copyright: Vadim Lopatin, 2014 Copyright: Vadim Lopatin, 2014
License: Boost License 1.0 License: Boost License 1.0
Authors: Vadim Lopatin, coolreader.org@gmail.com Authors: Vadim Lopatin, coolreader.org@gmail.com
*/ */
module dlangui.core.signals; module dlangui.core.signals;
import std.traits; 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 return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
alias params_t = ParameterTypeTuple!(__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); alias slot_t = return_t delegate(params_t);
private Collection!slot_t _listeners; private Collection!slot_t _listeners;
this(ref Signal!T1 v) {
_listeners.addAll(v._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() { final bool assigned() {
return _listeners.length > 0; return _listeners.length > 0;
} }

View File

@ -4,7 +4,7 @@ import dlangui.core.events;
import dlangui.core.i18n; import dlangui.core.i18n;
import dlangui.core.stdaction; import dlangui.core.stdaction;
import dlangui.core.files; import dlangui.core.files;
import dlangui.core.settings; public import dlangui.core.settings;
import dlangui.widgets.controls; import dlangui.widgets.controls;
import dlangui.widgets.lists; import dlangui.widgets.lists;
import dlangui.widgets.layouts; import dlangui.widgets.layouts;
@ -22,6 +22,7 @@ private import std.utf;
private import std.conv : to; private import std.conv : to;
private import std.array : split; private import std.array : split;
/// item on settings page
class SettingsItem { class SettingsItem {
protected string _id; protected string _id;
protected UIString _label; protected UIString _label;
@ -40,20 +41,27 @@ class SettingsItem {
} }
} }
/// items shows checkbox /// checkbox setting
class CheckboxItem : SettingsItem { class CheckboxItem : SettingsItem {
this(string id, UIString label) { private bool _inverse;
this(string id, UIString label, bool inverse = false) {
super(id, label); super(id, label);
_inverse = inverse;
} }
/// create setting widget /// create setting widget
override Widget createWidget(Setting settings) { override Widget createWidget(Setting settings) {
CheckBox res = new CheckBox(_id, _label); CheckBox res = new CheckBox(_id, _label);
Setting setting = settings.settingByPath(_id, SettingType.FALSE); 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; return res;
} }
} }
/// settings page - item of settings tree, can edit several settings
class SettingsPage { class SettingsPage {
protected SettingsPage _parent; protected SettingsPage _parent;
protected ObjectList!SettingsPage _children; protected ObjectList!SettingsPage _children;
@ -78,9 +86,14 @@ class SettingsPage {
return _children[index]; return _children[index];
} }
void addChild(SettingsPage item) { SettingsPage addChild(SettingsPage item) {
_children.add(item); _children.add(item);
item._parent = this; item._parent = this;
return item;
}
SettingsPage addChild(string id, UIString label) {
return addChild(new SettingsPage(id, label));
} }
@property int itemCount() { @property int itemCount() {
@ -92,9 +105,17 @@ class SettingsPage {
return _items[index]; return _items[index];
} }
void addItem(SettingsItem item) { SettingsItem addItem(SettingsItem item) {
_items.add(item); _items.add(item);
item._page = this; 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) /// create page widget (default implementation creates empty page)
@ -110,7 +131,7 @@ class SettingsPage {
} }
TreeItem createTreeItem() { TreeItem createTreeItem() {
return null; return new TreeItem(_id, _label);
} }
} }
@ -118,10 +139,10 @@ class SettingsPage {
class SettingsDialog : Dialog { class SettingsDialog : Dialog {
protected TreeWidget _tree; protected TreeWidget _tree;
protected FrameLayout _frame; protected FrameLayout _frame;
protected SettingsFile _settings; protected Setting _settings;
protected SettingsPage _layout; 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); super(caption, parent, DialogFlag.Modal | DialogFlag.Resizable | DialogFlag.Popup);
_settings = settings; _settings = settings;
_layout = layout; _layout = layout;
@ -152,10 +173,18 @@ class SettingsDialog : Dialog {
minWidth(600).minHeight(400); minWidth(600).minHeight(400);
_tree = new TreeWidget("prop_tree"); _tree = new TreeWidget("prop_tree");
_tree.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT); _tree.layoutHeight(FILL_PARENT).layoutHeight(FILL_PARENT);
_tree.minHeight(200).minWidth(100);
_tree.selectionListener = &onTreeItemSelected; _tree.selectionListener = &onTreeItemSelected;
_tree.fontSize = 16; _tree.fontSize = 16;
_frame = new FrameLayout("prop_pages"); _frame = new FrameLayout("prop_pages");
_frame.minHeight(200).minWidth(100);
createControls(_layout, _tree.items); 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));
} }
} }

View File

@ -3,6 +3,7 @@
ACTION_OK=Ok ACTION_OK=Ok
ACTION_CANCEL=Cancel ACTION_CANCEL=Cancel
ACTION_YES=Yes ACTION_YES=Yes
ACTION_APPLY=Apply
ACTION_NO=No ACTION_NO=No
ACTION_CLOSE=Close ACTION_CLOSE=Close
ACTION_ABORT=Abort ACTION_ABORT=Abort