UI: Theme selector

Fixed conflict with upstream
This commit is contained in:
redtide 2021-04-06 22:49:04 +02:00 committed by Jean Pierre Cimalando
parent 54442fa86c
commit e6bb118d09
9 changed files with 366 additions and 57 deletions

View file

@ -20,6 +20,7 @@ set(EDITOR_RESOURCES
Fonts/sfizz-fluentui-system-f20.ttf
Fonts/sfizz-misc-icons.ttf
Fonts/Roboto-Regular.ttf
Themes/Default/theme.xml
PARENT_SCOPE)
function(copy_editor_resources SOURCE_DIR DESTINATION_DIR)
@ -50,6 +51,8 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
src/editor/GUIPiano.cpp
src/editor/DlgAbout.h
src/editor/DlgAbout.cpp
src/editor/Theme.h
src/editor/Theme.cpp
src/editor/ColorHelpers.h
src/editor/ColorHelpers.cpp
src/editor/ImageHelpers.h
@ -104,7 +107,8 @@ else()
target_include_directories(sfizz_editor PRIVATE ${sfizz-gio_INCLUDE_DIRS})
target_link_libraries(sfizz_editor PRIVATE ${sfizz-gio_LIBRARIES})
endif()
target_link_libraries(sfizz_editor PRIVATE sfizz::colorspaces sfizz::stb_image sfizz::bit_array sfizz::filesystem)
target_link_libraries(sfizz_editor PRIVATE sfizz::colorspaces sfizz::stb_image
sfizz::bit_array sfizz::filesystem sfizz::pugixml)
# layout tool
if(NOT CMAKE_CROSSCOMPILING)

View file

@ -356,6 +356,17 @@ widget_class mainView {open
class ValueMenu
}
}
Fl_Group {} {
label Theme open
xywh {40 270 105 100} box ROUNDED_BOX labelsize 12 align 17 hide
class TitleGroup
} {
Fl_Spinner themeMenu_ {
comment {tag=kTagThemeMenu}
xywh {60 330 65 25} labelsize 12 textsize 12
class OptionMenu
}
}
}
Fl_Box piano_ {
xywh {5 400 790 70} labelsize 12

View file

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<sfizz-theme>
<color name="frameBackground">#d3d7cf</color>
<palette name="normal">
<color name="boxBackground">#babdb6</color>
<color name="text">#000000</color>
<color name="inactiveText">#b2b2b2</color>
<color name="highlightedText">#fd9800</color>
<color name="titleBoxText">#ffffff</color>
<color name="titleBoxBackground">#2e3436</color>
<color name="icon">#000000</color>
<color name="iconHighlight">#fd9800</color>
<color name="valueText">#ffffff</color>
<color name="valueBackground">#2e3436</color>
<color name="knobActiveTrack">#00b62a</color>
<color name="knobInactiveTrack">#303030</color>
<color name="knobLineIndicator">#000000</color>
</palette>
<palette name="inverted">
<color name="boxBackground">#2e3436</color>
<color name="text">#ffffff</color>
<color name="inactiveText">#b2b2b2</color>
<color name="highlightedText">#fd9800</color>
<color name="titleBoxText">#000000</color>
<color name="titleBoxBackground">#babdb6</color>
<color name="icon">#b2b2b2</color>
<color name="iconHighlight">#fd9800</color>
<color name="valueText">#000000</color>
<color name="valueBackground">#9a9a9a</color>
<color name="knobActiveTrack">#00b62a</color>
<color name="knobInactiveTrack">#606060</color>
<color name="knobLineIndicator">#ffffff</color>
</palette>
</sfizz-theme>

View file

@ -44,3 +44,38 @@ SColorHCY::SColorHCY(const SColorRGB &rgb)
y = vhcy[2];
a = rgb.a;
}
static int hexDigitFromChar(char c)
{
return (c >= '0' && c <= '9') ? (c - '0') :
(c >= 'a' && c <= 'z') ? (c - 'a' + 10) :
(c >= 'A' && c <= 'Z') ? (c - 'A' + 10) : -1;
}
bool colorFromHex(absl::string_view hex, CColor& color)
{
if (hex.empty() || hex[0] != '#')
return false;
hex = hex.substr(1, hex.size());
size_t length = hex.size();
uint32_t rgba = 0;
if (length == 6 || length == 8) {
for (size_t i = 0; i < length; ++i) {
int d = hexDigitFromChar(hex[i]);
if (d == -1)
return false;
rgba = (rgba << 4) | d;
}
}
if (length == 6)
rgba = (rgba << 8) | 0xff;
color.red = rgba >> 24;
color.green = (rgba >> 16) & 0xff;
color.blue = (rgba >> 8) & 0xff;
color.alpha = rgba & 0xff;
return true;
}

