From c3a8a171734bfeced58f4611365e85a6daed7db9 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 29 Aug 2025 23:06:55 +0200 Subject: ... --- accounts/gkleen@sif/shell/default.nix | 20 ++++ .../shell/quickshell-plugins/CMakeLists.txt | 116 +++++++++++++++++++++ .../gkleen@sif/shell/quickshell-plugins/Chrono.cpp | 88 ++++++++++++++++ .../gkleen@sif/shell/quickshell-plugins/Chrono.hpp | 57 ++++++++++ .../shell/quickshell-plugins/customplugin.h | 7 ++ .../shell/quickshell-plugins/default.nix | 20 ++++ accounts/gkleen@sif/shell/quickshell/Bar.qml | 81 ++++++++++++++ accounts/gkleen@sif/shell/quickshell/shell.qml | 13 +++ 8 files changed, 402 insertions(+) create mode 100644 accounts/gkleen@sif/shell/default.nix create mode 100644 accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt create mode 100644 accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp create mode 100644 accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp create mode 100644 accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h create mode 100644 accounts/gkleen@sif/shell/quickshell-plugins/default.nix create mode 100644 accounts/gkleen@sif/shell/quickshell/Bar.qml create mode 100644 accounts/gkleen@sif/shell/quickshell/shell.qml (limited to 'accounts/gkleen@sif/shell') diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix new file mode 100644 index 00000000..405ae4b6 --- /dev/null +++ b/accounts/gkleen@sif/shell/default.nix @@ -0,0 +1,20 @@ +{ config, pkgs, lib, ... }: + +{ + config = { + programs.quickshell = { + enable = true; + config = { + src = ./quickshell; + replacements = { + coreutils = toString pkgs.coreutils; + }; + }; + }; + systemd.user.services.quickshell = { + Service = { + Environment = "QML_IMPORT_PATH=${pkgs.qt6Packages.callPackage ./quickshell-plugins {}}/${pkgs.qt6.qtbase.qtQmlPrefix}"; + }; + }; + }; +} diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt new file mode 100644 index 00000000..aa363c4c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt @@ -0,0 +1,116 @@ +set(INSTALL_QMLDIR "" CACHE STRING "QML install dir") +set(INSTALL_QML_PREFIX "" CACHE STRING "QML install prefix") + +# There doesn't seem to be a standard cross-distro qml install path. +if ("${INSTALL_QMLDIR}" STREQUAL "" AND "${INSTALL_QML_PREFIX}" STREQUAL "") + message(WARNING "Neither INSTALL_QMLDIR nor INSTALL_QML_PREFIX is set. QML modules will not be installed.") +else() + if ("${INSTALL_QMLDIR}" STREQUAL "") + set(QML_FULL_INSTALLDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_QML_PREFIX}") + else() + set(QML_FULL_INSTALLDIR "${INSTALL_QMLDIR}") + endif() + + message(STATUS "QML install dir: ${QML_FULL_INSTALLDIR}") +endif() + +# Install a given target as a QML module. This is mostly pulled from ECM, as there does not seem +# to be an official way to do it. +# see https://github.com/KDE/extra-cmake-modules/blob/fe0f606bf7f222e36f7560fd7a2c33ef993e23bb/modules/ECMQmlModule6.cmake#L160 +function(install_qml_module arg_TARGET) + if (NOT DEFINED QML_FULL_INSTALLDIR) + return() + endif() + + qt_query_qml_module(${arg_TARGET} + URI module_uri + VERSION module_version + PLUGIN_TARGET module_plugin_target + TARGET_PATH module_target_path + QMLDIR module_qmldir + TYPEINFO module_typeinfo + QML_FILES module_qml_files + RESOURCES module_resources + ) + + set(module_dir "${QML_FULL_INSTALLDIR}/${module_target_path}") + + if (NOT TARGET "${module_plugin_target}") + message(FATAL_ERROR "install_qml_modules called for a target without a plugin") + endif() + + get_target_property(target_type "${arg_TARGET}" TYPE) + if (NOT "${target_type}" STREQUAL "STATIC_LIBRARY") + install( + TARGETS "${arg_TARGET}" + LIBRARY DESTINATION "${module_dir}" + RUNTIME DESTINATION "${module_dir}" + ) + + install( + TARGETS "${module_plugin_target}" + LIBRARY DESTINATION "${module_dir}" + RUNTIME DESTINATION "${module_dir}" + ) + endif() + + install(FILES "${module_qmldir}" DESTINATION "${module_dir}") + install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") + + # Install QML files + list(LENGTH module_qml_files num_files) + if (NOT "${module_qml_files}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) + qt_query_qml_module(${arg_TARGET} QML_FILES_DEPLOY_PATHS qml_files_deploy_paths) + + math(EXPR last_index "${num_files} - 1") + foreach(i RANGE 0 ${last_index}) + list(GET module_qml_files ${i} src_file) + list(GET qml_files_deploy_paths ${i} deploy_path) + get_filename_component(dst_name "${deploy_path}" NAME) + get_filename_component(dest_dir "${deploy_path}" DIRECTORY) + install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") + endforeach() + endif() + + # Install resources + list(LENGTH module_resources num_files) + if (NOT "${module_resources}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) + qt_query_qml_module(${arg_TARGET} RESOURCES_DEPLOY_PATHS resources_deploy_paths) + + math(EXPR last_index "${num_files} - 1") + foreach(i RANGE 0 ${last_index}) + list(GET module_resources ${i} src_file) + list(GET resources_deploy_paths ${i} deploy_path) + get_filename_component(dst_name "${deploy_path}" NAME) + get_filename_component(dest_dir "${deploy_path}" DIRECTORY) + install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") + endforeach() + endif() +endfunction() + + +cmake_minimum_required(VERSION 3.20) +project(custom LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Qml) + +qt_standard_project_setup(REQUIRES 6.6) + +qt6_policy(SET QTP0001 NEW) +qt6_add_qml_module(customplugin + URI "Custom" + PLUGIN_TARGET customplugin +) + +target_sources(customplugin PRIVATE + Chrono.cpp Chrono.hpp +) + +target_compile_features(customplugin PUBLIC cxx_std_26) + +target_link_libraries(customplugin PRIVATE + Qt6::Core + Qt6::Qml +) + +install_qml_module(customplugin) diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp new file mode 100644 index 00000000..929b7be6 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp @@ -0,0 +1,88 @@ +#include "Chrono.hpp" + +#include +#include +#include + +Chrono::Chrono(QObject* parent): QObject(parent) { + QObject::connect(&this->timer, &QTimer::timeout, this, &Chrono::onTimeout); + this->update(); +} + +bool Chrono::enabled() const { return this->mEnabled; } + +void Chrono::setEnabled(bool enabled) { + if (enabled == this->mEnabled) return; + this->mEnabled = enabled; + emit this->enabledChanged(); + this->update(); +} + +Chrono::Precision Chrono::precision() const { return this->mPrecision; } + +void Chrono::setPrecision(Chrono::Precision precision) { + if (precision == this->mPrecision) return; + this->mPrecision = precision; + emit this->precisionChanged(); + this->update(); +} + +void Chrono::onTimeout() { + this->setTime(this->targetTime); + this->schedule(this->targetTime); +} + +void Chrono::update() { + if (this->mEnabled) { + this->setTime(std::chrono::time_point()); + this->schedule(std::chrono::time_point()); + } else { + this->timer.stop(); + } +} + +void Chrono::setTime(const std::chrono::time_point& targetTime) { + auto currentTime = std::chrono::system_clock::now(); + auto offset = std::chrono::duration_cast(targetTime - currentTime); + this->currentTime = abs(offset.count()) < 500 ? targetTime : currentTime; + + switch (this->mPrecision) { + case Chrono::Hours: this->currentTime = std::chrono::time_point_cast(this->currentTime); + case Chrono::Minutes: this->currentTime = std::chrono::time_point_cast(this->currentTime); + case Chrono::Seconds: this->currentTime = std::chrono::time_point_cast(this->currentTime); + } + + emit this->dateChanged(); +} + +void Chrono::schedule(const std::chrono::time_point& targetTime) { + auto currentTime = std::chrono::system_clock::now(); + auto offset = std::chrono::duration_cast(targetTime - currentTime); + auto nextTime = abs(offset.count()) < 500 ? targetTime : currentTime; + + { + using namespace std::chrono_literals; + + switch (this->mPrecision) { + case Chrono::Hours: nextTime = std::chrono::time_point_cast(nextTime) + 1h; + case Chrono::Minutes: nextTime = std::chrono::time_point_cast(nextTime) + 1min; + case Chrono::Seconds: nextTime = std::chrono::time_point_cast(nextTime) + 1s; + } + } + + this->targetTime = nextTime; + auto delay = std::chrono::duration_cast(nextTime - currentTime); + this->timer.start(delay); +} + +QString Chrono::format() const { return this->mFormat; } +void Chrono::setFormat(QString format) { + if (format == this->mFormat) return; + this->mFormat = format; + emit this->formatChanged(); + this->update(); +} + +QString Chrono::date() const { + return QString::fromStdString(std::format(std::runtime_format(this->mFormat.toStdString()), std::chrono::zoned_time(std::chrono::current_zone(), std::chrono::time_point_cast(this->currentTime)))); +} diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp new file mode 100644 index 00000000..788fa88e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include +#include + +class Chrono : public QObject { + Q_OBJECT; + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); + Q_PROPERTY(Chrono::Precision precision READ precision WRITE setPrecision NOTIFY precisionChanged); + Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged); + Q_PROPERTY(QString date READ date NOTIFY dateChanged); + QML_ELEMENT; + +public: + enum Precision : quint8 { + Hours = 1, + Minutes = 2, + Seconds = 3, + }; + Q_ENUM(Precision); + + explicit Chrono(QObject* parent = nullptr); + + bool enabled() const; + void setEnabled(bool enabled); + + Chrono::Precision precision() const; + void setPrecision(Chrono::Precision precision); + + QString format() const; + void setFormat (QString format); + + QString date() const; + +signals: + void enabledChanged(); + void precisionChanged(); + void formatChanged(); + void dateChanged(); + +private slots: + void onTimeout(); + +private: + bool mEnabled = true; + Chrono::Precision mPrecision = Chrono::Seconds; + QString mFormat = "{:%c}"; + QTimer timer; + std::chrono::time_point currentTime, targetTime; + + void update(); + void setTime(const std::chrono::time_point& targetTime); + void schedule(const std::chrono::time_point& targetTime); +}; diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h new file mode 100644 index 00000000..e66ba9e3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h @@ -0,0 +1,7 @@ +#include + +class CustomPlugin : public QQmlEngineExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) +}; diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/default.nix b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix new file mode 100644 index 00000000..fafea90e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix @@ -0,0 +1,20 @@ +{ lib +, stdenv +, cmake +, qt6 +, fmt +}: +stdenv.mkDerivation rec { + name = "quickshell-custom"; + + src = ./.; + nativeBuildInputs = [ cmake qt6.wrapQtAppsHook ]; + buildInputs = [ + qt6.qtbase + qt6.qtdeclarative + ]; + + cmakeFlags = [ + (lib.cmakeFeature "INSTALL_QML_PREFIX" qt6.qtbase.qtQmlPrefix) + ]; +} diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml new file mode 100644 index 00000000..b7235a61 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml @@ -0,0 +1,81 @@ +import Quickshell +import Quickshell.Io +import Custom as Custom +import QtQuick + + +PanelWindow { + property var modelData + + anchors { + bottom: true + left: true + right: true + } + margins { + left: 26 + 8 + right: 26 + 8 + } + + screen: modelData + implicitHeight: 21 + color: Qt.rgba(0, 0, 0, 0.66) + + Row { + id: left + + height: parent.height + anchors.left: parent.left + anchors.leftMargin: 8 + anchors.verticalCenter: parent.verticalCenter + spacing: 5 + + Text { + color: "white" + anchors.verticalCenter: parent.verticalCenter + text: "left" + } + } + + Row { + id: center + + height: parent.height + anchors.centerIn: parent + spacing: 5 + + Text { + color: "white" + anchors.verticalCenter: parent.verticalCenter + text: "center" + } + } + + Row { + id: right + + height: parent.height + anchors.right: parent.right + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + spacing: 5 + + Text { + id: clock + color: "white" + + anchors.verticalCenter: parent.verticalCenter + + Custom.Chrono { + id: chrono + format: "W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}" + } + + text: chrono.date + + font.pointSize: 10 + font.family: "Fira Sans" + font.features: { "tnum": 1 } + } + } +} \ No newline at end of file diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml new file mode 100644 index 00000000..35fe5344 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml @@ -0,0 +1,13 @@ +import Quickshell + +ShellRoot { + settings.watchFiles: false + + Variants { + model: Quickshell.screens + + delegate: Bar { + modelData: item + } + } +} -- cgit v1.2.3