Custom buttons: improved UI, still wip

This commit is contained in:
LemmyCook 2025-09-03 20:51:51 -04:00
parent 7f34ca4122
commit 598bc48957
3 changed files with 279 additions and 48 deletions

View file

@ -1,26 +1,57 @@
import Quickshell import QtQuick
import QtQuick.Layouts
import qs.Commons import qs.Commons
import qs.Widgets
import qs.Services import qs.Services
import qs.Widgets
NIconButton { NIconButton {
id: root id: root
property ShellScreen screen // Widget properties passed from Bar.qml
property var screen
property real scaling: 1.0 property real scaling: 1.0
property bool allowUserSettings: true property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
icon: "favorite" // Get user settings from Settings data
tooltipText: "Hello world" property var widgetSettings: {
sizeRatio: 0.8 var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
colorBg: Color.mSurfaceVariant // Use settings or defaults from BarWidgetRegistry
property string userIcon: widgetSettings.icon || BarWidgetRegistry.widgetMetadata["CustomButton"].icon
property string userExecute: widgetSettings.execute || BarWidgetRegistry.widgetMetadata["CustomButton"].execute
icon: userIcon
tooltipText: userExecute ? `Execute: ${userExecute}` : "Custom Button - Configure in settings"
colorBg: Color.transparent
colorFg: Color.mOnSurface colorFg: Color.mOnSurface
colorBorder: Color.transparent colorBgHover: Color.applyOpacity(Color.mPrimary, "20")
colorBorderHover: Color.transparent colorFgHover: Color.mPrimary
anchors.verticalCenter: parent.verticalCenter
onClicked: { onClicked: {
if (userExecute) {
// Execute the user's command
Quickshell.execDetached(userExecute.split(" "))
Logger.log("CustomButton", `Executing command: ${userExecute}`)
} else {
Logger.warn("CustomButton", "No command configured for this button")
}
}
// Visual feedback when no command is set
opacity: userExecute ? 1.0 : 0.6
Component.onCompleted: {
Logger.log("CustomButton", `Initialized with icon: ${userIcon}, command: ${userExecute}`)
} }
} }

View file

@ -165,6 +165,7 @@ ColumnLayout {
onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section) onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section)
onRemoveWidget: (section, index) => removeWidgetFromSection(section, index) onRemoveWidget: (section, index) => removeWidgetFromSection(section, index)
onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex) onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings)
} }
// Center Section // Center Section
@ -176,6 +177,7 @@ ColumnLayout {
onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section) onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section)
onRemoveWidget: (section, index) => removeWidgetFromSection(section, index) onRemoveWidget: (section, index) => removeWidgetFromSection(section, index)
onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex) onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings)
} }
// Right Section // Right Section
@ -187,6 +189,7 @@ ColumnLayout {
onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section) onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section)
onRemoveWidget: (section, index) => removeWidgetFromSection(section, index) onRemoveWidget: (section, index) => removeWidgetFromSection(section, index)
onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex) onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex)
onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings)
} }
} }
} }
@ -197,6 +200,12 @@ ColumnLayout {
Layout.bottomMargin: Style.marginXL * scaling Layout.bottomMargin: Style.marginXL * scaling
} }
function updateWidgetSettingsInSection(section, index, settings) {
// Update the widget settings in the Settings data
Settings.data.bar.widgets[section][index] = settings
Logger.log("BarTab", `Updated widget settings for ${settings.id} in ${section} section`)
}
// Helper functions // Helper functions
function addWidgetToSection(widgetId, section) { function addWidgetToSection(widgetId, section) {
//Logger.log("BarTab", "Adding widget", widgetId, "to section", section) //Logger.log("BarTab", "Adding widget", widgetId, "to section", section)

View file

@ -1,5 +1,6 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts import QtQuick.Layouts
import qs.Commons import qs.Commons
import qs.Widgets import qs.Widgets
@ -16,6 +17,7 @@ NBox {
signal addWidget(string widgetId, string section) signal addWidget(string widgetId, string section)
signal removeWidget(string section, int index) signal removeWidget(string section, int index)
signal reorderWidget(string section, int fromIndex, int toIndex) signal reorderWidget(string section, int fromIndex, int toIndex)
signal updateWidgetSettings(string section, int index, var settings)
color: Color.mSurface color: Color.mSurface
Layout.fillWidth: true Layout.fillWidth: true
@ -33,11 +35,11 @@ NBox {
} }
// Generate widget color from name checksum // Generate widget color from name checksum
function getWidgetColor(widgetId) { function getWidgetColor(widget) {
const totalSum = widgetId.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 % 5) { switch (totalSum % 10) {
case 0: case 0:
return Color.mPrimary return Color.mPrimary
case 1: case 1:
@ -48,6 +50,194 @@ NBox {
return Color.mError return Color.mError
case 4: case 4:
return Color.mOnSurface return Color.mOnSurface
case 5:
return Qt.darker(Color.mPrimary, 1.3)
case 6:
return Qt.darker(Color.mSecondary, 1.3)
case 7:
return Qt.darker(Color.mTertiary, 1.3)
case 8:
return Qt.darker(Color.mError, 1.3)
case 9:
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
}
}
}
}
} }
} }
@ -122,7 +312,7 @@ NBox {
width: widgetContent.implicitWidth + Style.marginL * scaling width: widgetContent.implicitWidth + Style.marginL * scaling
height: 40 * scaling height: 40 * scaling
radius: Style.radiusL * scaling radius: Style.radiusL * scaling
color: root.getWidgetColor(modelData.id) color: root.getWidgetColor(modelData)
border.color: Color.mOutline border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling) border.width: Math.max(1, Style.borderS * scaling)
@ -172,7 +362,14 @@ NBox {
colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40") colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40")
colorFgHover: Color.mOnPrimary colorFgHover: Color.mOnPrimary
onClicked: { onClicked: {
// TODO open settings // Open widget settings dialog
var dialog = widgetSettingsDialog.createObject(root, {
widgetIndex: index,
widgetData: modelData,
widgetId: modelData.id,
parent: Overlay.overlay
})
dialog.open()
} }
} }
} }
@ -199,15 +396,15 @@ NBox {
drag.target: parent drag.target: parent
onPressed: mouse => { onPressed: mouse => {
// Check if the click is on the close button area // Check if the click is on the settings or close button area
const closeButtonX = widgetContent.x + widgetContent.width - 20 * scaling const buttonsX = widgetContent.x + widgetContent.width - 45 * scaling
const closeButtonY = widgetContent.y const buttonsY = widgetContent.y
const closeButtonWidth = 20 * scaling const buttonsWidth = 45 * scaling
const closeButtonHeight = 20 * scaling const buttonsHeight = 20 * scaling
if (mouseX >= closeButtonX && mouseX <= closeButtonX + closeButtonWidth if (mouseX >= buttonsX && mouseX <= buttonsX + buttonsWidth
&& mouseY >= closeButtonY && mouseY <= closeButtonY + closeButtonHeight) { && mouseY >= buttonsY && mouseY <= buttonsY + buttonsHeight) {
// Click is on the close button, don't start drag // Click is on the buttons, don't start drag
mouse.accepted = false mouse.accepted = false
return return
} }
@ -255,13 +452,7 @@ NBox {
if (targetIndex !== -1 && targetIndex !== index) { if (targetIndex !== -1 && targetIndex !== index) {
const fromIndex = index const fromIndex = index
const toIndex = targetIndex const toIndex = targetIndex
// Logger.log(
// "NSectionEditor",
// `Dropped widget from index ${fromIndex} to position ${toIndex} (distance: ${minDistance.toFixed(
// 2)})`)
reorderWidget(sectionId, fromIndex, toIndex) reorderWidget(sectionId, fromIndex, toIndex)
} else {
Logger.warn("NSectionEditor", `No valid drop target found for widget at index ${index}`)
} }
} }
} }