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

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