1 module des.gui.widget; 2 3 import des.gui.shape; 4 import des.gui.base; 5 import des.gui.context; 6 import des.gui.canvas; 7 import des.gui.event; 8 import des.gui.layout; 9 10 import des.util.stdext.algorithm : amap; 11 12 /// 13 class DiWidget : DesObject, TNode!(DiWidget,"diw_",""), DiLayoutItem 14 { 15 mixin DES; 16 mixin diw_TNodeHelper!(true,true); 17 mixin ClassLogger; 18 19 private: 20 21 bool base_ctor_finish, start_destroy; 22 23 protected: 24 25 /// 26 DiShape _shape; 27 28 /// 29 DiContext _context; 30 /// 31 DiCanvas _canvas; 32 33 /// 34 DiWidget current; 35 36 /// 37 DiLayout layout; 38 39 /// create childs widgets, set params, etc 40 void prepare() {} 41 42 /// call when context is changed 43 void updateContextDeps() {} 44 45 /// create shape object (default rect) 46 DiShape createShape() { return new DiRectShape; } 47 48 /// 49 bool _visible, _enable, _active, _focus; 50 51 public: 52 53 dstring name; 54 55 Signal!DiWidget onChangeVisible; 56 Signal!DiWidget onChangeEnable; 57 Signal!DiWidget onChangeActive; 58 Signal!DiWidget onChangeFocus; 59 60 invariant() 61 { 62 if( !base_ctor_finish || start_destroy ) return; 63 64 enforce( _shape !is null ); 65 66 enforce( ( _canvas is null && _context is null ) || 67 ( _canvas !is null && _context !is null ) ); 68 69 enforce( ( _context is null && __diw_parent_node !is null ) || 70 ( _context !is null && __diw_parent_node is null ) ); 71 } 72 73 /// 74 this( DiWidget par ) 75 in { assert( par !is null ); } body 76 { 77 _shape = registerChildEMM( createShape ); 78 diw_parent = par; 79 80 base_ctor_finish = true; 81 82 prepare(); 83 updateContextDeps(); 84 } 85 86 /// 87 this( DiContext ctx ) 88 in { assert( ctx !is null ); } body 89 { 90 _context = ctx; 91 _shape = registerChildEMM( createShape ); 92 _canvas = _context.createTop( this ); 93 94 prepare(); 95 updateContextDeps(); 96 97 base_ctor_finish = true; 98 } 99 100 /// 101 void setContext( DiContext ctx ) 102 in { assert( ctx !is null ); } body 103 { 104 auto old_context = context; 105 106 diw_parent = null; 107 108 if( _context !is null ) 109 _context.removeTop( this ); 110 111 _context = ctx; 112 113 _canvas = _context.createTop( this ); 114 115 if( old_context != context ) 116 updateContextDeps(); 117 } 118 119 /// 120 void setParent( DiWidget par ) 121 in { assert( par !is null ); } body 122 { 123 auto old_context = context; 124 125 if( _context !is null ) 126 { 127 _context.removeTop( this ); 128 _context = null; 129 _canvas = null; 130 } 131 132 diw_parent = par; 133 134 if( old_context != context ) 135 updateContextDeps(); 136 } 137 138 @property 139 { 140 /// 141 DiContext context() 142 { 143 if( diw_parent is null ) return _context; 144 else return diw_parent.context; 145 } 146 147 /// 148 DiCanvas canvas() 149 { 150 if( diw_parent is null ) return _canvas; 151 else return diw_parent.canvas; 152 } 153 154 /// 155 DiShape shape() { return _shape; } 156 /// 157 const(DiShape) shape() const { return _shape; } 158 159 /// easy access to shape pos 160 DiVec pos() const { return shape.pos; } 161 /// ditto 162 DiVec pos( in DiVec p ) 163 { 164 shape.rect = DiRect( p, shape.size ); 165 return p; 166 } 167 168 /// easy access to shape size 169 DiVec size() const { return shape.size; } 170 /// ditto 171 DiVec size( in DiVec s ) 172 { 173 shape.rect = DiRect( shape.pos, s ); 174 return s; 175 } 176 } 177 178 /// 179 void update() 180 { 181 selfUpdate(); 182 foreach( ch; diw_childs ) 183 ch.update(); 184 } 185 186 /// 187 void render() 188 in{ assert( canvas !is null ); } body 189 { 190 auto visible_rect = canvas.pushDrawRect( shape.rect ); 191 selfRender(); 192 foreach( ch; diw_childs ) 193 if( ch.shape.intersect( visible_rect ) ) 194 ch.render(); 195 canvas.popDrawRect(); 196 } 197 198 /// 199 void relayout() 200 { 201 if( layout && diw_childs.length ) 202 layout( shape, cast(DiLayoutItem[])diw_childs ); 203 } 204 205 /// 206 void processInput( in DiInputEvent ev ) 207 { 208 if( actionOnEvent( ev ) ) return; 209 210 setCurrent( findChildByEvent( ev ) ); 211 212 if( current !is null ) 213 current.processInput( ev ); 214 } 215 216 @property 217 { 218 /// 219 bool visible() const { return _visible; } 220 /// 221 bool visible( bool nc ) 222 { 223 _visible = nc; 224 onChangeVisible( this ); 225 return nc; 226 } 227 228 /// 229 bool enable() const { return _enable; } 230 /// 231 bool enable( bool nc ) 232 { 233 _enable = nc; 234 onChangeEnable( this ); 235 return nc; 236 } 237 238 /// 239 bool active() const { return _active; } 240 /// 241 bool active( bool nc ) 242 { 243 _active = nc; 244 onChangeActive( this ); 245 return nc; 246 } 247 248 /// 249 bool focus() const { return _focus; } 250 /// 251 bool focus( bool nc ) 252 { 253 _focus = nc; 254 onChangeFocus( this ); 255 return nc; 256 } 257 } 258 259 protected: 260 261 /// 262 void selfUpdate() { } 263 264 /// draw something on canvas or direct GL calls 265 void selfRender() { } 266 267 /// if do some action return true, false otherwise 268 bool actionOnEvent( in DiInputEvent ev ) 269 { return false; } 270 271 /// 272 DiWidget findChildByEvent( in DiInputEvent ev ) 273 { 274 // TODO 275 return null; 276 } 277 278 override void selfDestroy() 279 { 280 start_destroy = true; 281 diw_detachChilds( diw_childs ); 282 } 283 284 override void diw_attachCallback( DiWidget[] list ) 285 { relayout(); } 286 287 override void diw_detachCallback( DiWidget[] list ) 288 { relayout(); } 289 290 private: 291 292 void setCurrent( DiWidget new_current ) 293 { 294 if( current == new_current ) return; // do nothing 295 296 if( current !is null ) current.active = false; 297 298 current = new_current; 299 300 if( current !is null ) current.active = true; 301 } 302 }