diff --git a/AUTHORS b/AUTHORS index 5768f9c..7b5e9dc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,11 +1,3 @@ -## Special Thanks to - -- Stefonarch (Standreas) and the translation team at . -- LXQt Developers, for all their software, here using their [build tools]. - ## Translators -- **Italian**: Andrea Zanellato (author) - - -[build tools]: https://github.com/lxqt/lxqt-build-tools/ +- **Italian**: Andrea Zanellato diff --git a/CMakeLists.txt b/CMakeLists.txt index c0464e7..af6feb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.15) -project(Sqeleton - VERSION 0.1.1 +project(GummyConf + VERSION 0.1.0 LANGUAGES CXX ) set(CMAKE_CXX_STANDARD 17) @@ -10,6 +10,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) # Qt #=============================================================================== option(PROJECT_TRANSLATIONS_UPDATE "Update source translations [default: OFF]" OFF) +set(PROJECT_TRANSLATION_TEST_ENABLED 0 CACHE STRING "Whether to enable translation testing [default: 0]") +set(PROJECT_TRANSLATION_TEST "it" CACHE STRING "Country code of language to test in IDE [default: it]") set(PROJECT_QT_VERSION 5 CACHE STRING "Qt version to use [default: 5]") set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) @@ -23,22 +25,21 @@ find_package(Qtilitools REQUIRED) set(PROJECT_SOURCES src/application.hpp src/application.cpp - src/litebutton.hpp - src/litebutton.cpp src/dialogabout.hpp src/dialogabout.cpp - src/dialogprefs.hpp - src/dialogprefs.cpp - src/mainwindow.hpp - src/mainwindow.cpp + src/gummyd.hpp + src/gummyd.cpp + src/maindialog.hpp + src/maindialog.cpp src/qtilities.hpp src/settings.hpp src/settings.cpp + src/systemtrayicon.hpp + src/systemtrayicon.cpp ) set(PROJECT_UI_FILES src/dialogabout.ui - src/dialogprefs.ui - src/mainwindow.ui + src/maindialog.ui ) set(PROJECT_OTHER_FILES .github/workflows/build.yml @@ -48,8 +49,8 @@ set(PROJECT_OTHER_FILES .gitignore README.md ) -source_group("Other Files" FILES ${PROJECT_OTHER_FILES}) -source_group("UI Files" FILES ${PROJECT_UI_FILES}) +source_group("" FILES ${PROJECT_SOURCES}) +source_group("Misc" FILES ${PROJECT_OTHER_FILES}) #=============================================================================== # Resources #=============================================================================== @@ -90,6 +91,8 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE PROJECT_DATA_DIR="${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT_ID}" PROJECT_ICON_NAME="${PROJECT_ICON_FILE_NAME}" PROJECT_ICON_SYSTEM_PATH="${PROJECT_ICON_FILE_PATH}/${PROJECT_ICON_FILE_NAME}" + PROJECT_TRANSLATION_TEST="${PROJECT_TRANSLATION_TEST}" + PROJECT_TRANSLATION_TEST_ENABLED=${PROJECT_TRANSLATION_TEST_ENABLED} ) #=============================================================================== # Install application diff --git a/COPYING b/COPYING index 9d00415..97f9938 100644 --- a/COPYING +++ b/COPYING @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2023 Andrea Zanellato +Copyright (c) 2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Config.cmake b/Config.cmake index c4a620e..547340a 100644 --- a/Config.cmake +++ b/Config.cmake @@ -4,29 +4,27 @@ # Essential, non translatable application information (except DESCRIPTION). # Translatable strings are passed via code. #=============================================================================== -string(TOLOWER ${PROJECT_NAME} PROJECT_ID) # Might not be compatible with AppStream list(APPEND PROJECT_CATEGORIES "Qt;Utility") # Freedesktop menu categories -list(APPEND PROJECT_KEYWORDS "application;project;template") +list(APPEND PROJECT_KEYWORDS "configuration;gummy") +set(PROJECT_ID "gummy-conf") set(PROJECT_AUTHOR_NAME "Andrea Zanellato") set(PROJECT_AUTHOR_EMAIL "redtid3@gmail.com") # Used also for organization email -set(PROJECT_COPYRIGHT_YEAR "2021-2023") # TODO: from git -set(PROJECT_DESCRIPTION "Qt application template") +set(PROJECT_COPYRIGHT_YEAR "2024") # TODO: from git +set(PROJECT_DESCRIPTION "Gummy screen brightness manager configurator") set(PROJECT_ORGANIZATION_NAME "qtilities") # Might be equal to PROJECT_AUTHOR_NAME set(PROJECT_ORGANIZATION_URL "${PROJECT_ORGANIZATION_NAME}.github.io") -set(PROJECT_HOMEPAGE_URL "https://${PROJECT_ORGANIZATION_URL}/${PROJECT_ID}") set(PROJECT_REPOSITORY_URL "https://github.com/${PROJECT_ORGANIZATION_NAME}/${PROJECT_ID}") +set(PROJECT_HOMEPAGE_URL "${PROJECT_REPOSITORY_URL}") set(PROJECT_REPOSITORY_BRANCH "master") set(PROJECT_SPDX_ID "MIT") set(PROJECT_TRANSLATIONS_DIR "resources/translations") #=============================================================================== # Appstream #=============================================================================== -set(PROJECT_APPSTREAM_SPDX_ID "CC0-1.0") +set(PROJECT_APPSTREAM_SPDX_ID "CC0-1.0") include(AppStream) -to_appstream_id("io.github.${PROJECT_ORGANIZATION_NAME}.${PROJECT_NAME}" - PROJECT_APPSTREAM_ID) - -set(PROJECT_ICON_FORMAT "svg") +to_appstream_id("io.github.${PROJECT_ORGANIZATION_NAME}.${PROJECT_NAME}" PROJECT_APPSTREAM_ID) +set(PROJECT_ICON_FORMAT "svgz") if(UNIX AND NOT APPLE) set(PROJECT_ICON_FILE_NAME "${PROJECT_APPSTREAM_ID}.${PROJECT_ICON_FORMAT}") elseif(APPLE) diff --git a/README.md b/README.md index 68d9cb1..4fcb1b4 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,16 @@ -# Sqeleton +# GummyConf -[![CI]](https://github.com/qtilities/sqeleton/actions/workflows/build.yml) +[![CI]](https://github.com/qtilities/gummy-conf/actions/workflows/build.yml) -## Overview - -Qt application template repository. - -See the related [website page] for further information. - -## Features - -- Resource management following the Freedesktop [Desktop Entry] Specification - file naming convention (icons, desktop and appdata files) -- [Appstream] metadata information -- Locale translations via [LXQt build tools] -- Install step +Configuration tool for [gummy], screen brightness/temperature manager for Linux. ## Dependencies -Runtime: +### Runtime - Qt5/6 base -Build: +### Build - CMake - Qt Linguist Tools @@ -30,8 +18,8 @@ Build: ## Build -`CMAKE_BUILD_TYPE` is usually set to `Release`, though `None` might be a [valid alternative][2].
-`CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems.
+`CMAKE_BUILD_TYPE` is usually set to `Release`, though `None` might be a valid [alternative].
+`CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems.
Using `sudo make install` is discouraged, instead use the system package manager where possible. ```bash @@ -40,17 +28,20 @@ cmake --build build --verbose DESTDIR="$(pwd)/package" cmake --install build ``` +## Packages + +[![Packaging status]](https://repology.org/project/gummy-conf/versions) + ## Licenses -- Sqeleton is licensed under the [MIT] license. +- GummyConf is licensed under the [MIT] license. - Application icon is from [Openclipart], [CC0-1.0] license. -[Appstream]: https://freedesktop.org/software/appstream/docs/chap-Quickstart.html +[alternative]: https://wiki.archlinux.org/title/CMake_package_guidelines#Fixing_the_automatic_optimization_flag_override [CC0-1.0]: https://creativecommons.org/publicdomain/zero/1.0/ -[CI]: https://github.com/qtilities/sqeleton/actions/workflows/build.yml/badge.svg -[Desktop Entry]: https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html -[LXQt build tools]: https://github.com/lxqt/lxqt-build-tools +[CI]: https://github.com/qtilities/gummy-conf/actions/workflows/build.yml/badge.svg +[gummy]: https://github.com/f-fusco/gummy/ [MIT]: COPYING [Openclipart]: https://openclipart.org/ -[website page]: https://qtilities.github.io/sqeleton/ +[Packaging status]: https://repology.org/badge/vertical-allrepos/gummy-conf.svg diff --git a/resources/icons/application.icon b/resources/icons/application.icon index 28d32fa..dcf2a3e 100644 Binary files a/resources/icons/application.icon and b/resources/icons/application.icon differ diff --git a/resources/icons/sun.svgz b/resources/icons/sun.svgz new file mode 100644 index 0000000..4108f1b Binary files /dev/null and b/resources/icons/sun.svgz differ diff --git a/resources/resources.qrc b/resources/resources.qrc index ab4adae..5aa1d3c 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,10 +1,8 @@ - icons/application-exit.svgz - icons/help-about.svgz - icons/preferences-system.svgz about.md authors copying + icons/sun.svgz diff --git a/resources/translations/gummy-conf.desktop.yaml b/resources/translations/gummy-conf.desktop.yaml new file mode 100644 index 0000000..bc0b8de --- /dev/null +++ b/resources/translations/gummy-conf.desktop.yaml @@ -0,0 +1,3 @@ +Desktop Entry/Name: "Gummy Configuration" +Desktop Entry/GenericName: "Screen Brightness Configuration" +Desktop Entry/Comment: "Configuration editor for gummy" diff --git a/resources/translations/gummy-conf.ts b/resources/translations/gummy-conf.ts new file mode 100644 index 0000000..8234c6e --- /dev/null +++ b/resources/translations/gummy-conf.ts @@ -0,0 +1,107 @@ + + + + + Qtilities::Application + + + Auto&start + + + + + Qtilities::DialogAbout + + + Information + + + + + qrc:/about.html + + + + + Translators + + + + + qrc:/thanks.html + + + + + License + + + + + qrc:/license.html + + + + + Author + + + + + About + + + + + Qtilities::MainDialog + + + Brightness + + + + + Time range + + + + + Backlight + + + + + Temperature + + + + + About + + + + + + Start + + + + + Stop + + + + + Qtilities::SystemTrayIcon + + + &About + + + + + &Quit + + + + diff --git a/resources/translations/gummy-conf_it.desktop.yaml b/resources/translations/gummy-conf_it.desktop.yaml new file mode 100644 index 0000000..8eb6b38 --- /dev/null +++ b/resources/translations/gummy-conf_it.desktop.yaml @@ -0,0 +1,3 @@ +Desktop Entry/Name: "Impostazioni di gummy" +Desktop Entry/GenericName: "Impostazioni Luminosità Schermo" +Desktop Entry/Comment: "Editor di configurazione di gummy" diff --git a/resources/translations/gummy-conf_it.ts b/resources/translations/gummy-conf_it.ts new file mode 100644 index 0000000..bacf67a --- /dev/null +++ b/resources/translations/gummy-conf_it.ts @@ -0,0 +1,107 @@ + + + + + Qtilities::Application + + + Auto&start + &Avvio automatico + + + + Qtilities::DialogAbout + + + Information + Informazioni + + + + qrc:/about.html + + + + + Translators + Traduttori + + + + qrc:/thanks.html + + + + + License + Licenza + + + + qrc:/license.html + + + + + Author + Autore + + + + About + Informazioni + + + + Qtilities::MainDialog + + + Brightness + Luminosità + + + + Time range + Orari + + + + Backlight + Retroilluminazione + + + + Temperature + Temperatura + + + + About + Informazioni + + + + + Start + Avvia + + + + Stop + Ferma + + + + Qtilities::SystemTrayIcon + + + &About + &Informazioni + + + + &Quit + &Esci + + + diff --git a/src/application.cpp b/src/application.cpp index b71c703..4ad162a 100755 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -22,53 +22,58 @@ IN THE SOFTWARE. */ #include "application.hpp" -#include "mainwindow.hpp" +#include "maindialog.hpp" #include "dialogabout.hpp" -#include "dialogprefs.hpp" +#include "qtilities.hpp" +#include "systemtrayicon.hpp" -#include #include #include -#include +#include +#include Qtilities::Application::Application(int argc, char *argv[]) : QApplication(argc, argv) + , trayIcon_(new SystemTrayIcon(this)) + , gummyd_(new GummyD) { + // UseHighDpiPixmaps is default from Qt6 +#if QT_VERSION < 0x060000 + setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif setApplicationName(PROJECT_ID); setApplicationDisplayName(APPLICATION_NAME); setOrganizationName(ORGANIZATION_NAME); setOrganizationDomain(ORGANIZATION_DOMAIN); + setQuitOnLastWindowClosed(false); + initLocale(); initUi(); } void Qtilities::Application::initLocale() { -#if 1 - QLocale locale = QLocale::system(); -#else - QLocale locale(QLocale("it")); +#if PROJECT_TRANSLATION_TEST_ENABLED + QLocale locale(QLocale(PROJECT_TRANSLATION_TEST)); QLocale::setDefault(locale); +#else + QLocale locale = QLocale::system(); #endif - // Qt translations (buttons and the like) - QString translationsPath + // install the translations built-into Qt itself + if (qtTranslator_.load(QStringLiteral("qt_") + locale.name(), #if QT_VERSION < 0x060000 - = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + QLibraryInfo::location(QLibraryInfo::TranslationsPath))) #else - = QLibraryInfo::path(QLibraryInfo::TranslationsPath); + QLibraryInfo::path(QLibraryInfo::TranslationsPath))) #endif - QString translationsFileName = QStringLiteral("qt_") + locale.name(); - - if (qtTranslator_.load(translationsFileName, translationsPath)) installTranslator(&qtTranslator_); // E.g. "_en" - translationsFileName = QCoreApplication::applicationName().toLower() + '_' + locale.name(); - + QString translationsFileName = QCoreApplication::applicationName().toLower() + '_' + locale.name(); // Try first in the same binary directory, in case we are building, // otherwise read from system data - translationsPath = QCoreApplication::applicationDirPath(); + QString translationsPath = QCoreApplication::applicationDirPath(); bool isLoaded = translator_.load(translationsFileName, translationsPath); if (!isLoaded) { @@ -82,10 +87,6 @@ void Qtilities::Application::initLocale() void Qtilities::Application::initUi() { - // UseHighDpiPixmaps is default from Qt6 -#if QT_VERSION < 0x060000 - setAttribute(Qt::AA_UseHighDpiPixmaps, true); -#endif settings_.load(); QString icoLocalPath @@ -97,32 +98,104 @@ void Qtilities::Application::initUi() if (appIcon_.isNull()) appIcon_ = QIcon(icoSysPath); - mainWindow_ = new Qtilities::MainWindow; - mainWindow_->move(settings_.position()); - mainWindow_->resize(settings_.size()); - mainWindow_->setWindowIcon(appIcon_); - mainWindow_->setWindowTitle(applicationDisplayName()); - mainWindow_->show(); + mainDialog_ = new Qtilities::MainDialog; + mainDialog_->move(settings_.position()); + mainDialog_->resize(settings_.size()); + mainDialog_->setWindowTitle(applicationDisplayName()); + mainDialog_->show(); + mainDialog_->hide(); + + if(settings_.autostart() && !gummyd_->is_running()) + gummyd_->start(); + + if(gummyd_->is_running()) { + QIcon icon = QIcon(QSL(":/sun")); + trayIcon_->setIcon(icon); + mainDialog_->setWindowIcon(icon); + mainDialog_->setIsRunning(true); + } else { + trayIcon_->setIcon(appIcon_); + mainDialog_->setWindowIcon(appIcon_); + mainDialog_->setIsRunning(false); + } + trayIcon_->setWidget(mainDialog_); + + QMenu* trayMenu = trayIcon_->menu(); + QAction* before = trayMenu->actions().at(0); + actAutoStart_ = new QAction(tr("Auto&start"), trayMenu); + actAutoStart_->setCheckable(true); + actAutoStart_->setChecked(settings_.autostart()); + trayMenu->insertAction(before, actAutoStart_); - connect(this, &QApplication::aboutToQuit, mainWindow_, &QObject::deleteLater); + connect(this, &QApplication::aboutToQuit, mainDialog_, &QObject::deleteLater); connect(this, &QApplication::aboutToQuit, this, [this]() { - mainWindow_->saveSettings(); + mainDialog_->saveSettings(); + settings_.setAutostart(actAutoStart_->isChecked()); + settings_.autostart() ? createAutostartFile() : deleteAutostartFile(); settings_.save(); }); + connect(this, &Application::backlightChanged, this, &Application::onBacklightChanged); + connect(this, &Application::brightnessChanged, this, &Application::onBrightnessChanged); + connect(this, &Application::temperatureChanged, this, &Application::onTemperatureChanged); + connect(this, &Application::timeStartChanged, this, &Application::onTimeStartChanged); + connect(this, &Application::timeEndChanged, this, &Application::onTimeEndChanged); +} + +void Qtilities::Application::onBacklightChanged(int backlight) +{ + QStringList args; + args << "-b" << QString::number(backlight); + gummyd_->send_command(args); +} + +void Qtilities::Application::onBrightnessChanged(int brightness) +{ + QStringList args; + args << "-p" << QString::number(brightness); + gummyd_->send_command(args); +} + +void Qtilities::Application::onTemperatureChanged(int temperature) +{ + QStringList args; + args << "-t" << QString::number(temperature); + gummyd_->send_command(args); +} + +void Qtilities::Application::onTimeStartChanged(QTime) +{ + // TODO: onTimeStartChanged +} + +void Qtilities::Application::onTimeEndChanged(QTime) +{ + // TODO: onTimeEndChanged +} + +void Qtilities::Application::onStartStop() +{ + if(gummyd_->is_running()) { + gummyd_->stop(); + trayIcon_->setIcon(appIcon_); + mainDialog_->setWindowIcon(appIcon_); + mainDialog_->setIsRunning(false); + } else { + gummyd_->start(); + trayIcon_->setIcon(QIcon(QSL(":/sun"))); + mainDialog_->setWindowIcon(QIcon(QSL(":/sun"))); + mainDialog_->setIsRunning(true); + } } void Qtilities::Application::about() { - DialogAbout about(mainWindow_); + DialogAbout about(mainDialog_); about.exec(); } -void Qtilities::Application::preferences() +QMenu* Qtilities::Application::menu() const { - DialogPrefs prefs(mainWindow_); - connect(&prefs, &DialogPrefs::accepted, mainWindow_, &MainWindow::loadSettings); - prefs.loadSettings(); - prefs.exec(); + return trayIcon_->menu(); } int main(int argc, char *argv[]) diff --git a/src/application.hpp b/src/application.hpp index 59b016c..a92cd48 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -24,36 +24,61 @@ #pragma once #include "settings.hpp" +#include "gummyd.hpp" #include #include #include +#include +QT_BEGIN_NAMESPACE class QMenu; +class QAction; +QT_END_NAMESPACE namespace Qtilities { -class MainWindow; +class MainDialog; +class SystemTrayIcon; class Application : public QApplication { Q_OBJECT + friend class MainDialog; public: Application(int argc, char *argv[]); + void about(); - void preferences(); - QIcon icon() const { return appIcon_; } - Settings &settings() { return settings_; } + + QIcon icon() const { return appIcon_; } + Settings& settings() { return settings_; } + QMenu* menu() const; + +Q_SIGNALS: + void backlightChanged(int); + void brightnessChanged(int); + void temperatureChanged(int); + void timeStartChanged(QTime); + void timeEndChanged(QTime); private: void initLocale(); void initUi(); - MainWindow *mainWindow_; - Settings settings_; + void onAboutToQuit(); + void onBacklightChanged(int); + void onBrightnessChanged(int); + void onStartStop(); + void onTemperatureChanged(int); + void onTimeStartChanged(QTime); + void onTimeEndChanged(QTime); - QIcon appIcon_; - QTranslator qtTranslator_; - QTranslator translator_; + MainDialog* mainDialog_; + QAction* actAutoStart_; + QIcon appIcon_; + QTranslator qtTranslator_, translator_; + Settings settings_; + SystemTrayIcon* trayIcon_; + std::unique_ptr gummyd_; }; } // namespace Qtilities diff --git a/src/dialogabout.cpp b/src/dialogabout.cpp index 742d001..fc96a97 100644 --- a/src/dialogabout.cpp +++ b/src/dialogabout.cpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -31,12 +31,12 @@ Qtilities::DialogAbout::DialogAbout(QWidget *parent) : QDialog(parent) - , ui(new Qtilities::Ui::DialogAbout) + , ui_(new Qtilities::Ui::DialogAbout) { - ui->setupUi(this); - ui->tabInfo->setLayout(ui->layTabInfo); - ui->tabAuthors->setLayout(ui->layTabAuthors); - ui->tabLicense->setLayout(ui->layTabLicense); + ui_->setupUi(this); + ui_->tabInfo->setLayout(ui_->layTabInfo); + ui_->tabTranslators->setLayout(ui_->layTabTranslators); + ui_->tabLicense->setLayout(ui_->layTabLicense); QStringList list = {":/info", ":/authors", ":/license"}; QStringList texts; @@ -52,14 +52,14 @@ Qtilities::DialogAbout::DialogAbout(QWidget *parent) f.close(); } QString toTranslate = texts.at(0); - ui->txtInfo->setMarkdown(toTranslate.replace("__AUTHOR__", tr("Author"))); - ui->txtAuthors->setMarkdown(texts.at(1)); - ui->txtLicense->setMarkdown(texts.at(2)); + ui_->txtInfo->setMarkdown(toTranslate.replace("__AUTHOR__", tr("Author"))); + ui_->txtTranslators->setMarkdown(texts.at(1)); + ui_->txtLicense->setMarkdown(texts.at(2)); setWindowIcon(QIcon::fromTheme("help-about", QIcon(":/help-about"))); setWindowTitle(tr("About")); - connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &Qtilities::DialogAbout::close); + connect(ui_->buttonBox, &QDialogButtonBox::clicked, this, &Qtilities::DialogAbout::close); } -Qtilities::DialogAbout::~DialogAbout() { delete ui; } +Qtilities::DialogAbout::~DialogAbout() { delete ui_; } diff --git a/src/dialogabout.hpp b/src/dialogabout.hpp index 5a3e1d8..6e9a4f9 100644 --- a/src/dialogabout.hpp +++ b/src/dialogabout.hpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -34,10 +34,10 @@ class DialogAbout : public QDialog Q_OBJECT public: - explicit DialogAbout(QWidget *parent = nullptr); + explicit DialogAbout(QWidget* parent = nullptr); ~DialogAbout(); private: - Ui::DialogAbout *ui; + Ui::DialogAbout* ui_; }; } // namespace Qtilities diff --git a/src/dialogabout.ui b/src/dialogabout.ui index e45484c..6e81ab6 100644 --- a/src/dialogabout.ui +++ b/src/dialogabout.ui @@ -7,12 +7,27 @@ 0 0 480 - 320 + 400 + + QLayout::SetNoConstraint + + + 6 + + + 6 + + + 6 + + + 6 + - + 0 @@ -32,7 +47,7 @@ 10 10 441 - 221 + 291 @@ -51,7 +66,7 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'DejaVu Sans'; font-size:14pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -76,9 +91,9 @@ li.checked::marker { content: "\2612"; } - + - Thanks + Translators @@ -86,12 +101,12 @@ li.checked::marker { content: "\2612"; } 10 10 441 - 221 + 291 - + - + @@ -105,7 +120,7 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'DejaVu Sans'; font-size:14pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -143,7 +158,7 @@ li.checked::marker { content: "\2612"; } 10 10 441 - 221 + 291 @@ -162,7 +177,7 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'DejaVu Sans'; font-size:14pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -210,6 +225,12 @@ li.checked::marker { content: "\2612"; } + + tbwAbout + txtInfo + txtTranslators + txtLicense + diff --git a/src/gummyd.cpp b/src/gummyd.cpp new file mode 100644 index 0000000..1f8aca8 --- /dev/null +++ b/src/gummyd.cpp @@ -0,0 +1,83 @@ +/* + MIT License + + Copyright (c) 2024 Andrea Zanellato + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +#include "gummyd.hpp" + +#include + +#include +#include + +#include +#include +#include + +GummyD::GummyD() +{ +} + +GummyD::~GummyD() +{ +} + +bool GummyD::is_running() const +{ + std::filesystem::path xdgRuntimePath(std::getenv("XDG_RUNTIME_DIR")); + if (xdgRuntimePath.empty()) { + std::cerr << "XDG_RUNTIME_DIR not set.\n"; + return false; + } + std::filesystem::path gummydLockPath = xdgRuntimePath / u8"gummyd-lock"; + int fd = open(gummydLockPath.c_str(), O_RDWR | O_CREAT, 0640); + if (fd < 0) { + std::cerr << "Failed to open the lock file.\n"; + return false; + } + flock fl; + fl.l_type = F_WRLCK; // read/write lock + fl.l_whence = SEEK_SET; // beginning of file + fl.l_start = 0; // offset from l_whence + fl.l_len = 0; // length, 0 = to EOF + fcntl(fd, F_GETLK, &fl); + + return fl.l_type != F_UNLCK; +} + +void GummyD::start() +{ + QStringList args; + args << QStringLiteral("start"); + send_command(args); +} + +void GummyD::stop() +{ + QStringList args; + args << QStringLiteral("stop"); + send_command(args); +} + +void GummyD::send_command(const QStringList& args) +{ + QProcess::startDetached(QStringLiteral("gummy"), args); +} diff --git a/src/gummyd.hpp b/src/gummyd.hpp new file mode 100644 index 0000000..45cdbd2 --- /dev/null +++ b/src/gummyd.hpp @@ -0,0 +1,36 @@ +/* + MIT License + + Copyright (c) 2024 Andrea Zanellato + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +#include +#include + +class GummyD { +public: + GummyD(); + ~GummyD(); + + bool is_running() const; + void start(); + void stop(); + void send_command(const QStringList& args); +}; diff --git a/src/maindialog.cpp b/src/maindialog.cpp new file mode 100644 index 0000000..e8eaf8c --- /dev/null +++ b/src/maindialog.cpp @@ -0,0 +1,93 @@ +/* + MIT License + + Copyright (c) 2021-2024 Andrea Zanellato + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +#include "maindialog.hpp" +#include "ui_maindialog.h" +#include "application.hpp" +#include "settings.hpp" + +#include +#include + +Qtilities::MainDialog::MainDialog(QWidget *parent) + : QDialog(parent) + , ui_(new Qtilities::Ui::MainDialog) +{ + ui_->setupUi(this); + + // TODO + ui_->timeStart->setVisible(false); + ui_->timeEnd->setVisible(false); + ui_->lblTime->setVisible(false); + + loadSettings(); + + Application* theApp = static_cast(qApp); + connect(ui_->buttonBox, &QDialogButtonBox::accepted, this, &MainDialog::accept); + connect(ui_->buttonBox, &QDialogButtonBox::rejected, this, &MainDialog::reject); + connect(ui_->pbnAbout, &QPushButton::clicked, theApp, &Application::about); + connect(ui_->pbnStartStop, &QPushButton::clicked, theApp, &Application::onStartStop); +} + +Qtilities::MainDialog::~MainDialog() { delete ui_; } + +void Qtilities::MainDialog::setIsRunning(bool isRunning) +{ + if (isRunning) { + ui_->pbnStartStop->setIcon(QIcon::fromTheme(QString::fromUtf8("media-playback-stop"))); + ui_->pbnStartStop->setText(tr("Stop")); + return; + } + ui_->pbnStartStop->setIcon(QIcon::fromTheme(QString::fromUtf8("media-playback-start"))); + ui_->pbnStartStop->setText(tr("Start")); +} + +void Qtilities::MainDialog::loadSettings() +{ + Settings &settings = static_cast(qApp)->settings(); + // move and resize are set in Application + ui_->sbxBacklight->setValue(settings.backlight()); + ui_->sbxBright->setValue(settings.brightness()); + ui_->sbxTemp->setValue(settings.temperature()); + ui_->timeStart->setTime(settings.timeStart()); + ui_->timeEnd->setTime(settings.timeEnd()); +} + +void Qtilities::MainDialog::saveSettings() +{ + Settings& settings = static_cast(qApp)->settings(); + settings.setPosition(pos()); + settings.setSize(size()); + settings.setBacklight(ui_->sbxBacklight->value()); + settings.setBrightness(ui_->sbxBright->value()); + settings.setTemperature(ui_->sbxTemp->value()); + settings.setTimeStart(ui_->timeStart->time()); + settings.setTimeEnd(ui_->timeEnd->time()); +} + +void Qtilities::MainDialog::accept() +{ + saveSettings(); + QDialog::accept(); + hide(); +} diff --git a/src/maindialog.hpp b/src/maindialog.hpp new file mode 100644 index 0000000..feb59e3 --- /dev/null +++ b/src/maindialog.hpp @@ -0,0 +1,49 @@ +/* + MIT License + + Copyright (c) 2021-2024 Andrea Zanellato + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +#pragma once + +#include + +namespace Qtilities { +namespace Ui { +class MainDialog; +} +class MainDialog : public QDialog +{ + Q_OBJECT + +public: + explicit MainDialog(QWidget *parent = nullptr); + ~MainDialog(); + + void loadSettings(); + void saveSettings(); + void setIsRunning(bool); + +private: + void accept() override; + + Ui::MainDialog* ui_; +}; +} // namespace Qtilities diff --git a/src/maindialog.ui b/src/maindialog.ui new file mode 100644 index 0000000..b830626 --- /dev/null +++ b/src/maindialog.ui @@ -0,0 +1,217 @@ + + + Qtilities::MainDialog + + + + 0 + 0 + 480 + 320 + + + + + QLayout::SetNoConstraint + + + 6 + + + 6 + + + 6 + + + 6 + + + + + QLayout::SetNoConstraint + + + + + % + + + 35 + + + 100 + + + + + + + Brightness + + + + + + + Time range + + + + + + + K + + + 3200 + + + 6500 + + + + + + + Backlight + + + + + + + % + + + 35 + + + 100 + + + + + + + Temperature + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + QLayout::SetNoConstraint + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 12 + + + + + About + + + + + + false + + + + + + + Start + + + + + + false + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + sbxBacklight + sbxBright + sbxTemp + timeStart + timeEnd + + + + diff --git a/src/qtilities.hpp b/src/qtilities.hpp index 983a407..9d797b5 100644 --- a/src/qtilities.hpp +++ b/src/qtilities.hpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to diff --git a/src/settings.cpp b/src/settings.cpp index 4646263..5734c4a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -24,14 +24,18 @@ #include "settings.hpp" #include "qtilities.hpp" -#include +#include "application.hpp" #include Qtilities::Settings::Settings() - : bgColor_(Default::backgroundColor) - , fgColor_(Default::foregroundColor) + : autostart_(Default::autostart) + , backlight_(Default::backlightMax) + , brightness_(Default::brightnessMax) , position_(Default::position) , size_(Default::size) + , temperature_(Default::temperatureMax) + , timeStart_(Default::timeStart) + , timeEnd_(Default::timeEnd) { } @@ -42,11 +46,24 @@ void Qtilities::Settings::load() QApplication::applicationDisplayName()); settings.beginGroup("General"); - bgColor_ = settings.value(QSL("BackgroundColor"), Default::backgroundColor).value(); - fgColor_ = settings.value(QSL("ForegroundColor"), Default::foregroundColor).value(); - position_ = settings.value(QSL("Position"), Default::position).toPoint(); - size_ = settings.value(QSL("Size"), Default::size).toSize(); - settings.endGroup(); + + backlight_ = std::clamp( + settings.value(QSL("Backlight"), Default::backlightMax).toInt(), + Default::backlightMin, Default::backlightMax + ); + brightness_ = std::clamp( + settings.value(QSL("Brightness"), Default::brightnessMax).toInt(), + Default::brightnessMin, Default::brightnessMax + ); + temperature_ = std::clamp( + settings.value(QSL("Temperature"), Default::temperatureMax).toInt(), + Default::temperatureMin, Default::temperatureMax + ); + autostart_ = settings.value(QSL("Autostart"), Default::autostart).toBool(); + position_ = settings.value(QSL("Position"), Default::position).toPoint(); + size_ = settings.value(QSL("Size"), Default::size).toSize(); + timeStart_ = settings.value(QSL("TimeStart"), Default::timeStart).toTime(); + timeEnd_ = settings.value(QSL("TimeStop"), Default::timeEnd).toTime(); } void Qtilities::Settings::save() @@ -56,9 +73,63 @@ void Qtilities::Settings::save() QApplication::applicationDisplayName()); settings.beginGroup("General"); - settings.setValue(QSL("BackgroundColor"), bgColor_); - settings.setValue(QSL("ForegroundColor"), fgColor_); - settings.setValue(QSL("Position"), position_); - settings.setValue(QSL("Size"), size_); + settings.setValue(QSL("Autostart"), autostart_); + settings.setValue(QSL("Backlight"), backlight_); + settings.setValue(QSL("Brightness"), brightness_); + settings.setValue(QSL("Position"), position_); + settings.setValue(QSL("Size"), size_); + settings.setValue(QSL("Temperature"), temperature_); + settings.setValue(QSL("TimeStart"), timeStart_); + settings.setValue(QSL("TimeStop"), timeEnd_); settings.endGroup(); } + +void Qtilities::Settings::setBacklight(int backlight) +{ + if (backlight_ == backlight) + return; + + Application* theApp = static_cast(qApp); + backlight_ = backlight; + Q_EMIT theApp->backlightChanged(backlight); +} + +void Qtilities::Settings::setBrightness(int brightness) +{ + if (brightness_ == brightness) + return; + + Application* theApp = static_cast(qApp); + brightness_ = brightness; + Q_EMIT theApp->brightnessChanged(brightness); +} + +void Qtilities::Settings::setTemperature(int temperature) +{ + if (temperature_ == temperature) + return; + + Application* theApp = static_cast(qApp); + temperature_ = temperature; + Q_EMIT theApp->temperatureChanged(temperature); +} + +void Qtilities::Settings::setTimeStart(QTime time) +{ + if (timeStart_ == time) + return; + + Application* theApp = static_cast(qApp); + timeStart_ = time; + Q_EMIT theApp->timeStartChanged(time); +} + +void Qtilities::Settings::setTimeEnd(QTime time) +{ + if (timeEnd_ == time) + return; + + Application* theApp = static_cast(qApp); + timeEnd_ = time; + Q_EMIT theApp->timeEndChanged(time); +} diff --git a/src/settings.hpp b/src/settings.hpp index f490e17..f250bf7 100644 --- a/src/settings.hpp +++ b/src/settings.hpp @@ -1,7 +1,7 @@ /* MIT License - Copyright (c) 2021-2023 Andrea Zanellato + Copyright (c) 2021-2024 Andrea Zanellato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -26,14 +26,22 @@ #include #include #include +#include namespace Qtilities { namespace Default { -static constexpr QColor backgroundColor = QColor(0x92, 0xd7, 0xff); -static constexpr QColor foregroundColor = QColor(0x94, 0x00, 0x80); -static constexpr QPoint position = QPoint(200, 200); -static constexpr QSize size = QSize(240, 120); +static constexpr bool autostart {false}; +static constexpr int backlightMin {35}; +static constexpr int backlightMax {100}; +static constexpr int brightnessMin {backlightMin}; +static constexpr int brightnessMax {backlightMax}; +static constexpr int temperatureMin {3200}; +static constexpr int temperatureMax {6500}; +static constexpr QPoint position {200, 200}; +static constexpr QSize size {480, 320}; +static const QTime timeStart {6, 0}; +static const QTime timeEnd {16, 0}; } // namespace Default class Settings @@ -44,22 +52,38 @@ class Settings void load(); void save(); - QColor backgroundColor() const { return bgColor_; } - void setBackgroundColor(const QColor &bgColor) { bgColor_ = bgColor; } - - QColor foregroundColor() const { return fgColor_; } - void setForegroundColor(const QColor &fgColor) { fgColor_ = fgColor; } + bool autostart() const { return autostart_; } + void setAutostart(bool autostart) { autostart_ = autostart; } QPoint position() const { return position_; } - void setPosition(const QPoint &position) { position_ = position; } + void setPosition(const QPoint& position) { position_ = position; } QSize size() const { return size_; } - void setSize(const QSize &size) { size_ = size; } + void setSize(const QSize& size) { size_ = size; } + + int backlight() const { return backlight_; } + void setBacklight(int backlight); + + int brightness() const { return brightness_; } + void setBrightness(int brightness); + + int temperature() const { return temperature_; } + void setTemperature(int temperature); + + QTime timeStart() const { return timeStart_; } + void setTimeStart(QTime time); + + QTime timeEnd() const { return timeEnd_; } + void setTimeEnd(QTime time); private: - QColor bgColor_; - QColor fgColor_; + bool autostart_; + int backlight_; + int brightness_; + int temperature_; QPoint position_; - QSize size_; + QSize size_; + QTime timeStart_; + QTime timeEnd_; }; } // namespace Qtilities diff --git a/src/systemtrayicon.cpp b/src/systemtrayicon.cpp new file mode 100644 index 0000000..0c8f381 --- /dev/null +++ b/src/systemtrayicon.cpp @@ -0,0 +1,87 @@ +/* + MIT License + + Copyright (c) 2023-2024 Andrea Zanellato + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +#include "systemtrayicon.hpp" +#include "application.hpp" + +#include +#include +#include +#include + +Qtilities::SystemTrayIcon::SystemTrayIcon(const QIcon &icon, QObject *parent) + : QObject{parent} +{ + init(); + trayIcon_->setIcon(icon); +} + +Qtilities::SystemTrayIcon::SystemTrayIcon(QObject *parent) + : QObject{parent} +{ + init(); +} + +void Qtilities::SystemTrayIcon::setIcon(const QIcon &icon) +{ +#if QT_VERSION < 0x060000 + // Qt5 Workaround to display the SVG icon, see https://bugreports.qt.io/browse/QTBUG-53550 + QIcon qt5icon = QIcon(icon.pixmap(32)); + trayIcon_->setIcon(qt5icon); +#else + trayIcon_->setIcon(icon); +#endif +} + +void Qtilities::SystemTrayIcon::setWidget(QWidget *widget) +{ + if (!widget) + return; + + connect(trayIcon_, &QSystemTrayIcon::activated, this, + [widget](QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick) + widget->setVisible(widget->isHidden()); + }); +} + +void Qtilities::SystemTrayIcon::init() +{ + trayIcon_ = new QSystemTrayIcon(this); + trayMenu_ = new QMenu; + actAbout_ = new QAction(QIcon::fromTheme("help-about", QIcon(":/help-about")), tr("&About"), this); + actQuit_ = new QAction(QIcon::fromTheme("application-exit", QIcon(":/application-exit")), + tr("&Quit"), this); + + trayMenu_->addAction(actAbout_); + trayMenu_->addAction(actQuit_); + trayIcon_->setContextMenu(trayMenu_); + trayIcon_->show(); + + Application *theApp = static_cast(qApp); + connect(actAbout_, &QAction::triggered, theApp, &Application::about); + connect(actQuit_, &QAction::triggered, qApp, &QCoreApplication::quit); + + connect(qApp, &QApplication::aboutToQuit, trayIcon_, &QObject::deleteLater); + connect(qApp, &QApplication::aboutToQuit, trayMenu_, &QObject::deleteLater); +} diff --git a/src/systemtrayicon.hpp b/src/systemtrayicon.hpp new file mode 100644 index 0000000..6c1e62f --- /dev/null +++ b/src/systemtrayicon.hpp @@ -0,0 +1,55 @@ +/* + MIT License + + Copyright (c) 2023-2024 Andrea Zanellato + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ +#pragma once + +#include + +class QAction; +class QSystemTrayIcon; +class QMenu; + +namespace Qtilities { + +class SystemTrayIcon : public QObject +{ + Q_OBJECT + +public: + explicit SystemTrayIcon(const QIcon &icon, QObject *parent); + explicit SystemTrayIcon(QObject *parent); + + QSystemTrayIcon* get() const { return trayIcon_; } + QMenu* menu() const { return trayMenu_; } + + void setIcon(const QIcon &); + void setWidget(QWidget *); + +private: + void init(); + + QSystemTrayIcon *trayIcon_; + QMenu *trayMenu_; + QAction *actAbout_, *actPrefs_, *actQuit_; +}; +} // namespace Qtilities