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

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