1 /** 2 Copyright: Copyright (c) 2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 8 module anchovy.gui.behaviors.listbehavior; 9 10 import std.stdio; 11 import std.math : ceil; 12 13 import anchovy.gui; 14 import anchovy.gui.interfaces.iwidgetbehavior; 15 import anchovy.gui.interfaces.ilayout; 16 import anchovy.gui.databinding.list; 17 18 class ViewportLayout : ILayout 19 { 20 /// Called by widget when MinimizeLayout event occurs. 21 void minimize(Widget root) 22 { 23 Widget[] children = root.getPropertyAs!("children", Widget[]); 24 25 if (children.length > 0) 26 { 27 ivec2 childSize = children[0].getPropertyAs!("prefSize", ivec2); 28 29 root.setProperty!("prefSize")(ivec2(childSize.x, 0)); 30 } 31 } 32 33 /// Called by widget when ExpandLayout event occurs. 34 void expand(Widget root) 35 { 36 Widget[] children = root.getPropertyAs!("children", Widget[]); 37 38 if (children.length > 0) 39 { 40 ivec2 childSize = children[0].getPropertyAs!("prefSize", ivec2); 41 ivec2 childMinSize = children[0].getPropertyAs!("minSize", ivec2); 42 childSize = ivec2(max(childSize.x, childMinSize.x), max(childSize.y, childMinSize.y)); 43 44 children[0].setProperty!("size")(childSize); 45 } 46 } 47 48 /// Called by container to update its children positions and sizes. 49 void onContainerResized(Widget root, ivec2 oldSize, ivec2 newSize) 50 { 51 52 } 53 } 54 55 class BasicListBehavior : IWidgetBehavior 56 { 57 protected: 58 59 Widget _viewport; 60 Widget _canvas; 61 Widget _vertscroll; 62 63 public: 64 65 override void attachTo(Widget widget) 66 { 67 _viewport = widget["subwidgets"].get!(Widget[string]).get("viewport", null); 68 _canvas = widget["subwidgets"].get!(Widget[string]).get("canvas", null); 69 _vertscroll = widget["subwidgets"].get!(Widget[string]).get("vert-scroll", null); 70 } 71 } 72 73 class WidgetListBehavior : BasicListBehavior 74 { 75 override void attachTo(Widget widget) 76 { 77 super.attachTo(widget); 78 79 if (_viewport && _canvas && _vertscroll) 80 { 81 _viewport.setProperty!("layout", ILayout)(new ViewportLayout); 82 _viewport.property("size").valueChanged.connect(&updateSize); 83 _canvas.property("size").valueChanged.connect(&updateSize); 84 _vertscroll.property("sliderPos").valueChanged.connect(&sliderMoved); 85 86 sliderMoved(null, _vertscroll.property("sliderPos").value); 87 } 88 } 89 90 void sliderMoved(FlexibleObject widget, Variant position) 91 { 92 ivec2 viewSize = _viewport.getPropertyAs!("size", ivec2); 93 ivec2 canvasSize = _canvas.getPropertyAs!("size", ivec2); 94 95 int avalPos = canvasSize.y - viewSize.y; 96 int newCanvasPos = avalPos < 0 ? 0 : -cast(int)(position.get!double * avalPos); 97 98 _canvas.setProperty!("position")(ivec2(0, newCanvasPos)); 99 } 100 101 void updateSize(FlexibleObject widget, Variant size) 102 { 103 _vertscroll.setProperty!"sliderSize"(scrollSize()); 104 } 105 106 double scrollSize() 107 { 108 ivec2 viewSize = _viewport.getPropertyAs!("size", ivec2); 109 ivec2 canvasSize = _canvas.getPropertyAs!("size", ivec2); 110 111 return cast(double)viewSize.y / canvasSize.y; 112 } 113 } 114 115 116 class StringListBehavior : BasicListBehavior 117 { 118 alias StringList = List!dstring; 119 120 protected: 121 StringList _list; 122 size_t itemsPerPage; // Number of items that are visible in viewport. 123 size_t firstVisible; 124 125 uint _itemHeight = 16; 126 size_t numLabels = 0; 127 GuiContext context; 128 129 public: 130 131 override void attachPropertiesTo(Widget widget) 132 { 133 widget.property("list").valueChanged.connect(&listAssigned); 134 } 135 136 void listAssigned(FlexibleObject widget, Variant newList) 137 { 138 if (newList.type !is typeid(StringList)) return; 139 140 auto oldList = _list; 141 142 _list = newList.get!StringList; 143 144 if (_list is null && oldList !is null) 145 { 146 oldList.listChangedSignal.disconnectAll(); 147 } 148 149 refreshItems(); 150 151 if (_list is null) return; 152 153 _list.listChangedSignal.connect((){refreshItems();}); 154 } 155 156 override void attachTo(Widget widget) 157 { 158 super.attachTo(widget); 159 160 //writeln(_viewport , _canvas , _vertscroll); 161 162 if (_viewport && _canvas && _vertscroll) 163 { 164 context = _viewport.getPropertyAs!("context", GuiContext); 165 166 _viewport.setProperty!("layout", ILayout)(new ViewportLayout); 167 _viewport.property("size").valueChanged.connect(&updateSize); 168 _vertscroll.property("sliderPos").valueChanged.connect(&sliderMoved); 169 170 sliderMoved(null, _vertscroll.property("sliderPos").value); 171 172 refreshItems(); 173 } 174 } 175 176 void refreshPageSize() 177 { 178 if (_list is null) 179 { 180 itemsPerPage = 0; 181 } 182 183 ivec2 viewSize = _viewport.getPropertyAs!("size", ivec2); 184 185 itemsPerPage = cast(size_t)(cast(real)viewSize.y / _itemHeight).ceil; 186 187 if (itemsPerPage > numLabels) 188 { 189 foreach (_; 0..itemsPerPage - numLabels) 190 { 191 context.createWidget("label", _canvas); 192 } 193 } 194 else if (itemsPerPage < numLabels) 195 { 196 Widget[] labels = _canvas.getPropertyAs!("children", Widget[]); 197 198 labels.length = itemsPerPage; 199 200 _canvas.setProperty!("position")(ivec2(0, 0)); 201 } 202 203 numLabels = itemsPerPage; 204 205 if (_list is null) return; 206 207 double sliderSize = cast(double)itemsPerPage / _list.length; 208 _vertscroll.setProperty!("sliderSize", double)(sliderSize); 209 } 210 211 void refreshPagePos() 212 { 213 if (_list is null) return; 214 215 double sliderPos = _vertscroll.getPropertyAs!("sliderPos", double); 216 217 ptrdiff_t visible = cast(ptrdiff_t)(sliderPos * (_list.length - itemsPerPage)); 218 firstVisible = visible < 0 ? 0 : visible; 219 220 if (_list.length > itemsPerPage) 221 { 222 ivec2 viewSize = _viewport.getPropertyAs!("size", ivec2); 223 long canvasSize = _list.length * _itemHeight; 224 long viewPos = cast(long)(sliderPos * cast(double)(canvasSize - viewSize.y)); 225 226 _canvas.setProperty!("position")(ivec2(0, cast(int)(firstVisible*_itemHeight - viewPos))); 227 } 228 else 229 { 230 _canvas.setProperty!("position")(ivec2(0, 0)); 231 } 232 } 233 234 void addItems() 235 { 236 if (_list is null) return; 237 238 size_t listLength = _list.length; 239 size_t itemsToShow = itemsPerPage < listLength ? itemsPerPage : listLength; 240 241 Widget[] labels = _canvas.getPropertyAs!("children", Widget[]); 242 243 foreach(itemIndex; firstVisible..firstVisible + itemsToShow) 244 { 245 labels[itemIndex - firstVisible].setProperty!"text"(_list[itemIndex]); 246 } 247 } 248 249 void refreshItems() 250 { 251 refreshPageSize(); 252 refreshPagePos(); 253 addItems(); 254 } 255 256 void sliderMoved(FlexibleObject widget, Variant position) 257 { 258 if (_list is null) return; 259 260 ivec2 viewSize = _viewport.getPropertyAs!("size", ivec2); 261 262 long avalPos = _list.length * _itemHeight - viewSize.y; 263 264 refreshItems(); 265 } 266 267 void updateSize(FlexibleObject widget, Variant size) 268 { 269 refreshItems(); 270 } 271 272 double scrollSize() 273 { 274 ivec2 viewSize = _viewport.getPropertyAs!("size", ivec2); 275 276 return cast(double)viewSize.y / _list.length * _itemHeight; 277 } 278 }