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 1
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;
165 // Check if this element can have children
166 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
170 // - Get the fixed and minimum sizes of the element
171 for( child = Element->FirstChild; child; child = child->NextSibling )
173 // Ignore elements that will not be rendered
174 if( child->Flags & ELEFLAG_NORENDER ) continue ;
176 // Absolutely positioned elements don't affect dimensions
177 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
179 // Fixed width elements
180 if( child->FixedWith )
183 fixedSize += child->FixedWith;
185 else if( child->Flags & ELEFLAG_NOSTRETCH )
188 fixedSize += child->MinWith;
191 if( child->FixedCross && maxCross < child->FixedCross )
192 maxCross = child->FixedCross;
193 if( child->MinCross && maxCross < child->MinCross )
194 maxCross = child->MinCross;
198 // Get the dynamic with size from the unused space in the element
199 if( nChildren > nFixed ) {
200 if( Element->Flags & ELEFLAG_VERTICAL )
201 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
203 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
204 dynWith -= fixedSize;
205 dynWith -= Element->GapSize * (nChildren-1);
206 if( dynWith < 0 ) return ;
207 dynWith /= nChildren - nFixed;
213 // Get the cross size
214 if( Element->Flags & ELEFLAG_VERTICAL )
215 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
217 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
219 _SysDebug("%i (p=%i) - WxH=%ix%i",
220 Element->ID, (Element->Parent ? Element->Parent->ID : -1),
221 Element->CachedW, Element->CachedH
223 _SysDebug(" %s dynWith = %i, fullCross = %i",
224 (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
228 // Pass 2 - Set sizes and recurse
229 for( child = Element->FirstChild; child; child = child->NextSibling )
233 // Ignore elements that will not be rendered
234 if( child->Flags & ELEFLAG_NORENDER ) continue ;
235 // Don't resize floating elements
236 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
238 // --- Cross Size ---
239 // TODO: Expand to fill?
240 // TODO: Extra flag so options are (Expand, Equal, Wrap)
241 if( child->FixedCross )
242 cross = child->FixedCross;
243 else if( child->Flags & ELEFLAG_NOEXPAND )
244 cross = child->MinCross;
249 if( child->FixedWith )
250 with = child->FixedWith;
251 else if( child->Flags & ELEFLAG_NOSTRETCH )
252 with = child->MinWith;
257 if(with < child->MinWith) with = child->MinWith;
258 if(cross < child->MinCross) cross = child->MinCross;
260 _SysDebug("with = %i", with);
262 // Update the dimensions if they have changed
263 if( Element->Flags & ELEFLAG_VERTICAL ) {
264 // If no change, don't recurse
265 if( child->CachedW == cross && child->CachedH == with )
267 child->CachedW = cross;
268 child->CachedH = with;
271 // If no change, don't recurse
272 if( child->CachedW == with && child->CachedH == cross )
274 child->CachedW = with;
275 child->CachedH = cross;
278 // Force the positions of child elements to be recalculated
281 // Recurse down so the child elements can be updated
282 Widget_UpdateDimensions(child);
288 * \brief Update the position of child elements
290 void Widget_UpdatePosition(tElement *Element)
295 if( Element->Flags & ELEFLAG_NORENDER ) return ;
297 // Check if this element can have children
298 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
301 // _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
302 // Element, Element->ID, Element->Type, Element->Flags);
305 x = Element->CachedX + Element->PaddingL;
306 y = Element->CachedY + Element->PaddingT;
309 for(child = Element->FirstChild; child; child = child->NextSibling)
312 // Ignore elements that will not be rendered
313 if( child->Flags & ELEFLAG_NORENDER ) continue ;
317 // Handle alignment (across parent)
318 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
319 if(Element->Flags & ELEFLAG_VERTICAL)
320 newX += Element->CachedW/2 - child->CachedW/2;
322 newY += Element->CachedH/2 - child->CachedH/2;
324 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
325 if(Element->Flags & ELEFLAG_VERTICAL )
326 newX += Element->CachedW - child->CachedW
327 - Element->PaddingL - Element->PaddingR;
329 newY += Element->CachedH - child->CachedH
330 - Element->PaddingT - Element->PaddingB;
333 // _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
335 // Check for changes, and don't update if there was no change
336 if( newX != child->CachedX || newY != child->CachedY )
338 child->CachedX = newX;
339 child->CachedY = newY;
340 // Update child's children positions
341 Widget_UpdatePosition(child);
345 if(Element->Flags & ELEFLAG_VERTICAL ) {
346 y += child->CachedH + Element->GapSize;
349 x += child->CachedW + Element->GapSize;
355 * \brief Update the minimum dimensions of the element
356 * \note Called after a child's minimum dimensions have changed
358 void Widget_UpdateMinDims(tElement *Element)
368 for(child = Element->FirstChild; child; child = child->NextSibling)
370 if( (Element->Flags & ELEFLAG_VERTICAL) )
372 if(child->FixedCross)
373 minW += child->FixedCross;
375 minW += child->MinCross;
377 minH += child->FixedWith;
379 minH += child->MinWith;
383 if(child->FixedCross)
384 minH += child->FixedCross;
386 minH += child->MinCross;
388 minW += child->FixedWith;
390 minW += child->MinWith;
394 if( Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL) )
396 Element->MinCross = Element->PaddingL + minW + Element->PaddingR;
397 Element->MinWith = Element->PaddingT + minH + Element->PaddingB;
401 Element->MinWith = Element->PaddingL + minW + Element->PaddingR;
402 Element->MinCross = Element->PaddingL + minH + Element->PaddingR;
406 Widget_UpdateMinDims(Element->Parent);
409 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
411 tElement *ret, *next, *ele;
413 next = &Info->RootElement;
418 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
420 if(ele->Flags & ELEFLAG_NORENDER) continue;
421 if(X < ele->CachedX) continue;
422 if(Y < ele->CachedY) continue;
423 if(X >= ele->CachedX + ele->CachedW) continue;
424 if(Y >= ele->CachedY + ele->CachedH) continue;
432 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
436 if( ID == -1 ) return &Info->RootElement;
438 if( ID < Info->TableSize ) return Info->ElementTable[ID];
440 ele = Info->ElementTable[ID % Info->TableSize];
441 while(ele && ele->ID != ID) ele = ele->ListNext;
445 // --- Message Handlers ---
446 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
448 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
449 tElement *parent, *new;
452 if( Len < sizeof(*Msg) )
454 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
457 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
458 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
460 if(Msg->Type >= ciWM_NumWidgetTypes)
462 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
467 parent = Widget_GetElementById(Info, Msg->Parent);
470 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
474 // Check if the ID is already in use
475 if( Widget_GetElementById(Info, Msg->NewID) )
478 // Create new element
479 new = calloc(sizeof(tElement), 1);
480 new->Window = parent->Window;
481 new->ID = Msg->NewID;
482 new->Type = Msg->Type;
483 new->Parent = parent;
484 new->Flags = Msg->Flags;
491 if( gaWM_WidgetTypes[new->Type]->Init )
492 gaWM_WidgetTypes[new->Type]->Init(new);
494 // Add to parent's list
495 if(parent->LastChild)
496 parent->LastChild->NextSibling = new;
498 parent->FirstChild = new;
499 parent->LastChild = new;
503 tElement *ele, *prev = NULL;
504 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
506 prev->ListNext = new;
508 Info->ElementTable[new->ID % Info->TableSize] = new;
511 Widget_UpdateMinDims(parent);
514 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
518 if( Len < sizeof(*Msg) )
521 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
523 ele = Widget_GetElementById(Info, Msg->WidgetID);
526 ele->Flags &= ~Msg->Mask;
527 ele->Flags |= Msg->Value & Msg->Mask;
530 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
534 if( Len < sizeof(*Msg) )
537 ele = Widget_GetElementById(Info, Msg->WidgetID);
540 ele->FixedWith = Msg->Value;
543 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
547 if( Len < sizeof(*Msg) + 1 )
549 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
552 ele = Widget_GetElementById(Info, Msg->WidgetID);
556 if( gaWM_WidgetTypes[ele->Type]->UpdateText )
558 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
562 // if(ele->Text) free(ele->Text);
563 // ele->Text = strdup(Msg->Text);
567 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
569 tWidgetWin *info = Target->RendererInfo;
572 case WNDMSG_RESIZE: {
573 const struct sWndMsg_Resize *msg = Data;
574 if(Len < sizeof(*msg)) return -1;
576 info->RootElement.CachedW = msg->W;
577 info->RootElement.CachedH = msg->H;
579 // TODO: Update dimensions of all child elements?
583 case WNDMSG_MOUSEMOVE: {
584 // _SysDebug("TODO: Support widget mouse move events");
587 case WNDMSG_MOUSEBTN: {
588 const struct sWndMsg_MouseButton *msg = Data;
589 tWidgetMsg_MouseBtn client_msg;
594 if(Len < sizeof(*msg)) return -1;
596 x = msg->X; y = msg->Y;
597 client_msg.Button = msg->Button;
598 client_msg.bPressed = msg->bPressed;
600 ele = Widget_GetElementByPos(info, x, y);
601 // Send event to all elements from `ele` upwards
602 for( ; ele; ele = ele->Parent )
604 if(gaWM_WidgetTypes[ele->Type]->MouseButton)
606 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
608 x - ele->CachedX, y - ele->CachedY,
609 msg->Button, msg->bPressed
611 // Allow a type to trap the input from going any higher
617 client_msg.X = x - ele->CachedX;
618 client_msg.Y = y - ele->CachedY;
619 client_msg.WidgetID = ele->ID;
620 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
626 case MSG_WIDGET_CREATE:
627 Widget_NewWidget(info, Len, Data);
630 case MSG_WIDGET_DELETE:
631 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
635 case MSG_WIDGET_SETFLAGS:
636 Widget_SetFlags(info, Len, Data);
640 case MSG_WIDGET_SETSIZE:
641 Widget_SetSize(info, Len, Data);
645 case MSG_WIDGET_SETTEXT:
646 Widget_SetText(info, Len, Data);
651 return 1; // Unhandled, pass to user
655 void Widget_Fire(tElement *Element)
658 msg.WidgetID = Element->ID;
659 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);