Usermode/AxWin3 - Splitting widget types out into separate files
[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         
323         if(!Element)    return;
324         
325         Element->MinCross = 0;
326         Element->MinWith = 0;
327         
328         for(child = Element->FirstChild; child; child = child->NextSibling)
329         {
330                 if( Element->Parent &&
331                         (Element->Flags & ELEFLAG_VERTICAL) == (Element->Parent->Flags & ELEFLAG_VERTICAL)
332                         )
333                 {
334                         if(child->FixedCross)
335                                 Element->MinCross += child->FixedCross;
336                         else
337                                 Element->MinCross += child->MinCross;
338                         if(child->FixedWith)
339                                 Element->MinWith += child->FixedWith;
340                         else
341                                 Element->MinWith += child->MinWith;
342                 }
343                 else
344                 {
345                         if(child->FixedCross)
346                                 Element->MinWith += child->FixedCross;
347                         else
348                                 Element->MinWith += child->MinCross;
349                         if(child->FixedWith)
350                                 Element->MinCross += child->FixedWith;
351                         else
352                                 Element->MinCross += child->MinWith;
353                 }
354         }
355         
356         // Recurse upwards
357         Widget_UpdateMinDims(Element->Parent);
358 }
359
360 tElement *Widget_GetElementByPos(tWidgetWin *Info, int X, int Y)
361 {
362         tElement        *ret, *next, *ele;
363         
364         next = &Info->RootElement;
365         while(next)
366         {
367                 ret = next;
368                 next = NULL;
369                 for(ele = ret->FirstChild; ele; ele = ele->NextSibling)
370                 {
371                         if(ele->Flags & ELEFLAG_NORENDER)       continue;
372                         if(X < ele->CachedX)    continue;
373                         if(Y < ele->CachedY)    continue;
374                         if(X >= ele->CachedX + ele->CachedW)    continue;
375                         if(Y >= ele->CachedY + ele->CachedH)    continue;
376                         next = ele;
377                 }
378         }
379         return ret;
380 }
381
382 // --- Helpers ---
383 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
384 {
385         tElement        *ele;
386
387         if( ID == -1 )  return &Info->RootElement;
388         
389         if( ID < Info->TableSize )      return Info->ElementTable[ID];
390
391         ele = Info->ElementTable[ID % Info->TableSize];
392         while(ele && ele->ID != ID)     ele = ele->ListNext;
393         return ele;
394 }
395
396 // --- Message Handlers ---
397 void Widget_NewWidget(tWidgetWin *Info, size_t Len, const tWidgetMsg_Create *Msg)
398 {
399         const int       max_debugname_len = Len - sizeof(tWidgetMsg_Create);
400         tElement        *parent, *new;
401
402         // Sanity check
403         if( Len < sizeof(*Msg) )
404                 return ;
405         if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
406                 return ;
407         
408         _SysDebug("Widget_NewWidget (%i %i Type %i Flags 0x%x)",
409                 Msg->Parent, Msg->NewID, Msg->Type, Msg->Flags);
410         
411         // Create
412         parent = Widget_GetElementById(Info, Msg->Parent);
413         if(!parent)
414         {
415                 _SysDebug("Widget_NewWidget - Bad parent ID %i", Msg->Parent);
416                 return ;
417         }
418
419         // Check if the ID is already in use
420         if( Widget_GetElementById(Info, Msg->NewID) )
421                 return ;
422
423         // Create new element
424         new = calloc(sizeof(tElement), 1);
425         new->Window = parent->Window;
426         new->ID = Msg->NewID;
427         new->Type = Msg->Type;
428         new->Parent = parent;
429         new->Flags = Msg->Flags;
430         new->PaddingT = 2;
431         new->PaddingB = 2;
432         new->PaddingL = 2;
433         new->PaddingR = 2;
434         new->CachedX = -1;
435         
436         if( new->Type < ciWM_NumWidgetTypes && gaWM_WidgetTypes[new->Type] && gaWM_WidgetTypes[new->Type]->Init )
437                 gaWM_WidgetTypes[new->Type]->Init(new);
438         
439         // Add to parent's list
440         if(parent->LastChild)
441                 parent->LastChild->NextSibling = new;
442         else
443                 parent->FirstChild = new;
444         parent->LastChild = new;
445
446         // Add to info
447         {
448                 tElement        *ele, *prev = NULL;
449                 for(ele = Info->ElementTable[new->ID % Info->TableSize]; ele; prev = ele, ele = ele->ListNext);
450                 if(prev)
451                         prev->ListNext = new;
452                 else
453                         Info->ElementTable[new->ID % Info->TableSize] = new;
454         }
455
456         Widget_UpdateMinDims(parent);
457 }
458
459 void Widget_SetFlags(tWidgetWin *Info, int Len, const tWidgetMsg_SetFlags *Msg)
460 {
461         tElement        *ele;
462         
463         if( Len < sizeof(*Msg) )
464                 return ;
465
466         _SysDebug("Widget_SetFlags: (%i 0x%x 0x%x)", Msg->WidgetID, Msg->Value, Msg->Mask);
467         
468         ele = Widget_GetElementById(Info, Msg->WidgetID);
469         if(!ele)        return;
470
471         ele->Flags &= ~Msg->Mask;
472         ele->Flags |= Msg->Value & Msg->Mask;
473 }
474
475 void Widget_SetSize(tWidgetWin *Info, int Len, const tWidgetMsg_SetSize *Msg)
476 {
477         tElement        *ele;
478         
479         if( Len < sizeof(*Msg) )
480                 return ;
481         
482         ele = Widget_GetElementById(Info, Msg->WidgetID);
483         if(!ele)        return ;
484         
485         ele->FixedWith = Msg->Value;
486 }
487
488 void Widget_SetText(tWidgetWin *Info, int Len, const tWidgetMsg_SetText *Msg)
489 {
490         tElement        *ele;
491         
492         if( Len < sizeof(*Msg) + 1 )
493                 return ;
494         if( Msg->Text[Len - sizeof(*Msg) - 1] != '\0' )
495                 return ;
496
497         ele = Widget_GetElementById(Info, Msg->WidgetID);
498         if(!ele)        return ;
499
500
501         if( ele->Type < ciWM_NumWidgetTypes
502          && gaWM_WidgetTypes[ele->Type]
503          && gaWM_WidgetTypes[ele->Type]->UpdateText )
504         {
505                 gaWM_WidgetTypes[ele->Type]->UpdateText( ele, Msg->Text );
506         }
507 //      else
508 //      {
509 //              if(ele->Text)   free(ele->Text);
510 //              ele->Text = strdup(Msg->Text);
511 //      }
512 }
513
514 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
515 {
516         tWidgetWin      *info = Target->RendererInfo;
517         switch(Msg)
518         {
519         case WNDMSG_RESIZE: {
520                 const struct sWndMsg_Resize     *msg = Data;
521                 if(Len < sizeof(*msg))  return -1;              
522
523                 info->RootElement.CachedW = msg->W;             
524                 info->RootElement.CachedH = msg->H;
525                 
526                 // TODO: Update dimensions of all child elements?
527                 
528                 return 0; }
529
530         case WNDMSG_MOUSEMOVE: {
531 //              _SysDebug("TODO: Support widget mouse move events");
532                 return 0; }
533
534         case WNDMSG_MOUSEBTN: {
535                 const struct sWndMsg_MouseButton        *msg = Data;
536                 tWidgetMsg_MouseBtn     client_msg;
537                 tElement        *ele;
538                  int    x, y;
539                  int    rv;
540                 
541                 if(Len < sizeof(*msg))  return -1;
542
543                 x = msg->X; y = msg->Y;
544                 client_msg.Button = msg->Button;
545                 client_msg.bPressed = msg->bPressed;
546
547                 ele = Widget_GetElementByPos(info, x, y);
548                 // Send event to all elements from `ele` upwards
549                 for( ; ele; ele = ele->Parent )
550                 {
551                         if(ele->Type < ciWM_NumWidgetTypes
552                          && gaWM_WidgetTypes[ele->Type]
553                          && gaWM_WidgetTypes[ele->Type]->MouseButton)
554                         {
555                                 rv = gaWM_WidgetTypes[ele->Type]->MouseButton(
556                                         ele,
557                                         x - ele->CachedX, y - ele->CachedY,
558                                         msg->Button, msg->bPressed
559                                         );
560                                 // Allow a type to trap the input from going any higher
561                                 if(rv == 0)     break;
562                         }
563                         else
564                         {
565                                 // Pass to user
566                                 client_msg.X = x - ele->CachedX;
567                                 client_msg.Y = y - ele->CachedY;
568                                 client_msg.WidgetID = ele->ID;
569                                 WM_SendMessage(Target, Target, MSG_WIDGET_MOUSEBTN, sizeof(client_msg), &client_msg);
570                         }
571                 }
572                 return 0; }
573
574         // New Widget
575         case MSG_WIDGET_CREATE:
576                 Widget_NewWidget(info, Len, Data);
577                 return 0;
578
579         case MSG_WIDGET_DELETE:
580                 _SysDebug("TODO: Implement MSG_WIDGET_DELETE");
581                 return 0;
582
583         // Set Flags
584         case MSG_WIDGET_SETFLAGS:
585                 Widget_SetFlags(info, Len, Data);
586                 return 0;
587         
588         // Set length
589         case MSG_WIDGET_SETSIZE:
590                 Widget_SetSize(info, Len, Data);
591                 return 0;
592         
593         // Set text
594         case MSG_WIDGET_SETTEXT:
595                 Widget_SetText(info, Len, Data);
596                 return 0;
597         
598         // 
599         default:
600                 return 1;       // Unhandled, pass to user
601         }
602 }
603
604 void Widget_Fire(tElement *Element)
605 {
606         tWidgetMsg_Fire msg;
607         msg.WidgetID = Element->ID;
608         WM_SendMessage(Element->Window, Element->Window, MSG_WIDGET_FIRE, sizeof(msg), &msg);
609 }
610

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