1 /** 2 Grimoire 3 Copyright (c) 2017 Enalye 4 5 This software is provided 'as-is', without any express or implied warranty. 6 In no event will the authors be held liable for any damages arising 7 from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute 11 it freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; 14 you must not claim that you wrote the original software. 15 If you use this software in a product, an acknowledgment 16 in the product documentation would be appreciated but 17 is not required. 18 19 2. Altered source versions must be plainly marked as such, 20 and must not be misrepresented as being the original software. 21 22 3. This notice may not be removed or altered from any source distribution. 23 */ 24 25 module ui.list.vlist; 26 27 import std.conv: to; 28 29 import core.all; 30 import render.all; 31 import common.all; 32 33 import ui.all; 34 35 private class ListContainer: WidgetGroup { 36 public { 37 View view; 38 VLayout layout; 39 } 40 41 this(Vec2f size) { 42 createGui(size); 43 } 44 45 override void onEvent(Event event) { 46 pushView(view, false); 47 super.onEvent(event); 48 popView(); 49 } 50 51 override void update(float deltaTime) { 52 pushView(view, false); 53 super.update(deltaTime); 54 popView(); 55 } 56 57 override void draw() { 58 pushView(view, true); 59 super.draw(); 60 popView(); 61 view.draw(_position); 62 } 63 64 protected void createGui(Vec2f newSize) { 65 _isFrame = true; 66 isLocked = true; 67 layout = new VLayout; 68 view = new View(to!Vec2u(newSize)); 69 view.position = Vec2f.zero; 70 size(newSize); 71 addChild(layout); 72 } 73 } 74 75 class VList: WidgetGroup { 76 protected { 77 ListContainer _container; 78 Slider _slider; 79 Vec2f _lastMousePos = Vec2f.zero; 80 float _layoutLength = 25f; 81 uint _nbElements = 0u; 82 uint _idElementSelected = 0u; 83 } 84 85 @property { 86 uint selected() const { return _idElementSelected; } 87 uint selected(uint id) { 88 if(id > _nbElements) 89 throw new Exception("VList: index out of bounds"); 90 _idElementSelected = id; 91 return _idElementSelected; 92 } 93 94 float layoutLength() const { return _layoutLength; } 95 float layoutLength(float length) { 96 _layoutLength = length; 97 _container.layout.size = Vec2f(_size.x, _layoutLength * _nbElements); 98 return _layoutLength; 99 } 100 } 101 102 this(Vec2f newSize) { 103 isLocked = true; 104 _slider = new VScrollbar; 105 _container = new ListContainer(newSize); 106 107 super.addChild(_slider); 108 super.addChild(_container); 109 110 size(newSize); 111 position(Vec2f.zero); 112 } 113 114 override void onEvent(Event event) { 115 super.onEvent(event); 116 if(event.type == EventType.MouseDown || event.type == EventType.MouseUp || event.type == EventType.MouseUpdate) { 117 if(_slider.isInside(event.position)) 118 _slider.onEvent(event); 119 else if(event.type == EventType.MouseDown) { 120 121 auto widgets = _container.layout.children; 122 foreach(uint id, ref Widget widget; _container.layout.children) { 123 widget.isValidated = false; 124 if(widget.isHovered) 125 _idElementSelected = id; 126 } 127 if(_idElementSelected < widgets.length) 128 widgets[_idElementSelected].isValidated = true; 129 } 130 } 131 132 if(!isOnInteractableWidget(_lastMousePos) && event.type == EventType.MouseWheel) 133 _slider.onEvent(event); 134 } 135 136 override void onPosition() { 137 auto offset = _size * (.5f - _anchor); 138 _slider.position = _position - Vec2f((_size.x - _slider.size.x) / 2f, 0f) + offset; 139 _container.position = _position + Vec2f(_slider.size.x / 2f, 0f) + offset; 140 } 141 142 override void onSize() { 143 _slider.size = Vec2f(10f, _size.y); 144 _container.layout.size = Vec2f(_size.x, _layoutLength * _nbElements); 145 _container.size = Vec2f(_size.x - _slider.size.x, _size.y); 146 _container.view.renderSize = _container.size.to!Vec2u; 147 onPosition(); 148 } 149 150 override void update(float deltaTime) { 151 super.update(deltaTime); 152 float min = _container.view.size.y / 2f; 153 float max = _container.layout.size.y - _container.view.size.y / 2f; 154 float exceedingHeight = _container.layout.size.y - _container.view.size.y; 155 156 if(exceedingHeight < 0f) { 157 _slider.max = 0; 158 _slider.step = 0; 159 } 160 else { 161 _slider.max = exceedingHeight / _layoutLength; 162 _slider.step = to!uint(_slider.max); 163 } 164 _container.view.position = Vec2f(0f, lerp(min, max, _slider.offset)); 165 } 166 167 override void addChild(Widget widget) { 168 widget.isValidated = (_nbElements == 0u); 169 170 _nbElements ++; 171 _container.layout.size = Vec2f(size.x, _layoutLength * _nbElements); 172 _container.layout.position = Vec2f(0f, _container.layout.size.y / 2f); 173 _container.layout.addChild(widget); 174 } 175 176 override void removeChildren() { 177 _nbElements = 0u; 178 _idElementSelected = 0u; 179 _container.layout.size = Vec2f(size.x, 0f); 180 _container.layout.position = Vec2f.zero; 181 _container.layout.removeChildren(); 182 } 183 184 override void removeChild(uint id) { 185 _container.layout.removeChild(id); 186 _nbElements = _container.layout.getChildrenCount(); 187 _idElementSelected = 0u; 188 _container.layout.size = Vec2f(size.x, _layoutLength * _nbElements); 189 _container.layout.position = Vec2f(0f, _container.layout.size.y / 2f); 190 } 191 192 override int getChildrenCount() { 193 return _container.layout.getChildrenCount(); 194 } 195 196 Widget[] getList() { 197 return _container.layout.children; 198 } 199 } 200