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.gridlist; 26 27 import std.conv: to; 28 29 import core.all; 30 import render.all; 31 import common.all; 32 33 import ui.widget; 34 import ui.layout; 35 import ui.slider; 36 37 private class GridContainer: WidgetGroup { 38 public { 39 View view; 40 GridLayout layout; 41 } 42 43 this(Vec2f size) { 44 createGui(size); 45 } 46 47 override void onEvent(Event event) { 48 pushView(view, false); 49 super.onEvent(event); 50 popView(); 51 } 52 53 override void update(float deltaTime) { 54 pushView(view, false); 55 super.update(deltaTime); 56 popView(); 57 } 58 59 override void draw() { 60 pushView(view, true); 61 super.draw(); 62 popView(); 63 view.draw(_position); 64 } 65 66 protected void createGui(Vec2f newSize) { 67 _isFrame = true; 68 isLocked = true; 69 layout = new GridLayout; 70 view = new View(to!Vec2u(newSize)); 71 view.position = Vec2f.zero; 72 size(newSize); 73 addChild(layout); 74 } 75 } 76 77 class GridList: WidgetGroup { 78 protected { 79 GridContainer _container; 80 Slider _slider; 81 Vec2f _lastMousePos = Vec2f.zero; 82 float _layoutLength = 74f; 83 uint _nbElements = 0u; 84 uint _idElementSelected = 0u; 85 uint _nbElementsPerLine = 4u; 86 } 87 88 @property { 89 uint selected() const { return _idElementSelected; } 90 uint selected(uint id) { 91 if(id > _nbElements) 92 throw new Exception("GridList: index out of bounds"); 93 _idElementSelected = id; 94 return _idElementSelected; 95 } 96 } 97 98 this(Vec2f size) { 99 createGui(size); 100 } 101 102 override void onEvent(Event event) { 103 super.onEvent(event); 104 if(event.type == EventType.MouseDown || event.type == EventType.MouseUp || event.type == EventType.MouseUpdate) { 105 if(_slider.isInside(event.position)) 106 _slider.onEvent(event); 107 else if(event.type == EventType.MouseDown) { 108 109 auto widgets = _container.layout.children; 110 foreach(uint id, ref Widget widget; _container.layout.children) { 111 widget.isValidated = false; 112 if(widget.isHovered) 113 _idElementSelected = id; 114 } 115 if(_idElementSelected < widgets.length) 116 widgets[_idElementSelected].isValidated = true; 117 } 118 } 119 120 if(!isOnInteractableWidget(_lastMousePos) && event.type == EventType.MouseWheel) 121 _slider.onEvent(event); 122 } 123 124 override void onPosition() { 125 _slider.position = _position - Vec2f((_size.x - _slider.size.x) / 2f, 0f); 126 _container.position = _position + Vec2f(_slider.size.x / 2f, 0f); 127 } 128 129 override void onSize() { 130 _slider.size = Vec2f(10f, _size.y); 131 _container.layout.capacity = Vec2u(_nbElementsPerLine, 0u); 132 _container.layout.size = Vec2f(_size.x, _layoutLength * (_nbElements / _nbElementsPerLine)); 133 _container.size = Vec2f(_size.x - _slider.size.x, _size.y); 134 _container.view.renderSize = _container.size.to!Vec2u; 135 onPosition(); 136 } 137 138 override void update(float deltaTime) { 139 super.update(deltaTime); 140 float min = _container.view.size.y / 2f; 141 float max = _container.layout.size.y - _container.view.size.y / 2f; 142 float exceedingHeight = _container.layout.size.y - _container.view.size.y; 143 144 if(exceedingHeight < 0f) { 145 _slider.max = 0; 146 _slider.step = 0; 147 } 148 else { 149 _slider.max = exceedingHeight / _layoutLength; 150 _slider.step = to!uint(_slider.max); 151 } 152 _container.view.position = Vec2f(0f, lerp(min, max, _slider.offset)); 153 } 154 155 override void addChild(Widget widget) { 156 widget.isValidated = (_nbElements == 0u); 157 158 _nbElements ++; 159 _container.layout.size = Vec2f(_size.x, _layoutLength * (_nbElements / _nbElementsPerLine)); 160 _container.layout.position = Vec2f(0f, _container.layout.size.y / 2f); 161 _container.layout.addChild(widget); 162 } 163 164 override void removeChildren() { 165 _nbElements = 0u; 166 _idElementSelected = 0u; 167 _container.layout.size = Vec2f(size.x, 0f); 168 _container.layout.position = Vec2f.zero; 169 _container.layout.removeChildren(); 170 } 171 172 override void removeChild(uint id) { 173 _container.layout.removeChild(id); 174 _nbElements = _container.layout.getChildrenCount(); 175 _idElementSelected = 0u; 176 _container.layout.size = Vec2f(_size.x, _layoutLength * (_nbElements / _nbElementsPerLine)); 177 _container.layout.position = Vec2f(0f, _container.layout.size.y / 2f); 178 } 179 180 override int getChildrenCount() { 181 return _container.layout.getChildrenCount(); 182 } 183 184 Widget[] getList() { 185 return _container.layout.children; 186 } 187 188 protected void createGui(Vec2f newSize) { 189 isLocked = true; 190 _slider = new VScrollbar; 191 _container = new GridContainer(newSize); 192 193 super.addChild(_slider); 194 super.addChild(_container); 195 196 size(newSize); 197 position(Vec2f.zero); 198 } 199 } 200