Files
IfcOpenShell/src/bonsaiviewer/ViewerSettings.cpp
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

190 lines
8.7 KiB
C++
Raw Normal View History

2026-05-11 09:17:45 +10:00
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
2026-05-15 07:22:31 +10:00
#include "ViewerSettings.h"
2026-05-11 09:17:45 +10:00
#include "components/Style.h"
#include <QColor>
#include <QSettings>
namespace {
constexpr const char* kThemeModeKey = "interface/theme/mode";
constexpr const char* kThemeColorPrefix = "interface/theme/colors/";
2026-05-20 09:38:10 +10:00
using Spec = bonsaiviewer::ViewerSettings::ThemeColorSpec;
2026-05-11 09:17:45 +10:00
const std::vector<Spec> kThemeColorSpecs = {
2026-05-20 09:38:10 +10:00
{"app_background", "App Background", bonsaiviewer::components::style::palette::app_background, "#eef1f5"},
{"border", "Border", bonsaiviewer::components::style::palette::border, "#c9d1dc"},
{"selection_background", "Selection Background", bonsaiviewer::components::style::palette::selection_background,
2026-05-11 09:17:45 +10:00
"#2f9e44"},
2026-05-20 09:38:10 +10:00
{"tab_bar_background", "Tab Bar Background", bonsaiviewer::components::style::palette::tab_bar_background,
2026-05-11 09:17:45 +10:00
"#eef1f5"},
2026-05-20 09:38:10 +10:00
{"tab_background", "Tab Background", bonsaiviewer::components::style::palette::tab_background, "#e3e8ef"},
{"ribbon_background", "Ribbon Background", bonsaiviewer::components::style::palette::ribbon_background,
2026-05-11 09:17:45 +10:00
"#e3e8ef"},
2026-05-20 09:38:10 +10:00
{"ribbon_button_hover", "Ribbon Button Hover", bonsaiviewer::components::style::palette::ribbon_button_hover,
2026-05-11 09:17:45 +10:00
"#d6dde7"},
2026-05-20 09:38:10 +10:00
{"ribbon_button_pressed", "Ribbon Button Pressed", bonsaiviewer::components::style::palette::ribbon_button_pressed,
2026-05-11 09:17:45 +10:00
"#cad3df"},
{"viewport_shell_background", "Viewport Shell Background",
2026-05-20 09:38:10 +10:00
bonsaiviewer::components::style::palette::viewport_shell_background, "#dbe1e8"},
{"viewport_background", "Viewport Background", bonsaiviewer::components::style::palette::viewport_background,
2026-05-11 09:17:45 +10:00
"#eef2f6"},
2026-05-20 09:38:10 +10:00
{"panel_background", "Panel Background", bonsaiviewer::components::style::palette::panel_background, "#ffffff"},
{"control_background", "Control Background", bonsaiviewer::components::style::palette::control_background,
2026-05-11 09:17:45 +10:00
"#f5f7fa"},
2026-05-20 09:38:10 +10:00
{"control_border_focus", "Control Border Focus", bonsaiviewer::components::style::palette::control_border_focus,
2026-05-11 09:17:45 +10:00
"#7c8ca3"},
2026-05-20 09:38:10 +10:00
{"box_background", "Box Background", bonsaiviewer::components::style::palette::box_background, "#f5f7fa"},
{"scroll_handle", "Scroll Handle", bonsaiviewer::components::style::palette::scroll_handle, "#b1bac8"},
{"scroll_handle_hover", "Scroll Handle Hover", bonsaiviewer::components::style::palette::scroll_handle_hover,
2026-05-11 09:17:45 +10:00
"#929daf"},
2026-05-20 09:38:10 +10:00
{"status_background", "Status Background", bonsaiviewer::components::style::palette::status_background,
2026-05-11 09:17:45 +10:00
"#edf1f5"},
{"section_header_background", "Section Header Background",
2026-05-20 09:38:10 +10:00
bonsaiviewer::components::style::palette::section_header_background, "#eef2f6"},
{"primary_text", "Primary Text", bonsaiviewer::components::style::palette::primary_text, "#1f2937"},
{"secondary_text", "Secondary Text", bonsaiviewer::components::style::palette::secondary_text, "#5b6676"},
{"disabled_text", "Disabled Text", bonsaiviewer::components::style::palette::disabled_text, "#8b95a3"},
{"warning_text", "Warning Text", bonsaiviewer::components::style::palette::warning_text, "#b26b00"},
{"selection_text", "Selection Text", bonsaiviewer::components::style::palette::selection_text, "#ffffff"},
{"hover_text", "Hover Text", bonsaiviewer::components::style::palette::hover_text, "#0f172a"},
2026-05-11 09:17:45 +10:00
{"icon_color", "Icon Color", "#e7ebf2", "#445066"},
{"icon_active_color", "Icon Active Color", "#ffffff", "#101828"},
{"icon_disabled_color", "Icon Disabled Color", "#6f7988", "#98a2b3"},
{"icon_accent_color", "Accent Icon Color", "#39b54a", "#2f9e44"},
{"icon_accent_active_color", "Accent Icon Active Color", "#53c763", "#267e37"},
};
int colorIndexForKey(const QString& key) {
for (size_t i = 0; i < kThemeColorSpecs.size(); ++i) {
if (QString::fromUtf8(kThemeColorSpecs[i].key) == key) {
return static_cast<int>(i);
}
}
return -1;
}
QString normalizedColor(const QString& value, const QString& fallback) {
const QString trimmed = value.trimmed();
if (!QColor::isValidColorName(trimmed)) return fallback;
return QColor(trimmed).name(QColor::HexRgb);
}
} // namespace
2026-05-20 09:38:10 +10:00
namespace bonsaiviewer {
2026-05-11 09:17:45 +10:00
2026-05-15 07:22:31 +10:00
ViewerSettings& ViewerSettings::instance() {
static ViewerSettings inst;
2026-05-11 09:17:45 +10:00
return inst;
}
2026-05-15 07:22:31 +10:00
const std::vector<ViewerSettings::ThemeColorSpec>& ViewerSettings::themeColorSpecs() {
2026-05-11 09:17:45 +10:00
return kThemeColorSpecs;
}
2026-05-15 07:22:31 +10:00
ViewerSettings::ViewerSettings() {
2026-05-11 09:17:45 +10:00
custom_colors_.resize(kThemeColorSpecs.size());
load();
}
2026-05-15 07:22:31 +10:00
ViewerSettings::ThemeMode ViewerSettings::themeMode() const {
2026-05-11 09:17:45 +10:00
return theme_mode_;
}
2026-05-15 07:22:31 +10:00
void ViewerSettings::setThemeMode(ThemeMode mode) {
2026-05-11 09:17:45 +10:00
if (theme_mode_ == mode) return;
theme_mode_ = mode;
persist();
emit themeModeChanged(mode);
emit themeChanged();
}
2026-05-15 07:22:31 +10:00
QString ViewerSettings::color(const QString& key) const {
2026-05-11 09:17:45 +10:00
const int index = colorIndexForKey(key);
if (index < 0) return {};
const auto& spec = kThemeColorSpecs[static_cast<size_t>(index)];
switch (theme_mode_) {
case ThemeMode::Dark:
return QString::fromUtf8(spec.dark_default);
case ThemeMode::Light:
return QString::fromUtf8(spec.light_default);
case ThemeMode::Custom:
return customColor(key);
}
return QString::fromUtf8(spec.dark_default);
}
2026-05-15 07:22:31 +10:00
QString ViewerSettings::customColor(const QString& key) const {
2026-05-11 09:17:45 +10:00
const int index = colorIndexForKey(key);
if (index < 0) return {};
const auto& spec = kThemeColorSpecs[static_cast<size_t>(index)];
const QString& stored = custom_colors_[static_cast<size_t>(index)];
return stored.isEmpty() ? QString::fromUtf8(spec.dark_default) : stored;
}
2026-05-15 07:22:31 +10:00
void ViewerSettings::setCustomColor(const QString& key, const QString& value) {
2026-05-11 09:17:45 +10:00
const int index = colorIndexForKey(key);
if (index < 0) return;
const auto& spec = kThemeColorSpecs[static_cast<size_t>(index)];
const QString normalized = normalizedColor(value, QString::fromUtf8(spec.dark_default));
QString& stored = custom_colors_[static_cast<size_t>(index)];
if (stored == normalized) return;
stored = normalized;
persist();
if (theme_mode_ == ThemeMode::Custom) {
emit themeChanged();
}
}
2026-05-15 07:22:31 +10:00
void ViewerSettings::load() {
2026-05-11 09:17:45 +10:00
QSettings settings;
int raw_mode = settings.value(kThemeModeKey, static_cast<int>(ThemeMode::Dark)).toInt();
if (raw_mode < static_cast<int>(ThemeMode::Dark) || raw_mode > static_cast<int>(ThemeMode::Custom)) {
raw_mode = static_cast<int>(ThemeMode::Dark);
}
theme_mode_ = static_cast<ThemeMode>(raw_mode);
for (size_t i = 0; i < kThemeColorSpecs.size(); ++i) {
const auto& spec = kThemeColorSpecs[i];
const QString stored = settings.value(QString::fromUtf8(kThemeColorPrefix) + QString::fromUtf8(spec.key),
QString::fromUtf8(spec.dark_default))
.toString();
custom_colors_[i] = normalizedColor(stored, QString::fromUtf8(spec.dark_default));
}
}
2026-05-15 07:22:31 +10:00
void ViewerSettings::persist() const {
2026-05-11 09:17:45 +10:00
QSettings settings;
settings.setValue(kThemeModeKey, static_cast<int>(theme_mode_));
for (size_t i = 0; i < kThemeColorSpecs.size(); ++i) {
settings.setValue(QString::fromUtf8(kThemeColorPrefix) + QString::fromUtf8(kThemeColorSpecs[i].key),
custom_colors_[i]);
}
}
2026-05-20 09:38:10 +10:00
} // namespace bonsaiviewer