Work on WM's sizing code, not quite complete yet
[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_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 void WM_SetText(tElement *Element, char *Text)
94 {
95         if(!Element)    return ;
96         if(Element->Text)       free(Element->Text);
97         Element->Text = strdup(Text);
98         
99         switch(Element->Type)
100         {
101         case ELETYPE_IMAGE:
102                 if(Element->Data)       free(Element->Data);
103                 Element->Data = Image_Load( Element->Text );
104                 if(!Element->Data) {
105                         Element->Flags &= ~ELEFLAG_FIXEDSIZE;
106                         return ;
107                 }
108                 
109                 //Element->Flags |= ELEFLAG_FIXEDSIZE;
110                 Element->CachedW = ((tImage*)Element->Data)->Width;
111                 Element->CachedH = ((tImage*)Element->Data)->Height;
112                 
113                 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
114                         Element->MinCross = ((tImage*)Element->Data)->Width;
115                         Element->MinWith = ((tImage*)Element->Data)->Height;
116                 }
117                 else {
118                         Element->MinWith = ((tImage*)Element->Data)->Width;
119                         Element->MinCross = ((tImage*)Element->Data)->Height;
120                 }
121                 break;
122         }
123         
124         return ;
125 }
126
127 // --- Pre-Rendering ---
128 /**
129  * \brief Updates the dimensions of an element
130  * 
131  * The dimensions of an element are calculated from the parent's
132  * cross dimension (the side at right angles to the alignment) sans some
133  * padding.
134  */
135 void WM_UpdateDimensions(tElement *Element, int Pass)
136 {
137         tElement        *child;
138          int    nChildren = 0;
139          int    nFixed = 0;
140          int    maxCross = 0;
141          int    fixedSize = 0;
142          int    fullCross, dynWith;
143         
144         _SysDebug("%p -> Flags = 0x%x", Element, Element->Flags);
145         _SysDebug("%p ->CachedH = %i, ->PaddingT = %i, ->PaddingB = %i",
146                 Element, Element->CachedH, Element->PaddingT, Element->PaddingB
147                 );
148         _SysDebug("%p ->CachedW = %i, ->PaddingL = %i, ->PaddingR = %i",
149                 Element, Element->CachedW, Element->PaddingL, Element->PaddingR
150                 );
151         
152         // Pass 1
153         for( child = Element->FirstChild; child; child = child->NextSibling )
154         {
155                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )
156                         continue ;
157                 
158                 _SysDebug("%p,%p ->FixedWith = %i", Element, child, child->FixedWith);
159                 if( child->FixedWith )
160                 {
161                         nFixed ++;
162                         fixedSize += child->FixedWith;
163                 }
164                 
165                 if( child->FixedCross && maxCross < child->FixedCross )
166                         maxCross = child->FixedCross;
167                 if( child->MinCross && maxCross < child->MinCross )
168                         maxCross = child->MinCross;
169                 nChildren ++;
170         }
171         
172         _SysDebug("%p - nChildren = %i, nFixed = %i", Element, nChildren, nFixed);
173         if( nChildren > nFixed ) {
174                 if( Element->Flags & ELEFLAG_VERTICAL )
175                         dynWith = Element->CachedH - Element->PaddingT
176                                 - Element->PaddingB;
177                 else
178                         dynWith = Element->CachedW - Element->PaddingL
179                                 - Element->PaddingR;
180                 dynWith -= fixedSize;
181                 if( dynWith < 0 )       return ;
182                 dynWith /= nChildren - nFixed;
183                 _SysDebug("%p - dynWith = %i", Element, dynWith);
184         }
185         
186         if( Element->Flags & ELEFLAG_VERTICAL )
187                 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
188         else
189                 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
190         
191         _SysDebug("%p - fullCross = %i", Element, fullCross);
192         
193         // Pass 2 - Set sizes and recurse
194         for( child = Element->FirstChild; child; child = child->NextSibling )
195         {
196                 // Cross Size
197                 if( child->FixedCross ) {
198                         if( Element->Flags & ELEFLAG_VERTICAL )
199                                 child->CachedW = child->FixedCross;
200                         else
201                                 child->CachedH = child->FixedCross;
202                 }
203                 else {
204                          int    cross;
205                         _SysDebug("%p,%p ->MinCross = %i", Element, child, child->MinCross);
206                         // Expand to fill?
207                         // TODO: Extra flag so options are (Expand, Equal, Wrap)
208                         if( child->Flags & ELEFLAG_NOEXPAND )
209                                 cross = child->MinCross;
210                         else
211                                 cross = fullCross;
212                         
213                         _SysDebug("%p,%p - cross = %i", Element, child, cross);
214                         
215                         if( Element->Flags & ELEFLAG_VERTICAL )
216                                 child->CachedW = cross;
217                         else
218                                 child->CachedH = cross;
219                 }
220                 
221                 // With size
222                 if( child->FixedWith ) {
223                         if( Element->Flags & ELEFLAG_VERTICAL )
224                                 child->CachedH = child->FixedWith;
225                         else
226                                 child->CachedW = child->FixedWith;
227                 }
228                 else {
229                         if( Element->Flags & ELEFLAG_VERTICAL )
230                                 child->CachedH = dynWith;
231                         else
232                                 child->CachedW = dynWith;
233                 }
234                 
235                 WM_UpdateDimensions(child, 0);
236         }
237 }
238
239 /**
240  * \brief Updates the position of an element
241  * 
242  * The parent element sets the positions of its children
243  */
244 void WM_UpdatePosition(tElement *Element)
245 {
246         tElement        *child;
247          int    x, y;
248         
249         if( Element->Flags & ELEFLAG_NORENDER ) return ;
250         
251         _SysDebug("Element=%p{PaddingL:%i, PaddingT:%i}",
252                 Element, Element->PaddingL, Element->PaddingT);
253         
254         // Initialise
255         x = Element->CachedX + Element->PaddingL;
256         y = Element->CachedY + Element->PaddingT;
257         
258         // Update each child
259         for(child = Element->FirstChild; child; child = child->NextSibling)
260         {
261                 child->CachedX = x;
262                 child->CachedY = y;
263                 
264                 // Set Alignment
265                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
266                         if(Element->Flags & ELEFLAG_VERTICAL )
267                                 child->CachedX += Element->CachedW/2 - child->CachedW/2;
268                         else
269                                 child->CachedY += Element->CachedH/2 - child->CachedH/2;
270                 }
271                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
272                         if(Element->Flags & ELEFLAG_VERTICAL )
273                                 child->CachedX += Element->CachedW - child->CachedW;
274                         else
275                                 child->CachedY += Element->CachedH - child->CachedH;
276                 }
277                 
278                 // Update child's children positions
279                 WM_UpdatePosition(child);
280                 
281                 // Increment
282                 if(Element->Flags & ELEFLAG_VERTICAL ) {
283                         y += child->CachedH + Element->GapSize;
284                 }
285                 else {
286                         x += child->CachedW + Element->GapSize;
287                 }
288         }
289         
290         _SysDebug("Element %p (%i,%i)",
291                  Element, Element->CachedX, Element->CachedY
292                 );
293 }
294
295
296 /**
297  * \brief Update the minimum dimensions of the element
298  * \note Called after a child's minimum dimensions have changed
299  */
300 void WM_UpdateMinDims(tElement *Element)
301 {
302         tElement        *child;
303         
304         if(!Element)    return;
305         
306         Element->MinCross = 0;
307         Element->MinWith = 0;
308         
309         for(child = Element->FirstChild; child; child = child->NextSibling)
310         {
311                 if( Element->Parent &&
312                         (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
313                         )
314                 {
315                         if(child->FixedCross)
316                                 Element->MinCross += child->FixedCross;
317                         else
318                                 Element->MinCross += child->MinCross;
319                         if(child->FixedWith)
320                                 Element->MinWith += child->FixedWith;
321                         else
322                                 Element->MinWith += child->MinWith;
323                 }
324                 else
325                 {
326                         if(child->FixedCross)
327                                 Element->MinWith += child->FixedCross;
328                         else
329                                 Element->MinWith += child->MinCross;
330                         if(child->FixedWith)
331                                 Element->MinCross += child->FixedWith;
332                         else
333                                 Element->MinCross += child->MinWith;
334                 }
335         }
336         
337         // Recurse upwards
338         WM_UpdateMinDims(Element->Parent);
339 }
340
341 // --- Render ---
342 void WM_RenderWidget(tElement *Element)
343 {
344         tElement        *child;
345         
346         if( Element->Flags & ELEFLAG_NORENDER ) return ;
347         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
348         
349         Decorator_RenderWidget(Element);
350         
351         for(child = Element->FirstChild; child; child = child->NextSibling)
352         {
353                 WM_RenderWidget(child);
354         }
355 }
356
357 void WM_Update(void)
358 {
359         gWM_RootElement.CachedX = 0;    gWM_RootElement.CachedY = 0;
360         gWM_RootElement.CachedW = giScreenWidth;
361         gWM_RootElement.CachedH = giScreenHeight;
362         gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
363         
364         WM_UpdateDimensions( &gWM_RootElement, 0 );
365         WM_UpdatePosition( &gWM_RootElement );
366         WM_RenderWidget( &gWM_RootElement );
367         
368         Video_Update();
369 }

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