Bugfixes to usermode 64-bit division, more work on GUI (now sizes almost correctly)
[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                  int    cross, with;
197                 
198                 _SysDebug("%p,%p ->MinCross = %i", Element, child, child->MinCross);
199
200                 
201                 // --- Cross Size ---
202                 if( child->FixedCross )
203                         cross = child->FixedCross;
204                 // Expand to fill?
205                 // TODO: Extra flag so options are (Expand, Equal, Wrap)
206                 else if( child->Flags & ELEFLAG_NOEXPAND )
207                         cross = child->MinCross;
208                 else
209                         cross = fullCross;
210                 _SysDebug("%p,%p - cross = %i", Element, child, cross);
211                 if( Element->Flags & ELEFLAG_VERTICAL )
212                         child->CachedW = cross;
213                 else
214                         child->CachedH = cross;
215                 
216                 // --- With Size ---
217                 if( child->FixedWith)
218                         with = child->FixedWith;
219                 else if( child->Flags & ELEFLAG_NOSTRETCH )
220                         with = child->MinWith;
221                 else
222                         with = dynWith;
223                 _SysDebug("%p,%p - with = %i", Element, child, with);
224                 if( Element->Flags & ELEFLAG_VERTICAL )
225                         child->CachedH = with;
226                 else
227                         child->CachedW = with;
228                 
229                 WM_UpdateDimensions(child, 0);
230         }
231 }
232
233 /**
234  * \brief Updates the position of an element
235  * 
236  * The parent element sets the positions of its children
237  */
238 void WM_UpdatePosition(tElement *Element)
239 {
240         tElement        *child;
241          int    x, y;
242         
243         if( Element->Flags & ELEFLAG_NORENDER ) return ;
244         
245         _SysDebug("Element=%p{PaddingL:%i, PaddingT:%i}",
246                 Element, Element->PaddingL, Element->PaddingT);
247         
248         // Initialise
249         x = Element->CachedX + Element->PaddingL;
250         y = Element->CachedY + Element->PaddingT;
251         
252         // Update each child
253         for(child = Element->FirstChild; child; child = child->NextSibling)
254         {
255                 child->CachedX = x;
256                 child->CachedY = y;
257                 
258                 // Set Alignment
259                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
260                         if(Element->Flags & ELEFLAG_VERTICAL )
261                                 child->CachedX += Element->CachedW/2 - child->CachedW/2;
262                         else
263                                 child->CachedY += Element->CachedH/2 - child->CachedH/2;
264                 }
265                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
266                         if(Element->Flags & ELEFLAG_VERTICAL )
267                                 child->CachedX += Element->CachedW - child->CachedW;
268                         else
269                                 child->CachedY += Element->CachedH - child->CachedH;
270                 }
271                 
272                 // Update child's children positions
273                 WM_UpdatePosition(child);
274                 
275                 // Increment
276                 if(Element->Flags & ELEFLAG_VERTICAL ) {
277                         y += child->CachedH + Element->GapSize;
278                 }
279                 else {
280                         x += child->CachedW + Element->GapSize;
281                 }
282         }
283         
284         _SysDebug("Element %p (%i,%i)",
285                  Element, Element->CachedX, Element->CachedY
286                 );
287 }
288
289
290 /**
291  * \brief Update the minimum dimensions of the element
292  * \note Called after a child's minimum dimensions have changed
293  */
294 void WM_UpdateMinDims(tElement *Element)
295 {
296         tElement        *child;
297         
298         if(!Element)    return;
299         
300         Element->MinCross = 0;
301         Element->MinWith = 0;
302         
303         for(child = Element->FirstChild; child; child = child->NextSibling)
304         {
305                 if( Element->Parent &&
306                         (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
307                         )
308                 {
309                         if(child->FixedCross)
310                                 Element->MinCross += child->FixedCross;
311                         else
312                                 Element->MinCross += child->MinCross;
313                         if(child->FixedWith)
314                                 Element->MinWith += child->FixedWith;
315                         else
316                                 Element->MinWith += child->MinWith;
317                 }
318                 else
319                 {
320                         if(child->FixedCross)
321                                 Element->MinWith += child->FixedCross;
322                         else
323                                 Element->MinWith += child->MinCross;
324                         if(child->FixedWith)
325                                 Element->MinCross += child->FixedWith;
326                         else
327                                 Element->MinCross += child->MinWith;
328                 }
329         }
330         
331         // Recurse upwards
332         WM_UpdateMinDims(Element->Parent);
333 }
334
335 // --- Render ---
336 void WM_RenderWidget(tElement *Element)
337 {
338         tElement        *child;
339         
340         if( Element->Flags & ELEFLAG_NORENDER ) return ;
341         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
342         
343         Decorator_RenderWidget(Element);
344         
345         for(child = Element->FirstChild; child; child = child->NextSibling)
346         {
347                 WM_RenderWidget(child);
348         }
349 }
350
351 void WM_Update(void)
352 {
353         gWM_RootElement.CachedX = 0;    gWM_RootElement.CachedY = 0;
354         gWM_RootElement.CachedW = giScreenWidth;
355         gWM_RootElement.CachedH = giScreenHeight;
356         gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
357         
358         WM_UpdateDimensions( &gWM_RootElement, 0 );
359         WM_UpdatePosition( &gWM_RootElement );
360         WM_RenderWidget( &gWM_RootElement );
361         
362         Video_Update();
363 }

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