92972d679604a33653b123890ec28586ab0b8d9d
[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 #include "include/image.h"
15
16 #define DEFAULT_ELETABLE_SIZE   64
17
18 // === PROTOTYPES ===
19  int    Renderer_Widget_Init(void);
20 tWindow *Renderer_Widget_Create(int Flags);
21 void    Renderer_Widget_Redraw(tWindow *Window);
22
23 void    Widget_RenderWidget(tWindow *Window, tElement *Element);
24 void    Widget_UpdateDimensions(tElement *Element);
25 void    Widget_UpdatePosition(tElement *Element);
26 // --- Messages
27 tElement        *Widget_GetElementById(tWidgetWin *Info, uint32_t ID);
28 void    Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg);
29 void    Widget_SetFlags(tWidgetWin *Info, int Len, tWidgetMsg_SetFlags *Msg);
30 void    Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg);
31 void    Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg);
32  int    Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data);
33 // --- Type helpers
34 void    Widget_TextBox_UpdateText(tElement *Element, const char *Text);
35 void    Widget_Image_UpdateText(tElement *Element, const char *Text);
36
37 // === GLOBALS ===
38 tWMRenderer     gRenderer_Widget = {
39         .Name = "Widget",
40         .CreateWindow = Renderer_Widget_Create,
41         .Redraw = Renderer_Widget_Redraw,
42         .HandleMessage = Renderer_Widget_HandleMessage
43 };
44         
45 // --- Element type flags
46 struct {
47         void    (*Init)(tElement *Ele);
48         void    (*Delete)(tElement *Ele);
49         void    (*UpdateFlags)(tElement *Ele);
50         void    (*UpdateSize)(tElement *Ele);
51         void    (*UpdateText)(tElement *Ele, const char *Text); // This should update Ele->Text
52 }       gaWM_WidgetTypes[NUM_ELETYPES] = {
53         {NULL, NULL, NULL, NULL, NULL}, // NULL
54         {NULL, NULL, NULL, NULL, NULL}, // Box
55         {NULL, NULL, NULL, NULL, Widget_TextBox_UpdateText},    // Text
56         {NULL, NULL, NULL, NULL, Widget_Image_UpdateText},      // Image
57         {NULL, NULL, NULL, NULL, NULL}  // Button
58 };
59 const int       ciWM_NumWidgetTypes = sizeof(gaWM_WidgetTypes)/sizeof(gaWM_WidgetTypes[0]);
60
61 // === CODE ===
62 int Renderer_Widget_Init(void)
63 {
64         WM_RegisterRenderer(&gRenderer_Widget); 
65
66         return 0;
67 }
68
69 tWindow *Renderer_Widget_Create(int Flags)
70 {
71         tWindow *ret;
72         tWidgetWin      *info;
73          int    eletable_size = DEFAULT_ELETABLE_SIZE;
74
75         _SysDebug("Renderer_Widget_Create: (Flags = 0x%x)", Flags);
76
77         // TODO: Use `Flags` as default element count?
78         // - Actaully, it's taken by the root ele flags
79         // - Use the upper bits?
80
81         ret = WM_CreateWindowStruct( sizeof(tWidgetWin) + sizeof(tElement*)*eletable_size );
82         info = ret->RendererInfo;
83         
84         info->TableSize = eletable_size;
85         info->RootElement.ID = -1;
86         info->RootElement.BackgroundColour = 0xCCCCCC;
87         info->RootElement.Flags = Flags;
88         
89         return ret;
90 }
91
92 void Renderer_Widget_Redraw(tWindow *Window)
93 {
94         tWidgetWin      *info = Window->RendererInfo;
95         WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
96
97         Widget_UpdateDimensions(&info->RootElement);
98         Widget_UpdatePosition(&info->RootElement);
99
100         Widget_RenderWidget(Window, &info->RootElement);
101 }
102
103 // --- Render / Resize ---
104 void Widget_RenderWidget(tWindow *Window, tElement *Element)
105 {
106         tElement        *child;
107         
108         if( Element->Flags & ELEFLAG_NORENDER ) return ;
109         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
110         
111         Widget_Decorator_RenderWidget(Window, Element);
112         
113         for(child = Element->FirstChild; child; child = child->NextSibling)
114         {
115                 Widget_RenderWidget(Window, child);
116         }
117 }
118
119 void Widget_UpdateDimensions(tElement *Element)
120 {
121         tElement        *child;
122          int    nChildren = 0;
123          int    nFixed = 0;
124          int    maxCross = 0;
125          int    fixedSize = 0;
126          int    fullCross, dynWith;
127         
128         // Pass 1
129         // - Get the fixed and minimum sizes of the element
130         for( child = Element->FirstChild; child; child = child->NextSibling )
131         {
132                 // Ignore elements that will not be rendered
133                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
134                 
135                 // Absolutely positioned elements don't affect dimensions
136                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue ;
137         
138                 // Fixed width elements 
139                 if( child->FixedWith )
140                 {
141                         nFixed ++;
142                         fixedSize += child->FixedWith;
143                 }
144                 else if( child->Flags & ELEFLAG_NOSTRETCH )
145                 {
146                         nFixed ++;
147                         fixedSize += child->MinWith;
148                 }
149                 
150                 if( child->FixedCross && maxCross < child->FixedCross )
151                         maxCross = child->FixedCross;
152                 if( child->MinCross && maxCross < child->MinCross )
153                         maxCross = child->MinCross;
154                 nChildren ++;
155         }
156
157         // Get the dynamic with size from the unused space in the element
158         if( nChildren > nFixed ) {
159                 if( Element->Flags & ELEFLAG_VERTICAL )
160                         dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
161                 else
162                         dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
163                 dynWith -= fixedSize;
164                 if( dynWith < 0 )       return ;
165                 dynWith /= nChildren - nFixed;
166         }
167         
168         _SysDebug("%i - nChildren = %i, nFixed = %i, dynWith = %i, fixedSize = %i",
169                 Element->ID, nChildren, nFixed, dynWith, fixedSize);
170
171         // Get the cross size
172         if( Element->Flags & ELEFLAG_VERTICAL )
173                 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
174         else
175                 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
176         
177         // Pass 2 - Set sizes and recurse
178         for( child = Element->FirstChild; child; child = child->NextSibling )
179         {
180                  int    cross, with;
181
182                 // Ignore elements that will not be rendered
183                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
184                 
185                 // --- Cross Size ---
186                 // TODO: Expand to fill?
187                 // TODO: Extra flag so options are (Expand, Equal, Wrap)
188                 if( child->FixedCross )
189                         cross = child->FixedCross;
190                 else if( child->Flags & ELEFLAG_NOEXPAND )
191                         cross = child->MinCross;
192                 else
193                         cross = fullCross;
194                 
195                 // --- With Size ---
196                 if( child->FixedWith )
197                         with = child->FixedWith;
198                 else if( child->Flags & ELEFLAG_NOSTRETCH )
199                         with = child->MinWith;
200                 else
201                         with = dynWith;
202         
203
204                 if(with < child->MinWith)       with = child->MinWith;
205                 if(cross < child->MinCross)     cross = child->MinCross;
206                 
207                 // Update the dimensions if they have changed
208                 if( Element->Flags & ELEFLAG_VERTICAL ) {
209                         // If no change, don't recurse
210                         if( child->CachedW == cross && child->CachedH == with )
211                                 continue ;
212                         child->CachedW = cross;
213                         child->CachedH = with;
214                 }
215                 else {
216                         // If no change, don't recurse
217                         if( child->CachedW == with && child->CachedH == cross )
218                                 continue ;
219                         child->CachedW = with;
220                         child->CachedH = cross;
221                 }
222                 
223                 // Force the positions of child elements to be recalculated
224                 child->CachedX = -1;
225         
226                 // Recurse down so the child elements can be updated    
227                 Widget_UpdateDimensions(child);
228         }
229         
230 }
231
232 /**
233  * \brief Update the position of child elements
234  */
235 void Widget_UpdatePosition(tElement *Element)
236 {
237         tElement        *child;
238          int    x, y;
239         
240         if( Element->Flags & ELEFLAG_NORENDER ) return ;
241
242         _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
243                 Element, Element->ID, Element->Type, Element->Flags);
244         
245         // Initialise
246         x = Element->CachedX + Element->PaddingL;
247         y = Element->CachedY + Element->PaddingT;
248         
249         // Update each child
250         for(child = Element->FirstChild; child; child = child->NextSibling)
251         {
252                  int    newX, newY;
253                 // Ignore elements that will not be rendered
254                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
255
256                 newX = x; newY = y;
257                 
258                 // Handle alignment
259                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
260                         if(Element->Flags & ELEFLAG_VERTICAL)
261                                 newX += Element->CachedW/2 - child->CachedW/2;
262                         else
263                                 newY += Element->CachedH/2 - child->CachedH/2;
264                 }
265                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
266                         if(Element->Flags & ELEFLAG_VERTICAL )
267                                 newX += Element->CachedW - child->CachedW
268                                         - Element->PaddingL - Element->PaddingR;
269                         else
270                                 newY += Element->CachedH - child->CachedH
271                                         - Element->PaddingT - Element->PaddingB;
272                 }
273
274                 _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
275
276                 // Check for changes, and don't update if there was no change
277                 if( newX != child->CachedX || newY != child->CachedY )
278                 {
279                         child->CachedX = newX;
280                         child->CachedY = newY;
281                         // Update child's children positions
282                         Widget_UpdatePosition(child);
283                 }
284                 
285                 // Increment
286                 if(Element->Flags & ELEFLAG_VERTICAL ) {
287                         y += child->CachedH + Element->GapSize;
288                 }
289                 else {
290                         x += child->CachedW + Element->GapSize;
291                 }
292         }
293 }
294
295 /**
296  * \brief Update the minimum dimensions of the element
297  * \note Called after a child's minimum dimensions have changed
298  */
299 void Widget_UpdateMinDims(tElement *Element)
300 {
301         tElement        *child;
302         
303         if(!Element)    return;
304         
305         Element->MinCross = 0;
306         Element->MinWith = 0;
307         
308         for(child = Element->FirstChild; child; child = child->NextSibling)
309         {
310                 if( Element->Parent &&
311                         (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
312                         )
313                 {
314                         if(child->FixedCross)
315                                 Element->MinCross += child->FixedCross;
316                         else
317                                 Element->MinCross += child->MinCross;
318                         if(child->FixedWith)
319                                 Element->MinWith += child->FixedWith;
320                         else
321                                 Element->MinWith += child->MinWith;
322                 }
323                 else
324                 {
325                         if(child->FixedCross)
326                                 Element->MinWith += child->FixedCross;
327                         else
328                                 Element->MinWith += child->MinCross;
329                         if(child->FixedWith)
330                                 Element->MinCross += child->FixedWith;
331                         else
332                                 Element->MinCross += child->MinWith;
333                 }
334         }
335         
336         // Recurse upwards
337         Widget_UpdateMinDims(Element->Parent);
338 }
339
340
341 // --- Helpers ---
342 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
343 {
344         tElement        *ele;
345
346         if( ID == -1 )  return &Info->RootElement;
347         
348         if( ID < Info->TableSize )      return Info->ElementTable[ID];
349
350         ele = Info->ElementTable[ID % Info->TableSize];
351         while(ele && ele->ID != ID)     ele = ele->ListNext;
352         return ele;
353 }
354
355 // --- Message Handlers ---
356 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
357 {
358         const int       max_debugname_len = Len - sizeof(tWidgetMsg_Create);
359         tElement        *parent, *new;
360
361         // Sanity check
362         if( Len < sizeof(tWidgetMsg_Create) )
363                 return ;
364         if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
365                 return ;
366         
367         _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
368                 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
369         
370         // Create
371         parent = Widget_GetElementById(Info, Msg->Parent);
372         if(!parent)
373         {
374                 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
375                 return ;
376         }
377
378         // Check if the ID is already in use
379         if( Widget_GetElementById(Info, Msg->NewID) )
380                 return ;
381
382         // Create new element
383         new = calloc(sizeof(tElement), 1);
384         new->ID = Msg->NewID;
385         new->Type = Msg->Type;
386         new->Parent = parent;
387         new->Flags = Msg->Flags;
388         new->PaddingT = 2;
389         new->PaddingB = 2;
390         new->PaddingL = 2;
391         new->PaddingR = 2;
392         new->CachedX = -1;
393         
394         if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type].Init )
395                 gaWM_WidgetTypes[new->Type].Init(new);
396         
397         // Add to parent's list
398         if(parent->LastChild)
399                 parent->LastChild->NextSibling = new;
400         else
401                 parent->FirstChild = new;
402         parent->LastChild = new;
403
404         // Add to info
405         {
406                 tElement        *ele, *prev = NULL;
407                 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
408                 if(prev)
409                         prev->ListNext = new;
410                 else
411                         Info->ElementTable[new->ID % Info->TableSize] = new;
412         }
413
414         Widget_UpdateMinDims(parent);
415 }
416
417 void Widget_SetFlags(tWidgetWin *Info, int Len, tWidgetMsg_SetFlags *Msg)
418 {
419         tElement        *ele;
420         
421         if( Len < sizeof(tWidgetMsg_SetFlags) )
422                 return ;
423
424         _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
425         
426         ele = Widget_GetElementById(Info, Msg->WidgetID);
427         if(!ele)        return;
428
429         Msg->Value &= Msg->Mask;
430         
431         ele->Flags &= ~Msg->Mask;
432         ele->Flags |= Msg->Value;
433 }
434
435 void Widget_SetSize(tWidgetWin *Info, int Len, tWidgetMsg_SetSize *Msg)
436 {
437         tElement        *ele;
438         
439         if( Len < sizeof(tWidgetMsg_SetSize) )
440                 return ;
441         
442         ele = Widget_GetElementById(Info, Msg->WidgetID);
443         if(!ele)        return ;
444         
445         ele->FixedWith = Msg->Value;
446 }
447
448 void Widget_SetText(tWidgetWin *Info, int Len, tWidgetMsg_SetText *Msg)
449 {
450         tElement        *ele;
451         
452         if( Len < sizeof(tWidgetMsg_SetText) + 1 )
453                 return ;
454         if( Msg->Text[Len - sizeof(tWidgetMsg_SetText) - 1] != '\0' )
455                 return ;
456
457         ele = Widget_GetElementById(Info, Msg->WidgetID);
458         if(!ele)        return ;
459
460
461         if( ele->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[ele->Type].UpdateText )
462         {
463                 gaWM_WidgetTypes[ele->Type].UpdateText( ele, Msg->Text );
464         }
465 //      else
466 //      {
467 //              if(ele->Text)   free(ele->Text);
468 //              ele->Text = strdup(Msg->Text);
469 //      }
470 }
471
472 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data)
473 {
474         tWidgetWin      *info = Target->RendererInfo;
475         switch(Msg)
476         {
477         case WNDMSG_RESIZE: {
478                 struct sWndMsg_Resize   *msg = Data;
479                 if(Len < sizeof(*msg))  return -1;              
480
481                 info->RootElement.CachedW = msg->W;             
482                 info->RootElement.CachedH = msg->H;
483                 return 0; }
484
485         // New Widget
486         case MSG_WIDGET_CREATE:
487                 Widget_NewWidget(info, Len, Data);
488                 return 0;
489
490         case MSG_WIDGET_DELETE:
491                 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
492                 return 0;
493
494         // Set Flags
495         case MSG_WIDGET_SETFLAGS:
496                 Widget_SetFlags(info, Len, Data);
497                 return 0;
498         
499         // Set length
500         case MSG_WIDGET_SETSIZE:
501                 Widget_SetSize(info, Len, Data);
502                 return 0;
503         
504         // Set text
505         case MSG_WIDGET_SETTEXT:
506                 Widget_SetText(info, Len, Data);
507                 return 0;
508         
509         // 
510         default:
511                 return 1;       // Unhandled, pass to user
512         }
513 }
514
515 // --- Type Helpers
516 void Widget_TextBox_UpdateText(tElement *Element, const char *Text)
517 {
518          int    w=0, h=0;
519
520         if(Element->Text)       free(Element->Text);
521         Element->Text = strdup(Text);
522
523         WM_Render_GetTextDims(NULL, Element->Text, &w, &h);
524         if(Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL)) {
525                 Element->MinCross = w;
526                 Element->MinWith = h;
527         }
528         else {
529                 Element->MinWith = w;
530                 Element->MinCross = h;
531         }
532
533         Widget_UpdateMinDims(Element->Parent);
534 }
535
536 void Widget_Image_UpdateText(tElement *Element, const char *Text)
537 {
538         if(Element->Data)       free(Element->Data);
539         Element->Data = Image_Load( Text );
540         if(!Element->Data) {
541 //              Element->Flags &= ~ELEFLAG_FIXEDSIZE;
542                 return ;
543         }
544         
545         Element->CachedW = ((tImage*)Element->Data)->Width;
546         Element->CachedH = ((tImage*)Element->Data)->Height;
547         
548         if(Element->Parent && (Element->Parent->Flags & ELEFLAG_VERTICAL) ) {
549                 Element->MinCross = ((tImage*)Element->Data)->Width;
550                 Element->MinWith = ((tImage*)Element->Data)->Height;
551         }
552         else {
553                 Element->MinWith = ((tImage*)Element->Data)->Width;
554                 Element->MinCross = ((tImage*)Element->Data)->Height;
555         }
556
557         Widget_UpdateMinDims(Element->Parent);
558         
559         // NOTE: Doesn't update Element->Text because it's useless
560 }
561

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