µGFX  2.9
version 2.9
gwin_slider.c
Go to the documentation of this file.
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 /**
9  * @file src/gwin/gwin_slider.c
10  * @brief GWIN sub-system slider code
11  */
12 
13 #include "../../gfx.h"
14 
15 #if (GFX_USE_GWIN && GWIN_NEED_SLIDER) || defined(__DOXYGEN__)
16 
17 #include "gwin_class.h"
18 
19 // Calculate the slider position from the display position
20 static int SliderCalcPosFromDPos(GSliderObject *gsw) {
21  int halfbit;
22 
23  // Set the new position
24  if (gsw->w.g.width < gsw->w.g.height) {
25  if (gsw->dpos >= gsw->w.g.height-GWIN_SLIDER_DEAD_BAND)
26  return gsw->min;
27  if (gsw->dpos < GWIN_SLIDER_DEAD_BAND)
28  return gsw->max;
29  halfbit = gsw->w.g.height/2-GWIN_SLIDER_DEAD_BAND;
30  if (gsw->min > gsw->max)
31  halfbit = -halfbit;
32  return (((int)(gsw->w.g.height-(GWIN_SLIDER_DEAD_BAND+1)-gsw->dpos))*(gsw->max-gsw->min) + halfbit)/(gsw->w.g.height-(2*GWIN_SLIDER_DEAD_BAND+1)) + gsw->min;
33  }
34  if (gsw->dpos >= gsw->w.g.width-GWIN_SLIDER_DEAD_BAND)
35  return gsw->max;
36  if (gsw->dpos < GWIN_SLIDER_DEAD_BAND)
37  return gsw->min;
38  halfbit = gsw->w.g.width/2-GWIN_SLIDER_DEAD_BAND;
39  if (gsw->min > gsw->max)
40  halfbit = -halfbit;
41  return (((int)gsw->dpos-GWIN_SLIDER_DEAD_BAND)*(gsw->max-gsw->min) + halfbit)/(gsw->w.g.width-(2*GWIN_SLIDER_DEAD_BAND+1)) + gsw->min;
42 }
43 
44 // Send the slider event
45 static void SendSliderEvent(GSliderObject *gsw, gU8 action) {
46  GSourceListener * psl;
47  GEvent * pe;
48  #define pse ((GEventGWinSlider *)pe)
49 
50  // Does this slider want more than just SET events?
51  if (action != GSLIDER_EVENT_SET && !(gsw->w.g.flags & GSLIDER_FLG_EXTENDED_EVENTS))
52  return;
53 
54  psl = 0;
55  while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) {
56  // Work out which action to send.
57  // This precedence order helps provide some protection against missed events.
58  // Saving it into srcflags works regardless of if a buffer is available.
59  if (psl->srcflags < action)
60  psl->srcflags = action;
61 
62  // Skip sending if no buffer is available
63  if (!(pe = geventGetEventBuffer(psl)))
64  continue;
65 
66  // Fill in the event
67  pse->type = GEVENT_GWIN_SLIDER;
68  pse->gwin = (GHandle)gsw;
69  pse->action = psl->srcflags;
70  #if GWIN_WIDGET_TAGS
71  pse->tag = gsw->w.tag;
72  #endif
73 
74  // If it is a cancel or set use the defined position else use the calculated position.
75  pse->position = pse->action >= GSLIDER_EVENT_CANCEL ? gsw->pos : SliderCalcPosFromDPos(gsw);
76 
77  // Cleanup and send.
78  psl->srcflags = 0;
79  geventSendEvent(psl);
80  }
81 
82  #undef pse
83 }
84 
85 // Reset the display position back to the value predicted by the saved slider position
86 static void SliderResetDisplayPos(GSliderObject *gsw) {
87  if (gsw->w.g.width < gsw->w.g.height)
88  gsw->dpos = gsw->w.g.height-1-(gsw->w.g.height-1)*(gsw->pos-gsw->min)/(gsw->max-gsw->min);
89  else
90  gsw->dpos = (gsw->w.g.width-1)*(gsw->pos-gsw->min)/(gsw->max-gsw->min);
91 }
92 
93 #if GINPUT_NEED_MOUSE
94  // Set the display position from the mouse position
95  static void SetDisplayPosFromMouse(GSliderObject *gsw, gCoord x, gCoord y) {
96  if (gsw->w.g.width < gsw->w.g.height)
97  gsw->dpos = y < 0 ? 0 : (y >= gsw->w.g.height ? gsw->w.g.height-1 : y);
98  else
99  gsw->dpos = x < 0 ? 0 : (x >= gsw->w.g.width ? gsw->w.g.width-1 : x);
100  }
101 
102  // A mouse up event
103  static void SliderMouseUp(GWidgetObject *gw, gCoord x, gCoord y) {
104  #define gsw ((GSliderObject *)gw)
105 
106  #if !GWIN_BUTTON_LAZY_RELEASE
107  // Are we over the slider?
108  if (x < 0 || x >= gsw->w.g.width || y < 0 || y >= gsw->w.g.height) {
109  // No - restore the slider
110  SliderResetDisplayPos(gsw);
111  _gwinUpdate(&gsw->w.g);
112  SendSliderEvent(gsw, GSLIDER_EVENT_CANCEL);
113  return;
114  }
115  #endif
116 
117  // Set the new position
118  SetDisplayPosFromMouse(gsw, x, y);
119  gsw->pos = SliderCalcPosFromDPos(gsw);
120 
121  // Update the display
122  #if GWIN_SLIDER_NOSNAP
123  //only adjust dpos if it equals one of the end values.
124  if (gsw->w.g.width < gsw->w.g.height) {
125  if (gsw->pos == gsw->min)
126  gsw->dpos = gsw->w.g.height-1;
127  else if (gsw->pos == gsw->max)
128  gsw->dpos = 0;
129  } else {
130  if (gsw->pos == gsw->max)
131  gsw->dpos = gsw->w.g.width-1;
132  else if (gsw->pos == gsw->min)
133  gsw->dpos = 0;
134  }
135  #else
136  SliderResetDisplayPos(gsw);
137  #endif
138  _gwinUpdate(&gsw->w.g);
139 
140  // Generate the event
141  SendSliderEvent(gsw, GSLIDER_EVENT_SET);
142 
143  #undef gsw
144  }
145 
146  // A mouse down event
147  static void SliderMouseDown(GWidgetObject *gw, gCoord x, gCoord y) {
148  #define gsw ((GSliderObject *)gw)
149 
150  // Determine the display position
151  SetDisplayPosFromMouse(gsw, x, y);
152 
153  // Update the display
154  _gwinUpdate(&gsw->w.g);
155 
156  // Send the event
157  SendSliderEvent(gsw, GSLIDER_EVENT_START);
158 
159  #undef gsw
160  }
161 
162  // A mouse move event
163  static void SliderMouseMove(GWidgetObject *gw, gCoord x, gCoord y) {
164  #define gsw ((GSliderObject *)gw)
165 
166  // Determine the display position
167  SetDisplayPosFromMouse(gsw, x, y);
168 
169  // Update the display
170  _gwinUpdate(&gsw->w.g);
171 
172  // Send the event
173  SendSliderEvent(gsw, GSLIDER_EVENT_MOVE);
174 
175  #undef gsw
176  }
177 #endif
178 
179 #if GINPUT_NEED_TOGGLE
180  // A toggle on has occurred
181  static void SliderToggleOn(GWidgetObject *gw, gU16 role) {
182  #define gsw ((GSliderObject *)gw)
183 
184  if (role) {
185  gwinSliderSetPosition(&gsw->w.g, gsw->pos+(gsw->max-gsw->min)/GWIN_SLIDER_TOGGLE_INC);
186  SendSliderEvent(gsw, GSLIDER_EVENT_SET);
187  } else {
188  gwinSliderSetPosition(&gsw->w.g, gsw->pos-(gsw->max-gsw->min)/GWIN_SLIDER_TOGGLE_INC);
189  SendSliderEvent(gsw, GSLIDER_EVENT_SET);
190  }
191  #undef gsw
192  }
193 
194  static void SliderToggleAssign(GWidgetObject *gw, gU16 role, gU16 instance) {
195  if (role)
196  ((GSliderObject *)gw)->t_up = instance;
197  else
198  ((GSliderObject *)gw)->t_dn = instance;
199  }
200 
201  static gU16 SliderToggleGet(GWidgetObject *gw, gU16 role) {
202  return role ? ((GSliderObject *)gw)->t_up : ((GSliderObject *)gw)->t_dn;
203  }
204 #endif
205 
206 #if GINPUT_NEED_DIAL
207  // A dial move event
208  static void SliderDialMove(GWidgetObject *gw, gU16 role, gU16 value, gU16 max) {
209  #define gsw ((GSliderObject *)gw)
210  (void) role;
211 
212  // Set the new position
213  gsw->pos = (gU16)((gU32)value*(gsw->max-gsw->min)/max + gsw->min);
214 
215  SliderResetDisplayPos(gsw);
216  _gwinUpdate(&gsw->w.g);
217 
218  // Generate the event
219  SendSliderEvent(gsw, GSLIDER_EVENT_SET);
220  #undef gsw
221  }
222 
223  static void SliderDialAssign(GWidgetObject *gw, gU16 role, gU16 instance) {
224  (void) role;
225  ((GSliderObject *)gw)->dial = instance;
226  }
227 
228  static gU16 SliderDialGet(GWidgetObject *gw, gU16 role) {
229  (void) role;
230  return ((GSliderObject *)gw)->dial;
231  }
232 #endif
233 
234 // The slider VMT table
235 static const gwidgetVMT sliderVMT = {
236  {
237  "Slider", // The classname
238  sizeof(GSliderObject), // The object size
239  _gwidgetDestroy, // The destroy routine
240  _gwidgetRedraw, // The redraw routine
241  0, // The after-clear routine
242  },
243  gwinSliderDraw_Std, // The default drawing routine
244  #if GINPUT_NEED_MOUSE
245  {
246  SliderMouseDown, // Process mouse down events
247  SliderMouseUp, // Process mouse up events
248  SliderMouseMove, // Process mouse move events
249  },
250  #endif
251  #if GINPUT_NEED_KEYBOARD || GWIN_NEED_KEYBOARD
252  {
253  0 // Process keyboard events
254  },
255  #endif
256  #if GINPUT_NEED_TOGGLE
257  {
258  2, // 1 toggle role
259  SliderToggleAssign, // Assign Toggles
260  SliderToggleGet, // Get Toggles
261  0, // Process toggle off events (NOT USED)
262  SliderToggleOn, // Process toggle on events
263  },
264  #endif
265  #if GINPUT_NEED_DIAL
266  {
267  1, // 1 dial roles
268  SliderDialAssign, // Assign Dials
269  SliderDialGet, // Get Dials
270  SliderDialMove, // Process dial move events
271  },
272  #endif
273 };
274 
275 GHandle gwinGSliderCreate(GDisplay *g, GSliderObject *gs, const GWidgetInit *pInit) {
276  if (!(gs = (GSliderObject *)_gwidgetCreate(g, &gs->w, pInit, &sliderVMT)))
277  return 0;
278  #if GINPUT_NEED_TOGGLE
279  gs->t_dn = GWIDGET_NO_INSTANCE;
280  gs->t_up = GWIDGET_NO_INSTANCE;
281  #endif
282  #if GINPUT_NEED_DIAL
283  gs->dial = GWIDGET_NO_INSTANCE;
284  #endif
285  gs->min = 0;
286  gs->max = 100;
287  gs->pos = 0;
288  SliderResetDisplayPos(gs);
289  gwinSetVisible((GHandle)gs, pInit->g.show);
290  return (GHandle)gs;
291 }
292 
293 void gwinSliderSetRange(GHandle gh, int min, int max) {
294  #define gsw ((GSliderObject *)gh)
295 
296  if (gh->vmt != (gwinVMT *)&sliderVMT)
297  return;
298 
299  if (min == max) // prevent divide by 0 errors.
300  max++;
301  gsw->min = min;
302  gsw->max = max;
303  gsw->pos = min;
304  SliderResetDisplayPos(gsw);
305  #undef gsw
306 }
307 
308 void gwinSliderSetPosition(GHandle gh, int pos) {
309  #define gsw ((GSliderObject *)gh)
310 
311  if (gh->vmt != (gwinVMT *)&sliderVMT)
312  return;
313 
314  if (gsw->min <= gsw->max) {
315  if (pos < gsw->min) gsw->pos = gsw->min;
316  else if (pos > gsw->max) gsw->pos = gsw->max;
317  else gsw->pos = pos;
318  } else {
319  if (pos > gsw->min) gsw->pos = gsw->min;
320  else if (pos < gsw->max) gsw->pos = gsw->max;
321  else gsw->pos = pos;
322  }
323  SliderResetDisplayPos(gsw);
324  _gwinUpdate(gh);
325 
326  #undef gsw
327 }
328 
329 void gwinSliderSendExtendedEvents(GHandle gh, gBool enabled) {
330  if (gh->vmt != (gwinVMT *)&sliderVMT)
331  return;
332 
333  if (enabled)
335  else
337 }
338 
339 /*----------------------------------------------------------
340  * Custom Draw Routines
341  *----------------------------------------------------------*/
342 
343 void gwinSliderDraw_Std(GWidgetObject *gw, void *param) {
344  #define gsw ((GSliderObject *)gw)
345  const GColorSet * pcol;
346  (void) param;
347 
348  if (gw->g.vmt != (gwinVMT *)&sliderVMT)
349  return;
350 
351  if ((gw->g.flags & GWIN_FLG_SYSENABLED))
352  pcol = &gw->pstyle->enabled;
353  else
354  pcol = &gw->pstyle->disabled;
355 
356  // Vertical slider
357  if (gw->g.width < gw->g.height) {
358  if (gsw->dpos != gw->g.height-1)
359  gdispGFillArea(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.width, gw->g.height - gsw->dpos, pcol->progress); // Active area
360  if (gsw->dpos != 0)
361  gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gsw->dpos, pcol->fill); // Inactive area
362  gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
363  gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos, pcol->edge); // Thumb
364  if (gsw->dpos >= 2)
365  gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos-2, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos-2, pcol->edge); // Thumb
366  if (gsw->dpos <= gw->g.height-2)
367  gdispGDrawLine(gw->g.display, gw->g.x, gw->g.y+gsw->dpos+2, gw->g.x+gw->g.width-1, gw->g.y+gsw->dpos+2, pcol->edge); // Thumb
368 
369  // Horizontal slider
370  } else {
371  if (gsw->dpos != gw->g.width-1)
372  gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.width-gsw->dpos, gw->g.height, pcol->fill); // Inactive area
373  if (gsw->dpos != 0)
374  gdispGFillArea(gw->g.display, gw->g.x, gw->g.y, gsw->dpos, gw->g.height, pcol->progress); // Active area
375  gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
376  gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-1, pcol->edge); // Thumb
377  if (gsw->dpos >= 2)
378  gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos-2, gw->g.y, gw->g.x+gsw->dpos-2, gw->g.y+gw->g.height-1, pcol->edge); // Thumb
379  if (gsw->dpos <= gw->g.width-2)
380  gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos+2, gw->g.y, gw->g.x+gsw->dpos+2, gw->g.y+gw->g.height-1, pcol->edge); // Thumb
381  }
382 
383  // Draw the string
384  gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, gJustifyCenter);
385 
386  #undef gsw
387 }
388 
389 #if GDISP_NEED_IMAGE
390 void gwinSliderDraw_Image(GWidgetObject *gw, void *param) {
391  #define gsw ((GSliderObject *)gw)
392  #define gi ((gImage *)param)
393  const GColorSet * pcol;
394  gCoord z, v;
395 
396  if (gw->g.vmt != (gwinVMT *)&sliderVMT)
397  return;
398 
399  if ((gw->g.flags & GWIN_FLG_SYSENABLED))
400  pcol = &gw->pstyle->enabled;
401  else
402  pcol = &gw->pstyle->disabled;
403 
404  if (gw->g.width < gw->g.height) { // Vertical slider
405  if (gsw->dpos != 0) // The unfilled area
406  gdispGFillArea(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gsw->dpos-1, gw->pstyle->enabled.progress); // Inactive area
407  if (gsw->dpos != gw->g.height-1) { // The filled area
408  for(z=gw->g.height, v=gi->height; z > gsw->dpos;) {
409  z -= v;
410  if (z < gsw->dpos) {
411  v -= gsw->dpos - z;
412  z = gsw->dpos;
413  }
414  gdispGImageDraw(gw->g.display, gi, gw->g.x+1, gw->g.y+z+1, gw->g.width-1, v-2, 0, gi->height-v);
415  }
416  }
417  gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
418  gdispGDrawLine(gw->g.display, gw->g.x+1, gw->g.y+gsw->dpos, gw->g.x+gw->g.width-2, gw->g.y+gsw->dpos, pcol->edge); // Thumb
419 
420  // Horizontal slider
421  } else {
422  if (gsw->dpos != gw->g.width-1) // The unfilled area
423  gdispGFillArea(gw->g.display, gw->g.x+gsw->dpos+1, gw->g.y+1, gw->g.width-gsw->dpos-2, gw->g.height-2, gw->pstyle->enabled.progress); // Inactive area
424  if (gsw->dpos != 0) { // The filled area
425  for(z=0, v=gi->width; z < gsw->dpos; z += v) {
426  if (z+v > gsw->dpos)
427  v -= z+v - gsw->dpos;
428  gdispGImageDraw(gw->g.display, gi, gw->g.x+z+1, gw->g.y+1, v-1, gw->g.height-2, 0, 0);
429  }
430  }
431  gdispGDrawBox(gw->g.display, gw->g.x, gw->g.y, gw->g.width, gw->g.height, pcol->edge); // Edge
432  gdispGDrawLine(gw->g.display, gw->g.x+gsw->dpos, gw->g.y+1, gw->g.x+gsw->dpos, gw->g.y+gw->g.height-2, pcol->edge); // Thumb
433  }
434  gdispGDrawStringBox(gw->g.display, gw->g.x+1, gw->g.y+1, gw->g.width-2, gw->g.height-2, gw->text, gw->g.font, pcol->text, gJustifyCenter);
435 
436  #undef gsw
437  #undef gi
438 }
439 #endif /* GDISP_NEED_IMAGE */
440 
441 #endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */
void gdispGDrawStringBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, const char *str, gFont font, gColor color, gJustify justify)
Draw a text string vertically centered within the specified box.
void gdispGFillArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Fill an area with a color.
void gdispGDrawLine(GDisplay *g, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor color)
Draw a line.
gI16 gCoord
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
void gdispGDrawBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Draw a rectangular box.
@ gJustifyCenter
Definition: gdisp.h:62
GEvent * geventGetEventBuffer(GSourceListener *psl)
Get the event buffer from the GSourceListener.
Definition: gevent.c:187
void geventSendEvent(GSourceListener *psl)
Called by a source to indicate the listener's event buffer has been filled.
Definition: gevent.c:200
GSourceListener * geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr)
Called by a source with a possible event to get a listener record.
Definition: gevent.c:163
#define GWIN_SLIDER_DEAD_BAND
The number of pixels of dead-band at each end of the slider.
Definition: gwin_options.h:357
#define GWIN_SLIDER_TOGGLE_INC
How many toggles it takes to go from minimum to maximum value on a slider.
Definition: gwin_options.h:366
gdispImageError gdispGImageDraw(GDisplay *g, gImage *img, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord sx, gCoord sy)
Draw the image.
void gwinSliderDraw_Std(GWidgetObject *gw, void *param)
The default rendering function for the slider widget.
Definition: gwin_slider.c:343
void gwinSliderDraw_Image(GWidgetObject *gw, void *param)
The default rendering function.
void gwinSliderSetRange(GHandle gh, int min, int max)
Set the slider range.
Definition: gwin_slider.c:293
GHandle gwinGSliderCreate(GDisplay *g, GSliderObject *gs, const GWidgetInit *pInit)
Create a slider window.
Definition: gwin_slider.c:275
void gwinSliderSetPosition(GHandle gh, int pos)
Set the slider position.
Definition: gwin_slider.c:308
void gwinSliderSendExtendedEvents(GHandle gh, gBool enabled)
Should the slider send extended events.
Definition: gwin_slider.c:329
#define GSLIDER_FLG_EXTENDED_EVENTS
The internal slider object flags.
Definition: gwin_slider.h:53
void gwinSetVisible(GHandle gh, gBool visible)
Sets whether a window is visible or not.
The GColorSet structure.
Definition: gwin_widget.h:37
gColor fill
Definition: gwin_widget.h:40
gColor text
Definition: gwin_widget.h:38
gColor progress
Definition: gwin_widget.h:41
gColor edge
Definition: gwin_widget.h:39
The structure to initialise a widget.
Definition: gwin_widget.h:97
GWindowInit g
Definition: gwin_widget.h:98
The GWIN Widget structure.
Definition: gwin_widget.h:118
GWindowObject g
Definition: gwin_widget.h:119
const GWidgetStyle * pstyle
Definition: gwin_widget.h:123
const char * text
Definition: gwin_widget.h:120
GColorSet disabled
Definition: gwin_widget.h:56
GColorSet enabled
Definition: gwin_widget.h:55
gBool show
Definition: gwin.h:80
A window object structure.
Definition: gwin.h:40
GDisplay * display
Definition: gwin.h:46
gCoord x
Definition: gwin.h:47
gCoord width
Definition: gwin.h:49
const struct gwinVMT * vmt
Definition: gwin.h:45
gU32 flags
Definition: gwin.h:53
gCoord y
Definition: gwin.h:48
gCoord height
Definition: gwin.h:50
The Virtual Method Table for a widget.
Definition: gwin_class.h:85
The Virtual Method Table for a GWIN window.
Definition: gwin_class.h:55