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 (*UpdateFlags)(tElement *This);
41 void (*UpdateSize)(tElement *This);
42 void (*UpdateText)(tElement *This);
43 } gaWM_WidgetTypes[MAX_ELETYPES] = {
44 {NULL, NULL, NULL, NULL}, // NULL
45 {NULL, NULL, NULL, NULL} // Box
49 // --- Widget Creation and Control ---
50 tAxWin_Element *AxWin_CreateElement(tElement *Parent, int Type, int Flags, const char *DebugName)
53 const char *dbgName = DebugName ? DebugName : "";
55 ret = calloc(sizeof(tElement)+strlen(dbgName)+1, 1);
60 strcpy(ret->DebugName, dbgName);
61 if(Parent == NULL) Parent = &gWM_RootElement;
65 // Append to parent's list
67 Parent->LastChild->NextSibling = ret;
68 Parent->LastChild = ret;
69 if(!Parent->FirstChild) Parent->FirstChild = ret;
76 if( gaWM_WidgetTypes[Type].Init )
77 gaWM_WidgetTypes[Type].Init(ret);
79 WM_UpdateMinDims(ret->Parent);
87 void AxWin_DeleteElement(tElement *Element)
89 // TODO: Implement AxWin_DeleteElement
94 * \brief Alter an element's flags
96 void AxWin_SetFlags(tElement *Element, int Flags)
98 // Permissions are handled in the message handler
100 gWM_RootElement.Flags = Flags;
104 Element->Flags = Flags;
108 void AxWin_SetSize(tElement *Element, int Size)
110 if(!Element) return ;
111 Element->FixedWith = Size;
116 * \brief Set the text field of an element
117 * \note Used for the image path on ELETYPE_IMAGE
119 void AxWin_SetText(tElement *Element, const char *Text)
121 if(!Element) return ;
122 if(Element->Text) free(Element->Text);
123 Element->Text = strdup(Text);
125 switch(Element->Type)
128 if(Element->Data) free(Element->Data);
129 Element->Data = Image_Load( Element->Text );
131 Element->Flags &= ~ELEFLAG_FIXEDSIZE;
135 //Element->Flags |= ELEFLAG_FIXEDSIZE;
136 Element->CachedW = ((tImage*)Element->Data)->Width;
137 Element->CachedH = ((tImage*)Element->Data)->Height;
139 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
140 Element->MinCross = ((tImage*)Element->Data)->Width;
141 Element->MinWith = ((tImage*)Element->Data)->Height;
144 Element->MinWith = ((tImage*)Element->Data)->Width;
145 Element->MinCross = ((tImage*)Element->Data)->Height;
152 Video_GetTextDims(NULL, Element->Text, &w, &h);
153 if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
154 Element->MinCross = w;
155 Element->MinWith = h;
158 Element->MinWith = w;
159 Element->MinCross = h;
163 default: // Any other, no special case
170 // --- Pre-Rendering ---
172 * \name Pre-Rendering
173 * \brief Updates the element positions and sizes
177 * \brief Updates the dimensions of an element
178 * \todo What is the \a Pass parameter for
180 * The dimensions of an element are calculated from the parent's
181 * cross dimension (the side at right angles to the alignment) sans some
184 void WM_UpdateDimensions(tElement *Element, int Pass)
191 int fullCross, dynWith;
193 _SysDebug("WM_UpdateDimensions %p'%s'", Element, Element->DebugName);
194 _SysDebug(" -> Flags = 0x%x", Element->Flags);
195 _SysDebug(" ->CachedH = %i, ->PaddingT = %i, ->PaddingB = %i",
196 Element->CachedH, Element->PaddingT, Element->PaddingB
198 _SysDebug(" ->CachedW = %i, ->PaddingL = %i, ->PaddingR = %i",
199 Element->CachedW, Element->PaddingL, Element->PaddingR
203 for( child = Element->FirstChild; child; child = child->NextSibling )
205 if( child->Flags & ELEFLAG_ABSOLUTEPOS )
208 _SysDebug(" > %p'%s' ->FixedWith = %i", child, child->DebugName, child->FixedWith);
209 if( child->FixedWith )
212 fixedSize += child->FixedWith;
215 if( child->FixedCross && maxCross < child->FixedCross )
216 maxCross = child->FixedCross;
217 if( child->MinCross && maxCross < child->MinCross )
218 maxCross = child->MinCross;
222 _SysDebug(" - nChildren = %i, nFixed = %i", Element, nChildren, nFixed);
223 if( nChildren > nFixed ) {
224 if( Element->Flags & ELEFLAG_VERTICAL )
225 dynWith = Element->CachedH - Element->PaddingT
228 dynWith = Element->CachedW - Element->PaddingL
230 dynWith -= fixedSize;
231 if( dynWith < 0 ) return ;
232 dynWith /= nChildren - nFixed;
233 _SysDebug(" - dynWith = %i", dynWith);
236 if( Element->Flags & ELEFLAG_VERTICAL )
237 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
239 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
241 _SysDebug(" - fullCross = %i", Element, fullCross);
243 // Pass 2 - Set sizes and recurse
244 for( child = Element->FirstChild; child; child = child->NextSibling )
248 _SysDebug(" > %p'%s' ->MinCross = %i", child, child->DebugName, child->MinCross);
251 // --- Cross Size ---
252 if( child->FixedCross )
253 cross = child->FixedCross;
255 // TODO: Extra flag so options are (Expand, Equal, Wrap)
256 else if( child->Flags & ELEFLAG_NOEXPAND )
257 cross = child->MinCross;
260 _SysDebug(" > %p'%s' - cross = %i", child, child->DebugName, cross);
261 if( Element->Flags & ELEFLAG_VERTICAL )
262 child->CachedW = cross;
264 child->CachedH = cross;
267 if( child->FixedWith)
268 with = child->FixedWith;
269 else if( child->Flags & ELEFLAG_NOSTRETCH )
270 with = child->MinWith;
273 _SysDebug(" > %p'%s' - with = %i", child, child->DebugName, with);
274 if( Element->Flags & ELEFLAG_VERTICAL )
275 child->CachedH = with;
277 child->CachedW = with;
279 WM_UpdateDimensions(child, Pass);
282 _SysDebug("%p'%s' Done", Element, Element->DebugName);
286 * \brief Updates the position of an element
288 * The parent element sets the positions of its children
290 void WM_UpdatePosition(tElement *Element)
294 static int depth = 0;
295 char indent[depth+1];
297 if( Element->Flags & ELEFLAG_NORENDER ) return ;
299 memset(indent, ' ', depth);
300 indent[depth] = '\0';
303 _SysDebug("%sWM_UpdatePosition %p'%s'{PaddingL:%i, PaddingT:%i}",
304 indent, Element, Element->DebugName, Element->PaddingL, Element->PaddingT);
307 x = Element->CachedX + Element->PaddingL;
308 y = Element->CachedY + Element->PaddingT;
310 _SysDebug("%s- Alignment = %s", indent,
311 (Element->Flags & ELEFLAG_VERTICAL) ? "vertical" : "horizontal");
314 for(child = Element->FirstChild; child; child = child->NextSibling)
316 _SysDebug("%s- x = %i, y = %i", indent, x, y);
321 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
322 _SysDebug("%sChild being aligned to center", indent);
323 if(Element->Flags & ELEFLAG_VERTICAL)
324 child->CachedX += Element->CachedW/2 - child->CachedW/2;
326 child->CachedY += Element->CachedH/2 - child->CachedH/2;
328 else if( Element->Flags & ELEFLAG_ALIGN_END) {
329 _SysDebug("%sChild being aligned to end", indent);
330 if(Element->Flags & ELEFLAG_VERTICAL )
331 child->CachedX += Element->CachedW
332 - Element->PaddingL - Element->PaddingR
335 child->CachedY += Element->CachedH
341 _SysDebug("%s> %p'%s' at (%i,%i)", indent, child, child->DebugName,
342 child->CachedX, child->CachedY);
344 // Update child's children positions
345 WM_UpdatePosition(child);
348 if(Element->Flags & ELEFLAG_VERTICAL ) {
349 y += child->CachedH + Element->GapSize;
352 x += child->CachedW + Element->GapSize;
356 _SysDebug("%sElement %p'%s' (%i,%i)",
357 indent, Element, Element->DebugName, Element->CachedX, Element->CachedY
363 * \brief Update the minimum dimensions of the element
364 * \note Called after a child's minimum dimensions have changed
366 void WM_UpdateMinDims(tElement *Element)
372 Element->MinCross = 0;
373 Element->MinWith = 0;
375 for(child = Element->FirstChild; child; child = child->NextSibling)
377 if( Element->Parent &&
378 (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
381 if(child->FixedCross)
382 Element->MinCross += child->FixedCross;
384 Element->MinCross += child->MinCross;
386 Element->MinWith += child->FixedWith;
388 Element->MinWith += child->MinWith;
392 if(child->FixedCross)
393 Element->MinWith += child->FixedCross;
395 Element->MinWith += child->MinCross;
397 Element->MinCross += child->FixedWith;
399 Element->MinCross += child->MinWith;
404 WM_UpdateMinDims(Element->Parent);
411 void WM_RenderWidget(tElement *Element)
415 if( Element->Flags & ELEFLAG_NORENDER ) return ;
416 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
418 Decorator_RenderWidget(Element);
420 for(child = Element->FirstChild; child; child = child->NextSibling)
422 WM_RenderWidget(child);
428 gWM_RootElement.CachedX = 0; gWM_RootElement.CachedY = 0;
429 gWM_RootElement.CachedW = giScreenWidth;
430 gWM_RootElement.CachedH = giScreenHeight;
431 gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
433 WM_UpdateDimensions( &gWM_RootElement, 0 );
434 WM_UpdatePosition( &gWM_RootElement );
435 WM_RenderWidget( &gWM_RootElement );