xref: /haiku/src/apps/icon-o-matic/generic/gui/panel/color_picker/ColorSlider.cpp (revision f068ecea0a72f16044bfa3cb932d57576fa622d3)
1 /*
2  * Copyright 2001 Werner Freytag - please read to the LICENSE file
3  *
4  * Copyright 2002-2015, Stephan Aßmus <superstippi@gmx.de>
5  * All rights reserved.
6  *
7  */
8 
9 #include "ColorSlider.h"
10 
11 #include <stdio.h>
12 
13 #include <Bitmap.h>
14 #include <ControlLook.h>
15 #include <LayoutUtils.h>
16 #include <OS.h>
17 #include <Window.h>
18 #include <math.h>
19 
20 #include "support_ui.h"
21 
22 #include "rgb_hsv.h"
23 
24 
25 #define round(x) (int)(x+.5)
26 
27 enum {
28 	MSG_UPDATE			= 'Updt',
29 };
30 
31 #define MAX_X 255
32 #define MAX_Y 255
33 
34 
ColorSlider(SelectedColorMode mode,float value1,float value2,orientation dir,border_style border)35 ColorSlider::ColorSlider(SelectedColorMode mode,
36 	float value1, float value2, orientation dir, border_style border)
37 	:  BControl("ColorSlider", "", new BMessage(MSG_COLOR_SLIDER),
38 		B_WILL_DRAW | B_FRAME_EVENTS)
39 {
40 	_Init(mode, value1, value2, dir, border);
41 	FrameResized(Bounds().Width(), Bounds().Height());
42 }
43 
44 
ColorSlider(BPoint offsetPoint,SelectedColorMode mode,float value1,float value2,orientation dir,border_style border)45 ColorSlider::ColorSlider(BPoint offsetPoint, SelectedColorMode mode,
46 	float value1, float value2, orientation dir, border_style border)
47 	:  BControl(BRect(0.0, 0.0, 35.0, 265.0).OffsetToCopy(offsetPoint),
48 		"ColorSlider", "", new BMessage(MSG_COLOR_SLIDER),
49 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS)
50 {
51 	_Init(mode, value1, value2, dir, border);
52 	FrameResized(Bounds().Width(), Bounds().Height());
53 }
54 
55 
~ColorSlider()56 ColorSlider::~ColorSlider()
57 {
58 	delete fBitmap;
59 }
60 
61 
62 BSize
MinSize()63 ColorSlider::MinSize()
64 {
65 	BSize minSize;
66 	if (fOrientation == B_VERTICAL)
67 		minSize = BSize(36, 10 + MAX_Y / 17);
68 	else
69 		minSize = BSize(10 + MAX_X / 17, 10);
70 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize);
71 }
72 
73 
74 BSize
PreferredSize()75 ColorSlider::PreferredSize()
76 {
77 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize());
78 }
79 
80 
81 BSize
MaxSize()82 ColorSlider::MaxSize()
83 {
84 	BSize maxSize;
85 	if (fOrientation == B_VERTICAL)
86 		maxSize = BSize(36, 10 + MAX_Y);
87 	else
88 		maxSize = BSize(10 + MAX_X, 18);
89 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
90 }
91 
92 
93 void
AttachedToWindow()94 ColorSlider::AttachedToWindow()
95 {
96 }
97 
98 
99 status_t
Invoke(BMessage * message)100 ColorSlider::Invoke(BMessage* message)
101 {
102 	if (message == NULL)
103 		message = Message();
104 
105 	if (message == NULL)
106 		return BControl::Invoke(message);
107 
108 	message->RemoveName("value");
109 	message->RemoveName("begin");
110 
111 	switch (fMode) {
112 		case R_SELECTED:
113 		case G_SELECTED:
114 		case B_SELECTED:
115 			message->AddFloat("value", 1.0 - (float)Value() / 255);
116 			break;
117 
118 		case H_SELECTED:
119 			message->AddFloat("value", (1.0 - (float)Value() / 255) * 6);
120 			break;
121 
122 		case S_SELECTED:
123 		case V_SELECTED:
124 			message->AddFloat("value", 1.0 - (float)Value() / 255);
125 			break;
126 	}
127 
128 	// some other parts of WonderBrush rely on this.
129 	// if the flag is present, it triggers generating an undo action
130 	// fMouseDown is not set yet the first message is sent
131 	if (!fMouseDown)
132 		message->AddBool("begin", true);
133 
134 	return BControl::Invoke(message);
135 }
136 
137 
138 void
Draw(BRect updateRect)139 ColorSlider::Draw(BRect updateRect)
140 {
141 	if (fBitmapDirty && fBitmap != NULL) {
142 		_FillBitmap(fBitmap, fMode, fFixedValue1, fFixedValue2, fOrientation);
143 		fBitmapDirty = false;
144 	}
145 
146 	BRect bounds = _BitmapRect();
147 
148 	// Frame
149 	if (fBorderStyle == B_FANCY_BORDER) {
150 		bounds.InsetBy(-2, -2);
151 		rgb_color color = LowColor();
152 		be_control_look->DrawTextControlBorder(this, bounds, updateRect,
153 			color);
154 	}
155 
156 	// Color slider fill
157 	if (fBitmap != NULL)
158 		DrawBitmap(fBitmap, bounds.LeftTop());
159 	else {
160 		SetHighColor(255, 0, 0);
161 		FillRect(bounds);
162 	}
163 
164 	// Marker background
165 	if (fOrientation == B_VERTICAL) {
166 		bounds.InsetBy(-2, -2);
167 		BRect r = Bounds();
168 		FillRect(BRect(r.left, r.top, bounds.left - 1, r.bottom),
169 			B_SOLID_LOW);
170 		FillRect(BRect(bounds.right + 1, r.top, r.right, r.bottom),
171 			B_SOLID_LOW);
172 		FillRect(
173 			BRect(bounds.left, r.top, bounds.right, bounds.top - 1),
174 			B_SOLID_LOW);
175 		FillRect(
176 			BRect(bounds.left, bounds.bottom + 1, bounds.right, r.bottom),
177 			B_SOLID_LOW);
178 	}
179 
180 	// marker
181 	if (fOrientation == B_VERTICAL) {
182 		// draw the triangle markers
183 		SetHighColor(0, 0, 0);
184 		BRect r = Bounds();
185 		float offset = Value() * (r.Height() - 10) / 255.0;
186 		_DrawTriangle(
187 			BPoint(r.left, offset),
188 			BPoint(r.left + 5.0, offset + 5.0),
189 			BPoint(r.left, offset + 10.0));
190 
191 		_DrawTriangle(
192 			BPoint(r.right, offset),
193 			BPoint(r.right - 5.0, offset + 5.0),
194 			BPoint(r.right, offset + 10.0));
195 	} else {
196 		BRect r = bounds;
197 		float x = bounds.left - 2 + (255 - Value()) * bounds.Width() / 255.0;
198 		if (x > r.left) {
199 			SetHighColor(255, 255, 255);
200 			StrokeLine(BPoint(x, bounds.top),
201 				BPoint(x, bounds.bottom));
202 		}
203 		if (x + 1 > r.left) {
204 			SetHighColor(0, 0, 0);
205 			StrokeLine(BPoint(x + 1, bounds.top),
206 				BPoint(x + 1, bounds.bottom));
207 		}
208 		if (x + 3 < r.right) {
209 			SetHighColor(0, 0, 0);
210 			StrokeLine(BPoint(x + 3, bounds.top),
211 				BPoint(x + 3, bounds.bottom));
212 		}
213 		if (x + 4 < r.right) {
214 			SetHighColor(255, 255, 255);
215 			StrokeLine(BPoint(x + 4, bounds.top),
216 				BPoint(x + 4, bounds.bottom));
217 		}
218 	}
219 }
220 
221 
222 void
FrameResized(float width,float height)223 ColorSlider::FrameResized(float width, float height)
224 {
225 	BRect r = _BitmapRect();
226 	_AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1);
227 	Invalidate();
228 }
229 
230 
231 void
MouseDown(BPoint where)232 ColorSlider::MouseDown(BPoint where)
233 {
234 	SetMouseEventMask(B_POINTER_EVENTS,
235 		B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS);
236 	_TrackMouse(where);
237 	fMouseDown = true;
238 }
239 
240 
241 void
MouseUp(BPoint where)242 ColorSlider::MouseUp(BPoint where)
243 {
244 	fMouseDown = false;
245 }
246 
247 
248 void
MouseMoved(BPoint where,uint32 code,const BMessage * dragMessage)249 ColorSlider::MouseMoved( BPoint where, uint32 code,
250 	const BMessage* dragMessage)
251 {
252 	if (dragMessage != NULL || !fMouseDown)
253 		return;
254 
255 	_TrackMouse(where);
256 }
257 
258 
259 void
SetValue(int32 value)260 ColorSlider::SetValue(int32 value)
261 {
262 	value = max_c(min_c(value, 255), 0);
263 	if (value != Value()) {
264 		BControl::SetValue(value);
265 		Invalidate();
266 	}
267 }
268 
269 
270 void
SetModeAndValues(SelectedColorMode mode,float value1,float value2)271 ColorSlider::SetModeAndValues(SelectedColorMode mode,
272 							  float value1, float value2)
273 {
274 	float R = 0;
275 	float G = 0;
276 	float B = 0;
277 	float h = 0;
278 	float s = 0;
279 	float v = 0;
280 
281 	switch (fMode) {
282 		case R_SELECTED:
283 			R = 255 - Value();
284 			G = round(fFixedValue1 * 255.0);
285 			B = round(fFixedValue2 * 255.0);
286 			break;
287 
288 		case G_SELECTED:
289 			R = round(fFixedValue1 * 255.0);
290 			G = 255 - Value();
291 			B = round(fFixedValue2 * 255.0);
292 			break;
293 
294 		case B_SELECTED:
295 			R = round(fFixedValue1 * 255.0);
296 			G = round(fFixedValue2 * 255.0);
297 			B = 255 - Value();
298 			break;
299 
300 		case H_SELECTED:
301 			h = (1.0 - (float)Value()/255.0)*6.0;
302 			s = fFixedValue1;
303 			v = fFixedValue2;
304 			break;
305 
306 		case S_SELECTED:
307 			h = fFixedValue1;
308 			s = 1.0 - (float)Value()/255.0;
309 			v = fFixedValue2;
310 			break;
311 
312 		case V_SELECTED:
313 			h = fFixedValue1;
314 			s = fFixedValue2;
315 			v = 1.0 - (float)Value()/255.0;
316 			break;
317 	}
318 
319 	if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) {
320 		HSV_to_RGB(h, s, v, R, G, B);
321 		R *= 255.0;
322 		G *= 255.0;
323 		B *= 255.0;
324 	}
325 
326 	rgb_color color = { (uint8)round(R), (uint8)round(G), (uint8)round(B),
327 		255 };
328 
329 	fMode = mode;
330 	SetOtherValues(value1, value2);
331 
332 	SetMarkerToColor(color);
333 	_Update();
334 }
335 
336 
337 void
SetOtherValues(float value1,float value2)338 ColorSlider::SetOtherValues(float value1, float value2)
339 {
340 	fFixedValue1 = value1;
341 	fFixedValue2 = value2;
342 
343 	if (fMode != H_SELECTED)
344 		_Update();
345 }
346 
347 
348 void
GetOtherValues(float * value1,float * value2) const349 ColorSlider::GetOtherValues(float* value1, float* value2) const
350 {
351 	if (value1 != NULL)
352 		*value1 = fFixedValue1;
353 
354 	if (value2 != NULL)
355 		*value2 = fFixedValue2;
356 }
357 
358 
359 void
SetMarkerToColor(rgb_color color)360 ColorSlider::SetMarkerToColor(rgb_color color)
361 {
362 	float h = 0;
363 	float s = 0;
364 	float v = 0;
365 	if ((fMode & (H_SELECTED | S_SELECTED | V_SELECTED)) != 0) {
366 		RGB_to_HSV(
367 			(float)color.red / 255.0,
368 			(float)color.green / 255.0,
369 			(float)color.blue / 255.0,
370 			h, s, v
371 		);
372 	}
373 
374 	switch (fMode) {
375 		case R_SELECTED:
376 			SetValue(255 - color.red);
377 			break;
378 
379 		case G_SELECTED:
380 			SetValue(255 - color.green);
381 			break;
382 
383 		case B_SELECTED:
384 			SetValue(255 - color.blue);
385 			break;
386 
387 		case H_SELECTED:
388 			SetValue(255.0 - round(h / 6.0 * 255.0));
389 			break;
390 
391 		case S_SELECTED:
392 			SetValue(255.0 - round(s * 255.0));
393 			break;
394 
395 		case V_SELECTED:
396 			SetValue(255.0 - round(v * 255.0));
397 			break;
398 	}
399 }
400 
401 
402 // #pragma mark - private
403 
404 
405 void
_Init(SelectedColorMode mode,float value1,float value2,orientation dir,border_style border)406 ColorSlider::_Init(SelectedColorMode mode, float value1, float value2,
407 	orientation dir, border_style border)
408 {
409 	fMode = mode;
410 	fFixedValue1 = value1;
411 	fFixedValue2 = value2;
412 	fMouseDown = false;
413 	fBitmap = NULL;
414 	fBitmapDirty = true;
415 	fOrientation = dir;
416 	fBorderStyle = border;
417 
418 	SetViewColor(B_TRANSPARENT_COLOR);
419 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
420 }
421 
422 
423 void
_AllocBitmap(int32 width,int32 height)424 ColorSlider::_AllocBitmap(int32 width, int32 height)
425 {
426 	if (width < 2 || height < 2)
427 		return;
428 
429 	delete fBitmap;
430 	fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32);
431 
432 	fBitmapDirty = true;
433 }
434 
435 
436 void
_Update()437 ColorSlider::_Update()
438 {
439 	fBitmapDirty = true;
440 	Invalidate();
441 }
442 
443 
444 BRect
_BitmapRect() const445 ColorSlider::_BitmapRect() const
446 {
447 	BRect r = Bounds();
448 	if (fOrientation == B_VERTICAL)
449 		r.InsetBy(7, 3);
450 	if (fBorderStyle == B_FANCY_BORDER)
451 		r.InsetBy(2, 2);
452 	return r;
453 }
454 
455 
456 void
_FillBitmap(BBitmap * bitmap,SelectedColorMode mode,float fixedValue1,float fixedValue2,orientation orient) const457 ColorSlider::_FillBitmap(BBitmap* bitmap, SelectedColorMode mode,
458 	float fixedValue1, float fixedValue2, orientation orient) const
459 {
460 	int32 width = bitmap->Bounds().IntegerWidth();
461 	int32 height = bitmap->Bounds().IntegerHeight();
462 	uint32 bpr = bitmap->BytesPerRow();
463 
464 	uint8* bits = (uint8*)bitmap->Bits();
465 
466 	float r = 0;
467 	float g = 0;
468 	float b = 0;
469 	float h;
470 	float s;
471 	float v;
472 
473 	switch (mode) {
474 		case R_SELECTED:
475 			g = round(fixedValue1 * 255);
476 			b = round(fixedValue2 * 255);
477 			if (orient == B_VERTICAL) {
478 				for (int y = 0; y <= height; y++) {
479 					r = 255 - y * 255 / height;
480 					_DrawColorLineY(bits, width, r, g, b);
481 					bits += bpr;
482 				}
483 			} else {
484 				for (int x = 0; x <= width; x++) {
485 					r = x * 255 / width;
486 					_DrawColorLineX(bits, height, bpr, r, g, b);
487 					bits += 4;
488 				}
489 			}
490 			break;
491 
492 		case G_SELECTED:
493 			r = round(fixedValue1 * 255);
494 			b = round(fixedValue2 * 255);
495 			if (orient == B_VERTICAL) {
496 				for (int y = 0; y <= height; y++) {
497 					g = 255 - y * 255 / height;
498 					_DrawColorLineY(bits, width, r, g, b);
499 					bits += bpr;
500 				}
501 			} else {
502 				for (int x = 0; x <= width; x++) {
503 					g = 255 - x * 255 / width;
504 					_DrawColorLineX(bits, height, bpr, r, g, b);
505 					bits += 4;
506 				}
507 			}
508 			break;
509 
510 		case B_SELECTED:
511 			r = round(fixedValue1 * 255);
512 			g = round(fixedValue2 * 255);
513 			if (orient == B_VERTICAL) {
514 				for (int y = 0; y <= height; y++) {
515 					b = 255 - y * 255 / height;
516 					_DrawColorLineY(bits, width, r, g, b);
517 					bits += bpr;
518 				}
519 			} else {
520 				for (int x = 0; x <= width; x++) {
521 					b = x * 255 / width;
522 					_DrawColorLineX(bits, height, bpr, r, g, b);
523 					bits += 4;
524 				}
525 			}
526 
527 		case H_SELECTED:
528 			s = 1.0;//fixedValue1;
529 			v = 1.0;//fixedValue2;
530 			if (orient == B_VERTICAL) {
531 				for (int y = 0; y <= height; y++) {
532 					HSV_to_RGB(6.0 - (float)y * 6.0 / height, s, v, r, g, b);
533 					_DrawColorLineY(bits, width, r * 255, g * 255, b * 255);
534 					bits += bpr;
535 				}
536 			} else {
537 				for (int x = 0; x <= width; x++) {
538 					HSV_to_RGB((float)x * 6.0 / width, s, v, r, g, b);
539 					_DrawColorLineX(bits, height, bpr,
540 						r * 255, g * 255, b * 255);
541 					bits += 4;
542 				}
543 			}
544 			break;
545 
546 		case S_SELECTED:
547 			h = fixedValue1;
548 			v = 1.0;//fixedValue2;
549 			if (orient == B_VERTICAL) {
550 				for (int y = 0; y <= height; y++) {
551 					HSV_to_RGB(h, 1.0 - (float)y / height, v, r, g, b);
552 					_DrawColorLineY(bits, width, r * 255, g * 255, b * 255);
553 					bits += bpr;
554 				}
555 			} else {
556 				for (int x = 0; x <= width; x++) {
557 					HSV_to_RGB(h, 1.0 - (float)x / width, v, r, g, b);
558 					_DrawColorLineX(bits, height, bpr,
559 						r * 255, g * 255, b * 255);
560 					bits += 4;
561 				}
562 			}
563 			break;
564 
565 		case V_SELECTED:
566 			h = fixedValue1;
567 			s = 1.0;//fixedValue2;
568 			if (orient == B_VERTICAL) {
569 				for (int y = 0; y <= height; y++) {
570 					HSV_to_RGB(h, s, 1.0 - (float)y / height, r, g, b);
571 					_DrawColorLineY(bits, width, r * 255, g * 255, b * 255);
572 					bits += bpr;
573 				}
574 			} else {
575 				for (int x = 0; x <= width; x++) {
576 					HSV_to_RGB(h, s, (float)x / width, r, g, b);
577 					_DrawColorLineX(bits, height, bpr,
578 						r * 255, g * 255, b * 255);
579 					bits += 4;
580 				}
581 			}
582 			break;
583 	}
584 }
585 
586 
587 void
_DrawColorLineY(uint8 * bits,int width,int r,int g,int b)588 ColorSlider::_DrawColorLineY(uint8* bits, int width, int r, int g, int b)
589 {
590 	for (int x = 0; x <= width; x++) {
591 		bits[0] = b;
592 		bits[1] = g;
593 		bits[2] = r;
594 		bits[3] = 255;
595 		bits += 4;
596 	}
597 }
598 
599 
600 void
_DrawColorLineX(uint8 * bits,int height,int bpr,int r,int g,int b)601 ColorSlider::_DrawColorLineX(uint8* bits, int height, int bpr,
602 	int r, int g, int b)
603 {
604 	for (int y = 0; y <= height; y++) {
605 		bits[0] = b;
606 		bits[1] = g;
607 		bits[2] = r;
608 		bits[3] = 255;
609 		bits += bpr;
610 	}
611 }
612 
613 
614 void
_DrawTriangle(BPoint point1,BPoint point2,BPoint point3)615 ColorSlider::_DrawTriangle(BPoint point1, BPoint point2, BPoint point3)
616 {
617 	StrokeLine(point1, point2);
618 	StrokeLine(point3);
619 	StrokeLine(point1);
620 }
621 
622 
623 void
_TrackMouse(BPoint where)624 ColorSlider::_TrackMouse(BPoint where)
625 {
626 	BRect b(_BitmapRect());
627 
628 	if (fOrientation == B_VERTICAL)
629 		SetValue((int)(((where.y - b.top) / b.Height()) * 255.0));
630 	else
631 		SetValue(255 - (int)(((where.x - b.left) / b.Width()) * 255.0));
632 
633 	Invoke();
634 }
635