1 /* Public domain */
2
3 /*
4 * Implementation of a typical Agar widget which uses surface mappings to
5 * efficiently draw surfaces, regardless of the underlying graphics system.
6 *
7 * If you are not familiar with the way the Agar object system handles
8 * inheritance, see demos/objsystem.
9 */
10
11 #include "agartest.h"
12 #include "customwidget_mywidget.h"
13
14 /*
15 * This is a generic constructor function. It is completely optional, but
16 * customary of FooNew() functions to allocate, initialize and attach an
17 * instance of the class.
18 */
19 MyWidget *
MyWidgetNew(void * parent,const char * foo)20 MyWidgetNew(void *parent, const char *foo)
21 {
22 MyWidget *my;
23
24 /* Create a new instance of the MyWidget class */
25 my = malloc(sizeof(MyWidget));
26 AG_ObjectInit(my, &myWidgetClass);
27
28 /* Set some constructor arguments */
29 my->foo = foo;
30
31 /* Attach the object to the parent (no-op if parent is NULL) */
32 AG_ObjectAttach(parent, my);
33
34 return (my);
35 }
36
37 /*
38 * This function requests a minimal geometry for displaying the widget.
39 * It is expected to return the width and height in pixels into r.
40 *
41 * Note: Some widgets will provide FooSizeHint() functions to allow the
42 * programmer to request an initial size in pixels or some other metric
43 * FooSizeHint() typically sets some structure variable, which are then
44 * used here.
45 */
46 static void
SizeRequest(void * p,AG_SizeReq * r)47 SizeRequest(void *p, AG_SizeReq *r)
48 {
49 MyWidget *my = p;
50
51 if (my->mySurface == -1) {
52 /*
53 * We can use AG_TextSize() to return the dimensions of rendered
54 * text, without rendering it.
55 */
56 AG_TextSize("Custom widget!", &r->w, &r->h);
57 } else {
58 /*
59 * We can use the geometry of the rendered surface. The
60 * AGWIDGET_SURFACE() macro returns the AG_Surface given a
61 * Widget surface handle.
62 */
63 r->w = AGWIDGET_SURFACE(my,my->mySurface)->w;
64 r->h = AGWIDGET_SURFACE(my,my->mySurface)->h;
65 }
66 }
67
68 /*
69 * This function is called by the parent widget after it decided how much
70 * space to allocate to this widget. It is mostly useful to container
71 * widgets, but other widgets generally use it to check if the allocated
72 * geometry can be handled by Draw().
73 */
74 static int
SizeAllocate(void * p,const AG_SizeAlloc * a)75 SizeAllocate(void *p, const AG_SizeAlloc *a)
76 {
77 MyWidget *my = p;
78
79 /* If we return -1, Draw() will not be called. */
80 if (a->w < 5 || a->h < 5)
81 return (-1);
82
83 TestMsg(my->ti, "Allocated %dx%d pixels", a->w, a->h);
84 return (0);
85 }
86
87 /*
88 * Draw function. Invoked from GUI rendering context to draw the widget
89 * at its current location. All primitive and surface operations operate
90 * on widget coordinates.
91 */
92 static void
Draw(void * p)93 Draw(void *p)
94 {
95 MyWidget *my = p;
96 AG_Color c;
97
98 /*
99 * Draw a box spanning the widget area. Use the widget's
100 * default color (the "color" style property).
101 */
102 AG_DrawBox(my,
103 AG_RECT(0, 0, AGWIDGET(my)->w, AGWIDGET(my)->h), 1,
104 AG_WCOLOR(my,0));
105
106 /*
107 * Render some text onto a surface. The default text color
108 * (the "text-color" style property) will be used.
109 */
110 if (my->mySurface == -1) {
111 my->mySurface = AG_WidgetMapSurface(my,
112 AG_TextRender("Custom widget!"));
113 }
114
115 c = AG_ColorRGB(250, 250, 0);
116 AG_DrawLine(my, 0, 0, my->x, my->y, c);
117 AG_DrawLine(my, AGWIDGET(my)->w, 0, my->x, my->y, c);
118 AG_DrawLine(my, 0, AGWIDGET(my)->h, my->x, my->y, c);
119 AG_DrawLine(my, AGWIDGET(my)->w, AGWIDGET(my)->h, my->x, my->y, c);
120 AG_DrawCircle(my, my->x, my->y, 50, c);
121
122 /* Draw the mapped surface centered around the cursor. */
123 AG_WidgetBlitSurface(my, my->mySurface,
124 my->x - AGWIDGET_SURFACE(my,my->mySurface)->w/2,
125 my->y - AGWIDGET_SURFACE(my,my->mySurface)->h/2);
126 }
127
128 /* Mouse motion event handler */
129 static void
MouseMotion(AG_Event * event)130 MouseMotion(AG_Event *event)
131 {
132 MyWidget *my = AG_SELF();
133 int x = AG_INT(1);
134 int y = AG_INT(2);
135
136 if (x != my->x || y != my->y) {
137 AG_Redraw(my);
138 }
139 my->x = x;
140 my->y = y;
141 }
142
143 /* Mouse click event handler */
144 static void
MouseButtonDown(AG_Event * event)145 MouseButtonDown(AG_Event *event)
146 {
147 MyWidget *my = AG_SELF();
148 int button = AG_INT(1);
149 int x = AG_INT(2);
150 int y = AG_INT(3);
151
152 if (button != AG_MOUSE_LEFT) {
153 return;
154 }
155 TestMsg(my->ti, "Click at %d,%d", x, y);
156 AG_WidgetFocus(my);
157 }
158
159 /* Mouse click event handler */
160 static void
MouseButtonUp(AG_Event * event)161 MouseButtonUp(AG_Event *event)
162 {
163 /* MyWidget *my = AG_SELF(); */
164 /* int button = AG_INT(1); */
165 /* int x = AG_INT(2); */
166 /* int y = AG_INT(3); */
167
168 /* ... */
169 }
170
171 /* Keystroke event handler */
172 static void
KeyDown(AG_Event * event)173 KeyDown(AG_Event *event)
174 {
175 MyWidget *my = AG_SELF();
176 int keysym = AG_INT(1);
177 /* int keymod = AG_INT(2); */
178 Uint32 unicode = AG_INT(3);
179
180 TestMsg(my->ti, "Keystroke: 0x%x (Uni=%x)", keysym, unicode);
181 }
182
183 /* Keystroke event handler */
184 static void
KeyUp(AG_Event * event)185 KeyUp(AG_Event *event)
186 {
187 /* MyWidget *my = AG_SELF(); */
188 /* int keysym = AG_INT(1); */
189
190 /* ... */
191 }
192
193 /*
194 * Initialization routine. Note that the object system will automatically
195 * invoke the initialization routines of the parent classes first.
196 */
197 static void
Init(void * obj)198 Init(void *obj)
199 {
200 MyWidget *my = obj;
201
202 /* Allow this widget to grab focus. */
203 AGWIDGET(my)->flags |= AG_WIDGET_FOCUSABLE;
204
205 /* Receive mouse motion events unconditionally. */
206 AGWIDGET(my)->flags |= AG_WIDGET_UNFOCUSED_MOTION;
207
208 /* The widget will be using the font engine. */
209 AGWIDGET(my)->flags |= AG_WIDGET_USE_TEXT;
210
211 /* The widget will react to mouse hover events. */
212 AGWIDGET(my)->flags |= AG_WIDGET_USE_MOUSEOVER;
213
214 /* Initialize instance variables. */
215 my->foo = "";
216 my->x = 0;
217 my->y = 0;
218
219 /*
220 * We'll eventually need to create and map a surface, but we cannot
221 * do this from Init(), because it involves texture operations in
222 * GL mode which are thread-unsafe. We wait until Draw() to do that.
223 */
224 my->mySurface = -1;
225
226 /*
227 * Map our event handlers. For a list of all meaningful events
228 * we can handle, see AG_Object(3), AG_Widget(3) and AG_Window(3).
229 *
230 * Here we register handlers for the common AG_Window(3) events.
231 */
232 AG_SetEvent(my, "mouse-button-up", MouseButtonUp, NULL);
233 AG_SetEvent(my, "mouse-button-down", MouseButtonDown, NULL);
234 AG_SetEvent(my, "mouse-motion", MouseMotion, NULL);
235 AG_SetEvent(my, "key-up", KeyUp, NULL);
236 AG_SetEvent(my, "key-down", KeyDown, NULL);
237 }
238
239 /*
240 * This structure describes our widget class. It inherits from AG_ObjectClass.
241 * Any of the function members may be NULL. See AG_Widget(3) for details.
242 */
243 AG_WidgetClass myWidgetClass = {
244 {
245 "AG_Widget:MyWidget", /* Name of class */
246 sizeof(MyWidget), /* Size of structure */
247 { 0,0 }, /* Version for load/save */
248 Init, /* Initialize dataset */
249 NULL, /* Free dataset */
250 NULL, /* Destroy widget */
251 NULL, /* Load widget (for GUI builder) */
252 NULL, /* Save widget (for GUI builder) */
253 NULL /* Edit (for GUI builder) */
254 },
255 Draw, /* Render widget */
256 SizeRequest, /* Default size requisition */
257 SizeAllocate /* Size allocation callback */
258 };
259