From 0c74719c98cf874fc6ed8641e52277a89287ef9b Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sun, 27 Oct 2024 14:40:05 +0000 Subject: [PATCH] dialog: Add first qml util --- .clang-format | 65 ++++++++++++++++++++++++++ .gitignore | 3 ++ .vscode/settings.json | 66 +++++++++++++++++++++++++++ CMakeLists.txt | 20 ++++++++ VERSION | 1 + utils/dialog/CMakeLists.txt | 36 +++++++++++++++ utils/dialog/Dialog.cpp | 13 ++++++ utils/dialog/Dialog.hpp | 26 +++++++++++ utils/dialog/main.cpp | 89 ++++++++++++++++++++++++++++++++++++ utils/dialog/main.qml | 91 +++++++++++++++++++++++++++++++++++++ 10 files changed, 410 insertions(+) create mode 100644 .clang-format create mode 100644 .vscode/settings.json create mode 100644 CMakeLists.txt create mode 100644 VERSION create mode 100644 utils/dialog/CMakeLists.txt create mode 100644 utils/dialog/Dialog.cpp create mode 100644 utils/dialog/Dialog.hpp create mode 100644 utils/dialog/main.cpp create mode 100644 utils/dialog/main.qml diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..90314ef --- /dev/null +++ b/.clang-format @@ -0,0 +1,65 @@ +--- +Language: Cpp +BasedOnStyle: LLVM + +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +ColumnLimit: 180 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentWidth: 4 +PointerAlignment: Left +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: Never + +AllowShortEnumsOnASingleLine: false + +BraceWrapping: + AfterEnum: false + +AlignConsecutiveDeclarations: AcrossEmptyLines + +NamespaceIndentation: All diff --git a/.gitignore b/.gitignore index 259148f..e724c5a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ *.exe *.out *.app + +build/ +.cache/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1abc296 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,66 @@ +{ + "files.associations": { + "cmath": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdint": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "string_view": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "format": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stdfloat": "cpp", + "streambuf": "cpp", + "cctype": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp", + "variant": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "cwctype": "cpp", + "locale": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..65f6dab --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) + +# Get version +file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW) +string(STRIP ${VER_RAW} VER) + +project(hyprland-qtutils VERSION ${VER} LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) +find_package(PkgConfig REQUIRED) + +pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) + +qt_standard_project_setup(REQUIRES 6.5) + +add_subdirectory(utils/dialog) + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6c6aa7c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file diff --git a/utils/dialog/CMakeLists.txt b/utils/dialog/CMakeLists.txt new file mode 100644 index 0000000..ad7ae44 --- /dev/null +++ b/utils/dialog/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.16) + +project(hyprland-dialog VERSION ${VER} LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) +find_package(PkgConfig REQUIRED) + +pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) + +qt_standard_project_setup(REQUIRES 6.5) + +qt_add_executable(hyprland-dialog + main.cpp + Dialog.cpp +) + +qt_add_qml_module(hyprland-dialog + URI org.hyprland.dialog + VERSION 1.0 + QML_FILES main.qml +) + +target_link_libraries(hyprland-dialog PRIVATE + Qt6::Widgets Qt6::QuickControls2 Qt6::WaylandClientPrivate PkgConfig::hyprutils +) + + +include(GNUInstallDirs) +install(TARGETS hyprland-dialog + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/utils/dialog/Dialog.cpp b/utils/dialog/Dialog.cpp new file mode 100644 index 0000000..8b161c7 --- /dev/null +++ b/utils/dialog/Dialog.cpp @@ -0,0 +1,13 @@ +#include "Dialog.hpp" +#include +#include +using namespace Hyprutils::String; + +CDialog::CDialog(QObject* parent) : QObject(parent) { + ; +} + +void CDialog::onButtonPress(QString buttonName) { + std::print("{}\n", trim(buttonName.toStdString())); + exit(0); +} diff --git a/utils/dialog/Dialog.hpp b/utils/dialog/Dialog.hpp new file mode 100644 index 0000000..21ed6d6 --- /dev/null +++ b/utils/dialog/Dialog.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class CDialog : public QObject { + Q_OBJECT; + QML_NAMED_ELEMENT(Dialog); + QML_SINGLETON; + Q_PROPERTY(QString dialogTitle MEMBER title CONSTANT); + Q_PROPERTY(QString dialogText MEMBER text CONSTANT); + Q_PROPERTY(QVector dialogButtons MEMBER buttons CONSTANT); + + public: + explicit CDialog(QObject* parent = nullptr); + + QString title, text; + QVector buttons; + + Q_INVOKABLE void onButtonPress(QString buttonName = ""); +}; diff --git a/utils/dialog/main.cpp b/utils/dialog/main.cpp new file mode 100644 index 0000000..0e2c449 --- /dev/null +++ b/utils/dialog/main.cpp @@ -0,0 +1,89 @@ +#include "Dialog.hpp" +#include +#include +#include +#include +#include +#include +#include + +using namespace Hyprutils::String; + +int main(int argc, char* argv[]) { + // disable logs to not trash the stdout + qputenv("QT_LOGGING_RULES", QByteArray("*.debug=false;qml=false")); + + QString appTitle; + auto dialog = new CDialog(); + + for (int i = 1; i < argc; ++i) { + std::string_view arg = argv[i]; + + if (arg == "--title") { + if (i + 1 >= argc) { + std::print(stderr, "--title requires a parameter\n"); + return 1; + } + + dialog->title = argv[i + 1]; + + i++; + continue; + } + + if (arg == "--apptitle") { + if (i + 1 >= argc) { + std::print(stderr, "--apptitle requires a parameter\n"); + return 1; + } + + appTitle = argv[i + 1]; + + i++; + continue; + } + + if (arg == "--text") { + if (i + 1 >= argc) { + std::print(stderr, "--text requires a parameter\n"); + return 1; + } + + dialog->text = argv[i + 1]; + + i++; + continue; + } + + if (arg == "--buttons") { + if (i + 1 >= argc) { + std::print(stderr, "--buttons requires a parameter\n"); + return 1; + } + + CVarList buttonz(argv[i + 1], 0, ';', true); + + for (auto& b : buttonz) { + dialog->buttons.emplace_back(b.c_str()); + } + + i++; + continue; + } + + std::print(stderr, "invalid arg {}\n", argv[i]); + return 1; + } + + QApplication app(argc, argv); + app.setApplicationName(appTitle.isEmpty() ? dialog->title : appTitle); + + if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) + QQuickStyle::setStyle("org.kde.desktop"); + + QQmlApplicationEngine engine; + engine.rootContext()->setContextProperty("dialog", dialog); + engine.load("qrc:/qt/qml/org/hyprland/dialog/main.qml"); + + return app.exec(); +} diff --git a/utils/dialog/main.qml b/utils/dialog/main.qml new file mode 100644 index 0000000..055296e --- /dev/null +++ b/utils/dialog/main.qml @@ -0,0 +1,91 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +ApplicationWindow { + id: window + + FontMetrics { id: fontMetrics } + + property var windowPadding: 10 + + minimumWidth: Math.max(fontMetrics.height * 10, mainLayout.Layout.minimumWidth) + mainLayout.anchors.margins * 2 + windowPadding * 2 + minimumHeight: Math.max(fontMetrics.height * 10, mainLayout.Layout.minimumHeight) + mainLayout.anchors.margins * 2 + windowPadding * 2 + maximumWidth: minimumWidth + maximumHeight: minimumHeight + visible: true + + component Separator: Rectangle { + color: Qt.darker(system.windowText, 1.5) + } + + component VSeparator: Separator { + implicitWidth: 1 + Layout.fillHeight: true + Layout.topMargin: fontMetrics.height + Layout.bottomMargin: fontMetrics.height + } + + component HSeparator: Separator { + implicitHeight: 1 + Layout.fillWidth: true + Layout.leftMargin: fontMetrics.height * 8 + Layout.rightMargin: fontMetrics.height * 8 + } + + SystemPalette { + id: system + colorGroup: SystemPalette.Active + } + + ColumnLayout { + id: mainLayout + spacing: fontMetrics.height + + anchors { + fill: parent + margins: 4 + } + + Text { + font.pointSize: fontMetrics.height + color: system.windowText + text: dialog.dialogTitle + Layout.alignment: Qt.AlignHCenter + } + + HSeparator {} + + Text { + color: system.windowText + text: dialog.dialogText + Layout.alignment: Qt.AlignHCenter + } + + Rectangle { + color: "transparent" + Layout.minimumHeight: 10 + Layout.fillHeight: true + } + + RowLayout { + spacing: 6 + Layout.leftMargin: 20 + Layout.alignment: Qt.AlignRight + + Repeater { + model: dialog.dialogButtons + + Button { + required property int index; + text: dialog.dialogButtons[index] + onClicked: (e) => { + dialog.onButtonPress(dialog.dialogButtons[index]) + } + } + } + } + } +}