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>
15 #define DEFAULT_ELETABLE_SIZE 64
18 int Renderer_Widget_Init(void);
19 tWindow *Renderer_Widget_Create(int Flags);
20 void Renderer_Widget_Redraw(tWindow *Window);
21 void Widget_RenderWidget(tWindow *Window, tElement *Element);
22 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data);
23 void Widget_UpdateDimensions(tElement *Element);
24 void Widget_UpdatePosition(tElement *Element);
25 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID);
26 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg);
29 tWMRenderer gRenderer_Widget = {
31 .CreateWindow = Renderer_Widget_Create,
32 .Redraw = Renderer_Widget_Redraw,
33 .HandleMessage = Renderer_Widget_HandleMessage
37 int Renderer_Widget_Init(void)
39 WM_RegisterRenderer(&gRenderer_Widget);
44 tWindow *Renderer_Widget_Create(int Flags)
48 int eletable_size = DEFAULT_ELETABLE_SIZE;
50 // TODO: Use `Flags` as default element count?
52 ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
53 info = ret->RendererInfo;
55 info->TableSize = eletable_size;
56 info->RootElement.BackgroundColour = 0xCCCCCC;
61 void Renderer_Widget_Redraw(tWindow *Window)
63 tWidgetWin *info = Window->RendererInfo;
64 WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
66 Widget_UpdateDimensions(&info->RootElement);
67 Widget_UpdatePosition(&info->RootElement);
69 Widget_RenderWidget(Window, &info->RootElement);
72 // --- Render / Resize ---
73 void Widget_RenderWidget(tWindow *Window, tElement *Element)
77 if( Element->Flags & ELEFLAG_NORENDER ) return ;
78 if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
80 Widget_Decorator_RenderWidget(Window, Element);
82 for(child = Element->FirstChild; child; child = child->NextSibling)
84 Widget_RenderWidget(Window, child);
88 void Widget_UpdateDimensions(tElement *Element)
95 int fullCross, dynWith;
98 // - Get the fixed and minimum sizes of the element
99 for( child = Element->FirstChild; child; child = child->NextSibling )
101 // Ignore elements that will not be rendered
102 if( child->Flags & ELEFLAG_NORENDER ) continue ;
104 // Absolutely positioned elements don't affect dimensions
105 if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
107 // Fixed width elements
108 if( child->FixedWith )
111 fixedSize += child->FixedWith;
114 if( child->FixedCross && maxCross < child->FixedCross )
115 maxCross = child->FixedCross;
116 if( child->MinCross && maxCross < child->MinCross )
117 maxCross = child->MinCross;
121 // Get the dynamic with size from the unused space in the element
122 if( nChildren > nFixed ) {
123 if( Element->Flags & ELEFLAG_VERTICAL )
124 dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
126 dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
127 dynWith -= fixedSize;
128 if( dynWith < 0 ) return ;
129 dynWith /= nChildren - nFixed;
132 // Get the cross size
133 if( Element->Flags & ELEFLAG_VERTICAL )
134 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
136 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
138 // Pass 2 - Set sizes and recurse
139 for( child = Element->FirstChild; child; child = child->NextSibling )
143 // Ignore elements that will not be rendered
144 if( child->Flags & ELEFLAG_NORENDER ) continue ;
146 // --- Cross Size ---
147 // TODO: Expand to fill?
148 // TODO: Extra flag so options are (Expand, Equal, Wrap)
149 if( child->FixedCross )
150 cross = child->FixedCross;
151 else if( child->Flags & ELEFLAG_NOEXPAND )
152 cross = child->MinCross;
157 if( child->FixedWith)
158 with = child->FixedWith;
159 else if( child->Flags & ELEFLAG_NOSTRETCH )
160 with = child->MinWith;
165 // Update the dimensions if they have changed
166 if( Element->Flags & ELEFLAG_VERTICAL ) {
167 // If no change, don't recurse
168 if( child->CachedW == cross && child->CachedH == with )
170 child->CachedW = cross;
171 child->CachedH = with;
174 // If no change, don't recurse
175 if( child->CachedW == with && child->CachedH == cross )
177 child->CachedW = with;
178 child->CachedH = cross;
181 // Force the positions of child elements to be recalculated
184 // Recurse down so the child elements can be updated
185 Widget_UpdateDimensions(child);
191 * \brief Update the position of child elements
193 void Widget_UpdatePosition(tElement *Element)
198 if( Element->Flags & ELEFLAG_NORENDER ) return ;
201 x = Element->CachedX + Element->PaddingL;
202 y = Element->CachedY + Element->PaddingT;
205 for(child = Element->FirstChild; child; child = child->NextSibling)
208 // Ignore elements that will not be rendered
209 if( child->Flags & ELEFLAG_NORENDER ) continue ;
214 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
215 if(Element->Flags & ELEFLAG_VERTICAL)
216 newX += Element->CachedW/2 - child->CachedW/2;
218 newY += Element->CachedH/2 - child->CachedH/2;
220 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
221 if(Element->Flags & ELEFLAG_VERTICAL )
222 newX += Element->CachedW - child->CachedW
223 - Element->PaddingL - Element->PaddingR;
225 newY += Element->CachedH - child->CachedH
226 - Element->PaddingT - Element->PaddingB;
229 // Check for changes, and don't update if there was no change
230 if( newX != child->CachedX || newY != child->CachedY )
232 child->CachedX = newX;
233 child->CachedY = newY;
234 // Update child's children positions
235 Widget_UpdatePosition(child);
239 if(Element->Flags & ELEFLAG_VERTICAL ) {
240 y += child->CachedH + Element->GapSize;
243 x += child->CachedW + Element->GapSize;
250 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
255 return &Info->RootElement;
257 if( ID < Info->TableSize ) return Info->ElementTable[ID];
259 ele = Info->ElementTable[ID % Info->TableSize];
260 while(ele && ele->ID != ID) ele = ele->ListNext;
264 // --- Message Handlers ---
265 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
267 const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
268 tElement *parent, *new;
271 if( Len < sizeof(tWidgetMsg_Create) )
273 if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
277 parent = Widget_GetElementById(Info, Msg->Parent);
280 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
284 // Check if the ID is already in use
285 if( Widget_GetElementById(Info, Msg->NewID) )
288 // Create new element
289 new = calloc(sizeof(tElement), 1);
290 new->ID = Msg->NewID;
291 new->Type = Msg->Type;
292 new->Parent = parent;
293 new->Flags = Msg->Flags;
299 // Add to parent's list
300 if(parent->LastChild)
301 parent->LastChild->NextSibling = new;
303 parent->FirstChild = new;
304 new->NextSibling = parent->LastChild;
305 parent->LastChild = new;
309 tElement *ele, *prev = NULL;
310 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
312 prev->ListNext = new;
314 Info->ElementTable[new->ID % Info->TableSize] = new;
318 void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg)
322 if( Len < sizeof(tWidgetMsg_SetSize) )
325 ele = Widget_GetElementById(Info, Msg->WidgetID);
328 ele->FixedWith = Msg->Value;
331 void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg)
336 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data)
338 tWidgetWin *info = Target->RendererInfo;
341 case WNDMSG_RESIZE: {
342 struct sWndMsg_Resize *msg = Data;
343 if(Len < sizeof(*msg)) return -1;
345 info->RootElement.CachedW = msg->W;
346 info->RootElement.CachedH = msg->H;
350 case MSG_WIDGET_CREATE:
351 Widget_NewWidget(info, Len, Data);
355 case MSG_WIDGET_SETSIZE:
356 Widget_SetSize(info, Len, Data);
359 case MSG_WIDGET_SETTEXT:
360 Widget_SetText(info, Len, Data);
365 return 1; // Unhandled, pass to user