Tree widget, continue

This commit is contained in:
Vadim Lopatin 2014-12-01 15:07:26 +03:00
parent 88a288c6b2
commit 8a64035050
4 changed files with 159 additions and 13 deletions

View File

@ -632,6 +632,7 @@ extern (C) int UIAppMain(string[] args) {
tree2.newChild("g2_2", "Group 2 item 2"d); tree2.newChild("g2_2", "Group 2 item 2"d);
tree2.newChild("g2_3", "Group 2 item 3"d); tree2.newChild("g2_3", "Group 2 item 3"d);
tree2.newChild("g2_4", "Group 2 item 4"d); tree2.newChild("g2_4", "Group 2 item 4"d);
tree.items.selectItem(tree.items.child(0));
tabs.addTab(tree, "Tree"d); tabs.addTab(tree, "Tree"d);

View File

@ -156,6 +156,32 @@
fontFamily="MonoSpace" fontFamily="MonoSpace"
fontSize="16" fontSize="16"
/> />
<style id="TREE_ITEM"
padding="2,2,2,2"
margins="0,0,0,0"
minWidth="100"
minHeight="16"
layoutWidth="FILL_PARENT"
layoutHeight="WRAP_CONTENT"
fontFace="Arial"
fontFamily="SansSerif"
fontSize="16">
<state state_focused="true" backgroundColor="#40C0C000"/>
<state state_pressed="true" backgroundColor="#4080C000"/>
<state state_selected="true" backgroundColor="#00F8F9Fa"/>
<state state_hovered="true" backgroundColor="#C0FFFF00"/>
</style>
<style id="TREE_ITEM_EXPAND_ICON"
align="Center"
textFlags="Parent"
/>
<style id="TREE_ITEM_ICON"
align="Center"
textFlags="Parent"
/>
<style id="TREE_ITEM_LABEL"
align="Center"
textFlags="Parent"
/>
</theme> </theme>

View File

@ -30,6 +30,8 @@ module dlangui.widgets.controls;
import dlangui.widgets.widget; import dlangui.widgets.widget;
import dlangui.widgets.layouts; import dlangui.widgets.layouts;
import std.algorithm;
/// vertical spacer to fill empty space in vertical layouts /// vertical spacer to fill empty space in vertical layouts
class VSpacer : Widget { class VSpacer : Widget {
this() { this() {
@ -149,6 +151,15 @@ class ImageWidget : Widget {
_drawableId = null; _drawableId = null;
return this; return this;
} }
/// set custom drawable (not one from resources)
@property ImageWidget drawable(string drawableId) {
if (_drawableId.equal(drawableId))
return this;
_drawableId = drawableId;
_drawable.clear();
requestLayout();
return this;
}
override void measure(int parentWidth, int parentHeight) { override void measure(int parentWidth, int parentHeight) {
DrawableRef img = drawable; DrawableRef img = drawable;

View File

@ -66,12 +66,14 @@ class TreeItem {
bool isFullyExpanded() { bool isFullyExpanded() {
if (!_expanded) if (!_expanded)
return false; return false;
return isVisible(); if (!_parent)
return true;
return _parent.isFullyExpanded();
} }
/** Returns true if all parents are expanded. */ /** Returns true if all parents are expanded. */
bool isVisible() { bool isVisible() {
if (_parent) if (_parent)
return _parent.isVisible(); return _parent.isFullyExpanded();
return false; return false;
} }
void expand() { void expand() {
@ -79,6 +81,19 @@ class TreeItem {
if (_parent) if (_parent)
_parent.expand(); _parent.expand();
} }
void collapse() {
_expanded = false;
}
@property TreeItem selectedItem() {
if (_parent)
return _parent.selectedItem();
return null;
}
bool isSelected() {
return (selectedItem is this);
}
/// get widget text /// get widget text
@property dstring text() { return _text; } @property dstring text() { return _text; }
@ -144,15 +159,32 @@ class TreeItem {
if (_parent) if (_parent)
_parent.onUpdate(item); _parent.onUpdate(item);
} }
protected void toggleExpand(TreeItem item) {
if (_parent)
_parent.toggleExpand(item);
}
protected void selectItem(TreeItem item) {
if (_parent)
_parent.selectItem(item);
}
} }
interface OnTreeContentChangeListener { interface OnTreeContentChangeListener {
void onTreeContentChange(TreeItems source); void onTreeContentChange(TreeItems source);
} }
interface OnTreeStateChangeListener {
void onTreeStateChange(TreeItems source);
}
class TreeItems : TreeItem { class TreeItems : TreeItem {
// signal handler OnTreeContentChangeListener // signal handler OnTreeContentChangeListener
Signal!OnTreeContentChangeListener listener; Signal!OnTreeContentChangeListener contentListener;
Signal!OnTreeStateChangeListener stateListener;
protected TreeItem _selectedItem;
this() { this() {
super("tree"); super("tree");
@ -160,9 +192,31 @@ class TreeItems : TreeItem {
/// notify listeners /// notify listeners
override protected void onUpdate(TreeItem item) { override protected void onUpdate(TreeItem item) {
if (listener.assigned) if (contentListener.assigned)
listener(this); contentListener(this);
} }
override void toggleExpand(TreeItem item) {
if (item.expanded)
item.collapse();
else
item.expand();
if (stateListener.assigned)
stateListener(this);
}
override void selectItem(TreeItem item) {
if (_selectedItem is item)
return;
_selectedItem = item;
if (stateListener.assigned)
stateListener(this);
}
@property override TreeItem selectedItem() {
return _selectedItem;
}
} }
class TreeItemWidget : HorizontalLayout { class TreeItemWidget : HorizontalLayout {
@ -173,6 +227,7 @@ class TreeItemWidget : HorizontalLayout {
TextWidget _label; TextWidget _label;
this(TreeItem item) { this(TreeItem item) {
super(item.id); super(item.id);
styleId = "TREE_ITEM";
_item = item; _item = item;
_tab = new TextWidget("tab"); _tab = new TextWidget("tab");
dchar[] tabText; dchar[] tabText;
@ -180,34 +235,68 @@ class TreeItemWidget : HorizontalLayout {
for (int i = 1; i < _item.level; i++) for (int i = 1; i < _item.level; i++)
tabText ~= singleTab; tabText ~= singleTab;
_tab.text = cast(dstring)tabText; _tab.text = cast(dstring)tabText;
if (_item.hasChildren) {
_expander = new ImageWidget("expander", _item.hasChildren && _item.expanded ? "arrow_right_down_black" : "arrow_right_hollow"); _expander = new ImageWidget("expander", _item.hasChildren && _item.expanded ? "arrow_right_down_black" : "arrow_right_hollow");
if (!_item.hasChildren) _expander.styleId = "TREE_ITEM_EXPANDER_ICON";
_expander.visibility = Visibility.Gone; //_expander.setState(State.Parent);
if (_item.iconRes.length > 0) _expander.onClickListener.connect(delegate(Widget source) {
_item.toggleExpand(_item);
return true;
});
}
onClickListener.connect(delegate(Widget source) {
_item.selectItem(_item);
return true;
});
if (_item.iconRes.length > 0) {
_icon = new ImageWidget("icon", _item.iconRes); _icon = new ImageWidget("icon", _item.iconRes);
_icon.styleId = "TREE_ITEM_ICON";
_icon.setState(State.Parent);
}
_label = new TextWidget("label", _item.text); _label = new TextWidget("label", _item.text);
_label.styleId = "TREE_ITEM_LABEL";
_label.setState(State.Parent);
// append children
addChild(_tab); addChild(_tab);
if (_expander)
addChild(_expander); addChild(_expander);
if (_icon) if (_icon)
addChild(_icon); addChild(_icon);
addChild(_label); addChild(_label);
} }
void updateWidget() {
if (_expander) {
_expander.drawable = _item.expanded ? "arrow_right_down_black" : "arrow_right_hollow";
}
if (_item.isVisible)
visibility = Visibility.Visible;
else
visibility = Visibility.Gone;
if (_item.isSelected)
setState(State.Selected);
else
resetState(State.Selected);
}
} }
class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener { class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener, OnTreeStateChangeListener {
protected TreeItems _tree; protected TreeItems _tree;
@property ref TreeItems items() { return _tree; } @property ref TreeItems items() { return _tree; }
protected bool _needUpdateWidgets; protected bool _needUpdateWidgets;
protected bool _needUpdateWidgetStates;
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"); contentWidget = new VerticalLayout("TREE_CONTENT");
_tree = new TreeItems(); _tree = new TreeItems();
_tree.listener.connect(this); _tree.contentListener.connect(this);
_tree.stateListener.connect(this);
_needUpdateWidgets = true; _needUpdateWidgets = true;
_needUpdateWidgetStates = true;
} }
~this() { ~this() {
@ -235,6 +324,15 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
_needUpdateWidgets = false; _needUpdateWidgets = false;
} }
protected void updateWidgetStates() {
for (int i = 0; i < _contentWidget.childCount; i++) {
TreeItemWidget child = cast(TreeItemWidget)_contentWidget.child(i);
if (child)
child.updateWidget();
}
_needUpdateWidgetStates = false;
}
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) { override void layout(Rect rc) {
if (visibility == Visibility.Gone) { if (visibility == Visibility.Gone) {
@ -242,6 +340,8 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
} }
if (_needUpdateWidgets) if (_needUpdateWidgets)
updateWidgets(); updateWidgets();
if (_needUpdateWidgetStates)
updateWidgetStates();
super.layout(rc); super.layout(rc);
} }
@ -252,6 +352,8 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
} }
if (_needUpdateWidgets) if (_needUpdateWidgets)
updateWidgets(); updateWidgets();
if (_needUpdateWidgetStates)
updateWidgetStates();
super.measure(parentWidth, parentHeight); super.measure(parentWidth, parentHeight);
} }
@ -260,6 +362,12 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
_needUpdateWidgets = true; _needUpdateWidgets = true;
requestLayout(); requestLayout();
} }
override void onTreeStateChange(TreeItems source) {
_needUpdateWidgetStates = true;
requestLayout();
}
} }
class TreeWidget : TreeWidgetBase { class TreeWidget : TreeWidgetBase {