xref: /plan9/sys/src/libcontrol/slider.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <control.h>
8 
9 typedef struct Slider Slider;
10 
11 struct Slider
12 {
13 	Control;
14 	int		border;
15 	CImage	*image;
16 	CImage	*textcolor;
17 	CImage	*bordercolor;
18 	CImage	*indicatorcolor;
19 	int		absolute;
20 	int		max;
21 	int		vis;
22 	int		value;
23 	int		clamphigh;
24 	int		clamplow;
25 	int		horizontal;
26 	int		lastbut;
27 };
28 
29 enum{
30 	EAbsolute,
31 	EBorder,
32 	EBordercolor,
33 	EClamp,
34 	EFocus,
35 	EFormat,
36 	EHide,
37 	EImage,
38 	EIndicatorcolor,
39 	EMax,
40 	EOrient,
41 	ERect,
42 	EReveal,
43 	EShow,
44 	ESize,
45 	EValue,
46 	EVis,
47 };
48 
49 static char *cmds[] = {
50 	[EAbsolute] =		"absolute",
51 	[EBorder] =		"border",
52 	[EBordercolor] =	"bordercolor",
53 	[EClamp] =		"clamp",
54 	[EFocus] = 		"focus",
55 	[EFormat] = 		"format",
56 	[EHide] =			"hide",
57 	[EImage] =		"image",
58 	[EIndicatorcolor] =	"indicatorcolor",
59 	[EMax] =			"max",
60 	[EOrient] =		"orient",
61 	[ERect] =			"rect",
62 	[EReveal] =		"reveal",
63 	[EShow] =			"show",
64 	[ESize] =			"size",
65 	[EValue] =			"value",
66 	[EVis] =			"vis",
67 };
68 
69 static void
sliderfree(Control * c)70 sliderfree(Control *c)
71 {
72 	Slider *s;
73 
74 	s = (Slider*)c;
75 	_putctlimage(s->image);
76 	_putctlimage(s->textcolor);
77 	_putctlimage(s->bordercolor);
78 	_putctlimage(s->indicatorcolor);
79 }
80 
81 static void
slidershow(Slider * s)82 slidershow(Slider *s)
83 {
84 	Rectangle r, t;
85 	int l, h, d;
86 
87 	if (s->hidden)
88 		return;
89 	r = s->rect;
90 	draw(s->screen, r, s->image->image, nil, s->image->image->r.min);
91 	if(s->border > 0){
92 		border(s->screen, r, s->border, s->bordercolor->image, s->bordercolor->image->r.min);
93 		r = insetrect(r, s->border);
94 	}
95 	if(s->max <= 0)
96 		return;
97 	if(s->horizontal)
98 		d = Dx(r);
99 	else
100 		d = Dy(r);
101 	l = muldiv(s->value, d, s->max);
102 	h = muldiv(s->value+s->vis, d, s->max);
103 	if(s->clamplow && s->clamphigh){
104 		l = 0;
105 		h = d;
106 	}else if(s->clamplow){
107 		h = l;
108 		l = 0;
109 	}else if(s->clamphigh)
110 		h = d;
111 	t = r;
112 	if(s->horizontal){
113 		r.max.x = r.min.x+h;
114 		r.min.x += l;
115 	}else{
116 		r.max.y = r.min.y+h;
117 		r.min.y += l;
118 	}
119 	if(rectclip(&r, t))
120 		draw(s->screen, r, s->indicatorcolor->image, nil, s->indicatorcolor->image->r.min);
121 	flushimage(display, 1);
122 }
123 
124 static void
sliderctl(Control * c,CParse * cp)125 sliderctl(Control *c, CParse *cp)
126 {
127 	int cmd, prev;
128 	Rectangle r;
129 	Slider *s;
130 
131 	s = (Slider*)c;
132 	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
133 	switch(cmd){
134 	default:
135 		ctlerror("%q: unrecognized message '%s'", s->name, cp->str);
136 		break;
137 	case EAbsolute:
138 		_ctlargcount(s, cp, 2);
139 		s->absolute = cp->iargs[1];
140 		break;
141 	case EBorder:
142 		_ctlargcount(s, cp, 2);
143 		if(cp->iargs[1] < 0)
144 			ctlerror("%q: bad border: %c", s->name, cp->str);
145 		s->border = cp->iargs[1];
146 		break;
147 	case EBordercolor:
148 		_ctlargcount(s, cp, 2);
149 		_setctlimage(s, &s->bordercolor, cp->args[1]);
150 		break;
151 	case EClamp:
152 		_ctlargcount(s, cp, 3);
153 		if(strcmp(cp->args[1], "high") == 0)
154 			s->clamphigh = cp->iargs[2];
155 		else if(strcmp(cp->args[1], "low") == 0)
156 			s->clamplow = cp->iargs[2];
157 		else
158 			ctlerror("%q: unrecognized clamp: %s", s->name, cp->str);
159 		break;
160 	case EFocus:
161 		/* ignore focus change */
162 		break;
163 	case EFormat:
164 		_ctlargcount(s, cp, 2);
165 		s->format = ctlstrdup(cp->args[1]);
166 		break;
167 	case EHide:
168 		_ctlargcount(s, cp, 1);
169 		s->hidden = 1;
170 		break;
171 	case EImage:
172 		_ctlargcount(s, cp, 2);
173 		_setctlimage(s, &s->image, cp->args[1]);
174 		break;
175 	case EIndicatorcolor:
176 		_ctlargcount(s, cp, 2);
177 		_setctlimage(s, &s->indicatorcolor, cp->args[1]);
178 		break;
179 	case EMax:
180 		_ctlargcount(s, cp, 2);
181 		if(cp->iargs[1] < 0)
182 			ctlerror("%q: negative max value: %s", s->name, cp->str);
183 		if(s->max != cp->iargs[1]){
184 			s->max = cp->iargs[1];
185 			slidershow(s);
186 		}
187 		break;
188 	case EOrient:
189 		_ctlargcount(s, cp, 2);
190 		prev = s->horizontal;
191 		if(strncmp(cp->args[1], "hor", 3) == 0)
192 			s->horizontal = 1;
193 		else if(strncmp(cp->args[1], "ver", 3) == 0)
194 			s->horizontal = 0;
195 		else
196 			ctlerror("%q: unrecognized orientation: %s", s->name, cp->str);
197 		if(s->horizontal != prev)
198 			slidershow(s);
199 		break;
200 	case ERect:
201 		_ctlargcount(s, cp, 5);
202 		r.min.x = cp->iargs[1];
203 		r.min.y = cp->iargs[2];
204 		r.max.x = cp->iargs[3];
205 		r.max.y = cp->iargs[4];
206 		if(Dx(r)<=0 || Dy(r)<=0)
207 			ctlerror("%q: bad rectangle: %s", s->name, cp->str);
208 		s->rect = r;
209 		break;
210 	case EReveal:
211 		_ctlargcount(s, cp, 1);
212 		s->hidden = 0;
213 		slidershow(s);
214 		break;
215 	case EShow:
216 		_ctlargcount(s, cp, 1);
217 		slidershow(s);
218 		break;
219 	case ESize:
220 		if (cp->nargs == 3)
221 			r.max = Pt(0x7fffffff, 0x7fffffff);
222 		else{
223 			_ctlargcount(s, cp, 5);
224 			r.max.x = cp->iargs[3];
225 			r.max.y = cp->iargs[4];
226 		}
227 		r.min.x = cp->iargs[1];
228 		r.min.y = cp->iargs[2];
229 		if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
230 			ctlerror("%q: bad sizes: %s", s->name, cp->str);
231 		s->size.min = r.min;
232 		s->size.max = r.max;
233 		break;
234 	case EValue:
235 		_ctlargcount(s, cp, 2);
236 		if(s->value != cp->iargs[1]){
237 			s->value = cp->iargs[1];
238 			slidershow(s);
239 		}
240 		break;
241 	case EVis:
242 		_ctlargcount(s, cp, 2);
243 		if(s->vis != cp->iargs[1]){
244 			s->vis = cp->iargs[1];
245 			slidershow(s);
246 		}
247 		break;
248 	}
249 }
250 
251 static void
slidermouse(Control * c,Mouse * m)252 slidermouse(Control *c, Mouse *m)
253 {
254 	Rectangle r;
255 	int v, l, d, b;
256 	Slider *s;
257 
258 	s =(Slider*)c;
259 	if(m->buttons == 0){
260 		/* buttons now up */
261 		s->lastbut = 0;
262 		return;
263 	}
264 	if(!s->absolute && s->lastbut==m->buttons && s->lastbut!=2){
265 		/* clicks only on buttons 1 & 3; continuous motion on 2 (or when absolute) */
266 		return;
267 	}
268 	if(s->lastbut!=0 && m->buttons!=s->lastbut){
269 		/* buttons down have changed; wait for button up */
270 		return;
271 	}
272 	s->lastbut = m->buttons;
273 
274 	r = insetrect(s->rect, s->border);
275 	if(s->horizontal){
276 		v = m->xy.x - r.min.x;
277 		d = Dx(r);
278 	}else{
279 		v = m->xy.y - r.min.y;
280 		d = Dy(r);
281 	}
282 	if(s->absolute)
283 		b = 2;
284 	else
285 		b = m->buttons;
286 	switch(b){
287 	default:
288 		return;
289 	case 1:
290 		l = s->value - muldiv(v, s->vis, d);
291 		break;
292 	case 2:
293 		l = muldiv(v, s->max, d);
294 		break;
295 	case 4:
296 		l = s->value + muldiv(v, s->vis, d);
297 		break;
298 	}
299 	if(l < 0)
300 		l = 0;
301 	if(l > s->max)
302 		l = s->max;
303 	if(l != s->value){
304 		s->value = l;
305 		chanprint(s->event, s->format, s->name, s->value);
306 		slidershow(s);
307 	}
308 }
309 
310 Control*
createslider(Controlset * cs,char * name)311 createslider(Controlset *cs, char *name)
312 {
313 	Slider *s;
314 
315 	s = (Slider*)_createctl(cs, "slider", sizeof(Slider), name);
316 	s->image = _getctlimage("white");
317 	s->textcolor = _getctlimage("black");
318 	s->bordercolor = _getctlimage("black");
319 	s->indicatorcolor = _getctlimage("black");
320 	s->format = ctlstrdup("%q: value %d");
321 	s->border = 0;
322 	s->mouse = slidermouse;
323 	s->ctl = sliderctl;
324 	s->exit = sliderfree;
325 	return (Control*)s;
326 }
327