File indexing completed on 2024-12-08 11:02:06
0001 /*************************************************************************** 0002 * Copyright (C) 2020 by Renaud Guezennec * 0003 * http://www.rolisteam.org/contact * 0004 * * 0005 * This software is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 0019 ***************************************************************************/ 0020 #include <common_qml/theme.h> 0021 0022 #include <QColor> 0023 #include <QDebug> 0024 #include <QFile> 0025 #include <QFileInfo> 0026 #include <QRegularExpression> 0027 #include <QTextStream> 0028 0029 namespace customization 0030 { 0031 0032 namespace 0033 { 0034 QString pathToDarkMode(const QString& url, bool darkMode) 0035 { 0036 auto path= url; 0037 path= path.replace("qrc", ""); 0038 if(!darkMode || !QFileInfo::exists(path)) 0039 return url; 0040 0041 QFileInfo info(path); 0042 auto fileName= info.fileName(); 0043 auto prefixPath= info.absolutePath(); 0044 0045 auto combo= QString("%1/+dark/%2").arg(prefixPath, fileName); 0046 0047 if(QFileInfo::exists(combo)) 0048 return QString("qrc%1").arg(combo); 0049 else 0050 return url; 0051 } 0052 } // namespace 0053 StyleSheet::StyleSheet(Theme* parent) : QQmlPropertyMap(parent) {} 0054 0055 void StyleSheet::insertOrUpdate(const QString& key, const QVariant& value) 0056 { 0057 insert(key, value); 0058 0059 emit valueChanged(key, value); 0060 } 0061 0062 QString Theme::m_dataPath= ""; 0063 0064 Theme::Theme(QObject* parent) : QObject(parent) 0065 { 0066 loadData(m_dataPath); 0067 } 0068 0069 StyleSheet* Theme::styleSheet(const QString& id) 0070 { 0071 auto it= m_styleSheets.find(id); 0072 if(it == m_styleSheets.end()) 0073 return nullptr; 0074 return it->second; 0075 } 0076 0077 bool Theme::nightMode() const 0078 { 0079 return m_nightMode; 0080 } 0081 0082 QString Theme::folder() const 0083 { 0084 return m_nightMode ? QStringLiteral("+dark/") : QString(); 0085 } 0086 0087 void Theme::setPath(const QString& path) 0088 { 0089 m_dataPath= path; 0090 Q_ASSERT(QFileInfo::exists(m_dataPath)); 0091 } 0092 0093 void Theme::setNightMode(bool b) 0094 { 0095 if(b == m_nightMode) 0096 return; 0097 m_nightMode= b; 0098 emit nightModeChanged(m_nightMode); 0099 emit folderChanged(folder()); 0100 loadData(m_dataPath); 0101 } 0102 0103 Theme* Theme::instance() 0104 { 0105 static Theme theme; 0106 return &theme; 0107 } 0108 0109 void Theme::loadData(const QString& source) 0110 { 0111 QFile file(source); 0112 if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) 0113 return; 0114 0115 QTextStream in(&file); 0116 StyleSheet* currentStyleSheet= nullptr; 0117 while(!in.atEnd()) 0118 { 0119 auto line= file.readLine(); 0120 if(line.isEmpty()) 0121 continue; 0122 0123 auto str= line; 0124 0125 if(str.startsWith("[")) 0126 { 0127 auto label= str.mid(1, str.length() - 3); 0128 currentStyleSheet= addStyleSheet(label); 0129 } 0130 else if(currentStyleSheet) 0131 { 0132 QRegularExpression exp("^(\\w+)=(.*)$"); 0133 auto match= exp.match(str, 0, QRegularExpression::PartialPreferCompleteMatch); 0134 if(match.hasMatch()) 0135 { 0136 auto key= match.captured(1); 0137 auto value= match.captured(2); 0138 bool ok; 0139 auto intVal= value.toInt(&ok); 0140 if(ok) 0141 { 0142 currentStyleSheet->insertOrUpdate(key, intVal); 0143 continue; 0144 } 0145 0146 auto realVal= value.toDouble(&ok); 0147 if(ok) 0148 { 0149 currentStyleSheet->insertOrUpdate(key, realVal); 0150 continue; 0151 } 0152 0153 QColor color; 0154 color.setNamedColor(value); 0155 if(color.isValid()) 0156 { 0157 if(!key.endsWith("_dark")) 0158 { 0159 currentStyleSheet->insertOrUpdate(key, darkColor(color)); 0160 } 0161 else if(m_nightMode) 0162 { 0163 currentStyleSheet->insertOrUpdate(key.remove("_dark"), color); 0164 } 0165 } 0166 else 0167 { 0168 currentStyleSheet->insertOrUpdate(key, pathToDarkMode(value, m_nightMode)); 0169 } 0170 } 0171 } 0172 } 0173 } 0174 0175 QColor Theme::darkColor(const QColor& color) 0176 { 0177 if(!m_nightMode) 0178 return color; 0179 0180 std::vector<int> c({color.red(), color.green(), color.blue()}); 0181 auto brighness= [](const QColor& current) 0182 { return ((current.red() * 299) + (current.green() * 587) + (current.blue() * 114)) / 1000; }; 0183 auto avg= std::accumulate(c.begin(), c.end(), 0.0) / static_cast<double>(c.size()); 0184 auto allEqual= std::all_of(c.begin(), c.end(), [color](int tmp) { return color.red() == tmp; }); 0185 QColor result= color; 0186 if(allEqual) 0187 { 0188 auto cVal= static_cast<int>(255 - avg); 0189 result= QColor(cVal, cVal, cVal); 0190 } 0191 else 0192 { 0193 auto brighnessVal= brighness(result); 0194 auto target= 255 - brighness(result); 0195 auto darker= brighnessVal > 128; 0196 int i= 0; 0197 while(std::abs(brighnessVal - target) > 50 && i < 8) 0198 { 0199 result= darker ? result.darker(120) : result.lighter(120); 0200 brighnessVal= brighness(result); 0201 ++i; 0202 } 0203 } 0204 result.setAlpha(color.alpha()); 0205 return result; 0206 } 0207 0208 StyleSheet* Theme::addStyleSheet(const QString& name) 0209 { 0210 auto styleSheet= Theme::styleSheet(name); 0211 if(nullptr == styleSheet) 0212 { 0213 styleSheet= new StyleSheet(this); 0214 m_styleSheets.insert({name, styleSheet}); 0215 } 0216 return styleSheet; 0217 } 0218 0219 QFont Theme::imFont() const 0220 { 0221 return m_imFont; 0222 } 0223 0224 void Theme::setImFont(const QFont& newImFont) 0225 { 0226 if(m_imFont == newImFont) 0227 return; 0228 m_imFont= newImFont; 0229 emit imFontChanged(); 0230 } 0231 0232 QFont Theme::imLittleFont() const 0233 { 0234 QFont res= m_imFont; 0235 res.setPointSize(res.pointSize() * 0.8); 0236 return res; 0237 } 0238 0239 QFont Theme::imBigFont() const 0240 { 0241 QFont res= m_imFont; 0242 res.setPointSize(res.pointSize() * 2); 0243 res.setBold(true); 0244 return res; 0245 } 0246 0247 } // namespace customization