Usermode/AxWin3 - Implementing more of the widget code
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / renderer_widget.c
1 /*
2  * Acess2 Window Manager v3
3  * - By John Hodge (thePowersGang)
4  *
5  * render_widget.c
6  * - AxWin2 Widget port
7  */
8 #include <common.h>
9 #include <wm_renderer.h>
10 #include <renderer_widget.h>
11 #include <string.h>
12 #include <wm_messages.h>
13 #include <stdlib.h>
14
15 #define DEFAULT_ELETABLE_SIZE   64
16
17 // === PROTOTYPES ===
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);
27
28 // === GLOBALS ===
29 tWMRenderer     gRenderer_Widget = {
30         .Name = "Widget",
31         .CreateWindow = Renderer_Widget_Create,
32         .Redraw = Renderer_Widget_Redraw,
33         .HandleMessage = Renderer_Widget_HandleMessage
34 };
35
36 // === CODE ===
37 int Renderer_Widget_Init(void)
38 {
39         WM_RegisterRenderer(&gRenderer_Widget); 
40
41         return 0;
42 }
43
44 tWindow *Renderer_Widget_Create(int Flags)
45 {
46         tWindow *ret;
47         tWidgetWin      *info;
48          int    eletable_size = DEFAULT_ELETABLE_SIZE;
49
50         // TODO: Use `Flags` as default element count?
51
52         ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
53         info = ret->RendererInfo;
54         
55         info->TableSize = eletable_size;
56         info->RootElement.BackgroundColour = 0xCCCCCC;
57         
58         return ret;
59 }
60
61 void Renderer_Widget_Redraw(tWindow *Window)
62 {
63         tWidgetWin      *info = Window->RendererInfo;
64         WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
65
66         Widget_UpdateDimensions(&info->RootElement);
67         Widget_UpdatePosition(&info->RootElement);
68
69         Widget_RenderWidget(Window, &info->RootElement);
70 }
71
72 // --- Render / Resize ---
73 void Widget_RenderWidget(tWindow *Window, tElement *Element)
74 {
75         tElement        *child;
76         
77         if( Element->Flags & ELEFLAG_NORENDER ) return ;
78         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
79         
80         Widget_Decorator_RenderWidget(Window, Element);
81         
82         for(child = Element->FirstChild; child; child = child->NextSibling)
83         {
84                 Widget_RenderWidget(Window, child);
85         }
86 }
87
88 void Widget_UpdateDimensions(tElement *Element)
89 {
90         tElement        *child;
91          int    nChildren = 0;
92          int    nFixed = 0;
93          int    maxCross = 0;
94          int    fixedSize = 0;
95          int    fullCross, dynWith;
96         
97         // Pass 1
98         // - Get the fixed and minimum sizes of the element
99         for( child = Element->FirstChild; child; child = child->NextSibling )
100         {
101                 // Ignore elements that will not be rendered
102                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
103                 
104                 // Absolutely positioned elements don't affect dimensions
105                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue ;
106         
107                 // Fixed width elements 
108                 if( child->FixedWith )
109                 {
110                         nFixed ++;
111                         fixedSize += child->FixedWith;
112                 }
113                 
114                 if( child->FixedCross && maxCross < child->FixedCross )
115                         maxCross = child->FixedCross;
116                 if( child->MinCross && maxCross < child->MinCross )
117                         maxCross = child->MinCross;
118                 nChildren ++;
119         }
120         
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;
125                 else
126                         dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
127                 dynWith -= fixedSize;
128                 if( dynWith < 0 )       return ;
129                 dynWith /= nChildren - nFixed;
130         }
131         
132         // Get the cross size
133         if( Element->Flags & ELEFLAG_VERTICAL )
134                 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
135         else
136                 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
137         
138         // Pass 2 - Set sizes and recurse
139         for( child = Element->FirstChild; child; child = child->NextSibling )
140         {
141                  int    cross, with;
142
143                 // Ignore elements that will not be rendered
144                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
145                 
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;
153                 else
154                         cross = fullCross;
155                 
156                 // --- With Size ---
157                 if( child->FixedWith)
158                         with = child->FixedWith;
159                 else if( child->Flags & ELEFLAG_NOSTRETCH )
160                         with = child->MinWith;
161                 else
162                         with = dynWith;
163                 
164                 
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 )
169                                 continue ;
170                         child->CachedW = cross;
171                         child->CachedH = with;
172                 }
173                 else {
174                         // If no change, don't recurse
175                         if( child->CachedW == with && child->CachedH == cross )
176                                 continue ;
177                         child->CachedW = with;
178                         child->CachedH = cross;
179                 }
180                 
181                 // Force the positions of child elements to be recalculated
182                 child->CachedX = -1;
183         
184                 // Recurse down so the child elements can be updated    
185                 Widget_UpdateDimensions(child);
186         }
187         
188 }
189
190 /**
191  * \brief Update the position of child elements
192  */
193 void Widget_UpdatePosition(tElement *Element)
194 {
195         tElement        *child;
196          int    x, y;
197         
198         if( Element->Flags & ELEFLAG_NORENDER ) return ;
199         
200         // Initialise
201         x = Element->CachedX + Element->PaddingL;
202         y = Element->CachedY + Element->PaddingT;
203         
204         // Update each child
205         for(child = Element->FirstChild; child; child = child->NextSibling)
206         {
207                  int    newX, newY;
208                 // Ignore elements that will not be rendered
209                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
210
211                 newX = x; newY = y;
212                 
213                 // Handle alignment
214                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
215                         if(Element->Flags & ELEFLAG_VERTICAL)
216                                 newX += Element->CachedW/2 - child->CachedW/2;
217                         else
218                                 newY += Element->CachedH/2 - child->CachedH/2;
219                 }
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;
224                         else
225                                 newY += Element->CachedH - child->CachedH
226                                         - Element->PaddingT - Element->PaddingB;
227                 }
228
229                 // Check for changes, and don't update if there was no change
230                 if( newX != child->CachedX || newY != child->CachedY )
231                 {
232                         child->CachedX = newX;
233                         child->CachedY = newY;
234                         // Update child's children positions
235                         Widget_UpdatePosition(child);
236                 }
237                 
238                 // Increment
239                 if(Element->Flags & ELEFLAG_VERTICAL ) {
240                         y += child->CachedH + Element->GapSize;
241                 }
242                 else {
243                         x += child->CachedW + Element->GapSize;
244                 }
245         }
246 }
247
248
249 // --- Helpers ---
250 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
251 {
252         tElement        *ele;
253
254         if(ID == -1)
255                 return &Info->RootElement;
256         
257         if( ID < Info->TableSize )      return Info->ElementTable[ID];
258
259         ele = Info->ElementTable[ID % Info->TableSize];
260         while(ele && ele->ID != ID)     ele = ele->ListNext;
261         return ele;
262 }
263
264 // --- Message Handlers ---
265 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
266 {
267         const int       max_debugname_len = Len - sizeof(tWidgetMsg_Create);
268         tElement        *parent, *new;
269
270         // Sanity check
271         if( Len < sizeof(tWidgetMsg_Create) )
272                 return ;
273         if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
274                 return ;
275         
276         // Create
277         parent = Widget_GetElementById(Info, Msg->Parent);
278         if(!parent)
279         {
280                 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
281                 return ;
282         }
283
284         // Check if the ID is already in use
285         if( Widget_GetElementById(Info, Msg->NewID) )
286                 return ;
287
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;
294         new->PaddingT = 2;
295         new->PaddingB = 2;
296         new->PaddingL = 2;
297         new->PaddingR = 2;
298         
299         // Add to parent's list
300         if(parent->LastChild)
301                 parent->LastChild->NextSibling = new;
302         else
303                 parent->FirstChild = new;
304         new->NextSibling = parent->LastChild;
305         parent->LastChild = new;
306
307         // Add to info
308         {
309                 tElement        *ele, *prev = NULL;
310                 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
311                 if(prev)
312                         prev->ListNext = new;
313                 else
314                         Info->ElementTable[new->ID % Info->TableSize] = new;
315         }
316 }
317
318 void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg)
319 {
320         tElement        *ele;
321         
322         if( Len < sizeof(tWidgetMsg_SetSize) )
323                 return ;
324         
325         ele = Widget_GetElementById(Info, Msg->WidgetID);
326         if(!ele)        return ;
327         
328         ele->FixedWith = Msg->Value;
329 }
330
331 void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg)
332 {
333         
334 }
335
336 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data)
337 {
338         tWidgetWin      *info = Target->RendererInfo;
339         switch(Msg)
340         {
341         case WNDMSG_RESIZE: {
342                 struct sWndMsg_Resize   *msg = Data;
343                 if(Len < sizeof(*msg))  return -1;              
344
345                 info->RootElement.CachedW = msg->W;             
346                 info->RootElement.CachedH = msg->H;
347                 return 0; }
348
349         // New Widget
350         case MSG_WIDGET_CREATE:
351                 Widget_NewWidget(info, Len, Data);
352                 return 0;
353         
354         // Set length
355         case MSG_WIDGET_SETSIZE:
356                 Widget_SetSize(info, Len, Data);
357                 return 0;
358         // Set text
359         case MSG_WIDGET_SETTEXT:
360                 Widget_SetText(info, Len, Data);
361                 return 0;
362         
363         // 
364         default:
365                 return 1;       // Unhandled, pass to user
366         }
367 }
368

UCC git Repository :: git.ucc.asn.au