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 "widget/common.h"
16 #define DEFAULT_ELETABLE_SIZE 64
17 #define BORDER_EVERYTHING 0
20 int Renderer_Widget_Init(void);
21 tWindow *Renderer_Widget_Create(int Flags);
22 void Renderer_Widget_Redraw(tWindow *Window);
24 void Widget_RenderWidget(tWindow *Window, tElement *Element);
25 void Widget_UpdateDimensions(tElement *Element);
26 void Widget_UpdatePosition(tElement *Element);
28 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID);
29 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg);
30 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg);
31 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg);
32 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg);
33 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data);
36 tWMRenderer gRenderer_Widget = {
38 .CreateWindow = Renderer_Widget_Create,
39 .Redraw = Renderer_Widget_Redraw,
40 .HandleMessage = Renderer_Widget_HandleMessage
43 // --- Element callbacks
44 tWidgetDef *gaWM_WidgetTypes[NUM_ELETYPES];
45 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
46 tWidgetDef gWidget_NullWidgetDef;
49 int Renderer_Widget_Init(void)
52 WM_RegisterRenderer(&gRenderer_Widget);
54 for(i = 0; i < ciWM_NumWidgetTypes; i ++)
56 if(gaWM_WidgetTypes[i] != NULL) continue;
58 gaWM_WidgetTypes[i] = &gWidget_NullWidgetDef;
64 void Widget_int_SetTypeDef(int Type, tWidgetDef *Ptr)
66 if( Type < 0 || Type >= ciWM_NumWidgetTypes ) {
67 _SysDebug("ERROR - Widget ID %i out of range (from %p)",
68 Type, __builtin_return_address(0)
73 if( gaWM_WidgetTypes[Type] && gaWM_WidgetTypes[Type] != &gWidget_NullWidgetDef )
75 _SysDebug("ERROR - Widget ID %i redefined by %p",
76 Type, __builtin_return_address(0)
81 gaWM_WidgetTypes[Type] = Ptr;
82 _SysDebug("Registered type %i to %p", Type, Ptr);
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;
105 info->RootElement.PaddingT = 2;
106 info->RootElement.PaddingB = 2;
107 info->RootElement.PaddingL = 2;
108 info->RootElement.PaddingR = 2;
113 void Renderer_Widget_Redraw(tWindow *Window)
115 tWidgetWin *info = Window->RendererInfo;
116 WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
118 Widget_UpdateDimensions(&info->RootElement);
119 Widget_UpdatePosition(&info->RootElement);
121 Widget_RenderWidget(Window, &info->RootElement);
124 // --- Render / Resize ---
125 void Widget_RenderWidget(tWindow *Window, tElement *Element)
129 if( Element->Flags & ELEFLAG_NORENDER ) return ;
130 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
132 #if BORDER_EVERYTHING
135 Element->CachedX, Element->CachedY,
136 Element->CachedW, Element->CachedH,
141 if(gaWM_WidgetTypes[Element->Type]->Render)
143 gaWM_WidgetTypes[Element->Type]->Render(Window, Element);
147 Widget_Decorator_RenderWidget(Window, Element);
150 for(child = Element->FirstChild; child; child = child->NextSibling)
152 Widget_RenderWidget(Window, child);
156 void Widget_UpdateDimensions(tElement *Element)
163 int fullCross, dynWith = 0;
164 int bVertical = Element->Flags & ELEFLAG_VERTICAL;
166 // Check if this element can have children
167 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
171 // - Get the fixed and minimum sizes of the element
172 for( child = Element->FirstChild; child; child = child->NextSibling )
174 int minWith = bVertical ? child->MinH : child->MinW;
175 int minCross = bVertical ? child->MinW : child->MinH;
177 // Ignore elements that will not be rendered
178 if( child->Flags & ELEFLAG_NORENDER ) continue ;
180 // Absolutely positioned elements don't affect dimensions
181 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
183 // Fixed width elements
184 if( child->FixedWith )
187 fixedSize += child->FixedWith;
189 else if( child->Flags & ELEFLAG_NOSTRETCH )
192 fixedSize += minWith;
195 if( maxCross < child->FixedCross ) maxCross = child->FixedCross;
196 if( maxCross < minCross ) maxCross = minCross;
200 // Get the dynamic with size from the unused space in the element
201 if( nChildren > nFixed ) {
203 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
205 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
206 dynWith -= fixedSize;
207 dynWith -= Element->GapSize * (nChildren-1);
208 if( dynWith < 0 ) return ;
209 dynWith /= nChildren - nFixed;
215 // Get the cross size
217 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
219 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
221 _SysDebug("%i (p=%i) - WxH=%ix%i",
222 Element->ID, (Element->Parent ? Element->Parent->ID : -1),
223 Element->CachedW, Element->CachedH
225 _SysDebug(" %s dynWith = %i, fullCross = %i",
226 (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
230 // Pass 2 - Set sizes and recurse
231 for( child = Element->FirstChild; child; child = child->NextSibling )
235 // Ignore elements that will not be rendered
236 if( child->Flags & ELEFLAG_NORENDER ) continue ;
237 // Don't resize floating elements
238 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
241 if( child->Flags & ELEFLAG_NOEXPAND )
244 w = child->FixedCross ? child->FixedCross : fullCross;
246 w = child->FixedWith ? child->FixedWith : dynWith;
249 if( child->Flags & ELEFLAG_NOSTRETCH )
252 h = child->FixedWith ? child->FixedWith : dynWith;
254 h = child->FixedCross ? child->FixedCross : fullCross;
256 if(w < child->MinW) w = child->MinW;
257 if(h < child->MinH) h = child->MinH;
259 _SysDebug("Child %ix%i (min %ix%i)", w, h, child->MinW, child->MinH);
261 // Update the dimensions if they have changed
262 if( child->CachedW == w && child->CachedH == h )
267 // Force the positions of child elements to be recalculated
270 // Recurse down so the child elements can be updated
271 Widget_UpdateDimensions(child);
277 * \brief Update the position of child elements
279 void Widget_UpdatePosition(tElement *Element)
284 if( Element->Flags & ELEFLAG_NORENDER ) return ;
286 // Check if this element can have children
287 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
290 // _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
291 // Element, Element->ID, Element->Type, Element->Flags);
294 x = Element->CachedX + Element->PaddingL;
295 y = Element->CachedY + Element->PaddingT;
298 for(child = Element->FirstChild; child; child = child->NextSibling)
301 // Ignore elements that will not be rendered
302 if( child->Flags & ELEFLAG_NORENDER ) continue ;
306 // Handle alignment (across parent)
307 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
308 if(Element->Flags & ELEFLAG_VERTICAL)
309 newX += Element->CachedW/2 - child->CachedW/2;
311 newY += Element->CachedH/2 - child->CachedH/2;
313 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
314 if(Element->Flags & ELEFLAG_VERTICAL )
315 newX += Element->CachedW - child->CachedW
316 - Element->PaddingL - Element->PaddingR;
318 newY += Element->CachedH - child->CachedH
319 - Element->PaddingT - Element->PaddingB;
322 // _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
324 // Check for changes, and don't update if there was no change
325 if( newX != child->CachedX || newY != child->CachedY )
327 child->CachedX = newX;
328 child->CachedY = newY;
329 // Update child's children positions
330 Widget_UpdatePosition(child);
334 if(Element->Flags & ELEFLAG_VERTICAL ) {
335 y += child->CachedH + Element->GapSize;
338 x += child->CachedW + Element->GapSize;
344 * \brief Update the minimum dimensions of the element
345 * \note Called after a child's minimum dimensions have changed
347 void Widget_UpdateMinDims(tElement *Element)
359 for(child = Element->FirstChild; child; child = child->NextSibling)
363 if(Element->Flags & ELEFLAG_NORENDER) continue ;
365 if( (Element->Flags & ELEFLAG_VERTICAL) )
367 cross = child->FixedCross ? child->FixedCross : child->MinW;
368 if(minW < cross) minW = cross;
369 minH += child->FixedWith ? child->FixedWith : child->MinH;
373 cross = child->FixedCross ? child->FixedCross : child->MinH;
374 minW += child->FixedWith ? child->FixedWith : child->MinW;
375 if(minH < cross) minH = cross;
377 // _SysDebug("%i/%i cross = %i", Element->ID, child->ID, cross);
382 if( Element->Flags & ELEFLAG_VERTICAL )
383 minH += (nChildren - 1) * Element->GapSize;
385 minW += (nChildren - 1) * Element->GapSize;
387 Element->MinW = Element->PaddingL + minW + Element->PaddingR;
388 Element->MinH = Element->PaddingT + minH + Element->PaddingB;
391 Widget_UpdateMinDims(Element->Parent);
394 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
396 tElement *ret, *next, *ele;
398 next = &Info->RootElement;
403 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
405 if(ele->Flags & ELEFLAG_NORENDER) continue;
406 if(X < ele->CachedX) continue;
407 if(Y < ele->CachedY) continue;
408 if(X >= ele->CachedX + ele->CachedW) continue;
409 if(Y >= ele->CachedY + ele->CachedH) continue;
417 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
421 if( ID == -1 ) return &Info->RootElement;
423 if( ID < Info->TableSize ) return Info->ElementTable[ID];
425 ele = Info->ElementTable[ID % Info->TableSize];
426 while(ele && ele->ID != ID) ele = ele->ListNext;
430 // --- Message Handlers ---
431 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
433 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
434 tElement *parent, *new;
437 if( Len < sizeof(*Msg) )
439 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
442 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
443 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
445 if(Msg->Type >= ciWM_NumWidgetTypes)
447 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
452 parent = Widget_GetElementById(Info, Msg->Parent);
455 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
459 // Check if the ID is already in use
460 if( Widget_GetElementById(Info, Msg->NewID) )
463 // Create new element
464 new = calloc(sizeof(tElement), 1);
465 new->Window = parent->Window;
466 new->ID = Msg->NewID;
467 new->Type = Msg->Type;
468 new->Parent = parent;
469 new->Flags = Msg->Flags;
476 if( gaWM_WidgetTypes[new->Type]->Init )
477 gaWM_WidgetTypes[new->Type]->Init(new);
479 // Add to parent's list
480 if(parent->LastChild)
481 parent->LastChild->NextSibling = new;
483 parent->FirstChild = new;
484 parent->LastChild = new;
488 tElement *ele, *prev = NULL;
489 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
491 prev->ListNext = new;
493 Info->ElementTable[new->ID % Info->TableSize] = new;
496 Widget_UpdateMinDims(parent);
499 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
503 if( Len < sizeof(*Msg) )
506 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
508 ele = Widget_GetElementById(Info, Msg->WidgetID);
511 ele->Flags &= ~Msg->Mask;
512 ele->Flags |= Msg->Value & Msg->Mask;
515 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
519 if( Len < sizeof(*Msg) )
522 ele = Widget_GetElementById(Info, Msg->WidgetID);
525 ele->FixedWith = Msg->Value;
528 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
532 if( Len < sizeof(*Msg) + 1 )
534 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
537 ele = Widget_GetElementById(Info, Msg->WidgetID);
541 if( gaWM_WidgetTypes[ele->Type]->UpdateText )
543 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
547 // if(ele->Text) free(ele->Text);
548 // ele->Text = strdup(Msg->Text);
552 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
554 tWidgetWin *info = Target->RendererInfo;
557 case WNDMSG_RESIZE: {
558 const struct sWndMsg_Resize *msg = Data;
559 if(Len < sizeof(*msg)) return -1;
561 info->RootElement.CachedW = msg->W;
562 info->RootElement.CachedH = msg->H;
564 // TODO: Update dimensions of all child elements?
568 case WNDMSG_MOUSEMOVE: {
569 // _SysDebug("TODO: Support widget mouse move events");
572 case WNDMSG_MOUSEBTN: {
573 const struct sWndMsg_MouseButton *msg = Data;
574 tWidgetMsg_MouseBtn client_msg;
579 if(Len < sizeof(*msg)) return -1;
581 x = msg->X; y = msg->Y;
582 client_msg.Button = msg->Button;
583 client_msg.bPressed = msg->bPressed;
585 ele = Widget_GetElementByPos(info, x, y);
586 // Send event to all elements from `ele` upwards
587 for( ; ele; ele = ele->Parent )
589 if(gaWM_WidgetTypes[ele->Type]->MouseButton)
591 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
593 x - ele->CachedX, y - ele->CachedY,
594 msg->Button, msg->bPressed
596 // Allow a type to trap the input from going any higher
602 client_msg.X = x - ele->CachedX;
603 client_msg.Y = y - ele->CachedY;
604 client_msg.WidgetID = ele->ID;
605 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
611 case MSG_WIDGET_CREATE:
612 Widget_NewWidget(info, Len, Data);
615 case MSG_WIDGET_DELETE:
616 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
620 case MSG_WIDGET_SETFLAGS:
621 Widget_SetFlags(info, Len, Data);
625 case MSG_WIDGET_SETSIZE:
626 Widget_SetSize(info, Len, Data);
630 case MSG_WIDGET_SETTEXT:
631 Widget_SetText(info, Len, Data);
636 return 1; // Unhandled, pass to user
640 void Widget_Fire(tElement *Element)
643 msg.WidgetID = Element->ID;
644 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);