From 88a288c6b2cd3cfe4c385e148ada6f1c3a4a7fed Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Mon, 1 Dec 2014 12:29:34 +0300 Subject: [PATCH] Tree widget, continue development --- examples/example1/src/main.d | 9 ++ res/mdpi/arrow_right_down_black.png | Bin 0 -> 160 bytes res/mdpi/arrow_right_hollow.png | Bin 0 -> 2873 bytes src/dlangui/widgets/tree.d | 153 +++++++++++++++++++++++++--- src/dlangui/widgets/widget.d | 10 +- 5 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 res/mdpi/arrow_right_down_black.png create mode 100644 res/mdpi/arrow_right_hollow.png diff --git a/examples/example1/src/main.d b/examples/example1/src/main.d index 68701bf5..7a2f8852 100644 --- a/examples/example1/src/main.d +++ b/examples/example1/src/main.d @@ -623,6 +623,15 @@ extern (C) int UIAppMain(string[] args) { // tree view example TreeWidget tree = new TreeWidget("TREE1"); 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); diff --git a/res/mdpi/arrow_right_down_black.png b/res/mdpi/arrow_right_down_black.png new file mode 100644 index 0000000000000000000000000000000000000000..e9e92bd81fdf4bd128e748c77ff7d5460b6542f8 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^{2<>&kwQhmoCE(D|?ELZFbYr;B5V#`&w4Hu5qUa2znOd;jvC yprpXfy{XTF);q9j1vGMnEMO8{;lO%p{vYO+NjCN;O<#cAz~JfX=d#Wzp$PzFqASJ# literal 0 HcmV?d00001 diff --git a/res/mdpi/arrow_right_hollow.png b/res/mdpi/arrow_right_hollow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c0aab510e6a5424df2c88c1edf7f80d9056110c GIT binary patch literal 2873 zcmV-93&!+`P)uJ@VVD_UC<6{NG_fI~0ue<-1QkJoA_k0xBC#Thg@9ne9*`iQ#9$Or zQF$}6R&?d%y_c8YA7_1QpS|}zXYYO1x&V;8{kgn!SPFnNo`4_X6{c}T{8k*B#$jdxfFg<9uYy1K45IaYvHg`_dOZM)Sy63ve6hvv z1)yUy0P^?0*fb9UASvow`@mQCp^4`uNg&9uGcn1|&Nk+9SjOUl{-OWr@Hh0;_l(8q z{wNRKos+;6rV8ldy0Owz(}jF`W(JeRp&R{qi2rfmU!TJ;gp(Kmm5I1s5m_f-n#TRsj}B0%?E`vOzxB2#P=n*a3EfYETOrKoe*ICqM@{4K9Go;5xVgZi5G4 z1dM~{UdP6d+Yd3o?MrAqM0Kc|iV92owdyL5UC#5<>aVCa44|hpM4E zs0sQWIt5*Tu0n&*J!lk~f_{hI!w5`*sjxDv4V%CW*ah~3!{C*0BD@;TgA3v9a1~q+ zAA{TB3-ERLHar49hi4Ih5D^-ph8Q6X#0?2VqLBoIkE}zAkxHZUgRb+f=nat zP#6>iMMoK->`~sRLq)(kHo*Vn{;LcG6+edD1=7D>9j^O?D{Qg|tCDK{ym)H7&wDr6*;uGTJg8GHjVbnL{!cWyUB7MT6o-VNo_w8Yq`2<5Ub)hw4L3rj}5@qxMs0 zWMyP6Wy582WNT#4$d1qunl{acmP#w5ouJ*Jy_Zv#bCKi7ZIf$}8d zZdVy&)LYdbX%I9R8VMQ|8r>Q*nyQ)sn)#Z|n)kKvS`4iu ztvy=3T65Yu+7a4Yv^%sXb>ww?bn(=Yu(!=O6^iuTp>)p_Y^{w=i z^lS773}6Fm1Fpe-gF!>Ip{*g$u-szvGhed;vo5pW&GpS$<~8QGEXWp~7V9lKEnZq0SaK{6Sl+dwSOr*Z zvFf(^Xl-N7w{EeXveC4Ov)N}e%%C!Y7^RFWwrE>d+x51mZQt2h+X?JW*!^a2WS?Sx z)P8cQ&Qi|OhNWW;>JChYI)@QQx?`Nj^#uJBl~d&PK+RZLOLos~K(b5>qmrMN0})tOkySZ3_W zICNY@+|jrX%s^&6b2i>5eqa0y%Z;^%^_=a@u3%4b9605ii3Ep)@`TAmhs0fpQ%O!q zl}XcFH*PieWwLj2ZSq`7V9Mc?h17`D)-+sNT-qs~3@?S(ldh7UlRlVXkWrK|vf6I- z?$tAVKYn8-l({mqQ$Q8{O!WzMg`0(=S&msXS#Pt$vrpzo=kRj+a`kh!z=6$;c zwT88(J6|n-WB%w`m$h~4pmp)YIh_ z3ETV2tjiAU!0h1dxU-n=E9e!)6|Z;4?!H=SSy{V>ut&IOq{_dl zbFb#!9eY1iCsp6Bajj|Hr?hX|zPbJE{X++w546-O*Ot`2Kgd0Jx6Z4syT zu9enWavU5N9)I?I-1m1*_?_rJ$vD~agVqoG+9++s?NEDe`%Fht$4F;X=in*dQ{7$m zU2Q)a|9JSc+Uc4zvS-T963!N$T{xF_ZuWe}`RNOZ7sk3{yB}PPym+f8xTpV;-=!;; zJuhGEb?H5K#o@~7t9DmUU1MD9xNd#Dz0azz?I)|B+WM{g+Xrk0I&awC=o(x)cy`EX z=)z6+o0o6-+`4{y+3mqQ%kSJBju{@g%f35#FZJHb`&swrA8dGtepviS>QUumrN{L@ z>;2q1Vm)$Z)P1z?N$8UYW2~{~zhwUMVZ87u`Dx{Z>O|9|`Q+&->FRy-Sjp7DHs zy69KwU-!MxeeuI@&cF4|M9z%AfP?@5 z`Tzg`fam}Kbua(`>RI+y?e7jT@qQ9J+u00v@9M??UT0FeNZZv4c`00009a7bBm z000XU000XU0RWnu7ytkO2XskIMF-vt0S5{-Fu!NF0000)Nkl 0; } + /// returns number of children of this widget @property int childCount() { return _children.count; } /// 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 int childIndex(TreeItem item) { return _children.indexOf(item); } - + /// notify listeners + protected void onUpdate(TreeItem item) { + if (_parent) + _parent.onUpdate(item); + } } interface OnTreeContentChangeListener { @@ -109,36 +152,114 @@ interface OnTreeContentChangeListener { class TreeItems : TreeItem { // 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) { super(ID, hscrollbarMode, vscrollbarMode); + contentWidget = new VerticalLayout("TREE_CONTENT"); + _tree = new TreeItems(); + _tree.listener.connect(this); + _needUpdateWidgets = true; } - /// process horizontal scrollbar event - override bool onHScroll(ScrollEvent event) { - return true; + ~this() { + if (_tree) { + destroy(_tree); + _tree = null; + } } - /// process vertical scrollbar event - override bool onVScroll(ScrollEvent event) { - return true; + /** Override to use custom tree item widgets. */ + protected Widget createItemWidget(TreeItem item) { + return new TreeItemWidget(item); } - /// update horizontal scrollbar widget position - override protected void updateHScrollBar() { - // override it + protected void addWidgets(TreeItem item) { + if (item.level > 0) + _contentWidget.addChild(createItemWidget(item)); + for (int i = 0; i < item.childCount; i++) + addWidgets(item.child(i)); } - /// update verticat scrollbar widget position - override protected void updateVScrollBar() { - // override it + protected void updateWidgets() { + _contentWidget.removeAllChildren(); + 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 { diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 49bb6857..b61cf896 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -1207,7 +1207,10 @@ class Widget { /// sets window (to be used for top level widget from Window implementation). TODO: hide it from API? @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 @@ -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 override int childIndex(Widget item) { return _children.indexOf(item); } + + override void removeAllChildren() { + _children.clear(); + } + }