Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow customizing imgui colors more flexibly. #77985

Merged
merged 5 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions data/raw/imgui_style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"//": "See doc/COLOR.md for documentation on how to use this file.",
"inherit_base_colors": false,
"colors": {

}
}
109 changes: 109 additions & 0 deletions doc/COLOR.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,112 @@ which are displayed as:

**Note:** Items that use `relic data` automatically turn the item's displayed name in `pink`. Color tags override this, in case one wants to "hide" it from the player, or to make them look "mundane". Similarly, if one wants, otherwise mundane items can have colored names.

# User color customization
## Base colors
Users can customize the color appearance by setting the color triplets in `config/base_colors.json`. The default config looks like this:
```json
[
{
"type": "colordef",
"BLACK": [ 20, 30, 38 ],
"DGRAY": [ 121, 97, 49 ],
"GRAY": [ 173, 135, 59 ],
"WHITE": [ 215, 181, 109 ],
"BROWN": [ 120, 83, 42 ],
"YELLOW": [ 231, 155, 32 ],
"RED": [ 181, 45, 26 ],
"LRED": [ 200, 105, 65 ],
"GREEN": [ 142, 140, 61 ],
"LGREEN": [ 153, 171, 79 ],
"BLUE": [ 35, 65, 86 ],
"LBLUE": [ 88, 102, 105 ],
"MAGENTA": [ 217, 131, 22 ],
"LMAGENTA": [ 218, 153, 71 ],
"CYAN": [ 77, 72, 127 ],
"LCYAN": [ 131, 109, 175 ]
}
]
```
The mapping values are [Red, Green, Blue] color triplets, with numbers for each ranging from 0 to 255.

If for whatever reason one wanted to make all the "red" colors appear blue on the screen, they could change the `"RED": [ 181, 45, 26 ],` to something like `"RED": [ 10, 10, 220 ],`.

The game provides a nice built-in color manager with several pre-defined color themes that can be easily switched between.


## GUI colors
The UI *menus* do not (always) adhere to the base color customization, and can be customized separately, in the `config/imgui_style.json`. The default config is:
```json
{
"inherit_base_colors": false,
"colors": {
}
}
```
Changing `inherit_base_colors` to `true` would make the imgui menus follow the base colors theme with some reasonable defaults.

