Custom buttons: refactored files structure

This commit is contained in:
LemmyCook 2025-09-03 21:27:42 -04:00
parent 598bc48957
commit 807867ef42
6 changed files with 218 additions and 245 deletions

View file

@ -1,19 +1,21 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
NIconButton { NIconButton {
id: root id: root
// Widget properties passed from Bar.qml // Widget properties passed from Bar.qml
property var screen property var screen
property real scaling: 1.0 property real scaling: 1.0
property string barSection: "" property string barSection: ""
property int sectionWidgetIndex: -1 property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0 property int sectionWidgetsCount: 0
// Get user settings from Settings data // Get user settings from Settings data
property var widgetSettings: { property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase() var section = barSection.replace("Section", "").toLowerCase()
@ -25,19 +27,15 @@ NIconButton {
} }
return {} return {}
} }
// Use settings or defaults from BarWidgetRegistry // Use settings or defaults from BarWidgetRegistry
property string userIcon: widgetSettings.icon || BarWidgetRegistry.widgetMetadata["CustomButton"].icon property string userIcon: widgetSettings.icon || BarWidgetRegistry.widgetMetadata["CustomButton"].icon
property string userExecute: widgetSettings.execute || BarWidgetRegistry.widgetMetadata["CustomButton"].execute property string userExecute: widgetSettings.execute || BarWidgetRegistry.widgetMetadata["CustomButton"].execute
icon: userIcon icon: userIcon
tooltipText: userExecute ? `Execute: ${userExecute}` : "Custom Button - Configure in settings" tooltipText: userExecute ? `Execute: ${userExecute}` : "Custom Button - Configure in settings"
opacity: userExecute ? Style.opacityFull : Style.opacityMedium
colorBg: Color.transparent
colorFg: Color.mOnSurface
colorBgHover: Color.applyOpacity(Color.mPrimary, "20")
colorFgHover: Color.mPrimary
onClicked: { onClicked: {
if (userExecute) { if (userExecute) {
// Execute the user's command // Execute the user's command
@ -47,11 +45,8 @@ NIconButton {
Logger.warn("CustomButton", "No command configured for this button") Logger.warn("CustomButton", "No command configured for this button")
} }
} }
// Visual feedback when no command is set
opacity: userExecute ? 1.0 : 0.6
Component.onCompleted: { Component.onCompleted: {
Logger.log("CustomButton", `Initialized with icon: ${userIcon}, command: ${userExecute}`) Logger.log("CustomButton", `Initialized with icon: ${userIcon}, command: ${userExecute}`)
} }
} }

View file

