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 extern tWidgetDef _widget_typedef_ELETYPE_IMAGE;
21 extern tWidgetDef _widget_typedef_ELETYPE_BUTTON;
22 extern tWidgetDef _widget_typedef_ELETYPE_TEXT;
23 extern tWidgetDef _widget_typedef_ELETYPE_TEXTINPUT;
24 extern tWidgetDef _widget_typedef_ELETYPE_SPACER;
25 extern tWidgetDef _widget_typedef_ELETYPE_SUBWIN;
28 int Renderer_Widget_Init(void);
29 tWindow *Renderer_Widget_Create(int Flags);
30 void Renderer_Widget_Redraw(tWindow *Window);
32 void Widget_RenderWidget(tWindow *Window, tElement *Element);
33 void Widget_UpdateDimensions(tElement *Element);
34 void Widget_UpdatePosition(tElement *Element);
36 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID);
37 int Widget_IPC_Create(tWindow *Win, size_t Len, const void *Data);
38 int Widget_IPC_NewWidgetSubwin(tWindow *Win, size_t Len, const void *Data);
39 // int Widget_IPC_Delete(tWindow *Win, size_t Len, const void *Data);
40 int Widget_IPC_SetFocus(tWindow *Win, size_t Len, const void *Data);
41 int Widget_IPC_SetFlags(tWindow *Win, size_t Len, const void *Data);
42 int Widget_IPC_SetSize(tWindow *Win, size_t Len, const void *Data);
43 int Widget_IPC_SetText(tWindow *Win, size_t Len, const void *Data);
44 int Widget_IPC_GetText(tWindow *Win, size_t Len, const void *Data);
45 // int Widget_IPC_SetColour(tWindow *Win, size_t Len, const void *Data);
46 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data);
49 tWMRenderer gRenderer_Widget = {
51 .CreateWindow = Renderer_Widget_Create,
52 .Redraw = Renderer_Widget_Redraw,
53 .HandleMessage = Renderer_Widget_HandleMessage,
54 .nIPCHandlers = N_IPC_WIDGET,
56 [IPC_WIDGET_CREATE] = Widget_IPC_Create,
57 [IPC_WIDGET_CREATESUBWIN] = Widget_IPC_NewWidgetSubwin,
58 [IPC_WIDGET_SETFOCUS] = Widget_IPC_SetFocus,
59 [IPC_WIDGET_SETFLAGS] = Widget_IPC_SetFlags,
60 [IPC_WIDGET_SETSIZE] = Widget_IPC_SetSize,
61 [IPC_WIDGET_SETTEXT] = Widget_IPC_SetText,
62 [IPC_WIDGET_GETTEXT] = Widget_IPC_GetText,
66 // --- Element callbacks
67 tWidgetDef *gaWM_WidgetTypes[NUM_ELETYPES] = {
68 [ELETYPE_IMAGE] = &_widget_typedef_ELETYPE_IMAGE,
69 [ELETYPE_BUTTON] = &_widget_typedef_ELETYPE_BUTTON,
70 [ELETYPE_TEXT] = &_widget_typedef_ELETYPE_TEXT,
71 [ELETYPE_TEXTINPUT] = &_widget_typedef_ELETYPE_TEXTINPUT,
72 [ELETYPE_SPACER] = &_widget_typedef_ELETYPE_SPACER,
73 [ELETYPE_SUBWIN] = &_widget_typedef_ELETYPE_SUBWIN,
75 const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
76 tWidgetDef gWidget_NullWidgetDef;
79 int Renderer_Widget_Init(void)
81 WM_RegisterRenderer(&gRenderer_Widget);
83 for(int i = 0; i < ciWM_NumWidgetTypes; i ++)
85 if(gaWM_WidgetTypes[i] != NULL) continue;
87 gaWM_WidgetTypes[i] = &gWidget_NullWidgetDef;
93 void Widget_int_SetTypeDef(int Type, tWidgetDef *Ptr)
95 if( Type < 0 || Type >= ciWM_NumWidgetTypes ) {
96 _SysDebug("ERROR - Widget ID %i out of range (from %p)",
97 Type, __builtin_return_address(0)
102 if( gaWM_WidgetTypes[Type] && gaWM_WidgetTypes[Type] != &gWidget_NullWidgetDef )
104 _SysDebug("ERROR - Widget ID %i redefined by %p",
105 Type, __builtin_return_address(0)
110 gaWM_WidgetTypes[Type] = Ptr;
111 _SysDebug("Registered widget type %i '%s'", Type, Ptr->Name);
114 tWindow *Renderer_Widget_Create(int Flags)
118 int eletable_size = DEFAULT_ELETABLE_SIZE;
120 //_SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
122 // TODO: Use `Flags` as default element count?
123 // - Actaully, it's taken by the root ele flags
124 // - Use the upper bits?
126 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
127 info = ret->RendererInfo;
129 info->TableSize = eletable_size;
130 info->FocusedElement = &info->RootElement;
131 info->RootElement.Window = ret;
132 info->RootElement.ID = -1;
133 info->RootElement.BackgroundColour = 0xCCCCCC;
134 info->RootElement.Flags = Flags;
135 info->RootElement.PaddingT = 2;
136 info->RootElement.PaddingB = 2;
137 info->RootElement.PaddingL = 2;
138 info->RootElement.PaddingR = 2;
143 void Renderer_Widget_Redraw(tWindow *Window)
145 tWidgetWin *info = Window->RendererInfo;
146 WM_Render_FillRect(Window, 0, 0, Window->W, Window->H, info->RootElement.BackgroundColour);
148 Widget_UpdateDimensions(&info->RootElement);
149 Widget_UpdatePosition(&info->RootElement);
151 Widget_RenderWidget(Window, &info->RootElement);
154 // --- Render / Resize ---
155 void Widget_RenderWidget(tWindow *Window, tElement *Element)
159 if( Element->Flags & ELEFLAG_NORENDER ) return ;
160 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
162 #if BORDER_EVERYTHING
165 Element->CachedX, Element->CachedY,
166 Element->CachedW, Element->CachedH,
171 if(gaWM_WidgetTypes[Element->Type]->Render)
173 gaWM_WidgetTypes[Element->Type]->Render(Window, Element);
176 for(child = Element->FirstChild; child; child = child->NextSibling)
178 Widget_RenderWidget(Window, child);
182 void Widget_UpdateDimensions(tElement *Element)
189 int fullCross, dynWith = 0;
190 int bVertical = Element->Flags & ELEFLAG_VERTICAL;
192 // Check if this element can have children
193 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
197 // - Get the fixed and minimum sizes of the element
198 for( child = Element->FirstChild; child; child = child->NextSibling )
200 int minWith = bVertical ? child->MinH : child->MinW;
201 int minCross = bVertical ? child->MinW : child->MinH;
203 // Ignore elements that will not be rendered
204 if( child->Flags & ELEFLAG_NORENDER ) continue ;
206 // Absolutely positioned elements don't affect dimensions
207 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
209 // Fixed width elements
210 if( child->FixedWith )
213 fixedSize += child->FixedWith;
215 else if( child->Flags & ELEFLAG_NOSTRETCH )
218 fixedSize += minWith;
221 if( maxCross < child->FixedCross ) maxCross = child->FixedCross;
222 if( maxCross < minCross ) maxCross = minCross;
226 // Get the dynamic with size from the unused space in the element
227 if( nChildren > nFixed ) {
229 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
231 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
232 dynWith -= fixedSize;
233 dynWith -= Element->GapSize * (nChildren-1);
234 if( dynWith < 0 ) return ;
235 dynWith /= nChildren - nFixed;
241 // Get the cross size
243 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
245 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
247 //_SysDebug("%i (p=%i) - WxH=%ix%i",
248 // Element->ID, (Element->Parent ? Element->Parent->ID : -1),
249 // Element->CachedW, Element->CachedH
251 //_SysDebug(" %s dynWith = %i, fullCross = %i",
252 // (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
253 // dynWith, fullCross
256 // Pass 2 - Set sizes and recurse
257 for( child = Element->FirstChild; child; child = child->NextSibling )
261 // Ignore elements that will not be rendered
262 if( child->Flags & ELEFLAG_NORENDER ) continue ;
263 // Don't resize floating elements
264 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
267 if( child->Flags & (bVertical ? ELEFLAG_NOEXPAND : ELEFLAG_NOSTRETCH) )
270 w = child->FixedCross ? child->FixedCross : fullCross;
272 w = child->FixedWith ? child->FixedWith : dynWith;
275 if( child->Flags & (bVertical ? ELEFLAG_NOSTRETCH : ELEFLAG_NOEXPAND) )
278 h = child->FixedWith ? child->FixedWith : dynWith;
280 h = child->FixedCross ? child->FixedCross : fullCross;
282 if(w < child->MinW) w = child->MinW;
283 if(h < child->MinH) h = child->MinH;
285 // _SysDebug("Child %ix%i (min %ix%i)", w, h, child->MinW, child->MinH);
287 // Update the dimensions if they have changed
288 if( child->CachedW == w && child->CachedH == h )
293 // Force the positions of child elements to be recalculated
296 // Recurse down so the child elements can be updated
297 Widget_UpdateDimensions(child);
303 * \brief Update the position of child elements
305 void Widget_UpdatePosition(tElement *Element)
309 if( Element->Flags & ELEFLAG_NORENDER ) return ;
311 // Check if this element can have children
312 if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
315 // _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
316 // Element, Element->ID, Element->Type, Element->Flags);
319 x = Element->CachedX + Element->PaddingL;
320 y = Element->CachedY + Element->PaddingT;
323 for(tElement *child = Element->FirstChild; child; child = child->NextSibling)
326 // Ignore elements that will not be rendered
327 if( child->Flags & ELEFLAG_NORENDER ) continue ;
331 // Handle alignment (across parent)
332 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
333 if(Element->Flags & ELEFLAG_VERTICAL)
334 newX += Element->CachedW/2 - child->CachedW/2;
336 newY += Element->CachedH/2 - child->CachedH/2;
338 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
339 if(Element->Flags & ELEFLAG_VERTICAL )
340 newX += Element->CachedW - child->CachedW
341 - Element->PaddingL - Element->PaddingR;
343 newY += Element->CachedH - child->CachedH
344 - Element->PaddingT - Element->PaddingB;
347 // _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
349 // Check for changes, and don't update if there was no change
350 if( newX != child->CachedX || newY != child->CachedY )
352 child->CachedX = newX;
353 child->CachedY = newY;
354 // Update child's children positions
355 Widget_UpdatePosition(child);
359 if(Element->Flags & ELEFLAG_VERTICAL ) {
360 y += child->CachedH + Element->GapSize;
363 x += child->CachedW + Element->GapSize;
369 * \brief Update the minimum dimensions of the element
370 * \note Called after a child's minimum dimensions have changed
372 void Widget_UpdateMinDims(tElement *Element)
383 for( tElement *child = Element->FirstChild; child; child = child->NextSibling )
387 if(Element->Flags & ELEFLAG_NORENDER) continue ;
389 if( (Element->Flags & ELEFLAG_VERTICAL) )
391 cross = child->FixedCross ? child->FixedCross : child->MinW;
392 if(minW < cross) minW = cross;
393 minH += child->FixedWith ? child->FixedWith : child->MinH;
397 cross = child->FixedCross ? child->FixedCross : child->MinH;
398 minW += child->FixedWith ? child->FixedWith : child->MinW;
399 if(minH < cross) minH = cross;
401 // _SysDebug("%i/%i cross = %i", Element->ID, child->ID, cross);
406 if( Element->Flags & ELEFLAG_VERTICAL )
407 minH += (nChildren - 1) * Element->GapSize;
409 minW += (nChildren - 1) * Element->GapSize;
411 Element->MinW = Element->PaddingL + minW + Element->PaddingR;
412 Element->MinH = Element->PaddingT + minH + Element->PaddingB;
415 Widget_UpdateMinDims(Element->Parent);
418 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
421 tElement *next = &Info->RootElement;
426 for(tElement *ele = ret->FirstChild; ele; ele = ele->NextSibling)
428 if(ele->Flags & ELEFLAG_NORENDER) continue;
429 if(X < ele->CachedX) continue;
430 if(Y < ele->CachedY) continue;
431 if(X >= ele->CachedX + ele->CachedW) continue;
432 if(Y >= ele->CachedY + ele->CachedH) continue;
440 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
444 if( ID == -1 ) return &Info->RootElement;
446 if( ID < Info->TableSize ) return Info->ElementTable[ID];
448 ele = Info->ElementTable[ID % Info->TableSize];
449 while(ele && ele->ID != ID) ele = ele->ListNext;
453 tElement *Widget_int_Create(tWidgetWin *Info, tElement *Parent, int ID, int Type, int Flags)
455 if( Widget_GetElementById(Info, ID) )
457 if( Type >= NUM_ELETYPES ) {
461 _SysDebug("Widget Create #%i '%s' 0x%x",
462 ID, gaWM_WidgetTypes[Type]->Name, Flags);
464 // Create new element
465 tElement *new = calloc(sizeof(tElement), 1);
466 new->Window = Parent->Window;
469 new->Parent = Parent;
477 if( gaWM_WidgetTypes[Type]->Init )
478 gaWM_WidgetTypes[Type]->Init(new);
480 // Add to parent's list
481 if(Parent->LastChild)
482 Parent->LastChild->NextSibling = new;
484 Parent->FirstChild = new;
485 Parent->LastChild = new;
489 tElement *ele, *prev = NULL;
490 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
492 prev->ListNext = new;
494 Info->ElementTable[new->ID % Info->TableSize] = new;
500 void Widget_SetFocus(tWidgetWin *Info, tElement *Ele)
504 Info->FocusedElement = Ele;
508 // --- Message Handlers ---
509 int Widget_IPC_Create(tWindow *Win, size_t Len, const void *Data)
511 tWidgetWin *Info = Win->RendererInfo;
512 const tWidgetIPC_Create *Msg = Data;
513 const int max_debugname_len = Len - sizeof(*Msg);
517 if( Len < sizeof(*Msg) )
519 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
522 _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
523 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
525 if(Msg->Type >= ciWM_NumWidgetTypes)
527 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
532 parent = Widget_GetElementById(Info, Msg->Parent);
535 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
539 Widget_int_Create(Info, parent, Msg->NewID, Msg->Type, Msg->Flags);
541 Widget_UpdateMinDims(parent);
545 int Widget_IPC_NewWidgetSubwin(tWindow *Win, size_t Len, const void *Data)
547 tWidgetWin *Info = Win->RendererInfo;
548 const tWidgetIPC_CreateSubWin *Msg = Data;
549 const int max_debugname_len = Len - sizeof(*Msg);
550 tElement *parent, *new;
553 if( Len < sizeof(*Msg) )
555 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
558 _SysDebug("Widget_NewWidgetSubwin(%i %i Type %i Flags 0x%x Subwin %i)",
559 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags, Msg->WindowHandle);
561 parent = Widget_GetElementById(Info, Msg->Parent);
562 if(!parent) return 1;
563 if( Widget_GetElementById(Info, Msg->NewID) ) return 1;
565 new = Widget_int_Create(Info, parent, Msg->NewID, Msg->Type, Msg->Flags);
566 new->Data = WM_GetWindowByID(parent->Window, Msg->WindowHandle);
567 Widget_UpdateMinDims(parent);
571 // TODO: Widget_IPC_Delete
573 int Widget_IPC_SetFocus(tWindow *Win, size_t Len, const void *Data)
575 tWidgetWin *info = Win->RendererInfo;
577 const tWidgetIPC_SetFocus *msg = Data;
578 if(Len < sizeof(*msg)) return -1;
580 _SysDebug("Widget_SetFocus(%i)", msg->WidgetID);
582 ele = Widget_GetElementById(info, msg->WidgetID);
583 Widget_SetFocus(info, ele);
587 int Widget_IPC_SetFlags(tWindow *Win, size_t Len, const void *Data)
589 tWidgetWin *Info = Win->RendererInfo;
590 const tWidgetIPC_SetFlags *Msg = Data;
593 if( Len < sizeof(*Msg) )
596 _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
598 ele = Widget_GetElementById(Info, Msg->WidgetID);
601 ele->Flags &= ~Msg->Mask;
602 ele->Flags |= Msg->Value & Msg->Mask;
607 int Widget_IPC_SetSize(tWindow *Win, size_t Len, const void *Data)
609 tWidgetWin *Info = Win->RendererInfo;
610 const tWidgetIPC_SetSize *Msg = Data;
613 if( Len < sizeof(*Msg) )
616 _SysDebug("Widget_SetSize(%i, %i)", Msg->WidgetID, Msg->Value);
618 ele = Widget_GetElementById(Info, Msg->WidgetID);
621 ele->FixedWith = Msg->Value;
625 int Widget_IPC_SetText(tWindow *Win, size_t Len, const void *Data)
627 tWidgetWin *Info = Win->RendererInfo;
628 const tWidgetIPC_SetText *Msg = Data;
631 if( Len < sizeof(*Msg) + 1 )
633 if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
636 _SysDebug("Widget_SetText(%i, '%.30s')", Msg->WidgetID, Msg->Text);
637 ele = Widget_GetElementById(Info, Msg->WidgetID);
640 if( gaWM_WidgetTypes[ele->Type]->UpdateText )
642 _SysDebug(" - calling handler");
643 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
647 // if(ele->Text) free(ele->Text);
648 // ele->Text = strdup(Msg->Text);
653 int Widget_IPC_GetText(tWindow *Win, size_t Len, const void *Data)
655 tWidgetWin *Info = Win->RendererInfo;
656 const tWidgetIPC_SetText *Msg = Data;
657 if( Len < sizeof(*Msg) )
660 const char *text = NULL;
661 tElement *ele = Widget_GetElementById(Info, Msg->WidgetID);
665 char buf[sizeof(tWidgetIPC_SetText) + strlen(text?text:"") + 1];
666 tWidgetIPC_SetText *omsg = (void*)buf;
669 omsg->WidgetID = Msg->WidgetID;
670 strcpy(omsg->Text, text);
677 WM_SendIPCReply(Win, IPC_WIDGET_GETTEXT, sizeof(buf), buf);
681 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
683 tWidgetWin *info = Target->RendererInfo;
687 case WNDMSG_RESIZE: {
688 const struct sWndMsg_Resize *msg = Data;
689 if(Len < sizeof(*msg)) return -1;
691 info->RootElement.CachedW = msg->W;
692 info->RootElement.CachedH = msg->H;
694 // TODO: Update dimensions of all child elements?
698 case WNDMSG_MOUSEMOVE: {
699 // _SysDebug("TODO: Support widget mouse move events");
702 case WNDMSG_MOUSEBTN: {
703 const struct sWndMsg_MouseButton *msg = Data;
704 tWidgetMsg_MouseBtn client_msg;
708 if(Len < sizeof(*msg)) return -1;
710 x = msg->X; y = msg->Y;
711 client_msg.Button = msg->Button;
712 client_msg.bPressed = msg->bPressed;
714 ele = Widget_GetElementByPos(info, x, y);
715 Widget_SetFocus(info, ele);
716 // Send event to all elements from `ele` upwards
717 for( ; ele; ele = ele->Parent )
719 if(gaWM_WidgetTypes[ele->Type]->MouseButton)
721 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
723 x - ele->CachedX, y - ele->CachedY,
724 msg->Button, msg->bPressed
726 // Allow a type to trap the input from going any higher
732 client_msg.X = x - ele->CachedX;
733 client_msg.Y = y - ele->CachedY;
734 client_msg.WidgetID = ele->ID;
735 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
740 case WNDMSG_KEYDOWN: {
741 const struct sWndMsg_KeyAction *msg = Data;
742 if(Len < sizeof(*msg)) return -1;
744 if(!info->FocusedElement) return 0;
745 ele = info->FocusedElement;
747 if(gaWM_WidgetTypes[ele->Type]->KeyDown)
748 gaWM_WidgetTypes[ele->Type]->KeyDown(ele, msg->KeySym, msg->UCS32);
751 // TODO: Pass to user
756 case WNDMSG_KEYFIRE: {
757 const struct sWndMsg_KeyAction *msg = Data;
758 if(Len < sizeof(*msg)) return -1;
760 if(!info->FocusedElement) return 0;
761 ele = info->FocusedElement;
763 if(gaWM_WidgetTypes[ele->Type]->KeyFire)
764 gaWM_WidgetTypes[ele->Type]->KeyFire(ele, msg->KeySym, msg->UCS32);
767 // TODO: Pass the buck
772 const struct sWndMsg_KeyAction *msg = Data;
773 if(Len < sizeof(*msg)) return -1;
775 if(!info->FocusedElement) return 0;
776 ele = info->FocusedElement;
778 if(gaWM_WidgetTypes[ele->Type]->KeyUp)
779 gaWM_WidgetTypes[ele->Type]->KeyUp(ele, msg->KeySym);
782 // TODO: Pass the buck
788 return 1; // Unhandled, pass to user
792 void Widget_Fire(tElement *Element)
795 msg.WidgetID = Element->ID;
796 _SysDebug("Widget_Fire: Fire on %p %i", Element->Window, Element->ID);
797 WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);