diff --git a/sandbox/main.qml b/sandbox/main.qml index f80da55a..c436414e 100644 --- a/sandbox/main.qml +++ b/sandbox/main.qml @@ -300,12 +300,16 @@ StatusWindow { selected: viewLoader.source.toString().includes(title) onClicked: mainPageView.page(title); } - StatusNavigationListItem { title: "StatusModal" selected: viewLoader.source.toString().includes("Popups") onClicked: mainPageView.control("Popups"); } + StatusNavigationListItem { + title: "StatusDialog" + selected: viewLoader.source.toString().includes(title) + onClicked: mainPageView.page(title); + } StatusListSectionHeadline { text: "StatusQ.Platform" } StatusNavigationListItem { title: "StatusMacNotification" diff --git a/sandbox/pages/StatusDialogPage.qml b/sandbox/pages/StatusDialogPage.qml new file mode 100644 index 00000000..04bdd40f --- /dev/null +++ b/sandbox/pages/StatusDialogPage.qml @@ -0,0 +1,282 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQml.Models 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Popups.Dialog 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 + +ColumnLayout { + id: root + + StatusButton { + text: "Content" + onClicked: contentDialog.open() + + StatusDialog { + id: contentDialog + + standardButtons: Dialog.ApplyRole + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } + + StatusButton { + text: "Title and content" + onClicked: titleContentDialog.open() + + StatusDialog { + id: titleContentDialog + title: "Title" + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } + + StatusButton { + text: "No buttons" + onClicked: noButtonsDialog.open() + + StatusDialog { + id: noButtonsDialog + title: "No buttons" + + standardButtons: Dialog.NoButton + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } + + StatusButton { + text: "Long title" + onClicked: longTitleDialog.open() + + StatusDialog { + id: longTitleDialog + title: "Long title long title Long title long title Long title long title" + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } + + StatusButton { + text: "Long title elided" + onClicked: longTitleElidedDialog.open() + + StatusDialog { + id: longTitleElidedDialog + title: "Long title long title Long title long title Long title long title" + + width: 400 + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } + + StatusButton { + text: "Title subtitle" + onClicked: titleSubtitleDialog.open() + + StatusDialog { + id: titleSubtitleDialog + title: "This title will be ignored" + + header: StatusDialogHeader { + headline.title: "Title" + headline.subtitle: "Subtitle" + + actions.closeButton.onClicked: titleSubtitleDialog.close() + } + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } + + StatusButton { + text: "Auto adjustable" + onClicked: autoAdjustableDialog.open() + + StatusDialog { + id: autoAdjustableDialog + + header: StatusDialogHeader { + headline.title: "Dialog size will auto adapt" + headline.subtitle: "To conent size" + + actions.closeButton.onClicked: autoAdjustableDialog.close() + } + + footer: StatusDialogFooter { + leftButtons: ObjectModel { + StatusRoundButton { + icon.name: "arrow-left" + onClicked: autoAdjustableDialogContent.implicitWidth -= 100 + } + StatusRoundButton { + icon.name: "arrow-right" + onClicked: autoAdjustableDialogContent.implicitWidth += 100 + } + } + rightButtons: ObjectModel { + StatusRoundButton { + icon.name: "arrow-down" + onClicked: autoAdjustableDialogContent.implicitHeight -= 100 + } + StatusRoundButton { + icon.name: "arrow-up" + onClicked: autoAdjustableDialogContent.implicitHeight += 100 + } + } + } + + Rectangle { + id: autoAdjustableDialogContent + + anchors.fill: parent + + implicitWidth: 200 + implicitHeight: 200 + + color: Theme.palette.primaryColor3 + + StatusBaseText { + anchors.centerIn: parent + text: "W: %1 H: %2\nIW: %3 IH: %4".arg(parent.width).arg(parent.height).arg(parent.implicitWidth).arg(parent.implicitHeight) + } + } + } + } + + StatusButton { + text: "Complex dialog" + onClicked: complexDialog.open() + + StatusDialog { + id: complexDialog + + header: StatusDialogHeader { + id: dialogHeader + + headline.title: "Complex dialog" + headline.subtitle: "identicon, title subtitle, custom actions" + + leftComponent: StatusLetterIdenticon { + name: dialogHeader.headline.title + } + + actions { + infoButton.visible: true + + customButtons: ObjectModel { + StatusFlatRoundButton { + icon.name: "warning" + icon.width: 20 + icon.height: 20 + } + } + + closeButton.onClicked: complexDialog.close() + } + } + + footer: StatusDialogFooter { + leftButtons: ObjectModel { + StatusRoundButton { + icon.name: "arrow-left" + } + } + rightButtons: ObjectModel { + StatusButton { + text: "Custom cancel action" + type: StatusButton.Danger + } + StatusButton { + text: "Custom approve action" + } + } + } + + ColumnLayout { + anchors.fill: parent + + StatusBaseText { + text: "Content A" + } + + StatusBaseText { + text: "Content B" + } + + StatusBaseText { + text: "Content C" + } + } + } + } + + StatusButton { + text: "Custom header/footer" + onClicked: customHeaderAndFooter.open() + + StatusDialog { + id: customHeaderAndFooter + title: "No buttons" + + header: Rectangle { + implicitHeight: customHeaderText.height * 2 + implicitWidth: customHeaderText.width * 2 + + border.width: 2 + color: Theme.palette.statusPopupMenu.hoverBackgroundColor + + StatusBaseText { + id: customHeaderText + anchors.centerIn: parent + text: "Custom header" + } + } + + footer: Rectangle { + implicitHeight: customHeaderText.height * 2 + implicitWidth: customHeaderText.width * 2 + + border.width: 2 + color: Theme.palette.statusPopupMenu.hoverBackgroundColor + + StatusBaseText { + id: customFooter + anchors.centerIn: parent + text: "Custom footer" + } + } + + + StatusBaseText { + anchors.fill: parent + text: "Content" + } + } + } +} diff --git a/src/StatusQ/Popups/Dialog/StatusDialog.qml b/src/StatusQ/Popups/Dialog/StatusDialog.qml new file mode 100644 index 00000000..ffd3a717 --- /dev/null +++ b/src/StatusQ/Popups/Dialog/StatusDialog.qml @@ -0,0 +1,104 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQml.Models 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 + +Dialog { + id: root + + anchors.centerIn: Overlay.overlay + + padding: 16 + margins: 64 + modal: true + + standardButtons: Dialog.Cancel | Dialog.Ok + + Overlay.modal: Rectangle { + color: Theme.palette.backdropColor + } + + background: Rectangle { + color: Theme.palette.statusModal.backgroundColor + radius: 8 + } + + header: StatusDialogHeader { + visible: root.title + headline.title: root.title + actions.closeButton.onClicked: root.close() + } + + footer: StatusDialogFooter { + id: footerItem + + readonly property int rejectRoleFlags: Dialog.Cancel | Dialog.Close | Dialog.Abort + readonly property int noRoleFlags: Dialog.No | Dialog.NoToAll + readonly property int acceptRoleFlags: Dialog.Ok | Dialog.Open | Dialog.Save | Dialog.SaveAll | Dialog.Retry | Dialog.Ignore + readonly property int yesRoleFlags: Dialog.Yes | Dialog.YesToAll + + visible: rightButtons && + root.standardButtons & (rejectRoleFlags | noRoleFlags | acceptRoleFlags | yesRoleFlags | Dialog.ApplyRole) + + rightButtons: ObjectModel { + StatusButton { + visible: root.standardButtons & footerItem.rejectRoleFlags + type: StatusButton.Danger + text: { + if (root.standardButtons & Dialog.Close) return qsTr("Close") + if (root.standardButtons & Dialog.Abort) return qsTr("Abort") + return qsTr("Cancel") + } + + onClicked: root.reject() + } + + StatusButton { + visible: root.standardButtons & footerItem.noRoleFlags + type: StatusButton.Danger + text: { + if (root.standardButtons & Dialog.NoToAll) return qsTr("No to all") + return qsTr("No") + } + + onClicked: root.reject() + } + + StatusButton { + visible: root.standardButtons & footerItem.acceptRoleFlags + text: { + if (root.standardButtons & Dialog.Open) return qsTr("Open") + if (root.standardButtons & Dialog.Save) return qsTr("Save") + if (root.standardButtons & Dialog.SaveAll) return qsTr("Save all") + if (root.standardButtons & Dialog.Retry) return qsTr("Retry") + if (root.standardButtons & Dialog.Ignore) return qsTr("Ignore") + return qsTr("Ok") + } + + onClicked: root.accept() + } + + StatusButton { + visible: root.standardButtons & footerItem.yesRoleFlags + type: StatusButton.Danger + text: { + if (root.standardButtons & Dialog.YesToAll) return qsTr("Yes to all") + return qsTr("Yes") + } + + onClicked: root.accept() + } + + StatusButton { + visible: root.standardButtons & Dialog.ApplyRole + text: qsTr("Apply") + + onClicked: root.applied() + } + } + } +} diff --git a/src/StatusQ/Popups/Dialog/StatusDialogDivider.qml b/src/StatusQ/Popups/Dialog/StatusDialogDivider.qml new file mode 100644 index 00000000..bc15a91a --- /dev/null +++ b/src/StatusQ/Popups/Dialog/StatusDialogDivider.qml @@ -0,0 +1,8 @@ +import QtQuick 2.14 + +import StatusQ.Core.Theme 0.1 + +Rectangle { + color: Theme.palette.baseColor2 + implicitHeight: 1 +} diff --git a/src/StatusQ/Popups/Dialog/StatusDialogFooter.qml b/src/StatusQ/Popups/Dialog/StatusDialogFooter.qml new file mode 100644 index 00000000..e650e0d7 --- /dev/null +++ b/src/StatusQ/Popups/Dialog/StatusDialogFooter.qml @@ -0,0 +1,48 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 +import QtQml.Models 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +Rectangle { + id: root + + property ObjectModel leftButtons + property ObjectModel rightButtons + + color: Theme.palette.statusModal.backgroundColor + radius: 8 + + implicitHeight: layout.implicitHeight + layout.anchors.topMargin + layout.anchors.bottomMargin + implicitWidth: layout.implicitWidth + layout.anchors.leftMargin + layout.anchors.rightMargin + + RowLayout { + id: layout + + clip: true + + anchors { + fill: parent + margins: 16 + } + + Repeater { + model: root.leftButtons + onItemAdded: item.Layout.fillHeight = true + } + + Item { + Layout.fillWidth: true + } + + Repeater { + model: root.rightButtons + onItemAdded: item.Layout.fillHeight = true + } + } + + StatusDialogDivider { + width: parent.width + } +} diff --git a/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml b/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml new file mode 100644 index 00000000..91156524 --- /dev/null +++ b/src/StatusQ/Popups/Dialog/StatusDialogHeader.qml @@ -0,0 +1,58 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +Rectangle { + id: root + + readonly property alias headline: headline + readonly property alias actions: actions + + property alias leftComponent: leftComponentLoader.sourceComponent + + color: Theme.palette.statusModal.backgroundColor + radius: 8 + + implicitHeight: layout.implicitHeight + layout.anchors.topMargin + layout.anchors.bottomMargin + implicitWidth: layout.implicitWidth + layout.anchors.leftMargin + layout.anchors.rightMargin + + RowLayout { + id: layout + + clip: true + + anchors { + fill: parent + margins: 16 + } + + spacing: 8 + + Loader { + id: leftComponentLoader + + Layout.fillHeight: true + visible: sourceComponent + } + + StatusTitleSubtitle { + id: headline + + Layout.fillWidth: true + Layout.fillHeight: true + } + + StatusHeaderActions { + id: actions + + Layout.alignment: Qt.AlignTop + } + } + + StatusDialogDivider { + anchors.bottom: parent.bottom + width: parent.width + } +} diff --git a/src/StatusQ/Popups/Dialog/StatusHeaderActions.qml b/src/StatusQ/Popups/Dialog/StatusHeaderActions.qml new file mode 100644 index 00000000..05be23fd --- /dev/null +++ b/src/StatusQ/Popups/Dialog/StatusHeaderActions.qml @@ -0,0 +1,70 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 +import QtQml.Models 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 + +Item { + id: root + + property ObjectModel customButtons + + readonly property alias closeButton: closeButton + readonly property alias infoButton: infoButton + + implicitHeight: layout.implicitHeight + implicitWidth: layout.implicitWidth + + QtObject { + id: d + readonly property int buttonSize: 32 + readonly property int iconSize: 20 + } + + RowLayout { + id: layout + + anchors.fill: parent + + Repeater { + model: root.customButtons + onItemAdded: { + item.Layout.fillHeight = true + item.Layout.preferredHeight = d.buttonSize + item.Layout.preferredWidth = d.buttonSize + } + } + + StatusFlatRoundButton { + id: infoButton + + Layout.fillHeight: true + Layout.preferredHeight: d.buttonSize + Layout.preferredWidth: d.buttonSize + + visible: false + + type: StatusFlatRoundButton.Type.Secondary + icon.name: "info" + icon.color: Theme.palette.directColor1 + icon.width: d.iconSize + icon.height: d.iconSize + } + + StatusFlatRoundButton { + id: closeButton + + Layout.fillHeight: true + Layout.preferredHeight: d.buttonSize + Layout.preferredWidth: d.buttonSize + + type: StatusFlatRoundButton.Type.Secondary + icon.name: "close" + icon.color: Theme.palette.directColor1 + icon.width: d.iconSize + icon.height: d.iconSize + } + } +} diff --git a/src/StatusQ/Popups/Dialog/StatusTitleSubtitle.qml b/src/StatusQ/Popups/Dialog/StatusTitleSubtitle.qml new file mode 100644 index 00000000..92fa044a --- /dev/null +++ b/src/StatusQ/Popups/Dialog/StatusTitleSubtitle.qml @@ -0,0 +1,50 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +Item { + id: root + + property alias title: title.text + property alias subtitle: subtitle.text + + implicitHeight: layout.implicitHeight + implicitWidth: layout.implicitWidth + + ColumnLayout { + id: layout + + anchors.fill: parent + spacing: 0 + + StatusBaseText { + id: title + + Layout.fillWidth: true + + color: Theme.palette.directColor1 + font { + family: Theme.palette.baseFont.name + pixelSize: 17 + bold: true + } + elide: Text.ElideMiddle + } + + StatusBaseText { + id: subtitle + + Layout.fillWidth: true + + visible: text !== "" + color: Theme.palette.baseColor1 + font { + family: Theme.palette.baseFont.name + pixelSize: 15 + } + elide: Text.ElideMiddle + } + } +} diff --git a/src/StatusQ/Popups/Dialog/qmldir b/src/StatusQ/Popups/Dialog/qmldir new file mode 100644 index 00000000..f06e149e --- /dev/null +++ b/src/StatusQ/Popups/Dialog/qmldir @@ -0,0 +1,8 @@ +module StatusQ.Popups.Dialog + +StatusDialog 0.1 StatusDialog.qml +StatusDialogDivider 0.1 StatusDialogDivider.qml +StatusDialogFooter 0.1 StatusDialogFooter.qml +StatusDialogHeader 0.1 StatusDialogHeader.qml +StatusHeaderActions 0.1 StatusHeaderActions.qml +StatusTitleSubtitle 0.1 StatusTitleSubtitle.qml diff --git a/src/StatusQ/Popups/StatusModal.qml b/src/StatusQ/Popups/StatusModal.qml index 0cb83606..ae0c144a 100644 --- a/src/StatusQ/Popups/StatusModal.qml +++ b/src/StatusQ/Popups/StatusModal.qml @@ -50,6 +50,9 @@ import "statusModal" as Spares For a list of components available see StatusQ. */ + +// Deprecation annotations come with Qt6.2 +// @Deprecated { reason: "Use StatusDialog instead, see reasoning: https://github.com/status-im/StatusQ/issues/720" } QC.Popup { id: statusModal diff --git a/src/StatusQ/Popups/qmldir b/src/StatusQ/Popups/qmldir index 381befc9..02ceb60b 100644 --- a/src/StatusQ/Popups/qmldir +++ b/src/StatusQ/Popups/qmldir @@ -7,6 +7,7 @@ StatusMenuItemDelegate 0.1 StatusMenuItemDelegate.qml StatusMenuHeadline 0.1 StatusMenuHeadline.qml StatusModal 0.1 StatusModal.qml StatusStackModal 0.1 StatusStackModal.qml +StatusDialog 0.1 StatusDialog.qml StatusSearchPopup 0.1 StatusSearchPopup.qml StatusModalDivider 0.1 StatusModalDivider.qml StatusSearchPopupMenuItem 0.1 StatusSearchPopupMenuItem.qml diff --git a/statusq.qrc b/statusq.qrc index e553be83..d85db052 100644 --- a/statusq.qrc +++ b/statusq.qrc @@ -10502,5 +10502,12 @@ src/StatusQ/Animations/SkeletonGradientStop.qml src/StatusQ/Animations/qmldir src/assets/img/icons/arrow-up.svg + src/StatusQ/Popups/Dialog/qmldir + src/StatusQ/Popups/Dialog/StatusDialog.qml + src/StatusQ/Popups/Dialog/StatusTitleSubtitle.qml + src/StatusQ/Popups/Dialog/StatusHeaderActions.qml + src/StatusQ/Popups/Dialog/StatusDialogHeader.qml + src/StatusQ/Popups/Dialog/StatusDialogFooter.qml + src/StatusQ/Popups/Dialog/StatusDialogDivider.qml