+ tWidgetWin *info = Window->RendererInfo;
+ WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
+
+ Widget_UpdateDimensions(&info->RootElement);
+ Widget_UpdatePosition(&info->RootElement);
+
+ Widget_RenderWidget(Window, &info->RootElement);
+}
+
+// --- Render / Resize ---
+void Widget_RenderWidget(tWindow *Window, tElement *Element)
+{
+ tElement *child;
+
+ if( Element->Flags & ELEFLAG_NORENDER ) return ;
+ if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
+
+ Widget_Decorator_RenderWidget(Window, Element);
+
+ for(child = Element->FirstChild; child; child = child->NextSibling)
+ {
+ Widget_RenderWidget(Window, child);
+ }
+}
+
+void Widget_UpdateDimensions(tElement *Element)
+{
+ tElement *child;
+ int nChildren = 0;
+ int nFixed = 0;
+ int maxCross = 0;
+ int fixedSize = 0;
+ int fullCross, dynWith;
+
+ // Pass 1
+ // - Get the fixed and minimum sizes of the element
+ for( child = Element->FirstChild; child; child = child->NextSibling )
+ {
+ // Ignore elements that will not be rendered
+ if( child->Flags & ELEFLAG_NORENDER ) continue ;
+
+ // Absolutely positioned elements don't affect dimensions
+ if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue ;
+
+ // Fixed width elements
+ if( child->FixedWith )
+ {
+ nFixed ++;
+ fixedSize += child->FixedWith;
+ }
+ else if( child->Flags & ELEFLAG_NOSTRETCH )
+ {
+ nFixed ++;
+ fixedSize += child->MinWith;
+ }
+
+ if( child->FixedCross && maxCross < child->FixedCross )
+ maxCross = child->FixedCross;
+ if( child->MinCross && maxCross < child->MinCross )
+ maxCross = child->MinCross;
+ nChildren ++;
+ }
+
+ // Get the dynamic with size from the unused space in the element
+ if( nChildren > nFixed ) {
+ if( Element->Flags & ELEFLAG_VERTICAL )
+ dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
+ else
+ dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
+ dynWith -= fixedSize;
+ if( dynWith < 0 ) return ;
+ dynWith /= nChildren - nFixed;
+ }
+
+// _SysDebug("%i - nChildren = %i, nFixed = %i, dynWith = %i, fixedSize = %i",
+// Element->ID, nChildren, nFixed, dynWith, fixedSize);
+
+ // Get the cross size
+ if( Element->Flags & ELEFLAG_VERTICAL )
+ fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
+ else
+ fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
+
+ // Pass 2 - Set sizes and recurse
+ for( child = Element->FirstChild; child; child = child->NextSibling )
+ {
+ int cross, with;
+
+ // Ignore elements that will not be rendered
+ if( child->Flags & ELEFLAG_NORENDER ) continue ;
+
+ // --- Cross Size ---
+ // TODO: Expand to fill?
+ // TODO: Extra flag so options are (Expand, Equal, Wrap)
+ if( child->FixedCross )
+ cross = child->FixedCross;
+ else if( child->Flags & ELEFLAG_NOEXPAND )
+ cross = child->MinCross;
+ else
+ cross = fullCross;
+
+ // --- With Size ---
+ if( child->FixedWith )
+ with = child->FixedWith;
+ else if( child->Flags & ELEFLAG_NOSTRETCH )
+ with = child->MinWith;
+ else
+ with = dynWith;
+
+
+ if(with < child->MinWith) with = child->MinWith;
+ if(cross < child->MinCross) cross = child->MinCross;
+
+ // Update the dimensions if they have changed
+ if( Element->Flags & ELEFLAG_VERTICAL ) {
+ // If no change, don't recurse
+ if( child->CachedW == cross && child->CachedH == with )
+ continue ;
+ child->CachedW = cross;
+ child->CachedH = with;
+ }
+ else {
+ // If no change, don't recurse
+ if( child->CachedW == with && child->CachedH == cross )
+ continue ;
+ child->CachedW = with;
+ child->CachedH = cross;
+ }
+
+ // Force the positions of child elements to be recalculated
+ child->CachedX = -1;
+
+ // Recurse down so the child elements can be updated
+ Widget_UpdateDimensions(child);
+ }
+
+}
+
+/**
+ * \brief Update the position of child elements
+ */
+void Widget_UpdatePosition(tElement *Element)
+{
+ tElement *child;
+ int x, y;
+
+ if( Element->Flags & ELEFLAG_NORENDER ) return ;
+
+// _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
+// Element, Element->ID, Element->Type, Element->Flags);
+
+ // Initialise
+ x = Element->CachedX + Element->PaddingL;
+ y = Element->CachedY + Element->PaddingT;
+
+ // Update each child
+ for(child = Element->FirstChild; child; child = child->NextSibling)
+ {
+ int newX, newY;
+ // Ignore elements that will not be rendered
+ if( child->Flags & ELEFLAG_NORENDER ) continue ;
+
+ newX = x; newY = y;
+
+ // Handle alignment
+ if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
+ if(Element->Flags & ELEFLAG_VERTICAL)
+ newX += Element->CachedW/2 - child->CachedW/2;
+ else
+ newY += Element->CachedH/2 - child->CachedH/2;
+ }
+ else if( Element->Flags & ELEFLAG_ALIGN_END ) {
+ if(Element->Flags & ELEFLAG_VERTICAL )
+ newX += Element->CachedW - child->CachedW
+ - Element->PaddingL - Element->PaddingR;
+ else
+ newY += Element->CachedH - child->CachedH
+ - Element->PaddingT - Element->PaddingB;
+ }
+
+// _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
+
+ // Check for changes, and don't update if there was no change
+ if( newX != child->CachedX || newY != child->CachedY )
+ {
+ child->CachedX = newX;
+ child->CachedY = newY;
+ // Update child's children positions
+ Widget_UpdatePosition(child);
+ }
+
+ // Increment
+ if(Element->Flags & ELEFLAG_VERTICAL ) {
+ y += child->CachedH + Element->GapSize;
+ }
+ else {
+ x += child->CachedW + Element->GapSize;
+ }
+ }
+}
+
+/**
+ * \brief Update the minimum dimensions of the element
+ * \note Called after a child's minimum dimensions have changed
+ */
+void Widget_UpdateMinDims(tElement *Element)
+{
+ tElement *child;
+
+ if(!Element) return;
+
+ Element->MinCross = 0;
+ Element->MinWith = 0;
+
+ for(child = Element->FirstChild; child; child = child->NextSibling)
+ {
+ if( Element->Parent &&
+ (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
+ )
+ {
+ if(child->FixedCross)
+ Element->MinCross += child->FixedCross;
+ else
+ Element->MinCross += child->MinCross;
+ if(child->FixedWith)
+ Element->MinWith += child->FixedWith;
+ else
+ Element->MinWith += child->MinWith;
+ }
+ else
+ {
+ if(child->FixedCross)
+ Element->MinWith += child->FixedCross;
+ else
+ Element->MinWith += child->MinCross;
+ if(child->FixedWith)
+ Element->MinCross += child->FixedWith;
+ else
+ Element->MinCross += child->MinWith;
+ }
+ }
+
+ // Recurse upwards
+ Widget_UpdateMinDims(Element->Parent);
+}
+
+tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
+{
+ tElement *ret, *next, *ele;
+
+ next = &Info->RootElement;
+ while(next)
+ {
+ ret = next;
+ next = NULL;
+ for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
+ {
+ if(ele->Flags & ELEFLAG_NORENDER) continue;
+ if(X < ele->CachedX) continue;
+ if(Y < ele->CachedY) continue;
+ if(X >= ele->CachedX + ele->CachedW) continue;
+ if(Y >= ele->CachedY + ele->CachedH) continue;
+ next = ele;
+ }
+ }
+ return ret;
+}
+
+// --- Helpers ---
+tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
+{
+ tElement *ele;
+
+ if( ID == -1 ) return &Info->RootElement;
+
+ if( ID < Info->TableSize ) return Info->ElementTable[ID];
+
+ ele = Info->ElementTable[ID % Info->TableSize];
+ while(ele && ele->ID != ID) ele = ele->ListNext;
+ return ele;
+}
+
+// --- Message Handlers ---
+void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
+{
+ const int max_debugname_len = Len - sizeof(tWidgetMsg_Create);
+ tElement *parent, *new;
+
+ // Sanity check
+ if( Len < sizeof(tWidgetMsg_Create) )
+ return ;
+ if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
+ return ;
+
+ _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
+ Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
+
+ // Create
+ parent = Widget_GetElementById(Info, Msg->Parent);
+ if(!parent)
+ {
+ _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
+ return ;
+ }
+
+ // Check if the ID is already in use
+ if( Widget_GetElementById(Info, Msg->NewID) )
+ return ;
+
+ // Create new element
+ new = calloc(sizeof(tElement), 1);
+ new->Window = parent->Window;
+ new->ID = Msg->NewID;
+ new->Type = Msg->Type;
+ new->Parent = parent;
+ new->Flags = Msg->Flags;
+ new->PaddingT = 2;
+ new->PaddingB = 2;
+ new->PaddingL = 2;
+ new->PaddingR = 2;
+ new->CachedX = -1;
+
+ if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type].Init )
+ gaWM_WidgetTypes[new->Type].Init(new);
+
+ // Add to parent's list
+ if(parent->LastChild)
+ parent->LastChild->NextSibling = new;
+ else
+ parent->FirstChild = new;
+ parent->LastChild = new;
+
+ // Add to info
+ {
+ tElement *ele, *prev = NULL;
+ for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
+ if(prev)
+ prev->ListNext = new;
+ else
+ Info->ElementTable[new->ID % Info->TableSize] = new;
+ }
+
+ Widget_UpdateMinDims(parent);
+}
+
+void Widget_SetFlags(tWidgetWin *Info, int Len, tWidgetMsg_SetFlags *Msg)
+{
+ tElement *ele;
+
+ if( Len < sizeof(tWidgetMsg_SetFlags) )
+ return ;
+
+ _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
+
+ ele = Widget_GetElementById(Info, Msg->WidgetID);
+ if(!ele) return;
+
+ Msg->Value &= Msg->Mask;
+
+ ele->Flags &= ~Msg->Mask;
+ ele->Flags |= Msg->Value;
+}
+
+void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg)
+{
+ tElement *ele;
+
+ if( Len < sizeof(tWidgetMsg_SetSize) )
+ return ;
+
+ ele = Widget_GetElementById(Info, Msg->WidgetID);
+ if(!ele) return ;
+
+ ele->FixedWith = Msg->Value;
+}
+
+void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg)
+{
+ tElement *ele;
+
+ if( Len < sizeof(tWidgetMsg_SetText) + 1 )
+ return ;
+ if( Msg->Text[Len - sizeof(tWidgetMsg_SetText) - 1] != '\0' )
+ return ;
+
+ ele = Widget_GetElementById(Info, Msg->WidgetID);
+ if(!ele) return ;
+
+
+ if( ele->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[ele->Type].UpdateText )
+ {
+ gaWM_WidgetTypes[ele->Type].UpdateText( ele, Msg->Text );
+ }
+// else
+// {
+// if(ele->Text) free(ele->Text);
+// ele->Text = strdup(Msg->Text);
+// }