Created Hook system (let's users run commands after specific actions)
NInputAction: create NTextInput with NButton HooksService: add dark/light mode hook, add wallpaper change hook HooksTab: create 1 NInputAction for each hook Wallpaper: add hook functionallity
This commit is contained in:
parent
d53a404bf1
commit
37eefe3663
9 changed files with 274 additions and 3 deletions
|
|
@ -335,6 +335,13 @@ Singleton {
|
||||||
property string manualSunrise: "06:30"
|
property string manualSunrise: "06:30"
|
||||||
property string manualSunset: "18:30"
|
property string manualSunset: "18:30"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
property JsonObject hooks: JsonObject {
|
||||||
|
property bool enabled: false
|
||||||
|
property string wallpaperChange: ""
|
||||||
|
property string darkModeChange: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,9 @@ Variants {
|
||||||
// Make the overview darker
|
// Make the overview darker
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Settings.data.colorSchemes.darkMode ? Qt.alpha(Color.mSurface, Style.opacityMedium) : Qt.alpha(Color.mOnSurface, Style.opacityMedium)
|
color: Settings.data.colorSchemes.darkMode ? Qt.alpha(Color.mSurface,
|
||||||
|
Style.opacityMedium) : Qt.alpha(Color.mOnSurface,
|
||||||
|
Style.opacityMedium)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ NPanel {
|
||||||
About,
|
About,
|
||||||
Audio,
|
Audio,
|
||||||
Bar,
|
Bar,
|
||||||
|
Hooks,
|
||||||
Launcher,
|
Launcher,
|
||||||
Brightness,
|
Brightness,
|
||||||
ColorScheme,
|
ColorScheme,
|
||||||
|
|
@ -111,6 +112,10 @@ NPanel {
|
||||||
id: aboutTab
|
id: aboutTab
|
||||||
Tabs.AboutTab {}
|
Tabs.AboutTab {}
|
||||||
}
|
}
|
||||||
|
Component {
|
||||||
|
id: hooksTab
|
||||||
|
Tabs.HooksTab {}
|
||||||
|
}
|
||||||
|
|
||||||
// Order *DOES* matter
|
// Order *DOES* matter
|
||||||
function updateTabsModel() {
|
function updateTabsModel() {
|
||||||
|
|
@ -181,6 +186,11 @@ NPanel {
|
||||||
"label": "Screen Recorder",
|
"label": "Screen Recorder",
|
||||||
"icon": "videocam",
|
"icon": "videocam",
|
||||||
"source": screenRecorderTab
|
"source": screenRecorderTab
|
||||||
|
}, {
|
||||||
|
"id": SettingsPanel.Tab.Hooks,
|
||||||
|
"label": "Hooks",
|
||||||
|
"icon": "link",
|
||||||
|
"source": hooksTab
|
||||||
}, {
|
}, {
|
||||||
"id": SettingsPanel.Tab.About,
|
"id": SettingsPanel.Tab.About,
|
||||||
"label": "About",
|
"label": "About",
|
||||||
|
|
|
||||||
104
Modules/SettingsPanel/Tabs/HooksTab.qml
Normal file
104
Modules/SettingsPanel/Tabs/HooksTab.qml
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real scaling: 1.0
|
||||||
|
|
||||||
|
contentWidth: contentColumn.width
|
||||||
|
contentHeight: contentColumn.height
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentColumn
|
||||||
|
spacing: Style.marginL * scaling
|
||||||
|
width: root.width
|
||||||
|
|
||||||
|
// Enable/Disable Toggle
|
||||||
|
NToggle {
|
||||||
|
label: "Enable Hooks"
|
||||||
|
description: "Enable or disable all hook commands."
|
||||||
|
checked: Settings.data.hooks.enabled
|
||||||
|
onToggled: checked => Settings.data.hooks.enabled = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
visible: Settings.data.hooks.enabled
|
||||||
|
spacing: Style.marginL * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wallpaper Hook Section
|
||||||
|
NInputAction {
|
||||||
|
id: wallpaperHookInput
|
||||||
|
label: "Wallpaper Change Hook"
|
||||||
|
description: "Command to be executed when wallpaper changes."
|
||||||
|
placeholderText: "e.g., notify-send \"Wallpaper\" \"Changed\""
|
||||||
|
text: Settings.data.hooks.wallpaperChange
|
||||||
|
onEditingFinished: {
|
||||||
|
Settings.data.hooks.wallpaperChange = wallpaperHookInput.text
|
||||||
|
}
|
||||||
|
onActionClicked: {
|
||||||
|
if (wallpaperHookInput.text) {
|
||||||
|
HooksService.executeWallpaperHook("test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
NDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark Mode Hook Section
|
||||||
|
NInputAction {
|
||||||
|
id: darkModeHookInput
|
||||||
|
label: "Theme Toggle Hook"
|
||||||
|
description: "Command to be executed when theme toggles between dark and light mode."
|
||||||
|
placeholderText: "e.g., notify-send \"Theme\" \"Toggled\""
|
||||||
|
text: Settings.data.hooks.darkModeChange
|
||||||
|
onEditingFinished: {
|
||||||
|
Settings.data.hooks.darkModeChange = darkModeHookInput.text
|
||||||
|
}
|
||||||
|
onActionClicked: {
|
||||||
|
if (darkModeHookInput.text) {
|
||||||
|
HooksService.executeDarkModeHook(Settings.data.colorSchemes.darkMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
NDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info section
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginS * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "Hook Command Information"
|
||||||
|
font.pointSize: Style.fontSizeM * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mOnSurface
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "• Commands are executed via shell (sh -c)\n• Commands run in background (detached)\n• Test buttons execute with current values"
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Services/HooksService.qml
Normal file
63
Services/HooksService.qml
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Hook connections for automatic script execution
|
||||||
|
Connections {
|
||||||
|
target: Settings.data.colorSchemes
|
||||||
|
function onDarkModeChanged() {
|
||||||
|
executeDarkModeHook(Settings.data.colorSchemes.darkMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute wallpaper change hook
|
||||||
|
function executeWallpaperHook(wallpaperPath) {
|
||||||
|
if (!Settings.data.hooks?.enabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const script = Settings.data.hooks?.wallpaperChange
|
||||||
|
if (!script || script === "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const command = script.replace(/\$1/g, wallpaperPath)
|
||||||
|
Quickshell.execDetached(["sh", "-c", command])
|
||||||
|
Logger.log("HooksService", `Executed wallpaper hook: ${command}`)
|
||||||
|
} catch (e) {
|
||||||
|
Logger.error("HooksService", `Failed to execute wallpaper hook: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute dark mode change hook
|
||||||
|
function executeDarkModeHook(isDarkMode) {
|
||||||
|
if (!Settings.data.hooks?.enabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const script = Settings.data.hooks?.darkModeChange
|
||||||
|
if (!script || script === "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const command = script.replace(/\$1/g, isDarkMode ? "true" : "false")
|
||||||
|
Quickshell.execDetached(["sh", "-c", command])
|
||||||
|
Logger.log("HooksService", `Executed dark mode hook: ${command}`)
|
||||||
|
} catch (e) {
|
||||||
|
Logger.error("HooksService", `Failed to execute dark mode hook: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the service
|
||||||
|
function init() {
|
||||||
|
Logger.log("HooksService", "Service initialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -256,6 +256,11 @@ Singleton {
|
||||||
// Update cache directly
|
// Update cache directly
|
||||||
currentWallpapers[screenName] = path
|
currentWallpapers[screenName] = path
|
||||||
|
|
||||||
|
// Execute wallpaper change hook
|
||||||
|
if (HooksService) {
|
||||||
|
HooksService.executeWallpaperHook(path)
|
||||||
|
}
|
||||||
|
|
||||||
// Update Settings - still need immutable update for Settings persistence
|
// Update Settings - still need immutable update for Settings persistence
|
||||||
// The slice() ensures Settings detects the change and saves properly
|
// The slice() ensures Settings detects the change and saves properly
|
||||||
var monitors = Settings.data.wallpaper.monitors || []
|
var monitors = Settings.data.wallpaper.monitors || []
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ Rectangle {
|
||||||
target: ripple
|
target: ripple
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
from: 0
|
from: 0
|
||||||
to: 0.2
|
to: 0.05
|
||||||
duration: 100
|
duration: 100
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +160,7 @@ Rectangle {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: ripple
|
target: ripple
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
from: 0.2
|
from: 0.05
|
||||||
to: 0
|
to: 0
|
||||||
duration: 300
|
duration: 300
|
||||||
easing.type: Easing.InCubic
|
easing.type: Easing.InCubic
|
||||||
|
|
|
||||||
77
Widgets/NInputAction.qml
Normal file
77
Widgets/NInputAction.qml
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Commons
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Public properties
|
||||||
|
property string label: ""
|
||||||
|
property string description: ""
|
||||||
|
property string placeholderText: ""
|
||||||
|
property string text: ""
|
||||||
|
property string actionButtonText: "Test"
|
||||||
|
property string actionButtonIcon: "play_arrow"
|
||||||
|
property bool actionButtonEnabled: text !== ""
|
||||||
|
|
||||||
|
// Signals
|
||||||
|
signal editingFinished
|
||||||
|
signal actionClicked
|
||||||
|
|
||||||
|
// Internal properties
|
||||||
|
property real scaling: 1.0
|
||||||
|
|
||||||
|
// Label
|
||||||
|
NText {
|
||||||
|
text: root.label
|
||||||
|
font.pointSize: Style.fontSizeL * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Color.mOnSurface
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description
|
||||||
|
NText {
|
||||||
|
text: root.description
|
||||||
|
font.pointSize: Style.fontSizeS * scaling
|
||||||
|
color: Color.mOnSurfaceVariant
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input and button row
|
||||||
|
RowLayout {
|
||||||
|
spacing: Style.marginM * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NTextInput {
|
||||||
|
id: textInput
|
||||||
|
placeholderText: root.placeholderText
|
||||||
|
text: root.text
|
||||||
|
onEditingFinished: {
|
||||||
|
root.text = text
|
||||||
|
root.editingFinished()
|
||||||
|
}
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
NButton {
|
||||||
|
text: root.actionButtonText
|
||||||
|
icon: root.actionButtonIcon
|
||||||
|
backgroundColor: Color.mSecondary
|
||||||
|
textColor: Color.mOnSecondary
|
||||||
|
hoverColor: Color.mTertiary
|
||||||
|
pressColor: Color.mPrimary
|
||||||
|
enabled: root.actionButtonEnabled
|
||||||
|
Layout.fillWidth: false
|
||||||
|
onClicked: {
|
||||||
|
root.actionClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -110,6 +110,9 @@ ShellRoot {
|
||||||
// Initialize UpdateService
|
// Initialize UpdateService
|
||||||
UpdateService.init()
|
UpdateService.init()
|
||||||
|
|
||||||
|
// Initialize HooksService
|
||||||
|
HooksService.init()
|
||||||
|
|
||||||
// Kickoff NightLight service
|
// Kickoff NightLight service
|
||||||
NightLightService.apply()
|
NightLightService.apply()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue