mirror of https://github.com/buggins/dlangui.git
Tree widget, continue development
This commit is contained in:
parent
d06ad1a2f1
commit
88a288c6b2
|
@ -623,6 +623,15 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
// tree view example
|
// tree view example
|
||||||
TreeWidget tree = new TreeWidget("TREE1");
|
TreeWidget tree = new TreeWidget("TREE1");
|
||||||
tree.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
tree.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
||||||
|
TreeItem tree1 = tree.items.newChild("group1", "Group 1"d);
|
||||||
|
tree1.newChild("g1_1", "Group 1 item 1"d);
|
||||||
|
tree1.newChild("g1_2", "Group 1 item 2"d);
|
||||||
|
tree1.newChild("g1_3", "Group 1 item 3"d);
|
||||||
|
TreeItem tree2 = tree.items.newChild("group2", "Group 2"d);
|
||||||
|
tree2.newChild("g2_1", "Group 2 item 1"d);
|
||||||
|
tree2.newChild("g2_2", "Group 2 item 2"d);
|
||||||
|
tree2.newChild("g2_3", "Group 2 item 3"d);
|
||||||
|
tree2.newChild("g2_4", "Group 2 item 4"d);
|
||||||
tabs.addTab(tree, "Tree"d);
|
tabs.addTab(tree, "Tree"d);
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 160 B |
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -3,6 +3,7 @@ module dlangui.widgets.tree;
|
||||||
import dlangui.widgets.widget;
|
import dlangui.widgets.widget;
|
||||||
import dlangui.widgets.controls;
|
import dlangui.widgets.controls;
|
||||||
import dlangui.widgets.scroll;
|
import dlangui.widgets.scroll;
|
||||||
|
import dlangui.widgets.layouts;
|
||||||
import std.conv;
|
import std.conv;
|
||||||
import std.algorithm;
|
import std.algorithm;
|
||||||
|
|
||||||
|
@ -16,6 +17,36 @@ class TreeItem {
|
||||||
protected ObjectList!TreeItem _children;
|
protected ObjectList!TreeItem _children;
|
||||||
protected bool _expanded;
|
protected bool _expanded;
|
||||||
|
|
||||||
|
this(string id) {
|
||||||
|
_id = id;
|
||||||
|
_expanded = true;
|
||||||
|
}
|
||||||
|
this(string id, dstring label, string iconRes = null) {
|
||||||
|
_id = id;
|
||||||
|
_expanded = true;
|
||||||
|
_iconRes = iconRes;
|
||||||
|
_text = label;
|
||||||
|
}
|
||||||
|
this(string id, UIString label, string iconRes = null) {
|
||||||
|
_id = id;
|
||||||
|
_expanded = true;
|
||||||
|
_iconRes = iconRes;
|
||||||
|
_text = label;
|
||||||
|
}
|
||||||
|
this(string id, string labelRes, string iconRes = null) {
|
||||||
|
_id = id;
|
||||||
|
_expanded = true;
|
||||||
|
_iconRes = iconRes;
|
||||||
|
_text = labelRes;
|
||||||
|
}
|
||||||
|
/// create and add new child item
|
||||||
|
TreeItem newChild(string id, dstring label, string iconRes = null) {
|
||||||
|
TreeItem res = new TreeItem(id, label, iconRes);
|
||||||
|
addChild(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@property TreeItem parent() { return _parent; }
|
@property TreeItem parent() { return _parent; }
|
||||||
@property protected TreeItem parent(TreeItem p) { _parent = p; return this; }
|
@property protected TreeItem parent(TreeItem p) { _parent = p; return this; }
|
||||||
@property string id() { return _id; }
|
@property string id() { return _id; }
|
||||||
|
@ -31,11 +62,16 @@ class TreeItem {
|
||||||
}
|
}
|
||||||
@property bool expanded() { return _expanded; }
|
@property bool expanded() { return _expanded; }
|
||||||
@property protected TreeItem expanded(bool expanded) { _expanded = expanded; return this; }
|
@property protected TreeItem expanded(bool expanded) { _expanded = expanded; return this; }
|
||||||
|
/** Returns true if this item and all parents are expanded. */
|
||||||
bool isFullyExpanded() {
|
bool isFullyExpanded() {
|
||||||
if (!_expanded)
|
if (!_expanded)
|
||||||
return false;
|
return false;
|
||||||
|
return isVisible();
|
||||||
|
}
|
||||||
|
/** Returns true if all parents are expanded. */
|
||||||
|
bool isVisible() {
|
||||||
if (_parent)
|
if (_parent)
|
||||||
return _parent.isFullyExpanded();
|
return _parent.isVisible();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void expand() {
|
void expand() {
|
||||||
|
@ -72,6 +108,9 @@ class TreeItem {
|
||||||
return _parent.topParent;
|
return _parent.topParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns true if item has at least one child
|
||||||
|
@property bool hasChildren() { return childCount > 0; }
|
||||||
|
|
||||||
/// returns number of children of this widget
|
/// returns number of children of this widget
|
||||||
@property int childCount() { return _children.count; }
|
@property int childCount() { return _children.count; }
|
||||||
/// returns child by index
|
/// returns child by index
|
||||||
|
@ -100,7 +139,11 @@ class TreeItem {
|
||||||
}
|
}
|
||||||
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
||||||
int childIndex(TreeItem item) { return _children.indexOf(item); }
|
int childIndex(TreeItem item) { return _children.indexOf(item); }
|
||||||
|
/// notify listeners
|
||||||
|
protected void onUpdate(TreeItem item) {
|
||||||
|
if (_parent)
|
||||||
|
_parent.onUpdate(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnTreeContentChangeListener {
|
interface OnTreeContentChangeListener {
|
||||||
|
@ -109,36 +152,114 @@ interface OnTreeContentChangeListener {
|
||||||
|
|
||||||
class TreeItems : TreeItem {
|
class TreeItems : TreeItem {
|
||||||
// signal handler OnTreeContentChangeListener
|
// signal handler OnTreeContentChangeListener
|
||||||
Listener!OnTreeContentChangeListener listener;
|
Signal!OnTreeContentChangeListener listener;
|
||||||
|
|
||||||
|
this() {
|
||||||
|
super("tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// notify listeners
|
||||||
|
override protected void onUpdate(TreeItem item) {
|
||||||
|
if (listener.assigned)
|
||||||
|
listener(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TreeWidgetBase : ScrollWidgetBase {
|
class TreeItemWidget : HorizontalLayout {
|
||||||
|
TreeItem _item;
|
||||||
|
TextWidget _tab;
|
||||||
|
ImageWidget _expander;
|
||||||
|
ImageWidget _icon;
|
||||||
|
TextWidget _label;
|
||||||
|
this(TreeItem item) {
|
||||||
|
super(item.id);
|
||||||
|
_item = item;
|
||||||
|
_tab = new TextWidget("tab");
|
||||||
|
dchar[] tabText;
|
||||||
|
dchar[] singleTab = [' ', ' ', ' ', ' '];
|
||||||
|
for (int i = 1; i < _item.level; i++)
|
||||||
|
tabText ~= singleTab;
|
||||||
|
_tab.text = cast(dstring)tabText;
|
||||||
|
_expander = new ImageWidget("expander", _item.hasChildren && _item.expanded ? "arrow_right_down_black" : "arrow_right_hollow");
|
||||||
|
if (!_item.hasChildren)
|
||||||
|
_expander.visibility = Visibility.Gone;
|
||||||
|
if (_item.iconRes.length > 0)
|
||||||
|
_icon = new ImageWidget("icon", _item.iconRes);
|
||||||
|
_label = new TextWidget("label", _item.text);
|
||||||
|
addChild(_tab);
|
||||||
|
addChild(_expander);
|
||||||
|
if (_icon)
|
||||||
|
addChild(_icon);
|
||||||
|
addChild(_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
|
||||||
|
|
||||||
|
protected TreeItems _tree;
|
||||||
|
|
||||||
|
@property ref TreeItems items() { return _tree; }
|
||||||
|
|
||||||
|
protected bool _needUpdateWidgets;
|
||||||
|
|
||||||
this(string ID = null, ScrollBarMode hscrollbarMode = ScrollBarMode.Visible, ScrollBarMode vscrollbarMode = ScrollBarMode.Visible) {
|
this(string ID = null, ScrollBarMode hscrollbarMode = ScrollBarMode.Visible, ScrollBarMode vscrollbarMode = ScrollBarMode.Visible) {
|
||||||
super(ID, hscrollbarMode, vscrollbarMode);
|
super(ID, hscrollbarMode, vscrollbarMode);
|
||||||
|
contentWidget = new VerticalLayout("TREE_CONTENT");
|
||||||
|
_tree = new TreeItems();
|
||||||
|
_tree.listener.connect(this);
|
||||||
|
_needUpdateWidgets = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// process horizontal scrollbar event
|
~this() {
|
||||||
override bool onHScroll(ScrollEvent event) {
|
if (_tree) {
|
||||||
return true;
|
destroy(_tree);
|
||||||
|
_tree = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// process vertical scrollbar event
|
/** Override to use custom tree item widgets. */
|
||||||
override bool onVScroll(ScrollEvent event) {
|
protected Widget createItemWidget(TreeItem item) {
|
||||||
return true;
|
return new TreeItemWidget(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// update horizontal scrollbar widget position
|
protected void addWidgets(TreeItem item) {
|
||||||
override protected void updateHScrollBar() {
|
if (item.level > 0)
|
||||||
// override it
|
_contentWidget.addChild(createItemWidget(item));
|
||||||
|
for (int i = 0; i < item.childCount; i++)
|
||||||
|
addWidgets(item.child(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// update verticat scrollbar widget position
|
protected void updateWidgets() {
|
||||||
override protected void updateVScrollBar() {
|
_contentWidget.removeAllChildren();
|
||||||
// override it
|
addWidgets(_tree);
|
||||||
|
_needUpdateWidgets = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
|
||||||
|
override void layout(Rect rc) {
|
||||||
|
if (visibility == Visibility.Gone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_needUpdateWidgets)
|
||||||
|
updateWidgets();
|
||||||
|
super.layout(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
|
||||||
|
override void measure(int parentWidth, int parentHeight) {
|
||||||
|
if (visibility == Visibility.Gone) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_needUpdateWidgets)
|
||||||
|
updateWidgets();
|
||||||
|
super.measure(parentWidth, parentHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// listener
|
||||||
|
override void onTreeContentChange(TreeItems source) {
|
||||||
|
_needUpdateWidgets = true;
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TreeWidget : TreeWidgetBase {
|
class TreeWidget : TreeWidgetBase {
|
||||||
|
|
|
@ -1207,7 +1207,10 @@ class Widget {
|
||||||
/// sets window (to be used for top level widget from Window implementation). TODO: hide it from API?
|
/// sets window (to be used for top level widget from Window implementation). TODO: hide it from API?
|
||||||
@property void window(Window window) { _window = window; }
|
@property void window(Window window) { _window = window; }
|
||||||
|
|
||||||
|
void removeAllChildren() {
|
||||||
|
// override
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// object list holder, owning its objects - on destroy of holder, all own objects will be destroyed
|
/// object list holder, owning its objects - on destroy of holder, all own objects will be destroyed
|
||||||
|
@ -1316,4 +1319,9 @@ class WidgetGroup : Widget {
|
||||||
}
|
}
|
||||||
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
/// returns index of widget in child list, -1 if passed widget is not a child of this widget
|
||||||
override int childIndex(Widget item) { return _children.indexOf(item); }
|
override int childIndex(Widget item) { return _children.indexOf(item); }
|
||||||
|
|
||||||
|
override void removeAllChildren() {
|
||||||
|
_children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue