1 // Written in the D programming language. 2 /++ 3 + Authors: KanzakiKino 4 + Copyright: KanzakiKino 2018 5 + License: LGPL-3.0 6 ++/ 7 module w4d.widget.list; 8 import w4d.layout.placer.lineup, 9 w4d.layout.fill, 10 w4d.parser.colorset, 11 w4d.style.rect, 12 w4d.style.scalar, 13 w4d.style.widget, 14 w4d.task.window, 15 w4d.widget.base, 16 w4d.widget.wrapper, 17 w4d.event, 18 w4d.exception; 19 import gl3n.linalg; 20 import std.algorithm, 21 std.array, 22 std.conv; 23 24 /// A handler that handles changing selection. 25 alias SelectChangeHandler = EventHandler!( void, ListItemWidget[] ); 26 27 /// A widget of list. 28 class ListWidget : Widget 29 { 30 protected ListItemWidget[] _items; 31 /// Child items. 32 @property items () { return _items; } 33 34 /// All selected child items. 35 @property selectedItems () 36 { 37 ListItemWidget[] result; 38 foreach ( item; items ) { 39 if ( item.isSelected ) { 40 result ~= item; 41 } 42 result ~= item.selectedItems; 43 } 44 return result; 45 } 46 /// 47 override @property Widget[] children () 48 { 49 return items.to!( Widget[] ); 50 } 51 52 protected bool _multiselect; 53 /// Whether the list widget is multi-selectable. 54 @property multiselectable () { return _multiselect; } 55 56 protected Widget _dragging; 57 58 /// 59 SelectChangeHandler onSelectChange; 60 61 /// 62 override bool handleMouseButton ( MouseButton btn, bool status, vec2 pos ) 63 { 64 if ( super.handleMouseButton(btn,status,pos) ) return true; 65 66 if ( btn == MouseButton.Left && status ) { 67 if ( auto child = findChildAt( pos ) ) { 68 _dragging = child; 69 70 toggleItem( child.to!ListItemWidget ); 71 return true; 72 } 73 } else if ( btn == MouseButton.Left && !status ) { 74 _dragging = null; 75 } 76 return false; 77 } 78 79 /// 80 this () 81 { 82 super(); 83 parseColorSetsFromFile!"colorset/list.yaml"( style ); 84 setLayout!( FillLayout, VerticalLineupPlacer ); 85 86 _multiselect = false; 87 } 88 89 /// Changes multi-selectable. 90 void setMultiselectable ( bool b ) 91 { 92 deselect(); 93 _multiselect = b; 94 } 95 96 /// Adds an item. 97 void addItem ( ListItemWidget w ) 98 { 99 enforce( w, "Null is invalid." ); 100 _items ~= w; 101 w.setParent( this ); 102 } 103 /// Removes an item. 104 void removeItem ( ListItemWidget w ) 105 { 106 _items = _items.remove!( x => x is w ); 107 } 108 /// Removes all items. 109 void removeAllItems () 110 { 111 _items = []; 112 } 113 114 /// Unselects all items. 115 void deselect () 116 { 117 foreach ( i; items ) { 118 unselectItem( i ); 119 i.deselect(); 120 } 121 } 122 123 /// Selects the item. 124 void selectItem ( ListItemWidget w ) 125 { 126 if ( w.isSelected ) return; 127 if ( !multiselectable ) deselect(); 128 129 w.enableState( WidgetState.Selected ); 130 onSelectChange.call( selectedItems ); 131 } 132 /// Unselects the item. 133 void unselectItem ( ListItemWidget w ) 134 { 135 if ( !w.isSelected ) return; 136 137 w.disableState( WidgetState.Selected ); 138 onSelectChange.call( selectedItems ); 139 } 140 /// Toggles selected state of the item. 141 void toggleItem ( ListItemWidget w ) 142 { 143 if ( w.isSelected ) { 144 unselectItem( w ); 145 } else { 146 selectItem( w ); 147 } 148 } 149 150 /// 151 override @property bool trackable () { return false; } 152 /// 153 override @property bool focusable () { return true; } 154 } 155 156 /// A widget of list item. 157 class ListItemWidget : WrapperWidget 158 { 159 /// 160 this () 161 { 162 super(); 163 164 parseColorSetsFromFile!"colorset/listitem.yaml"( style ); 165 style.box.size.width = Scalar.Auto; 166 style.box.paddings = Rect( 1.mm ); 167 } 168 169 /// Checks if the item is selected. 170 @property isSelected () 171 { 172 return !!(status & WidgetState.Selected); 173 } 174 /// Selected child items. (for tree item widget). 175 @property ListItemWidget[] selectedItems () 176 { 177 return []; 178 } 179 180 /// Changes the parent list widget. 181 void setParent ( ListWidget ) { } 182 /// Unselects all child items. (for tree item widget). 183 void deselect () { } 184 185 /// 186 override @property bool trackable () { return false; } 187 /// 188 override @property bool focusable () { return false; } 189 }