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"),
223 // dynWith, fullCross
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 tElement *Widget_int_Create(tWidgetWin *Info, tElement *Parent, int ID, int Type, int Flags)
428 if( Widget_GetElementById(Info, ID) )
431 // Create new element
432 tElement *new = calloc(sizeof(tElement), 1);
433 new->Window = Parent->Window;
436 new->Parent = Parent;
444 if( gaWM_WidgetTypes[Type]->Init )
445 gaWM_WidgetTypes[Type]->Init(new);
447 // Add to parent's list
448 if(Parent->LastChild)
449 Parent->LastChild->NextSibling = new;
451 Parent->FirstChild = new;
452 Parent->LastChild = new;
456 tElement *ele, *prev = NULL;
457 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
459 prev->ListNext = new;
461 Info->ElementTable[new->ID % Info->TableSize] = new;
467 // --- Message Handlers ---
468 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
470 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
474 if( Len < sizeof(*Msg) )
476 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
479 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
480 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
482 if(Msg->Type >= ciWM_NumWidgetTypes)
484 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
489 parent = Widget_GetElementById(Info, Msg->Parent);
492 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
496 Widget_int_Create(Info, parent, Msg->NewID, Msg->Type, Msg->Flags);
498 Widget_UpdateMinDims(parent);
501 void Widget_NewWidgetSubwin(tWidgetWin *Info, size_t Len, const tWidgetMsg_CreateSubWin *Msg)
503 const int max_debugname_len = Len - sizeof(tWidgetMsg_CreateSubWin);
504 tElement *parent, *new;
507 if( Len < sizeof(*Msg) )
509 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
512 parent = Widget_GetElementById(Info, Msg->Parent);
514 if( Widget_GetElementById(Info, Msg->NewID) ) return ;
516 new = Widget_int_Create(Info, parent, Msg->NewID, Msg->Type, Msg->Flags);
517 new->Data = WM_GetWindowByID(parent->Window, Msg->WindowHandle);
518 Widget_UpdateMinDims(parent);
521 void Widget_SetFocus(tWidgetWin *Info, tElement *Ele)
525 Info->FocusedElement = Ele;
528 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
532 if( Len < sizeof(*Msg) )
535 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
537 ele = Widget_GetElementById(Info, Msg->WidgetID);
540 ele->Flags &= ~Msg->Mask;
541 ele->Flags |= Msg->Value & Msg->Mask;
544 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
548 if( Len < sizeof(*Msg) )
551 ele = Widget_GetElementById(Info, Msg->WidgetID);
554 ele->FixedWith = Msg->Value;
557 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
561 if( Len < sizeof(*Msg) + 1 )
563 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
566 ele = Widget_GetElementById(Info, Msg->WidgetID);
570 if( gaWM_WidgetTypes[ele->Type]->UpdateText )
572 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
576 // if(ele->Text) free(ele->Text);
577 // ele->Text = strdup(Msg->Text);
581 int Widget_GetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
583 if( Len < sizeof(*Msg) )
585 if( Len > sizeof(*Msg) )
586 return 1; // Pass to user
588 const char *text = NULL;
589 tElement *ele = Widget_GetElementById(Info, Msg->WidgetID);
593 char buf[sizeof(tWidgetMsg_SetText) + strlen(text?text:"") + 1];
594 tWidgetMsg_SetText *omsg = (void*)buf;
597 omsg->WidgetID = Msg->WidgetID;
598 strcpy(omsg->Text, text);
605 WM_SendMessage(Info->RootElement.Window, Info->RootElement.Window, MSG_WIDGET_GETTEXT, sizeof(buf), buf);
609 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
611 tWidgetWin *info = Target->RendererInfo;
615 case WNDMSG_RESIZE: {
616 const struct sWndMsg_Resize *msg = Data;
617 if(Len < sizeof(*msg)) return -1;
619 info->RootElement.CachedW = msg->W;
620 info->RootElement.CachedH = msg->H;
622 // TODO: Update dimensions of all child elements?
626 case WNDMSG_MOUSEMOVE: {
627 // _SysDebug("TODO: Support widget mouse move events");
630 case WNDMSG_MOUSEBTN: {
631 const struct sWndMsg_MouseButton *msg = Data;
632 tWidgetMsg_MouseBtn client_msg;
636 if(Len < sizeof(*msg)) return -1;
638 x = msg->X; y = msg->Y;
639 client_msg.Button = msg->Button;
640 client_msg.bPressed = msg->bPressed;
642 ele = Widget_GetElementByPos(info, x, y);
643 Widget_SetFocus(info, ele);
644 // Send event to all elements from `ele` upwards
645 for( ; ele; ele = ele->Parent )
647 if(gaWM_WidgetTypes[ele->Type]->MouseButton)
649 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
651 x - ele->CachedX, y - ele->CachedY,
652 msg->Button, msg->bPressed
654 // Allow a type to trap the input from going any higher
660 client_msg.X = x - ele->CachedX;
661 client_msg.Y = y - ele->CachedY;
662 client_msg.WidgetID = ele->ID;
663 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
668 case WNDMSG_KEYDOWN: {
669 const struct sWndMsg_KeyAction *msg = Data;
670 if(Len < sizeof(*msg)) return -1;
672 if(!info->FocusedElement) return 0;
673 ele = info->FocusedElement;
675 if(gaWM_WidgetTypes[ele->Type]->KeyDown)
676 gaWM_WidgetTypes[ele->Type]->KeyDown(ele, msg->KeySym, msg->UCS32);
679 // TODO: Pass to user
684 case WNDMSG_KEYFIRE: {
685 const struct sWndMsg_KeyAction *msg = Data;
686 if(Len < sizeof(*msg)) return -1;
688 if(!info->FocusedElement) return 0;
689 ele = info->FocusedElement;
691 if(gaWM_WidgetTypes[ele->Type]->KeyFire)
692 gaWM_WidgetTypes[ele->Type]->KeyFire(ele, msg->KeySym, msg->UCS32);
695 // TODO: Pass the buck
700 const struct sWndMsg_KeyAction *msg = Data;
701 if(Len < sizeof(*msg)) return -1;
703 if(!info->FocusedElement) return 0;
704 ele = info->FocusedElement;
706 if(gaWM_WidgetTypes[ele->Type]->KeyUp)
707 gaWM_WidgetTypes[ele->Type]->KeyUp(ele, msg->KeySym);
710 // TODO: Pass the buck
715 case MSG_WIDGET_CREATE:
716 Widget_NewWidget(info, Len, Data);
718 case MSG_WIDGET_CREATESUBWIN:
719 Widget_NewWidgetSubwin(info, Len, Data);
723 case MSG_WIDGET_DELETE:
724 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
727 // Set focused widget
728 case MSG_WIDGET_SETFOCUS: {
730 const tWidgetMsg_SetFocus *msg = Data;
731 if(Len < sizeof(*msg)) return -1;
733 ele = Widget_GetElementById(info, msg->WidgetID);
734 Widget_SetFocus(info, ele);
738 case MSG_WIDGET_SETFLAGS:
739 Widget_SetFlags(info, Len, Data);
743 case MSG_WIDGET_SETSIZE:
744 Widget_SetSize(info, Len, Data);
748 case MSG_WIDGET_SETTEXT:
749 Widget_SetText(info, Len, Data);
751 case MSG_WIDGET_GETTEXT:
752 return Widget_GetText(info, Len, Data);
756 return 1; // Unhandled, pass to user
760 void Widget_Fire(tElement *Element)
763 msg.WidgetID = Element->ID;
764 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);