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);
36 int Widget_Button_MouseButton(tElement *Element, int X, int Y, int Button, int bPress);
39 tWMRenderer gRenderer_Widget = {
41 .CreateWindow = Renderer_Widget_Create,
42 .Redraw = Renderer_Widget_Redraw,
43 .HandleMessage = Renderer_Widget_HandleMessage
46 // --- Element callbacks
48 void (*Init)(tElement *Ele);
49 void (*Delete)(tElement *Ele);
51 void (*UpdateFlags)(tElement *Ele);
52 void (*UpdateSize)(tElement *Ele);
53 void (*UpdateText)(tElement *Ele, const char *Text); // This should update Ele->Text
56 * \name Input handlers
57 * \note Returns boolean unhandled
60 int (*MouseButton)(tElement *Ele, int X, int Y, int Button, int bPressed);
61 int (*MouseMove)(tElement *Ele, int X, int Y);
62 int (*KeyDown)(tElement *Ele, int KeySym, int Character);
63 int (*KeyUp)(tElement *Ele, int KeySym);
64 int (*KeyFire)(tElement *Ele, int KeySym, int Character);
68 } gaWM_WidgetTypes[NUM_ELETYPES] = {
71 {.UpdateText = Widget_TextBox_UpdateText}, // Text
72 {.UpdateText = Widget_Image_UpdateText}, // Image
73 {.MouseButton = Widget_Button_MouseButton} // Button
75 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
78 int Renderer_Widget_Init(void)
80 WM_RegisterRenderer(&gRenderer_Widget);
85 tWindow *Renderer_Widget_Create(int Flags)
89 int eletable_size = DEFAULT_ELETABLE_SIZE;
91 _SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
93 // TODO: Use `Flags` as default element count?
94 // - Actaully, it's taken by the root ele flags
95 // - Use the upper bits?
97 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
98 info = ret->RendererInfo;
100 info->TableSize = eletable_size;
101 info->RootElement.Window = ret;
102 info->RootElement.ID = -1;
103 info->RootElement.BackgroundColour = 0xCCCCCC;
104 info->RootElement.Flags = Flags;
109 void Renderer_Widget_Redraw(tWindow *Window)
111 tWidgetWin *info = Window->RendererInfo;
112 WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
114 Widget_UpdateDimensions(&info->RootElement);
115 Widget_UpdatePosition(&info->RootElement);
117 Widget_RenderWidget(Window, &info->RootElement);
120 // --- Render / Resize ---
121 void Widget_RenderWidget(tWindow *Window, tElement *Element)
125 if( Element->Flags & ELEFLAG_NORENDER ) return ;
126 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
128 Widget_Decorator_RenderWidget(Window, Element);
130 for(child = Element->FirstChild; child; child = child->NextSibling)
132 Widget_RenderWidget(Window, child);
136 void Widget_UpdateDimensions(tElement *Element)
143 int fullCross, dynWith;
146 // - Get the fixed and minimum sizes of the element
147 for( child = Element->FirstChild; child; child = child->NextSibling )
149 // Ignore elements that will not be rendered
150 if( child->Flags & ELEFLAG_NORENDER ) continue ;
152 // Absolutely positioned elements don't affect dimensions
153 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
155 // Fixed width elements
156 if( child->FixedWith )
159 fixedSize += child->FixedWith;
161 else if( child->Flags & ELEFLAG_NOSTRETCH )
164 fixedSize += child->MinWith;
167 if( child->FixedCross && maxCross < child->FixedCross )
168 maxCross = child->FixedCross;
169 if( child->MinCross && maxCross < child->MinCross )
170 maxCross = child->MinCross;
174 // Get the dynamic with size from the unused space in the element
175 if( nChildren > nFixed ) {
176 if( Element->Flags & ELEFLAG_VERTICAL )
177 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
179 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
180 dynWith -= fixedSize;
181 if( dynWith < 0 ) return ;
182 dynWith /= nChildren - nFixed;
185 _SysDebug("%i - nChildren = %i, nFixed = %i, dynWith = %i, fixedSize = %i",
186 Element->ID, nChildren, nFixed, dynWith, fixedSize);
188 // Get the cross size
189 if( Element->Flags & ELEFLAG_VERTICAL )
190 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
192 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
194 // Pass 2 - Set sizes and recurse
195 for( child = Element->FirstChild; child; child = child->NextSibling )
199 // Ignore elements that will not be rendered
200 if( child->Flags & ELEFLAG_NORENDER ) continue ;
202 // --- Cross Size ---
203 // TODO: Expand to fill?
204 // TODO: Extra flag so options are (Expand, Equal, Wrap)
205 if( child->FixedCross )
206 cross = child->FixedCross;
207 else if( child->Flags & ELEFLAG_NOEXPAND )
208 cross = child->MinCross;
213 if( child->FixedWith )
214 with = child->FixedWith;
215 else if( child->Flags & ELEFLAG_NOSTRETCH )
216 with = child->MinWith;
221 if(with < child->MinWith) with = child->MinWith;
222 if(cross < child->MinCross) cross = child->MinCross;
224 // Update the dimensions if they have changed
225 if( Element->Flags & ELEFLAG_VERTICAL ) {
226 // If no change, don't recurse
227 if( child->CachedW == cross && child->CachedH == with )
229 child->CachedW = cross;
230 child->CachedH = with;
233 // If no change, don't recurse
234 if( child->CachedW == with && child->CachedH == cross )
236 child->CachedW = with;
237 child->CachedH = cross;
240 // Force the positions of child elements to be recalculated
243 // Recurse down so the child elements can be updated
244 Widget_UpdateDimensions(child);
250 * \brief Update the position of child elements
252 void Widget_UpdatePosition(tElement *Element)
257 if( Element->Flags & ELEFLAG_NORENDER ) return ;
259 _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
260 Element, Element->ID, Element->Type, Element->Flags);
263 x = Element->CachedX + Element->PaddingL;
264 y = Element->CachedY + Element->PaddingT;
267 for(child = Element->FirstChild; child; child = child->NextSibling)
270 // Ignore elements that will not be rendered
271 if( child->Flags & ELEFLAG_NORENDER ) continue ;
276 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
277 if(Element->Flags & ELEFLAG_VERTICAL)
278 newX += Element->CachedW/2 - child->CachedW/2;
280 newY += Element->CachedH/2 - child->CachedH/2;
282 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
283 if(Element->Flags & ELEFLAG_VERTICAL )
284 newX += Element->CachedW - child->CachedW
285 - Element->PaddingL - Element->PaddingR;
287 newY += Element->CachedH - child->CachedH
288 - Element->PaddingT - Element->PaddingB;
291 _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
293 // Check for changes, and don't update if there was no change
294 if( newX != child->CachedX || newY != child->CachedY )
296 child->CachedX = newX;
297 child->CachedY = newY;
298 // Update child's children positions
299 Widget_UpdatePosition(child);
303 if(Element->Flags & ELEFLAG_VERTICAL ) {
304 y += child->CachedH + Element->GapSize;
307 x += child->CachedW + Element->GapSize;
313 * \brief Update the minimum dimensions of the element
314 * \note Called after a child's minimum dimensions have changed
316 void Widget_UpdateMinDims(tElement *Element)
322 Element->MinCross = 0;
323 Element->MinWith = 0;
325 for(child = Element->FirstChild; child; child = child->NextSibling)
327 if( Element->Parent &&
328 (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
331 if(child->FixedCross)
332 Element->MinCross += child->FixedCross;
334 Element->MinCross += child->MinCross;
336 Element->MinWith += child->FixedWith;
338 Element->MinWith += child->MinWith;
342 if(child->FixedCross)
343 Element->MinWith += child->FixedCross;
345 Element->MinWith += child->MinCross;
347 Element->MinCross += child->FixedWith;
349 Element->MinCross += child->MinWith;
354 Widget_UpdateMinDims(Element->Parent);
357 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
359 tElement *ret, *next, *ele;
361 next = &Info->RootElement;
366 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
368 if(ele->Flags & ELEFLAG_NORENDER) continue;
369 if(X < ele->CachedX) continue;
370 if(Y < ele->CachedY) continue;
371 if(X >= ele->CachedX + ele->CachedW) continue;
372 if(Y >= ele->CachedY + ele->CachedH) continue;
380 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
384 if( ID == -1 ) return &Info->RootElement;
386 if( ID < Info->TableSize ) return Info->ElementTable[ID];
388 ele = Info->ElementTable[ID % Info->TableSize];
389 while(ele && ele->ID != ID) ele = ele->ListNext;
393 // --- Message Handlers ---
394 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
396 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
397 tElement *parent, *new;
400 if( Len < sizeof(tWidgetMsg_Create) )
402 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
405 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
406 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
409 parent = Widget_GetElementById(Info, Msg->Parent);
412 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
416 // Check if the ID is already in use
417 if( Widget_GetElementById(Info, Msg->NewID) )
420 // Create new element
421 new = calloc(sizeof(tElement), 1);
422 new->Window = parent->Window;
423 new->ID = Msg->NewID;
424 new->Type = Msg->Type;
425 new->Parent = parent;
426 new->Flags = Msg->Flags;
433 if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type].Init )
434 gaWM_WidgetTypes[new->Type].Init(new);
436 // Add to parent's list
437 if(parent->LastChild)
438 parent->LastChild->NextSibling = new;
440 parent->FirstChild = new;
441 parent->LastChild = new;
445 tElement *ele, *prev = NULL;
446 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
448 prev->ListNext = new;
450 Info->ElementTable[new->ID % Info->TableSize] = new;
453 Widget_UpdateMinDims(parent);
456 void Widget_SetFlags(tWidgetWin *Info, int Len, tWidgetMsg_SetFlags *Msg)
460 if( Len < sizeof(tWidgetMsg_SetFlags) )
463 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
465 ele = Widget_GetElementById(Info, Msg->WidgetID);
468 Msg->Value &= Msg->Mask;
470 ele->Flags &= ~Msg->Mask;
471 ele->Flags |= Msg->Value;
474 void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg)
478 if( Len < sizeof(tWidgetMsg_SetSize) )
481 ele = Widget_GetElementById(Info, Msg->WidgetID);
484 ele->FixedWith = Msg->Value;
487 void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg)
491 if( Len < sizeof(tWidgetMsg_SetText) + 1 )
493 if( Msg->Text[Len - sizeof(tWidgetMsg_SetText) - 1] != '\0' )
496 ele = Widget_GetElementById(Info, Msg->WidgetID);
500 if( ele->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[ele->Type].UpdateText )
502 gaWM_WidgetTypes[ele->Type].UpdateText( ele, Msg->Text );
506 // if(ele->Text) free(ele->Text);
507 // ele->Text = strdup(Msg->Text);
511 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data)
513 tWidgetWin *info = Target->RendererInfo;
516 case WNDMSG_RESIZE: {
517 struct sWndMsg_Resize *msg = Data;
518 if(Len < sizeof(*msg)) return -1;
520 info->RootElement.CachedW = msg->W;
521 info->RootElement.CachedH = msg->H;
523 // TODO: Update dimensions of all child elements?
527 case WNDMSG_MOUSEBTN: {
528 struct sWndMsg_MouseButton *msg = Data;
529 tWidgetMsg_MouseBtn client_msg;
534 if(Len < sizeof(*msg)) return -1;
536 x = msg->X; y = msg->Y;
537 client_msg.Button = msg->Button;
538 client_msg.bPressed = msg->bPressed;
540 ele = Widget_GetElementByPos(info, x, y);
541 // Send event to all elements from `ele` upwards
542 for( ; ele; ele = ele->Parent )
544 if(ele->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[ele->Type].MouseButton)
546 rv = gaWM_WidgetTypes[ele->Type].MouseButton(
548 x - ele->CachedX, y - ele->CachedY,
549 msg->Button, msg->bPressed
551 // Allow a type to trap the input from going any higher
557 client_msg.X = x - ele->CachedX;
558 client_msg.Y = y - ele->CachedY;
559 client_msg.WidgetID = ele->ID;
560 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
566 case MSG_WIDGET_CREATE:
567 Widget_NewWidget(info, Len, Data);
570 case MSG_WIDGET_DELETE:
571 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
575 case MSG_WIDGET_SETFLAGS:
576 Widget_SetFlags(info, Len, Data);
580 case MSG_WIDGET_SETSIZE:
581 Widget_SetSize(info, Len, Data);
585 case MSG_WIDGET_SETTEXT:
586 Widget_SetText(info, Len, Data);
591 return 1; // Unhandled, pass to user
595 void Widget_Fire(tElement *Element)
598 msg.WidgetID = Element->ID;
599 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);
603 void Widget_TextBox_UpdateText(tElement *Element, const char *Text)
607 if(Element->Text) free(Element->Text);
608 Element->Text = strdup(Text);
610 WM_Render_GetTextDims(NULL, Element->Text, &w, &h);
611 if(Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL)) {
612 Element->MinCross = w;
613 Element->MinWith = h;
616 Element->MinWith = w;
617 Element->MinCross = h;
620 Widget_UpdateMinDims(Element->Parent);
623 void Widget_Image_UpdateText(tElement *Element, const char *Text)
625 if(Element->Data) free(Element->Data);
626 Element->Data = Image_Load( Text );
628 // Element->Flags &= ~ELEFLAG_FIXEDSIZE;
632 Element->CachedW = ((tImage*)Element->Data)->Width;
633 Element->CachedH = ((tImage*)Element->Data)->Height;
635 if(Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL) ) {
636 Element->MinCross = ((tImage*)Element->Data)->Width;
637 Element->MinWith = ((tImage*)Element->Data)->Height;
640 Element->MinWith = ((tImage*)Element->Data)->Width;
641 Element->MinCross = ((tImage*)Element->Data)->Height;
644 Widget_UpdateMinDims(Element->Parent);
646 // NOTE: Doesn't update Element->Text because it's useless
649 int Widget_Button_MouseButton(tElement *Element, int X, int Y, int Button, int bPress)
651 _SysDebug("Ele %i - Button %i %s",
653 (bPress ? "pressed" : "released")
655 if(!bPress) Widget_Fire(Element);