2022-10-29 18:27:20 +01:00
// SPDX-License-Identifier: GPL-3.0-only
/*
2022-11-03 03:54:57 +00:00
* Prism Launcher - Minecraft Launcher
2023-06-28 22:02:34 +01:00
* Copyright ( C ) 2022 Tayou < git @ tayou . org >
2022-10-29 18:27:20 +01:00
*
* 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 , version 3.
*
* 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 < https : //www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and
* permission notice :
*
* Copyright 2013 - 2021 MultiMC Contributors
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
2016-11-06 03:29:12 +00:00
# include "CustomTheme.h"
# include <FileSystem.h>
2022-11-03 04:05:07 +00:00
# include <Json.h>
2022-10-29 18:27:20 +01:00
# include "ThemeManager.h"
2016-11-06 03:29:12 +00:00
2022-11-03 04:05:07 +00:00
const char * themeFile = " theme.json " ;
2016-11-06 03:29:12 +00:00
2022-11-03 04:05:07 +00:00
static bool readThemeJson ( const QString & path ,
QPalette & palette ,
double & fadeAmount ,
QColor & fadeColor ,
QString & name ,
QString & widgets ,
QString & qssFilePath ,
bool & dataIncomplete )
2016-11-06 03:29:12 +00:00
{
2018-07-15 13:51:05 +01:00
QFileInfo pathInfo ( path ) ;
2022-11-03 04:05:07 +00:00
if ( pathInfo . exists ( ) & & pathInfo . isFile ( ) ) {
try {
2018-07-15 13:51:05 +01:00
auto doc = Json : : requireDocument ( path , " Theme JSON file " ) ;
const QJsonObject root = doc . object ( ) ;
2022-10-29 18:27:20 +01:00
dataIncomplete = ! root . contains ( " qssFilePath " ) ;
2018-07-15 13:51:05 +01:00
name = Json : : requireString ( root , " name " , " Theme name " ) ;
widgets = Json : : requireString ( root , " widgets " , " Qt widget theme " ) ;
2022-10-29 18:27:20 +01:00
qssFilePath = Json : : ensureString ( root , " qssFilePath " , " themeStyle.css " ) ;
2018-07-15 13:51:05 +01:00
auto colorsRoot = Json : : requireObject ( root , " colors " , " colors object " ) ;
2022-11-03 04:05:07 +00:00
auto readColor = [ & ] ( QString colorName ) - > QColor {
2018-07-15 13:51:05 +01:00
auto colorValue = Json : : ensureString ( colorsRoot , colorName , QString ( ) ) ;
2022-11-03 04:05:07 +00:00
if ( ! colorValue . isEmpty ( ) ) {
2018-07-15 13:51:05 +01:00
QColor color ( colorValue ) ;
2022-11-03 04:05:07 +00:00
if ( ! color . isValid ( ) ) {
2022-11-01 14:41:08 +00:00
themeWarningLog ( ) < < " Color value " < < colorValue < < " for " < < colorName < < " was not recognized. " ;
2018-07-15 13:51:05 +01:00
return QColor ( ) ;
}
return color ;
}
return QColor ( ) ;
} ;
2022-11-03 04:05:07 +00:00
auto readAndSetColor = [ & ] ( QPalette : : ColorRole role , QString colorName ) {
2018-07-15 13:51:05 +01:00
auto color = readColor ( colorName ) ;
2022-11-03 04:05:07 +00:00
if ( color . isValid ( ) ) {
2018-07-15 13:51:05 +01:00
palette . setColor ( role , color ) ;
2022-11-03 04:05:07 +00:00
} else {
2022-11-01 14:41:08 +00:00
themeDebugLog ( ) < < " Color value for " < < colorName < < " was not present. " ;
2018-07-15 13:51:05 +01:00
}
} ;
// palette
readAndSetColor ( QPalette : : Window , " Window " ) ;
readAndSetColor ( QPalette : : WindowText , " WindowText " ) ;
readAndSetColor ( QPalette : : Base , " Base " ) ;
readAndSetColor ( QPalette : : AlternateBase , " AlternateBase " ) ;
readAndSetColor ( QPalette : : ToolTipBase , " ToolTipBase " ) ;
readAndSetColor ( QPalette : : ToolTipText , " ToolTipText " ) ;
readAndSetColor ( QPalette : : Text , " Text " ) ;
readAndSetColor ( QPalette : : Button , " Button " ) ;
readAndSetColor ( QPalette : : ButtonText , " ButtonText " ) ;
readAndSetColor ( QPalette : : BrightText , " BrightText " ) ;
readAndSetColor ( QPalette : : Link , " Link " ) ;
readAndSetColor ( QPalette : : Highlight , " Highlight " ) ;
readAndSetColor ( QPalette : : HighlightedText , " HighlightedText " ) ;
2022-11-03 04:05:07 +00:00
// fade
2018-07-15 13:51:05 +01:00
fadeColor = readColor ( " fadeColor " ) ;
fadeAmount = Json : : ensureDouble ( colorsRoot , " fadeAmount " , 0.5 , " fade amount " ) ;
2022-11-03 04:05:07 +00:00
} catch ( const Exception & e ) {
2022-11-01 14:41:08 +00:00
themeWarningLog ( ) < < " Couldn't load theme json: " < < e . cause ( ) ;
2018-07-15 13:51:05 +01:00
return false ;
}
2022-11-03 04:05:07 +00:00
} else {
2022-11-01 14:41:08 +00:00
themeDebugLog ( ) < < " No theme json present. " ;
2018-07-15 13:51:05 +01:00
return false ;
}
return true ;
2016-11-06 03:29:12 +00:00
}
2022-11-03 04:05:07 +00:00
static bool writeThemeJson ( const QString & path ,
const QPalette & palette ,
double fadeAmount ,
QColor fadeColor ,
QString name ,
QString widgets ,
QString qssFilePath )
2016-11-06 03:29:12 +00:00
{
2018-07-15 13:51:05 +01:00
QJsonObject rootObj ;
rootObj . insert ( " name " , name ) ;
rootObj . insert ( " widgets " , widgets ) ;
2022-10-29 18:27:20 +01:00
rootObj . insert ( " qssFilePath " , qssFilePath ) ;
2018-07-15 13:51:05 +01:00
QJsonObject colorsObj ;
2022-11-03 04:05:07 +00:00
auto insertColor = [ & ] ( QPalette : : ColorRole role , QString colorName ) { colorsObj . insert ( colorName , palette . color ( role ) . name ( ) ) ; } ;
2018-07-15 13:51:05 +01:00
// palette
insertColor ( QPalette : : Window , " Window " ) ;
insertColor ( QPalette : : WindowText , " WindowText " ) ;
insertColor ( QPalette : : Base , " Base " ) ;
insertColor ( QPalette : : AlternateBase , " AlternateBase " ) ;
insertColor ( QPalette : : ToolTipBase , " ToolTipBase " ) ;
insertColor ( QPalette : : ToolTipText , " ToolTipText " ) ;
insertColor ( QPalette : : Text , " Text " ) ;
insertColor ( QPalette : : Button , " Button " ) ;
insertColor ( QPalette : : ButtonText , " ButtonText " ) ;
insertColor ( QPalette : : BrightText , " BrightText " ) ;
insertColor ( QPalette : : Link , " Link " ) ;
insertColor ( QPalette : : Highlight , " Highlight " ) ;
insertColor ( QPalette : : HighlightedText , " HighlightedText " ) ;
// fade
colorsObj . insert ( " fadeColor " , fadeColor . name ( ) ) ;
colorsObj . insert ( " fadeAmount " , fadeAmount ) ;
rootObj . insert ( " colors " , colorsObj ) ;
2022-11-03 04:05:07 +00:00
try {
2018-07-15 13:51:05 +01:00
Json : : write ( rootObj , path ) ;
return true ;
2022-11-03 04:05:07 +00:00
} catch ( const Exception & e ) {
2022-11-01 14:41:08 +00:00
themeWarningLog ( ) < < " Failed to write theme json to " < < path ;
2018-07-15 13:51:05 +01:00
return false ;
}
2016-11-06 03:29:12 +00:00
}
2022-10-29 18:27:20 +01:00
/// @param baseTheme Base Theme
/// @param fileInfo FileInfo object for file to load
/// @param isManifest whether to load a theme manifest or a qss file
CustomTheme : : CustomTheme ( ITheme * baseTheme , QFileInfo & fileInfo , bool isManifest )
2016-11-06 03:29:12 +00:00
{
2022-10-29 18:27:20 +01:00
if ( isManifest ) {
m_id = fileInfo . dir ( ) . dirName ( ) ;
2018-07-15 13:51:05 +01:00
2022-10-29 18:27:20 +01:00
QString path = FS : : PathCombine ( " themes " , m_id ) ;
QString pathResources = FS : : PathCombine ( " themes " , m_id , " resources " ) ;
2018-07-15 13:51:05 +01:00
2022-11-03 04:05:07 +00:00
if ( ! FS : : ensureFolderPathExists ( path ) | | ! FS : : ensureFolderPathExists ( pathResources ) ) {
2022-11-01 14:41:08 +00:00
themeWarningLog ( ) < < " couldn't create folder for theme! " ;
2022-10-29 18:27:20 +01:00
return ;
}
2018-07-15 13:51:05 +01:00
2022-10-29 18:27:20 +01:00
auto themeFilePath = FS : : PathCombine ( path , themeFile ) ;
bool jsonDataIncomplete = false ;
2018-07-15 13:51:05 +01:00
m_palette = baseTheme - > colorScheme ( ) ;
2023-01-09 15:54:10 +00:00
if ( readThemeJson ( themeFilePath , m_palette , m_fadeAmount , m_fadeColor , m_name , m_widgets , m_qssFilePath , jsonDataIncomplete ) ) {
// If theme data was found, fade "Disabled" color of each role according to FadeAmount
2022-10-29 18:27:20 +01:00
m_palette = fadeInactive ( m_palette , m_fadeAmount , m_fadeColor ) ;
2023-01-09 15:54:10 +00:00
} else {
themeDebugLog ( ) < < " Did not read theme json file correctly, not changing theme, keeping previous. " ;
return ;
2018-07-15 13:51:05 +01:00
}
2023-01-09 15:54:10 +00:00
// FIXME: This is kinda jank, it only actually checks if the qss file path is not present. It should actually check for any relevant missing data (e.g. name, colors)
2022-11-03 04:05:07 +00:00
if ( jsonDataIncomplete ) {
2022-10-29 18:27:20 +01:00
writeThemeJson ( fileInfo . absoluteFilePath ( ) , m_palette , m_fadeAmount , m_fadeColor , m_name , m_widgets , m_qssFilePath ) ;
2018-07-15 13:51:05 +01:00
}
2022-10-29 18:27:20 +01:00
2022-11-03 03:54:57 +00:00
auto qssFilePath = FS : : PathCombine ( path , m_qssFilePath ) ;
2022-11-03 04:05:07 +00:00
QFileInfo info ( qssFilePath ) ;
if ( info . isFile ( ) ) {
try {
2023-01-09 15:54:10 +00:00
// TODO: validate qss?
2022-11-03 03:54:57 +00:00
m_styleSheet = QString : : fromUtf8 ( FS : : read ( qssFilePath ) ) ;
2022-11-03 04:05:07 +00:00
} catch ( const Exception & e ) {
2023-01-09 15:54:10 +00:00
themeWarningLog ( ) < < " Couldn't load qss: " < < e . cause ( ) < < " from " < < qssFilePath ;
return ;
2022-10-29 18:27:20 +01:00
}
2022-11-03 04:05:07 +00:00
} else {
2023-01-09 15:54:10 +00:00
themeDebugLog ( ) < < " No theme qss present. " ;
2018-07-15 13:51:05 +01:00
}
2022-10-29 18:27:20 +01:00
} else {
m_id = fileInfo . fileName ( ) ;
m_name = fileInfo . baseName ( ) ;
QString path = fileInfo . filePath ( ) ;
2022-11-03 04:05:07 +00:00
// themeDebugLog << "Theme ID: " << m_id;
// themeDebugLog << "Theme Name: " << m_name;
// themeDebugLog << "Theme Path: " << path;
2022-10-29 18:27:20 +01:00
2022-11-03 04:05:07 +00:00
if ( ! FS : : ensureFilePathExists ( path ) ) {
2022-11-01 14:41:08 +00:00
themeWarningLog ( ) < < m_name < < " Theme file path doesn't exist! " ;
2022-10-29 18:27:20 +01:00
m_palette = baseTheme - > colorScheme ( ) ;
m_styleSheet = baseTheme - > appStyleSheet ( ) ;
return ;
}
m_palette = baseTheme - > colorScheme ( ) ;
2022-11-03 04:05:07 +00:00
try {
2022-10-29 18:27:20 +01:00
// TODO: validate qss?
m_styleSheet = QString : : fromUtf8 ( FS : : read ( path ) ) ;
2022-11-03 04:05:07 +00:00
} catch ( const Exception & e ) {
2022-11-01 14:41:08 +00:00
themeWarningLog ( ) < < " Couldn't load qss: " < < e . cause ( ) < < " from " < < path ;
2022-10-29 18:27:20 +01:00
m_styleSheet = baseTheme - > appStyleSheet ( ) ;
2018-07-15 13:51:05 +01:00
}
}
2016-11-06 03:29:12 +00:00
}
2016-11-06 04:48:52 +00:00
QStringList CustomTheme : : searchPaths ( )
{
2018-07-15 13:51:05 +01:00
return { FS : : PathCombine ( " themes " , m_id , " resources " ) } ;
2016-11-06 04:48:52 +00:00
}
2016-11-06 03:29:12 +00:00
QString CustomTheme : : id ( )
{
2018-07-15 13:51:05 +01:00
return m_id ;
2016-11-06 03:29:12 +00:00
}
QString CustomTheme : : name ( )
{
2018-07-15 13:51:05 +01:00
return m_name ;
2016-11-06 03:29:12 +00:00
}
2017-01-15 21:56:03 +00:00
bool CustomTheme : : hasColorScheme ( )
{
2018-07-15 13:51:05 +01:00
return true ;
2017-01-15 21:56:03 +00:00
}
2016-11-06 03:29:12 +00:00
QPalette CustomTheme : : colorScheme ( )
{
2018-07-15 13:51:05 +01:00
return m_palette ;
2016-11-06 03:29:12 +00:00
}
2017-01-15 21:56:03 +00:00
bool CustomTheme : : hasStyleSheet ( )
{
2018-07-15 13:51:05 +01:00
return true ;
2017-01-15 21:56:03 +00:00
}
2016-11-06 03:29:12 +00:00
QString CustomTheme : : appStyleSheet ( )
{
2018-07-15 13:51:05 +01:00
return m_styleSheet ;
2016-11-06 03:29:12 +00:00
}
double CustomTheme : : fadeAmount ( )
{
2018-07-15 13:51:05 +01:00
return m_fadeAmount ;
2016-11-06 03:29:12 +00:00
}
QColor CustomTheme : : fadeColor ( )
{
2018-07-15 13:51:05 +01:00
return m_fadeColor ;
2016-11-06 03:29:12 +00:00
}
QString CustomTheme : : qtTheme ( )
{
2018-07-15 13:51:05 +01:00
return m_widgets ;
2016-11-06 03:29:12 +00:00
}