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
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, const tWidgetMsg_Create *Msg);
29 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg);
30 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg);
31 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg);
32 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data);
35 tWMRenderer gRenderer_Widget = {
37 .CreateWindow = Renderer_Widget_Create,
38 .Redraw = Renderer_Widget_Redraw,
39 .HandleMessage = Renderer_Widget_HandleMessage
42 // --- Element callbacks
43 tWidgetDef *gaWM_WidgetTypes[NUM_ELETYPES];
44 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
47 int Renderer_Widget_Init(void)
49 WM_RegisterRenderer(&gRenderer_Widget);
54 void Widget_int_SetTypeDef(int Type, tWidgetDef *Ptr)
56 if( Type < 0 || Type >= ciWM_NumWidgetTypes ) {
57 _SysDebug("ERROR - Widget ID %i out of range (from %p)",
58 Type, __builtin_return_address(0)
63 if( gaWM_WidgetTypes[Type] ) {
64 _SysDebug("ERROR - Widget ID %i redefined by %p",
65 Type, __builtin_return_address(0)
70 gaWM_WidgetTypes[Type] = Ptr;
71 _SysDebug("Registered type %i to %p", Type, Ptr);
74 tWindow *Renderer_Widget_Create(int Flags)
78 int eletable_size = DEFAULT_ELETABLE_SIZE;
80 _SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
82 // TODO: Use `Flags` as default element count?
83 // - Actaully, it's taken by the root ele flags
84 // - Use the upper bits?
86 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
87 info = ret->RendererInfo;
89 info->TableSize = eletable_size;
90 info->RootElement.Window = ret;
91 info->RootElement.ID = -1;
92 info->RootElement.BackgroundColour = 0xCCCCCC;
93 info->RootElement.Flags = Flags;
94 info->RootElement.PaddingT = 2;
95 info->RootElement.PaddingB = 2;
96 info->RootElement.PaddingL = 2;
97 info->RootElement.PaddingR = 2;
102 void Renderer_Widget_Redraw(tWindow *Window)
104 tWidgetWin *info = Window->RendererInfo;
105 WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
107 Widget_UpdateDimensions(&info->RootElement);
108 Widget_UpdatePosition(&info->RootElement);
110 Widget_RenderWidget(Window, &info->RootElement);
113 // --- Render / Resize ---
114 void Widget_RenderWidget(tWindow *Window, tElement *Element)
118 if( Element->Flags & ELEFLAG_NORENDER ) return ;
119 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
121 if( Element->Type < ciWM_NumWidgetTypes
122 && gaWM_WidgetTypes[Element->Type]
123 && gaWM_WidgetTypes[Element->Type]->Render
126 gaWM_WidgetTypes[Element->Type]->Render(Window, Element);
130 Widget_Decorator_RenderWidget(Window, Element);
133 for(child = Element->FirstChild; child; child = child->NextSibling)
135 Widget_RenderWidget(Window, child);
139 void Widget_UpdateDimensions(tElement *Element)
146 int fullCross, dynWith;
149 // - Get the fixed and minimum sizes of the element
150 for( child = Element->FirstChild; child; child = child->NextSibling )
152 // Ignore elements that will not be rendered
153 if( child->Flags & ELEFLAG_NORENDER ) continue ;
155 // Absolutely positioned elements don't affect dimensions
156 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
158 // Fixed width elements
159 if( child->FixedWith )
162 fixedSize += child->FixedWith;
164 else if( child->Flags & ELEFLAG_NOSTRETCH )
167 fixedSize += child->MinWith;
170 if( child->FixedCross && maxCross < child->FixedCross )
171 maxCross = child->FixedCross;
172 if( child->MinCross && maxCross < child->MinCross )
173 maxCross = child->MinCross;
177 // Get the dynamic with size from the unused space in the element
178 if( nChildren > nFixed ) {
179 if( Element->Flags & ELEFLAG_VERTICAL )
180 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
182 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
183 dynWith -= fixedSize;
184 if( dynWith < 0 ) return ;
185 dynWith /= nChildren - nFixed;
188 // _SysDebug("%i - nChildren = %i, nFixed = %i, dynWith = %i, fixedSize = %i",
189 // Element->ID, nChildren, nFixed, dynWith, fixedSize);
191 // Get the cross size
192 if( Element->Flags & ELEFLAG_VERTICAL )
193 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
195 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
197 // Pass 2 - Set sizes and recurse
198 for( child = Element->FirstChild; child; child = child->NextSibling )
202 // Ignore elements that will not be rendered
203 if( child->Flags & ELEFLAG_NORENDER ) continue ;
205 // --- Cross Size ---
206 // TODO: Expand to fill?
207 // TODO: Extra flag so options are (Expand, Equal, Wrap)
208 if( child->FixedCross )
209 cross = child->FixedCross;
210 else if( child->Flags & ELEFLAG_NOEXPAND )
211 cross = child->MinCross;
216 if( child->FixedWith )
217 with = child->FixedWith;
218 else if( child->Flags & ELEFLAG_NOSTRETCH )
219 with = child->MinWith;
224 if(with < child->MinWith) with = child->MinWith;
225 if(cross < child->MinCross) cross = child->MinCross;
227 // Update the dimensions if they have changed
228 if( Element->Flags & ELEFLAG_VERTICAL ) {
229 // If no change, don't recurse
230 if( child->CachedW == cross && child->CachedH == with )
232 child->CachedW = cross;
233 child->CachedH = with;
236 // If no change, don't recurse
237 if( child->CachedW == with && child->CachedH == cross )
239 child->CachedW = with;
240 child->CachedH = cross;
243 // Force the positions of child elements to be recalculated
246 // Recurse down so the child elements can be updated
247 Widget_UpdateDimensions(child);
253 * \brief Update the position of child elements
255 void Widget_UpdatePosition(tElement *Element)
260 if( Element->Flags & ELEFLAG_NORENDER ) return ;
262 // _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
263 // Element, Element->ID, Element->Type, Element->Flags);
266 x = Element->CachedX + Element->PaddingL;
267 y = Element->CachedY + Element->PaddingT;
270 for(child = Element->FirstChild; child; child = child->NextSibling)
273 // Ignore elements that will not be rendered
274 if( child->Flags & ELEFLAG_NORENDER ) continue ;
279 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
280 if(Element->Flags & ELEFLAG_VERTICAL)
281 newX += Element->CachedW/2 - child->CachedW/2;
283 newY += Element->CachedH/2 - child->CachedH/2;
285 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
286 if(Element->Flags & ELEFLAG_VERTICAL )
287 newX += Element->CachedW - child->CachedW
288 - Element->PaddingL - Element->PaddingR;
290 newY += Element->CachedH - child->CachedH
291 - Element->PaddingT - Element->PaddingB;
294 // _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
296 // Check for changes, and don't update if there was no change
297 if( newX != child->CachedX || newY != child->CachedY )
299 child->CachedX = newX;
300 child->CachedY = newY;
301 // Update child's children positions
302 Widget_UpdatePosition(child);
306 if(Element->Flags & ELEFLAG_VERTICAL ) {
307 y += child->CachedH + Element->GapSize;
310 x += child->CachedW + Element->GapSize;
316 * \brief Update the minimum dimensions of the element
317 * \note Called after a child's minimum dimensions have changed
319 void Widget_UpdateMinDims(tElement *Element)
329 for(child = Element->FirstChild; child; child = child->NextSibling)
331 if( (Element->Flags & ELEFLAG_VERTICAL) )
333 if(child->FixedCross)
334 minW += child->FixedCross;
336 minW += child->MinCross;
338 minH += child->FixedWith;
340 minH += child->MinWith;
344 if(child->FixedCross)
345 minH += child->FixedCross;
347 minH += child->MinCross;
349 minW += child->FixedWith;
351 minW += child->MinWith;
355 if( Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL) )
357 Element->MinCross = Element->PaddingL + minW + Element->PaddingR;
358 Element->MinWith = Element->PaddingT + minH + Element->PaddingB;
362 Element->MinWith = Element->PaddingL + minW + Element->PaddingR;
363 Element->MinCross = Element->PaddingL + minH + Element->PaddingR;
367 Widget_UpdateMinDims(Element->Parent);
370 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
372 tElement *ret, *next, *ele;
374 next = &Info->RootElement;
379 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
381 if(ele->Flags & ELEFLAG_NORENDER) continue;
382 if(X < ele->CachedX) continue;
383 if(Y < ele->CachedY) continue;
384 if(X >= ele->CachedX + ele->CachedW) continue;
385 if(Y >= ele->CachedY + ele->CachedH) continue;
393 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
397 if( ID == -1 ) return &Info->RootElement;
399 if( ID < Info->TableSize ) return Info->ElementTable[ID];
401 ele = Info->ElementTable[ID % Info->TableSize];
402 while(ele && ele->ID != ID) ele = ele->ListNext;
406 // --- Message Handlers ---
407 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
409 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
410 tElement *parent, *new;
413 if( Len < sizeof(*Msg) )
415 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
418 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
419 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
422 parent = Widget_GetElementById(Info, Msg->Parent);
425 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
429 // Check if the ID is already in use
430 if( Widget_GetElementById(Info, Msg->NewID) )
433 // Create new element
434 new = calloc(sizeof(tElement), 1);
435 new->Window = parent->Window;
436 new->ID = Msg->NewID;
437 new->Type = Msg->Type;
438 new->Parent = parent;
439 new->Flags = Msg->Flags;
446 if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type] && gaWM_WidgetTypes[new->Type]->Init )
447 gaWM_WidgetTypes[new->Type]->Init(new);
449 // Add to parent's list
450 if(parent->LastChild)
451 parent->LastChild->NextSibling = new;
453 parent->FirstChild = new;
454 parent->LastChild = new;
458 tElement *ele, *prev = NULL;
459 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
461 prev->ListNext = new;
463 Info->ElementTable[new->ID % Info->TableSize] = new;
466 Widget_UpdateMinDims(parent);
469 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
473 if( Len < sizeof(*Msg) )
476 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
478 ele = Widget_GetElementById(Info, Msg->WidgetID);
481 ele->Flags &= ~Msg->Mask;
482 ele->Flags |= Msg->Value & Msg->Mask;
485 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
489 if( Len < sizeof(*Msg) )
492 ele = Widget_GetElementById(Info, Msg->WidgetID);
495 ele->FixedWith = Msg->Value;
498 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
502 if( Len < sizeof(*Msg) + 1 )
504 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
507 ele = Widget_GetElementById(Info, Msg->WidgetID);
511 if( ele->Type < ciWM_NumWidgetTypes
512 && gaWM_WidgetTypes[ele->Type]
513 && gaWM_WidgetTypes[ele->Type]->UpdateText )
515 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
519 // if(ele->Text) free(ele->Text);
520 // ele->Text = strdup(Msg->Text);
524 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
526 tWidgetWin *info = Target->RendererInfo;
529 case WNDMSG_RESIZE: {
530 const struct sWndMsg_Resize *msg = Data;
531 if(Len < sizeof(*msg)) return -1;
533 info->RootElement.CachedW = msg->W;
534 info->RootElement.CachedH = msg->H;
536 // TODO: Update dimensions of all child elements?
540 case WNDMSG_MOUSEMOVE: {
541 // _SysDebug("TODO: Support widget mouse move events");
544 case WNDMSG_MOUSEBTN: {
545 const struct sWndMsg_MouseButton *msg = Data;
546 tWidgetMsg_MouseBtn client_msg;
551 if(Len < sizeof(*msg)) return -1;
553 x = msg->X; y = msg->Y;
554 client_msg.Button = msg->Button;
555 client_msg.bPressed = msg->bPressed;
557 ele = Widget_GetElementByPos(info, x, y);
558 // Send event to all elements from `ele` upwards
559 for( ; ele; ele = ele->Parent )
561 if(ele->Type < ciWM_NumWidgetTypes
562 && gaWM_WidgetTypes[ele->Type]
563 && gaWM_WidgetTypes[ele->Type]->MouseButton)
565 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
567 x - ele->CachedX, y - ele->CachedY,
568 msg->Button, msg->bPressed
570 // Allow a type to trap the input from going any higher
576 client_msg.X = x - ele->CachedX;
577 client_msg.Y = y - ele->CachedY;
578 client_msg.WidgetID = ele->ID;
579 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
585 case MSG_WIDGET_CREATE:
586 Widget_NewWidget(info, Len, Data);
589 case MSG_WIDGET_DELETE:
590 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
594 case MSG_WIDGET_SETFLAGS:
595 Widget_SetFlags(info, Len, Data);
599 case MSG_WIDGET_SETSIZE:
600 Widget_SetSize(info, Len, Data);
604 case MSG_WIDGET_SETTEXT:
605 Widget_SetText(info, Len, Data);
610 return 1; // Unhandled, pass to user
614 void Widget_Fire(tElement *Element)
617 msg.WidgetID = Element->ID;
618 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);