Mental note, adding resources is a good idea
[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 <string.h>
10 #include "wm.h"
11
12 // === IMPORTS ===
13 extern void     Decorator_RenderWidget(tElement *Element);
14 extern void     Video_GetTextDims(tFont *Font, const char *Text, int *W, int *H);
15
16 // === PROTOTYPES ===
17 tElement        *WM_CreateElement(tElement *Parent, int Type, int Flags, const char *DebugName);
18 void    WM_UpdateMinDims(tElement *Element);
19 void    WM_SetFlags(tElement *Element, int Flags);
20 void    WM_SetSize(tElement *Element, int Size);
21 void    WM_SetText(tElement *Element, const char *Text);
22 void    WM_UpdateDimensions(tElement *Element, int Pass);
23 void    WM_UpdatePosition(tElement *Element);
24 void    WM_RenderWidget(tElement *Element);
25 void    WM_Update(void);
26
27 // === GLOBALS ===
28 tElement        gWM_RootElement = {
29         DebugName: "ROOT"
30 };
31 struct {
32         void    (*Init)(tElement *This);
33         void    (*UpdateFlags)(tElement *This);
34         void    (*UpdateSize)(tElement *This);
35         void    (*UpdateText)(tElement *This);
36 }       gaWM_WidgetTypes[MAX_ELETYPES] = {
37         {NULL, NULL, NULL, NULL},       // NULL
38         {NULL, NULL, NULL, NULL}        // Box
39 };
40
41 // === CODE ===
42 // --- Widget Creation and Control ---
43 tElement *WM_CreateElement(tElement *Parent, int Type, int Flags, const char *DebugName)
44 {
45         tElement        *ret;
46         const char      *dbgName = DebugName ? DebugName : "";
47         
48         ret = calloc(sizeof(tElement)+strlen(dbgName)+1, 1);
49         if(!ret)        return NULL;
50         
51         // Prepare
52         ret->Type = Type;
53         strcpy(ret->DebugName, dbgName);
54         if(Parent == NULL)      Parent = &gWM_RootElement;
55         ret->Parent = Parent;
56         ret->Flags = Flags;
57         
58         // Append to parent's list
59         if(Parent->LastChild)
60                 Parent->LastChild->NextSibling = ret;
61         Parent->LastChild = ret;
62         if(!Parent->FirstChild) Parent->FirstChild = ret;
63         
64         ret->PaddingL = 2;
65         ret->PaddingR = 2;
66         ret->PaddingT = 2;
67         ret->PaddingB = 2;
68         
69         if( gaWM_WidgetTypes[Type].Init )
70                 gaWM_WidgetTypes[Type].Init(ret);
71         
72         WM_UpdateMinDims(ret->Parent);
73         
74         return ret;
75 }
76
77 /**
78  * \brief Alter an element's flags 
79  */
80 void WM_SetFlags(tElement *Element, int Flags)
81 {
82         // Permissions are handled in the message handler
83         if(!Element) {
84                 gWM_RootElement.Flags = Flags;
85                 return ;
86         }
87         
88         Element->Flags = Flags;
89         return ;
90 }
91
92 void WM_SetSize(tElement *Element, int Size)
93 {
94         if(!Element)    return ;
95         Element->FixedWith = Size;
96         return ;
97 }
98
99 /**
100  * \brief Set the text field of an element
101  * \note Used for the image path on ELETYPE_IMAGE
102  */
103 void WM_SetText(tElement *Element, const char *Text)
104 {
105         if(!Element)    return ;
106         if(Element->Text)       free(Element->Text);
107         Element->Text = strdup(Text);
108         
109         switch(Element->Type)
110         {
111         case ELETYPE_IMAGE:
112                 if(Element->Data)       free(Element->Data);
113                 Element->Data = Image_Load( Element->Text );
114                 if(!Element->Data) {
115                         Element->Flags &= ~ELEFLAG_FIXEDSIZE;
116                         return ;
117                 }
118                 
119                 //Element->Flags |= ELEFLAG_FIXEDSIZE;
120                 Element->CachedW = ((tImage*)Element->Data)->Width;
121                 Element->CachedH = ((tImage*)Element->Data)->Height;
122                 
123                 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
124                         Element->MinCross = ((tImage*)Element->Data)->Width;
125                         Element->MinWith = ((tImage*)Element->Data)->Height;
126                 }
127                 else {
128                         Element->MinWith = ((tImage*)Element->Data)->Width;
129                         Element->MinCross = ((tImage*)Element->Data)->Height;
130                 }
131                 break;
132         
133         case ELETYPE_TEXT:
134                 {
135                  int    w=0, h=0;
136                 Video_GetTextDims(NULL, Element->Text, &w, &h);
137                 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
138                         Element->MinCross = w;
139                         Element->MinWith = h;
140                 }
141                 else {
142                         Element->MinWith = w;
143                         Element->MinCross = h;
144                 }
145                 }
146                 break;
147         }
148         
149         return ;
150 }
151
152 // --- Pre-Rendering ---
153 /**
154  * \name Pre-Rendering
155  * \brief Updates the element positions and sizes
156  * \{
157  */
158 /**
159  * \brief Updates the dimensions of an element
160  * 
161  * The dimensions of an element are calculated from the parent's
162  * cross dimension (the side at right angles to the alignment) sans some
163  * padding.
164  */
165 void WM_UpdateDimensions(tElement *Element, int Pass)
166 {
167         tElement        *child;
168          int    nChildren = 0;
169          int    nFixed = 0;
170          int    maxCross = 0;
171          int    fixedSize = 0;
172          int    fullCross, dynWith;
173         
174         _SysDebug("WM_UpdateDimensions %p'%s'", Element, Element->DebugName);
175         _SysDebug(" -> Flags = 0x%x", Element->Flags);
176         _SysDebug(" ->CachedH = %i, ->PaddingT = %i, ->PaddingB = %i",
177                 Element->CachedH, Element->PaddingT, Element->PaddingB
178                 );
179         _SysDebug(" ->CachedW = %i, ->PaddingL = %i, ->PaddingR = %i",
180                 Element->CachedW, Element->PaddingL, Element->PaddingR
181                 );
182         
183         // Pass 1
184         for( child = Element->FirstChild; child; child = child->NextSibling )
185         {
186                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )
187                         continue ;
188                 
189                 _SysDebug(" > %p'%s' ->FixedWith = %i", child, child->DebugName, child->FixedWith);
190                 if( child->FixedWith )
191                 {
192                         nFixed ++;
193                         fixedSize += child->FixedWith;
194                 }
195                 
196                 if( child->FixedCross && maxCross < child->FixedCross )
197                         maxCross = child->FixedCross;
198                 if( child->MinCross && maxCross < child->MinCross )
199                         maxCross = child->MinCross;
200                 nChildren ++;
201         }
202         
203         _SysDebug(" - nChildren = %i, nFixed = %i", Element, nChildren, nFixed);
204         if( nChildren > nFixed ) {
205                 if( Element->Flags & ELEFLAG_VERTICAL )
206                         dynWith = Element->CachedH - Element->PaddingT
207                                 - Element->PaddingB;
208                 else
209                         dynWith = Element->CachedW - Element->PaddingL
210                                 - Element->PaddingR;
211                 dynWith -= fixedSize;
212                 if( dynWith < 0 )       return ;
213                 dynWith /= nChildren - nFixed;
214                 _SysDebug(" - dynWith = %i", dynWith);
215         }
216         
217         if( Element->Flags & ELEFLAG_VERTICAL )
218                 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
219         else
220                 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
221         
222         _SysDebug(" - fullCross = %i", Element, fullCross);
223         
224         // Pass 2 - Set sizes and recurse
225         for( child = Element->FirstChild; child; child = child->NextSibling )
226         {
227                  int    cross, with;
228                 
229                 _SysDebug(" > %p'%s' ->MinCross = %i", child, child->DebugName, child->MinCross);
230
231                 
232                 // --- Cross Size ---
233                 if( child->FixedCross )
234                         cross = child->FixedCross;
235                 // Expand to fill?
236                 // TODO: Extra flag so options are (Expand, Equal, Wrap)
237                 else if( child->Flags & ELEFLAG_NOEXPAND )
238                         cross = child->MinCross;
239                 else
240                         cross = fullCross;
241                 _SysDebug(" > %p'%s' - cross = %i", child, child->DebugName, cross);
242                 if( Element->Flags & ELEFLAG_VERTICAL )
243                         child->CachedW = cross;
244                 else
245                         child->CachedH = cross;
246                 
247                 // --- With Size ---
248                 if( child->FixedWith)
249                         with = child->FixedWith;
250                 else if( child->Flags & ELEFLAG_NOSTRETCH )
251                         with = child->MinWith;
252                 else
253                         with = dynWith;
254                 _SysDebug(" > %p'%s' - with = %i", child, child->DebugName, with);
255                 if( Element->Flags & ELEFLAG_VERTICAL )
256                         child->CachedH = with;
257                 else
258                         child->CachedW = with;
259                 
260                 WM_UpdateDimensions(child, 0);
261         }
262         
263         _SysDebug("%p'%s' Done", Element, Element->DebugName);
264 }
265
266 /**
267  * \brief Updates the position of an element
268  * 
269  * The parent element sets the positions of its children
270  */
271 void WM_UpdatePosition(tElement *Element)
272 {
273         tElement        *child;
274          int    x, y;
275         static int      depth = 0;
276         char    indent[depth+1];
277         
278         if( Element->Flags & ELEFLAG_NORENDER ) return ;
279         
280         memset(indent, ' ', depth);
281         indent[depth] = '\0';
282         depth ++;
283         
284         _SysDebug("%sWM_UpdatePosition %p'%s'{PaddingL:%i, PaddingT:%i}",
285                 indent, Element, Element->DebugName, Element->PaddingL, Element->PaddingT);
286         
287         // Initialise
288         x = Element->CachedX + Element->PaddingL;
289         y = Element->CachedY + Element->PaddingT;
290         
291         _SysDebug("%s- Alignment = %s", indent,
292                 (Element->Flags & ELEFLAG_VERTICAL) ? "vertical" : "horizontal");
293
294         // Update each child
295         for(child = Element->FirstChild; child; child = child->NextSibling)
296         {
297                 _SysDebug("%s- x = %i, y = %i", indent, x, y);
298                 child->CachedX = x;
299                 child->CachedY = y;
300                 
301                 // Set Alignment
302                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
303                         _SysDebug("%sChild being aligned to center", indent);
304                         if(Element->Flags & ELEFLAG_VERTICAL)
305                                 child->CachedX += Element->CachedW/2 - child->CachedW/2;
306                         else
307                                 child->CachedY += Element->CachedH/2 - child->CachedH/2;
308                 }
309                 else if( Element->Flags & ELEFLAG_ALIGN_END) {
310                         _SysDebug("%sChild being aligned to end", indent);
311                         if(Element->Flags & ELEFLAG_VERTICAL )
312                                 child->CachedX += Element->CachedW
313                                         - Element->PaddingL - Element->PaddingR
314                                         - child->CachedW;
315                         else
316                                 child->CachedY += Element->CachedH
317                                         - Element->PaddingT
318                                         - Element->PaddingB
319                                         - child->CachedH;
320                 }
321                 
322                 _SysDebug("%s> %p'%s' at (%i,%i)", indent, child, child->DebugName,
323                         child->CachedX, child->CachedY);
324         
325                 // Update child's children positions
326                 WM_UpdatePosition(child);
327                 
328                 // Increment
329                 if(Element->Flags & ELEFLAG_VERTICAL ) {
330                         y += child->CachedH + Element->GapSize;
331                 }
332                 else {
333                         x += child->CachedW + Element->GapSize;
334                 }
335         }
336         
337         _SysDebug("%sElement %p'%s' (%i,%i)",
338                 indent, Element, Element->DebugName, Element->CachedX, Element->CachedY
339                 );
340         depth --;
341 }
342
343 /**
344  * \brief Update the minimum dimensions of the element
345  * \note Called after a child's minimum dimensions have changed
346  */
347 void WM_UpdateMinDims(tElement *Element)
348 {
349         tElement        *child;
350         
351         if(!Element)    return;
352         
353         Element->MinCross = 0;
354         Element->MinWith = 0;
355         
356         for(child = Element->FirstChild; child; child = child->NextSibling)
357         {
358                 if( Element->Parent &&
359                         (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
360                         )
361                 {
362                         if(child->FixedCross)
363                                 Element->MinCross += child->FixedCross;
364                         else
365                                 Element->MinCross += child->MinCross;
366                         if(child->FixedWith)
367                                 Element->MinWith += child->FixedWith;
368                         else
369                                 Element->MinWith += child->MinWith;
370                 }
371                 else
372                 {
373                         if(child->FixedCross)
374                                 Element->MinWith += child->FixedCross;
375                         else
376                                 Element->MinWith += child->MinCross;
377                         if(child->FixedWith)
378                                 Element->MinCross += child->FixedWith;
379                         else
380                                 Element->MinCross += child->MinWith;
381                 }
382         }
383         
384         // Recurse upwards
385         WM_UpdateMinDims(Element->Parent);
386 }
387 /**
388  * \}
389  */
390
391 // --- Render ---
392 void WM_RenderWidget(tElement *Element)
393 {
394         tElement        *child;
395         
396         if( Element->Flags & ELEFLAG_NORENDER ) return ;
397         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
398         
399         Decorator_RenderWidget(Element);
400         
401         for(child = Element->FirstChild; child; child = child->NextSibling)
402         {
403                 WM_RenderWidget(child);
404         }
405 }
406
407 void WM_Update(void)
408 {
409         gWM_RootElement.CachedX = 0;    gWM_RootElement.CachedY = 0;
410         gWM_RootElement.CachedW = giScreenWidth;
411         gWM_RootElement.CachedH = giScreenHeight;
412         gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
413         
414         WM_UpdateDimensions( &gWM_RootElement, 0 );
415         WM_UpdatePosition( &gWM_RootElement );
416         WM_RenderWidget( &gWM_RootElement );
417         
418         Video_Update();
419 }

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