View file

@ -8,6 +8,7 @@
#include "utility/vstgui_before.h"
#include "vstgui/lib/ccolor.h"
#include "utility/vstgui_after.h"
#include <absl/strings/string_view.h>
using namespace VSTGUI;
@ -33,3 +34,5 @@ struct SColorHCY {
float h {}, c {}, y {}, a { 1.0 };
};
bool colorFromHex(absl::string_view hex, CColor& color);

View file

@ -15,6 +15,7 @@
#include "NativeHelpers.h"
#include "VSTGUIHelpers.h"
#include "BitArray.h"
#include "Theme.h"
#include "plugin/MessageUtils.h"
#include <absl/strings/string_view.h>
#include <absl/strings/match.h>
@ -32,6 +33,7 @@
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <memory>
#include "utility/vstgui_before.h"
#include "vstgui/vstgui.h"
@ -50,6 +52,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
std::string currentSfzFile_;
std::string currentScalaFile_;
std::string currentThemeName_;
std::string userFilesDir_;
std::string fallbackFilesDir_;
@ -91,6 +94,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
kTagSetCCPan,
kTagChooseUserFilesDir,
kTagAbout,
kTagThemeMenu,
kTagFirstChangePanel,
kTagLastChangePanel = kTagFirstChangePanel + kNumPanels - 1,
};
@ -119,6 +123,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
CTextLabel* keyswitchLabel_ = nullptr;
CTextLabel* keyswitchInactiveLabel_ = nullptr;
CTextLabel* keyswitchBadge_ = nullptr;
COptionMenu* themeMenu_ = nullptr;
std::unique_ptr<Theme> theme_;
STitleContainer* userFilesGroup_ = nullptr;
STextButton* userFilesDirButton_ = nullptr;
@ -590,54 +596,14 @@ void Editor::Impl::createFrameContents()
backgroundBitmap_ = background;
{
const CColor frameBackground = { 0xd3, 0xd7, 0xcf };
struct Palette {
CColor boxBackground;
CColor text;
CColor inactiveText;
CColor highlightedText;
CColor titleBoxText;
CColor titleBoxBackground;
CColor icon;
CColor iconHighlight;
CColor valueText;
CColor valueBackground;
CColor knobActiveTrackColor;
CColor knobInactiveTrackColor;
CColor knobLineIndicatorColor;
};
Palette normalPalette;
normalPalette.boxBackground = { 0xba, 0xbd, 0xb6 };
normalPalette.text = { 0x00, 0x00, 0x00 };
normalPalette.inactiveText = { 0xb2, 0xb2, 0xb2 };
normalPalette.highlightedText = { 0xfd, 0x98, 0x00 };
normalPalette.titleBoxText = { 0xff, 0xff, 0xff };
normalPalette.titleBoxBackground = { 0x2e, 0x34, 0x36 };
normalPalette.icon = normalPalette.text;
normalPalette.iconHighlight = { 0xfd, 0x98, 0x00 };
normalPalette.valueText = { 0xff, 0xff, 0xff };
normalPalette.valueBackground = { 0x2e, 0x34, 0x36 };
normalPalette.knobActiveTrackColor = { 0x00, 0xb6, 0x2a };
normalPalette.knobInactiveTrackColor = { 0x30, 0x30, 0x30 };
normalPalette.knobLineIndicatorColor = { 0x00, 0x00, 0x00 };
Palette invertedPalette;
invertedPalette.boxBackground = { 0x2e, 0x34, 0x36 };
invertedPalette.text = { 0xff, 0xff, 0xff };
invertedPalette.inactiveText = { 0xb2, 0xb2, 0xb2 };
invertedPalette.highlightedText = { 0xfd, 0x98, 0x00 };
invertedPalette.titleBoxText = { 0x00, 0x00, 0x00 };
invertedPalette.titleBoxBackground = { 0xba, 0xbd, 0xb6 };
invertedPalette.icon = { 0xb2, 0xb2, 0xb2 };
invertedPalette.iconHighlight = { 0xfd, 0x98, 0x00 };
invertedPalette.valueText = { 0x00, 0x00, 0x00 };
invertedPalette.valueBackground = { 0x9a, 0x9a, 0x9a };
invertedPalette.knobActiveTrackColor = { 0x00, 0xb6, 0x2a };
invertedPalette.knobInactiveTrackColor = { 0x60, 0x60, 0x60 };
invertedPalette.knobLineIndicatorColor = { 0xff, 0xff, 0xff };
Palette& defaultPalette = normalPalette;
// Try to load the Default theme from disk or hardcoded one as fallback
Theme* theme = new Theme;
theme_.reset(theme);
currentThemeName_ = theme->loadCurrentName();
theme->load(currentThemeName_);
Palette& invertedPalette = theme->invertedPalette;
Palette& defaultPalette = theme->normalPalette;
Palette* palette = &defaultPalette;
auto enterPalette = [&palette](Palette& p) { palette = &p; };
@ -697,9 +663,9 @@ void Editor::Impl::createFrameContents()
};
auto createStyledKnob = [this, &palette](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int) {
SStyledKnob* knob = new SStyledKnob(bounds, this, tag);
knob->setActiveTrackColor(palette->knobActiveTrackColor);
knob->setInactiveTrackColor(palette->knobInactiveTrackColor);
knob->setLineIndicatorColor(palette->knobLineIndicatorColor);
knob->setActiveTrackColor(palette->knobActiveTrack);
knob->setInactiveTrackColor(palette->knobInactiveTrack);
knob->setLineIndicatorColor(palette->knobLineIndicator);
return knob;
};
auto createValueLabel = [&palette](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) {
@ -781,6 +747,18 @@ void Editor::Impl::createFrameContents()
vm->setRoundRectRadius(5.0);
return vm;
};
auto createOptionMenu = [this, &palette](const CRect& bounds, int tag, const char*, CHoriTxtAlign align, int fontsize) {
auto* cb = new COptionMenu(bounds, this, tag);
cb->setHoriAlign(align);
auto font = makeOwned<CFontDesc>("Roboto", fontsize);
cb->setFont(font);
cb->setFontColor(palette->valueText);
cb->setBackColor(palette->valueBackground);
cb->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
cb->setStyle(CParamDisplay::kRoundRectStyle);
cb->setRoundRectRadius(5.0);
return cb;
};
auto createGlyphButton = [this, &palette](UTF8StringPtr glyph, const CRect& bounds, int tag, int fontsize) {
STextButton* btn = new STextButton(bounds, this, tag, glyph);
btn->setFont(makeOwned<CFontDesc>("Sfizz Fluent System F20", fontsize));
@ -821,10 +799,12 @@ void Editor::Impl::createFrameContents()
btn->setFont(makeOwned<CFontDesc>("Sfizz Fluent System F20", fontsize));
return btn;
};
auto createPiano = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) {
auto createPiano = [&palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) {
SPiano* piano = new SPiano(bounds);
auto font = makeOwned<CFontDesc>("Roboto", fontsize);
piano->setFont(font);
piano->setFontColor(palette->text);
piano->setBackColor(palette->boxBackground);
return piano;
};
auto createChevronDropDown = [this, &palette](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) {
@ -858,7 +838,7 @@ void Editor::Impl::createFrameContents()
box->setNameLabelFontColor(palette->text);
box->setKnobFont(font);
box->setKnobFontColor(palette->text);
box->setKnobLineIndicatorColor(palette->knobLineIndicatorColor);
box->setKnobLineIndicatorColor(palette->knobLineIndicator);
box->setValueToStringFunction([](float value, std::string& text) -> bool {
text = std::to_string(std::lround(value * 127));
return true;
@ -877,7 +857,7 @@ void Editor::Impl::createFrameContents()
#include "layout/main.hpp"
mainView->setBackgroundColor(frameBackground);
mainView->setBackgroundColor(theme->frameBackground);
#if LINUX
if (!isZenityAvailable()) {
@ -1111,6 +1091,19 @@ void Editor::Impl::createFrameContents()
}
applyBackgroundForCurrentPanel();
if (COptionMenu* menu = themeMenu_) {
const std::vector<std::string>& names = Theme::getAvailableNames();
size_t index = ~size_t(0);
for (size_t i = 0, n = names.size(); i < n; ++i) {
const std::string& name = names[i];
menu->addEntry(UTF8String(name));
if (name == currentThemeName_)
index = i;
}
if (index != ~size_t(0))
menu->setCurrent(index);
}
}
void Editor::Impl::chooseSfzFile()
@ -1847,6 +1840,14 @@ void Editor::Impl::valueChanged(CControl* ctl)
Call::later([this]() { aboutDialog_->setVisible(true); });
break;
case kTagThemeMenu:
{
currentThemeName_ = Theme::getAvailableNames()[int(value)];
Theme::storeCurrentName(currentThemeName_);
// TODO: live reload theme
}
break;
default:
if (tag >= kTagFirstChangePanel && tag <= kTagLastChangePanel) {
int panelId = tag - kTagFirstChangePanel;

View file

@ -0,0 +1,164 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "Theme.h"
#include "ColorHelpers.h"
#include "VSTGUIHelpers.h"
#include "plugin/SfizzSettings.h"
#include "sfizz/utility/StringViewHelpers.h"
#include <pugixml.hpp>
#include <ghc/fs_std.hpp>
#include <algorithm>
#include <iostream>
void Theme::clear() {
frameBackground = {};
normalPalette = {};
invertedPalette = {};
}
void Theme::load(const std::string& name)
{
fs::path resPath = getResourceBasePath();
fs::path themePath = resPath / "Themes" / fs::u8path(name) / "theme.xml";
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(themePath.c_str());
if (!result) {
std::cerr << "[sfizz] cannot load theme from " << resPath << '\n';
return;
}
loadDocument(doc);
}
void Theme::loadDocument(const pugi::xml_document& doc)
{
pugi::xml_node rootNode(doc.child("sfizz-theme"));
if (!rootNode) {
std::cerr << "[sfizz] trying to load an invalid theme\n";
return;
}
///
auto loadChildColorNodes = [this](pugi::xml_node topNode, bool inverted) {
for (pugi::xml_node colorNode : topNode.children("color")) {
absl::string_view name = colorNode.attribute("name").as_string();
CColor* slot = getColorFromName(name, inverted);
if (!slot) {
std::cerr << "[sfizz] color not recognized: " << name << "\n";
continue;
}
*slot = {};
absl::string_view colorText = colorNode.text().as_string();
if (!colorFromHex(colorText, *slot))
std::cerr << "[sfizz] invalid color value: " << colorText << "\n";
}
};
///
loadChildColorNodes(rootNode, false);
for (pugi::xml_node paletteNode : rootNode.children("palette")) {
absl::string_view paletteName = paletteNode.attribute("name").as_string();
bool inverted;
if (paletteName == "normal")
inverted = false;
else if (paletteName == "inverted")
inverted = true;
else {
std::cerr << "[sfizz] palette not recognized: " << paletteName << "\n";
continue;
}
loadChildColorNodes(paletteNode, inverted);
}
}
void Theme::storeCurrentName(absl::string_view name)
{
SfizzSettings settings;
settings.store("current_theme", name);
}
std::string Theme::loadCurrentName()
{
SfizzSettings settings;
return settings.load_or("current_theme", "Default");
}
const std::vector<std::string>& Theme::getAvailableNames()
{
static const std::vector<std::string> names = extractAvailableNames();
return names;
}
std::vector<std::string> Theme::extractAvailableNames()
{
fs::path themesPath = getResourceBasePath() / "Themes";
std::error_code ec;
fs::directory_iterator it(themesPath, ec);
if (ec) {
std::cerr << "[sfizz] error reading the theme directory: " << ec.message() << '\n';
return {};
}
std::vector<std::string> names;
for (; !ec && it != fs::directory_iterator(); it.increment(ec)) {
const fs::directory_entry& entry = *it;
if (entry.is_directory())
names.emplace_back(entry.path().filename().u8string());
}
std::sort(
names.begin(), names.end(),
[](const std::string& a, const std::string& b) -> bool {
return (a == "Default") ? true : (b == "Default") ? false : a < b;
});
return names;
}
CColor* Theme::getColorFromName(absl::string_view name, bool fromInvertedPalette)
{
CColor* c = nullptr;
switch (hash(name)) {
#define COLOR_CASE(X) \
case hash(#X): \
c = &X; \
break
#define PALETTE_COLOR_CASE(X) \
case hash(#X): \
c = fromInvertedPalette ? \
&invertedPalette.X : &normalPalette.X; \
break
COLOR_CASE(frameBackground);
PALETTE_COLOR_CASE(boxBackground);
PALETTE_COLOR_CASE(highlightedText);
PALETTE_COLOR_CASE(icon);
PALETTE_COLOR_CASE(iconHighlight);
PALETTE_COLOR_CASE(inactiveText);
PALETTE_COLOR_CASE(knobActiveTrack);
PALETTE_COLOR_CASE(knobInactiveTrack);
PALETTE_COLOR_CASE(knobLineIndicator);
PALETTE_COLOR_CASE(text);
PALETTE_COLOR_CASE(titleBoxBackground);
PALETTE_COLOR_CASE(titleBoxText);
PALETTE_COLOR_CASE(valueBackground);
PALETTE_COLOR_CASE(valueText);
#undef COLOR_CASE
#undef PALETTE_COLOR_CASE
default: break;
}
return c;
}

View file

@ -0,0 +1,51 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "utility/vstgui_before.h"
#include "vstgui/vstgui.h"
#include "utility/vstgui_after.h"
#include <absl/strings/string_view.h>
#include <string>
#include <vector>
namespace pugi { class xml_document; }
using namespace VSTGUI;
struct Palette {
CColor boxBackground;
CColor text;
CColor inactiveText;
CColor highlightedText;
CColor titleBoxText;
CColor titleBoxBackground;
CColor icon;
CColor iconHighlight;
CColor valueText;
CColor valueBackground;
CColor knobActiveTrack;
CColor knobInactiveTrack;
CColor knobLineIndicator;
};
struct Theme {
CColor frameBackground;
Palette normalPalette {};
Palette invertedPalette {};
void clear();
void load(const std::string& name);
void loadDocument(const pugi::xml_document& doc);
static void storeCurrentName(absl::string_view name);
static std::string loadCurrentName();
static const std::vector<std::string>& getAvailableNames();
CColor* getColorFromName(absl::string_view name, bool fromInvertedPalette);
private:
static std::vector<std::string> extractAvailableNames();
};

View file

@ -181,6 +181,12 @@ view__66->addView(view__69);
auto* const view__70 = createValueMenu(CRect(100, 60, 180, 85), kTagSetOscillatorQuality, "", kCenterText, 12);
oscillatorQualitySlider_ = view__70;
view__66->addView(view__70);
auto* const view__71 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
piano_ = view__71;
view__0->addView(view__71);
auto* const view__71 = createTitleGroup(CRect(35, 161, 140, 261), -1, "Theme", kCenterText, 12);
view__46->addView(view__71);
view__71->setVisible(false);
auto* const view__72 = createOptionMenu(CRect(20, 60, 85, 85), kTagThemeMenu, "", kCenterText, 12);
themeMenu_ = view__72;
view__71->addView(view__72);
auto* const view__73 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
piano_ = view__73;
view__0->addView(view__73);