2 * Acess2 Window Manager v3
3 * - By John Hodge (thePowersGang)
9 #include <wm_renderer.h>
10 #include <renderer_widget.h>
12 #include <wm_messages.h>
14 #include "include/image.h"
16 #define DEFAULT_ELETABLE_SIZE 64
19 int Renderer_Widget_Init(void);
20 tWindow *Renderer_Widget_Create(int Flags);
21 void Renderer_Widget_Redraw(tWindow *Window);
23 void Widget_RenderWidget(tWindow *Window, tElement *Element);
24 void Widget_UpdateDimensions(tElement *Element);
25 void Widget_UpdatePosition(tElement *Element);
27 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID);
28 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg);
29 void Widget_SetFlags(tWidgetWin *Info, int Len, tWidgetMsg_SetFlags *Msg);
30 void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg);
31 void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg);
32 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data);
34 void Widget_TextBox_UpdateText(tElement *Element, const char *Text);
35 void Widget_Image_UpdateText(tElement *Element, const char *Text);
38 tWMRenderer gRenderer_Widget = {
40 .CreateWindow = Renderer_Widget_Create,
41 .Redraw = Renderer_Widget_Redraw,
42 .HandleMessage = Renderer_Widget_HandleMessage
45 // --- Element type flags
47 void (*Init)(tElement *Ele);
48 void (*Delete)(tElement *Ele);
49 void (*UpdateFlags)(tElement *Ele);
50 void (*UpdateSize)(tElement *Ele);
51 void (*UpdateText)(tElement *Ele, const char *Text); // This should update Ele->Text
52 } gaWM_WidgetTypes[NUM_ELETYPES] = {
53 {NULL, NULL, NULL, NULL, NULL}, // NULL
54 {NULL, NULL, NULL, NULL, NULL}, // Box
55 {NULL, NULL, NULL, NULL, Widget_TextBox_UpdateText}, // Text
56 {NULL, NULL, NULL, NULL, Widget_Image_UpdateText}, // Image
57 {NULL, NULL, NULL, NULL, NULL} // Button
59 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
62 int Renderer_Widget_Init(void)
64 WM_RegisterRenderer(&gRenderer_Widget);
69 tWindow *Renderer_Widget_Create(int Flags)
73 int eletable_size = DEFAULT_ELETABLE_SIZE;
75 _SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
77 // TODO: Use `Flags` as default element count?
78 // - Actaully, it's taken by the root ele flags
79 // - Use the upper bits?
81 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
82 info = ret->RendererInfo;
84 info->TableSize = eletable_size;
85 info->RootElement.ID = -1;
86 info->RootElement.BackgroundColour = 0xCCCCCC;
87 info->RootElement.Flags = Flags;
92 void Renderer_Widget_Redraw(tWindow *Window)
94 tWidgetWin *info = Window->RendererInfo;
95 WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
97 Widget_UpdateDimensions(&info->RootElement);
98 Widget_UpdatePosition(&info->RootElement);
100 Widget_RenderWidget(Window, &info->RootElement);
103 // --- Render / Resize ---
104 void Widget_RenderWidget(tWindow *Window, tElement *Element)
108 if( Element->Flags & ELEFLAG_NORENDER ) return ;
109 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
111 Widget_Decorator_RenderWidget(Window, Element);
113 for(child = Element->FirstChild; child; child = child->NextSibling)
115 Widget_RenderWidget(Window, child);
119 void Widget_UpdateDimensions(tElement *Element)
126 int fullCross, dynWith;
129 // - Get the fixed and minimum sizes of the element
130 for( child = Element->FirstChild; child; child = child->NextSibling )
132 // Ignore elements that will not be rendered
133 if( child->Flags & ELEFLAG_NORENDER ) continue ;
135 // Absolutely positioned elements don't affect dimensions
136 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
138 // Fixed width elements
139 if( child->FixedWith )
142 fixedSize += child->FixedWith;
144 else if( child->Flags & ELEFLAG_NOSTRETCH )
147 fixedSize += child->MinWith;
150 if( child->FixedCross && maxCross < child->FixedCross )
151 maxCross = child->FixedCross;
152 if( child->MinCross && maxCross < child->MinCross )
153 maxCross = child->MinCross;
157 // Get the dynamic with size from the unused space in the element
158 if( nChildren > nFixed ) {
159 if( Element->Flags & ELEFLAG_VERTICAL )
160 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
162 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
163 dynWith -= fixedSize;
164 if( dynWith < 0 ) return ;
165 dynWith /= nChildren - nFixed;
168 _SysDebug("%i - nChildren = %i, nFixed = %i, dynWith = %i, fixedSize = %i",
169 Element->ID, nChildren, nFixed, dynWith, fixedSize);
171 // Get the cross size
172 if( Element->Flags & ELEFLAG_VERTICAL )
173 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
175 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
177 // Pass 2 - Set sizes and recurse
178 for( child = Element->FirstChild; child; child = child->NextSibling )
182 // Ignore elements that will not be rendered
183 if( child->Flags & ELEFLAG_NORENDER ) continue ;
185 // --- Cross Size ---
186 // TODO: Expand to fill?
187 // TODO: Extra flag so options are (Expand, Equal, Wrap)
188 if( child->FixedCross )
189 cross = child->FixedCross;
190 else if( child->Flags & ELEFLAG_NOEXPAND )
191 cross = child->MinCross;
196 if( child->FixedWith )
197 with = child->FixedWith;
198 else if( child->Flags & ELEFLAG_NOSTRETCH )
199 with = child->MinWith;
204 if(with < child->MinWith) with = child->MinWith;
205 if(cross < child->MinCross) cross = child->MinCross;
207 // Update the dimensions if they have changed
208 if( Element->Flags & ELEFLAG_VERTICAL ) {
209 // If no change, don't recurse
210 if( child->CachedW == cross && child->CachedH == with )
212 child->CachedW = cross;
213 child->CachedH = with;
216 // If no change, don't recurse
217 if( child->CachedW == with && child->CachedH == cross )
219 child->CachedW = with;
220 child->CachedH = cross;
223 // Force the positions of child elements to be recalculated
226 // Recurse down so the child elements can be updated
227 Widget_UpdateDimensions(child);
233 * \brief Update the position of child elements
235 void Widget_UpdatePosition(tElement *Element)
240 if( Element->Flags & ELEFLAG_NORENDER ) return ;
242 _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
243 Element, Element->ID, Element->Type, Element->Flags);
246 x = Element->CachedX + Element->PaddingL;
247 y = Element->CachedY + Element->PaddingT;
250 for(child = Element->FirstChild; child; child = child->NextSibling)
253 // Ignore elements that will not be rendered
254 if( child->Flags & ELEFLAG_NORENDER ) continue ;
259 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
260 if(Element->Flags & ELEFLAG_VERTICAL)
261 newX += Element->CachedW/2 - child->CachedW/2;
263 newY += Element->CachedH/2 - child->CachedH/2;
265 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
266 if(Element->Flags & ELEFLAG_VERTICAL )
267 newX += Element->CachedW - child->CachedW
268 - Element->PaddingL - Element->PaddingR;
270 newY += Element->CachedH - child->CachedH
271 - Element->PaddingT - Element->PaddingB;
274 _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
276 // Check for changes, and don't update if there was no change
277 if( newX != child->CachedX || newY != child->CachedY )
279 child->CachedX = newX;
280 child->CachedY = newY;
281 // Update child's children positions
282 Widget_UpdatePosition(child);
286 if(Element->Flags & ELEFLAG_VERTICAL ) {
287 y += child->CachedH + Element->GapSize;
290 x += child->CachedW + Element->GapSize;
296 * \brief Update the minimum dimensions of the element
297 * \note Called after a child's minimum dimensions have changed
299 void Widget_UpdateMinDims(tElement *Element)
305 Element->MinCross = 0;
306 Element->MinWith = 0;
308 for(child = Element->FirstChild; child; child = child->NextSibling)
310 if( Element->Parent &&
311 (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
314 if(child->FixedCross)
315 Element->MinCross += child->FixedCross;
317 Element->MinCross += child->MinCross;
319 Element->MinWith += child->FixedWith;
321 Element->MinWith += child->MinWith;
325 if(child->FixedCross)
326 Element->MinWith += child->FixedCross;
328 Element->MinWith += child->MinCross;
330 Element->MinCross += child->FixedWith;
332 Element->MinCross += child->MinWith;
337 Widget_UpdateMinDims(Element->Parent);
342 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
346 if( ID == -1 ) return &Info->RootElement;
348 if( ID < Info->TableSize ) return Info->ElementTable[ID];
350 ele = Info->ElementTable[ID % Info->TableSize];
351 while(ele && ele->ID != ID) ele = ele->ListNext;
355 // --- Message Handlers ---
356 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
358 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
359 tElement *parent, *new;
362 if( Len < sizeof(tWidgetMsg_Create) )
364 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
367 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
368 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
371 parent = Widget_GetElementById(Info, Msg->Parent);
374 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
378 // Check if the ID is already in use
379 if( Widget_GetElementById(Info, Msg->NewID) )
382 // Create new element
383 new = calloc(sizeof(tElement), 1);
384 new->ID = Msg->NewID;
385 new->Type = Msg->Type;
386 new->Parent = parent;
387 new->Flags = Msg->Flags;
394 if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type].Init )
395 gaWM_WidgetTypes[new->Type].Init(new);
397 // Add to parent's list
398 if(parent->LastChild)
399 parent->LastChild->NextSibling = new;
401 parent->FirstChild = new;
402 parent->LastChild = new;
406 tElement *ele, *prev = NULL;
407 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
409 prev->ListNext = new;
411 Info->ElementTable[new->ID % Info->TableSize] = new;
414 Widget_UpdateMinDims(parent);
417 void Widget_SetFlags(tWidgetWin *Info, int Len, tWidgetMsg_SetFlags *Msg)
421 if( Len < sizeof(tWidgetMsg_SetFlags) )
424 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
426 ele = Widget_GetElementById(Info, Msg->WidgetID);
429 Msg->Value &= Msg->Mask;
431 ele->Flags &= ~Msg->Mask;
432 ele->Flags |= Msg->Value;
435 void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg)
439 if( Len < sizeof(tWidgetMsg_SetSize) )
442 ele = Widget_GetElementById(Info, Msg->WidgetID);
445 ele->FixedWith = Msg->Value;
448 void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg)
452 if( Len < sizeof(tWidgetMsg_SetText) + 1 )
454 if( Msg->Text[Len - sizeof(tWidgetMsg_SetText) - 1] != '\0' )
457 ele = Widget_GetElementById(Info, Msg->WidgetID);
461 if( ele->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[ele->Type].UpdateText )
463 gaWM_WidgetTypes[ele->Type].UpdateText( ele, Msg->Text );
467 // if(ele->Text) free(ele->Text);
468 // ele->Text = strdup(Msg->Text);
472 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data)
474 tWidgetWin *info = Target->RendererInfo;
477 case WNDMSG_RESIZE: {
478 struct sWndMsg_Resize *msg = Data;
479 if(Len < sizeof(*msg)) return -1;
481 info->RootElement.CachedW = msg->W;
482 info->RootElement.CachedH = msg->H;
486 case MSG_WIDGET_CREATE:
487 Widget_NewWidget(info, Len, Data);
490 case MSG_WIDGET_DELETE:
491 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
495 case MSG_WIDGET_SETFLAGS:
496 Widget_SetFlags(info, Len, Data);
500 case MSG_WIDGET_SETSIZE:
501 Widget_SetSize(info, Len, Data);
505 case MSG_WIDGET_SETTEXT:
506 Widget_SetText(info, Len, Data);
511 return 1; // Unhandled, pass to user
516 void Widget_TextBox_UpdateText(tElement *Element, const char *Text)
520 if(Element->Text) free(Element->Text);
521 Element->Text = strdup(Text);
523 WM_Render_GetTextDims(NULL, Element->Text, &w, &h);
524 if(Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL)) {
525 Element->MinCross = w;
526 Element->MinWith = h;
529 Element->MinWith = w;
530 Element->MinCross = h;
533 Widget_UpdateMinDims(Element->Parent);
536 void Widget_Image_UpdateText(tElement *Element, const char *Text)
538 if(Element->Data) free(Element->Data);
539 Element->Data = Image_Load( Text );
541 // Element->Flags &= ~ELEFLAG_FIXEDSIZE;
545 Element->CachedW = ((tImage*)Element->Data)->Width;
546 Element->CachedH = ((tImage*)Element->Data)->Height;
548 if(Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL) ) {
549 Element->MinCross = ((tImage*)Element->Data)->Width;
550 Element->MinWith = ((tImage*)Element->Data)->Height;
553 Element->MinWith = ((tImage*)Element->Data)->Width;
554 Element->MinCross = ((tImage*)Element->Data)->Height;
557 Widget_UpdateMinDims(Element->Parent);
559 // NOTE: Doesn't update Element->Text because it's useless