1 2 module ws.gui.base; 3 4 import 5 std.algorithm, 6 std.array, 7 ws.draw, 8 ws.wm, 9 ws.gui.dragger; 10 11 public import 12 ws.gui.point, 13 ws.gui.input, 14 ws.gui.style; 15 16 T[] without(T)(T[] array, T elem){ 17 auto i = array.countUntil(elem); 18 if(i < 0) 19 return array; 20 return array[0..i] ~ array[i+1..$]; 21 } 22 23 24 class Base { 25 26 Style style; 27 Mouse.cursor cursor; 28 int[2] size; 29 int[2] pos; 30 int[2] cursorPos; 31 Base parent; 32 Base mouseChild; 33 Base keyboardChild; 34 bool hidden = false; 35 Base[] children; 36 DrawEmpty _draw; 37 bool[int] buttons; 38 bool dragging; 39 bool drawOutside = true; 40 41 T addNew(T, Args...)(Args args){ 42 T e = new T(args); 43 add(e); 44 return e; 45 } 46 47 48 Base add(Base gui){ 49 if(gui.parent) 50 throw new Exception("Trying to embed element that already has a parent"); 51 gui.parent = this; 52 gui.hidden = false; 53 children ~= gui; 54 return gui; 55 } 56 57 Base[] hiddenChildren(){ 58 return children.filter!"a.hidden".array; 59 } 60 61 void remove(Base widget){ 62 children = children.without(widget); 63 if(widget.parent == this) 64 widget.parent = null; 65 } 66 67 68 69 Base findChild(int x, int y, Base[] filter=[]){ 70 foreach(child; children){ 71 if(child.hidden || filter.canFind(child)) 72 continue; 73 if(child.pos.x <= x && child.pos.x+child.size.x >= x && child.pos.y <= y && child.pos.y+child.size.y >= y){ 74 return child; 75 } 76 } 77 if(filter.canFind(this)) 78 return null; 79 return this; 80 } 81 82 83 Base drag(int[2] offset){ 84 return null; 85 } 86 87 Base dropTarget(int x, int y, Base draggable){ 88 auto targetChild = findChild(x, y, [this,draggable]); 89 if(targetChild) 90 return targetChild.dropTarget(x, y, draggable); 91 return null; 92 } 93 94 void dropPreview(int x, int y, Base draggable, bool start){ 95 assert(false, "dropPreview not implemented"); 96 } 97 98 void drop(int x, int y, Base draggable){ 99 if(findChild(x, y, [this,draggable])) 100 findChild(x, y, [this,draggable]).drop(x, y, draggable); 101 } 102 103 104 Base root(){ 105 if(parent) 106 return parent.root; 107 return this; 108 } 109 110 Draggable grab(int x, int y){ 111 if(findChild(x, y) != this) 112 return findChild(x, y).grab(x, y); 113 return null; 114 } 115 116 void receive(Draggable what){ 117 assert(0, "receiveShadow implies receive"); 118 } 119 120 Base receiveShadow(Draggable what, int x, int y){ 121 foreach(child; children) 122 if(child.pos.x < x && child.pos.x+child.size.x > x && child.pos.y < y && child.pos.y+child.size.y > y) 123 return child.receiveShadow(what, x, y); 124 return null; 125 } 126 127 128 void setTop(Base child){ 129 if(child.hidden || keyboardChild == child) 130 return; 131 132 if(keyboardChild) 133 keyboardChild.onKeyboardFocus(false); 134 135 if(parent) 136 parent.setTop(this); 137 138 children = children.without(child); 139 children = child ~ children; 140 141 keyboardChild = child; 142 child.onKeyboardFocus(true); 143 } 144 145 bool hasFocus(){ 146 if(!parent) 147 return false; 148 return parent.keyboardChild == this; 149 } 150 151 bool hasMouseFocus(){ 152 if(!parent) 153 return false; 154 return parent.mouseChild == this && parent.hasMouseFocus; 155 } 156 157 void show(){ 158 if(!hidden) 159 return; 160 hidden = false; 161 if(parent) 162 root.onMouseMove(cursorPos.x, cursorPos.y); 163 onShow; 164 } 165 166 void hide(){ 167 if(hidden) 168 return; 169 hidden = true; 170 onMouseFocus(false); 171 onKeyboardFocus(false); 172 if(parent){ 173 if(parent.keyboardChild == this){ 174 foreach(pc; parent.children){ 175 if(pc.hidden) 176 continue; 177 parent.setTop(pc); 178 break; 179 } 180 } 181 auto p = parent; 182 while(p.parent) 183 p = p.parent; 184 p.onMouseMove(cursorPos.x, cursorPos.y); 185 } 186 onHide(); 187 } 188 189 void onShow(){}; 190 void onHide(){}; 191 192 void onClose(){}; 193 194 void resize(int[2] size){ 195 this.size = size; 196 } 197 198 void resizeRequest(Base child, int[2] size){} 199 200 void move(int[2] pos){ 201 foreach(c; children) 202 c.move([c.pos.x+pos.x-this.pos.x, c.pos.y+pos.y-this.pos.y]); 203 this.pos = pos; 204 } 205 206 void moveLocal(int[2] pos){ 207 if(parent) 208 move([parent.pos.x+pos.x, parent.pos.y+pos.y]); 209 else 210 move(pos); 211 } 212 213 void onKeyboard(Keyboard.key key, bool pressed){ 214 if(keyboardChild) 215 keyboardChild.onKeyboard(key, pressed); 216 }; 217 218 void onKeyboard(dchar c){ 219 if(keyboardChild) 220 keyboardChild.onKeyboard(c); 221 }; 222 223 void onKeyboardFocus(bool focus){ 224 if(keyboardChild) 225 keyboardChild.onKeyboardFocus(false); 226 } 227 228 void onMouseMove(int x, int y){ 229 bool foundFocus = false; 230 cursorPos = [x,y]; 231 if(dragging || buttons.values.any && !dragging){ 232 dragging = true; 233 foundFocus = true; 234 }else{ 235 auto child = findChild(x, y); 236 if(child == mouseChild){ 237 foundFocus = true; 238 }else if(child && child != this){ 239 if(mouseChild) 240 mouseChild.onMouseFocus(false); 241 child.onMouseFocus(true); 242 if(wm.active) 243 wm.active.setCursor(child.cursor); 244 mouseChild = child; 245 foundFocus = true; 246 } 247 } 248 249 if(mouseChild){ 250 if(!foundFocus){ 251 mouseChild.onMouseFocus(false); 252 mouseChild = null; 253 if(wm.active) 254 wm.active.setCursor(cursor); 255 }else{ 256 mouseChild.onMouseMove(x, y); 257 } 258 } 259 }; 260 261 void onMouseButton(Mouse.button b, bool p, int x, int y){ 262 buttons[b] = p; 263 if(mouseChild) 264 mouseChild.onMouseButton(b, p, x, y); 265 if(!buttons.values.any && dragging){ 266 dragging = false; 267 onMouseMove(x, y); 268 } 269 }; 270 271 void onMouseFocus(bool f){ 272 if(!f && mouseChild){ 273 mouseChild.onMouseFocus(false); 274 mouseChild = null; 275 } 276 }; 277 278 void setCursor(Mouse.cursor c){ 279 cursor = c; 280 if(parent && parent.mouseChild == this) 281 wm.active.setCursor(cursor); 282 } 283 284 @property 285 DrawEmpty draw(){ 286 if(!_draw && parent) 287 return parent.draw; 288 return _draw; 289 } 290 291 void onDraw(){ 292 foreach_reverse(c; children) 293 if(!c.hidden && 294 (drawOutside 295 || c.pos.x-c.size.w > pos.x 296 && c.pos.x < pos.x+size.w 297 && c.pos.y-c.size.h > pos.y 298 && c.pos.y < pos.y+size.h)) 299 c.onDraw; 300 } 301 302 void setStyle(Style style){ 303 this.style = style; 304 } 305 306 }