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_3", "Group 2 item 3"d);
tree2.newChild("g2_4", "Group 2 item 4"d);
tree.items.selectItem(tree.items.child(0));
tabs.addTab(tree, "Tree"d);

View File

@ -156,6 +156,32 @@
fontFamily="MonoSpace"
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>

View File

@ -30,6 +30,8 @@ module dlangui.widgets.controls;
import dlangui.widgets.widget;
import dlangui.widgets.layouts;
import std.algorithm;
/// vertical spacer to fill empty space in vertical layouts
class VSpacer : Widget {
this() {
@ -149,6 +151,15 @@ class ImageWidget : Widget {
_drawableId = null;
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) {
DrawableRef img = drawable;

View File

@ -66,12 +66,14 @@ class TreeItem {
bool isFullyExpanded() {
if (!_expanded)
return false;
return isVisible();
if (!_parent)
return true;
return _parent.isFullyExpanded();
}
/** Returns true if all parents are expanded. */
bool isVisible() {
if (_parent)
return _parent.isVisible();
return _parent.isFullyExpanded();
return false;
}
void expand() {
@ -79,6 +81,19 @@ class TreeItem {
if (_parent)
_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
@property dstring text() { return _text; }
@ -144,15 +159,32 @@ class TreeItem {
if (_parent)
_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 {
void onTreeContentChange(TreeItems source);
}
interface OnTreeStateChangeListener {
void onTreeStateChange(TreeItems source);
}
class TreeItems : TreeItem {
// signal handler OnTreeContentChangeListener
Signal!OnTreeContentChangeListener listener;
Signal!OnTreeContentChangeListener contentListener;
Signal!OnTreeStateChangeListener stateListener;
protected TreeItem _selectedItem;
this() {
super("tree");
@ -160,9 +192,31 @@ class TreeItems : TreeItem {
/// notify listeners
override protected void onUpdate(TreeItem item) {
if (listener.assigned)
listener(this);
if (contentListener.assigned)
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 {
@ -173,6 +227,7 @@ class TreeItemWidget : HorizontalLayout {
TextWidget _label;
this(TreeItem item) {
super(item.id);
styleId = "TREE_ITEM";
_item = item;
_tab = new TextWidget("tab");
dchar[] tabText;
@ -180,34 +235,68 @@ class TreeItemWidget : HorizontalLayout {
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)
if (_item.hasChildren) {
_expander = new ImageWidget("expander", _item.hasChildren && _item.expanded ? "arrow_right_down_black" : "arrow_right_hollow");
_expander.styleId = "TREE_ITEM_EXPANDER_ICON";
//_expander.setState(State.Parent);
_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.styleId = "TREE_ITEM_ICON";
_icon.setState(State.Parent);
}
_label = new TextWidget("label", _item.text);
_label.styleId = "TREE_ITEM_LABEL";
_label.setState(State.Parent);
// append children
addChild(_tab);
addChild(_expander);
if (_expander)
addChild(_expander);
if (_icon)
addChild(_icon);
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;
@property ref TreeItems items() { return _tree; }
protected bool _needUpdateWidgets;
protected bool _needUpdateWidgetStates;
this(string ID = null, ScrollBarMode hscrollbarMode = ScrollBarMode.Visible, ScrollBarMode vscrollbarMode = ScrollBarMode.Visible) {
super(ID, hscrollbarMode, vscrollbarMode);
contentWidget = new VerticalLayout("TREE_CONTENT");
_tree = new TreeItems();
_tree.listener.connect(this);
_tree.contentListener.connect(this);
_tree.stateListener.connect(this);
_needUpdateWidgets = true;
_needUpdateWidgetStates = true;
}
~this() {
@ -235,6 +324,15 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
_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).
override void layout(Rect rc) {
if (visibility == Visibility.Gone) {
@ -242,6 +340,8 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
}
if (_needUpdateWidgets)
updateWidgets();
if (_needUpdateWidgetStates)
updateWidgetStates();
super.layout(rc);
}
@ -252,6 +352,8 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
}
if (_needUpdateWidgets)
updateWidgets();
if (_needUpdateWidgetStates)
updateWidgetStates();
super.measure(parentWidth, parentHeight);
}
@ -260,6 +362,12 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener {
_needUpdateWidgets = true;
requestLayout();
}
override void onTreeStateChange(TreeItems source) {
_needUpdateWidgetStates = true;
requestLayout();
}
}
class TreeWidget : TreeWidgetBase {