fix(StatusBaseButton): adapt to handle actions when it is disabled

- add an `interactive` property as a drop-in replacement for `enabled`;
the UI looks "disabled" but can still display e.g. a tooltip or some
loading animation
- add the ability to display a tooltip
- remove DisabledTooltipButton and replace it with a regular
`Status(Flat)Button`, using the above new features
- update storybook with the new `interactive` switch

Fixes #10185
This commit is contained in:
Lukáš Tinkl 2024-03-12 16:44:27 +01:00 committed by Lukáš Tinkl
parent ad7f39f91c
commit d25cefcc2e
11 changed files with 76 additions and 113 deletions

View File

@ -51,10 +51,12 @@ SplitView {
size: modelData size: modelData
text: ctrlText.text text: ctrlText.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
} }
} }
@ -67,10 +69,12 @@ SplitView {
size: modelData size: modelData
icon.name: ctrlIconName.text icon.name: ctrlIconName.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
} }
} }
@ -84,10 +88,12 @@ SplitView {
text: ctrlText.text text: ctrlText.text
icon.name: ctrlIconName.text icon.name: ctrlIconName.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
} }
} }
@ -101,10 +107,12 @@ SplitView {
size: modelData size: modelData
icon.name: ctrlIconName.text icon.name: ctrlIconName.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
isRoundIcon: true isRoundIcon: true
radius: height/2 radius: height/2
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
@ -125,10 +133,12 @@ SplitView {
size: modelData size: modelData
text: ctrlText.text text: ctrlText.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
} }
} }
@ -141,10 +151,12 @@ SplitView {
size: modelData size: modelData
icon.name: ctrlIconName.text icon.name: ctrlIconName.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
} }
} }
@ -158,10 +170,12 @@ SplitView {
text: ctrlText.text text: ctrlText.text
icon.name: ctrlIconName.text icon.name: ctrlIconName.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
} }
} }
@ -175,10 +189,12 @@ SplitView {
size: modelData size: modelData
icon.name: ctrlIconName.text icon.name: ctrlIconName.text
asset.emoji: d.effectiveEmoji asset.emoji: d.effectiveEmoji
tooltip.text: ctrlTooltip.text
textPosition: d.effectiveTextPosition textPosition: d.effectiveTextPosition
type: ctrlType.currentIndex type: ctrlType.currentIndex
loading: ctrlLoading.checked loading: ctrlLoading.checked
enabled: ctrlEnabled.checked enabled: ctrlEnabled.checked
interactive: ctrlInteractive.checked
isRoundIcon: true isRoundIcon: true
radius: height/2 radius: height/2
textFillWidth: ctrlFillWidth.checked textFillWidth: ctrlFillWidth.checked
@ -234,6 +250,14 @@ SplitView {
text: "enabled" text: "enabled"
} }
} }
RowLayout {
Label { text: "Tooltip:" }
TextField {
id: ctrlTooltip
placeholderText: "Tooltip"
text: "Sample tooltip"
}
}
RowLayout { RowLayout {
Label { text: "Type:" } Label { text: "Type:" }
ComboBox { ComboBox {
@ -260,6 +284,11 @@ SplitView {
id: ctrlLoading id: ctrlLoading
text: "Loading" text: "Loading"
} }
Switch {
id: ctrlInteractive
text: "Interactive"
checked: true
}
Switch { Switch {
id: ctrlEnabled id: ctrlEnabled
text: "Enabled" text: "Enabled"

View File

@ -33,7 +33,10 @@ Button {
color: d.textColor color: d.textColor
} }
property alias tooltip: tooltip
property bool loading property bool loading
property bool interactive: true
property color normalColor property color normalColor
property color hoverColor property color hoverColor
@ -56,9 +59,14 @@ Button {
QtObject { QtObject {
id: d id: d
readonly property color textColor: root.hovered && (root.enabled || root.loading) ? root.textHoverColor : readonly property color textColor: {
root.enabled || root.loading ? root.textColor if (!root.interactive || !root.enabled)
: root.disabledTextColor return root.disabledTextColor
if (root.hovered)
return root.textHoverColor
return root.textColor
}
readonly property bool iconOnly: root.display === AbstractButton.IconOnly || root.text === "" readonly property bool iconOnly: root.display === AbstractButton.IconOnly || root.text === ""
readonly property int iconSize: { readonly property int iconSize: {
switch(root.size) { switch(root.size) {
@ -119,9 +127,9 @@ Button {
radius: root.radius radius: root.radius
border.color: root.borderColor border.color: root.borderColor
color: { color: {
if (root.enabled) if (!root.enabled || !root.interactive)
return !root.loading && (root.hovered || root.highlighted) ? hoverColor : normalColor; return disabledColor
return disabledColor; return !root.loading && (root.hovered || root.highlighted) ? hoverColor : normalColor
} }
} }
@ -151,7 +159,7 @@ Button {
id: roundIcon id: roundIcon
StatusRoundIcon { StatusRoundIcon {
opacity: !root.loading && root.icon.name !== "" && root.display !== AbstractButton.TextOnly opacity: !root.loading && root.icon.name !== "" && root.display !== AbstractButton.TextOnly
asset.name: root.icon.name asset.name: root.icon.name
asset.width: d.iconSize asset.width: d.iconSize
asset.height: d.iconSize asset.height: d.iconSize
@ -216,14 +224,21 @@ Button {
} }
} }
// stop the mouse clicks in the "loading" state w/o disabling the whole button // stop the mouse clicks in the "loading" or non-interactive state w/o disabling the whole button
// as this would make it impossible to have hover events or a tooltip // as this would make it impossible to have hover events or a tooltip
MouseArea { MouseArea {
id: mouseArea
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.AllButtons acceptedButtons: Qt.AllButtons
enabled: root.loading enabled: root.loading || !root.interactive
onPressed: mouse.accepted = true onPressed: mouse.accepted = true
onWheel: wheel.accepted = true onWheel: wheel.accepted = true
cursorShape: !root.loading ? Qt.PointingHandCursor: undefined // always works; 'undefined' resets to default cursor cursorShape: root.interactive && !root.loading ? Qt.PointingHandCursor: undefined // always works; 'undefined' resets to default cursor
}
StatusToolTip {
id: tooltip
visible: tooltip.text !== "" && root.hovered
offset: -(tooltip.x + tooltip.width/2 - root.width/2)
} }
} }

View File

@ -40,6 +40,6 @@ StatusBaseButton {
disabledTextColor: Theme.palette.baseColor1 disabledTextColor: Theme.palette.baseColor1
borderColor: (type === StatusBaseButton.Type.Normal || hovered) && !loading ? "transparent" borderColor: (type === StatusBaseButton.Type.Normal || hovered) && !loading && interactive ? "transparent"
: Theme.palette.baseColor2 : Theme.palette.baseColor2
} }

View File

@ -139,16 +139,12 @@ StackView {
title: qsTr("Tokens") title: qsTr("Tokens")
buttons: [ buttons: [
DisabledTooltipButton { StatusButton {
readonly property bool buttonEnabled: root.isPrivilegedTokenOwnerProfile && root.arePrivilegedTokensDeployed objectName: "addNewItemButton"
buttonType: DisabledTooltipButton.Normal
aliasedObjectName: "addNewItemButton"
text: qsTr("Mint token") text: qsTr("Mint token")
enabled: root.isAdminOnly || buttonEnabled interactive: root.isPrivilegedTokenOwnerProfile && root.arePrivilegedTokensDeployed
interactive: buttonEnabled
onClicked: root.push(newTokenViewComponent, StackView.Immediate) onClicked: root.push(newTokenViewComponent, StackView.Immediate)
tooltipText: qsTr("In order to mint, you must hodl the TokenMaster token for %1").arg(root.communityName) tooltip.text: root.isAdminOnly ? qsTr("In order to mint, you must hodl the TokenMaster token for %1").arg(root.communityName) : ""
} }
] ]

View File

@ -282,16 +282,15 @@ Item {
} }
} }
DisabledTooltipButton { StatusButton {
id: startBtn id: startBtn
interactive: startButtonEnabled interactive: startButtonEnabled
buttonType: DisabledTooltipButton.Normal objectName: "ensStartButton"
aliasedObjectName: "ensStartButton"
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.padding anchors.bottomMargin: Style.current.padding
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Start") text: qsTr("Start")
tooltipText: root.tooltipText tooltip.text: root.tooltipText
onClicked: startBtnClicked() onClicked: startBtnClicked()
} }
} }

View File

@ -65,8 +65,7 @@ SettingsContentBase {
priv.hasAnyProfileShowcaseChanges priv.hasAnyProfileShowcaseChanges
saveChangesButtonEnabled: !!descriptionPanel.displayName.text && descriptionPanel.displayName.valid saveChangesButtonEnabled: !!descriptionPanel.displayName.text && descriptionPanel.displayName.valid
toast.saveChangesTooltipVisible: root.dirty toast.saveChangesTooltipText: saveChangesButtonEnabled ? "" : qsTr("Invalid changes made to Identity")
toast.saveChangesTooltipText: qsTr("Invalid changes made to Identity")
autoscrollWhenDirty: profileTabBar.currentIndex === MyProfileView.Identity autoscrollWhenDirty: profileTabBar.currentIndex === MyProfileView.Identity
onResetChangesClicked: priv.reset() onResetChangesClicked: priv.reset()

View File

@ -38,14 +38,13 @@ Rectangle {
height: parent.height height: parent.height
spacing: Style.current.padding spacing: Style.current.padding
DisabledTooltipButton { StatusFlatButton {
buttonType: DisabledTooltipButton.Flat objectName: "walletFooterSendButton"
aliasedObjectName: "walletFooterSendButton" icon.name: "send"
icon: "send"
text: root.isCommunityOwnershipTransfer ? qsTr("Send Owner token to transfer %1 Community ownership").arg(root.communityName) : qsTr("Send") text: root.isCommunityOwnershipTransfer ? qsTr("Send Owner token to transfer %1 Community ownership").arg(root.communityName) : qsTr("Send")
interactive: networkConnectionStore.sendBuyBridgeEnabled interactive: networkConnectionStore.sendBuyBridgeEnabled
onClicked: root.launchSendModal() onClicked: root.launchSendModal()
tooltipText: networkConnectionStore.sendBuyBridgeToolTipText tooltip.text: networkConnectionStore.sendBuyBridgeToolTipText
visible: !walletStore.overview.isWatchOnlyAccount && walletStore.overview.canSend visible: !walletStore.overview.isWatchOnlyAccount && walletStore.overview.canSend
} }
@ -57,13 +56,12 @@ Rectangle {
} }
} }
DisabledTooltipButton { StatusFlatButton {
icon: "bridge" icon.name: "bridge"
buttonType: DisabledTooltipButton.Flat
text: qsTr("Bridge") text: qsTr("Bridge")
interactive: networkConnectionStore.sendBuyBridgeEnabled interactive: networkConnectionStore.sendBuyBridgeEnabled
onClicked: root.launchBridgeModal() onClicked: root.launchBridgeModal()
tooltipText: networkConnectionStore.sendBuyBridgeToolTipText tooltip.text: networkConnectionStore.sendBuyBridgeToolTipText
visible: !walletStore.overview.isWatchOnlyAccount && !root.isCommunityOwnershipTransfer && walletStore.overview.canSend visible: !walletStore.overview.isWatchOnlyAccount && !root.isCommunityOwnershipTransfer && walletStore.overview.canSend
} }

View File

@ -1,62 +0,0 @@
import QtQuick 2.15
import StatusQ.Controls 0.1
Item {
id: root
property string aliasedObjectName
property string text
property string icon
property alias tooltipText: tooltip.text
property int buttonType: DisabledTooltipButton.Normal
property bool interactive: true
property Component buttonComponent: buttonType === DisabledTooltipButton.Normal ? normalButton : flatButton
signal clicked()
enum Type {
Normal, // 0
Flat // 1
}
implicitWidth: !!buttonLoader.item ? buttonLoader.item.width : 0
implicitHeight: !!buttonLoader.item ? buttonLoader.item.height : 0
Loader {
id: buttonLoader
anchors.centerIn: parent
sourceComponent: buttonComponent
active: root.visible
}
HoverHandler {
id: hoverHandler
enabled: !root.interactive
cursorShape: Qt.PointingHandCursor
}
StatusToolTip {
id: tooltip
visible: hoverHandler.hovered && !!text
offset: -(tooltip.x + tooltip.width/2 - root.width/2)
}
Component{
id: flatButton
StatusFlatButton {
objectName: root.aliasedObjectName
icon.name: root.icon
text: root.text
enabled: root.interactive
onClicked: root.clicked()
}
}
Component{
id: normalButton
StatusButton {
objectName: root.aliasedObjectName
icon.name: root.icon
text: root.text
enabled: root.interactive
onClicked: root.clicked()
}
}
}

View File

@ -9,7 +9,6 @@ CopyButton 1.0 CopyButton.qml
CopyButtonWithCircle 1.0 CopyButtonWithCircle.qml CopyButtonWithCircle 1.0 CopyButtonWithCircle.qml
CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml CopyToClipBoardButton 1.0 CopyToClipBoardButton.qml
CurrencyAmountInput 1.0 CurrencyAmountInput.qml CurrencyAmountInput 1.0 CurrencyAmountInput.qml
DisabledTooltipButton 1.0 DisabledTooltipButton.qml
EmojiHash 1.0 EmojiHash.qml EmojiHash 1.0 EmojiHash.qml
EmptyShapeRectangleFooterListView 1.0 EmptyShapeRectangleFooterListView.qml EmptyShapeRectangleFooterListView 1.0 EmptyShapeRectangleFooterListView.qml
ErrorDetails 1.0 ErrorDetails.qml ErrorDetails 1.0 ErrorDetails.qml

View File

@ -18,8 +18,7 @@ Rectangle {
property bool saveChangesButtonEnabled: false property bool saveChangesButtonEnabled: false
property bool saveForLaterButtonVisible property bool saveForLaterButtonVisible
property alias saveChangesText: saveChangesButton.text property alias saveChangesText: saveChangesButton.text
property alias saveChangesTooltipText: saveChangesButton.tooltipText property string saveChangesTooltipText
property alias saveChangesTooltipVisible: saveChangesButton.enabled
property alias saveForLaterText: saveForLaterButton.text property alias saveForLaterText: saveForLaterButton.text
property alias cancelChangesText: cancelChangesButton.text property alias cancelChangesText: cancelChangesButton.text
property alias changesDetectedText: changesDetectedTextItem.text property alias changesDetectedText: changesDetectedTextItem.text
@ -148,13 +147,12 @@ Rectangle {
onClicked: root.saveForLaterClicked() onClicked: root.saveForLaterClicked()
} }
DisabledTooltipButton { StatusButton {
id: saveChangesButton id: saveChangesButton
objectName: "settingsDirtyToastMessageSaveButton" objectName: "settingsDirtyToastMessageSaveButton"
buttonType: DisabledTooltipButton.Normal
text: root.defaultSaveChangesText text: root.defaultSaveChangesText
enabled: root.active && root.saveChangesButtonEnabled
interactive: root.active && root.saveChangesButtonEnabled interactive: root.active && root.saveChangesButtonEnabled
tooltip.text: root.saveChangesTooltipText
onClicked: root.saveChangesClicked() onClicked: root.saveChangesClicked()
} }
} }

View File

@ -123,7 +123,8 @@ Pane {
objectName: "editProfileButton" objectName: "editProfileButton"
size: StatusButton.Size.Small size: StatusButton.Size.Small
text: qsTr("Edit Profile") text: qsTr("Edit Profile")
enabled: !root.readOnly interactive: !root.readOnly
tooltip.text: interactive ? "" : qsTr("Not available in preview mode")
onClicked: { onClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile) Global.changeAppSectionBySectionType(Constants.appSection.profile)
root.closeRequested() root.closeRequested()
@ -319,15 +320,6 @@ Pane {
Loader { Loader {
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
Layout.preferredHeight: menuButton.visible ? menuButton.height : -1 Layout.preferredHeight: menuButton.visible ? menuButton.height : -1
HoverHandler {
id: actionButtonHoverHandler
enabled: root.readOnly
}
StatusToolTip {
text: qsTr("Not available in preview mode")
visible: actionButtonHoverHandler.hovered
}
sourceComponent: { sourceComponent: {
// current user // current user