It is, however, also possible to tweak the specific element colors with higher fidelity and not be restricted to the 8-color pallete by specifying the respective elements and colors in the `colors` object. Like so:
```json
{
"inherit_base_colors": false,
"colors": {
"ImGuiCol_Text" : [1.00, 1.00, 1.00, 1.00],
"ImGuiCol_TextDisabled" : [0.50, 0.50, 0.50, 1.00],
"ImGuiCol_WindowBg" : [0.06, 0.06, 0.06, 0.94],
"ImGuiCol_ChildBg" : [0.00, 0.00, 0.00, 0.00],
"ImGuiCol_PopupBg" : [0.08, 0.08, 0.08, 0.94],
"ImGuiCol_Border" : [0.43, 0.43, 0.50, 0.50],
"ImGuiCol_BorderShadow" : [0.00, 0.00, 0.00, 0.00],
"ImGuiCol_FrameBg" : [0.16, 0.29, 0.48, 0.54],
"ImGuiCol_FrameBgHovered" : [0.26, 0.59, 0.98, 0.40],
"ImGuiCol_FrameBgActive" : [0.26, 0.59, 0.98, 0.67],
"ImGuiCol_TitleBg" : [0.04, 0.04, 0.04, 1.00],
"ImGuiCol_TitleBgActive" : [0.16, 0.29, 0.48, 1.00],
"ImGuiCol_TitleBgCollapsed" : [0.00, 0.00, 0.00, 0.51],
"ImGuiCol_MenuBarBg" : [0.14, 0.14, 0.14, 1.00],
"ImGuiCol_ScrollbarBg" : [0.02, 0.02, 0.02, 0.53],
"ImGuiCol_ScrollbarGrab" : [0.31, 0.31, 0.31, 1.00],
"ImGuiCol_ScrollbarGrabHovered" : [0.41, 0.41, 0.41, 1.00],
"ImGuiCol_ScrollbarGrabActive" : [0.51, 0.51, 0.51, 1.00],
"ImGuiCol_CheckMark" : [0.26, 0.59, 0.98, 1.00],
"ImGuiCol_SliderGrab" : [0.24, 0.52, 0.88, 1.00],
"ImGuiCol_SliderGrabActive" : [0.26, 0.59, 0.98, 1.00],
"ImGuiCol_Button" : [0.26, 0.59, 0.98, 0.40],
"ImGuiCol_ButtonHovered" : [0.26, 0.59, 0.98, 1.00],
"ImGuiCol_ButtonActive" : [0.06, 0.53, 0.98, 1.00],
"ImGuiCol_Header" : [0.26, 0.59, 0.98, 0.31],
"ImGuiCol_HeaderHovered" : [0.26, 0.59, 0.98, 0.80],
"ImGuiCol_HeaderActive" : [0.26, 0.59, 0.98, 1.00],
"ImGuiCol_Separator" : [0.43, 0.43, 0.50, 0.50],
"ImGuiCol_SeparatorHovered" : [0.10, 0.40, 0.75, 0.78],
"ImGuiCol_SeparatorActive" : [0.10, 0.40, 0.75, 1.00],
"ImGuiCol_ResizeGrip" : [0.26, 0.59, 0.98, 0.20],
"ImGuiCol_ResizeGripHovered" : [0.26, 0.59, 0.98, 0.67],
"ImGuiCol_ResizeGripActive" : [0.26, 0.59, 0.98, 0.95],
"ImGuiCol_Tab" : [0.18, 0.35, 0.58, 0.86],
"ImGuiCol_TabHovered" : [0.26, 0.59, 0.98, 0.80],
"ImGuiCol_TabActive" : [0.20, 0.41, 0.68, 1.00],
"ImGuiCol_TabUnfocused" : [0.07, 0.10, 0.15, 0.97],
"ImGuiCol_TabUnfocusedActive" : [0.14, 0.26, 0.42, 1.00],
"ImGuiCol_PlotLines" : [0.61, 0.61, 0.61, 1.00],
"ImGuiCol_PlotLinesHovered" : [1.00, 0.43, 0.35, 1.00],
"ImGuiCol_PlotHistogram" : [0.90, 0.70, 0.00, 1.00],
"ImGuiCol_PlotHistogramHovered" : [1.00, 0.60, 0.00, 1.00],
"ImGuiCol_TableHeaderBg" : [0.19, 0.19, 0.20, 1.00],
"ImGuiCol_TableBorderStrong" : [0.31, 0.31, 0.35, 1.00],
"ImGuiCol_TableBorderLight" : [0.23, 0.23, 0.25, 1.00],
"ImGuiCol_TableRowBg" : [0.00, 0.00, 0.00, 0.00],
"ImGuiCol_TableRowBgAlt" : [1.00, 1.00, 1.00, 0.06],
"ImGuiCol_TextSelectedBg" : [0.26, 0.59, 0.98, 0.35],
"ImGuiCol_DragDropTarget" : [1.00, 1.00, 0.00, 0.90],
"ImGuiCol_NavHighlight" : [0.26, 0.59, 0.98, 1.00],
"ImGuiCol_NavWindowingHighlight" : [1.00, 1.00, 1.00, 0.70],
"ImGuiCol_NavWindowingDimBg" : [0.80, 0.80, 0.80, 0.20],
"ImGuiCol_ModalWindowDimBg" : [0.80, 0.80, 0.80, 0.35]
}
}
```
The keys in the mapping are the ones provided by ImGui library, and it should be "obvious" or perhaps "intuitive" what those do (which is to say, there is no explicit documentation at the moment). Values are `[red, green, blue, opacity]` quadruplets with values between `0.0` and `1.0` inclusive. However `[red, green, blue]` triplets are also accepted, and in those cases opacity is assumed to be `1.0` (i.e. fully non-transparent).
The values in the above snippet are those of the default ImGui theme that the game uses, so copy-pasting that into your own `config/imgui_style.json` should yield no visual difference from the empty config. They can be used as a reference.

You can play around with specific values in the imgui playground (accessible from the game main menu -> imgui Demo Screen -> Configuration -> Style -> Colors). You can then easily "Export" the chosen colors to use in `imgui_style.json` config.
193 changes: 152 additions & 41 deletions src/cata_imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "color.h"
#include "input.h"
#include "output.h"
#include "path_info.h"
#include "system_locale.h"
#include "ui_manager.h"
#include "input_context.h"
Expand Down Expand Up @@ -1058,48 +1059,158 @@ void cataimgui::EndRightAlign()
ImGui::EndTable();
}

