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

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