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

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