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

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