@ -37,207 +37,29 @@ NBox {
// Generate widget color from name checksum // Generate widget color from name checksum
function getWidgetColor(widget) { function getWidgetColor(widget) {
const totalSum = JSON.stringify(widget).split('').reduce((acc, character) => { const totalSum = JSON.stringify(widget).split('').reduce((acc, character) => {
return acc + character.charCodeAt(0) return acc + character.charCodeAt(0)
}, 0) }, 0)
switch (totalSum % 10) { switch (totalSum % 10) {
case 0: case 0:
return Color.mPrimary return Color.mPrimary
case 1: case 1:
return Color.mSecondary return Color.mSecondary
case 2: case 2:
return Color.mTertiary return Color.mTertiary
case 3: case 3:
return Color.mError return Color.mError
case 4: case 4:
return Color.mOnSurface return Color.mOnSurface
case 5: case 5:
return Qt.darker(Color.mPrimary, 1.3) return Qt.darker(Color.mPrimary, 1.3)
case 6: case 6:
return Qt.darker(Color.mSecondary, 1.3) return Qt.darker(Color.mSecondary, 1.3)
case 7: case 7:
return Qt.darker(Color.mTertiary, 1.3) return Qt.darker(Color.mTertiary, 1.3)
case 8: case 8:
return Qt.darker(Color.mError, 1.3) return Qt.darker(Color.mError, 1.3)
case 9: case 9:
return Qt.darker(Color.mOnSurface, 1.3) return Qt.darker(Color.mOnSurface, 1.3)
}
}
// Widget Settings Dialog Component
Component {
id: widgetSettingsDialog
Popup {
id: settingsPopup
property int widgetIndex: -1
property var widgetData: null
property string widgetId: ""
// Center popup in parent
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
width: 400 * scaling
height: content.implicitHeight + padding * 2
padding: Style.marginL * scaling
modal: true
background: Rectangle {
id: bgRect
color: Color.mSurface
radius: Style.radiusL * scaling
border.color: Color.mPrimary
border.width: Style.borderM * scaling
}
ColumnLayout {
id: content
width: parent.width
spacing: Style.marginM * scaling
// Title
RowLayout {
Layout.fillWidth: true
NText {
text: "Widget Settings: " + settingsPopup.widgetId
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true
}
NIconButton {
icon: "close"
colorBg: Color.transparent
colorFg: Color.mOnSurface
colorBgHover: Color.applyOpacity(Color.mError, "20")
colorFgHover: Color.mError
onClicked: settingsPopup.close()
}
}
// Separator
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Color.mOutline
}
// Settings based on widget type
Loader {
id: settingsLoader
Layout.fillWidth: true
sourceComponent: {
if (settingsPopup.widgetId === "CustomButton") {
return customButtonSettings
}
// Add more widget settings components here as needed
return null
}
}
// Action buttons
RowLayout {
Layout.fillWidth: true
Layout.topMargin: Style.marginM * scaling
Item {
Layout.fillWidth: true
}
NButton {
text: "Cancel"
outlined: true
onClicked: settingsPopup.close()
}
NButton {
text: "Save"
onClicked: {
if (settingsLoader.item && settingsLoader.item.saveSettings) {
var newSettings = settingsLoader.item.saveSettings()
root.updateWidgetSettings(sectionId, settingsPopup.widgetIndex, newSettings)
settingsPopup.close()
}
}
}
}
}
// CustomButton settings component
Component {
id: customButtonSettings
ColumnLayout {
spacing: Style.marginM * scaling
property alias iconField: iconInput
property alias executeField: executeInput
function saveSettings() {
var settings = Object.assign({}, settingsPopup.widgetData)
settings.icon = iconInput.text
settings.execute = executeInput.text
return settings
}
// Icon setting
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXS * scaling
NText {
text: "Icon Name"
font.pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
}
NTextInput{
id: iconInput
Layout.fillWidth: true
//placeholder: "Enter icon name (e.g., favorite, home, settings)"
text: settingsPopup.widgetData.icon || ""
}
NText {
text: "Use Material Icon names from the icon set"
font.pointSize: Style.fontSizeXS * scaling
color: Color.applyOpacity(Color.mOnSurfaceVariant, "80")
}
}
// Execute command setting
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginXS * scaling
NText {
text: "Execute Command"
font.pointSize: Style.fontSizeS * scaling
color: Color.mOnSurfaceVariant
}
NTextInput {
id: executeInput
Layout.fillWidth: true
//placeholder: "Enter command to execute (e.g., firefox, code, terminal)"
text: settingsPopup.widgetData.execute || ""
}
NText {
text: "Command or application to run when clicked"
font.pointSize: Style.fontSizeXS * scaling
color: Color.applyOpacity(Color.mOnSurfaceVariant, "80")
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
}
}
} }
} }
@ -301,7 +123,6 @@ NBox {
spacing: Style.marginS * scaling spacing: Style.marginS * scaling
flow: Flow.LeftToRight flow: Flow.LeftToRight
Repeater { Repeater {
model: widgetModel model: widgetModel
delegate: Rectangle { delegate: Rectangle {
@ -363,18 +184,25 @@ NBox {
colorFgHover: Color.mOnPrimary colorFgHover: Color.mOnPrimary
onClicked: { onClicked: {
// Open widget settings dialog // Open widget settings dialog
var dialog = widgetSettingsDialog.createObject(root, { var dialog = Qt.createComponent("BarWidgetSettingsDialog.qml").createObject(root, {
widgetIndex: index, "widgetIndex": index,
widgetData: modelData, "widgetData": modelData,
widgetId: modelData.id, "widgetId": modelData.id,
parent: Overlay.overlay "parent": Overlay.overlay
}) })
// })
// var dialog = widgetSettingsDialog.createObject(root, {
// widgetIndex: index,
// widgetData: modelData,
// widgetId: modelData.id,
// parent: Overlay.overlay
// })
dialog.open() dialog.open()
} }
} }
} }
NIconButton { NIconButton {
icon: "close" icon: "close"
sizeRatio: 0.6 sizeRatio: 0.6
@ -402,20 +230,20 @@ NBox {
const buttonsWidth = 45 * scaling const buttonsWidth = 45 * scaling
const buttonsHeight = 20 * scaling const buttonsHeight = 20 * scaling
if (mouseX >= buttonsX && mouseX <= buttonsX + buttonsWidth if (mouseX >= buttonsX && mouseX <= buttonsX + buttonsWidth && mouseY >= buttonsY
&& mouseY >= buttonsY && mouseY <= buttonsY + buttonsHeight) { && mouseY <= buttonsY + buttonsHeight) {
// Click is on the buttons, don't start drag // Click is on the buttons, don't start drag
mouse.accepted = false mouse.accepted = false
return return
} }
//Logger.log("NSectionEditor", `Started dragging widget: ${modelData.id} at index ${index}`) //Logger.log("BarSectionEditor", `Started dragging widget: ${modelData.id} at index ${index}`)
// Bring to front when starting drag // Bring to front when starting drag
widgetItem.z = 1000 widgetItem.z = 1000
} }
onReleased: { onReleased: {
//Logger.log("NSectionEditor", `Released widget: ${modelData.id} at index ${index}`) //Logger.log("BarSectionEditor", `Released widget: ${modelData.id} at index ${index}`)
// Reset z-index when drag ends // Reset z-index when drag ends
widgetItem.z = 0 widgetItem.z = 0
@ -478,16 +306,16 @@ NBox {
radius: Style.radiusS * scaling radius: Style.radiusS * scaling
} }
onEntered: function (drag) {//Logger.log("NSectionEditor", "Entered start drop zone") onEntered: function (drag) {//Logger.log("BarSectionEditor", "Entered start drop zone")
} }
onDropped: function (drop) { onDropped: function (drop) {
//Logger.log("NSectionEditor", "Dropped on start zone") //Logger.log("BarSectionEditor", "Dropped on start zone")
if (drop.source && drop.source.widgetIndex !== undefined) { if (drop.source && drop.source.widgetIndex !== undefined) {
const fromIndex = drop.source.widgetIndex const fromIndex = drop.source.widgetIndex
const toIndex = 0 // Insert at the beginning const toIndex = 0 // Insert at the beginning
if (fromIndex !== toIndex) { if (fromIndex !== toIndex) {
//Logger.log("NSectionEditor", `Dropped widget from index ${fromIndex} to beginning`) //Logger.log("BarSectionEditor", `Dropped widget from index ${fromIndex} to beginning`)
reorderWidget(sectionId, fromIndex, toIndex) reorderWidget(sectionId, fromIndex, toIndex)
} }
} }
@ -512,16 +340,16 @@ NBox {
radius: Style.radiusS * scaling radius: Style.radiusS * scaling
} }
onEntered: function (drag) {//Logger.log("NSectionEditor", "Entered end drop zone") onEntered: function (drag) {//Logger.log("BarSectionEditor", "Entered end drop zone")
} }
onDropped: function (drop) { onDropped: function (drop) {
//Logger.log("NSectionEditor", "Dropped on end zone") //Logger.log("BarSectionEditor", "Dropped on end zone")
if (drop.source && drop.source.widgetIndex !== undefined) { if (drop.source && drop.source.widgetIndex !== undefined) {
const fromIndex = drop.source.widgetIndex const fromIndex = drop.source.widgetIndex
const toIndex = widgetModel.length // Insert at the end const toIndex = widgetModel.length // Insert at the end
if (fromIndex !== toIndex) { if (fromIndex !== toIndex) {
//Logger.log("NSectionEditor", `Dropped widget from index ${fromIndex} to end`) //Logger.log("BarSectionEditor", `Dropped widget from index ${fromIndex} to end`)
reorderWidget(sectionId, fromIndex, toIndex) reorderWidget(sectionId, fromIndex, toIndex)
} }
} }

View file

@ -0,0 +1,147 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
// Widget Settings Dialog Component
Popup {
id: settingsPopup
property int widgetIndex: -1
property var widgetData: null
property string widgetId: ""
// Center popup in parent
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
width: 400 * scaling
height: content.implicitHeight + padding * 2
padding: Style.marginL * scaling
modal: true
background: Rectangle {
id: bgRect
color: Color.mSurface
radius: Style.radiusL * scaling
border.color: Color.mPrimary
border.width: Style.borderM * scaling
}
ColumnLayout {
id: content
width: parent.width
spacing: Style.marginM * scaling
// Title
RowLayout {
Layout.fillWidth: true
NText {
text: "Widget Settings: " + settingsPopup.widgetId
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true
}
NIconButton {
icon: "close"
colorBg: Color.transparent
colorFg: Color.mOnSurface
colorBgHover: Color.applyOpacity(Color.mError, "20")
colorFgHover: Color.mError
onClicked: settingsPopup.close()
}
}
// Separator
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Color.mOutline
}
// Settings based on widget type
Loader {
id: settingsLoader
Layout.fillWidth: true
sourceComponent: {
if (settingsPopup.widgetId === "CustomButton") {
return customButtonSettings
}
// Add more widget settings components here as needed
return null
}
}
// Action buttons
RowLayout {
Layout.fillWidth: true
Layout.topMargin: Style.marginM * scaling
Item {
Layout.fillWidth: true
}
NButton {
text: "Cancel"
outlined: true
onClicked: settingsPopup.close()
}
NButton {
text: "Save"
onClicked: {
if (settingsLoader.item && settingsLoader.item.saveSettings) {
var newSettings = settingsLoader.item.saveSettings()
root.updateWidgetSettings(sectionId, settingsPopup.widgetIndex, newSettings)
settingsPopup.close()
}
}
}
}
}
// CustomButton settings component
Component {
id: customButtonSettings
ColumnLayout {
spacing: Style.marginM * scaling
property alias iconField: iconInput
property alias executeField: executeInput
function saveSettings() {
var settings = Object.assign({}, settingsPopup.widgetData)
settings.icon = iconInput.text
settings.execute = executeInput.text
return settings
}
// Icon setting
NTextInput {
id: iconInput
Layout.fillWidth: true
label: "Icon Name"
description: "Use Material Icon names from the icon set"
text: settingsPopup.widgetData.icon || ""
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
}
// Execute command setting
NTextInput {
id: executeInput
Layout.fillWidth: true
label: "Execute Command"
description: "Command or application to run when clicked"
text: settingsPopup.widgetData.execute || ""
placeholderText: "Enter command to execute (app or custom script)"
}
}
}
}

View file

@ -4,6 +4,7 @@ import QtQuick.Layouts
import qs.Commons import qs.Commons
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
import qs.Modules.SettingsPanel.Extras
ColumnLayout { ColumnLayout {
id: root id: root
@ -157,7 +158,7 @@ ColumnLayout {
spacing: Style.marginM * scaling spacing: Style.marginM * scaling
// Left Section // Left Section
NSectionEditor { BarSectionEditor {
sectionName: "Left" sectionName: "Left"
sectionId: "left" sectionId: "left"
widgetModel: Settings.data.bar.widgets.left widgetModel: Settings.data.bar.widgets.left
@ -169,7 +170,7 @@ ColumnLayout {
} }
// Center Section // Center Section
NSectionEditor { BarSectionEditor {
sectionName: "Center" sectionName: "Center"
sectionId: "center" sectionId: "center"
widgetModel: Settings.data.bar.widgets.center widgetModel: Settings.data.bar.widgets.center
@ -181,7 +182,7 @@ ColumnLayout {
} }
// Right Section // Right Section
NSectionEditor { BarSectionEditor {
sectionName: "Right" sectionName: "Right"
sectionId: "right" sectionId: "right"
widgetModel: Settings.data.bar.widgets.right widgetModel: Settings.data.bar.widgets.right

View file

@ -35,13 +35,12 @@ Singleton {
}) })
property var widgetMetadata: ({ property var widgetMetadata: ({
"CustomButton": { "CustomButton": {
allowUserSettings: true, "allowUserSettings": true,
icon: "favorite", "icon": "favorite",
execute: "" "execute": ""
}, }
}) })
// Component definitions - these are loaded once at startup // Component definitions - these are loaded once at startup
property Component activeWindowComponent: Component { property Component activeWindowComponent: Component {

View file

@ -39,10 +39,13 @@ ColumnLayout {
// Container // Container
Rectangle { Rectangle {
id: frame id: frame
implicitWidth: parent.width
implicitHeight: Style.baseWidgetSize * 1.1 * scaling Layout.fillWidth: true
Layout.minimumWidth: 80 * scaling Layout.minimumWidth: 80 * scaling
Layout.maximumWidth: root.inputMaxWidth Layout.maximumWidth: root.inputMaxWidth
implicitWidth: parent.width
implicitHeight: Style.baseWidgetSize * 1.1 * scaling
radius: Style.radiusM * scaling radius: Style.radiusM * scaling
color: Color.mSurface color: Color.mSurface
border.color: Color.mOutline border.color: Color.mOutline