Appearance: Hide most colors by default and compute them automatically.

In the new "automatic" mode, the number of displayed colors
is just 3, as opposed to the full 38. Much more manageable!

The HSL routines added in this commit were derived from
https://gist.github.com/ciembor/1494530 which is itself derived
from the Wikipedia page describing HSL/HSV.

Part of #15543 and #11636.

Change-Id: I230a358d18c379fb0673162e0b3cbdb8d1b8d84e
Reviewed-on: https://review.haiku-os.org/c/haiku/+/7479
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Augustin Cavalier
2024-04-11 17:38:45 +00:00
committed by waddlesplash
parent 9b655000bc
commit 979a0bc487
6 changed files with 296 additions and 28 deletions
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _HSL_H
#define _HSL_H
#include <GraphicsDefs.h>
typedef struct hsl_color {
float hue, saturation, lightness;
static hsl_color from_rgb(const rgb_color& rgb);
rgb_color to_rgb() const;
private:
static float hue_to_rgb(float p, float q, float t);
} hsl_color;
#endif // _HSL_H
+85
View File
@@ -0,0 +1,85 @@
/*
* Copyright 2024, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <HSL.h>
hsl_color
hsl_color::from_rgb(const rgb_color& rgb)
{
hsl_color result;
float r = rgb.red / 255.0f;
float g = rgb.green / 255.0f;
float b = rgb.blue / 255.0f;
float max = max_c(max_c(r, g), b);
float min = min_c(min_c(r, g), b);
result.hue = result.saturation = result.lightness = (max + min) / 2;
if (max == min) {
// grayscale
result.hue = result.saturation = 0;
} else {
float diff = max - min;
result.saturation
= (result.lightness > 0.5) ? (diff / (2 - max - min)) : (diff / (max + min));
if (max == r)
result.hue = (g - b) / diff + (g < b ? 6 : 0);
else if (max == g)
result.hue = (b - r) / diff + 2;
else if (max == b)
result.hue = (r - g) / diff + 4;
result.hue /= 6;
}
return result;
}
rgb_color
hsl_color::to_rgb() const
{
rgb_color result;
result.alpha = 255;
if (saturation == 0) {
// grayscale
result.red = result.green = result.blue = uint8(lightness * 255);
} else {
float q = lightness < 0.5 ? (lightness * (1 + saturation))
: (lightness + saturation - lightness * saturation);
float p = 2 * lightness - q;
result.red = uint8(hue_to_rgb(p, q, hue + 1./3) * 255);
result.green = uint8(hue_to_rgb(p, q, hue) * 255);
result.blue = uint8(hue_to_rgb(p, q, hue - 1./3) * 255);
}
return result;
}
// reference: https://en.wikipedia.org/wiki/HSL_and_HSV#Color_conversion_formulae
// (from_rgb() and to_rgb() are derived from the same)
float
hsl_color::hue_to_rgb(float p, float q, float t)
{
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1./6)
return p + (q - p) * 6 * t;
if (t < 1./2)
return q;
if (t < 2./3)
return p + (q - p) * (2./3 - t) * 6;
return p;
}
+1
View File
@@ -37,6 +37,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
DateTimeEdit.cpp
DragTrackingFilter.cpp
DriverSettingsMessageAdapter.cpp
HSL.cpp
HashString.cpp
IconButton.cpp
IconView.cpp
+179 -25
View File
@@ -16,9 +16,11 @@
#include <Alert.h>
#include <Catalog.h>
#include <DefaultColors.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <HSL.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <Messenger.h>
@@ -37,6 +39,7 @@
#define B_TRANSLATION_CONTEXT "Colors tab"
#define COLOR_DROPPED 'cldp'
#define AUTO_ADJUST_CHANGED 'madj'
APRView::APRView(const char* name)
@@ -47,19 +50,17 @@ APRView::APRView(const char* name)
LoadSettings();
fAutoSelectCheckBox = new BCheckBox(B_TRANSLATE("Automatically pick secondary colors"),
new BMessage(AUTO_ADJUST_CHANGED));
fAutoSelectCheckBox->SetValue(true);
// Set up list of color attributes
fAttrList = new ColorWhichListView("AttributeList");
fScrollView = new BScrollView("ScrollView", fAttrList, 0, false, true);
fScrollView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
int32 count = color_description_count();
for (int32 i = 0; i < count; i++) {
const ColorDescription& description = *get_color_description(i);
const char* text = B_TRANSLATE_NOCOLLECT(description.text);
color_which which = description.which;
fAttrList->AddItem(new ColorWhichItem(text, which, ui_color(which)));
}
_CreateItems();
fColorPreview = new ColorPreview(new BMessage(COLOR_DROPPED), 0);
fColorPreview->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER,
@@ -69,6 +70,7 @@ APRView::APRView(const char* name)
"picker", new BMessage(UPDATE_COLOR));
BLayoutBuilder::Group<>(this, B_VERTICAL)
.Add(fAutoSelectCheckBox)
.Add(fScrollView, 10.0)
.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
.Add(fColorPreview)
@@ -91,6 +93,7 @@ APRView::~APRView()
void
APRView::AttachedToWindow()
{
fAutoSelectCheckBox->SetTarget(this);
fPicker->SetTarget(this);
fAttrList->SetTarget(this);
fColorPreview->SetTarget(this);
@@ -157,6 +160,12 @@ APRView::MessageReceived(BMessage *msg)
break;
}
case AUTO_ADJUST_CHANGED:
{
_CreateItems();
break;
}
default:
BView::MessageReceived(msg);
break;
@@ -222,10 +231,55 @@ APRView::IsRevertable()
void
APRView::_SetColor(color_which which, rgb_color color)
APRView::_CreateItems()
{
set_ui_color(which, color);
fCurrentColors.SetColor(ui_color_name(which), color);
while (fAttrList->CountItems() > 0)
delete fAttrList->RemoveItem((int32)0);
const bool autoSelect = fAutoSelectCheckBox->Value();
const int32 count = color_description_count();
for (int32 i = 0; i < count; i++) {
const ColorDescription& description = *get_color_description(i);
const color_which which = description.which;
if (autoSelect) {
if (which != B_PANEL_BACKGROUND_COLOR
&& which != B_STATUS_BAR_COLOR
&& which != B_WINDOW_TAB_COLOR) {
continue;
}
}
const char* text = B_TRANSLATE_NOCOLLECT(description.text);
fAttrList->AddItem(new ColorWhichItem(text, which, ui_color(which)));
}
fAttrList->Select(0);
}
void
APRView::_UpdatePreviews(const BMessage& colors)
{
rgb_color color;
for (int32 i = color_description_count() - 1; i >= 0; i--) {
ColorWhichItem* item = static_cast<ColorWhichItem*>(fAttrList->ItemAt(i));
if (item == NULL)
continue;
color = colors.GetColor(ui_color_name(item->ColorWhich()),
make_color(255, 0, 255));
item->SetColor(color);
fAttrList->InvalidateItem(i);
}
}
void
APRView::_SetUIColors(const BMessage& colors)
{
set_ui_colors(&colors);
fCurrentColors = colors;
}
@@ -248,26 +302,126 @@ APRView::_SetCurrentColor(rgb_color color)
void
APRView::_SetUIColors(const BMessage& colors)
APRView::_SetColor(color_which which, rgb_color color)
{
set_ui_colors(&colors);
fCurrentColors = colors;
_SetOneColor(which, color);
if (!fAutoSelectCheckBox->Value())
return;
// Protect against accidentally overwriting colors.
if (ui_color(which) == color)
return;
if (which == B_PANEL_BACKGROUND_COLOR) {
const bool isDark = color.IsDark();
_SetOneColor(B_MENU_BACKGROUND_COLOR, color);
_SetOneColor(B_SCROLL_BAR_THUMB_COLOR, color);
const rgb_color menuSelectedBackground
= tint_color(color, isDark ? B_LIGHTEN_2_TINT : B_DARKEN_2_TINT);
_SetOneColor(B_MENU_SELECTED_BACKGROUND_COLOR, menuSelectedBackground);
const rgb_color controlBackground = tint_color(color, 0.25 /* lighten "> 2" */);
_SetOneColor(B_CONTROL_BACKGROUND_COLOR, controlBackground);
const rgb_color controlBorder
= tint_color(color, isDark ? 0.4875 : 1.20 /* lighten/darken "1.5" */);
_SetOneColor(B_CONTROL_BORDER_COLOR, controlBorder);
const rgb_color windowBorder = tint_color(color, 0.75);
_SetOneColor(B_WINDOW_BORDER_COLOR, windowBorder);
const rgb_color inactiveWindowBorder = tint_color(color, B_LIGHTEN_1_TINT);
_SetOneColor(B_WINDOW_INACTIVE_TAB_COLOR, inactiveWindowBorder);
_SetOneColor(B_WINDOW_INACTIVE_BORDER_COLOR, inactiveWindowBorder);
const rgb_color listSelectedBackground
= tint_color(color, isDark ? 0.77 : 1.12 /* lighten/darken "< 1" */ );
_SetOneColor(B_LIST_SELECTED_BACKGROUND_COLOR, listSelectedBackground);
const color_which fromDefaults[] = {
B_MENU_ITEM_TEXT_COLOR,
B_MENU_SELECTED_ITEM_TEXT_COLOR,
B_MENU_SELECTED_BORDER_COLOR,
B_PANEL_TEXT_COLOR,
B_DOCUMENT_BACKGROUND_COLOR,
B_DOCUMENT_TEXT_COLOR,
B_CONTROL_TEXT_COLOR,
B_NAVIGATION_PULSE_COLOR,
B_WINDOW_INACTIVE_TEXT_COLOR,
B_LIST_BACKGROUND_COLOR,
B_LIST_ITEM_TEXT_COLOR,
B_LIST_SELECTED_ITEM_TEXT_COLOR,
B_SHINE_COLOR,
B_SHADOW_COLOR,
B_LINK_TEXT_COLOR,
B_LINK_HOVER_COLOR,
B_LINK_ACTIVE_COLOR,
B_LINK_VISITED_COLOR,
};
for (size_t i = 0; i < B_COUNT_OF(fromDefaults); i++)
_SetOneColor(fromDefaults[i], BPrivate::GetSystemColor(fromDefaults[i], isDark));
} else if (which == B_STATUS_BAR_COLOR) {
const hsl_color statusColorHSL = hsl_color::from_rgb(color);
hsl_color controlHighlight = statusColorHSL;
controlHighlight.saturation = max_c(0.2f, controlHighlight.saturation / 2.f);
_SetOneColor(B_CONTROL_HIGHLIGHT_COLOR, controlHighlight.to_rgb());
hsl_color controlMark = statusColorHSL;
controlMark.saturation = max_c(0.2f, controlMark.saturation * 0.67f);
controlMark.lightness = max_c(0.25f, controlMark.lightness * 0.55f);
_SetOneColor(B_CONTROL_MARK_COLOR, controlMark.to_rgb());
rgb_color keyboardNav; {
hsl_color keyboardNavHSL = statusColorHSL;
keyboardNavHSL.lightness = max_c(0.2f, keyboardNavHSL.lightness * 0.75f);
keyboardNav = keyboardNavHSL.to_rgb();
// Use primary color channel only.
if (keyboardNav.blue >= max_c(keyboardNav.red, keyboardNav.green))
keyboardNav.red = keyboardNav.green = 0;
else if (keyboardNav.red >= max_c(keyboardNav.green, keyboardNav.blue))
keyboardNav.green = keyboardNav.blue = 0;
else
keyboardNav.red = keyboardNav.blue = 0;
}
_SetOneColor(B_KEYBOARD_NAVIGATION_COLOR, keyboardNav);
} else if (which == B_WINDOW_TAB_COLOR) {
const bool isDark = color.IsDark();
const hsl_color tabColorHSL = hsl_color::from_rgb(color);
const float tabColorSaturation
= tabColorHSL.saturation != 0 ? tabColorHSL.saturation : tabColorHSL.lightness;
_SetOneColor(B_WINDOW_TEXT_COLOR,
BPrivate::GetSystemColor(B_WINDOW_TEXT_COLOR, isDark));
_SetOneColor(B_TOOL_TIP_TEXT_COLOR,
BPrivate::GetSystemColor(B_TOOL_TIP_TEXT_COLOR, isDark));
const rgb_color toolTipBackground = tint_color(color, isDark ? 1.7 : 0.15);
_SetOneColor(B_TOOL_TIP_BACKGROUND_COLOR, toolTipBackground);
hsl_color success = hsl_color::from_rgb(BPrivate::GetSystemColor(B_SUCCESS_COLOR, isDark));
success.saturation = max_c(0.25f, tabColorSaturation * 0.68f);
_SetOneColor(B_SUCCESS_COLOR, success.to_rgb());
hsl_color failure = hsl_color::from_rgb(BPrivate::GetSystemColor(B_FAILURE_COLOR, isDark));
failure.saturation = max_c(0.25f, tabColorSaturation);
_SetOneColor(B_FAILURE_COLOR, failure.to_rgb());
}
}
void
APRView::_UpdatePreviews(const BMessage& colors)
APRView::_SetOneColor(color_which which, rgb_color color)
{
rgb_color color;
for (int32 i = color_description_count() - 1; i >= 0; i--) {
ColorWhichItem* item = static_cast<ColorWhichItem*>(fAttrList->ItemAt(i));
if (item == NULL)
continue;
if (ui_color(which) == color)
return;
color = colors.GetColor(ui_color_name(get_color_description(i)->which),
make_color(255, 0, 255));
item->SetColor(color);
fAttrList->InvalidateItem(i);
}
set_ui_color(which, color);
fCurrentColors.SetColor(ui_color_name(which), color);
}
+6 -1
View File
@@ -13,6 +13,7 @@
#include <Button.h>
#include <CheckBox.h>
#include <ColorControl.h>
#include <ListItem.h>
#include <ListView.h>
@@ -50,14 +51,18 @@ public:
bool IsRevertable();
private:
void _CreateItems();
void _UpdatePreviews(const BMessage& colors);
void _SetColor(color_which which, rgb_color color);
void _SetOneColor(color_which which, rgb_color color);
void _SetCurrentColor(rgb_color color);
void _SetUIColors(const BMessage& colors);
void _UpdatePreviews(const BMessage& colors);
private:
BColorControl* fPicker;
BCheckBox* fAutoSelectCheckBox;
BListView* fAttrList;
color_which fWhich;
+2 -2
View File
@@ -2,7 +2,7 @@ SubDir HAIKU_TOP src preferences appearance ;
AddSubDirSupportedPlatforms libbe_test ;
UsePrivateHeaders app interface [ FDirName servers app ] ;
UsePrivateHeaders shared app interface [ FDirName servers app ] ;
Preference Appearance :
APRMain.cpp
@@ -23,7 +23,7 @@ Preference Appearance :
#CursorWhichItem.cpp
#SysCursorAPI.cpp
: be [ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub
: be [ TargetLibstdc++ ] [ TargetLibsupc++ ] localestub shared
: Appearance.rdef
;