File indexing completed on 2024-05-05 04:46:59

0001 /*
0002  * <one line to give the program's name and a brief idea of what it does.>
0003  * Copyright (C) 2019  camilo <chiguitar@unal.edu.co>
0004  *
0005  * This program 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 3 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, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 #include "mauiapp.h"
0020 #include "fmh.h"
0021 #include "style.h"
0022 
0023 #include <QDir>
0024 #include <QStandardPaths>
0025 #include <QQuickWindow>
0026 #include <QQuickItem>
0027 #include <QWindow>
0028 
0029 #include <KLocalizedString>
0030 #include <KCoreAddons>
0031 
0032 #if defined BUNDLE_LUV_ICONS
0033 #include <QIcon>
0034 #endif
0035 
0036 #include <QQuickStyle>
0037 
0038 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0039 #include <MauiMan3/thememanager.h>
0040 #else
0041 #include <MauiMan4/thememanager.h>
0042 #endif
0043 
0044 #include "../mauikit_version.h"
0045 #include "moduleinfo.h"
0046 
0047 MauiApp *MauiApp::m_instance = nullptr;
0048 
0049 KAboutComponent MauiApp::aboutMauiKit()
0050 {
0051     return KAboutComponent("MauiKit", i18n("Multi-adaptable user interfaces."), MauiApp::getMauikitVersion(), "https://mauikit.org", KAboutLicense::GPL_V3);
0052 }
0053 
0054 MauiApp::MauiApp()
0055     : QObject(nullptr)
0056     , m_themeSettings( new MauiMan::ThemeManager(this))
0057     , m_controls(new CSDControls(this))
0058 {
0059     qDebug() << "CREATING INSTANCE OF MAUI APP";
0060     connect(qApp, &QCoreApplication::aboutToQuit, []()
0061     {
0062         qDebug() << "Lets remove MauiApp singleton instance";
0063         delete m_instance;
0064         m_instance = nullptr;
0065     });
0066     
0067     KAboutData aboutData(KAboutData::applicationData());
0068     if (aboutData.translators().isEmpty())
0069     {
0070         aboutData.setTranslator(i18ndc(nullptr, "NAME OF TRANSLATORS", "Your names"), //
0071                                 i18ndc(nullptr, "EMAIL OF TRANSLATORS", "Your emails"));
0072 
0073     }
0074 
0075     const auto MauiData = MauiKitCore::aboutData();
0076     aboutData.addComponent(MauiData.name(),
0077                            MauiKitCore::buildVersion(),
0078                            MauiData.version(),
0079                            MauiData.webAddress(),
0080                            MauiData.license().key());
0081 
0082     aboutData.addComponent("Qt", "", QT_VERSION_STR, "https://qt.io");
0083 
0084     aboutData.addComponent(QStringLiteral("KDE Frameworks"), "", KCoreAddons::versionString(), "https://kde.org");
0085 
0086 
0087 #if defined BUNDLE_LUV_ICONS
0088     const auto luvData = MauiKitCore::aboutLuv();
0089     aboutData.addComponent(luvData.name(),
0090                            "",
0091                            luvData.version(),
0092                            luvData.webAddress(),
0093                            luvData.license().key());
0094 #endif
0095 
0096     KAboutData::setApplicationData(aboutData);
0097     setDefaultMauiStyle();
0098 }
0099 
0100 QString MauiApp::getMauikitVersion()
0101 {
0102     return MAUIKIT_VERSION_STRING;
0103 }
0104 
0105 QString MauiApp::getIconName() const
0106 {
0107     return m_iconName;
0108 }
0109 
0110 void MauiApp::setIconName(const QString &value)
0111 {
0112     if (m_iconName == value)
0113         return;
0114     
0115     m_iconName = value;
0116     Q_EMIT this->iconNameChanged();
0117 }
0118 
0119 QString MauiApp::getDonationPage() const
0120 {
0121     return m_donationPage;
0122 }
0123 
0124 void MauiApp::setDonationPage(const QString &value)
0125 {
0126     if (m_donationPage == value)
0127         return;
0128     
0129     m_donationPage = value;
0130     Q_EMIT this->donationPageChanged();
0131 }
0132 
0133 KAboutData MauiApp::getAbout() const
0134 {
0135     return KAboutData::applicationData();
0136 }
0137 
0138 void MauiApp::setDefaultMauiStyle()
0139 {
0140 #if defined BUNDLE_LUV_ICONS
0141     Q_INIT_RESOURCE(icons);
0142     QIcon::setThemeSearchPaths({":/icons/luv-icon-theme"});
0143     QIcon::setThemeName("Luv");
0144 #endif
0145     
0146 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0147     QQuickStyle::setStyle("maui-style");
0148 #else
0149     QQuickStyle::setStyle(QStringLiteral("QtQuick.Controls.Maui"));
0150 #endif
0151 }
0152 
0153 MauiApp *MauiApp::qmlAttachedProperties(QObject *object)
0154 {
0155     Q_UNUSED(object)
0156     return MauiApp::instance();
0157 }
0158 
0159 CSDControls::CSDControls(QObject *parent) : QObject (parent)
0160   ,m_themeSettings( new MauiMan::ThemeManager(this))
0161 {       
0162     connect(m_themeSettings, &MauiMan::ThemeManager::enableCSDChanged, [this](bool enabled)
0163     {
0164         qDebug() << "CSD ENABLED CHANGED<<<<" << enabled;
0165         
0166         getWindowControlsSettings();
0167     });
0168     
0169     connect(m_themeSettings, &MauiMan::ThemeManager::windowControlsThemeChanged, [this](QString style)
0170     {
0171         m_styleName = style;
0172         setStyle();
0173         
0174         Q_EMIT styleNameChanged();
0175         Q_EMIT sourceChanged();
0176     });
0177     
0178     getWindowControlsSettings();
0179 }
0180 
0181 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0182 
0183 static QString CSDLookupPath = "org.mauikit.controls/csd.5/%1/config.conf";
0184 #else
0185 static QString CSDLookupPath = "org.mauikit.controls/csd.6/%1/config.conf";
0186 #endif
0187 
0188 void CSDControls::setStyle()
0189 {
0190     auto confFile = QStandardPaths::locate (QStandardPaths::GenericDataLocation, QString(CSDLookupPath).arg(m_styleName));
0191     QFileInfo file(confFile);
0192     if(file.exists ())
0193     {
0194         const auto dir = QUrl::fromLocalFile (file.dir ().absolutePath ());
0195         
0196         QSettings conf (confFile, QSettings::IniFormat);
0197         conf.beginGroup ("Decoration");
0198         m_source = dir.toString()+"/"+ conf.value("Source").toString();
0199         conf.endGroup ();
0200     }
0201     
0202     qDebug() << "CSD QML SOURCXE" << m_source;
0203     m_rightWindowControls =  QStringList {"I", "A", "X"};
0204 }
0205 
0206 void CSDControls::getWindowControlsSettings()
0207 {        
0208     if(m_enabledCSD_blocked)
0209         return;
0210     
0211     m_enableCSD = m_themeSettings->enableCSD();
0212     Q_EMIT enableCSDChanged();
0213     
0214     /*  #if (defined Q_OS_LINUX || defined Q_OS_FREEBSD) && !defined Q_OS_ANDROID
0215 
0216     #ifdef FORMFACTOR_FOUND
0217     
0218     if(m_formFactor->preferredMode() == 0)
0219     {
0220         m_enableCSD = m_themeSettings->enableCSD();
0221         Q_EMIT enableCSDChanged();
0222         
0223     }else
0224     {
0225         m_enableCSD = false;
0226         Q_EMIT enableCSDChanged();
0227         return;
0228     }
0229 #else //Fallback in case FormFactor is not found. and then check for the env var QT_QUICK_CONTROLS_MOBILE
0230     if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE"))
0231     {
0232         if (QByteArrayList {"0", "false"}.contains(qgetenv("QT_QUICK_CONTROLS_MOBILE")))
0233         {
0234             m_enableCSD = m_themeSettings->enableCSD();
0235             Q_EMIT enableCSDChanged();
0236         }else
0237         {
0238             return;
0239         }
0240     }
0241     #endif  */
0242     
0243     m_styleName = m_themeSettings->windowControlsTheme();
0244     setStyle();
0245 }
0246 
0247 bool CSDControls::enableCSD() const
0248 {
0249     return m_enableCSD;
0250 }
0251 
0252 void CSDControls::setEnableCSD(const bool &value)
0253 {
0254     m_enabledCSD_blocked = true;
0255     if (m_enableCSD == value)
0256         return;
0257     
0258     m_enableCSD = value;
0259     Q_EMIT enableCSDChanged();
0260 }
0261 
0262 void CSDControls::resetEnableCSD()
0263 {
0264     m_enabledCSD_blocked = false;
0265     getWindowControlsSettings();
0266 }
0267 
0268 QUrl CSDControls::source() const
0269 {
0270     return m_source;
0271 }
0272 
0273 QString CSDControls::styleName() const
0274 {
0275     return m_styleName;
0276 }
0277 
0278 CSDButton::CSDButton(QObject *parent): QObject(parent)
0279 {
0280     connect(this, &CSDButton::typeChanged, this, &CSDButton::setSources);
0281     // connect(this, &CSDButton::styleChanged, this, &CSDButton::setSources);
0282     connect(this, &CSDButton::stateChanged, this, &CSDButton::requestCurrentSource);
0283     // connect(MauiApp::instance()->controls(), &CSDControls::styleNameChanged, this, &CSDButton::setSources);
0284     
0285     m_style = MauiApp::instance()->controls()->styleName();
0286     setSources();
0287 }
0288 
0289 void CSDButton::setStyle(const QString& style)
0290 {
0291     if(m_style == style)
0292     {
0293         return;
0294     }
0295     
0296     m_style = style;
0297     Q_EMIT styleChanged();
0298 }
0299 
0300 QString CSDButton::style() const
0301 {
0302     return m_style;
0303 }
0304 
0305 QUrl CSDButton::source() const
0306 {
0307     return m_source;
0308 }
0309 
0310 void CSDButton::setSources()
0311 {
0312     const auto confFile = QStandardPaths::locate (QStandardPaths::GenericDataLocation, QString(CSDLookupPath).arg(m_style));
0313 
0314     QFileInfo file(confFile);
0315     if(file.exists ())
0316     {
0317         m_dir = QUrl::fromLocalFile (file.dir ().absolutePath ());
0318         QSettings conf (confFile, QSettings::IniFormat);
0319         m_sources.insert (CSDButtonState::Normal, extractStateValue (conf, CSDButtonState::Normal));
0320         m_sources.insert (CSDButtonState::Hover, extractStateValue (conf, CSDButtonState::Hover));
0321         m_sources.insert (CSDButtonState::Pressed, extractStateValue (conf, CSDButtonState::Pressed));
0322         m_sources.insert (CSDButtonState::Backdrop, extractStateValue (conf, CSDButtonState::Backdrop));
0323         m_sources.insert (CSDButtonState::Disabled, extractStateValue (conf, CSDButtonState::Disabled));
0324     }
0325     
0326     this->requestCurrentSource ();
0327 }
0328 
0329 CSDButton::CSDButtonState CSDButton::state() const
0330 {
0331     return m_state;
0332 }
0333 
0334 QUrl CSDButton::extractStateValue(QSettings &settings, const CSDButton::CSDButtonState &state)
0335 {
0336     QUrl res;
0337     
0338     settings.beginGroup (mapButtonType (m_type));
0339     res =  m_dir.toString ()+"/"+settings.value (mapButtonState (state)).toString ();
0340     settings.endGroup ();
0341 
0342     if(QFile::exists (res.toLocalFile ()))
0343     {
0344         return res;
0345     }else
0346     {
0347         return QUrl("dialog-close"); //put here a fallback button
0348     }
0349 }
0350 
0351 void CSDButton::requestCurrentSource()
0352 {
0353     m_source = this->m_sources.value (this->m_state);
0354     Q_EMIT this->sourceChanged ();
0355 }
0356 
0357 QString CSDButton::mapButtonType(const CSDButtonType &type)
0358 {
0359     switch(type)
0360     {
0361     case Close: return "Close";
0362     case Maximize: return "Maximize";
0363     case Minimize: return "Minimize";
0364     case Restore: return "Restore";
0365     case Fullscreen: return "Fullscreen";
0366     default: return "";
0367     }
0368 }
0369 
0370 QString CSDButton::mapButtonState(const CSDButtonState &type)
0371 {
0372     switch(type)
0373     {
0374     case Normal: return "Normal";
0375     case Hover: return "Hover";
0376     case Pressed: return "Pressed";
0377     case Backdrop: return "Backdrop";
0378     case Disabled: return "Disabled";
0379     default: return "";
0380     }
0381 }
0382 
0383 void CSDButton::setState(const CSDButtonState &newState)
0384 {
0385     if (m_state == newState)
0386         return;
0387     m_state = newState;
0388     Q_EMIT stateChanged();
0389 }
0390 
0391 CSDControls *MauiApp::controls() const
0392 {
0393     return m_controls;
0394 }
0395 
0396 CSDButton::CSDButtonType CSDButton::type() const
0397 {
0398     return m_type;
0399 }
0400 
0401 void CSDButton::setType(CSDButtonType newType)
0402 {
0403     if (m_type == newType)
0404         return;
0405     
0406     m_type = newType;
0407     Q_EMIT typeChanged();
0408 }
0409 
0410 CSDButton::CSDButtonType CSDButton::mapType(const QString &value)
0411 {
0412     if(value == "X") return  CSDButton::CSDButtonType::Close;
0413     if(value == "I")  return CSDButton::CSDButtonType::Minimize;
0414     if(value == "A")  return  CSDButton::CSDButtonType::Maximize;
0415     
0416     return CSDButtonType::None;
0417 }
0418 
0419 bool CSDButton::isHovered() const
0420 {
0421     return m_isHovered;
0422 }
0423 
0424 void CSDButton::setIsHovered(bool newIsHovered)
0425 {
0426     if (m_isHovered == newIsHovered)
0427         return;
0428     m_isHovered = newIsHovered;
0429     if(m_isHovered)
0430     {
0431         this->setState (CSDButtonState::Hover);
0432     }else
0433     {
0434         this->setState (m_isFocused ? CSDButtonState::Normal : CSDButtonState::Backdrop);
0435     }
0436     Q_EMIT isHoveredChanged();
0437 }
0438 
0439 bool CSDButton::isMaximized() const
0440 {
0441     return m_isMaximized;
0442 }
0443 
0444 void CSDButton::setIsMaximized(bool newIsMaximized)
0445 {
0446     if (m_isMaximized == newIsMaximized)
0447         return;
0448     m_isMaximized = newIsMaximized;
0449     if(m_type == CSDButtonType::Maximize && m_isMaximized)
0450     {
0451         this->setType (CSDButtonType::Restore);
0452     }else if(m_type == CSDButtonType::Restore && !m_isMaximized)
0453     {
0454         this->setType (CSDButtonType::Maximize);
0455     }
0456     Q_EMIT isMaximizedChanged();
0457 }
0458 
0459 bool CSDButton::isPressed() const
0460 {
0461     return m_isPressed;
0462 }
0463 
0464 void CSDButton::setIsPressed(bool newIsPressed)
0465 {
0466     if (m_isPressed == newIsPressed)
0467         return;
0468     m_isPressed = newIsPressed;
0469     if(m_isPressed)
0470     {
0471         this->setState (CSDButtonState::Pressed);
0472     }else
0473     {
0474         this->setState (CSDButtonState::Normal);
0475     }
0476     Q_EMIT isPressedChanged();
0477 }
0478 
0479 bool CSDButton::isFocused() const
0480 {
0481     return m_isFocused;
0482 }
0483 
0484 void CSDButton::setIsFocused(bool newIsFocused)
0485 {
0486     if (m_isFocused == newIsFocused)
0487         return;
0488     m_isFocused = newIsFocused;
0489     
0490     if(m_isFocused)
0491     {
0492         this->setState (CSDButtonState::Normal);
0493     }
0494     else
0495     {
0496         this->setState (CSDButtonState::Backdrop);
0497     }
0498     Q_EMIT isFocusedChanged();
0499 }