mirror of https://github.com/buggins/dlangui.git
Tree widget, continue
This commit is contained in:
parent
88a288c6b2
commit
8a64035050
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue