*/
#include "common.h"
#include <stdlib.h>
-#include <strings.h>
+#include <string.h>
#include "wm.h"
+#include <acess/sys.h> // _SysDebug
// === IMPORTS ===
-extern void Decorator_RenderWidget(tElement *Element);
+extern void Video_GetTextDims(tFont *Font, const char *Text, int *W, int *H);
// === PROTOTYPES ===
-tElement *WM_CreateElement(tElement *Parent, int Type, int Flags);
-void WM_SetFlags(tElement *Element, int Flags);
-void WM_SetSize(tElement *Element, int Size);
-void WM_SetText(tElement *Element, char *Text);
-void WM_UpdateDimensions(tElement *Element, int Pass);
-void WM_UpdatePosition(tElement *Element);
-void WM_RenderWidget(tElement *Element);
-void WM_Update(void);
+tApplication *AxWin_RegisterClient(tIPC_Type *IPCType, void *Ident, const char *Name);
+void AxWin_DeregisterClient(tApplication *App);
+tApplication *AxWin_GetClient(tIPC_Type *Method, void *Ident);
+tElement *AxWin_CreateElement(tElement *Parent, int Type, int Flags, const char *DebugName);
+void AxWin_DeleteElement(tElement *Element);
+void AxWin_SetFlags(tElement *Element, int Flags);
+void AxWin_SetSize(tElement *Element, int Size);
+void AxWin_SetText(tElement *Element, const char *Text);
// === GLOBALS ===
-tElement gWM_RootElement;
+// - TODO: Handle windows by having multiple root elements
+tElement gWM_RootElement = {
+ .DebugName = "ROOT"
+};
+tWindow *gWM_WindowFirst;
+tWindow *gWM_WindowLast;
+tApplication *gWM_Applications;
+ int giWM_MaxAreaX = 0;
+ int giWM_MaxAreaY = 0;
+ int giWM_MaxAreaW = -1;
+ int giWM_MaxAreaH = -1;
+
+// --- Element type flags
struct {
void (*Init)(tElement *This);
+ void (*Delete)(tElement *This);
void (*UpdateFlags)(tElement *This);
void (*UpdateSize)(tElement *This);
void (*UpdateText)(tElement *This);
} gaWM_WidgetTypes[MAX_ELETYPES] = {
- {NULL, NULL, NULL, NULL}, // NULL
- {NULL, NULL, NULL, NULL} // Box
+ {NULL, NULL, NULL, NULL, NULL}, // NULL
+ {NULL, NULL, NULL, NULL, NULL}, // Window
+ {NULL, NULL, NULL, NULL, NULL} // Box
};
+const int ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
// === CODE ===
+tApplication *AxWin_RegisterClient(tIPC_Type *Method, void *Ident, const char *Name)
+{
+ int identlen = Method->GetIdentSize(Ident);
+ // Structure, empty string, Name, Ident
+ tApplication *ret = calloc( 1, sizeof(tApplication) + 1 + strlen(Name) + 1 + identlen );
+
+ // DebugName is empty
+
+ // Name/Title
+ ret->Name = &ret->MetaElement.DebugName[1];
+ strcpy(ret->Name, Name);
+ // Ident
+ ret->Ident = ret->Name + strlen(Name) + 1;
+ memcpy(ret->Ident, Ident, identlen);
+ // IPC Type
+ ret->IPCType = Method;
+
+ // Element index
+ ret->MaxElementIndex = DEFAULT_ELEMENTS_PER_APP;
+ ret->EleIndex = calloc( 1, ret->MaxElementIndex * sizeof(*ret->EleIndex) );
+
+ // Add to global list
+ ret->Next = gWM_Applications;
+ gWM_Applications = ret;
+
+ // TODO: Inform listeners of the new application
+
+ return ret;
+}
+
+void AxWin_DeregisterClient(tApplication *App)
+{
+ // TODO: Complete implementing DeregisterClient
+ tElement *win, *next;
+
+ for( win = App->MetaElement.FirstChild; win; win = next )
+ {
+ next = win->NextSibling;
+ AxWin_DeleteElement(win);
+ }
+
+ // TODO: Inform listeners of deleted application
+
+ // Remove from list
+ {
+ tApplication *app, *prev = NULL;
+ for( app = gWM_Applications; app; app = app->Next )
+ {
+ if( app == App ) break;
+ prev = app;
+ }
+
+ if( app )
+ {
+ if(prev)
+ prev->Next = App->Next;
+ else
+ gWM_Applications = App->Next;
+ }
+ }
+
+ free(App);
+}
+
+/**
+ * \brief Get an application handle from a client identifier
+ */
+tApplication *AxWin_GetClient(tIPC_Type *Method, void *Ident)
+{
+ // TODO: Faster and smarter technique
+ tApplication *app;
+ for( app = gWM_Applications; app; app = app->Next )
+ {
+ if( app->IPCType != Method ) continue;
+ if( Method->CompareIdent( app->Ident, Ident ) != 0 ) continue;
+ return app;
+ }
+ return NULL;
+}
+
+tElement *AxWin_CreateAppWindow(tApplication *App, const char *Name)
+{
+ tElement *ret;
+ tWindow *win;
+
+ win = calloc(1, sizeof(tWindow) + 1);
+ if(!win) return NULL;
+
+ ret = &win->Element;
+ ret->Type = ELETYPE_WINDOW;
+ ret->Data = win;
+ ret->Parent = &App->MetaElement;
+
+ // Add to parent list
+ if(ret->Parent->LastChild)
+ ret->Parent->LastChild->NextSibling = ret;
+ ret->Parent->LastChild = ret;
+ if(!ret->Parent->FirstChild)
+ ret->Parent->FirstChild = ret;
+
+ ret->Text = strdup(Name);
+
+ return ret;
+}
+
// --- Widget Creation and Control ---
-tElement *WM_CreateElement(tElement *Parent, int Type, int Flags)
+tAxWin_Element *AxWin_CreateElement(tElement *Parent, int Type, int Flags, const char *DebugName)
{
tElement *ret;
+ const char *dbgName = DebugName ? DebugName : "";
- ret = calloc(sizeof(tElement), 1);
+ ret = calloc(sizeof(tElement)+strlen(dbgName)+1, 1);
if(!ret) return NULL;
// Prepare
ret->Type = Type;
+ strcpy(ret->DebugName, dbgName);
if(Parent == NULL) Parent = &gWM_RootElement;
ret->Parent = Parent;
+ ret->Flags = Flags;
// Append to parent's list
- ret->NextSibling = Parent->LastChild;
+ if(Parent->LastChild)
+ Parent->LastChild->NextSibling = ret;
Parent->LastChild = ret;
if(!Parent->FirstChild) Parent->FirstChild = ret;
ret->PaddingT = 2;
ret->PaddingB = 2;
+ if( Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[Type].Init )
+ gaWM_WidgetTypes[Type].Init(ret);
+
+ WM_UpdateMinDims(ret->Parent);
+
return ret;
}
-void WM_SetFlags(tElement *Element, int Flags)
+/**
+ * \brief Delete an element
+ */
+void AxWin_DeleteElement(tElement *Element)
+{
+ tElement *child, *next;
+
+ for(child = Element->FirstChild; child; child = next)
+ {
+ next = child->NextSibling;
+ AxWin_DeleteElement(child);
+ }
+
+ // TODO: Implement AxWin_DeleteElement
+ // TODO: Clean up related data.
+ if( Element->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[Element->Type].Delete )
+ gaWM_WidgetTypes[Element->Type].Delete(Element);
+
+ if(Element->Owner)
+ Element->Owner->EleIndex[ Element->ApplicationID ] = NULL;
+
+ Element->Type = 0;
+ Element->Owner = 0;
+ Element->Flags = 0;
+
+ free(Element);
+}
+
+/**
+ * \brief Alter an element's flags
+ */
+void AxWin_SetFlags(tElement *Element, int Flags)
{
// Permissions are handled in the message handler
if(!Element) {
return ;
}
-void WM_SetSize(tElement *Element, int Size)
+/**
+ * \brief Set the fixed lenghthways size of an element
+ */
+void AxWin_SetSize(tElement *Element, int Size)
{
if(!Element) return ;
- Element->Size = Size;
+ Element->FixedWith = Size;
return ;
}
-void WM_SetText(tElement *Element, char *Text)
+/**
+ * \brief Set the text field of an element
+ * \note Used for the image path on ELETYPE_IMAGE
+ */
+void AxWin_SetText(tElement *Element, const char *Text)
{
if(!Element) return ;
if(Element->Text) free(Element->Text);
return ;
}
- Element->Flags |= ELEFLAG_FIXEDSIZE;
+ //Element->Flags |= ELEFLAG_FIXEDSIZE;
Element->CachedW = ((tImage*)Element->Data)->Width;
Element->CachedH = ((tImage*)Element->Data)->Height;
- if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL)
- Element->Size = Element->CachedH;
- else
- Element->Size = Element->CachedW;
- break;
- }
-
- return ;
-}
-
-// --- Pre-Rendering ---
-#if 1
-void WM_UpdateDimensions(tElement *Element, int Pass)
-{
- int fixedSize = 0, maxCross = 0;
- int nFixed = 0, nChildren = 0;
- int minSize = 0;
- tElement *child;
-
- // Pass zero intialises
- if( Pass == 0 )
- {
- // If not a fixed size element, initialise the sizes
- if( !(Element->Flags & ELEFLAG_FIXEDSIZE) )
- {
- Element->CachedH = 0;
- Element->CachedW = 0;
- if( Element->Size )
- {
- if( Element->Parent->Flags & ELEFLAG_VERTICAL )
- Element->CachedH = Element->Size;
- else
- Element->CachedW = Element->Size;
- }
-
- if( !(Element->Flags & ELEFLAG_NOEXPAND) )
- {
- if( Element->Parent->Flags & ELEFLAG_VERTICAL )
- Element->CachedW = Element->Parent->CachedW;
- else
- Element->CachedH = Element->Parent->CachedH;
- }
- }
- }
-
- for( child = Element->FirstChild; child; child = child->NextSibling )
- {
- WM_UpdateDimensions( child, 0 );
-
- if( Element->Flags & ELEFLAG_VERTICAL )
- {
- if( child->CachedH ) {
- fixedSize += child->CachedH;
- minSize += child->CachedH;
- nFixed ++;
- }
- else
- minSize += child->MinHeight;
-
- if( maxCross < child->CachedW )
- maxCross = child->CachedW;;
- }
- else
- {
- if( child->CachedW ) {
- fixedSize += child->CachedW;
- minSize += child->CachedW;
- nFixed ++;
- }
- else
- minSize += child->MinWidth;
-
- if( maxCross < child->CachedH )
- maxCross = child->CachedH;
- }
- nChildren ++;
- }
- _SysDebug("WM_UpdateDimensions: nFixed=%i, fixedSize=%i, minSize=%i, maxCross=%i",
- nFixed, fixedSize, minSize, maxCross
- );
-
-
- // If we don't have our dimensions, get the child dimensions
- if( Element->CachedW == 0 || Element->CachedH == 0 )
- {
- if( Element->Flags & ELEFLAG_VERTICAL )
- {
- if( Element->CachedW == 0 && maxCross )
- Element->CachedW = Element->PaddingL
- + Element->PaddingR + maxCross;
-
- if( Element->CachedH == 0 && nFixed == nChildren )
- Element->CachedH = Element->PaddingT
- + Element->PaddingB + fixedSize
- + nChildren * Element->GapSize;
- }
- else
- {
- if( maxCross )
- Element->CachedH = Element->PaddingT
- + Element->PaddingB + maxCross;
-
- if( Element->CachedW == 0 && nFixed == nChildren )
- Element->CachedW = Element->PaddingL
- + Element->PaddingR + fixedSize
- + nChildren * Element->GapSize;
- }
- }
-
- // Now, if we have the "length" of the widget, we can size the children
- if( (Element->Flags & ELEFLAG_VERTICAL && Element->CachedH > 0)
- || (!(Element->Flags & ELEFLAG_VERTICAL) && Element->CachedW > 0) )
- {
- int dynSize;
-
- // Calculate the size of dynamically sized elements
- if( Element->Flags & ELEFLAG_VERTICAL )
- dynSize = Element->CachedH - Element->PaddingT
- - Element->PaddingB - fixedSize;
- else
- dynSize = Element->CachedW - Element->PaddingL
- - Element->PaddingR - fixedSize;
- dynSize /= nChildren - nFixed;
-
- // Itterate children again
- for( child = Element->FirstChild; child; child = child->NextSibling )
- {
- int tmp;
-
- // Get the size of them
- if(child->Size)
- tmp = child->Size;
- else
- tmp = dynSize;
-
- if( Element->Flags & ELEFLAG_VERTICAL ) {
- if( tmp < child->MinHeight )
- tmp = child->MinHeight;
- child->CachedH = tmp;
- }
- else {
- if( tmp < child->MinWidth )
- tmp = child->MinWidth;
- child->CachedW = tmp;
- }
-
- WM_UpdateDimensions(child, 1);
- }
- }
-}
-
-#else
-
-/**
- * \brief Updates the dimensions of an element
- *
- * The dimensions of an element are calculated from the parent's
- * cross dimension (the side at right angles to the alignment) sans some
- * padding.
- */
-void WM_UpdateDimensions(tElement *Element, int Pass)
-{
- tElement *child;
- int fixedChildSize = 0;
- int dynamicSize;
- int nChildren = 0;
- int nFixed = 0;
-
- _SysDebug("WM_UpdateDimensions: (Element=%p{Flags:0x%x}, Pass=%i)",
- Element, Element->Flags,
- Pass);
-
- if( Pass == 0 )
- {
- if( Element->Flags & ELEFLAG_NORENDER ) return ;
-
- if( !(Element->Flags & ELEFLAG_ABSOLUTEPOS) ) {
- Element->CachedX = 0;
- Element->CachedY = 0;
- }
- if( !(Element->Flags & ELEFLAG_FIXEDSIZE) ) {
- Element->CachedW = 0;
- Element->CachedH = 0;
- }
- }
-
- if( !(Element->Flags & ELEFLAG_FIXEDSIZE) ) {
- // If the element is sized, fix its dimension(s)
- if(Element->Size)
- {
- if(Element->Flags & ELEFLAG_NOEXPAND)
- {
- Element->CachedW = Element->Size;
- Element->CachedH = Element->Size;
- }
- else {
- if( Element->Parent->Flags & ELEFLAG_VERTICAL ) {
- Element->CachedH = Element->Size;
- Element->CachedW = Element->Parent->CachedW;
- if(Element->CachedW)
- Element->CachedW -= (Element->Parent->PaddingL + Element->Parent->PaddingR);
- }
- else {
- Element->CachedW = Element->Size;
- Element->CachedH = Element->Parent->CachedH;
- if(Element->CachedH)
- Element->CachedH -= (Element->Parent->PaddingT + Element->Parent->PaddingB);
- }
- }
+ if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
+ Element->MinCross = ((tImage*)Element->Data)->Width;
+ Element->MinWith = ((tImage*)Element->Data)->Height;
}
else {
- // Ok, so now we need to calculate the size of all child elements
- // However, if ELEFLAG_NOEXPAND is not set, we can still set one
- // dimension
- if( !(Element->Flags & ELEFLAG_NOEXPAND) ) {
- if( Element->Parent->Flags & ELEFLAG_VERTICAL ) {
- Element->CachedW = Element->Parent->CachedW;
- if(Element->CachedW)
- Element->CachedW -= (Element->Parent->PaddingL + Element->Parent->PaddingR);
- }
- else {
- Element->CachedH = Element->Parent->CachedH;
- if(Element->CachedH)
- Element->CachedH -= (Element->Parent->PaddingT + Element->Parent->PaddingB);
- }
- }
+ Element->MinWith = ((tImage*)Element->Data)->Width;
+ Element->MinCross = ((tImage*)Element->Data)->Height;
}
- }
+ break;
- // Process Children (first pass)
- for( child = Element->FirstChild; child; child = child->NextSibling )
- {
- if( child->Flags & ELEFLAG_NORENDER ) continue;
- WM_UpdateDimensions(child, 0);
-
- // Children that don't inherit positions are ignored
- if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue;
-
- fixedChildSize += child->Size;
- if(child->Size > 0)
- nFixed ++;
- nChildren ++;
-
- // If we are wrapping the children, get the largest cross size
- if( !(Element->Flags & ELEFLAG_FIXEDSIZE)
- && Element->Flags & ELEFLAG_NOEXPAND
- && Element->Size == 0 )
+ case ELETYPE_TEXT:
{
- if( Element->Flags & ELEFLAG_VERTICAL ) {
- if( Element->CachedW < child->CachedW )
- Element->CachedW = child->CachedW;
- }
- else {
- if( Element->CachedH < child->CachedH )
- Element->CachedH = child->CachedH;
- }
- }
- }
-
- // Let's avoid a #DIV0 shall we?
- if( nChildren > 0 )
- {
- // Calculate the size of dynamically sized children
- if( Element->Flags & ELEFLAG_VERTICAL ) {
- if( Element->CachedH == 0 ) {
- if( nFixed == nChildren )
- Element->CachedH = fixedChildSize;
- else
- return ;
- }
- dynamicSize = (Element->CachedH - (Element->PaddingT + Element->PaddingB) - fixedChildSize) / nChildren;
+ int w=0, h=0;
+ Video_GetTextDims(NULL, Element->Text, &w, &h);
+ if(Element->Parent && Element->Parent->Flags & ELEFLAG_VERTICAL) {
+ Element->MinCross = w;
+ Element->MinWith = h;
}
else {
- if( Element->CachedW == 0 ) {
- if( nFixed == nChildren )
- Element->CachedW = fixedChildSize;
- else
- return ;
- }
- dynamicSize = (Element->CachedW - (Element->PaddingL + Element->PaddingR) - fixedChildSize) / nChildren;
+ Element->MinWith = w;
+ Element->MinCross = h;
}
-
- // Process Children (second pass)
- for( child = Element->FirstChild; child; child = child->NextSibling )
- {
- if( child->Flags & ELEFLAG_NORENDER ) continue;
- // Children that don't inherit positions are ignored
- if( child->Flags & ELEFLAG_ABSOLUTEPOS ) continue;
-
- if(!child->Size) {
- if(child->Flags & ELEFLAG_VERTICAL)
- child->CachedH = dynamicSize;
- else
- child->CachedW = dynamicSize;
- }
-
- WM_UpdateDimensions(child, 1);
-
- // If we are wrapping the children, get the largest cross size
- if( Element->Flags & ELEFLAG_NOEXPAND ) {
- if( Element->Flags & ELEFLAG_VERTICAL ) {
- if( Element->CachedW < child->CachedW )
- Element->CachedW = child->CachedW;
- }
- else {
- if( Element->CachedH < child->CachedH )
- Element->CachedH = child->CachedH;
- }
- }
- }
- }
-
- // Add the padding
- //Element->CachedW += Element->PaddingL + Element->PaddingR;
- //Element->CachedH += Element->PaddingT + Element->PaddingB;
-
- _SysDebug("Pass %i, Element %p %ix%i",
- Pass, Element, Element->CachedW, Element->CachedH
- );
-
- // We should be done
- // Next function will do the coordinates
-}
-#endif
-
-
-/**
- * \brief Updates the position of an element
- *
- * The parent element sets the positions of its children
- */
-void WM_UpdatePosition(tElement *Element)
-{
- tElement *child;
- int x, y;
-
- if( Element->Flags & ELEFLAG_NORENDER ) return ;
-
- _SysDebug("Element=%p{PaddingL:%i, PaddingT:%i}",
- Element, Element->PaddingL, Element->PaddingT);
-
- // Initialise
- x = Element->CachedX + Element->PaddingL;
- y = Element->CachedY + Element->PaddingT;
-
- // Update each child
- for(child = Element->FirstChild; child; child = child->NextSibling)
- {
- child->CachedX = x;
- child->CachedY = y;
-
- // Set Alignment
- if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
- if(Element->Flags & ELEFLAG_VERTICAL )
- child->CachedX += Element->CachedW/2 - child->CachedW/2;
- else
- child->CachedY += Element->CachedH/2 - child->CachedH/2;
}
- else if( Element->Flags & ELEFLAG_ALIGN_END ) {
- if(Element->Flags & ELEFLAG_VERTICAL )
- child->CachedX += Element->CachedW - child->CachedW;
- else
- child->CachedY += Element->CachedH - child->CachedH;
- }
-
- // Update child's children positions
- WM_UpdatePosition(child);
-
- // Increment
- if(Element->Flags & ELEFLAG_VERTICAL ) {
- y += child->CachedH + Element->GapSize;
- }
- else {
- x += child->CachedW + Element->GapSize;
- }
- }
-
- _SysDebug("Element %p (%i,%i)",
- Element, Element->CachedX, Element->CachedY
- );
-}
-
-// --- Render ---
-void WM_RenderWidget(tElement *Element)
-{
- tElement *child;
-
- if( Element->Flags & ELEFLAG_NORENDER ) return ;
- if( Element->Flags & ELEFLAG_INVISIBLE ) return ;
-
- Decorator_RenderWidget(Element);
-
- for(child = Element->FirstChild; child; child = child->NextSibling)
- {
- WM_RenderWidget(child);
+ break;
+ default: // Any other, no special case
+ break ;
}
-}
-
-void WM_Update(void)
-{
- gWM_RootElement.CachedX = 0; gWM_RootElement.CachedY = 0;
- gWM_RootElement.CachedW = giScreenWidth;
- gWM_RootElement.CachedH = giScreenHeight;
- gWM_RootElement.Flags |= ELEFLAG_NOEXPAND|ELEFLAG_ABSOLUTEPOS|ELEFLAG_FIXEDSIZE;
- WM_UpdateDimensions( &gWM_RootElement, 0 );
- WM_UpdatePosition( &gWM_RootElement );
- WM_RenderWidget( &gWM_RootElement );
-
- Video_Update();
+ return ;
}