From 60711adff0dd41a4f6ed96c012b5a56333aaa826 Mon Sep 17 00:00:00 2001 From: ruinivist Date: Sun, 12 Apr 2026 12:05:16 +0000 Subject: [PATCH] refactor: migration to be based on https://github.com/ruinivist/kde-6-widget-starter --- .githooks/pre-commit | 14 ++ .gitignore | 26 +++ .prettierrc | 11 + .vscode/settings.json | 5 + Makefile | 155 ++++++++++++++ README.md | 97 ++++++++- package/contents/config/config.qml | 11 + package/contents/config/main.xml | 17 ++ {plasmoid => package}/contents/ui/Player.qml | 78 +++---- .../contents/ui/Representation.qml | 190 ++++++++++-------- package/contents/ui/configGeneral.qml | 40 ++++ package/contents/ui/main.qml | 37 ++++ {plasmoid => package}/metadata.json | 2 +- plasmoid/contents/config/config.qml | 10 - plasmoid/contents/config/main.xml | 19 -- plasmoid/contents/ui/GeneralConfig.qml | 46 ----- plasmoid/contents/ui/main.qml | 28 --- run | 3 - scripts/watch.sh | 54 +++++ 19 files changed, 613 insertions(+), 230 deletions(-) create mode 100755 .githooks/pre-commit create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 .vscode/settings.json create mode 100644 Makefile create mode 100644 package/contents/config/config.qml create mode 100644 package/contents/config/main.xml rename {plasmoid => package}/contents/ui/Player.qml (67%) rename {plasmoid => package}/contents/ui/Representation.qml (80%) create mode 100644 package/contents/ui/configGeneral.qml create mode 100644 package/contents/ui/main.qml rename {plasmoid => package}/metadata.json (90%) delete mode 100644 plasmoid/contents/config/config.qml delete mode 100644 plasmoid/contents/config/main.xml delete mode 100644 plasmoid/contents/ui/GeneralConfig.qml delete mode 100644 plasmoid/contents/ui/main.qml delete mode 100755 run create mode 100755 scripts/watch.sh diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..be47b3c --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(git rev-parse --show-toplevel)" +cd "$repo_root" + +echo "[pre-commit] Running make format..." +make format + +echo "[pre-commit] Staging formatting changes..." +git add -A + +echo "[pre-commit] Running make lint..." +make lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20fef1e --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# KDE / Qt +.kdev4/ +*.kdev4 +.directory +*.user +*.pro.user +*.moc +*.o +*.obj +*.rcc +*.ui +*.qrc +.qmake.stash + +# Build artifacts +build/ +*.plasmoid + +# Temporary files +*~ +*.swp +*.bak +.DS_Store + +# Logs +*.log diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..cc42f68 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "overrides": [ + { + "files": "*.json", + "options": { + "tabWidth": 4, + "useTabs": false + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..57cf996 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "qt-qml.qmlls.enabled": true, + "qt-qml.qmlls.customExePath": "/usr/lib/qt6/bin/qmlls", + "qt-qml.qmlls.customArgs": ["-I/usr/lib/qt6/qml", "-d/usr/share/doc/qt6"] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b0fb4bc --- /dev/null +++ b/Makefile @@ -0,0 +1,155 @@ +# Makefile for KDE 6 Plasma Widget Development + +# Variables +PACKAGE_DIR := package +METADATA_FILE := $(PACKAGE_DIR)/metadata.json +WIDGET_ID := $(shell grep -oP '"Id": "\K[^"]+' $(METADATA_FILE)) +BUILD_DIR := build +PLASMOID_FILE := $(WIDGET_ID).plasmoid + +# Default target +.PHONY: help +help: + @echo "KDE Plasma 6 Widget Developer Tools" + @echo "" + @echo "Usage:" + @echo " make dev Run widget in plasmoidviewer from local package (MODE=panel|desktop|hidpi)" + @echo " make watch Start Development Mode with Hot Reload" + @echo " make install Install/upgrade widget in local Plasma package store" + @echo " make uninstall Remove widget from local system" + @echo " make package Build .plasmoid file for distribution" + @echo " make clean Remove build artifacts" + @echo " make format Format QML, JSON, YAML, and Markdown files" + @echo " make lint Run non-mutating lint and formatting checks" + @echo " make hooks-install Configure Git to use versioned hooks from .githooks/" + +# Git Hooks +# Configure Git to use versioned hooks stored in this repository. +.PHONY: hooks-install +hooks-install: + git config core.hooksPath .githooks + @echo "Configured core.hooksPath=.githooks" + +# Testing Targets +# Run plasmoidviewer from the local package directory. +# Optional MODE values: panel, desktop, hidpi (omit MODE for default). +.PHONY: dev +dev: + @mode="$(MODE)"; \ + case "$$mode" in \ + ""|default) \ + plasmoidviewer -a $(PACKAGE_DIR) ;; \ + panel) \ + plasmoidviewer -a $(PACKAGE_DIR) -l topedge -f horizontal ;; \ + desktop) \ + plasmoidviewer -a $(PACKAGE_DIR) -l floating -f planar ;; \ + hidpi) \ + QT_SCALE_FACTOR=2 plasmoidviewer -a $(PACKAGE_DIR) ;; \ + *) \ + echo "Invalid MODE='$$mode'. Allowed: panel, desktop, hidpi (or omit MODE)."; \ + exit 1 ;; \ + esac + +# Development Tools +# Start file watcher for iterative development and reload workflow. +.PHONY: watch +watch: + ./scripts/watch.sh + +# Format all QML files in the package directory in-place. +.PHONY: format-qml +format-qml: + @command -v qmlformat >/dev/null 2>&1 || { echo "Missing required tool: qmlformat"; exit 1; } + find $(PACKAGE_DIR) -name "*.qml" -type f -exec qmlformat -i {} + + +# Format JSON/YAML/Markdown files with prettier. +.PHONY: format-prettier +format-prettier: + @command -v prettier >/dev/null 2>&1 || { echo "Missing required tool: prettier"; exit 1; } + find . -type f \ + \( -name "*.json" -o -name "*.yml" -o -name "*.yaml" -o -name "*.md" -o -name ".prettierrc" \) \ + -not -path "./.git/*" \ + -not -path "./build/*" \ + -print0 | xargs -0 -r prettier --write + +# Run all formatting passes. +.PHONY: format +format: format-qml format-prettier + +# Linting +# Check QML formatting without changing files. +.PHONY: lint-qml-format +lint-qml-format: + @command -v qmlformat >/dev/null 2>&1 || { echo "Missing required tool: qmlformat"; exit 1; } + @status=0; \ + find $(PACKAGE_DIR) -name "*.qml" -type f -print0 | \ + xargs -0 -r -I{} sh -c 'tmp_file="$$(mktemp)"; qmlformat "$$1" > "$$tmp_file"; if ! cmp -s "$$1" "$$tmp_file"; then echo "QML format mismatch: $$1"; rm -f "$$tmp_file"; exit 1; fi; rm -f "$$tmp_file"' _ {} || status=1; \ + if [ "$$status" -ne 0 ]; then \ + echo "Run 'make format-qml' to fix QML formatting."; \ + exit 1; \ + fi + +# Run semantic QML lint checks. +.PHONY: lint-qml +lint-qml: + @command -v qmllint >/dev/null 2>&1 || { echo "Missing required tool: qmllint"; exit 1; } + find $(PACKAGE_DIR) -name "*.qml" -type f -print0 | xargs -0 -r qmllint + +# Check JSON/YAML/Markdown formatting without changing files. +.PHONY: lint-prettier +lint-prettier: + @command -v prettier >/dev/null 2>&1 || { echo "Missing required tool: prettier"; exit 1; } + find . -type f \ + \( -name "*.json" -o -name "*.yml" -o -name "*.yaml" -o -name "*.md" -o -name ".prettierrc" \) \ + -not -path "./.git/*" \ + -not -path "./build/*" \ + -print0 | xargs -0 -r prettier --check + +# Run all lint and formatting checks. +.PHONY: lint +lint: lint-qml-format lint-qml lint-prettier + +# Installation +# Install/upgrade widget in local Plasma package store. +.PHONY: install +install: + @if kpackagetool6 --type Plasma/Applet --show $(WIDGET_ID) >/dev/null 2>&1; then \ + echo "Upgrading existing package: $(WIDGET_ID)"; \ + kpackagetool6 --type Plasma/Applet --upgrade $(PACKAGE_DIR); \ + else \ + echo "Installing new package: $(WIDGET_ID)"; \ + kpackagetool6 --type Plasma/Applet --install $(PACKAGE_DIR); \ + fi + +# Upgrade an already installed widget package with local changes. +.PHONY: upgrade +upgrade: + kpackagetool6 --type Plasma/Applet --upgrade $(PACKAGE_DIR) + +# Remove installed widget package from local system. +.PHONY: uninstall +uninstall: + @if kpackagetool6 --type Plasma/Applet --show $(WIDGET_ID) >/dev/null 2>&1; then \ + echo "Removing package registration: $(WIDGET_ID)"; \ + kpackagetool6 --type Plasma/Applet --remove $(WIDGET_ID); \ + else \ + echo "Package not registered: $(WIDGET_ID)"; \ + fi + +# Packaging +# Create distributable .plasmoid archive from current package contents. +.PHONY: package +package: + mkdir -p $(BUILD_DIR) + rm -f $(BUILD_DIR)/$(PLASMOID_FILE) + + # Zip package contents + cd $(PACKAGE_DIR) && zip -r ../$(BUILD_DIR)/$(PLASMOID_FILE) . + + @echo "" + @echo "Package built at: $(BUILD_DIR)/$(PLASMOID_FILE)" + +# Delete all generated build artifacts for a clean workspace. +.PHONY: clean +clean: + rm -rf $(BUILD_DIR) diff --git a/README.md b/README.md index 00b78f3..366d7db 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,95 @@ # NowPlaying -## now on kde 6: https://www.pling.com/p/2195583/ +NowPlaying is a KDE Plasma 6 widget inspired by the Cleartext Rainmeter widget. It displays the current song and artist with compact media controls, and is meant to live on the desktop rather than in a panel. -A KDE Plasma widget inspired from the Cleartext rainmeter widget. Still in beta. +Now on KDE 6: https://www.pling.com/p/2195583/ -Hello,\ -Full credit to the guy windows guy for the idea, I just made a clone.\ -Now of course, it is not as feature fledged (read as no features) as that one but I aim to add most, if not all of it, if time permits.\ -Feel free to open any issues, requests, suggestions etc. +## Prerequisites -PS: Don't forget to star the project. I care about fake internet points. +You need the basic KDE and Qt 6 tools for Plasma widget development. + +On Arch Linux, this is roughly: + +```bash +sudo pacman -S plasma-sdk inotify-tools kirigami2 qt6-base qt6-declarative +``` + +## Development + +Run the widget directly from the local package: + +```bash +make dev +``` + +Other test modes are available: + +```bash +make dev MODE=panel +make dev MODE=desktop +make dev MODE=hidpi +``` + +Use hot reload while editing: + +```bash +make watch +``` + +Install or upgrade the widget in your local Plasma package store: + +```bash +make install +``` + +Create a distributable package: + +```bash +make package +``` + +The package is written to `build/org.ruiny.NowPlaying.plasmoid`. + +## Project Structure + +```text +. +├── package/ +│ ├── metadata.json +│ └── contents/ +│ ├── config/ +│ │ ├── config.qml +│ │ └── main.xml +│ └── ui/ +│ ├── configGeneral.qml +│ ├── main.qml +│ ├── Player.qml +│ └── Representation.qml +├── scripts/ +│ └── watch.sh +└── Makefile +``` + +## Maintenance + +Format files: + +```bash +make format +``` + +Run lint checks: + +```bash +make lint +``` + +Enable the repository pre-commit hook: + +```bash +make hooks-install +``` + +## Credits + +Full credit to the Windows creator for the original idea. I just made a KDE clone. diff --git a/package/contents/config/config.qml b/package/contents/config/config.qml new file mode 100644 index 0000000..164708b --- /dev/null +++ b/package/contents/config/config.qml @@ -0,0 +1,11 @@ +import QtQuick +import org.kde.plasma.configuration + +ConfigModel { + ConfigCategory { + name: i18n("General") + icon: "preferences-desktop" + source: "configGeneral.qml" + } + +} diff --git a/package/contents/config/main.xml b/package/contents/config/main.xml new file mode 100644 index 0000000..d9b11eb --- /dev/null +++ b/package/contents/config/main.xml @@ -0,0 +1,17 @@ + + + + + + + 100 + + + + Noto Sans + + + diff --git a/plasmoid/contents/ui/Player.qml b/package/contents/ui/Player.qml similarity index 67% rename from plasmoid/contents/ui/Player.qml rename to package/contents/ui/Player.qml index 752e87a..3cc174c 100644 --- a/plasmoid/contents/ui/Player.qml +++ b/package/contents/ui/Player.qml @@ -4,33 +4,21 @@ Credits: https://github.com/ccatterina/plasmusic-toolbar looked handy, ty */ - -import QtQuick 2.15 -import QtQml.Models 2.3 +import QtQml.Models +import QtQuick import org.kde.plasma.private.mpris as Mpris QtObject { id: root - property var mpris2Model: Mpris.Mpris2Model { - onRowsInserted: (_, rowIndex) => { - const CONTAINER_ROLE = Qt.UserRole + 1 - const player = this.data(this.index(rowIndex, 0), CONTAINER_ROLE) - if (player.desktopEntry === root.sourceName) { - this.currentIndex = rowIndex; - } - } - } - property string sourceName: "any" - + property var mpris2Model readonly property bool ready: { - if (!mpris2Model.currentPlayer) { - return false - } + if (!mpris2Model.currentPlayer) + return false; + return mpris2Model.currentPlayer.desktopEntry === sourceName || sourceName === "any"; } - readonly property string artists: ready ? mpris2Model.currentPlayer.artist : "" readonly property string title: ready ? mpris2Model.currentPlayer.track : "" readonly property int playbackStatus: ready ? mpris2Model.currentPlayer.playbackStatus : Mpris.PlaybackStatus.Unknown @@ -41,22 +29,22 @@ QtObject { readonly property double songLength: ready ? mpris2Model.currentPlayer.length : 0 readonly property real volume: ready ? mpris2Model.currentPlayer.volume : 0 readonly property string identity: ready ? mpris2Model.currentPlayer.identity : "" - readonly property bool canGoNext: ready ? mpris2Model.currentPlayer.canGoNext : false readonly property bool canGoPrevious: ready ? mpris2Model.currentPlayer.canGoPrevious : false readonly property bool canPlay: ready ? mpris2Model.currentPlayer.canPlay : false readonly property bool canPause: ready ? mpris2Model.currentPlayer.canPause : false readonly property bool canSeek: ready ? mpris2Model.currentPlayer.canSeek : false readonly property bool canRaise: ready ? mpris2Model.currentPlayer.canRaise : false - // To know whether Shuffle and Loop can be changed we have to check if the property is defined, - // unlike the other commands, LoopStatus and Shuffle hasn't a specific propety such as + // unlike the other commands, LoopStatus and Shuffle do not have specific properties such as // CanPause, CanSeek, etc. - readonly property bool canChangeShuffle: ready ? mpris2Model.currentPlayer.shuffle != undefined : false - readonly property bool canChangeLoopStatus: ready ? mpris2Model.currentPlayer.loopStatus != undefined : false + readonly property bool canChangeShuffle: ready ? mpris2Model.currentPlayer.shuffle !== undefined : false + readonly property bool canChangeLoopStatus: ready ? mpris2Model.currentPlayer.loopStatus !== undefined : false function togglePlayPause() { - mpris2Model.currentPlayer?.PlayPause(); + if (mpris2Model.currentPlayer) + mpris2Model.currentPlayer.PlayPause(); + } function setPosition(position) { @@ -64,19 +52,25 @@ QtObject { } function next() { - mpris2Model.currentPlayer?.Next(); + if (mpris2Model.currentPlayer) + mpris2Model.currentPlayer.Next(); + } function previous() { - mpris2Model.currentPlayer?.Previous(); + if (mpris2Model.currentPlayer) + mpris2Model.currentPlayer.Previous(); + } function updatePosition() { - mpris2Model.currentPlayer?.updatePosition(); + if (mpris2Model.currentPlayer) + mpris2Model.currentPlayer.updatePosition(); + } function setVolume(volume) { - mpris2Model.currentPlayer.volume = volume + mpris2Model.currentPlayer.volume = volume; } function changeVolume(delta, showOSD) { @@ -84,11 +78,11 @@ QtObject { } function setShuffle(shuffle) { - mpris2Model.currentPlayer.shuffle = shuffle + mpris2Model.currentPlayer.shuffle = shuffle; } function setLoopStatus(loopStatus) { - mpris2Model.currentPlayer.loopStatus = loopStatus + mpris2Model.currentPlayer.loopStatus = loopStatus; } function raise() { @@ -96,12 +90,22 @@ QtObject { } function formatTrackTime(s) { - var hours = Math.floor(s / 3600) - var minutes = Math.floor((s - (hours * 3600)) / 60) - var seconds = Math.ceil(s - (hours * 3600) - (minutes * 60)) - minutes = (minutes < 10) ? "0" + minutes : minutes - seconds = (seconds < 10) ? "0" + seconds : seconds - var time = minutes + ":" + seconds - return time + const hours = Math.floor(s / 3600); + let minutes = Math.floor((s - hours * 3600) / 60); + let seconds = Math.ceil(s - hours * 3600 - minutes * 60); + minutes = minutes < 10 ? "0" + minutes : minutes; + seconds = seconds < 10 ? "0" + seconds : seconds; + return minutes + ":" + seconds; } + + mpris2Model: Mpris.Mpris2Model { + onRowsInserted: (_, rowIndex) => { + const CONTAINER_ROLE = Qt.UserRole + 1; + const player = this.data(this.index(rowIndex, 0), CONTAINER_ROLE); + if (player.desktopEntry === root.sourceName) + this.currentIndex = rowIndex; + + } + } + } diff --git a/plasmoid/contents/ui/Representation.qml b/package/contents/ui/Representation.qml similarity index 80% rename from plasmoid/contents/ui/Representation.qml rename to package/contents/ui/Representation.qml index e7a7990..efba567 100644 --- a/plasmoid/contents/ui/Representation.qml +++ b/package/contents/ui/Representation.qml @@ -1,36 +1,60 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 -import org.kde.plasma.components as PlasmaComponents -import org.kde.plasma.core as PlasmaCore +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts import org.kde.kirigami as Kirigami +import org.kde.plasma.components as PlasmaComponents MouseArea { id: mediaControlsMouseArea - focus: true - Keys.onPressed: { - if (!event.modifiers) { - event.accepted = true - if (event.key === Qt.Key_Space || event.key === Qt.Key_K) { - player.togglePlayPause() - } else if (event.key === Qt.Key_P) { - player.previous() - } else if (event.key === Qt.Key_N) { - player.next() - } else { - event.accepted = false - } - } - } - hoverEnabled: true readonly property double buttonSize: 16 + focus: true + hoverEnabled: true + Keys.onPressed: (event) => { + if (!event.modifiers) { + event.accepted = true; + if (event.key === Qt.Key_Space || event.key === Qt.Key_K) + player.togglePlayPause(); + else if (event.key === Qt.Key_P) + player.previous(); + else if (event.key === Qt.Key_N) + player.next(); + else + event.accepted = false; + } + } + RowLayout { + anchors.fill: parent + ColumnLayout { - Layout.fillHeight: true id: leftColumn + Layout.fillHeight: true + states: [ + State { + name: "buttonsVisible" + when: mediaControlsMouseArea.containsMouse + + PropertyChanges { + target: nowPlayingLabels + Layout.bottomMargin: 10 + } + + }, + State { + name: "buttonsHidden" + when: !mediaControlsMouseArea.containsMouse + + PropertyChanges { + target: nowPlayingLabels + Layout.bottomMargin: 0 + } + + } + ] + Item { Layout.fillHeight: true Layout.fillWidth: true @@ -38,80 +62,96 @@ MouseArea { ColumnLayout { id: nowPlayingLabels + Layout.alignment: Qt.AlignRight spacing: 0 - Label { - Layout.alignment: Qt.AlignRight + PlasmaComponents.Label { + Layout.alignment: Qt.AlignRight text: "NOW" lineHeight: 0.8 font.pixelSize: 16 font.bold: true + // qmllint disable missing-property font.family: plasmoid.configuration.fontFamily } - Label { + + PlasmaComponents.Label { id: nowPlayingLabel2 + Layout.alignment: Qt.AlignRight text: "PLAYING" lineHeight: 0.8 font.bold: true font.pixelSize: 16 + // qmllint disable missing-property font.family: plasmoid.configuration.fontFamily } + } RowLayout { id: mediaControls + Layout.alignment: Qt.AlignHCenter opacity: mediaControlsMouseArea.containsMouse ? 1 : 0 visible: opacity > 0 - Behavior on opacity { - PropertyAnimation { - easing.type: Easing.InOutQuad - duration: 250 - } - } - - Button { + QQC2.Button { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize + padding: 0 + background: null + onClicked: { + player.previous(); + console.log("prev clicked"); + } + contentItem: Kirigami.Icon { source: "media-skip-backward" } + + } + + QQC2.Button { + id: playButton + + Layout.preferredWidth: buttonSize + Layout.preferredHeight: buttonSize padding: 0 background: null onClicked: { - player.previous() - console.log("prev clicked") + player.togglePlayPause(); + console.log("pause clicked"); } - } - Button { - Layout.preferredWidth: buttonSize - Layout.preferredHeight: buttonSize - id: playButton + contentItem: Kirigami.Icon { source: player.playbackStatus === 2 ? "media-playback-pause" : "media-playback-start" } - padding: 0 - background: null - onClicked: { - player.togglePlayPause() - console.log("pause clicked") - } + } - Button { + + QQC2.Button { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize + padding: 0 + background: null + onClicked: player.next() + contentItem: Kirigami.Icon { source: "media-skip-forward" } - onClicked: { - player.next() - } - padding: 0 - background: null + } + + Behavior on opacity { + PropertyAnimation { + duration: 250 + easing.type: Easing.InOutQuad + } + + } + } Item { @@ -119,61 +159,53 @@ MouseArea { Layout.fillWidth: true } - states: [ - State { - name: "buttonsVisible" - when: mediaControlsMouseArea.containsMouse - PropertyChanges { - target: nowPlayingLabels - Layout.bottomMargin: 10 - } - }, - State { - name: "buttonsHidden" - when: !mediaControlsMouseArea.containsMouse - PropertyChanges { - target: nowPlayingLabels - Layout.bottomMargin: 0 - } - } - ] - transitions: Transition { NumberAnimation { - properties: "Layout.bottomMargin" duration: 250 easing.type: Easing.InOutQuad + properties: "Layout.bottomMargin" } + } + } Rectangle { id: separator - width: 1 + Layout.fillHeight: true + width: 1 } ColumnLayout { - Layout.fillWidth: true id: infoColumn + + Layout.fillWidth: true + PlasmaComponents.Label { - font.family: plasmoid.configuration.fontFamily - text: player.title Layout.fillWidth: true + text: player.title font.pixelSize: 28 - lineHeight: 0.8 font.bold: true + // qmllint disable missing-property + font.family: plasmoid.configuration.fontFamily + lineHeight: 0.8 elide: Text.ElideRight } + PlasmaComponents.Label { - font.family: plasmoid.configuration.fontFamily - elide: Text.ElideRight Layout.maximumWidth: 300 Layout.fillWidth: true text: player.artists font.pixelSize: 26 + // qmllint disable missing-property + font.family: plasmoid.configuration.fontFamily lineHeight: 0.8 + elide: Text.ElideRight } + } + } + } diff --git a/package/contents/ui/configGeneral.qml b/package/contents/ui/configGeneral.qml new file mode 100644 index 0000000..fa61994 --- /dev/null +++ b/package/contents/ui/configGeneral.qml @@ -0,0 +1,40 @@ +import QtQuick +import QtQuick.Controls as QQC2 +import QtQuick.Layouts +import org.kde.kcmutils as KCM +import org.kde.kirigami as Kirigami + +KCM.SimpleKCM { + id: configRoot + + property alias cfg_opacity: opacitySpinBox.value + property string cfg_fontFamily + readonly property var availableFonts: Qt.fontFamilies() + + onCfg_fontFamilyChanged: { + const index = availableFonts.indexOf(cfg_fontFamily); + if (index >= 0 && fontFamilyComboBox.currentIndex !== index) + fontFamilyComboBox.currentIndex = index; + + } + + Kirigami.FormLayout { + QQC2.SpinBox { + id: opacitySpinBox + + Kirigami.FormData.label: i18n("Opacity percent:") + from: 0 + to: 100 + } + + QQC2.ComboBox { + id: fontFamilyComboBox + + Kirigami.FormData.label: i18n("Font:") + model: configRoot.availableFonts + onActivated: configRoot.cfg_fontFamily = currentText + } + + } + +} diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml new file mode 100644 index 0000000..1868611 --- /dev/null +++ b/package/contents/ui/main.qml @@ -0,0 +1,37 @@ +import QtQuick +import QtQuick.Layouts +import org.kde.kirigami as Kirigami +import org.kde.plasma.plasmoid + +PlasmoidItem { + id: root + + // Properties accessed from configuration. + // "qmllint" sees `plasmoid` as QObject here, but Plasma injects `configuration` at runtime. + // qmllint disable missing-property + property int configuredOpacity: plasmoid.configuration.opacity + + width: Kirigami.Units.gridUnit * 25 + height: Kirigami.Units.gridUnit * 5 + Layout.minimumWidth: Kirigami.Units.gridUnit * 25 + Layout.minimumHeight: Kirigami.Units.gridUnit * 5 + Plasmoid.backgroundHints: "NoBackground" + opacity: configuredOpacity / 100 + + Player { + id: player + } + + fullRepresentation: Representation { + Layout.minimumWidth: Kirigami.Units.gridUnit * 25 + Layout.minimumHeight: Kirigami.Units.gridUnit * 5 + Layout.preferredWidth: root.width + Layout.preferredHeight: root.height + } + + compactRepresentation: Representation { + Layout.minimumWidth: Kirigami.Units.gridUnit * 25 + Layout.minimumHeight: Kirigami.Units.gridUnit * 5 + } + +} diff --git a/plasmoid/metadata.json b/package/metadata.json similarity index 90% rename from plasmoid/metadata.json rename to package/metadata.json index d3fceaa..df52c36 100644 --- a/plasmoid/metadata.json +++ b/package/metadata.json @@ -16,6 +16,6 @@ }, "KPackageStructure": "Plasma/Applet", "X-Plasma-API-Minimum-Version": "6.0", - "X-Plasma-Provides": [ "org.kde.plasma.multimediacontrols" ], + "X-Plasma-Provides": ["org.kde.plasma.multimediacontrols"], "X-Plasma-DBusActivationService": "org.mpris.MediaPlayer2.*" } diff --git a/plasmoid/contents/config/config.qml b/plasmoid/contents/config/config.qml deleted file mode 100644 index 7253f1c..0000000 --- a/plasmoid/contents/config/config.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.12 -import org.kde.plasma.configuration 2.0 - -ConfigModel { - ConfigCategory { - name: i18n("General") - icon: "preferences-desktop" - source: "GeneralConfig.qml" - } -} \ No newline at end of file diff --git a/plasmoid/contents/config/main.xml b/plasmoid/contents/config/main.xml deleted file mode 100644 index 7feb4f2..0000000 --- a/plasmoid/contents/config/main.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - 100 - - - - "Noto Sans" - - - - - diff --git a/plasmoid/contents/ui/GeneralConfig.qml b/plasmoid/contents/ui/GeneralConfig.qml deleted file mode 100644 index 8ded248..0000000 --- a/plasmoid/contents/ui/GeneralConfig.qml +++ /dev/null @@ -1,46 +0,0 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 -import org.kde.plasma.core as PlasmaCore -import org.kde.kirigami as Kirigami - -Item { - id: configRoot - - signal configurationChanged - - property alias cfg_opacity: opacitySpinBox.value - - property string cfg_fontFamily - - ColumnLayout { - spacing: Kirigami.Units.mediumSpacing - - RowLayout{ - Label { - text: i18n("Opacity percent") - } - SpinBox { - id: opacitySpinBox - from: 0 - to: 100 - } - } - - RowLayout{ - Label { - text: i18n("Font") - } - ComboBox{ - id: fontFamilyComboBox - model: Qt.fontFamilies() - onCurrentIndexChanged: { - var current = Qt.fontFamilies()[currentIndex] - cfg_fontFamily=current - configRoot.configurationChanged() - } - } - } - - } -} diff --git a/plasmoid/contents/ui/main.qml b/plasmoid/contents/ui/main.qml deleted file mode 100644 index e27e3a1..0000000 --- a/plasmoid/contents/ui/main.qml +++ /dev/null @@ -1,28 +0,0 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 -import org.kde.plasma.plasmoid -import org.kde.plasma.core as PlasmaCore -import org.kde.plasma.plasma5support as Plasma5Support -import org.kde.plasma.private.mpris as Mpris -import org.kde.kirigami as Kirigami - -PlasmoidItem { - id: root - - Layout.minimumWidth: Kirigami.Units.gridUnit*25 - Layout.minimumHeight: Kirigami.Units.gridUnit*5 - - Plasmoid.backgroundHints: "NoBackground" - - opacity: plasmoid.configuration.opacity/100 - - Representation{ - anchors.fill: parent - } - - - Player{ - id: player - } -} diff --git a/run b/run deleted file mode 100755 index b4550cf..0000000 --- a/run +++ /dev/null @@ -1,3 +0,0 @@ -kpackagetool6 -t Plasma/Applet --install plasmoid/ -kpackagetool6 -t Plasma/Applet --upgrade plasmoid/ -plasmoidviewer -a ./plasmoid \ No newline at end of file diff --git a/scripts/watch.sh b/scripts/watch.sh new file mode 100755 index 0000000..f185069 --- /dev/null +++ b/scripts/watch.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Get the directory where the script is located. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PACKAGE_DIR="$SCRIPT_DIR/package" +PLASMOID_NAME=$(grep -oP '"Id": "\K[^"]+' "$PACKAGE_DIR/metadata.json") + +# Check for inotify-tools. +if ! command -v inotifywait &> /dev/null; then + echo "Error: inotify-tools is typically not installed." + echo "Please install it with your package manager (e.g., sudo apt install inotify-tools)" + exit 1 +fi + +echo "Found Widget ID: $PLASMOID_NAME" +echo "Starting Hot Reload Watcher..." +echo "Press Ctrl+C to stop." + +# Cleanup function to kill the viewer when the script exits. +cleanup() { + echo -e "\nStopping watcher..." + if [ -n "$PID" ]; then + kill "$PID" 2>/dev/null + fi + exit +} + +trap cleanup SIGINT SIGTERM + +# Initial start. +echo -e "\n[$(date +%T)] Starting plasmoidviewer..." +plasmoidviewer -a "$PACKAGE_DIR" & +PID=$! + +# Watch loop. +while true; do + # Wait for any file modification in the package directory. + inotifywait -r -e modify -e create -e delete "$PACKAGE_DIR" -q + + echo -e "\n[$(date +%T)] Change detected! Reloading..." + + # Kill the current instance. + if [ -n "$PID" ]; then + kill "$PID" 2>/dev/null + wait "$PID" 2>/dev/null + fi + + # Small delay to let Qt/KDE clean up properly. + sleep 0.5 + + # Restart with absolute path. + plasmoidviewer -a "$PACKAGE_DIR" & + PID=$! +done