diff --git a/CMakeLists.txt b/CMakeLists.txt index d6f16085184c..5883132da4e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -732,6 +732,7 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/skin/legacy/launchimage.cpp src/skin/legacy/legacyskinparser.cpp src/skin/legacy/pixmapsource.cpp + src/skin/legacy/legacyskin.cpp src/skin/legacy/skincontext.cpp src/skin/legacy/tooltips.cpp src/skin/skinloader.cpp diff --git a/src/mixxx.cpp b/src/mixxx.cpp index 711cd8454bd5..c6790aa29b47 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -113,7 +113,7 @@ MixxxMainWindow::MixxxMainWindow( initializeWindow(); // Show launch image immediately so the user knows Mixxx is starting - m_pSkinLoader = std::make_unique(m_pCoreServices->getSettings()); + m_pSkinLoader = std::make_unique(m_pCoreServices->getSettings()); m_pLaunchImage = m_pSkinLoader->loadLaunchImage(this); m_pCentralWidget = (QWidget*)m_pLaunchImage; setCentralWidget(m_pCentralWidget); @@ -1090,13 +1090,7 @@ bool MixxxMainWindow::loadConfiguredSkin() { // TODO: use std::shared_ptr throughout skin widgets instead of these hacky get() calls m_pCentralWidget = m_pSkinLoader->loadConfiguredSkin(this, &m_skinCreatedControls, - m_pCoreServices->getKeyboardEventFilter().get(), - m_pCoreServices->getPlayerManager().get(), - m_pCoreServices->getControllerManager().get(), - m_pCoreServices->getLibrary().get(), - m_pCoreServices->getVinylControlManager().get(), - m_pCoreServices->getEffectsManager().get(), - m_pCoreServices->getRecordingManager().get()); + m_pCoreServices.get()); if (centralWidget() == m_pLaunchImage) { initializationProgressUpdate(100, ""); } diff --git a/src/mixxx.h b/src/mixxx.h index 6016430bf306..a593abb5abe2 100644 --- a/src/mixxx.h +++ b/src/mixxx.h @@ -25,10 +25,15 @@ class EngineMaster; class GuiTick; class LaunchImage; class Library; -class SkinLoader; class VisualsManager; class WMainMenuBar; +namespace mixxx { +namespace skin { +class SkinLoader; +} +} // namespace mixxx + #ifdef __ENGINEPRIME__ namespace mixxx { class LibraryExporter; @@ -116,7 +121,7 @@ class MixxxMainWindow : public QMainWindow { QWidget* m_pCentralWidget; LaunchImage* m_pLaunchImage; - std::shared_ptr m_pSkinLoader; + std::shared_ptr m_pSkinLoader; GuiTick* m_pGuiTick; VisualsManager* m_pVisualsManager; diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index 0da47b1a76d2..24e802743409 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -53,7 +53,7 @@ DlgPreferences::DlgPreferences( MixxxMainWindow* mixxx, - std::shared_ptr pSkinLoader, + std::shared_ptr pSkinLoader, std::shared_ptr pSoundManager, std::shared_ptr pPlayerManager, std::shared_ptr pControllerManager, diff --git a/src/preferences/dialog/dlgpreferences.h b/src/preferences/dialog/dlgpreferences.h index 4362bbe2cdb2..e48a28b38536 100644 --- a/src/preferences/dialog/dlgpreferences.h +++ b/src/preferences/dialog/dlgpreferences.h @@ -43,7 +43,6 @@ class DlgPrefLV2; class LV2Backend; class ControllerManager; class EffectsManager; -class SkinLoader; class PlayerManager; class Library; class VinylControlManager; @@ -51,6 +50,12 @@ class VinylControlManager; class DlgPrefModplug; #endif // __MODPLUG__ +namespace mixxx { +namespace skin { +class SkinLoader; +} +} // namespace mixxx + class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { Q_OBJECT public: @@ -66,7 +71,7 @@ class DlgPreferences : public QDialog, public Ui::DlgPreferencesDlg { }; DlgPreferences(MixxxMainWindow* mixxx, - std::shared_ptr pSkinLoader, + std::shared_ptr pSkinLoader, std::shared_ptr pSoundManager, std::shared_ptr pPlayerManager, std::shared_ptr pControllerManager, diff --git a/src/preferences/dialog/dlgprefinterface.cpp b/src/preferences/dialog/dlgprefinterface.cpp index 27917233d9d5..af4f2093b280 100644 --- a/src/preferences/dialog/dlgprefinterface.cpp +++ b/src/preferences/dialog/dlgprefinterface.cpp @@ -15,59 +15,24 @@ #include "moc_dlgprefinterface.cpp" #include "preferences/usersettings.h" #include "skin/legacy/legacyskinparser.h" +#include "skin/skin.h" #include "skin/skinloader.h" #include "util/screensaver.h" #include "util/widgethelper.h" using mixxx::skin::SkinManifest; - -namespace { - -const QRegExp kMinSizeRegExp("(\\d+), *(\\d+)<"); - -bool skinFitsScreenSize( - const QScreen& screen, - const QString& skin) { - // Use the full resolution of the entire screen that is - // available in full-screen mode. - const auto screenSize = screen.size(); - QFile skinfile(skin + QStringLiteral("/skin.xml")); - if (skinfile.open(QFile::ReadOnly | QFile::Text)) { - QTextStream in(&skinfile); - bool found_size = false; - while (!in.atEnd()) { - if (kMinSizeRegExp.indexIn(in.readLine()) != -1) { - found_size = true; - break; - } - } - if (found_size) { - return !(kMinSizeRegExp.cap(1).toInt() > screenSize.width() || - kMinSizeRegExp.cap(2).toInt() > screenSize.height()); - } - } - - // If regex failed, fall back to skin name parsing. - QString skinName = skin.left(skin.indexOf(QRegExp("\\d"))); - QString resName = skin.right(skin.count() - skinName.count()); - QString res = resName.left(resName.lastIndexOf(QRegExp("\\d")) + 1); - QString skinWidth = res.left(res.indexOf("x")); - QString skinHeight = res.right(res.count() - skinWidth.count() - 1); - return skinWidth.toInt() <= screenSize.width() && - skinHeight.toInt() <= screenSize.height(); -} - -} // namespace +using mixxx::skin::SkinPointer; DlgPrefInterface::DlgPrefInterface( QWidget* parent, MixxxMainWindow* mixxx, - std::shared_ptr pSkinLoader, + std::shared_ptr pSkinLoader, UserSettingsPointer pConfig) : DlgPreferencePage(parent), m_pConfig(pConfig), m_mixxx(mixxx), m_pSkinLoader(pSkinLoader), + m_pSkin(pSkinLoader->getConfiguredSkin()), m_dScaleFactorAuto(1.0), m_bUseAutoScaleFactor(false), m_dScaleFactor(1.0), @@ -145,34 +110,25 @@ DlgPrefInterface::DlgPrefInterface( skinDescriptionText->setText(""); skinDescriptionText->hide(); - QList skinSearchPaths = m_pSkinLoader->getSkinSearchPaths(); - QList skins; - for (QDir& dir : skinSearchPaths) { - dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); - skins.append(dir.entryInfoList()); + const QList skins = m_pSkinLoader->getSkins(); + int index = 0; + for (const SkinPointer& pSkin : skins) { + ComboBoxSkinconf->insertItem(index, pSkin->name()); + m_skins.insert(pSkin->name(), pSkin); + index++; } - QString configuredSkinPath = m_pSkinLoader->getConfiguredSkinPath(); - int index = 0; + ComboBoxSkinconf->setCurrentIndex(index); + // schemes must be updated here to populate the drop-down box and set m_colorScheme + slotUpdateSchemes(); + skinPreviewLabel->setPixmap(m_pSkin->preview(m_colorScheme)); const auto* const pScreen = getScreen(); - for (const QFileInfo& skinInfo : skins) { - ComboBoxSkinconf->insertItem(index, skinInfo.fileName()); - - if (skinInfo.absoluteFilePath() == configuredSkinPath) { - m_skin = skinInfo.fileName(); - ComboBoxSkinconf->setCurrentIndex(index); - // schemes must be updated here to populate the drop-down box and set m_colorScheme - slotUpdateSchemes(); - skinPreviewLabel->setPixmap(m_pSkinLoader->getSkinPreview(m_skin, m_colorScheme)); - if (skinFitsScreenSize(*pScreen, configuredSkinPath)) { - warningLabel->hide(); - } else { - warningLabel->show(); - } - slotSetSkinDescription(m_skin); - } - index++; + if (m_pSkin->fitsScreenSize(*pScreen)) { + warningLabel->hide(); + } else { + warningLabel->show(); } + slotSetSkinDescription(); connect(ComboBoxSkinconf, QOverload::of(&QComboBox::currentIndexChanged), @@ -234,8 +190,7 @@ QScreen* DlgPrefInterface::getScreen() const { void DlgPrefInterface::slotUpdateSchemes() { // Re-populates the scheme combobox and attempts to pick the color scheme from config file. // Since this involves opening a file we won't do this as part of regular slotUpdate - QList schlist = LegacySkinParser::getSchemeList( - m_pSkinLoader->getSkinPath(m_skin)); + const QList schlist = m_pSkin->colorschemes(); ComboBoxSchemeconf->clear(); @@ -243,7 +198,7 @@ void DlgPrefInterface::slotUpdateSchemes() { ComboBoxSchemeconf->setEnabled(false); ComboBoxSchemeconf->addItem(tr("This skin does not support color schemes", nullptr)); ComboBoxSchemeconf->setCurrentIndex(0); - // clear m_colorScheme so that SkinLoader::getSkinPreview returns the correct preview + // clear m_colorScheme so that the correct skin preview is loaded m_colorScheme = QString(); } else { ComboBoxSchemeconf->setEnabled(true); @@ -269,11 +224,15 @@ void DlgPrefInterface::slotUpdateSchemes() { } void DlgPrefInterface::slotUpdate() { - m_skinOnUpdate = m_pConfig->getValueString(ConfigKey("[Config]", "ResizableSkin")); - if (m_skinOnUpdate.isEmpty()) { - m_skinOnUpdate = m_pSkinLoader->getDefaultSkinName(); + const QString skinNameOnUpdate = + m_pConfig->getValueString(ConfigKey("[Config]", "ResizableSkin")); + const SkinPointer pSkinOnUpdate = m_skins[skinNameOnUpdate]; + if (pSkinOnUpdate != nullptr && pSkinOnUpdate->isValid()) { + m_skinNameOnUpdate = pSkinOnUpdate->name(); + } else { + m_skinNameOnUpdate = m_pSkinLoader->getDefaultSkinName(); } - ComboBoxSkinconf->setCurrentIndex(ComboBoxSkinconf->findText(m_skinOnUpdate)); + ComboBoxSkinconf->setCurrentIndex(ComboBoxSkinconf->findText(m_skinNameOnUpdate)); slotUpdateSchemes(); m_bRebootMixxxView = false; @@ -375,14 +334,12 @@ void DlgPrefInterface::slotSetScheme(int) { m_colorScheme = newScheme; m_bRebootMixxxView = true; } - skinPreviewLabel->setPixmap(m_pSkinLoader->getSkinPreview(m_skin, m_colorScheme)); + skinPreviewLabel->setPixmap(m_pSkin->preview(m_colorScheme)); } -void DlgPrefInterface::slotSetSkinDescription(const QString& skin) { - SkinManifest manifest = LegacySkinParser::getSkinManifest( - LegacySkinParser::openSkin(m_pSkinLoader->getSkinPath(skin))); - QString description = QString::fromStdString(manifest.description()); - if (manifest.has_description() && !description.isEmpty()) { +void DlgPrefInterface::slotSetSkinDescription() { + const QString description = m_pSkin->description(); + if (!description.isEmpty()) { skinDescriptionText->show(); skinDescriptionText->setText(description); } else { @@ -391,26 +348,30 @@ void DlgPrefInterface::slotSetSkinDescription(const QString& skin) { } void DlgPrefInterface::slotSetSkin(int) { - QString newSkin = ComboBoxSkinconf->currentText(); - if (newSkin != m_skin) { - m_skin = newSkin; - m_bRebootMixxxView = newSkin != m_skinOnUpdate; - const auto* const pScreen = getScreen(); - if (pScreen && - skinFitsScreenSize(*pScreen, m_pSkinLoader->getSkinPath(m_skin))) { - warningLabel->hide(); - } else { - warningLabel->show(); - } - slotUpdateSchemes(); - slotSetSkinDescription(m_skin); + QString newSkinName = ComboBoxSkinconf->currentText(); + if (newSkinName == m_pSkin->name()) { + return; } - skinPreviewLabel->setPixmap(m_pSkinLoader->getSkinPreview(newSkin, m_colorScheme)); + const SkinPointer pNewSkin = m_skins[newSkinName]; + VERIFY_OR_DEBUG_ASSERT(pNewSkin != nullptr && pNewSkin->isValid()) { + return; + } + m_pSkin = pNewSkin; + m_bRebootMixxxView = newSkinName != m_skinNameOnUpdate; + const auto* const pScreen = getScreen(); + if (pScreen && m_pSkin->fitsScreenSize(*pScreen)) { + warningLabel->hide(); + } else { + warningLabel->show(); + } + slotUpdateSchemes(); + slotSetSkinDescription(); + skinPreviewLabel->setPixmap(m_pSkin->preview(m_colorScheme)); } void DlgPrefInterface::slotApply() { - m_pConfig->set(ConfigKey("[Config]", "ResizableSkin"), m_skin); + m_pConfig->set(ConfigKey("[Config]", "ResizableSkin"), m_pSkin->name()); m_pConfig->set(ConfigKey("[Config]", "Scheme"), m_colorScheme); QString locale = ComboBoxLocale->itemData( @@ -449,7 +410,7 @@ void DlgPrefInterface::slotApply() { if (m_bRebootMixxxView) { m_mixxx->rebootMixxxView(); // Allow switching skins multiple times without closing the dialog - m_skinOnUpdate = m_skin; + m_skinNameOnUpdate = m_pSkin->name(); } m_bRebootMixxxView = false; } diff --git a/src/preferences/dialog/dlgprefinterface.h b/src/preferences/dialog/dlgprefinterface.h index 694abc2dc1cf..d7004d46ea04 100644 --- a/src/preferences/dialog/dlgprefinterface.h +++ b/src/preferences/dialog/dlgprefinterface.h @@ -1,27 +1,35 @@ #pragma once +#include #include #include +#include #include "preferences/constants.h" #include "preferences/dialog/dlgpreferencepage.h" #include "preferences/dialog/ui_dlgprefinterfacedlg.h" #include "preferences/usersettings.h" +#include "skin/skin.h" class ControlProxy; class ControlPotmeter; -class SkinLoader; class PlayerManager; class MixxxMainWindow; class ControlObject; +namespace mixxx { +namespace skin { +class SkinLoader; +} +} // namespace mixxx + class DlgPrefInterface : public DlgPreferencePage, public Ui::DlgPrefControlsDlg { Q_OBJECT public: DlgPrefInterface( QWidget* parent, MixxxMainWindow* mixxx, - std::shared_ptr pSkinLoader, + std::shared_ptr pSkinLoader, UserSettingsPointer pConfig); ~DlgPrefInterface() override = default; @@ -30,8 +38,9 @@ class DlgPrefInterface : public DlgPreferencePage, public Ui::DlgPrefControlsDlg void slotApply() override; void slotResetToDefaults() override; + private slots: void slotSetTooltips(); - void slotSetSkinDescription(const QString& skin); + void slotSetSkinDescription(); void slotSetSkin(int); void slotSetScheme(int); void slotUpdateSchemes(); @@ -52,10 +61,11 @@ class DlgPrefInterface : public DlgPreferencePage, public Ui::DlgPrefControlsDlg UserSettingsPointer m_pConfig; ControlObject* m_pControlTrackTimeDisplay; MixxxMainWindow *m_mixxx; - std::shared_ptr m_pSkinLoader; + std::shared_ptr m_pSkinLoader; - QString m_skin; - QString m_skinOnUpdate; + QMap m_skins; + mixxx::skin::SkinPointer m_pSkin; + QString m_skinNameOnUpdate; QString m_colorScheme; QString m_localeOnUpdate; mixxx::TooltipsPreference m_tooltipMode; diff --git a/src/skin/legacy/legacyskin.cpp b/src/skin/legacy/legacyskin.cpp new file mode 100644 index 000000000000..8880b9faf5cb --- /dev/null +++ b/src/skin/legacy/legacyskin.cpp @@ -0,0 +1,144 @@ +#include "skin/legacy/legacyskin.h" + +#include "coreservices.h" +#include "skin/legacy/legacyskinparser.h" + +namespace { + +const QRegExp kMinSizeRegExp("(\\d+), *(\\d+)<"); +const QString kSkinManifestFileName(QStringLiteral("skin.xml")); + +} // namespace + +namespace mixxx { +namespace skin { +namespace legacy { + +// static +SkinPointer LegacySkin::fromDirectory(const QDir& dir) { + if (dir.exists(kSkinManifestFileName)) { + return std::make_shared(QFileInfo(dir.absolutePath())); + } + return nullptr; +} + +LegacySkin::LegacySkin(const QFileInfo& path) + : m_path(path) { + DEBUG_ASSERT(isValid()); +} + +bool LegacySkin::isValid() const { + return !m_path.filePath().isEmpty() && m_path.exists(); +} + +QFileInfo LegacySkin::path() const { + DEBUG_ASSERT(isValid()); + return m_path; +} + +QPixmap LegacySkin::preview(const QString& schemeName = QString()) const { + QPixmap preview; + if (!schemeName.isEmpty()) { + QString schemeNameUnformatted = schemeName; + QString schemeNameFormatted = schemeNameUnformatted.replace(" ", ""); + preview.load(m_path.absoluteFilePath() + + QStringLiteral("/skin_preview_") + schemeNameFormatted + + QStringLiteral(".png")); + } else { + preview.load(m_path.absoluteFilePath() + QStringLiteral("/skin_preview.png")); + } + if (!preview.isNull()) { + return preview; + } + preview.load(":/images/skin_preview_placeholder.png"); + return preview; +} + +QFileInfo LegacySkin::skinFile() const { + DEBUG_ASSERT(isValid()); + return QFileInfo(path().absoluteFilePath() + QStringLiteral("/") + kSkinManifestFileName); +} + +QString LegacySkin::name() const { + DEBUG_ASSERT(isValid()); + return m_path.fileName(); +} + +QList LegacySkin::colorschemes() const { + DEBUG_ASSERT(isValid()); + return LegacySkinParser::getSchemeList(path().absoluteFilePath()); +} + +QString LegacySkin::description() const { + DEBUG_ASSERT(isValid()); + SkinManifest manifest = LegacySkinParser::getSkinManifest( + LegacySkinParser::openSkin(path().absoluteFilePath())); + QString description = QString::fromStdString(manifest.description()); + if (!manifest.has_description() || description.isEmpty()) { + return QString(); + } + return description; +} + +bool LegacySkin::fitsScreenSize(const QScreen& screen) const { + DEBUG_ASSERT(isValid()); + // Use the full resolution of the entire screen that is + // available in full-screen mode. + const auto screenSize = screen.size(); + QFile file(skinFile().absoluteFilePath()); + if (file.open(QFile::ReadOnly | QFile::Text)) { + QTextStream in(&file); + bool found_size = false; + while (!in.atEnd()) { + if (kMinSizeRegExp.indexIn(in.readLine()) != -1) { + found_size = true; + break; + } + } + if (found_size) { + return !(kMinSizeRegExp.cap(1).toInt() > screenSize.width() || + kMinSizeRegExp.cap(2).toInt() > screenSize.height()); + } + } + + // If regex failed, fall back to skin name parsing. + QString skinName = name().left(name().indexOf(QRegExp("\\d"))); + QString resName = name().right(name().count() - skinName.count()); + QString res = resName.left(resName.lastIndexOf(QRegExp("\\d")) + 1); + QString skinWidth = res.left(res.indexOf("x")); + QString skinHeight = res.right(res.count() - skinWidth.count() - 1); + return skinWidth.toInt() <= screenSize.width() && + skinHeight.toInt() <= screenSize.height(); +} + +LaunchImage* LegacySkin::loadLaunchImage(QWidget* pParent, UserSettingsPointer pConfig) const { + VERIFY_OR_DEBUG_ASSERT(isValid()) { + return nullptr; + } + LegacySkinParser parser(pConfig); + LaunchImage* pLaunchImage = parser.parseLaunchImage(m_path.absoluteFilePath(), pParent); + return pLaunchImage; +} + +QWidget* LegacySkin::loadSkin(QWidget* pParent, + UserSettingsPointer pConfig, + QSet* pSkinCreatedControls, + mixxx::CoreServices* pCoreServices) const { + VERIFY_OR_DEBUG_ASSERT(isValid()) { + return nullptr; + } + LegacySkinParser legacy(pConfig, + pSkinCreatedControls, + pCoreServices->getKeyboardEventFilter().get(), + pCoreServices->getPlayerManager().get(), + pCoreServices->getControllerManager().get(), + pCoreServices->getLibrary().get(), + pCoreServices->getVinylControlManager().get(), + pCoreServices->getEffectsManager().get(), + pCoreServices->getRecordingManager().get()); + return legacy.parseSkin(m_path.absoluteFilePath(), pParent); +} + +} // namespace legacy +} // namespace skin +} // namespace mixxx diff --git a/src/skin/legacy/legacyskin.h b/src/skin/legacy/legacyskin.h new file mode 100644 index 000000000000..bd011813cd99 --- /dev/null +++ b/src/skin/legacy/legacyskin.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "skin/skin.h" + +namespace mixxx { +namespace skin { +namespace legacy { + +class LegacySkin : public mixxx::skin::Skin { + public: + LegacySkin() = default; + LegacySkin(const QFileInfo& path); + + static SkinPointer fromDirectory(const QDir& dir); + + mixxx::skin::SkinType type() const override { + return mixxx::skin::SkinType::Legacy; + }; + bool isValid() const override; + QFileInfo path() const override; + QPixmap preview(const QString& schemeName) const override; + + QString name() const override; + QString description() const override; + QList colorschemes() const override; + + bool fitsScreenSize(const QScreen& screen) const override; + LaunchImage* loadLaunchImage(QWidget* pParent, UserSettingsPointer pConfig) const override; + QWidget* loadSkin(QWidget* pParent, + UserSettingsPointer pConfig, + QSet* pSkinCreatedControls, + mixxx::CoreServices* pCoreServices) const override; + + private: + QFileInfo skinFile() const; + + QFileInfo m_path; +}; + +} // namespace legacy +} // namespace skin +} // namespace mixxx diff --git a/src/skin/skin.h b/src/skin/skin.h new file mode 100644 index 000000000000..386be45d95c5 --- /dev/null +++ b/src/skin/skin.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "preferences/usersettings.h" + +class ControlObject; +class LaunchImage; + +namespace mixxx { + +class CoreServices; + +namespace skin { + +enum class SkinType { + Legacy, +}; + +class Skin { + public: + virtual ~Skin() = default; + + virtual SkinType type() const = 0; + + virtual bool isValid() const = 0; + virtual QFileInfo path() const = 0; + virtual QPixmap preview(const QString& schemeName) const = 0; + + virtual QString name() const = 0; + virtual QString description() const = 0; + virtual QList colorschemes() const = 0; + + virtual bool fitsScreenSize(const QScreen& screen) const = 0; + + virtual LaunchImage* loadLaunchImage(QWidget* pParent, UserSettingsPointer pConfig) const = 0; + virtual QWidget* loadSkin(QWidget* pParent, + UserSettingsPointer pConfig, + QSet* pSkinCreatedControls, + mixxx::CoreServices* pCoreServices) const = 0; +}; + +typedef std::shared_ptr SkinPointer; + +} // namespace skin +} // namespace mixxx diff --git a/src/skin/skinloader.cpp b/src/skin/skinloader.cpp index d54fbab610ed..a4754ff699cc 100644 --- a/src/skin/skinloader.cpp +++ b/src/skin/skinloader.cpp @@ -5,16 +5,22 @@ #include #include -#include "vinylcontrol/vinylcontrolmanager.h" -#include "skin/legacy/legacyskinparser.h" #include "controllers/controllermanager.h" -#include "library/library.h" #include "effects/effectsmanager.h" +#include "library/library.h" #include "mixer/playermanager.h" -#include "util/debug.h" +#include "recording/recordingmanager.h" #include "skin/legacy/launchimage.h" +#include "skin/legacy/legacyskin.h" +#include "skin/legacy/legacyskinparser.h" +#include "util/debug.h" #include "util/timer.h" -#include "recording/recordingmanager.h" +#include "vinylcontrol/vinylcontrolmanager.h" + +namespace mixxx { +namespace skin { + +using legacy::LegacySkin; SkinLoader::SkinLoader(UserSettingsPointer pConfig) : m_pConfig(pConfig) { @@ -24,6 +30,26 @@ SkinLoader::~SkinLoader() { LegacySkinParser::clearSharedGroupStrings(); } +QList SkinLoader::getSkins() const { + const QList skinSearchPaths = getSkinSearchPaths(); + QList skins; + for (const QDir& dir : skinSearchPaths) { + const QList fileInfos = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QFileInfo& fileInfo : fileInfos) { + QDir skinDir(fileInfo.absoluteFilePath()); + SkinPointer pSkin = skinFromDirectory(skinDir); + if (pSkin) { + VERIFY_OR_DEBUG_ASSERT(pSkin->isValid()) { + continue; + } + skins.append(pSkin); + } + } + } + + return skins; +} + QList SkinLoader::getSkinSearchPaths() const { QList searchPaths; @@ -45,33 +71,23 @@ QList SkinLoader::getSkinSearchPaths() const { return searchPaths; } -QString SkinLoader::getSkinPath(const QString& skinName) const { +SkinPointer SkinLoader::getSkin(const QString& skinName) const { const QList skinSearchPaths = getSkinSearchPaths(); for (QDir dir : skinSearchPaths) { if (dir.cd(skinName)) { - return dir.absolutePath(); + SkinPointer pSkin = skinFromDirectory(dir); + if (pSkin) { + VERIFY_OR_DEBUG_ASSERT(pSkin->isValid()) { + continue; + } + return pSkin; + } } } - return QString(); -} - -QPixmap SkinLoader::getSkinPreview(const QString& skinName, const QString& schemeName) const { - QPixmap preview; - if (!schemeName.isEmpty()) { - QString schemeNameUnformatted = schemeName; - QString schemeNameFormatted = schemeNameUnformatted.replace(" ",""); - preview.load(getSkinPath(skinName) + "/skin_preview_" + schemeNameFormatted + ".png"); - } else { - preview.load(getSkinPath(skinName) + "/skin_preview.png"); - } - if (!preview.isNull()){ - return preview; - } - preview.load(":/images/skin_preview_placeholder.png"); - return preview; + return nullptr; } -QString SkinLoader::getConfiguredSkinPath() const { +SkinPointer SkinLoader::getConfiguredSkin() const { QString configSkin = m_pConfig->getValueString(ConfigKey("[Config]", "ResizableSkin")); // If we don't have a skin defined, we might be migrating from 1.11 and @@ -81,21 +97,18 @@ QString SkinLoader::getConfiguredSkinPath() const { if (!oldSkin.isEmpty()) { configSkin = pickResizableSkin(oldSkin); } - // If the old skin was empty or we couldn't guess a skin, go with the - // default. - if (configSkin.isEmpty()) { - configSkin = getDefaultSkinName(); - } } - QString skinPath = getSkinPath(configSkin); + SkinPointer pSkin = getSkin(configSkin); - if (skinPath.isEmpty()) { - skinPath = getSkinPath(getDefaultSkinName()); - qDebug() << "Could not find the user's configured skin." - << "Falling back on the default skin:" << skinPath; + if (pSkin == nullptr || !pSkin->isValid()) { + const QString defaultSkinName = getDefaultSkinName(); + pSkin = getSkin(defaultSkinName); + qWarning() << "Configured skin" << configSkin + << "not found, falling back to default skin" + << defaultSkinName; } - return skinPath; + return pSkin; } QString SkinLoader::getDefaultSkinName() const { @@ -104,41 +117,63 @@ QString SkinLoader::getDefaultSkinName() const { QWidget* SkinLoader::loadConfiguredSkin(QWidget* pParent, QSet* pSkinCreatedControls, - KeyboardEventFilter* pKeyboard, - PlayerManager* pPlayerManager, - ControllerManager* pControllerManager, - Library* pLibrary, - VinylControlManager* pVCMan, - EffectsManager* pEffectsManager, - RecordingManager* pRecordingManager) { + mixxx::CoreServices* pCoreServices) { ScopedTimer timer("SkinLoader::loadConfiguredSkin"); - QString skinPath = getConfiguredSkinPath(); - - // If we don't have a skin path then fail. - if (skinPath.isEmpty()) { + SkinPointer pSkin = getConfiguredSkin(); + + // If we don't have a skin then fail. This makes sense here, because the + // method above already tried to fall back to the default skin if the + // configured one is not available. If `pSkin` is nullptr, we both the + // configured and the default skin were not found, so there is nothing we + // can do. + VERIFY_OR_DEBUG_ASSERT(pSkin != nullptr && pSkin->isValid()) { return nullptr; } - LegacySkinParser legacy(m_pConfig, - pSkinCreatedControls, - pKeyboard, - pPlayerManager, - pControllerManager, - pLibrary, - pVCMan, - pEffectsManager, - pRecordingManager); - return legacy.parseSkin(skinPath, pParent); + QWidget* pLoadedSkin = pSkin->loadSkin(pParent, m_pConfig, pSkinCreatedControls, pCoreServices); + + // If the skin exists but failed to load, try to fall back to the default skin. + if (pLoadedSkin == nullptr) { + const QString defaultSkinName = getDefaultSkinName(); + if (defaultSkinName == pSkin->name()) { + qCritical() << "Configured skin " << pSkin->name() + << " failed to load, no fallback available (it already " + "is the default skin)"; + } else { + qWarning() << "Configured skin " << pSkin->name() + << " failed to load, falling back to default skin " + << defaultSkinName; + pSkin = getSkin(defaultSkinName); + // If we don't have a skin then fail. + VERIFY_OR_DEBUG_ASSERT(pSkin != nullptr && pSkin->isValid()) { + qCritical() << "Default skin" << defaultSkinName << "not found"; + return nullptr; + } + + // This might also fail, but + pLoadedSkin = pSkin->loadSkin(pParent, m_pConfig, pSkinCreatedControls, pCoreServices); + } + DEBUG_ASSERT(pLoadedSkin != nullptr); + } + + VERIFY_OR_DEBUG_ASSERT(pLoadedSkin != nullptr) { + qCritical() << "No skin can be loaded, please check your installation."; + } + return pLoadedSkin; } -LaunchImage* SkinLoader::loadLaunchImage(QWidget* pParent) { - QString skinPath = getConfiguredSkinPath(); - LegacySkinParser parser(m_pConfig); - LaunchImage* pLaunchImage = parser.parseLaunchImage(skinPath, pParent); +LaunchImage* SkinLoader::loadLaunchImage(QWidget* pParent) const { + SkinPointer pSkin = getConfiguredSkin(); + VERIFY_OR_DEBUG_ASSERT(pSkin != nullptr && pSkin->isValid()) { + return nullptr; + } + + LaunchImage* pLaunchImage = pSkin->loadLaunchImage(pParent, m_pConfig); if (pLaunchImage == nullptr) { // Construct default LaunchImage pLaunchImage = new LaunchImage(pParent, QString()); } + return pLaunchImage; } @@ -154,3 +189,15 @@ QString SkinLoader::pickResizableSkin(const QString& oldSkin) const { } return QString(); } + +SkinPointer SkinLoader::skinFromDirectory(const QDir& dir) const { + SkinPointer pSkin = LegacySkin::fromDirectory(dir); + if (pSkin && pSkin->isValid()) { + return pSkin; + } + + return nullptr; +} + +} // namespace skin +} // namespace mixxx diff --git a/src/skin/skinloader.h b/src/skin/skinloader.h index 9b3796508ffb..e392e5203e80 100644 --- a/src/skin/skinloader.h +++ b/src/skin/skinloader.h @@ -6,16 +6,10 @@ #include #include "preferences/usersettings.h" +#include "skin/skin.h" -class KeyboardEventFilter; -class PlayerManager; -class ControllerManager; -class ControlObject; -class Library; -class VinylControlManager; -class EffectsManager; -class RecordingManager; -class LaunchImage; +namespace mixxx { +namespace skin { class SkinLoader { public: @@ -24,24 +18,22 @@ class SkinLoader { QWidget* loadConfiguredSkin(QWidget* pParent, QSet* skinCreatedControls, - KeyboardEventFilter* pKeyboard, - PlayerManager* pPlayerManager, - ControllerManager* pControllerManager, - Library* pLibrary, - VinylControlManager* pVCMan, - EffectsManager* pEffectsManager, - RecordingManager* pRecordingManager); - - LaunchImage* loadLaunchImage(QWidget* pParent); - - QString getSkinPath(const QString& skinName) const; - QPixmap getSkinPreview(const QString& skinName, const QString& schemeName) const; - QString getConfiguredSkinPath() const; + mixxx::CoreServices* pCoreServices); + + LaunchImage* loadLaunchImage(QWidget* pParent) const; + + SkinPointer getSkin(const QString& skinName) const; + SkinPointer getConfiguredSkin() const; QString getDefaultSkinName() const; QList getSkinSearchPaths() const; + QList getSkins() const; private: QString pickResizableSkin(const QString& oldSkin) const; + SkinPointer skinFromDirectory(const QDir& dir) const; UserSettingsPointer m_pConfig; }; + +} // namespace skin +} // namespace mixxx