Custom buttons: improved UI, still wip
This commit is contained in:
parent
7f34ca4122
commit
598bc48957
3 changed files with 279 additions and 48 deletions
|
|
@ -1,26 +1,57 @@
|
|||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
NIconButton {
|
||||
id: root
|
||||
|
||||
property ShellScreen screen
|
||||
|
||||
// Widget properties passed from Bar.qml
|
||||
property var screen
|
||||
property real scaling: 1.0
|
||||
property bool allowUserSettings: true
|
||||
|
||||
icon: "favorite"
|
||||
tooltipText: "Hello world"
|
||||
sizeRatio: 0.8
|
||||
|
||||
colorBg: Color.mSurfaceVariant
|
||||
colorFg: Color.mOnSurface
|
||||
colorBorder: Color.transparent
|
||||
colorBorderHover: Color.transparent
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
|
||||
property string barSection: ""
|
||||
property int sectionWidgetIndex: -1
|
||||
property int sectionWidgetsCount: 0
|
||||
|
||||
// Get user settings from Settings data
|
||||
property var widgetSettings: {
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
colorBgHover: Color.applyOpacity(Color.mPrimary, "20")
|
||||
colorFgHover: Color.mPrimary
|
||||
|
||||
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}`)
|
||||
}
|
||||
}
|
||||
|
|
@ -165,6 +165,7 @@ ColumnLayout {
|
|||
onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section)
|
||||
onRemoveWidget: (section, index) => removeWidgetFromSection(section, index)
|
||||
onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex)
|
||||
onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings)
|
||||
}
|
||||
|
||||
// Center Section
|
||||
|
|
@ -176,6 +177,7 @@ ColumnLayout {
|
|||
onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section)
|
||||
onRemoveWidget: (section, index) => removeWidgetFromSection(section, index)
|
||||
onReorderWidget: (section, fromIndex, toIndex) => reorderWidgetInSection(section, fromIndex, toIndex)
|
||||
onUpdateWidgetSettings: (section, index, settings) => updateWidgetSettingsInSection(section, index, settings)
|
||||
}
|
||||
|
||||
// Right Section
|
||||
|
|
@ -187,6 +189,7 @@ ColumnLayout {
|
|||
onAddWidget: (widgetId, section) => addWidgetToSection(widgetId, section)
|
||||
onRemoveWidget: (section, index) => removeWidgetFromSection(section, index)
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
function addWidgetToSection(widgetId, section) {
|
||||
//Logger.log("BarTab", "Adding widget", widgetId, "to section", section)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
|
|
@ -16,6 +17,7 @@ NBox {
|
|||
signal addWidget(string widgetId, string section)
|
||||
signal removeWidget(string section, int index)
|
||||
signal reorderWidget(string section, int fromIndex, int toIndex)
|
||||
signal updateWidgetSettings(string section, int index, var settings)
|
||||
|
||||
color: Color.mSurface
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -33,21 +35,209 @@ NBox {
|
|||
}
|
||||
|
||||
// Generate widget color from name checksum
|
||||
function getWidgetColor(widgetId) {
|
||||
const totalSum = widgetId.split('').reduce((acc, character) => {
|
||||
function getWidgetColor(widget) {
|
||||
const totalSum = JSON.stringify(widget).split('').reduce((acc, character) => {
|
||||
return acc + character.charCodeAt(0)
|
||||
}, 0)
|
||||
switch (totalSum % 5) {
|
||||
case 0:
|
||||
return Color.mPrimary
|
||||
case 1:
|
||||
return Color.mSecondary
|
||||
case 2:
|
||||
return Color.mTertiary
|
||||
case 3:
|
||||
return Color.mError
|
||||
case 4:
|
||||
return Color.mOnSurface
|
||||
switch (totalSum % 10) {
|
||||
case 0:
|
||||
return Color.mPrimary
|
||||
case 1:
|
||||
return Color.mSecondary
|
||||
case 2:
|
||||
return Color.mTertiary
|
||||
case 3:
|
||||
return Color.mError
|
||||
case 4:
|
||||
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
|
||||
height: 40 * scaling
|
||||
radius: Style.radiusL * scaling
|
||||
color: root.getWidgetColor(modelData.id)
|
||||
color: root.getWidgetColor(modelData)
|
||||
border.color: Color.mOutline
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
|
|
@ -172,7 +362,14 @@ NBox {
|
|||
colorBgHover: Color.applyOpacity(Color.mOnPrimary, "40")
|
||||
colorFgHover: Color.mOnPrimary
|
||||
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
|
||||
|
||||
onPressed: mouse => {
|
||||
// Check if the click is on the close button area
|
||||
const closeButtonX = widgetContent.x + widgetContent.width - 20 * scaling
|
||||
const closeButtonY = widgetContent.y
|
||||
const closeButtonWidth = 20 * scaling
|
||||
const closeButtonHeight = 20 * scaling
|
||||
// Check if the click is on the settings or close button area
|
||||
const buttonsX = widgetContent.x + widgetContent.width - 45 * scaling
|
||||
const buttonsY = widgetContent.y
|
||||
const buttonsWidth = 45 * scaling
|
||||
const buttonsHeight = 20 * scaling
|
||||
|
||||
if (mouseX >= closeButtonX && mouseX <= closeButtonX + closeButtonWidth
|
||||
&& mouseY >= closeButtonY && mouseY <= closeButtonY + closeButtonHeight) {
|
||||
// Click is on the close button, don't start drag
|
||||
if (mouseX >= buttonsX && mouseX <= buttonsX + buttonsWidth
|
||||
&& mouseY >= buttonsY && mouseY <= buttonsY + buttonsHeight) {
|
||||
// Click is on the buttons, don't start drag
|
||||
mouse.accepted = false
|
||||
return
|
||||
}
|
||||
|
|
@ -255,13 +452,7 @@ NBox {
|
|||
if (targetIndex !== -1 && targetIndex !== index) {
|
||||
const fromIndex = index
|
||||
const toIndex = targetIndex
|
||||
// Logger.log(
|
||||
// "NSectionEditor",
|
||||
// `Dropped widget from index ${fromIndex} to position ${toIndex} (distance: ${minDistance.toFixed(
|
||||
// 2)})`)
|
||||
reorderWidget(sectionId, fromIndex, toIndex)
|
||||
} else {
|
||||
Logger.warn("NSectionEditor", `No valid drop target found for widget at index ${index}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue