/* SPDX-FileCopyrightText: 2026 Project Tick * SPDX-FileContributor: Project Tick * SPDX-License-Identifier: GPL-3.0-or-later * * MeshMC - A Custom Launcher for Minecraft * Copyright (C) 2026 Project Tick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "AppearancePage.h" #include "ui_AppearancePage.h" #include "Application.h" #include "ui/themes/ITheme.h" #include "ui/themes/ThemeManager.h" #include "ui/themes/CatPack.h" #include static const QStringList previewIconNames = { "new", "centralmods", "viewfolder", "launch", "copy", "about", "settings", "accounts"}; AppearancePage::AppearancePage(QWidget* parent) : QWidget(parent), ui(new Ui::AppearancePage) { ui->setupUi(this); ui->catPreview->setGraphicsEffect(new QGraphicsOpacityEffect(this)); connect(ui->widgetStyleComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearancePage::applyWidgetTheme); connect(ui->iconsComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearancePage::applyIconTheme); connect(ui->catPackComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearancePage::applyCatTheme); loadSettings(); } AppearancePage::~AppearancePage() { delete ui; } bool AppearancePage::apply() { applySettings(); return true; } void AppearancePage::applyWidgetTheme(int index) { auto settings = APPLICATION->settings(); auto originalTheme = settings->get("ApplicationTheme").toString(); auto newTheme = ui->widgetStyleComboBox->itemData(index).toString(); if (originalTheme != newTheme) { settings->set("ApplicationTheme", newTheme); APPLICATION->themeManager()->applyCurrentlySelectedTheme(); // Sync icon combo to the auto-resolved icon theme auto resolvedIcon = settings->get("IconTheme").toString(); auto iconThemes = APPLICATION->themeManager()->iconThemes(); QString resolvedFamily; for (const auto& entry : iconThemes) { if (entry.id == resolvedIcon) { resolvedFamily = entry.family; break; } } ui->iconsComboBox->blockSignals(true); for (int i = 0; i < ui->iconsComboBox->count(); i++) { if (ui->iconsComboBox->itemData(i).toString() == resolvedFamily) { ui->iconsComboBox->setCurrentIndex(i); break; } } ui->iconsComboBox->blockSignals(false); } updateIconPreview(); } void AppearancePage::applyIconTheme(int index) { auto settings = APPLICATION->settings(); auto tm = APPLICATION->themeManager(); auto family = ui->iconsComboBox->itemData(index).toString(); auto resolved = tm->resolveIconTheme(family); if (resolved.isEmpty()) return; auto originalIconTheme = settings->get("IconTheme").toString(); if (originalIconTheme != resolved) { settings->set("IconTheme", resolved); tm->applyCurrentlySelectedTheme(); } updateIconPreview(); } void AppearancePage::applySettings() { // Theme and icon changes are already persisted live via // applyWidgetTheme/applyIconTheme. This is intentionally minimal — settings // are saved on combo change. } void AppearancePage::loadSettings() { auto settings = APPLICATION->settings(); auto tm = APPLICATION->themeManager(); // Block signals during population ui->widgetStyleComboBox->blockSignals(true); ui->iconsComboBox->blockSignals(true); ui->catPackComboBox->blockSignals(true); // --- Widget themes (flat list) --- ui->widgetStyleComboBox->clear(); auto currentThemeId = settings->get("ApplicationTheme").toString(); auto themes = tm->allThemes(); int themeIdx = 0; for (size_t i = 0; i < themes.size(); i++) { auto* theme = themes[i]; ui->widgetStyleComboBox->addItem(theme->name(), theme->id()); if (!theme->tooltip().isEmpty()) { ui->widgetStyleComboBox->setItemData( static_cast(i), theme->tooltip(), Qt::ToolTipRole); } if (theme->id() == currentThemeId) { themeIdx = static_cast(i); } } ui->widgetStyleComboBox->setCurrentIndex(themeIdx); // --- Icon themes (one entry per family, auto-resolves dark/light) --- ui->iconsComboBox->clear(); auto currentIconTheme = settings->get("IconTheme").toString(); auto iconThemeList = tm->iconThemes(); int iconIdx = 0; // Find the family of the currently active icon theme QString currentFamily; for (const auto& entry : iconThemeList) { if (entry.id == currentIconTheme) { currentFamily = entry.family; break; } } // Populate combo with one entry per family QSet seenFamilies; int comboIdx = 0; for (const auto& entry : iconThemeList) { if (seenFamilies.contains(entry.family)) continue; seenFamilies.insert(entry.family); QString displayName = entry.variant.isEmpty() ? entry.name : entry.family; ui->iconsComboBox->addItem(displayName, entry.family); if (entry.family == currentFamily) { iconIdx = comboIdx; } comboIdx++; } ui->iconsComboBox->setCurrentIndex(iconIdx); // --- Cat Packs --- ui->catPackComboBox->clear(); auto currentCat = settings->get("BackgroundCat").toString(); auto cats = tm->getValidCatPacks(); int catIdx = 0; for (int i = 0; i < cats.size(); i++) { auto* cat = cats[i]; QIcon catIcon(cat->path()); ui->catPackComboBox->addItem(catIcon, cat->name(), cat->id()); if (cat->id() == currentCat) { catIdx = i; } } ui->catPackComboBox->setCurrentIndex(catIdx); // Unblock signals ui->widgetStyleComboBox->blockSignals(false); ui->iconsComboBox->blockSignals(false); ui->catPackComboBox->blockSignals(false); // Initial previews updateIconPreview(); updateCatPreview(); } void AppearancePage::updateIconPreview() { QList previewButtons = {ui->icon1, ui->icon2, ui->icon3, ui->icon4, ui->icon5, ui->icon6, ui->icon7, ui->icon8}; for (int i = 0; i < previewButtons.size() && i < previewIconNames.size(); i++) { previewButtons[i]->setIcon( APPLICATION->getThemedIcon(previewIconNames[i])); } } void AppearancePage::applyCatTheme(int index) { auto settings = APPLICATION->settings(); auto originalCat = settings->get("BackgroundCat").toString(); auto newCat = ui->catPackComboBox->itemData(index).toString(); if (originalCat != newCat) { settings->set("BackgroundCat", newCat); } updateCatPreview(); } void AppearancePage::updateCatPreview() { QIcon catPackIcon(APPLICATION->themeManager()->getCatPack()); ui->catPreview->setIcon(catPackIcon); auto effect = dynamic_cast(ui->catPreview->graphicsEffect()); if (effect) effect->setOpacity(1.0); }