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 }