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