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

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