void cataimgui::init_colors()
// Use the base terminal palette to reasonably color ImGui elements.
// This might be useful to easily apply the color theme (chosen via
// Color Manager, likely) to ImGui with minimal effort.
static void inherit_base_colors()
{
ImGuiStyle &style = ImGui::GetStyle();

style.Colors[ImGuiCol_Text] = c_white;
style.Colors[ImGuiCol_TextDisabled] = c_dark_gray;
style.Colors[ImGuiCol_WindowBg] = c_black;
style.Colors[ImGuiCol_ChildBg] = c_black;
style.Colors[ImGuiCol_PopupBg] = c_black;
style.Colors[ImGuiCol_Border] = c_white;
style.Colors[ImGuiCol_BorderShadow] = c_blue;
style.Colors[ImGuiCol_FrameBg] = c_dark_gray;
style.Colors[ImGuiCol_FrameBgHovered] = c_black;
style.Colors[ImGuiCol_FrameBgActive] = c_dark_gray;
style.Colors[ImGuiCol_TitleBg] = c_blue;
style.Colors[ImGuiCol_TitleBgActive] = c_dark_gray;
style.Colors[ImGuiCol_TitleBgCollapsed] = c_blue;
style.Colors[ImGuiCol_MenuBarBg] = c_black;
style.Colors[ImGuiCol_ScrollbarBg] = c_black;
style.Colors[ImGuiCol_ScrollbarGrab] = c_dark_gray;
style.Colors[ImGuiCol_ScrollbarGrabHovered] = c_light_gray;
style.Colors[ImGuiCol_ScrollbarGrabActive] = c_white;
style.Colors[ImGuiCol_CheckMark] = c_white;
style.Colors[ImGuiCol_SliderGrab] = c_white;
style.Colors[ImGuiCol_SliderGrabActive] = c_white;
style.Colors[ImGuiCol_Button] = c_dark_gray;
style.Colors[ImGuiCol_ButtonHovered] = c_dark_gray;
style.Colors[ImGuiCol_ButtonActive] = c_blue;
style.Colors[ImGuiCol_Header] = c_blue;
style.Colors[ImGuiCol_HeaderHovered] = c_black;
style.Colors[ImGuiCol_HeaderActive] = c_dark_gray;
style.Colors[ImGuiCol_Separator] = c_dark_gray;
style.Colors[ImGuiCol_SeparatorHovered] = c_white;
style.Colors[ImGuiCol_SeparatorActive] = c_white;
style.Colors[ImGuiCol_ResizeGrip] = c_light_gray;
style.Colors[ImGuiCol_ResizeGripHovered] = c_white;
style.Colors[ImGuiCol_ResizeGripActive] = c_white;
style.Colors[ImGuiCol_Tab] = c_black;
style.Colors[ImGuiCol_TabHovered] = c_blue;
style.Colors[ImGuiCol_TabActive] = c_blue;
style.Colors[ImGuiCol_TabUnfocused] = c_black;
style.Colors[ImGuiCol_TabUnfocusedActive] = c_black;
style.Colors[ImGuiCol_TextSelectedBg] = c_blue;
style.Colors[ImGuiCol_NavHighlight] = c_blue;
style.Colors[ImGuiCol_Text] = c_white;
style.Colors[ImGuiCol_TextDisabled] = c_dark_gray;
style.Colors[ImGuiCol_WindowBg] = c_black;
style.Colors[ImGuiCol_ChildBg] = c_black;
style.Colors[ImGuiCol_PopupBg] = c_black;
style.Colors[ImGuiCol_Border] = c_white;
style.Colors[ImGuiCol_BorderShadow] = c_blue;
style.Colors[ImGuiCol_FrameBg] = c_dark_gray;
style.Colors[ImGuiCol_FrameBgHovered] = c_black;
style.Colors[ImGuiCol_FrameBgActive] = c_dark_gray;
style.Colors[ImGuiCol_TitleBg] = c_dark_gray;
style.Colors[ImGuiCol_TitleBgActive] = c_light_blue;
style.Colors[ImGuiCol_TitleBgCollapsed] = c_dark_gray;
style.Colors[ImGuiCol_MenuBarBg] = c_black;
style.Colors[ImGuiCol_ScrollbarBg] = c_black;
style.Colors[ImGuiCol_ScrollbarGrab] = c_dark_gray;
style.Colors[ImGuiCol_ScrollbarGrabHovered] = c_light_gray;
style.Colors[ImGuiCol_ScrollbarGrabActive] = c_white;
style.Colors[ImGuiCol_CheckMark] = c_white;
style.Colors[ImGuiCol_SliderGrab] = c_white;
style.Colors[ImGuiCol_SliderGrabActive] = c_white;
style.Colors[ImGuiCol_Button] = c_dark_gray;
style.Colors[ImGuiCol_ButtonHovered] = c_dark_gray;
style.Colors[ImGuiCol_ButtonActive] = c_blue;
style.Colors[ImGuiCol_Header] = c_blue;
style.Colors[ImGuiCol_HeaderHovered] = c_black;
style.Colors[ImGuiCol_HeaderActive] = c_dark_gray;
style.Colors[ImGuiCol_Separator] = c_dark_gray;
style.Colors[ImGuiCol_SeparatorHovered] = c_white;
style.Colors[ImGuiCol_SeparatorActive] = c_white;
style.Colors[ImGuiCol_ResizeGrip] = c_light_gray;
style.Colors[ImGuiCol_ResizeGripHovered] = c_white;
style.Colors[ImGuiCol_ResizeGripActive] = c_white;
style.Colors[ImGuiCol_Tab] = c_black;
style.Colors[ImGuiCol_TabHovered] = c_blue;
style.Colors[ImGuiCol_TabActive] = c_blue;
style.Colors[ImGuiCol_TabUnfocused] = c_black;
style.Colors[ImGuiCol_TabUnfocusedActive] = c_black;
style.Colors[ImGuiCol_TextSelectedBg] = c_blue;
style.Colors[ImGuiCol_NavHighlight] = c_blue;
}

