Usermode/AxWin3 - Implemented sending messages to windows
[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
14 // === PROTOTYPES ===
15  int    Renderer_Widget_Init(void);
16 tWindow *Renderer_Widget_Create(int Flags);
17 void    Renderer_Widget_Redraw(tWindow *Window);
18 void    Widget_RenderWidget(tWindow *Window, tElement *Element);
19  int    Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data);
20 void    Widget_UpdateDimensions(tElement *Element);
21 void    Widget_UpdatePosition(tElement *Element);
22 tElement        *Widget_GetElementById(tWidgetWin *Info, uint32_t ID);
23 void    Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg);
24
25 // === GLOBALS ===
26 tWMRenderer     gRenderer_Widget = {
27         .Name = "Widget",
28         .CreateWindow = Renderer_Widget_Create,
29         .Redraw = Renderer_Widget_Redraw,
30         .HandleMessage = Renderer_Widget_HandleMessage
31 };
32
33 // === CODE ===
34 int Renderer_Widget_Init(void)
35 {
36         WM_RegisterRenderer(&gRenderer_Widget); 
37
38         return 0;
39 }
40
41 tWindow *Renderer_Widget_Create(int Flags)
42 {
43         tWindow *ret;
44         tWidgetWin      *info;
45         ret = WM_CreateWindowStruct( sizeof(tWidgetWin) );
46         info = ret->RendererInfo;
47         
48         info->RootElement.BackgroundColour = 0xCCCCCC;
49         
50         return ret;
51 }
52
53 void Renderer_Widget_Redraw(tWindow *Window)
54 {
55         tWidgetWin      *info = Window->RendererInfo;
56         WM_Render_FillRect(Window, 0, 0, 0xFFF, 0xFFF, info->RootElement.BackgroundColour);
57
58         Widget_UpdateDimensions(&info->RootElement);
59         Widget_UpdatePosition(&info->RootElement);
60
61         Widget_RenderWidget(Window, &info->RootElement);
62 }
63
64 // --- Render / Resize ---
65 void Widget_RenderWidget(tWindow *Window, tElement *Element)
66 {
67         tElement        *child;
68         
69         if( Element->Flags & ELEFLAG_NORENDER ) return ;
70         if( Element->Flags & ELEFLAG_INVISIBLE )        return ;
71         
72         Widget_Decorator_RenderWidget(Window, Element);
73         
74         for(child = Element->FirstChild; child; child = child->NextSibling)
75         {
76                 Widget_RenderWidget(Window, child);
77         }
78 }
79
80 void Widget_UpdateDimensions(tElement *Element)
81 {
82         tElement        *child;
83          int    nChildren = 0;
84          int    nFixed = 0;
85          int    maxCross = 0;
86          int    fixedSize = 0;
87          int    fullCross, dynWith;
88         
89         // Pass 1
90         // - Get the fixed and minimum sizes of the element
91         for( child = Element->FirstChild; child; child = child->NextSibling )
92         {
93                 // Ignore elements that will not be rendered
94                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
95                 
96                 // Absolutely positioned elements don't affect dimensions
97                 if( child->Flags & ELEFLAG_ABSOLUTEPOS )        continue ;
98         
99                 // Fixed width elements 
100                 if( child->FixedWith )
101                 {
102                         nFixed ++;
103                         fixedSize += child->FixedWith;
104                 }
105                 
106                 if( child->FixedCross && maxCross < child->FixedCross )
107                         maxCross = child->FixedCross;
108                 if( child->MinCross && maxCross < child->MinCross )
109                         maxCross = child->MinCross;
110                 nChildren ++;
111         }
112         
113         // Get the dynamic with size from the unused space in the element
114         if( nChildren > nFixed ) {
115                 if( Element->Flags & ELEFLAG_VERTICAL )
116                         dynWith = Element->CachedH - Element->PaddingT - Element->PaddingB;
117                 else
118                         dynWith = Element->CachedW - Element->PaddingL - Element->PaddingR;
119                 dynWith -= fixedSize;
120                 if( dynWith < 0 )       return ;
121                 dynWith /= nChildren - nFixed;
122         }
123         
124         // Get the cross size
125         if( Element->Flags & ELEFLAG_VERTICAL )
126                 fullCross = Element->CachedW - Element->PaddingL - Element->PaddingR;
127         else
128                 fullCross = Element->CachedH - Element->PaddingT - Element->PaddingB;
129         
130         // Pass 2 - Set sizes and recurse
131         for( child = Element->FirstChild; child; child = child->NextSibling )
132         {
133                  int    cross, with;
134
135                 // Ignore elements that will not be rendered
136                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
137                 
138                 // --- Cross Size ---
139                 // TODO: Expand to fill?
140                 // TODO: Extra flag so options are (Expand, Equal, Wrap)
141                 if( child->FixedCross )
142                         cross = child->FixedCross;
143                 else if( child->Flags & ELEFLAG_NOEXPAND )
144                         cross = child->MinCross;
145                 else
146                         cross = fullCross;
147                 
148                 // --- With Size ---
149                 if( child->FixedWith)
150                         with = child->FixedWith;
151                 else if( child->Flags & ELEFLAG_NOSTRETCH )
152                         with = child->MinWith;
153                 else
154                         with = dynWith;
155                 
156                 
157                 // Update the dimensions if they have changed
158                 if( Element->Flags & ELEFLAG_VERTICAL ) {
159                         // If no change, don't recurse
160                         if( child->CachedW == cross && child->CachedH == with )
161                                 continue ;
162                         child->CachedW = cross;
163                         child->CachedH = with;
164                 }
165                 else {
166                         // If no change, don't recurse
167                         if( child->CachedW == with && child->CachedH == cross )
168                                 continue ;
169                         child->CachedW = with;
170                         child->CachedH = cross;
171                 }
172                 
173                 // Force the positions of child elements to be recalculated
174                 child->CachedX = -1;
175         
176                 // Recurse down so the child elements can be updated    
177                 Widget_UpdateDimensions(child);
178         }
179         
180 }
181
182 /**
183  * \brief Update the position of child elements
184  */
185 void Widget_UpdatePosition(tElement *Element)
186 {
187         tElement        *child;
188          int    x, y;
189         
190         if( Element->Flags & ELEFLAG_NORENDER ) return ;
191         
192         // Initialise
193         x = Element->CachedX + Element->PaddingL;
194         y = Element->CachedY + Element->PaddingT;
195         
196         // Update each child
197         for(child = Element->FirstChild; child; child = child->NextSibling)
198         {
199                  int    newX, newY;
200                 // Ignore elements that will not be rendered
201                 if( child->Flags & ELEFLAG_NORENDER )   continue ;
202
203                 newX = x; newY = y;
204                 
205                 // Handle alignment
206                 if( Element->Flags & ELEFLAG_ALIGN_CENTER ) {
207                         if(Element->Flags & ELEFLAG_VERTICAL)
208                                 newX += Element->CachedW/2 - child->CachedW/2;
209                         else
210                                 newY += Element->CachedH/2 - child->CachedH/2;
211                 }
212                 else if( Element->Flags & ELEFLAG_ALIGN_END ) {
213                         if(Element->Flags & ELEFLAG_VERTICAL )
214                                 newX += Element->CachedW - child->CachedW
215                                         - Element->PaddingL - Element->PaddingR;
216                         else
217                                 newY += Element->CachedH - child->CachedH
218                                         - Element->PaddingT - Element->PaddingB;
219                 }
220
221                 // Check for changes, and don't update if there was no change
222                 if( newX != child->CachedX || newY != child->CachedY )
223                 {
224                         child->CachedX = newX;
225                         child->CachedY = newY;
226                         // Update child's children positions
227                         Widget_UpdatePosition(child);
228                 }
229                 
230                 // Increment
231                 if(Element->Flags & ELEFLAG_VERTICAL ) {
232                         y += child->CachedH + Element->GapSize;
233                 }
234                 else {
235                         x += child->CachedW + Element->GapSize;
236                 }
237         }
238 }
239
240
241 // --- Helpers ---
242 tElement *Widget_GetElementById(tWidgetWin *Info, uint32_t ID)
243 {
244         tElement        *ele;
245
246         if(ID == -1)
247                 return &Info->RootElement;
248         
249         if( ID < Info->TableSize )      return Info->ElementTable[ID];
250
251         ele = Info->ElementTable[ID % Info->TableSize];
252         while(ele && ele->ID != ID)     ele = ele->ListNext;
253         return ele;
254 }
255
256 // --- Message Handlers ---
257 void Widget_NewWidget(tWidgetWin *Info, size_t Len, tWidgetMsg_Create *Msg)
258 {
259         const int       max_debugname_len = Len - sizeof(tWidgetMsg_Create);
260         tElement        *parent;        
261
262         // Sanity check
263         if( Len < sizeof(tWidgetMsg_Create) )
264                 return ;
265         if( strnlen(Msg->DebugName, max_debugname_len) == max_debugname_len )
266                 return ;
267         
268         // Create
269         parent = Widget_GetElementById(Info, Msg->Parent);
270         
271 }
272
273 int Renderer_Widget_HandleMessage(tWindow *Target, int Msg, int Len, void *Data)
274 {
275         tWidgetWin      *info = Target->RendererInfo;
276         switch(Msg)
277         {
278         case WNDMSG_RESIZE: {
279                 struct sWndMsg_Resize   *msg = Data;
280                 if(Len < sizeof(*msg))  return -1;              
281
282                 info->RootElement.CachedW = msg->W;             
283                 info->RootElement.CachedH = msg->H;
284                 return 0; }
285
286         // New Widget
287         case MSG_WIDGET_CREATE:
288                 Widget_NewWidget(info, Len, Data);
289                 return 0;
290         // 
291         default:
292                 return 1;       // Unhandled, pass to user
293         }
294 }
295

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