2 * Acess GUI (AxWin) Version 2
3 * By John Hodge (thePowersGang)
5 * Window Manager and Widget Control
11 #include <acess/sys.h> // _SysDebug
14 extern void Decorator_RenderWidget(tElement *Element);
15 extern void Video_GetTextDims(tFont *Font, const char *Text, int *W, int *H);
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);
30 // - TODO: Handle windows by having multiple root elements
31 tElement gWM_RootElement = {
35 tApplication *gWM_Applications;
37 // --- Element type flags
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
48 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
51 tApplication *AxWin_RegisterClient(int IdentLen, void *Ident, tMessages_Handle_Callback *Cb, const char *Name)
53 tApplication *ret = calloc( 1, sizeof(tApplication) + 1 + strlen(Name) + 1 + IdentLen );
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;
61 ret->Next = gWM_Applications;
62 gWM_Applications = ret;
64 // TODO: Inform listeners of the new application
69 void AxWin_DeregisterClient(tApplication *App)
72 // TODO: Implement DeregisterClient
74 for( win = App->MetaElement.FirstChild; win; win = next )
76 next = win->NextSibling;
77 AxWin_DeleteElement(win);
80 // TODO: Inform listeners of deleted application
81 // TODO: Remove from list
85 tElement *AxWin_CreateWindow(tApplication *App, const char *Name)
89 // TODO: Implement _CreateTab
91 ret = AxWin_CreateElement(&App->MetaElement, ELETYPE_WINDOW, 0, NULL);
92 ret->Text = strdup(Name);
96 // --- Widget Creation and Control ---
97 tAxWin_Element *AxWin_CreateElement(tElement *Parent, int Type, int Flags, const char *DebugName)
100 const char *dbgName = DebugName ? DebugName : "";
102 ret = calloc(sizeof(tElement)+strlen(dbgName)+1, 1);
103 if(!ret) return NULL;
107 strcpy(ret->DebugName, dbgName);
108 if(Parent == NULL) Parent = &gWM_RootElement;
109 ret->Parent = Parent;
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;
123 if( Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[Type].Init )
124 gaWM_WidgetTypes[Type].Init(ret);
126 WM_UpdateMinDims(ret->Parent);
134 void AxWin_DeleteElement(tElement *Element)
136 tElement *child, *next;
138 for(child = Element->FirstChild; child; child = next)
140 next = child->NextSibling;
141 AxWin_DeleteElement(child);
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);
153 * \brief Alter an element's flags
155 void AxWin_SetFlags(tElement *Element, int Flags)
157 // Permissions are handled in the message handler
159 gWM_RootElement.Flags = Flags;
163 Element->Flags = Flags;
167 void AxWin_SetSize(tElement *Element, int Size)
169 if(!Element) return ;
170 Element->FixedWith = Size;
175 * \brief Set the text field of an element
176 * \note Used for the image path on ELETYPE_IMAGE
178 void AxWin_SetText(tElement *Element, const char *Text)
180 if(!Element) return ;
181 if(Element->Text) free(Element->Text);
182 Element->Text = strdup(Text);
184 switch(Element->Type)
187 if(Element->Data) free(Element->Data);
188 Element->Data = Image_Load( Element->Text );
190 Element->Flags &= ~ELEFLAG_FIXEDSIZE;
194 //Element->Flags |= ELEFLAG_FIXEDSIZE;
195 Element->CachedW = ((tImage*)Element->Data)->Width;
196 Element->CachedH = ((tImage*)Element->Data)->Height;
198 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
199 Element->MinCross = ((tImage*)Element->Data)->Width;
200 Element->MinWith = ((tImage*)Element->Data)->Height;
203 Element->MinWith = ((tImage*)Element->Data)->Width;
204 Element->MinCross = ((tImage*)Element->Data)->Height;
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;
217 Element->MinWith = w;
218 Element->MinCross = h;
222 default: // Any other, no special case
229 // --- Pre-Rendering ---
231 * \name Pre-Rendering
232 * \brief Updates the element positions and sizes
236 * \brief Updates the dimensions of an element
237 * \todo What is the \a Pass parameter for
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
243 void WM_UpdateDimensions(tElement *Element, int Pass)
250 int fullCross, dynWith;
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
257 _SysDebug(" ->CachedW = %i, ->PaddingL = %i, ->PaddingR = %i",
258 Element->CachedW, Element->PaddingL, Element->PaddingR
262 for( child = Element->FirstChild; child; child = child->NextSibling )
264 if( child->Flags & ELEFLAG_ABSOLUTEPOS )
267 _SysDebug(" > %p'%s' ->FixedWith = %i", child, child->DebugName, child->FixedWith);
268 if( child->FixedWith )
271 fixedSize += child->FixedWith;
274 if( child->FixedCross && maxCross < child->FixedCross )
275 maxCross = child->FixedCross;
276 if( child->MinCross && maxCross < child->MinCross )
277 maxCross = child->MinCross;
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
287 dynWith = Element->CachedW - Element->PaddingL
289 dynWith -= fixedSize;
290 if( dynWith < 0 ) return ;
291 dynWith /= nChildren - nFixed;
292 _SysDebug(" - dynWith = %i", dynWith);
295 if( Element->Flags & ELEFLAG_VERTICAL )
296 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
298 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
300 _SysDebug(" - fullCross = %i", Element, fullCross);
302 // Pass 2 - Set sizes and recurse
303 for( child = Element->FirstChild; child; child = child->NextSibling )
307 _SysDebug(" > %p'%s' ->MinCross = %i", child, child->DebugName, child->MinCross);
310 // --- Cross Size ---
311 if( child->FixedCross )
312 cross = child->FixedCross;
314 // TODO: Extra flag so options are (Expand, Equal, Wrap)
315 else if( child->Flags & ELEFLAG_NOEXPAND )
316 cross = child->MinCross;
319 _SysDebug(" > %p'%s' - cross = %i", child, child->DebugName, cross);
320 if( Element->Flags & ELEFLAG_VERTICAL )
321 child->CachedW = cross;
323 child->CachedH = cross;
326 if( child->FixedWith)
327 with = child->FixedWith;
328 else if( child->Flags & ELEFLAG_NOSTRETCH )
329 with = child->MinWith;
332 _SysDebug(" > %p'%s' - with = %i", child, child->DebugName, with);
333 if( Element->Flags & ELEFLAG_VERTICAL )
334 child->CachedH = with;
336 child->CachedW = with;
338 WM_UpdateDimensions(child, Pass);
341 _SysDebug("%p'%s' Done", Element, Element->DebugName);
345 * \brief Updates the position of an element
347 * The parent element sets the positions of its children
349 void WM_UpdatePosition(tElement *Element)
353 static int depth = 0;
354 char indent[depth+1];
356 if( Element->Flags & ELEFLAG_NORENDER ) return ;
358 memset(indent, ' ', depth);
359 indent[depth] = '\0';
362 _SysDebug("%sWM_UpdatePosition %p'%s'{PaddingL:%i, PaddingT:%i}",
363 indent, Element, Element->DebugName, Element->PaddingL, Element->PaddingT);
366 x = Element->CachedX + Element->PaddingL;
367 y = Element->CachedY + Element->PaddingT;
369 _SysDebug("%s- Alignment = %s", indent,
370 (Element->Flags & ELEFLAG_VERTICAL) ? "vertical" : "horizontal");
373 for(child = Element->FirstChild; child; child = child->NextSibling)
375 _SysDebug("%s- x = %i, y = %i", indent, x, y);
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;
385 child->CachedY += Element->CachedH/2 - child->CachedH/2;
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
394 child->CachedY += Element->CachedH
400 _SysDebug("%s> %p'%s' at (%i,%i)", indent, child, child->DebugName,
401 child->CachedX, child->CachedY);
403 // Update child's children positions
404 WM_UpdatePosition(child);
407 if(Element->Flags & ELEFLAG_VERTICAL ) {
408 y += child->CachedH + Element->GapSize;
411 x += child->CachedW + Element->GapSize;
415 _SysDebug("%sElement %p'%s' (%i,%i)",
416 indent, Element, Element->DebugName, Element->CachedX, Element->CachedY
422 * \brief Update the minimum dimensions of the element
423 * \note Called after a child's minimum dimensions have changed
425 void WM_UpdateMinDims(tElement *Element)
431 Element->MinCross = 0;
432 Element->MinWith = 0;
434 for(child = Element->FirstChild; child; child = child->NextSibling)
436 if( Element->Parent &&
437 (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
440 if(child->FixedCross)
441 Element->MinCross += child->FixedCross;
443 Element->MinCross += child->MinCross;
445 Element->MinWith += child->FixedWith;
447 Element->MinWith += child->MinWith;
451 if(child->FixedCross)
452 Element->MinWith += child->FixedCross;
454 Element->MinWith += child->MinCross;
456 Element->MinCross += child->FixedWith;
458 Element->MinCross += child->MinWith;
463 WM_UpdateMinDims(Element->Parent);
470 void WM_RenderWidget(tElement *Element)
474 if( Element->Flags & ELEFLAG_NORENDER ) return ;
475 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
477 Decorator_RenderWidget(Element);
479 for(child = Element->FirstChild; child; child = child->NextSibling)
481 WM_RenderWidget(child);
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;
492 WM_UpdateDimensions( &gWM_RootElement, 0 );
493 WM_UpdatePosition( &gWM_RootElement );
494 WM_RenderWidget( &gWM_RootElement );