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

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