mirror of https://github.com/buggins/dlangui.git
settings dialog support
This commit is contained in:
parent
042c675431
commit
e654035492
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue