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

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