Overhauled color handling in preparation for supporting user-defined

colors. This change has no user-facing changes, but defines a new Theme
structure that is encapsulated within Colors and fit for serialization.
This commit is contained in:
casey langen 2017-03-12 13:38:35 -07:00
parent a5ae95bb28
commit 7d906dc03f
5 changed files with 249 additions and 96 deletions

View File

@ -39,16 +39,35 @@ using namespace cursespp;
/* if the terminal supports custom colors, these are the palette
indicies we'll use to store them */
#define COLOR_CUSTOM_WHITE 16
#define COLOR_CUSTOM_BLUE 17
#define COLOR_CUSTOM_RED 18
#define COLOR_CUSTOM_YELLOW 19
#define COLOR_CUSTOM_GREEN 20
#define COLOR_CUSTOM_BLACK 21
#define COLOR_CUSTOM_GREY 22
#define COLOR_CUSTOM_SELECTED_LIST_ITEM_BG 23
#define COLOR_CUSTOM_ORANGE 24
#define COLOR_CUSTOM_DARK_RED 25
#define THEME_COLOR_BACKGROUND 16
#define THEME_COLOR_FOREGROUND 17
#define THEME_COLOR_FOCUSED_BORDER 18
#define THEME_COLOR_TEXT_FOCUSED 19
#define THEME_COLOR_TEXT_ACTIVE 20
#define THEME_COLOR_TEXT_DISABLED 21
#define THEME_COLOR_TEXT_HIDDEN 22
#define THEME_COLOR_TEXT_WARNING 23
#define THEME_COLOR_TEXT_ERROR 24
#define THEME_COLOR_OVERLAY_BACKGROUND 25
#define THEME_COLOR_OVERLAY_FOREGROUND 26
#define THEME_COLOR_OVERLAY_BORDER 27
#define THEME_COLOR_OVERLAY_FOCUSED_BORDER 28
#define THEME_COLOR_SHORTCUTS_BACKGROUND 29
#define THEME_COLOR_SHORTCUTS_FOREGROUND 30
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 31
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 32
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 33
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 34
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 35
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 36
#define THEME_COLOR_BANNER_BACKGROUND 37
#define THEME_COLOR_BANNER_FOREGROUND 38
#define THEME_COLOR_LIST_HEADER_BACKGROUND 39
#define THEME_COLOR_LIST_HEADER_FOREGROUND 40
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 41
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 42
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 43
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 44
/* if we can't set custom colors, but we have the full 256 color
palette, use ones that most closely match our desired colors */
@ -61,27 +80,214 @@ palette, use ones that most closely match our desired colors */
#define COLOR_256_BLUE 123
#define COLOR_256_OFFWHITE 251
/* vars that hold our actual color definitions. these may change
later during init if the terminal supports > 8 colors */
static int white = COLOR_WHITE;
static int blue = COLOR_BLUE;
static int red = COLOR_RED;
static int darkRed = COLOR_RED;
static int yellow = COLOR_YELLOW;
static int green = COLOR_GREEN;
static int black = COLOR_BLACK;
static int orange = COLOR_YELLOW;
static int foreground = -1;
static int background = -1;
static int selected = -1;
static int grey = -1;
#define SCALE(x) ((x * 1000) / 255)
static int initColor(int id, int r, int g, int b) {
init_color(id, SCALE(r), SCALE(g), SCALE(b));
return id;
}
struct Theme {
struct Color {
enum Mode { Standard, Palette, Custom };
Color() {
Set(-1, -1, -1);
}
void Set(int colorId, int hex, int palette) {
this->colorId = colorId;
this->r = hex >> 16;
this->g = (hex >> 8) & 0x0000ff;
this->b = hex & 0x0000ff;
this->palette = palette;
}
void Set(int colorId, int r, int g, int b, int palette) {
this->colorId = colorId;
this->r = r;
this->g = g;
this->b = b;
this->palette = palette;
}
int Id(Mode mode, int defaultValue) {
if (mode == Standard) {
return defaultValue;
}
else if (mode == Palette) {
return this->palette;
}
init_color(this->colorId, SCALE(this->r), SCALE(this->g), SCALE(this->b));
return this->colorId;
}
int colorId;
int r, g, b;
int palette;
};
/* initialized with default values. the user can override them, then call Apply() */
Theme() {
/* main */
background.Set(THEME_COLOR_BACKGROUND, 0, 0, 0, COLOR_BLACK);
foreground.Set(THEME_COLOR_FOREGROUND, 230, 230, 230, COLOR_256_OFFWHITE);
focusedBorder.Set(THEME_COLOR_FOCUSED_BORDER, 220, 82, 86, COLOR_256_RED);
/* text */
textFocused.Set(THEME_COLOR_TEXT_FOCUSED, 220, 82, 86, COLOR_256_RED);
textActive.Set(THEME_COLOR_TEXT_ACTIVE, 166, 226, 46, COLOR_256_GREEN);
textDisabled.Set(THEME_COLOR_TEXT_DISABLED, 128, 128, 128, COLOR_256_MEDIUM_GRAY);
textHidden.Set(THEME_COLOR_TEXT_HIDDEN, 0, 0, 0, COLOR_BLACK);
textWarning.Set(THEME_COLOR_TEXT_WARNING, 230, 220, 116, COLOR_256_YELLOW);
textWarning.Set(THEME_COLOR_TEXT_ERROR, 220, 82, 86, COLOR_256_RED);
/* overlay */
overlayBackground.Set(THEME_COLOR_OVERLAY_BACKGROUND, 66, 66, 56, COLOR_256_MEDIUM_GRAY);
overlayForeground.Set(THEME_COLOR_OVERLAY_FOREGROUND, 230, 230, 230, COLOR_256_OFFWHITE);
overlayBorder.Set(THEME_COLOR_OVERLAY_BORDER, 102, 217, 238, COLOR_256_BLUE);
overlayFocusedBorder.Set(THEME_COLOR_OVERLAY_FOCUSED_BORDER, 220, 82, 86, COLOR_256_RED);
/* shortcut bar */
shortcutsBackground.Set(THEME_COLOR_SHORTCUTS_BACKGROUND, 66, 66, 56, COLOR_256_MEDIUM_GRAY);
shortcutsForeground.Set(THEME_COLOR_SHORTCUTS_FOREGROUND, 230, 220, 116, COLOR_256_YELLOW);
focusedShortcutsBackground.Set(THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED, 175, 66, 71, COLOR_256_DARK_RED);
focusedShortcutsForeground.Set(THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED, 230, 230, 230, COLOR_256_OFFWHITE);
/* buttons */
buttonBackgroundNormal.Set(THEME_COLOR_BUTTON_BACKGROUND_NORMAL, 230, 220, 116, COLOR_256_YELLOW);
buttonForegroundNormal.Set(THEME_COLOR_BUTTON_FOREGROUND_NORMAL, 0, 0, 0, COLOR_BLACK);
buttonBackgroundActive.Set(THEME_COLOR_BUTTON_BACKGROUND_ACTIVE, 166, 226, 46, COLOR_256_GREEN);
buttonForegroundActive.Set(THEME_COLOR_BUTTON_FOREGROUND_ACTIVE, 0, 0, 0, COLOR_BLACK);
/* banner */
bannerBackground.Set(THEME_COLOR_BANNER_BACKGROUND, 255, 150, 32, COLOR_256_ORANGE);
bannerForeground.Set(THEME_COLOR_BANNER_FOREGROUND, 0, 0, 0, COLOR_BLACK);
/* listview */
listHeaderBackground.Set(THEME_COLOR_LIST_HEADER_BACKGROUND, 0, 0, 0, COLOR_BLACK);
listHeaderForeground.Set(THEME_COLOR_LIST_HEADER_FOREGROUND, 166, 226, 46, COLOR_256_GREEN);
listHighlightedBackground.Set(THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND, 166, 226, 46, COLOR_256_GREEN);
listHighlightedForeground.Set(THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND, 0, 0, 0, COLOR_BLACK);
listActiveBackground.Set(THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND, 66, 66, 56, COLOR_256_MEDIUM_GRAY);
listActiveForeground.Set(THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND, 230, 220, 116, COLOR_256_YELLOW);
}
/* initializes all of the color pairs from the specified colors, then applies them
to the current session! */
void Apply(Color::Mode mode) {
int backgroundId = background.Id(mode, COLOR_BLACK);
int foregroundId = foreground.Id(mode, COLOR_WHITE);
/* main */
init_pair(CURSESPP_DEFAULT_CONTENT_COLOR, foregroundId, backgroundId);
init_pair(CURSESPP_DEFAULT_FRAME_COLOR, foregroundId, backgroundId);
init_pair(CURSESPP_FOCUSED_FRAME_COLOR, focusedBorder.Id(mode, COLOR_RED),backgroundId);
/* text */
init_pair(CURSESPP_TEXT_DEFAULT, foregroundId, backgroundId);
init_pair(CURSESPP_TEXT_DISABLED, textDisabled.Id(mode, COLOR_WHITE), backgroundId);
init_pair(CURSESPP_TEXT_FOCUSED, textFocused.Id(mode, COLOR_RED), backgroundId);
init_pair(CURSESPP_TEXT_ACTIVE, textActive.Id(mode, COLOR_GREEN), backgroundId);
init_pair(CURSESPP_TEXT_WARNING, textWarning.Id(mode, COLOR_YELLOW), backgroundId);
init_pair(CURSESPP_TEXT_ERROR, textError.Id(mode, COLOR_RED), backgroundId);
init_pair(CURSESPP_TEXT_HIDDEN, textHidden.Id(mode, COLOR_BLACK), backgroundId);
/* overlay */
int overlayBgId = overlayBackground.Id(mode, COLOR_BLACK);
init_pair(CURSESPP_OVERLAY_FRAME, overlayBorder.Id(mode, COLOR_BLUE), overlayBgId);
init_pair(CURSESPP_OVERLAY_CONTENT, overlayForeground.Id(mode, COLOR_WHITE), overlayBgId);
init_pair(CURSESPP_OVERLAY_INPUT_FRAME, overlayFocusedBorder.Id(mode, COLOR_RED), overlayBgId);
/* shortcuts */
init_pair(
CURSESPP_SHORTCUT_ROW_NORMAL,
shortcutsForeground.Id(mode, COLOR_YELLOW),
shortcutsBackground.Id(mode, COLOR_BLACK));
init_pair(
CURSESPP_SHORTCUT_ROW_FOCUSED,
focusedShortcutsForeground.Id(mode, COLOR_WHITE),
focusedShortcutsBackground.Id(mode, COLOR_RED));
/* buttons */
init_pair(
CURSESPP_BUTTON_NORMAL,
buttonForegroundNormal.Id(mode, COLOR_BLACK),
buttonBackgroundNormal.Id(mode, COLOR_YELLOW));
init_pair(
CURSESPP_BUTTON_HIGHLIGHTED,
buttonForegroundActive.Id(mode, COLOR_BLACK),
buttonBackgroundActive.Id(mode, COLOR_GREEN));
/* banner */
init_pair(
CURSESPP_BANNER,
bannerForeground.Id(mode, COLOR_BLACK),
bannerBackground.Id(mode, COLOR_YELLOW));
/* list items */
init_pair(
CURSESPP_LIST_ITEM_HEADER,
listHeaderForeground.Id(mode, COLOR_GREEN),
listHeaderBackground.Id(mode, COLOR_BLACK));
init_pair(
CURSESPP_SELECTED_LIST_ITEM,
listActiveForeground.Id(mode, COLOR_YELLOW),
listActiveBackground.Id(mode, COLOR_BLACK));
init_pair(
CURSESPP_HIGHLIGHTED_LIST_ITEM,
listHighlightedForeground.Id(mode, COLOR_BLACK),
listHighlightedBackground.Id(mode, COLOR_GREEN));
init_pair( /* note: swap active fg/bg */
CURSESPP_HIGHLIGHTED_SELECTED_LIST_ITEM,
listActiveBackground.Id(mode, COLOR_BLACK),
listActiveForeground.Id(mode, COLOR_YELLOW));
}
/* main */
Color background;
Color foreground;
Color focusedBorder;
/* text */
Color textFocused;
Color textActive;
Color textDisabled;
Color textHidden;
Color textWarning;
Color textError;
/* overlay */
Color overlayBackground;
Color overlayForeground;
Color overlayBorder;
Color overlayFocusedBorder;
/* shortcut bar */
Color shortcutsBackground;
Color shortcutsForeground;
Color focusedShortcutsBackground;
Color focusedShortcutsForeground;
/* buttons */
Color buttonBackgroundNormal;
Color buttonForegroundNormal;
Color buttonBackgroundActive;
Color buttonForegroundActive;
/* banner */
Color bannerBackground;
Color bannerForeground;
/* listview */
Color listHeaderBackground;
Color listHeaderForeground;
Color listHighlightedBackground;
Color listHighlightedForeground;
Color listActiveForeground;
Color listActiveBackground;
};
/* some terminals report custom colors are supported, and also
don't error when calling init_color(), but don't actually work. */
@ -104,68 +310,17 @@ static bool canChangeColors() {
Colors::Colors() {
}
static Theme defaultTheme;
void Colors::Init(bool disableCustomColors) {
start_color();
use_default_colors();
/* the default colors are a bit harsh for my taste, so
let's use custom colors if the terminal supports it. in
the future we'll allow users to configure this via setting */
using Color = Theme::Color;
Color::Mode mode = Color::Standard;
if (!disableCustomColors && COLORS > 8) {
if (canChangeColors()) {
red = initColor(COLOR_CUSTOM_RED, 220, 82, 86);
darkRed = initColor(COLOR_CUSTOM_DARK_RED, 175, 66, 71);
green = initColor(COLOR_CUSTOM_GREEN, 166, 226, 46);
yellow = initColor(COLOR_CUSTOM_YELLOW, 230, 220, 116);
selected = initColor(COLOR_CUSTOM_SELECTED_LIST_ITEM_BG, 66, 66, 56);
grey = initColor(COLOR_CUSTOM_GREY, 128, 128, 128);
orange = initColor(COLOR_CUSTOM_ORANGE, 255, 150, 32);
blue = initColor(COLOR_CUSTOM_BLUE, 102, 217, 238);
white = initColor(COLOR_CUSTOM_WHITE, 230, 230, 230);
foreground = COLOR_CUSTOM_WHITE;
}
else if (COLORS >= 256) {
green = COLOR_256_GREEN;
red = COLOR_256_RED;
darkRed = COLOR_256_DARK_RED;
yellow = COLOR_256_YELLOW;
selected = COLOR_256_MEDIUM_GRAY;
grey = COLOR_256_MEDIUM_GRAY;
orange = COLOR_256_ORANGE;
blue = COLOR_256_BLUE;
white = COLOR_256_OFFWHITE;
foreground = COLOR_256_OFFWHITE;
}
mode = canChangeColors() ? Color::Custom : Color::Palette;
}
init_pair(CURSESPP_SELECTED_LIST_ITEM, yellow, selected);
init_pair(CURSESPP_HIGHLIGHTED_LIST_ITEM, black, green);
init_pair(CURSESPP_HIGHLIGHTED_SELECTED_LIST_ITEM, black, yellow);
init_pair(CURSESPP_LIST_ITEM_HEADER, green, background);
init_pair(CURSESPP_DEFAULT_CONTENT_COLOR, foreground, background);
init_pair(CURSESPP_DEFAULT_FRAME_COLOR, foreground, background);
init_pair(CURSESPP_FOCUSED_FRAME_COLOR, red, background);
init_pair(CURSESPP_TEXT_DEFAULT, white, background);
init_pair(CURSESPP_TEXT_DISABLED, grey, background);
init_pair(CURSESPP_TEXT_FOCUSED, red, background);
init_pair(CURSESPP_TEXT_ACTIVE, green, background);
init_pair(CURSESPP_TEXT_WARNING, yellow, background);
init_pair(CURSESPP_TEXT_ERROR, red, background);
init_pair(CURSESPP_TEXT_HIDDEN, black, background);
init_pair(CURSESPP_TEXT_SEPARATOR, orange, background);
init_pair(CURSESPP_BUTTON_NORMAL, black, yellow);
init_pair(CURSESPP_BUTTON_NEGATIVE, white, red);
init_pair(CURSESPP_BUTTON_HIGHLIGHTED, black, green);
init_pair(CURSESPP_SHORTCUT_ROW_NORMAL, yellow, selected);
init_pair(CURSESPP_SHORTCUT_ROW_FOCUSED, white, darkRed);
init_pair(CURSESPP_OVERLAY_FRAME, blue, selected);
init_pair(CURSESPP_OVERLAY_BACKGROUND, white, selected);
init_pair(CURSESPP_OVERLAY_INPUT_FRAME, red, selected);
init_pair(CURSESPP_BANNER, black, orange);
defaultTheme.Apply(mode);
}

View File

@ -54,17 +54,15 @@
#define CURSESPP_TEXT_WARNING 12
#define CURSESPP_TEXT_ERROR 13
#define CURSESPP_TEXT_HIDDEN 14
#define CURSESPP_TEXT_SEPARATOR 15
#define CURSESPP_BUTTON_NORMAL 16
#define CURSESPP_BUTTON_NEGATIVE 17
#define CURSESPP_BUTTON_HIGHLIGHTED 18
#define CURSESPP_SHORTCUT_ROW_NORMAL 19
#define CURSESPP_SHORTCUT_ROW_FOCUSED 20
#define CURSESPP_OVERLAY_FRAME 21
#define CURSESPP_OVERLAY_BACKGROUND 22
#define CURSESPP_OVERLAY_CONTENT 22
#define CURSESPP_OVERLAY_INPUT_FRAME 23
#define CURSESPP_BANNER 24

View File

@ -46,7 +46,7 @@ using namespace cursespp;
DialogOverlay::DialogOverlay() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->width = this->height = 0;
this->autoDismiss = true;

View File

@ -53,16 +53,16 @@ using namespace cursespp;
InputOverlay::InputOverlay() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->width = this->height = this->setWidth = 0;
this->textInput.reset(new TextInput());
this->textInput->SetFocusOrder(0);
this->textInput->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->textInput->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->textInput->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->textInput->SetFocusedFrameColor(CURSESPP_OVERLAY_INPUT_FRAME);
this->textInput->SetFocusedContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->textInput->SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT);
this->textInput->EnterPressed.connect(this, &InputOverlay::OnInputEnterPressed);
this->textInput->TextChanged.connect(this, &InputOverlay::OnInputKeyPress);
this->AddWindow(this->textInput);

View File

@ -46,7 +46,7 @@ using namespace cursespp;
ListOverlay::ListOverlay() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->autoDismiss = true;
@ -54,8 +54,8 @@ ListOverlay::ListOverlay() {
this->setWidth = this->setWidthPercent = 0;
this->listWindow.reset(new ListWindow());
this->listWindow->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->listWindow->SetFocusedContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->listWindow->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->listWindow->SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT);
this->listWindow->SetFrameVisible(false);
this->listWindow->SetFocusOrder(0);
this->AddWindow(this->listWindow);