static void load_imgui_style_file( const cata_path &style_path )
{
ImGuiStyle &style = ImGui::GetStyle();

JsonValue jsin = json_loader::from_path( style_path );


JsonObject jo = jsin.get_object();


if( jo.has_bool( "inherit_base_colors" ) && jo.get_bool( "inherit_base_colors" ) ) {
inherit_base_colors();
}
JsonObject joc = jo.get_object( "colors" );

std::unordered_map<std::string, int> key_options = {
{"ImGuiCol_Text", ImGuiCol_Text},
{"ImGuiCol_TextDisabled", ImGuiCol_TextDisabled},
{"ImGuiCol_WindowBg", ImGuiCol_WindowBg},
{"ImGuiCol_ChildBg", ImGuiCol_ChildBg},
{"ImGuiCol_PopupBg", ImGuiCol_PopupBg},
{"ImGuiCol_Border", ImGuiCol_Border},
{"ImGuiCol_BorderShadow", ImGuiCol_BorderShadow},
{"ImGuiCol_FrameBg", ImGuiCol_FrameBg},
{"ImGuiCol_FrameBgHovered", ImGuiCol_FrameBgHovered},
{"ImGuiCol_FrameBgActive", ImGuiCol_FrameBgActive},
{"ImGuiCol_TitleBg", ImGuiCol_TitleBg},
{"ImGuiCol_TitleBgActive", ImGuiCol_TitleBgActive},
{"ImGuiCol_TitleBgCollapsed", ImGuiCol_TitleBgCollapsed},
{"ImGuiCol_MenuBarBg", ImGuiCol_MenuBarBg},
{"ImGuiCol_ScrollbarBg", ImGuiCol_ScrollbarBg},
{"ImGuiCol_ScrollbarGrab", ImGuiCol_ScrollbarGrab},
{"ImGuiCol_ScrollbarGrabHovered", ImGuiCol_ScrollbarGrabHovered},
{"ImGuiCol_ScrollbarGrabActive", ImGuiCol_ScrollbarGrabActive},
{"ImGuiCol_CheckMark", ImGuiCol_CheckMark},
{"ImGuiCol_SliderGrab", ImGuiCol_SliderGrab},
{"ImGuiCol_SliderGrabActive", ImGuiCol_SliderGrabActive},
{"ImGuiCol_Button", ImGuiCol_Button},
{"ImGuiCol_ButtonHovered", ImGuiCol_ButtonHovered},
{"ImGuiCol_ButtonActive", ImGuiCol_ButtonActive},
{"ImGuiCol_Header", ImGuiCol_Header},
{"ImGuiCol_HeaderHovered", ImGuiCol_HeaderHovered},
{"ImGuiCol_HeaderActive", ImGuiCol_HeaderActive},
{"ImGuiCol_Separator", ImGuiCol_Separator},
{"ImGuiCol_SeparatorHovered", ImGuiCol_SeparatorHovered},
{"ImGuiCol_SeparatorActive", ImGuiCol_SeparatorActive},
{"ImGuiCol_ResizeGrip", ImGuiCol_ResizeGrip},
{"ImGuiCol_ResizeGripHovered", ImGuiCol_ResizeGripHovered},
{"ImGuiCol_ResizeGripActive", ImGuiCol_ResizeGripActive},
{"ImGuiCol_Tab", ImGuiCol_Tab},
{"ImGuiCol_TabHovered", ImGuiCol_TabHovered},
{"ImGuiCol_TabActive", ImGuiCol_TabActive},
{"ImGuiCol_TabUnfocused", ImGuiCol_TabUnfocused},
{"ImGuiCol_TabUnfocusedActive", ImGuiCol_TabUnfocusedActive},
{"ImGuiCol_PlotLines", ImGuiCol_PlotLines},
{"ImGuiCol_PlotLinesHovered", ImGuiCol_PlotLinesHovered},
{"ImGuiCol_PlotHistogram", ImGuiCol_PlotHistogram},
{"ImGuiCol_PlotHistogramHovered", ImGuiCol_PlotHistogramHovered},
{"ImGuiCol_TableHeaderBg", ImGuiCol_TableHeaderBg},
{"ImGuiCol_TableBorderStrong", ImGuiCol_TableBorderStrong},
{"ImGuiCol_TableBorderLight", ImGuiCol_TableBorderLight},
{"ImGuiCol_TableRowBg", ImGuiCol_TableRowBg},
{"ImGuiCol_TableRowBgAlt", ImGuiCol_TableRowBgAlt},
{"ImGuiCol_TextSelectedBg", ImGuiCol_TextSelectedBg},
{"ImGuiCol_DragDropTarget", ImGuiCol_DragDropTarget},
{"ImGuiCol_NavHighlight", ImGuiCol_NavHighlight},
{"ImGuiCol_NavWindowingHighlight", ImGuiCol_NavWindowingHighlight},
{"ImGuiCol_NavWindowingDimBg", ImGuiCol_NavWindowingDimBg},
{"ImGuiCol_ModalWindowDimBg", ImGuiCol_ModalWindowDimBg},
};
for( const auto& [text_key, imgui_key] : key_options ) {
if( joc.has_array( text_key ) ) {
JsonArray jsarr = joc.get_array( text_key );
float alpha = 1.0; // default to full opacity if not specified explicitly
if( jsarr.has_float( 3 ) ) {
alpha = jsarr.get_float( 3 );
}
ImVec4 color = ImVec4(
jsarr.get_float( 0 ),
jsarr.get_float( 1 ),
jsarr.get_float( 2 ),
alpha
);
style.Colors[imgui_key] = color;
}
}

}

void cataimgui::init_colors()
{
const cata_path default_style_path = PATH_INFO::datadir_path() / "raw" / "imgui_style.json";
const cata_path style_path = PATH_INFO::config_dir_path() / "imgui_style.json";
if( !file_exist( style_path ) ) {
assure_dir_exist( PATH_INFO::config_dir() );
copy_file( default_style_path, style_path );
}

try {
load_imgui_style_file( style_path );
} catch( const JsonError &err ) {
debugmsg( "Failed to load imgui color data from \"%s\": %s",
style_path.generic_u8string(), err.what() );
}
}

4 changes: 3 additions & 1 deletion src/cata_imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ void PushMonoFont();
bool BeginRightAlign( const char *str_id );
void EndRightAlign();

// Set ImGui theme colors to match colors loaded by the player.
// Set ImGui theme colors to those chosen by the player.
// This loads the settings from `config/imgui_style.json` and - optionally - falls back to base colors
// for elements not explicitly specified.
void init_colors();
} // namespace cataimgui
Loading