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

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