commit 8ca1ee831a70f1539b13ac865a541b610b1f24b8 Author: Chris Date: Wed Nov 11 17:18:15 2026 +0000 Initial repo creation. diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f2caaa0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "interface"] + path = interface + url = https://git.teslos.co.uk/Triplehx3/TTK-Interface.git diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3a63ecb --- /dev/null +++ b/LICENSE @@ -0,0 +1,17 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2026 Teslos + +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, either version 3 of the License, or +(at your option) any later version. + +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 receive a copy of the GNU General Public License +along with this program. If not, see . \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4eb6258 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Teslos Tool Kit + +A simple Qt6 / C++ systray tool that acts as a toolkit for applications loaded via plugins. + +**Please note that you must have at least one TTK-Plugin installed for this toolkit to be of any use!** + +## Build + +```bash +git clone https://git.teslos.co.uk/Triplehx3/TTK-App.git +cd TTK-App +git submodule update --init --recursive +mkdir build +cd build +qmake6 .. +make +``` + +Or simply open the TTK.pro file in the cloned directory inside Qt Creator. + +Run the resulting executable from the build directory. + +## License +This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details. \ No newline at end of file diff --git a/TTK-App.pro b/TTK-App.pro new file mode 100644 index 0000000..12583cd --- /dev/null +++ b/TTK-App.pro @@ -0,0 +1,23 @@ +TARGET = TTK + +CONFIG(release, debug|release) { + +} else { + DEFINES += DEVMODE +} + +QMAKE_CXXFLAGS += -Wno-sfinae-incomplete + +QT += widgets + +HEADERS = interface/interface.h \ + plugindialog.h \ + window.h + +SOURCES = main.cpp \ + plugindialog.cpp \ + window.cpp + +RESOURCES = ttk.qrc + +LIBS = -L../plugins diff --git a/TTK.desktop b/TTK.desktop new file mode 100644 index 0000000..60fe284 --- /dev/null +++ b/TTK.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Version=1.0 +Name=TTK +Comment=A systray based toolkkit of mini but usefull applets. +Exec=/usr/bin/TTK +Icon=/usr/share/icons/hicolor/16x16/apps/TTK.png +Terminal=false +Type=Application +Categories=Utility;Development; \ No newline at end of file diff --git a/images/smile.png b/images/smile.png new file mode 100644 index 0000000..0852619 Binary files /dev/null and b/images/smile.png differ diff --git a/interface b/interface new file mode 160000 index 0000000..a60b998 --- /dev/null +++ b/interface @@ -0,0 +1 @@ +Subproject commit a60b998305bbff133ec53c3fd2e62a802b4481bf diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3fd1d37 --- /dev/null +++ b/main.cpp @@ -0,0 +1,14 @@ +#include "window.h" + +int main(int argc, char *argv[]) +{ + + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + + QApplication app(argc, argv); + QApplication::setQuitOnLastWindowClosed(false); + + Window window; + return app.exec(); + +} diff --git a/plugindialog.cpp b/plugindialog.cpp new file mode 100644 index 0000000..330b718 --- /dev/null +++ b/plugindialog.cpp @@ -0,0 +1,88 @@ +#include "plugindialog.h" +#include "interface/interface.h" + +PluginDialog::PluginDialog(const QString &path, const QStringList &fileNames, + QWidget *parent) : + QDialog(parent), + label(new QLabel), + treeWidget(new QTreeWidget), + okButton(new QPushButton(tr("OK"))) +{ + treeWidget->setAlternatingRowColors(false); + treeWidget->setSelectionMode(QAbstractItemView::NoSelection); + treeWidget->setColumnCount(1); + treeWidget->header()->hide(); + + okButton->setDefault(true); + + connect(okButton, &QAbstractButton::clicked, this, &QWidget::close); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->setColumnStretch(0, 1); + mainLayout->setColumnStretch(2, 1); + mainLayout->addWidget(label, 0, 0, 1, 3); + mainLayout->addWidget(treeWidget, 1, 0, 1, 3); + mainLayout->addWidget(okButton, 2, 1); + setLayout(mainLayout); + + interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon), + QIcon::Normal, QIcon::On); + interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon), + QIcon::Normal, QIcon::Off); + featureIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon)); + + setWindowTitle(tr("Plugin Information")); + findPlugins(path, fileNames); +} + +void PluginDialog::findPlugins(const QString &path, + const QStringList &fileNames) +{ + label->setText(tr("TTK found the following plugins\n" + "(looked in %1):") + .arg(QDir::toNativeSeparators(path))); + + const QDir dir(path); + + const auto staticInstances = QPluginLoader::staticInstances(); + for (QObject *plugin : staticInstances) + populateTreeWidget(plugin, tr("%1 (Static Plugin)") + .arg(plugin->metaObject()->className())); + + for (const QString &fileName : fileNames) { + QPluginLoader loader(dir.absoluteFilePath(fileName)); + QObject *plugin = loader.instance(); + if (plugin) + populateTreeWidget(plugin, fileName); + } +} + +void PluginDialog::populateTreeWidget(QObject *plugin, const QString &text) +{ + auto pluginItem = new QTreeWidgetItem(treeWidget); + pluginItem->setText(0, text); + pluginItem->setExpanded(true); + + QFont boldFont = pluginItem->font(0); + boldFont.setBold(true); + pluginItem->setFont(0, boldFont); + + if (plugin) { + auto iBrush = qobject_cast(plugin); + if (iBrush) + addItems(pluginItem, "About", iBrush->pdesc()); + } +} + +void PluginDialog::addItems(QTreeWidgetItem *pluginItem, + const char *interfaceName, + const QString &feature) +{ + auto interfaceItem = new QTreeWidgetItem(pluginItem); + interfaceItem->setText(0, interfaceName); + interfaceItem->setIcon(0, interfaceIcon); + + auto featureItem = new QTreeWidgetItem(interfaceItem); + featureItem->setText(0, feature); + featureItem->setIcon(0, featureIcon); +} diff --git a/plugindialog.h b/plugindialog.h new file mode 100644 index 0000000..ae9bae5 --- /dev/null +++ b/plugindialog.h @@ -0,0 +1,34 @@ +#ifndef PLUGINDIALOG_H +#define PLUGINDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class PluginDialog : public QDialog +{ + Q_OBJECT + +public: + PluginDialog(const QString &path, const QStringList &fileNames, + QWidget *parent = nullptr); + +private: + void findPlugins(const QString &path, const QStringList &fileNames); + void populateTreeWidget(QObject *plugin, const QString &text); + void addItems(QTreeWidgetItem *pluginItem, const char *interfaceName, + const QString &feature); + + QLabel *label; + QTreeWidget *treeWidget; + QPushButton *okButton; + QIcon interfaceIcon; + QIcon featureIcon; +}; + +#endif diff --git a/settingsdialog.cpp b/settingsdialog.cpp new file mode 100644 index 0000000..969ab5f --- /dev/null +++ b/settingsdialog.cpp @@ -0,0 +1,35 @@ +#include "settingsdialog.h" + +settingsdialog::settingsdialog(QWidget *parent) : + QDialog(parent)//, + // label(new QLabel), + // treeWidget(new QTreeWidget), + // okButton(new QPushButton(tr("OK")) + // ) +{ + // treeWidget->setAlternatingRowColors(false); + // treeWidget->setSelectionMode(QAbstractItemView::NoSelection); + // treeWidget->setColumnCount(1); + // treeWidget->header()->hide(); + + // okButton->setDefault(true); + + // connect(okButton, &QAbstractButton::clicked, this, &QWidget::close); + + // QGridLayout *mainLayout = new QGridLayout; + // mainLayout->setColumnStretch(0, 1); + // mainLayout->setColumnStretch(2, 1); + // mainLayout->addWidget(label, 0, 0, 1, 3); + // mainLayout->addWidget(treeWidget, 1, 0, 1, 3); + // mainLayout->addWidget(okButton, 2, 1); + // setLayout(mainLayout); + +// interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon), + // QIcon::Normal, QIcon::On); + // interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon), + // QIcon::Normal, QIcon::Off); + // featureIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon)); + + // setWindowTitle(tr("Plugin Information")); + // findPlugins(path, fileNames); +} diff --git a/settingsdialog.h b/settingsdialog.h new file mode 100644 index 0000000..0a2253c --- /dev/null +++ b/settingsdialog.h @@ -0,0 +1,16 @@ +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include + +class settingsdialog : public QDialog +{ + Q_OBJECT +public: + explicit settingsdialog(QWidget *parent = nullptr); + +signals: + +}; + +#endif // SETTINGSDIALOG_H diff --git a/ttk.qrc b/ttk.qrc new file mode 100644 index 0000000..d005968 --- /dev/null +++ b/ttk.qrc @@ -0,0 +1,5 @@ + + + images/smile.png + + diff --git a/window.cpp b/window.cpp new file mode 100644 index 0000000..b9ad2ee --- /dev/null +++ b/window.cpp @@ -0,0 +1,124 @@ +#include "window.h" +#include "interface/interface.h" +#include "plugindialog.h" + +Window::Window() +{ + createActions(); + createTrayIcon(); + loadPlugins(); + setIcon(); + trayIcon->show(); +} + +void Window::loadPlugins() +{ + QString pluginPath = ""; + +#ifdef DEVMODE + pluginPath = QCoreApplication::applicationDirPath() + "/plugins"; +#else + pluginPath = "/usr/lib/ttk/plugins"; +#endif + + pluginsDir = QDir(pluginPath); + + const auto entryList = pluginsDir.entryList(QDir::Files); + for (const QString &fileName : entryList) { + + QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); + QObject *plugin = loader.instance(); + if (plugin) { + + populateMenu(plugin); + pluginFileNames += fileName; + } + } + addtrayactions(); +} + +void Window::changePlugin() +{ + auto action = qobject_cast(sender()); + if (!action) + return; + + auto iPlugin = qobject_cast(action->parent()); + if (!iPlugin) + return; + + QWidget* output = new QWidget(); + output = iPlugin->pcontent(); + output->show(); +} + +void Window::populateMenu(QObject *plugin) +{ + + auto iPlugin = qobject_cast(plugin); + if (iPlugin) + + addToMenu(plugin, iPlugin->pname(), trayIconMenu, &Window::changePlugin); +} + +void Window::addToMenu(QObject *plugin, const QString &text, + QMenu *menu, Member member) +{ + auto action = new QAction(text, plugin); + connect(action, &QAction::triggered, this, member); + menu->addAction(action); +} + +void Window::setIcon() +{ + QIcon icon = QIcon(":/images/smile.png"); + setWindowIcon(icon); + trayIcon->setIcon(icon); + trayIcon->setToolTip("TTK"); +} + + +void Window::createActions() +{ + quitAction = new QAction(tr("&Quit"), this); + connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit); + + aboutAct = new QAction(tr("&About"), this); + connect(aboutAct, &QAction::triggered, this, &Window::about); + + aboutQtAct = new QAction(tr("About &Qt"), this); + connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt); + + aboutPluginsAct = new QAction(tr("About &Plugins"), this); + connect(aboutPluginsAct, &QAction::triggered, this, &Window::aboutPlugins); +} + +void Window::about() +{ + QMessageBox::about(this, tr("About TTK"), + tr("This is a wierd handy thing.")); +} + + +void Window::aboutPlugins() +{ + PluginDialog dialog(pluginsDir.path(), pluginFileNames, this); + dialog.exec(); +} + +void Window::addtrayactions() +{ + trayIconMenu->addSeparator(); + trayIconMenu->addAction(aboutAct); + trayIconMenu->addAction(aboutQtAct); + trayIconMenu->addAction(aboutPluginsAct); + trayIconMenu->addSeparator(); + trayIconMenu->addAction(quitAction); +} + +void Window::createTrayIcon() +{ + trayIconMenu = new QMenu(this); + trayIcon = new QSystemTrayIcon(this); + trayIcon->setContextMenu(trayIconMenu); +} diff --git a/window.h b/window.h new file mode 100644 index 0000000..5fc05eb --- /dev/null +++ b/window.h @@ -0,0 +1,46 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include +#include +#include +#include +#include +#include +#include + + +class Window : public QDialog +{ + Q_OBJECT + +public: + Window(); + +private: + void setIcon(); + void changePlugin(); + void about(); + void aboutPlugins(); + typedef void (Window::*Member)(); + void createActions(); + void createTrayIcon(); + void loadPlugins(); + void addtrayactions(); + void populateMenu(QObject *plugin); + void addToMenu(QObject *plugin, const QString &text, QMenu *menu,Member member); + + QAction *quitAction; + QStringList pluginFileNames; + QDir pluginsDir; + QMenu *fileMenu; + QMenu *helpMenu; + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; + QAction *exitAct; + QAction *aboutAct; + QAction *aboutQtAct; + QAction *aboutPluginsAct; +}; + +#endif