xref: /haiku/src/preferences/appearance/APRView.cpp (revision 19289f5b3a282192ea0fc1f769d2b9bb7cc2eb68)
1 /*
2  * Copyright 2002-2015 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm, darkwyrm@earthlink.net
7  *		Rene Gollent, rene@gollent.com
8  *		John Scipione, jscipione@gmail.com
9  *		Joseph Groover <looncraz@looncraz.net>
10  */
11 
12 
13 #include "APRView.h"
14 
15 #include <stdio.h>
16 
17 #include <Alert.h>
18 #include <Catalog.h>
19 #include <DefaultColors.h>
20 #include <Directory.h>
21 #include <Entry.h>
22 #include <File.h>
23 #include <HSL.h>
24 #include <LayoutBuilder.h>
25 #include <Locale.h>
26 #include <Messenger.h>
27 #include <Path.h>
28 #include <SpaceLayoutItem.h>
29 
30 #include "APRWindow.h"
31 #include "defs.h"
32 #include "ColorPreview.h"
33 #include "Colors.h"
34 #include "ColorWhichListView.h"
35 #include "ColorWhichItem.h"
36 
37 
38 #undef B_TRANSLATION_CONTEXT
39 #define B_TRANSLATION_CONTEXT "Colors tab"
40 
41 #define COLOR_DROPPED 'cldp'
42 #define AUTO_ADJUST_CHANGED 'madj'
43 
44 
APRView(const char * name)45 APRView::APRView(const char* name)
46 	:
47 	BView(name, B_WILL_DRAW)
48 {
49 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
50 
51 	LoadSettings();
52 
53 	fAutoSelectCheckBox = new BCheckBox(B_TRANSLATE("Automatically pick secondary colors"),
54 		new BMessage(AUTO_ADJUST_CHANGED));
55 	fAutoSelectCheckBox->SetValue(true);
56 
57 	// Set up list of color attributes
58 	fAttrList = new ColorWhichListView("AttributeList");
59 
60 	fScrollView = new BScrollView("ScrollView", fAttrList, 0, false, true);
61 	fScrollView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
62 
63 	_CreateItems();
64 
65 	fColorPreview = new ColorPreview(new BMessage(COLOR_DROPPED), 0);
66 	fColorPreview->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
67 		B_ALIGN_VERTICAL_CENTER));
68 
69 	fPicker = new BColorControl(B_ORIGIN, B_CELLS_32x8, 8.0,
70 		"picker", new BMessage(UPDATE_COLOR));
71 
72 	BLayoutBuilder::Group<>(this, B_VERTICAL)
73 		.Add(fAutoSelectCheckBox)
74 		.Add(fScrollView, 10.0)
75 		.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
76 			.Add(fColorPreview)
77 			.AddGlue()
78 			.Add(fPicker)
79 			.End()
80 		.SetInsets(B_USE_WINDOW_SPACING);
81 
82 	fColorPreview->Parent()->SetExplicitMaxSize(
83 		BSize(B_SIZE_UNSET, fPicker->Bounds().Height()));
84 	fAttrList->SetSelectionMessage(new BMessage(ATTRIBUTE_CHOSEN));
85 }
86 
87 
~APRView()88 APRView::~APRView()
89 {
90 }
91 
92 
93 void
AttachedToWindow()94 APRView::AttachedToWindow()
95 {
96 	fAutoSelectCheckBox->SetTarget(this);
97 	fPicker->SetTarget(this);
98 	fAttrList->SetTarget(this);
99 	fColorPreview->SetTarget(this);
100 
101 	fAttrList->Select(0);
102 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
103 }
104 
105 
106 void
MessageReceived(BMessage * msg)107 APRView::MessageReceived(BMessage *msg)
108 {
109 	switch (msg->what) {
110 		case SET_COLOR:
111 		{
112 			rgb_color* color;
113 			ssize_t size;
114 			color_which which;
115 
116 			if (msg->FindData(kRGBColor, B_RGB_COLOR_TYPE,
117 					(const void**)&color, &size) == B_OK
118 				&& msg->FindUInt32(kWhich, (uint32*)&which) == B_OK) {
119 				_SetColor(which, *color);
120 				Window()->PostMessage(kMsgUpdate);
121 			}
122 			break;
123 		}
124 
125 		case SET_CURRENT_COLOR:
126 		{
127 			rgb_color* color;
128 			ssize_t size;
129 
130 			if (msg->FindData(kRGBColor, B_RGB_COLOR_TYPE,
131 					(const void**)&color, &size) == B_OK) {
132 				_SetCurrentColor(*color);
133 				Window()->PostMessage(kMsgUpdate);
134 			}
135 			break;
136 		}
137 
138 		case UPDATE_COLOR:
139 		{
140 			// Received from the color fPicker when its color changes
141 			rgb_color color = fPicker->ValueAsColor();
142 			_SetCurrentColor(color);
143 
144 			Window()->PostMessage(kMsgUpdate);
145 			break;
146 		}
147 
148 		case ATTRIBUTE_CHOSEN:
149 		{
150 			// Received when the user chooses a GUI fAttribute from the list
151 
152 			ColorWhichItem* item = (ColorWhichItem*)
153 				fAttrList->ItemAt(fAttrList->CurrentSelection());
154 			if (item == NULL)
155 				break;
156 
157 			fWhich = item->ColorWhich();
158 			rgb_color color = ui_color(fWhich);
159 			_SetCurrentColor(color);
160 			break;
161 		}
162 
163 		case AUTO_ADJUST_CHANGED:
164 		{
165 			_CreateItems();
166 			break;
167 		}
168 
169 		default:
170 			BView::MessageReceived(msg);
171 			break;
172 	}
173 }
174 
175 
176 void
LoadSettings()177 APRView::LoadSettings()
178 {
179 	get_default_colors(&fDefaultColors);
180 	get_current_colors(&fCurrentColors);
181 	fPrevColors = fCurrentColors;
182 }
183 
184 
185 void
SetDefaults()186 APRView::SetDefaults()
187 {
188 	_SetUIColors(fDefaultColors);
189 	_UpdatePreviews(fDefaultColors);
190 
191 	// Use a default color that stands out to show errors clearly
192 	rgb_color color = fDefaultColors.GetColor(ui_color_name(fWhich),
193 		make_color(255, 0, 255));
194 
195 	fPicker->SetValue(color);
196 	fColorPreview->SetColor(color);
197 	fColorPreview->Invalidate();
198 
199 	Window()->PostMessage(kMsgUpdate);
200 }
201 
202 
203 void
Revert()204 APRView::Revert()
205 {
206 	_SetUIColors(fPrevColors);
207 	_UpdatePreviews(fPrevColors);
208 
209 	rgb_color color = fPrevColors.GetColor(ui_color_name(fWhich),
210 		make_color(255, 0, 255));
211 	fPicker->SetValue(color);
212 	fColorPreview->SetColor(color);
213 	fColorPreview->Invalidate();
214 
215 	Window()->PostMessage(kMsgUpdate);
216 }
217 
218 
219 bool
IsDefaultable()220 APRView::IsDefaultable()
221 {
222 	return !fDefaultColors.HasSameData(fCurrentColors);
223 }
224 
225 
226 bool
IsRevertable()227 APRView::IsRevertable()
228 {
229 	return !fPrevColors.HasSameData(fCurrentColors);
230 }
231 
232 
233 void
_CreateItems()234 APRView::_CreateItems()
235 {
236 	while (fAttrList->CountItems() > 0)
237 		delete fAttrList->RemoveItem((int32)0);
238 
239 	const bool autoSelect = fAutoSelectCheckBox->Value();
240 	const int32 count = color_description_count();
241 	for (int32 i = 0; i < count; i++) {
242 		const ColorDescription& description = *get_color_description(i);
243 		const color_which which = description.which;
244 		if (autoSelect) {
245 			if (which != B_PANEL_BACKGROUND_COLOR
246 					&& which != B_STATUS_BAR_COLOR
247 					&& which != B_WINDOW_TAB_COLOR) {
248 				continue;
249 			}
250 		}
251 
252 		const char* text = B_TRANSLATE_NOCOLLECT(description.text);
253 		fAttrList->AddItem(new ColorWhichItem(text, which, ui_color(which)));
254 	}
255 
256 	if (Window() != NULL)
257 		fAttrList->Select(0);
258 }
259 
260 
261 void
_UpdatePreviews(const BMessage & colors)262 APRView::_UpdatePreviews(const BMessage& colors)
263 {
264 	rgb_color color;
265 	for (int32 i = color_description_count() - 1; i >= 0; i--) {
266 		ColorWhichItem* item = static_cast<ColorWhichItem*>(fAttrList->ItemAt(i));
267 		if (item == NULL)
268 			continue;
269 
270 		color = colors.GetColor(ui_color_name(item->ColorWhich()),
271 			make_color(255, 0, 255));
272 
273 		item->SetColor(color);
274 		fAttrList->InvalidateItem(i);
275 	}
276 }
277 
278 
279 void
_SetUIColors(const BMessage & colors)280 APRView::_SetUIColors(const BMessage& colors)
281 {
282 	set_ui_colors(&colors);
283 	fCurrentColors = colors;
284 }
285 
286 
287 void
_SetCurrentColor(rgb_color color)288 APRView::_SetCurrentColor(rgb_color color)
289 {
290 	_SetColor(fWhich, color);
291 
292 	int32 currentIndex = fAttrList->CurrentSelection();
293 	ColorWhichItem* item = (ColorWhichItem*)fAttrList->ItemAt(currentIndex);
294 	if (item != NULL) {
295 		item->SetColor(color);
296 		fAttrList->InvalidateItem(currentIndex);
297 	}
298 
299 	fPicker->SetValue(color);
300 	fColorPreview->SetColor(color);
301 	fColorPreview->Invalidate();
302 }
303 
304 
305 void
_SetColor(color_which which,rgb_color color)306 APRView::_SetColor(color_which which, rgb_color color)
307 {
308 	_SetOneColor(which, color);
309 
310 	if (!fAutoSelectCheckBox->Value())
311 		return;
312 
313 	// Protect against accidentally overwriting colors.
314 	if (ui_color(which) == color)
315 		return;
316 
317 	if (which == B_PANEL_BACKGROUND_COLOR) {
318 		const bool isDark = color.IsDark();
319 
320 		_SetOneColor(B_MENU_BACKGROUND_COLOR, color);
321 		_SetOneColor(B_SCROLL_BAR_THUMB_COLOR, color);
322 
323 		const rgb_color menuSelectedBackground
324 			= tint_color(color, isDark ? 0.8 /* lighten "< 1" */ : B_DARKEN_2_TINT);
325 		_SetOneColor(B_MENU_SELECTED_BACKGROUND_COLOR, menuSelectedBackground);
326 
327 		const rgb_color controlBackground = tint_color(color, isDark
328 			? 0.8 /* lighten "< 1" */ : 0.25 /* lighten "> 2" */);
329 		_SetOneColor(B_CONTROL_BACKGROUND_COLOR, controlBackground);
330 
331 		const rgb_color controlBorder
332 			= tint_color(color, isDark ? 0.4875 : 1.20 /* lighten/darken "1.5" */);
333 		_SetOneColor(B_CONTROL_BORDER_COLOR, controlBorder);
334 
335 		const rgb_color windowBorder = tint_color(color, 0.75);
336 		_SetOneColor(B_WINDOW_BORDER_COLOR, windowBorder);
337 
338 		const rgb_color inactiveWindowBorder = tint_color(color, B_LIGHTEN_1_TINT);
339 		_SetOneColor(B_WINDOW_INACTIVE_TAB_COLOR, inactiveWindowBorder);
340 		_SetOneColor(B_WINDOW_INACTIVE_BORDER_COLOR, inactiveWindowBorder);
341 
342 		const rgb_color listSelectedBackground
343 			= tint_color(color, isDark ? 0.77 : 1.12 /* lighten/darken "< 1" */ );
344 		_SetOneColor(B_LIST_SELECTED_BACKGROUND_COLOR, listSelectedBackground);
345 
346 		const color_which fromDefaults[] = {
347 			B_MENU_ITEM_TEXT_COLOR,
348 			B_MENU_SELECTED_ITEM_TEXT_COLOR,
349 			B_MENU_SELECTED_BORDER_COLOR,
350 			B_PANEL_TEXT_COLOR,
351 			B_DOCUMENT_BACKGROUND_COLOR,
352 			B_DOCUMENT_TEXT_COLOR,
353 			B_CONTROL_TEXT_COLOR,
354 			B_NAVIGATION_PULSE_COLOR,
355 			B_WINDOW_INACTIVE_TEXT_COLOR,
356 			B_LIST_BACKGROUND_COLOR,
357 			B_LIST_ITEM_TEXT_COLOR,
358 			B_LIST_SELECTED_ITEM_TEXT_COLOR,
359 
360 			B_SHINE_COLOR,
361 			B_SHADOW_COLOR,
362 
363 			B_LINK_TEXT_COLOR,
364 			B_LINK_HOVER_COLOR,
365 			B_LINK_ACTIVE_COLOR,
366 			B_LINK_VISITED_COLOR,
367 		};
368 		for (size_t i = 0; i < B_COUNT_OF(fromDefaults); i++)
369 			_SetOneColor(fromDefaults[i], BPrivate::GetSystemColor(fromDefaults[i], isDark));
370 	} else if (which == B_STATUS_BAR_COLOR) {
371 		const hsl_color statusColorHSL = hsl_color::from_rgb(color);
372 
373 		hsl_color controlHighlight = statusColorHSL;
374 		controlHighlight.saturation = max_c(0.2f, controlHighlight.saturation / 2.f);
375 		_SetOneColor(B_CONTROL_HIGHLIGHT_COLOR, controlHighlight.to_rgb());
376 
377 		hsl_color controlMark = statusColorHSL;
378 		controlMark.saturation = max_c(0.2f, controlMark.saturation * 0.67f);
379 		controlMark.lightness = max_c(0.25f, controlMark.lightness * 0.55f);
380 		_SetOneColor(B_CONTROL_MARK_COLOR, controlMark.to_rgb());
381 
382 		rgb_color keyboardNav; {
383 			hsl_color keyboardNavHSL = statusColorHSL;
384 			keyboardNavHSL.lightness = max_c(0.2f, keyboardNavHSL.lightness * 0.75f);
385 			keyboardNav = keyboardNavHSL.to_rgb();
386 
387 			// Use primary color channel only.
388 			if (keyboardNav.blue >= max_c(keyboardNav.red, keyboardNav.green))
389 				keyboardNav.red = keyboardNav.green = 0;
390 			else if (keyboardNav.red >= max_c(keyboardNav.green, keyboardNav.blue))
391 				keyboardNav.green = keyboardNav.blue = 0;
392 			else
393 				keyboardNav.red = keyboardNav.blue = 0;
394 		}
395 		_SetOneColor(B_KEYBOARD_NAVIGATION_COLOR, keyboardNav);
396 	} else if (which == B_WINDOW_TAB_COLOR) {
397 		const bool isDark = color.IsDark();
398 		const hsl_color tabColorHSL = hsl_color::from_rgb(color);
399 		const float tabColorSaturation
400 			= tabColorHSL.saturation != 0 ? tabColorHSL.saturation : tabColorHSL.lightness;
401 
402 		_SetOneColor(B_WINDOW_TEXT_COLOR,
403 			BPrivate::GetSystemColor(B_WINDOW_TEXT_COLOR, isDark));
404 		_SetOneColor(B_TOOL_TIP_TEXT_COLOR,
405 			BPrivate::GetSystemColor(B_TOOL_TIP_TEXT_COLOR, isDark));
406 
407 		const rgb_color toolTipBackground = tint_color(color, isDark ? 1.7 : 0.15);
408 		_SetOneColor(B_TOOL_TIP_BACKGROUND_COLOR, toolTipBackground);
409 
410 		hsl_color success = hsl_color::from_rgb(BPrivate::GetSystemColor(B_SUCCESS_COLOR, isDark));
411 		success.saturation = max_c(0.25f, tabColorSaturation * 0.68f);
412 		_SetOneColor(B_SUCCESS_COLOR, success.to_rgb());
413 
414 		hsl_color failure = hsl_color::from_rgb(BPrivate::GetSystemColor(B_FAILURE_COLOR, isDark));
415 		failure.saturation = max_c(0.25f, tabColorSaturation);
416 		_SetOneColor(B_FAILURE_COLOR, failure.to_rgb());
417 	}
418 }
419 
420 
421 void
_SetOneColor(color_which which,rgb_color color)422 APRView::_SetOneColor(color_which which, rgb_color color)
423 {
424 	if (ui_color(which) == color)
425 		return;
426 
427 	set_ui_color(which, color);
428 	fCurrentColors.SetColor(ui_color_name(which), color);
429 }
430