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

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