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;
84 tWindow *Renderer_Widget_Create(int Flags)
88 int eletable_size = DEFAULT_ELETABLE_SIZE;
90 _SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
92 // TODO: Use `Flags` as default element count?
93 // - Actaully, it's taken by the root ele flags
94 // - Use the upper bits?
96 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
97 info = ret->RendererInfo;
99 info->TableSize = eletable_size;
100 info->FocusedElement = &info->RootElement;
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, Window->W, Window->H, 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);
146 for(child = Element->FirstChild; child; child = child->NextSibling)
148 Widget_RenderWidget(Window, child);
152 void Widget_UpdateDimensions(tElement *Element)
159 int fullCross, dynWith = 0;
160 int bVertical = Element->Flags & ELEFLAG_VERTICAL;
162 // Check if this element can have children
163 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
167 // - Get the fixed and minimum sizes of the element
168 for( child = Element->FirstChild; child; child = child->NextSibling )
170 int minWith = bVertical ? child->MinH : child->MinW;
171 int minCross = bVertical ? child->MinW : child->MinH;
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 += minWith;
191 if( maxCross < child->FixedCross ) maxCross = child->FixedCross;
192 if( maxCross < minCross ) maxCross = minCross;
196 // Get the dynamic with size from the unused space in the element
197 if( nChildren > nFixed ) {
199 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
201 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
202 dynWith -= fixedSize;
203 dynWith -= Element->GapSize * (nChildren-1);
204 if( dynWith < 0 ) return ;
205 dynWith /= nChildren - nFixed;
211 // Get the cross size
213 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
215 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
217 _SysDebug("%i (p=%i) - WxH=%ix%i",
218 Element->ID, (Element->Parent ? Element->Parent->ID : -1),
219 Element->CachedW, Element->CachedH
221 _SysDebug(" %s dynWith = %i, fullCross = %i",
222 (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
226 // Pass 2 - Set sizes and recurse
227 for( child = Element->FirstChild; child; child = child->NextSibling )
231 // Ignore elements that will not be rendered
232 if( child->Flags & ELEFLAG_NORENDER ) continue ;
233 // Don't resize floating elements
234 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
237 if( child->Flags & ELEFLAG_NOEXPAND )
240 w = child->FixedCross ? child->FixedCross : fullCross;
242 w = child->FixedWith ? child->FixedWith : dynWith;
245 if( child->Flags & ELEFLAG_NOSTRETCH )
248 h = child->FixedWith ? child->FixedWith : dynWith;
250 h = child->FixedCross ? child->FixedCross : fullCross;
252 if(w < child->MinW) w = child->MinW;
253 if(h < child->MinH) h = child->MinH;
255 // _SysDebug("Child %ix%i (min %ix%i)", w, h, child->MinW, child->MinH);
257 // Update the dimensions if they have changed
258 if( child->CachedW == w && child->CachedH == h )
263 // Force the positions of child elements to be recalculated
266 // Recurse down so the child elements can be updated
267 Widget_UpdateDimensions(child);
273 * \brief Update the position of child elements
275 void Widget_UpdatePosition(tElement *Element)
280 if( Element->Flags & ELEFLAG_NORENDER ) return ;
282 // Check if this element can have children
283 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
286 // _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
287 // Element, Element->ID, Element->Type, Element->Flags);
290 x = Element->CachedX + Element->PaddingL;
291 y = Element->CachedY + Element->PaddingT;
294 for(child = Element->FirstChild; child; child = child->NextSibling)
297 // Ignore elements that will not be rendered
298 if( child->Flags & ELEFLAG_NORENDER ) continue ;
302 // Handle alignment (across parent)
303 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
304 if(Element->Flags & ELEFLAG_VERTICAL)
305 newX += Element->CachedW/2 - child->CachedW/2;
307 newY += Element->CachedH/2 - child->CachedH/2;
309 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
310 if(Element->Flags & ELEFLAG_VERTICAL )
311 newX += Element->CachedW - child->CachedW
312 - Element->PaddingL - Element->PaddingR;
314 newY += Element->CachedH - child->CachedH
315 - Element->PaddingT - Element->PaddingB;
318 // _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
320 // Check for changes, and don't update if there was no change
321 if( newX != child->CachedX || newY != child->CachedY )
323 child->CachedX = newX;
324 child->CachedY = newY;
325 // Update child's children positions
326 Widget_UpdatePosition(child);
330 if(Element->Flags & ELEFLAG_VERTICAL ) {
331 y += child->CachedH + Element->GapSize;
334 x += child->CachedW + Element->GapSize;
340 * \brief Update the minimum dimensions of the element
341 * \note Called after a child's minimum dimensions have changed
343 void Widget_UpdateMinDims(tElement *Element)
355 for(child = Element->FirstChild; child; child = child->NextSibling)
359 if(Element->Flags & ELEFLAG_NORENDER) continue ;
361 if( (Element->Flags & ELEFLAG_VERTICAL) )
363 cross = child->FixedCross ? child->FixedCross : child->MinW;
364 if(minW < cross) minW = cross;
365 minH += child->FixedWith ? child->FixedWith : child->MinH;
369 cross = child->FixedCross ? child->FixedCross : child->MinH;
370 minW += child->FixedWith ? child->FixedWith : child->MinW;
371 if(minH < cross) minH = cross;
373 // _SysDebug("%i/%i cross = %i", Element->ID, child->ID, cross);
378 if( Element->Flags & ELEFLAG_VERTICAL )
379 minH += (nChildren - 1) * Element->GapSize;
381 minW += (nChildren - 1) * Element->GapSize;
383 Element->MinW = Element->PaddingL + minW + Element->PaddingR;
384 Element->MinH = Element->PaddingT + minH + Element->PaddingB;
387 Widget_UpdateMinDims(Element->Parent);
390 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
392 tElement *ret, *next, *ele;
394 next = &Info->RootElement;
399 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
401 if(ele->Flags & ELEFLAG_NORENDER) continue;
402 if(X < ele->CachedX) continue;
403 if(Y < ele->CachedY) continue;
404 if(X >= ele->CachedX + ele->CachedW) continue;
405 if(Y >= ele->CachedY + ele->CachedH) continue;
413 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
417 if( ID == -1 ) return &Info->RootElement;
419 if( ID < Info->TableSize ) return Info->ElementTable[ID];
421 ele = Info->ElementTable[ID % Info->TableSize];
422 while(ele && ele->ID != ID) ele = ele->ListNext;
426 // --- Message Handlers ---
427 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
429 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
430 tElement *parent, *new;
433 if( Len < sizeof(*Msg) )
435 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
438 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
439 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
441 if(Msg->Type >= ciWM_NumWidgetTypes)
443 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
448 parent = Widget_GetElementById(Info, Msg->Parent);
451 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
455 // Check if the ID is already in use
456 if( Widget_GetElementById(Info, Msg->NewID) )
459 // Create new element
460 new = calloc(sizeof(tElement), 1);
461 new->Window = parent->Window;
462 new->ID = Msg->NewID;
463 new->Type = Msg->Type;
464 new->Parent = parent;
465 new->Flags = Msg->Flags;
472 if( gaWM_WidgetTypes[new->Type]->Init )
473 gaWM_WidgetTypes[new->Type]->Init(new);
475 // Add to parent's list
476 if(parent->LastChild)
477 parent->LastChild->NextSibling = new;
479 parent->FirstChild = new;
480 parent->LastChild = new;
484 tElement *ele, *prev = NULL;
485 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
487 prev->ListNext = new;
489 Info->ElementTable[new->ID % Info->TableSize] = new;
492 Widget_UpdateMinDims(parent);
495 void Widget_SetFocus(tWidgetWin *Info, tElement *Ele)
499 Info->FocusedElement = Ele;
502 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
506 if( Len < sizeof(*Msg) )
509 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
511 ele = Widget_GetElementById(Info, Msg->WidgetID);
514 ele->Flags &= ~Msg->Mask;
515 ele->Flags |= Msg->Value & Msg->Mask;
518 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
522 if( Len < sizeof(*Msg) )
525 ele = Widget_GetElementById(Info, Msg->WidgetID);
528 ele->FixedWith = Msg->Value;
531 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
535 if( Len < sizeof(*Msg) + 1 )
537 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
540 ele = Widget_GetElementById(Info, Msg->WidgetID);
544 if( gaWM_WidgetTypes[ele->Type]->UpdateText )
546 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
550 // if(ele->Text) free(ele->Text);
551 // ele->Text = strdup(Msg->Text);
555 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
557 tWidgetWin *info = Target->RendererInfo;
561 case WNDMSG_RESIZE: {
562 const struct sWndMsg_Resize *msg = Data;
563 if(Len < sizeof(*msg)) return -1;
565 info->RootElement.CachedW = msg->W;
566 info->RootElement.CachedH = msg->H;
568 // TODO: Update dimensions of all child elements?
572 case WNDMSG_MOUSEMOVE: {
573 // _SysDebug("TODO: Support widget mouse move events");
576 case WNDMSG_MOUSEBTN: {
577 const struct sWndMsg_MouseButton *msg = Data;
578 tWidgetMsg_MouseBtn client_msg;
582 if(Len < sizeof(*msg)) return -1;
584 x = msg->X; y = msg->Y;
585 client_msg.Button = msg->Button;
586 client_msg.bPressed = msg->bPressed;
588 ele = Widget_GetElementByPos(info, x, y);
589 Widget_SetFocus(info, ele);
590 // Send event to all elements from `ele` upwards
591 for( ; ele; ele = ele->Parent )
593 if(gaWM_WidgetTypes[ele->Type]->MouseButton)
595 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
597 x - ele->CachedX, y - ele->CachedY,
598 msg->Button, msg->bPressed
600 // Allow a type to trap the input from going any higher
606 client_msg.X = x - ele->CachedX;
607 client_msg.Y = y - ele->CachedY;
608 client_msg.WidgetID = ele->ID;
609 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
614 case WNDMSG_KEYDOWN: {
615 const struct sWndMsg_KeyAction *msg = Data;
616 if(Len < sizeof(*msg)) return -1;
618 if(!info->FocusedElement) return 0;
619 ele = info->FocusedElement;
621 if(gaWM_WidgetTypes[ele->Type]->KeyDown)
622 gaWM_WidgetTypes[ele->Type]->KeyDown(ele, msg->KeySym, msg->UCS32);
625 // TODO: Pass to user
630 case WNDMSG_KEYFIRE: {
631 const struct sWndMsg_KeyAction *msg = Data;
632 if(Len < sizeof(*msg)) return -1;
634 if(!info->FocusedElement) return 0;
635 ele = info->FocusedElement;
637 if(gaWM_WidgetTypes[ele->Type]->KeyFire)
638 gaWM_WidgetTypes[ele->Type]->KeyFire(ele, msg->KeySym, msg->UCS32);
641 // TODO: Pass the buck
646 const struct sWndMsg_KeyAction *msg = Data;
647 if(Len < sizeof(*msg)) return -1;
649 if(!info->FocusedElement) return 0;
650 ele = info->FocusedElement;
652 if(gaWM_WidgetTypes[ele->Type]->KeyUp)
653 gaWM_WidgetTypes[ele->Type]->KeyUp(ele, msg->KeySym);
656 // TODO: Pass the buck
661 case MSG_WIDGET_CREATE:
662 Widget_NewWidget(info, Len, Data);
666 case MSG_WIDGET_DELETE:
667 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
670 // Set focused widget
671 case MSG_WIDGET_SETFOCUS: {
673 const tWidgetMsg_SetFocus *msg = Data;
674 if(Len < sizeof(*msg)) return -1;
676 ele = Widget_GetElementById(info, msg->WidgetID);
677 Widget_SetFocus(info, ele);
681 case MSG_WIDGET_SETFLAGS:
682 Widget_SetFlags(info, Len, Data);
686 case MSG_WIDGET_SETSIZE:
687 Widget_SetSize(info, Len, Data);
691 case MSG_WIDGET_SETTEXT:
692 Widget_SetText(info, Len, Data);
697 return 1; // Unhandled, pass to user
701 void Widget_Fire(tElement *Element)
704 msg.WidgetID = Element->ID;
705 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);