f3de26f573c88499b2444a1ff85d2723371f16d8
[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       0
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          int    bVertical = Element->Flags & ELEFLAG_VERTICAL;
165
166         // Check if this element can have children
167         if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
168                 return ;
169         
170         // Pass 1
171         // - Get the fixed and minimum sizes of the element
172         for( child = Element->FirstChild; child; child = child->NextSibling )
173         {
174                  int    minWith  = bVertical ? child->MinH : child->MinW;
175                  int    minCross = bVertical ? child->MinW : child->MinH;
176
177                 // Ignore elements that will not be rendered
178                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
179                 
180                 // Absolutely positioned elements don't affect dimensions
181                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue ;
182         
183                 // Fixed width elements 
184                 if( child->FixedWith )
185                 {
186                         nFixed ++;
187                         fixedSize += child->FixedWith;
188                 }
189                 else if( child->Flags & ELEFLAG_NOSTRETCH )
190                 {
191                         nFixed ++;
192                         fixedSize += minWith;
193                 }
194                 
195                 if( maxCross < child->FixedCross )      maxCross = child->FixedCross;
196                 if( maxCross < minCross )       maxCross = minCross;
197                 nChildren ++;
198         }
199
200         // Get the dynamic with size from the unused space in the element
201         if( nChildren > nFixed ) {
202                 if( bVertical )
203                         dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
204                 else
205                         dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
206                 dynWith -= fixedSize;
207                 dynWith -= Element->GapSize * (nChildren-1);
208                 if( dynWith < 0 )       return ;
209                 dynWith /= nChildren - nFixed;
210         }
211         else {
212                 dynWith = 0;
213         }
214         
215         // Get the cross size
216         if( bVertical )
217                 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
218         else
219                 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
220
221         _SysDebug("%i (p=%i) - WxH=%ix%i",
222                 Element->ID, (Element->Parent ? Element->Parent->ID : -1),
223                 Element->CachedW, Element->CachedH
224                 );
225         _SysDebug("  %s dynWith = %i, fullCross = %i",
226                 (Element->Flags & ELEFLAG_VERTICAL ? "Vert" : "Horiz"),
227                 dynWith, fullCross
228                 );
229         
230         // Pass 2 - Set sizes and recurse
231         for( child = Element->FirstChild; child; child = child->NextSibling )
232         {
233                  int    w, h;
234
235                 // Ignore elements that will not be rendered
236                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
237                 // Don't resize floating elements
238                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue ;
239                 
240                 // --- Width ---
241                 if( child->Flags & ELEFLAG_NOEXPAND )
242                         w = child->MinW;
243                 else if( bVertical )
244                         w = child->FixedCross ? child->FixedCross : fullCross;
245                 else
246                         w = child->FixedWith ? child->FixedWith : dynWith;
247         
248                 // --- Height ---
249                 if( child->Flags & ELEFLAG_NOSTRETCH )
250                         h = child->MinH;
251                 else if( bVertical )
252                         h = child->FixedWith ? child->FixedWith : dynWith;
253                 else
254                         h = child->FixedCross ? child->FixedCross : fullCross;
255
256                 if(w < child->MinW)     w = child->MinW;
257                 if(h < child->MinH)     h = child->MinH;
258         
259                 _SysDebug("Child %ix%i (min %ix%i)", w, h, child->MinW, child->MinH);
260         
261                 // Update the dimensions if they have changed
262                 if( child->CachedW == w && child->CachedH == h )
263                         continue ;
264                 child->CachedW = w;
265                 child->CachedH = h;
266                 
267                 // Force the positions of child elements to be recalculated
268                 child->CachedX = -1;
269         
270                 // Recurse down so the child elements can be updated    
271                 Widget_UpdateDimensions(child);
272         }
273         
274 }
275
276 /**
277  * \brief Update the position of child elements
278  */
279 void Widget_UpdatePosition(tElement *Element)
280 {
281         tElement        *child;
282          int    x, y;
283         
284         if( Element->Flags & ELEFLAG_NORENDER ) return ;
285
286         // Check if this element can have children
287         if( (gaWM_WidgetTypes[Element->Type]->Flags & WIDGETTYPE_FLAG_NOCHILDREN) )
288                 return ;
289
290 //      _SysDebug("Widget_UpdatePosition: (Element=%p(%i Type=%i Flags=0x%x))",
291 //              Element, Element->ID, Element->Type, Element->Flags);
292         
293         // Initialise
294         x = Element->CachedX + Element->PaddingL;
295         y = Element->CachedY + Element->PaddingT;
296         
297         // Update each child
298         for(child = Element->FirstChild; child; child = child->NextSibling)
299         {
300                  int    newX, newY;
301                 // Ignore elements that will not be rendered
302                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
303
304                 newX = x; newY = y;
305                 
306                 // Handle alignment (across parent)
307                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
308                         if(Element->Flags & ELEFLAG_VERTICAL)
309                                 newX += Element->CachedW/2 - child->CachedW/2;
310                         else
311                                 newY += Element->CachedH/2 - child->CachedH/2;
312                 }
313                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
314                         if(Element->Flags & ELEFLAG_VERTICAL )
315                                 newX += Element->CachedW - child->CachedW
316                                         - Element->PaddingL - Element->PaddingR;
317                         else
318                                 newY += Element->CachedH - child->CachedH
319                                         - Element->PaddingT - Element->PaddingB;
320                 }
321
322 //              _SysDebug(" Widget_UpdatePosition[%i]: newX = %i, newY = %i", Element->ID, newX, newY);
323
324                 // Check for changes, and don't update if there was no change
325                 if( newX != child->CachedX || newY != child->CachedY )
326                 {
327                         child->CachedX = newX;
328                         child->CachedY = newY;
329                         // Update child's children positions
330                         Widget_UpdatePosition(child);
331                 }
332                 
333                 // Increment
334                 if(Element->Flags & ELEFLAG_VERTICAL ) {
335                         y += child->CachedH + Element->GapSize;
336                 }
337                 else {
338                         x += child->CachedW + Element->GapSize;
339                 }
340         }
341 }
342
343 /**
344  * \brief Update the minimum dimensions of the element
345  * \note Called after a child's minimum dimensions have changed
346  */
347 void Widget_UpdateMinDims(tElement *Element)
348 {
349         tElement        *child;
350          int    minW, minH;
351          int    nChildren;
352         
353         if(!Element)    return;
354         
355         minW = 0;
356         minH = 0;
357         nChildren = 0;
358         
359         for(child = Element->FirstChild; child; child = child->NextSibling)
360         {
361                  int    cross;
362                 
363                 if(Element->Flags & ELEFLAG_NORENDER)   continue ;
364                 
365                 if( (Element->Flags & ELEFLAG_VERTICAL) )
366                 {
367                         cross = child->FixedCross ? child->FixedCross : child->MinW;
368                         if(minW < cross)        minW = cross;
369                         minH += child->FixedWith  ? child->FixedWith  : child->MinH;
370                 }
371                 else
372                 {
373                         cross = child->FixedCross ? child->FixedCross : child->MinH;
374                         minW += child->FixedWith  ? child->FixedWith  : child->MinW;
375                         if(minH < cross)        minH = cross;
376                 }
377 //              _SysDebug("%i/%i cross = %i", Element->ID, child->ID, cross);
378         
379                 nChildren ++;
380         }
381         
382         if( Element->Flags & ELEFLAG_VERTICAL )
383                 minH += (nChildren - 1) * Element->GapSize;
384         else
385                 minW += (nChildren - 1) * Element->GapSize;
386
387         Element->MinW = Element->PaddingL + minW + Element->PaddingR;
388         Element->MinH = Element->PaddingT + minH + Element->PaddingB;
389
390         // Recurse upwards
391         Widget_UpdateMinDims(Element->Parent);
392 }
393
394 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
395 {
396         tElement        *ret, *next, *ele;
397         
398         next = &Info->RootElement;
399         while(next)
400         {
401                 ret = next;
402                 next = NULL;
403                 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
404                 {
405                         if(ele->Flags & ELEFLAG_NORENDER)       continue;
406                         if(X < ele->CachedX)    continue;
407                         if(Y < ele->CachedY)    continue;
408                         if(X >= ele->CachedX + ele->CachedW)    continue;
409                         if(Y >= ele->CachedY + ele->CachedH)    continue;
410                         next = ele;
411                 }
412         }
413         return ret;
414 }
415
416 // --- Helpers ---
417 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
418 {
419         tElement        *ele;
420
421         if( ID == -1 )  return &Info->RootElement;
422         
423         if( ID < Info->TableSize )      return Info->ElementTable[ID];
424
425         ele = Info->ElementTable[ID % Info->TableSize];
426         while(ele && ele->ID != ID)     ele = ele->ListNext;
427         return ele;
428 }
429
430 // --- Message Handlers ---
431 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
432 {
433         const int       max_debugname_len = Len - sizeof(tWidgetMsg_Create);
434         tElement        *parent, *new;
435
436         // Sanity check
437         if( Len < sizeof(*Msg) )
438                 return ;
439         if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
440                 return ;
441         
442         _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
443                 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
444         
445         if(Msg->Type >= ciWM_NumWidgetTypes)
446         {
447                 _SysDebug("Widget_NewWidget - Bad widget type %i", Msg->Type);
448                 return ;
449         }
450
451         // Create
452         parent = Widget_GetElementById(Info, Msg->Parent);
453         if(!parent)
454         {
455                 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
456                 return ;
457         }
458
459         // Check if the ID is already in use
460         if( Widget_GetElementById(Info, Msg->NewID) )
461                 return ;
462
463         // Create new element
464         new = calloc(sizeof(tElement), 1);
465         new->Window = parent->Window;
466         new->ID = Msg->NewID;
467         new->Type = Msg->Type;
468         new->Parent = parent;
469         new->Flags = Msg->Flags;
470         new->PaddingT = 2;
471         new->PaddingB = 2;
472         new->PaddingL = 2;
473         new->PaddingR = 2;
474         new->CachedX = -1;
475         
476         if( gaWM_WidgetTypes[new->Type]->Init )
477                 gaWM_WidgetTypes[new->Type]->Init(new);
478         
479         // Add to parent's list
480         if(parent->LastChild)
481                 parent->LastChild->NextSibling = new;
482         else
483                 parent->FirstChild = new;
484         parent->LastChild = new;
485
486         // Add to info
487         {
488                 tElement        *ele, *prev = NULL;
489                 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
490                 if(prev)
491                         prev->ListNext = new;
492                 else
493                         Info->ElementTable[new->ID % Info->TableSize] = new;
494         }
495
496         Widget_UpdateMinDims(parent);
497 }
498
499 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
500 {
501         tElement        *ele;
502         
503         if( Len < sizeof(*Msg) )
504                 return ;
505
506         _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
507         
508         ele = Widget_GetElementById(Info, Msg->WidgetID);
509         if(!ele)        return;
510
511         ele->Flags &= ~Msg->Mask;
512         ele->Flags |= Msg->Value & Msg->Mask;
513 }
514
515 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
516 {
517         tElement        *ele;
518         
519         if( Len < sizeof(*Msg) )
520                 return ;
521         
522         ele = Widget_GetElementById(Info, Msg->WidgetID);
523         if(!ele)        return ;
524         
525         ele->FixedWith = Msg->Value;
526 }
527
528 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
529 {
530         tElement        *ele;
531         
532         if( Len < sizeof(*Msg) + 1 )
533                 return ;
534         if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
535                 return ;
536
537         ele = Widget_GetElementById(Info, Msg->WidgetID);
538         if(!ele)        return ;
539
540
541         if( gaWM_WidgetTypes[ele->Type]->UpdateText )
542         {
543                 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
544         }
545 //      else
546 //      {
547 //              if(ele->Text)   free(ele->Text);
548 //              ele->Text = strdup(Msg->Text);
549 //      }
550 }
551
552 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
553 {
554         tWidgetWin      *info = Target->RendererInfo;
555         switch(Msg)
556         {
557         case WNDMSG_RESIZE: {
558                 const struct sWndMsg_Resize     *msg = Data;
559                 if(Len < sizeof(*msg))  return -1;              
560
561                 info->RootElement.CachedW = msg->W;             
562                 info->RootElement.CachedH = msg->H;
563                 
564                 // TODO: Update dimensions of all child elements?
565                 
566                 return 0; }
567
568         case WNDMSG_MOUSEMOVE: {
569 //              _SysDebug("TODO: Support widget mouse move events");
570                 return 0; }
571
572         case WNDMSG_MOUSEBTN: {
573                 const struct sWndMsg_MouseButton        *msg = Data;
574                 tWidgetMsg_MouseBtn     client_msg;
575                 tElement        *ele;
576                  int    x, y;
577                  int    rv;
578                 
579                 if(Len < sizeof(*msg))  return -1;
580
581                 x = msg->X; y = msg->Y;
582                 client_msg.Button = msg->Button;
583                 client_msg.bPressed = msg->bPressed;
584
585                 ele = Widget_GetElementByPos(info, x, y);
586                 // Send event to all elements from `ele` upwards
587                 for( ; ele; ele = ele->Parent )
588                 {
589                         if(gaWM_WidgetTypes[ele->Type]->MouseButton)
590                         {
591                                 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
592                                         ele,
593                                         x - ele->CachedX, y - ele->CachedY,
594                                         msg->Button, msg->bPressed
595                                         );
596                                 // Allow a type to trap the input from going any higher
597                                 if(rv == 0)     break;
598                         }
599                         else
600                         {
601                                 // Pass to user
602                                 client_msg.X = x - ele->CachedX;
603                                 client_msg.Y = y - ele->CachedY;
604                                 client_msg.WidgetID = ele->ID;
605                                 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
606                         }
607                 }
608                 return 0; }
609
610         // New Widget
611         case MSG_WIDGET_CREATE:
612                 Widget_NewWidget(info, Len, Data);
613                 return 0;
614
615         case MSG_WIDGET_DELETE:
616                 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
617                 return 0;
618
619         // Set Flags
620         case MSG_WIDGET_SETFLAGS:
621                 Widget_SetFlags(info, Len, Data);
622                 return 0;
623         
624         // Set length
625         case MSG_WIDGET_SETSIZE:
626                 Widget_SetSize(info, Len, Data);
627                 return 0;
628         
629         // Set text
630         case MSG_WIDGET_SETTEXT:
631                 Widget_SetText(info, Len, Data);
632                 return 0;
633         
634         // 
635         default:
636                 return 1;       // Unhandled, pass to user
637         }
638 }
639
640 void Widget_Fire(tElement *Element)
641 {
642         tWidgetMsg_Fire msg;
643         msg.WidgetID = Element->ID;
644         WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);
645 }
646

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