163e8104ee0557780916ce98e7760ca857847ce7
[tpg/acess2.git] / Usermode / Applications / axwin2_src / WM / wm.c
1 /*
2  * Acess GUI (AxWin) Version 2
3  * By John Hodge (thePowersGang)
4  * 
5  * Window Manager and Widget Control
6  */
7 #include "common.h"
8 #include <stdlib.h>
9 #include <strings.h>
10 #include "wm.h"
11
12 // === IMPORTS ===
13 extern void     Decorator_RenderWidget(tElement *Element);
14
15 // === PROTOTYPES ===
16 tElement        *WM_CreateElement(tElement *Parent, int Type, int Flags);
17 void    WM_SetFlags(tElement *Element, int Flags);
18 void    WM_SetSize(tElement *Element, int Size);
19 void    WM_SetText(tElement *Element, char *Text);
20 void    WM_UpdateDimensions(tElement *Element, int Pass);
21 void    WM_UpdatePosition(tElement *Element);
22 void    WM_RenderWidget(tElement *Element);
23 void    WM_Update(void);
24
25 // === GLOBALS ===
26 tElement        gWM_RootElement;
27 struct {
28         void    (*Init)(tElement *This);
29         void    (*UpdateFlags)(tElement *This);
30         void    (*UpdateSize)(tElement *This);
31         void    (*UpdateText)(tElement *This);
32 }       gaWM_WidgetTypes[MAX_ELETYPES] = {
33         {NULL, NULL, NULL, NULL},       // NULL
34         {NULL, NULL, NULL, NULL}        // Box
35 };
36
37 // === CODE ===
38 // --- Widget Creation and Control ---
39 tElement *WM_CreateElement(tElement *Parent, int Type, int Flags)
40 {
41         tElement        *ret;
42         
43         ret = calloc(sizeof(tElement), 1);
44         if(!ret)        return NULL;
45         
46         // Prepare
47         ret->Type = Type;
48         if(Parent == NULL)      Parent = &gWM_RootElement;
49         ret->Parent = Parent;
50         
51         // Append to parent's list
52         ret->NextSibling = Parent->LastChild;
53         Parent->LastChild = ret;
54         if(!Parent->FirstChild) Parent->FirstChild = ret;
55         
56         ret->PaddingL = 2;
57         ret->PaddingR = 2;
58         ret->PaddingT = 2;
59         ret->PaddingB = 2;
60         
61         return ret;
62 }
63
64 void WM_SetFlags(tElement *Element, int Flags)
65 {
66         // Permissions are handled in the message handler
67         if(!Element) {
68                 gWM_RootElement.Flags = Flags;
69                 return ;
70         }
71         
72         Element->Flags = Flags;
73         return ;
74 }
75
76 void WM_SetSize(tElement *Element, int Size)
77 {
78         if(!Element)    return ;
79         Element->Size = Size;
80         return ;
81 }
82
83 void WM_SetText(tElement *Element, char *Text)
84 {
85         if(!Element)    return ;
86         if(Element->Text)       free(Element->Text);
87         Element->Text = strdup(Text);
88         
89         switch(Element->Type)
90         {
91         case ELETYPE_IMAGE:
92                 if(Element->Data)       free(Element->Data);
93                 Element->Data = Image_Load( Element->Text );
94                 if(!Element->Data) {
95                         Element->Flags &= ~ELEFLAG_FIXEDSIZE;
96                         return ;
97                 }
98                 
99                 Element->Flags |= ELEFLAG_FIXEDSIZE;
100                 Element->CachedW = ((tImage*)Element->Data)->Width;
101                 Element->CachedH = ((tImage*)Element->Data)->Height;
102                 
103                 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL)
104                         Element->Size = Element->CachedH;
105                 else
106                         Element->Size = Element->CachedW;
107                 break;
108         }
109         
110         return ;
111 }
112
113 // --- Pre-Rendering ---
114 #if 1
115 void WM_UpdateDimensions(tElement *Element, int Pass)
116 {
117          int    fixedSize = 0, maxCross = 0;
118          int    nFixed = 0, nChildren = 0;
119          int    minSize = 0;
120         tElement        *child;
121         
122         // Pass zero intialises
123         if( Pass == 0 )
124         {
125                 // If not a fixed size element, initialise the sizes
126                 if( !(Element->Flags & ELEFLAG_FIXEDSIZE) )
127                 {
128                         Element->CachedH = 0;
129                         Element->CachedW = 0;
130                         if( Element->Size )
131                         {
132                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL )
133                                         Element->CachedH = Element->Size;
134                                 else
135                                         Element->CachedW = Element->Size;
136                         }
137                         
138                         if( !(Element->Flags & ELEFLAG_NOEXPAND) )
139                         {
140                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL )
141                                         Element->CachedW = Element->Parent->CachedW;
142                                 else
143                                         Element->CachedH = Element->Parent->CachedH;
144                         }
145                 }
146         }
147
148         for( child = Element->FirstChild; child; child = child->NextSibling )
149         {
150                 WM_UpdateDimensions( child, 0 );
151                 
152                 if( Element->Flags & ELEFLAG_VERTICAL )
153                 {
154                         if( child->CachedH ) {
155                                 fixedSize += child->CachedH;
156                                 minSize += child->CachedH;
157                                 nFixed ++;
158                         }
159                         else
160                                 minSize += child->MinHeight;
161                         
162                         if( maxCross < child->CachedW )
163                                 maxCross = child->CachedW;;
164                 }
165                 else
166                 {
167                         if( child->CachedW ) {
168                                 fixedSize += child->CachedW;
169                                 minSize += child->CachedW;
170                                 nFixed ++;
171                         }
172                         else
173                                 minSize += child->MinWidth;
174                         
175                         if( maxCross < child->CachedH )
176                                 maxCross = child->CachedH;
177                 }
178                 nChildren ++;
179         }
180         _SysDebug("WM_UpdateDimensions: nFixed=%i, fixedSize=%i, minSize=%i, maxCross=%i",
181                 nFixed, fixedSize, minSize, maxCross
182                 );
183
184
185         // If we don't have our dimensions, get the child dimensions
186         if( Element->CachedW == 0 || Element->CachedH == 0 )
187         {       
188                 if( Element->Flags & ELEFLAG_VERTICAL )
189                 {
190                         if( Element->CachedW == 0 && maxCross )
191                                 Element->CachedW = Element->PaddingL
192                                         + Element->PaddingR + maxCross;
193                         
194                         if( Element->CachedH == 0 && nFixed == nChildren )
195                                 Element->CachedH = Element->PaddingT
196                                         + Element->PaddingB + fixedSize
197                                         + nChildren * Element->GapSize;
198                 }
199                 else
200                 {
201                         if( maxCross )
202                                 Element->CachedH = Element->PaddingT
203                                         + Element->PaddingB + maxCross;
204                         
205                         if( Element->CachedW == 0 && nFixed == nChildren )
206                                 Element->CachedW = Element->PaddingL
207                                         + Element->PaddingR + fixedSize
208                                         + nChildren * Element->GapSize;
209                 }
210         }
211         
212         // Now, if we have the "length" of the widget, we can size the children
213         if( (Element->Flags & ELEFLAG_VERTICAL && Element->CachedH > 0)
214          || (!(Element->Flags & ELEFLAG_VERTICAL) && Element->CachedW > 0) )
215         {
216                  int    dynSize;
217                 
218                 // Calculate the size of dynamically sized elements
219                 if( Element->Flags & ELEFLAG_VERTICAL )
220                         dynSize = Element->CachedH - Element->PaddingT
221                                  - Element->PaddingB - fixedSize;
222                 else
223                         dynSize = Element->CachedW - Element->PaddingL
224                                  - Element->PaddingR - fixedSize;
225                 dynSize /= nChildren - nFixed;
226                 
227                 // Itterate children again
228                 for( child = Element->FirstChild; child; child = child->NextSibling )
229                 {
230                          int    tmp;
231                         
232                         // Get the size of them
233                         if(child->Size)
234                                 tmp = child->Size;
235                         else
236                                 tmp = dynSize;
237                         
238                         if( Element->Flags & ELEFLAG_VERTICAL ) {
239                                 if( tmp < child->MinHeight )
240                                         tmp = child->MinHeight;
241                                 child->CachedH = tmp;
242                         }
243                         else {
244                                 if( tmp < child->MinWidth )
245                                         tmp = child->MinWidth;
246                                 child->CachedW = tmp;
247                         }
248                         
249                         WM_UpdateDimensions(child, 1);
250                 }
251         }
252 }
253
254 #else
255
256 /**
257  * \brief Updates the dimensions of an element
258  * 
259  * The dimensions of an element are calculated from the parent's
260  * cross dimension (the side at right angles to the alignment) sans some
261  * padding.
262  */
263 void WM_UpdateDimensions(tElement *Element, int Pass)
264 {
265         tElement        *child;
266          int    fixedChildSize = 0;
267          int    dynamicSize;
268          int    nChildren = 0;
269          int    nFixed = 0;
270         
271         _SysDebug("WM_UpdateDimensions: (Element=%p{Flags:0x%x}, Pass=%i)",
272                 Element, Element->Flags,
273                 Pass);
274         
275         if( Pass == 0 )
276         {
277                 if( Element->Flags & ELEFLAG_NORENDER ) return ;
278                 
279                 if( !(Element->Flags & ELEFLAG_ABSOLUTEPOS) ) {
280                         Element->CachedX = 0;
281                         Element->CachedY = 0;
282                 }
283                 if( !(Element->Flags & ELEFLAG_FIXEDSIZE) ) {
284                         Element->CachedW = 0;
285                         Element->CachedH = 0;
286                 }
287         }
288         
289         if( !(Element->Flags & ELEFLAG_FIXEDSIZE) ) {
290                 // If the element is sized, fix its dimension(s)
291                 if(Element->Size)
292                 {
293                         if(Element->Flags & ELEFLAG_NOEXPAND)
294                         {
295                                 Element->CachedW = Element->Size;
296                                 Element->CachedH = Element->Size;
297                         }
298                         else {
299                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL ) {
300                                         Element->CachedH = Element->Size;
301                                         Element->CachedW = Element->Parent->CachedW;
302                                         if(Element->CachedW)
303                                                 Element->CachedW -= (Element->Parent->PaddingL + Element->Parent->PaddingR);
304                                 }
305                                 else {
306                                         Element->CachedW = Element->Size;
307                                         Element->CachedH = Element->Parent->CachedH;
308                                         if(Element->CachedH)
309                                                 Element->CachedH -= (Element->Parent->PaddingT + Element->Parent->PaddingB);
310                                 }
311                         }
312                 }
313                 else {
314                         // Ok, so now we need to calculate the size of all child elements
315                         // However, if ELEFLAG_NOEXPAND is not set, we can still set one
316                         // dimension
317                         if( !(Element->Flags & ELEFLAG_NOEXPAND) ) {
318                                 if( Element->Parent->Flags & ELEFLAG_VERTICAL ) {
319                                         Element->CachedW = Element->Parent->CachedW;
320                                         if(Element->CachedW)
321                                                 Element->CachedW -= (Element->Parent->PaddingL + Element->Parent->PaddingR);
322                                 }
323                                 else {
324                                         Element->CachedH = Element->Parent->CachedH;
325                                         if(Element->CachedH)
326                                                 Element->CachedH -= (Element->Parent->PaddingT + Element->Parent->PaddingB);
327                                 }
328                         }
329                 }
330         }
331         
332         // Process Children (first pass)
333         for( child = Element->FirstChild; child; child = child->NextSibling )
334         {
335                 if( child->Flags & ELEFLAG_NORENDER )   continue;
336                 WM_UpdateDimensions(child, 0);
337                 
338                 // Children that don't inherit positions are ignored
339                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue;
340                 
341                 fixedChildSize += child->Size;
342                 if(child->Size > 0)
343                         nFixed ++;
344                 nChildren ++;
345                 
346                 // If we are wrapping the children, get the largest cross size
347                 if( !(Element->Flags & ELEFLAG_FIXEDSIZE)
348                  && Element->Flags & ELEFLAG_NOEXPAND
349                  && Element->Size == 0 )
350                 {
351                         if( Element->Flags & ELEFLAG_VERTICAL ) {
352                                 if( Element->CachedW < child->CachedW )
353                                         Element->CachedW = child->CachedW;
354                         }
355                         else {
356                                 if( Element->CachedH < child->CachedH )
357                                         Element->CachedH = child->CachedH;
358                         }
359                 }
360         }
361         
362         // Let's avoid a #DIV0 shall we?
363         if( nChildren > 0 )
364         {
365                 // Calculate the size of dynamically sized children
366                 if( Element->Flags & ELEFLAG_VERTICAL ) {
367                         if( Element->CachedH == 0 ) {
368                                 if( nFixed == nChildren )
369                                         Element->CachedH = fixedChildSize;
370                                 else
371                                         return ;
372                         }
373                         dynamicSize = (Element->CachedH - (Element->PaddingT + Element->PaddingB)  - fixedChildSize) / nChildren;
374                 }
375                 else {
376                         if( Element->CachedW == 0 ) {
377                                 if( nFixed == nChildren )
378                                         Element->CachedW = fixedChildSize;
379                                 else
380                                         return ;
381                         }
382                         dynamicSize = (Element->CachedW - (Element->PaddingL + Element->PaddingR) - fixedChildSize) / nChildren;
383                 }
384                 
385                 // Process Children (second pass)
386                 for( child = Element->FirstChild; child; child = child->NextSibling )
387                 {
388                         if( child->Flags & ELEFLAG_NORENDER )   continue;
389                         // Children that don't inherit positions are ignored
390                         if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue;
391                         
392                         if(!child->Size) {
393                                 if(child->Flags & ELEFLAG_VERTICAL)
394                                         child->CachedH = dynamicSize;
395                                 else
396                                         child->CachedW = dynamicSize;
397                         }
398                         
399                         WM_UpdateDimensions(child, 1);
400                         
401                         // If we are wrapping the children, get the largest cross size
402                         if( Element->Flags & ELEFLAG_NOEXPAND ) {
403                                 if( Element->Flags & ELEFLAG_VERTICAL ) {
404                                         if( Element->CachedW < child->CachedW )
405                                                 Element->CachedW = child->CachedW;
406                                 }
407                                 else {
408                                         if( Element->CachedH < child->CachedH )
409                                                 Element->CachedH = child->CachedH;
410                                 }
411                         }
412                 }
413         }
414         
415         // Add the padding
416         //Element->CachedW += Element->PaddingL + Element->PaddingR;
417         //Element->CachedH += Element->PaddingT + Element->PaddingB;
418         
419         _SysDebug("Pass %i, Element %p %ix%i",
420                 Pass, Element, Element->CachedW, Element->CachedH
421                 );
422         
423         // We should be done
424         // Next function will do the coordinates
425 }
426 #endif
427
428
429 /**
430  * \brief Updates the position of an element
431  * 
432  * The parent element sets the positions of its children
433  */
434 void WM_UpdatePosition(tElement *Element)
435 {
436         tElement        *child;
437          int    x, y;
438         
439         if( Element->Flags & ELEFLAG_NORENDER ) return ;
440         
441         _SysDebug("Element=%p{PaddingL:%i, PaddingT:%i}",
442                 Element, Element->PaddingL, Element->PaddingT);
443         
444         // Initialise
445         x = Element->CachedX + Element->PaddingL;
446         y = Element->CachedY + Element->PaddingT;
447         
448         // Update each child
449         for(child = Element->FirstChild; child; child = child->NextSibling)
450         {
451                 child->CachedX = x;
452                 child->CachedY = y;
453                 
454                 // Set Alignment
455                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
456                         if(Element->Flags & ELEFLAG_VERTICAL )
457                                 child->CachedX += Element->CachedW/2 - child->CachedW/2;
458                         else
459                                 child->CachedY += Element->CachedH/2 - child->CachedH/2;
460                 }
461                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
462                         if(Element->Flags & ELEFLAG_VERTICAL )
463                                 child->CachedX += Element->CachedW - child->CachedW;
464                         else
465                                 child->CachedY += Element->CachedH - child->CachedH;
466                 }
467                 
468                 // Update child's children positions
469                 WM_UpdatePosition(child);
470                 
471                 // Increment
472                 if(Element->Flags & ELEFLAG_VERTICAL ) {
473                         y += child->CachedH + Element->GapSize;
474                 }
475                 else {
476                         x += child->CachedW + Element->GapSize;
477                 }
478         }
479         
480         _SysDebug("Element %p (%i,%i)",
481                  Element, Element->CachedX, Element->CachedY
482                 );
483 }
484
485 // --- Render ---
486 void WM_RenderWidget(tElement *Element)
487 {
488         tElement        *child;
489         
490         if( Element->Flags & ELEFLAG_NORENDER ) return ;
491         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
492         
493         Decorator_RenderWidget(Element);
494         
495         for(child = Element->FirstChild; child; child = child->NextSibling)
496         {
497                 WM_RenderWidget(child);
498         }
499 }
500
501 void WM_Update(void)
502 {
503         gWM_RootElement.CachedX = 0;    gWM_RootElement.CachedY = 0;
504         gWM_RootElement.CachedW = giScreenWidth;
505         gWM_RootElement.CachedH = giScreenHeight;
506         gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
507         
508         WM_UpdateDimensions( &gWM_RootElement, 0 );
509         WM_UpdatePosition( &gWM_RootElement );
510         WM_RenderWidget( &gWM_RootElement );
511         
512         Video_Update();
513 }

UCC git Repository :: git.ucc.asn.au