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 int Widget_IPC_Create(tWindow *Win, size_t Len, const void *Data);
30 int Widget_IPC_NewWidgetSubwin(tWindow *Win, size_t Len, const void *Data);
31 // int Widget_IPC_Delete(tWindow *Win, size_t Len, const void *Data);
32 int Widget_IPC_SetFocus(tWindow *Win, size_t Len, const void *Data);
33 int Widget_IPC_SetFlags(tWindow *Win, size_t Len, const void *Data);
34 int Widget_IPC_SetSize(tWindow *Win, size_t Len, const void *Data);
35 int Widget_IPC_SetText(tWindow *Win, size_t Len, const void *Data);
36 int Widget_IPC_GetText(tWindow *Win, size_t Len, const void *Data);
37 // int Widget_IPC_SetColour(tWindow *Win, size_t Len, const void *Data);
38 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data);
41 tWMRenderer gRenderer_Widget = {
43 .CreateWindow = Renderer_Widget_Create,
44 .Redraw = Renderer_Widget_Redraw,
45 .HandleMessage = Renderer_Widget_HandleMessage,
46 .nIPCHandlers = N_IPC_WIDGET,
48 [IPC_WIDGET_CREATE] = Widget_IPC_Create,
49 [IPC_WIDGET_CREATESUBWIN] = Widget_IPC_NewWidgetSubwin,
50 [IPC_WIDGET_SETFOCUS] = Widget_IPC_SetFocus,
51 [IPC_WIDGET_SETFLAGS] = Widget_IPC_SetFlags,
52 [IPC_WIDGET_SETSIZE] = Widget_IPC_SetSize,
53 [IPC_WIDGET_SETTEXT] = Widget_IPC_SetText,
54 [IPC_WIDGET_GETTEXT] = Widget_IPC_GetText,
58 // --- Element callbacks
59 tWidgetDef *gaWM_WidgetTypes[NUM_ELETYPES];
60 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
61 tWidgetDef gWidget_NullWidgetDef;
64 int Renderer_Widget_Init(void)
67 WM_RegisterRenderer(&gRenderer_Widget);
69 for(i = 0; i < ciWM_NumWidgetTypes; i ++)
71 if(gaWM_WidgetTypes[i] != NULL) continue;
73 gaWM_WidgetTypes[i] = &gWidget_NullWidgetDef;
79 void Widget_int_SetTypeDef(int Type, tWidgetDef *Ptr)
81 if( Type < 0 || Type >= ciWM_NumWidgetTypes ) {
82 _SysDebug("ERROR - Widget ID %i out of range (from %p)",
83 Type, __builtin_return_address(0)
88 if( gaWM_WidgetTypes[Type] && gaWM_WidgetTypes[Type] != &gWidget_NullWidgetDef )
90 _SysDebug("ERROR - Widget ID %i redefined by %p",
91 Type, __builtin_return_address(0)
96 gaWM_WidgetTypes[Type] = Ptr;
99 tWindow *Renderer_Widget_Create(int Flags)
103 int eletable_size = DEFAULT_ELETABLE_SIZE;
105 //_SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
107 // TODO: Use `Flags` as default element count?
108 // - Actaully, it's taken by the root ele flags
109 // - Use the upper bits?
111 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
112 info = ret->RendererInfo;
114 info->TableSize = eletable_size;
115 info->FocusedElement = &info->RootElement;
116 info->RootElement.Window = ret;
117 info->RootElement.ID = -1;
118 info->RootElement.BackgroundColour = 0xCCCCCC;
119 info->RootElement.Flags = Flags;
120 info->RootElement.PaddingT = 2;
121 info->RootElement.PaddingB = 2;
122 info->RootElement.PaddingL = 2;
123 info->RootElement.PaddingR = 2;
128 void Renderer_Widget_Redraw(tWindow *Window)
130 tWidgetWin *info = Window->RendererInfo;
131 WM_Render_FillRect(Window, 0, 0, Window->W, Window->H, info->RootElement.BackgroundColour);
133 Widget_UpdateDimensions(&info->RootElement);
134 Widget_UpdatePosition(&info->RootElement);
136 Widget_RenderWidget(Window, &info->RootElement);
139 // --- Render / Resize ---
140 void Widget_RenderWidget(tWindow *Window, tElement *Element)
144 if( Element->Flags & ELEFLAG_NORENDER ) return ;
145 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
147 #if BORDER_EVERYTHING
150 Element->CachedX, Element->CachedY,
151 Element->CachedW, Element->CachedH,
156 if(gaWM_WidgetTypes[Element->Type]->Render)
158 gaWM_WidgetTypes[Element->Type]->Render(Window, Element);
161 for(child = Element->FirstChild; child; child = child->NextSibling)
163 Widget_RenderWidget(Window, child);
167 void Widget_UpdateDimensions(tElement *Element)
174 int fullCross, dynWith = 0;
175 int bVertical = Element->Flags & ELEFLAG_VERTICAL;
177 // Check if this element can have children
178 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
182 // - Get the fixed and minimum sizes of the element
183 for( child = Element->FirstChild; child; child = child->NextSibling )
185 int minWith = bVertical ? child->MinH : child->MinW;
186 int minCross = bVertical ? child->MinW : child->MinH;
188 // Ignore elements that will not be rendered
189 if( child->Flags & ELEFLAG_NORENDER ) continue ;
191 // Absolutely positioned elements don't affect dimensions
192 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
194 // Fixed width elements
195 if( child->FixedWith )
198 fixedSize += child->FixedWith;
200 else if( child->Flags & ELEFLAG_NOSTRETCH )
203 fixedSize += minWith;
206 if( maxCross < child->FixedCross ) maxCross = child->FixedCross;
207 if( maxCross < minCross ) maxCross = minCross;
211 // Get the dynamic with size from the unused space in the element
212 if( nChildren > nFixed ) {
214 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
216 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
217 dynWith -= fixedSize;
218 dynWith -= Element->GapSize * (nChildren-1);
219 if( dynWith < 0 ) return ;
220 dynWith /= nChildren - nFixed;
226 // Get the cross size
228 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
230 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
232 //_SysDebug("%i (p=%i) - WxH=%ix%i",
233 // Element->ID, (Element->Parent ? Element->Parent->ID : -1),
234 // Element->CachedW, Element->CachedH
236 //_SysDebug(" %s dynWith = %i, fullCross = %i",
237 // (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
238 // dynWith, fullCross
241 // Pass 2 - Set sizes and recurse
242 for( child = Element->FirstChild; child; child = child->NextSibling )
246 // Ignore elements that will not be rendered
247 if( child->Flags & ELEFLAG_NORENDER ) continue ;
248 // Don't resize floating elements
249 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
252 if( child->Flags & (bVertical ? ELEFLAG_NOEXPAND : ELEFLAG_NOSTRETCH) )
255 w = child->FixedCross ? child->FixedCross : fullCross;
257 w = child->FixedWith ? child->FixedWith : dynWith;
260 if( child->Flags & (bVertical ? ELEFLAG_NOSTRETCH : ELEFLAG_NOEXPAND) )
263 h = child->FixedWith ? child->FixedWith : dynWith;
265 h = child->FixedCross ? child->FixedCross : fullCross;
267 if(w < child->MinW) w = child->MinW;
268 if(h < child->MinH) h = child->MinH;
270 // _SysDebug("Child %ix%i (min %ix%i)", w, h, child->MinW, child->MinH);
272 // Update the dimensions if they have changed
273 if( child->CachedW == w && child->CachedH == h )
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)
370 for(child = Element->FirstChild; child; child = child->NextSibling)
374 if(Element->Flags & ELEFLAG_NORENDER) continue ;
376 if( (Element->Flags & ELEFLAG_VERTICAL) )
378 cross = child->FixedCross ? child->FixedCross : child->MinW;
379 if(minW < cross) minW = cross;
380 minH += child->FixedWith ? child->FixedWith : child->MinH;
384 cross = child->FixedCross ? child->FixedCross : child->MinH;
385 minW += child->FixedWith ? child->FixedWith : child->MinW;
386 if(minH < cross) minH = cross;
388 // _SysDebug("%i/%i cross = %i", Element->ID, child->ID, cross);
393 if( Element->Flags & ELEFLAG_VERTICAL )
394 minH += (nChildren - 1) * Element->GapSize;
396 minW += (nChildren - 1) * Element->GapSize;
398 Element->MinW = Element->PaddingL + minW + Element->PaddingR;
399 Element->MinH = Element->PaddingT + minH + Element->PaddingB;
402 Widget_UpdateMinDims(Element->Parent);
405 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
407 tElement *ret, *next, *ele;
409 next = &Info->RootElement;
414 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
416 if(ele->Flags & ELEFLAG_NORENDER) continue;
417 if(X < ele->CachedX) continue;
418 if(Y < ele->CachedY) continue;
419 if(X >= ele->CachedX + ele->CachedW) continue;
420 if(Y >= ele->CachedY + ele->CachedH) continue;
428 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
432 if( ID == -1 ) return &Info->RootElement;
434 if( ID < Info->TableSize ) return Info->ElementTable[ID];
436 ele = Info->ElementTable[ID % Info->TableSize];
437 while(ele && ele->ID != ID) ele = ele->ListNext;
441 tElement *Widget_int_Create(tWidgetWin *Info, tElement *Parent, int ID, int Type, int Flags)
443 if( Widget_GetElementById(Info, ID) )
446 // Create new element
447 tElement *new = calloc(sizeof(tElement), 1);
448 new->Window = Parent->Window;
451 new->Parent = Parent;
459 if( gaWM_WidgetTypes[Type]->Init )
460 gaWM_WidgetTypes[Type]->Init(new);
462 // Add to parent's list
463 if(Parent->LastChild)
464 Parent->LastChild->NextSibling = new;
466 Parent->FirstChild = new;
467 Parent->LastChild = new;
471 tElement *ele, *prev = NULL;
472 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
474 prev->ListNext = new;
476 Info->ElementTable[new->ID % Info->TableSize] = new;
482 void Widget_SetFocus(tWidgetWin *Info, tElement *Ele)
486 Info->FocusedElement = Ele;
490 // --- Message Handlers ---
491 int Widget_IPC_Create(tWindow *Win, size_t Len, const void *Data)
493 tWidgetWin *Info = Win->RendererInfo;
494 const tWidgetIPC_Create *Msg = Data;
495 const int max_debugname_len = Len - sizeof(*Msg);
499 if( Len < sizeof(*Msg) )
501 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
504 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
505 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
507 if(Msg->Type >= ciWM_NumWidgetTypes)
509 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
514 parent = Widget_GetElementById(Info, Msg->Parent);
517 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
521 Widget_int_Create(Info, parent, Msg->NewID, Msg->Type, Msg->Flags);
523 Widget_UpdateMinDims(parent);
527 int Widget_IPC_NewWidgetSubwin(tWindow *Win, size_t Len, const void *Data)
529 tWidgetWin *Info = Win->RendererInfo;
530 const tWidgetIPC_CreateSubWin *Msg = Data;
531 const int max_debugname_len = Len - sizeof(*Msg);
532 tElement *parent, *new;
535 if( Len < sizeof(*Msg) )
537 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
540 parent = Widget_GetElementById(Info, Msg->Parent);
541 if(!parent) return 1;
542 if( Widget_GetElementById(Info, Msg->NewID) ) return 1;
544 new = Widget_int_Create(Info, parent, Msg->NewID, Msg->Type, Msg->Flags);
545 new->Data = WM_GetWindowByID(parent->Window, Msg->WindowHandle);
546 Widget_UpdateMinDims(parent);
550 // TODO: Widget_IPC_Delete
552 int Widget_IPC_SetFocus(tWindow *Win, size_t Len, const void *Data)
554 tWidgetWin *info = Win->RendererInfo;
556 const tWidgetIPC_SetFocus *msg = Data;
557 if(Len < sizeof(*msg)) return -1;
559 ele = Widget_GetElementById(info, msg->WidgetID);
560 Widget_SetFocus(info, ele);
564 int Widget_IPC_SetFlags(tWindow *Win, size_t Len, const void *Data)
566 tWidgetWin *Info = Win->RendererInfo;
567 const tWidgetIPC_SetFlags *Msg = Data;
570 if( Len < sizeof(*Msg) )
573 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
575 ele = Widget_GetElementById(Info, Msg->WidgetID);
578 ele->Flags &= ~Msg->Mask;
579 ele->Flags |= Msg->Value & Msg->Mask;
584 int Widget_IPC_SetSize(tWindow *Win, size_t Len, const void *Data)
586 tWidgetWin *Info = Win->RendererInfo;
587 const tWidgetIPC_SetSize *Msg = Data;
590 if( Len < sizeof(*Msg) )
593 ele = Widget_GetElementById(Info, Msg->WidgetID);
596 ele->FixedWith = Msg->Value;
600 int Widget_IPC_SetText(tWindow *Win, size_t Len, const void *Data)
602 tWidgetWin *Info = Win->RendererInfo;
603 const tWidgetIPC_SetText *Msg = Data;
606 if( Len < sizeof(*Msg) + 1 )
608 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
611 ele = Widget_GetElementById(Info, Msg->WidgetID);
614 if( gaWM_WidgetTypes[ele->Type]->UpdateText )
616 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
620 // if(ele->Text) free(ele->Text);
621 // ele->Text = strdup(Msg->Text);
626 int Widget_IPC_GetText(tWindow *Win, size_t Len, const void *Data)
628 tWidgetWin *Info = Win->RendererInfo;
629 const tWidgetIPC_SetText *Msg = Data;
630 if( Len < sizeof(*Msg) )
633 const char *text = NULL;
634 tElement *ele = Widget_GetElementById(Info, Msg->WidgetID);
638 char buf[sizeof(tWidgetIPC_SetText) + strlen(text?text:"") + 1];
639 tWidgetIPC_SetText *omsg = (void*)buf;
642 omsg->WidgetID = Msg->WidgetID;
643 strcpy(omsg->Text, text);
650 WM_SendIPCReply(Win, IPC_WIDGET_GETTEXT, sizeof(buf), buf);
654 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
656 tWidgetWin *info = Target->RendererInfo;
660 case WNDMSG_RESIZE: {
661 const struct sWndMsg_Resize *msg = Data;
662 if(Len < sizeof(*msg)) return -1;
664 info->RootElement.CachedW = msg->W;
665 info->RootElement.CachedH = msg->H;
667 // TODO: Update dimensions of all child elements?
671 case WNDMSG_MOUSEMOVE: {
672 // _SysDebug("TODO: Support widget mouse move events");
675 case WNDMSG_MOUSEBTN: {
676 const struct sWndMsg_MouseButton *msg = Data;
677 tWidgetMsg_MouseBtn client_msg;
681 if(Len < sizeof(*msg)) return -1;
683 x = msg->X; y = msg->Y;
684 client_msg.Button = msg->Button;
685 client_msg.bPressed = msg->bPressed;
687 ele = Widget_GetElementByPos(info, x, y);
688 Widget_SetFocus(info, ele);
689 // Send event to all elements from `ele` upwards
690 for( ; ele; ele = ele->Parent )
692 if(gaWM_WidgetTypes[ele->Type]->MouseButton)
694 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
696 x - ele->CachedX, y - ele->CachedY,
697 msg->Button, msg->bPressed
699 // Allow a type to trap the input from going any higher
705 client_msg.X = x - ele->CachedX;
706 client_msg.Y = y - ele->CachedY;
707 client_msg.WidgetID = ele->ID;
708 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
713 case WNDMSG_KEYDOWN: {
714 const struct sWndMsg_KeyAction *msg = Data;
715 if(Len < sizeof(*msg)) return -1;
717 if(!info->FocusedElement) return 0;
718 ele = info->FocusedElement;
720 if(gaWM_WidgetTypes[ele->Type]->KeyDown)
721 gaWM_WidgetTypes[ele->Type]->KeyDown(ele, msg->KeySym, msg->UCS32);
724 // TODO: Pass to user
729 case WNDMSG_KEYFIRE: {
730 const struct sWndMsg_KeyAction *msg = Data;
731 if(Len < sizeof(*msg)) return -1;
733 if(!info->FocusedElement) return 0;
734 ele = info->FocusedElement;
736 if(gaWM_WidgetTypes[ele->Type]->KeyFire)
737 gaWM_WidgetTypes[ele->Type]->KeyFire(ele, msg->KeySym, msg->UCS32);
740 // TODO: Pass the buck
745 const struct sWndMsg_KeyAction *msg = Data;
746 if(Len < sizeof(*msg)) return -1;
748 if(!info->FocusedElement) return 0;
749 ele = info->FocusedElement;
751 if(gaWM_WidgetTypes[ele->Type]->KeyUp)
752 gaWM_WidgetTypes[ele->Type]->KeyUp(ele, msg->KeySym);
755 // TODO: Pass the buck
761 return 1; // Unhandled, pass to user
765 void Widget_Fire(tElement *Element)
768 msg.WidgetID = Element->ID;
769 _SysDebug("Widget_Fire: Fire on %p %i", Element->Window, Element->ID);
770 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);