mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
systemdialog: Update to match plasma-workspace implementation, and simplify UI
SystemDialog was added in 2021 and did not receive any testing while the API was in flux when portals started using it. Update the implementation to match plasma-workspace now that the API has settled. Also simplify the UI, remove automatic scrollview and other loaders that make initialization very complicated. Fixes https://invent.kde.org/plasma/plasma-mobile/-/issues/301
This commit is contained in:
parent
98f925b5f9
commit
8f39d156f1
3 changed files with 109 additions and 515 deletions
|
|
@ -1,74 +1,35 @@
|
||||||
/*
|
// SPDX-FileCopyrightText: 2021-2024 Devin Lin <devin@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls as QQC2
|
||||||
import QtQuick.Templates as T
|
import QtQuick.Layouts
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Window
|
||||||
import QtQuick.Window 2.15
|
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import org.kde.kirigami 2.18 as Kirigami
|
import QtQuick.Templates as T
|
||||||
import "private" as Private
|
import org.kde.kirigami as Kirigami
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
// -- public API: should match plasma-workspace implementation --
|
||||||
|
|
||||||
default property Item mainItem
|
default property Item mainItem
|
||||||
|
|
||||||
/**
|
|
||||||
* Title of the dialog.
|
|
||||||
*/
|
|
||||||
property string mainText: ""
|
property string mainText: ""
|
||||||
|
|
||||||
/**
|
|
||||||
* Subtitle of the dialog.
|
|
||||||
*/
|
|
||||||
property string subtitle: ""
|
property string subtitle: ""
|
||||||
|
|
||||||
/**
|
|
||||||
* This property holds the default padding of the content.
|
|
||||||
*/
|
|
||||||
property real padding: Kirigami.Units.smallSpacing
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This property holds the left padding of the content. If not specified, it uses `padding`.
|
|
||||||
*/
|
|
||||||
property real leftPadding: padding
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This property holds the right padding of the content. If not specified, it uses `padding`.
|
|
||||||
*/
|
|
||||||
property real rightPadding: padding
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This property holds the top padding of the content. If not specified, it uses `padding`.
|
|
||||||
*/
|
|
||||||
property real topPadding: padding
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This property holds the bottom padding of the content. If not specified, it uses `padding`.
|
|
||||||
*/
|
|
||||||
property real bottomPadding: padding
|
|
||||||
property alias standardButtons: footerButtonBox.standardButtons
|
|
||||||
|
|
||||||
readonly property int flags: Qt.FramelessWindowHint | Qt.Dialog
|
|
||||||
readonly property real dialogCornerRadius: Kirigami.Units.smallSpacing * 2
|
|
||||||
property list<T.Action> actions
|
|
||||||
property string iconName
|
property string iconName
|
||||||
|
property list<T.Action> actions
|
||||||
|
readonly property alias dialogButtonBox: footerButtonBox
|
||||||
|
|
||||||
implicitWidth: loader.implicitWidth
|
required property Window window
|
||||||
implicitHeight: loader.implicitHeight
|
|
||||||
|
|
||||||
readonly property real minimumHeight: implicitWidth
|
readonly property real minimumHeight: implicitWidth
|
||||||
readonly property real minimumWidth: implicitHeight
|
readonly property real minimumWidth: implicitHeight
|
||||||
|
readonly property int flags: Qt.FramelessWindowHint | Qt.Dialog
|
||||||
required property Kirigami.AbstractApplicationWindow window
|
property var standardButtons // footerButtonBox standardButtons
|
||||||
|
readonly property int spacing: Kirigami.Units.gridUnit
|
||||||
|
|
||||||
function present() {
|
function present() {
|
||||||
window.showFullScreen()
|
window.showMaximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowChanged: {
|
onWindowChanged: {
|
||||||
|
|
@ -77,250 +38,128 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load in async to speed up load times (especially on embedded devices)
|
Item {
|
||||||
Loader {
|
id: windowItem
|
||||||
id: loader
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
asynchronous: true
|
// margins for shadow
|
||||||
|
implicitWidth: Math.min(Screen.width, control.implicitWidth + 2 * Kirigami.Units.gridUnit)
|
||||||
|
implicitHeight: Math.min(Screen.height, control.implicitHeight + 2 * Kirigami.Units.gridUnit)
|
||||||
|
|
||||||
sourceComponent: Item {
|
// shadow
|
||||||
// margins for shadow
|
RectangularGlow {
|
||||||
implicitWidth: Math.min(Screen.width, control.implicitWidth + 2 * Kirigami.Units.gridUnit)
|
id: glow
|
||||||
implicitHeight: Math.min(Screen.height, control.implicitHeight + 2 * Kirigami.Units.gridUnit)
|
anchors.topMargin: 1
|
||||||
|
anchors.fill: control
|
||||||
// shadow
|
cached: true
|
||||||
RectangularGlow {
|
glowRadius: 2
|
||||||
id: glow
|
cornerRadius: Kirigami.Units.gridUnit
|
||||||
anchors.topMargin: 1
|
spread: 0.1
|
||||||
anchors.fill: control
|
color: Qt.rgba(0, 0, 0, 0.4)
|
||||||
cached: true
|
|
||||||
glowRadius: 2
|
|
||||||
cornerRadius: Kirigami.Units.gridUnit
|
|
||||||
spread: 0.1
|
|
||||||
color: Qt.rgba(0, 0, 0, 0.4)
|
|
||||||
}
|
|
||||||
|
|
||||||
// actual window
|
|
||||||
Control {
|
|
||||||
id: control
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: glow.cornerRadius
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
leftPadding: 0
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
Rectangle { // border
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: -1
|
|
||||||
radius: dialogCornerRadius + 1
|
|
||||||
color: Qt.darker(Kirigami.Theme.backgroundColor, 1.5)
|
|
||||||
}
|
|
||||||
Rectangle { // background colour
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: dialogCornerRadius
|
|
||||||
color: Kirigami.Theme.backgroundColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contentItem: column
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
readonly property var contents: ColumnLayout {
|
// actual window
|
||||||
id: column
|
QQC2.Control {
|
||||||
spacing: 0
|
id: control
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: glow.cornerRadius
|
||||||
|
topPadding: root.spacing
|
||||||
|
bottomPadding: root.spacing
|
||||||
|
rightPadding: root.spacing
|
||||||
|
leftPadding: root.spacing
|
||||||
|
|
||||||
// header
|
implicitWidth: Kirigami.Units.gridUnit * 22
|
||||||
Control {
|
|
||||||
id: headerControl
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
background: Item {
|
||||||
Layout.maximumWidth: root.window.maximumWidth
|
Rectangle { // border
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: -1
|
||||||
|
radius: Kirigami.Units.largeSpacing + 1
|
||||||
|
color: Qt.darker(Kirigami.Theme.backgroundColor, 1.5)
|
||||||
|
}
|
||||||
|
Rectangle { // background colour
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: Kirigami.Units.largeSpacing
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
topPadding: 0
|
contentItem: ColumnLayout {
|
||||||
leftPadding: 0
|
id: column
|
||||||
rightPadding: 0
|
spacing: 0
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
background: Item {}
|
// header
|
||||||
|
|
||||||
contentItem: RowLayout {
|
|
||||||
Kirigami.Heading {
|
Kirigami.Heading {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
Layout.maximumWidth: root.window.maximumWidth
|
||||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
level: 3
|
||||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
font.weight: Font.Bold
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
level: 2
|
|
||||||
text: root.mainText
|
text: root.mainText
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// content
|
QQC2.Label {
|
||||||
Control {
|
|
||||||
id: content
|
|
||||||
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: root.window.maximumWidth
|
|
||||||
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
background: Item {}
|
|
||||||
contentItem: ColumnLayout {
|
|
||||||
spacing: 0
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Label {
|
|
||||||
id: subtitleLabel
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
Layout.bottomMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.leftMargin: Kirigami.Units.gridUnit * 3
|
Layout.maximumWidth: root.window.maximumWidth
|
||||||
Layout.rightMargin: Kirigami.Units.gridUnit * 3
|
Layout.fillWidth: true
|
||||||
visible: root.subtitle !== ""
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: root.subtitle
|
text: root.subtitle
|
||||||
wrapMode: Label.Wrap
|
visible: text.length > 0
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// separator when scrolling
|
Kirigami.Icon {
|
||||||
Kirigami.Separator {
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
Layout.fillWidth: true
|
Layout.alignment: Qt.AlignCenter
|
||||||
opacity: root.mainItem && contentControl.flickableItem && contentControl.flickableItem.contentY !== 0 ? 1 : 0 // always maintain same height (as opposed to visible)
|
source: root.iconName
|
||||||
|
implicitWidth: Kirigami.Units.iconSizes.large
|
||||||
|
implicitHeight: Kirigami.Units.iconSizes.large
|
||||||
}
|
}
|
||||||
|
|
||||||
// mainItem is in scrollview, in case of overflow
|
// content
|
||||||
Private.ScrollView {
|
QQC2.Control {
|
||||||
id: contentControl
|
id: content
|
||||||
clip: true
|
|
||||||
|
|
||||||
// we cannot have contentItem inside a sub control (allowing for content padding within the scroll area),
|
|
||||||
// because if the contentItem is a Flickable (ex. ListView), the ScrollView needs it to be top level in order
|
|
||||||
// to decorate it
|
|
||||||
contentItem: root.mainItem
|
|
||||||
canFlickWithMouse: true
|
|
||||||
|
|
||||||
// ensure window colour scheme, and background color
|
|
||||||
Kirigami.Theme.inherit: false
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
|
||||||
|
|
||||||
// needs to explicitly be set for each side to work
|
|
||||||
leftPadding: root.leftPadding; topPadding: root.topPadding
|
|
||||||
rightPadding: root.rightPadding; bottomPadding: root.bottomPadding
|
|
||||||
|
|
||||||
// height of everything else in the dialog other than the content
|
|
||||||
property real otherHeights: headerControl.height + subtitleLabel.height + footerButtonBox.height + root.topPadding + root.bottomPadding;
|
|
||||||
|
|
||||||
property real calculatedMaximumWidth: root.window.maximumWidth > root.absoluteMaximumWidth ? root.absoluteMaximumWidth : root.window.maximumWidth
|
|
||||||
property real calculatedMaximumHeight: root.window.maximumHeight > root.absoluteMaximumHeight ? root.absoluteMaximumHeight : root.window.maximumHeight
|
|
||||||
property real calculatedImplicitWidth: root.mainItem ? (root.mainItem.implicitWidth ? root.mainItem.implicitWidth : root.mainItem.width) + root.leftPadding + root.rightPadding : 0
|
|
||||||
property real calculatedImplicitHeight: root.mainItem ? (root.mainItem.implicitHeight ? root.mainItem.implicitHeight : root.mainItem.height) + root.topPadding + root.bottomPadding : 0
|
|
||||||
|
|
||||||
// don't enforce preferred width and height if not set
|
|
||||||
Layout.preferredWidth: root.preferredWidth >= 0 ? root.preferredWidth : calculatedImplicitWidth + contentControl.rightSpacing
|
|
||||||
Layout.preferredHeight: root.preferredHeight >= 0 ? root.preferredHeight - otherHeights : calculatedImplicitHeight + contentControl.bottomSpacing
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.maximumWidth: calculatedMaximumWidth
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: calculatedMaximumHeight >= otherHeights ? calculatedMaximumHeight - otherHeights : 0 // we enforce maximum height solely from the content
|
Layout.topMargin: Kirigami.Units.gridUnit
|
||||||
|
Layout.maximumWidth: root.window.maximumWidth
|
||||||
|
|
||||||
// give an implied width and height to the contentItem so that features like word wrapping/eliding work
|
leftPadding: 0
|
||||||
// cannot placed directly in contentControl as a child, so we must use a property
|
rightPadding: 0
|
||||||
property var widthHint: Binding {
|
topPadding: 0
|
||||||
target: root.mainItem
|
bottomPadding: 0
|
||||||
property: "width"
|
|
||||||
// we want to avoid horizontal scrolling, so we apply maximumWidth as a hint if necessary
|
|
||||||
property real preferredWidthHint: contentControl.width - root.leftPadding - root.rightPadding - contentControl.rightSpacing
|
|
||||||
property real maximumWidthHint: contentControl.calculatedMaximumWidth - root.leftPadding - root.rightPadding - contentControl.rightSpacing
|
|
||||||
value: maximumWidthHint < preferredWidthHint ? maximumWidthHint : preferredWidthHint
|
|
||||||
}
|
|
||||||
property var heightHint: Binding {
|
|
||||||
target: root.mainItem
|
|
||||||
property: "height"
|
|
||||||
// we are okay with overflow, if it exceeds maximumHeight we will allow scrolling
|
|
||||||
value: contentControl.Layout.preferredHeight - root.topPadding - root.bottomPadding - contentControl.bottomSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
// give explicit warnings since the maximumHeight is ignored when negative, so developers aren't confused
|
contentItem: root.mainItem
|
||||||
Component.onCompleted: {
|
background: Item {}
|
||||||
if (contentControl.Layout.maximumHeight < 0 || contentControl.Layout.maximumHeight === Infinity) {
|
}
|
||||||
console.log("Dialog Warning: the calculated maximumHeight for the content is " + contentControl.Layout.maximumHeight + ", ignoring...");
|
|
||||||
|
QQC2.DialogButtonBox {
|
||||||
|
id: footerButtonBox
|
||||||
|
// ensure we never have no buttons, we always must have the cancel button available
|
||||||
|
standardButtons: (root.standardButtons === QQC2.DialogButtonBox.NoButton) ? QQC2.DialogButtonBox.Cancel : root.standardButtons
|
||||||
|
|
||||||
|
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: root.window.maximumWidth
|
||||||
|
leftPadding: 0
|
||||||
|
rightPadding: 0
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
onAccepted: root.window.accept()
|
||||||
|
onRejected: root.window.reject()
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.actions
|
||||||
|
delegate: QQC2.Button {
|
||||||
|
action: modelData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Control {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
contentItem: footerButtonBox
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property DialogButtonBox dialogButtonBox: DialogButtonBox {
|
|
||||||
//We want to report the same width on all so the button area is split equally
|
|
||||||
id: footerButtonBox
|
|
||||||
Layout.fillWidth: true
|
|
||||||
onAccepted: root.window.accept()
|
|
||||||
onRejected: root.window.reject()
|
|
||||||
implicitHeight: contentItem.implicitHeight
|
|
||||||
alignment: undefined
|
|
||||||
|
|
||||||
readonly property real sameWidth: 50
|
|
||||||
delegate: Private.MobileSystemDialogButton {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredWidth: footerButtonBox.sameWidth
|
|
||||||
|
|
||||||
readonly property point globalPos: root.window.visible ? mapToItem(footerButtonBox, Qt.point(x, y)) : Qt.point(0, 0)
|
|
||||||
verticalSeparator: globalPos.x > 0 && root.window.layout === Qt.Horizontal
|
|
||||||
horizontalSeparator: true
|
|
||||||
corners.bottomLeftRadius: verticalSeparator ? 0 : root.dialogCornerRadius
|
|
||||||
corners.bottomRightRadius: globalPos.x >= footerButtonBox.width ? root.dialogCornerRadius : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
leftPadding: 0
|
|
||||||
rightPadding: 0
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
|
|
||||||
contentItem: GridLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
|
|
||||||
rowSpacing: 0
|
|
||||||
columnSpacing: 0
|
|
||||||
rows: root.window.layout === Qt.Vertical ? Number.MAX_VALUE : 1
|
|
||||||
columns: root.window.layout !== Qt.Vertical ? Number.MAX_VALUE : 1
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.actions
|
|
||||||
delegate: Private.MobileSystemDialogButton {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredWidth: footerButtonBox.sameWidth
|
|
||||||
readonly property point globalPos: root.window.visible ? mapToItem(footerButtonBox, Qt.point(x, y)) : Qt.point(0, 0)
|
|
||||||
verticalSeparator: globalPos.x > 0 && root.window.layout === Qt.Horizontal
|
|
||||||
horizontalSeparator: true
|
|
||||||
corners.bottomLeftRadius: model.index === root.actions.length - 1 ? root.dialogCornerRadius : 0
|
|
||||||
corners.bottomRightRadius: model.index === root.actions.length - 1 && footerButtonBox.standardButtons === 0 ? root.dialogCornerRadius : 0
|
|
||||||
action: modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import QtQuick 2.15
|
|
||||||
import QtQuick.Controls 2.15
|
|
||||||
import QtQuick.Layouts 1.15
|
|
||||||
import QtQuick.Window 2.2
|
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import org.kde.kirigami 2.18 as Kirigami
|
|
||||||
|
|
||||||
AbstractButton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property alias corners: background.corners
|
|
||||||
|
|
||||||
property bool verticalSeparator: false
|
|
||||||
property bool horizontalSeparator: false
|
|
||||||
|
|
||||||
background: Kirigami.ShadowedRectangle {
|
|
||||||
id: background
|
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
|
||||||
Kirigami.Theme.inherit: false
|
|
||||||
color: {
|
|
||||||
if (root.down) {
|
|
||||||
let avg = (Kirigami.Theme.backgroundColor.r + Kirigami.Theme.backgroundColor.g + Kirigami.Theme.backgroundColor.b) / 3;
|
|
||||||
// sample down
|
|
||||||
avg = Math.max(0, (avg - 0.8) * 5);
|
|
||||||
return Qt.darker(Kirigami.Theme.backgroundColor, 1.1 + 0.4 * (1 - avg));
|
|
||||||
} else if (hoverHandler.hovered) {
|
|
||||||
return Qt.darker(Kirigami.Theme.backgroundColor, 1.05)
|
|
||||||
} else {
|
|
||||||
return Kirigami.Theme.backgroundColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.Separator {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
visible: root.verticalSeparator
|
|
||||||
}
|
|
||||||
Kirigami.Separator {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
visible: root.horizontalSeparator
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
leftPadding: Kirigami.Units.largeSpacing
|
|
||||||
rightPadding: Kirigami.Units.largeSpacing
|
|
||||||
topPadding: Kirigami.Units.largeSpacing
|
|
||||||
bottomPadding: Kirigami.Units.largeSpacing
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
implicitHeight: row.height + Kirigami.Units.smallSpacing
|
|
||||||
implicitWidth: row.implicitWidth
|
|
||||||
RowLayout {
|
|
||||||
id: row
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 0
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
Layout.preferredHeight: label.height
|
|
||||||
Layout.preferredWidth: height
|
|
||||||
Layout.rightMargin: Kirigami.Units.smallSpacing
|
|
||||||
visible: root.icon.name
|
|
||||||
source: root.icon.name
|
|
||||||
}
|
|
||||||
Label {
|
|
||||||
id: label
|
|
||||||
text: root.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
id: hoverHandler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
|
||||||
*/
|
|
||||||
import QtQuick 2.7
|
|
||||||
import QtQuick.Controls 2.0
|
|
||||||
import QtQml 2.15
|
|
||||||
import org.kde.kirigami 2.9 as Kirigami
|
|
||||||
|
|
||||||
// taken from Kirigami
|
|
||||||
MouseArea {
|
|
||||||
id: root
|
|
||||||
default property Item contentItem
|
|
||||||
property Flickable flickableItem
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
property int horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
|
|
||||||
property int verticalScrollBarPolicy: Qt.ScrollBarAsNeeded
|
|
||||||
property int topPadding: 0
|
|
||||||
property int leftPadding: 0
|
|
||||||
property int bottomPadding: 0
|
|
||||||
property int rightPadding: 0
|
|
||||||
property bool canFlickWithMouse: false
|
|
||||||
|
|
||||||
// Note: These are used because RefreshableScrollView overrides right and
|
|
||||||
// bottom padding properties.
|
|
||||||
property int rightSpacing: !Kirigami.Settings.hasTransientTouchInput && flickableItem && flickableItem.ScrollBar.vertical && flickableItem.ScrollBar.vertical.visible ? flickableItem.ScrollBar.vertical.width : 0
|
|
||||||
property int bottomSpacing: !Kirigami.Settings.hasTransientTouchInput && flickableItem && flickableItem.ScrollBar.horizontal && flickableItem.ScrollBar.horizontal.visible ? flickableItem.ScrollBar.horizontal.height : 0
|
|
||||||
|
|
||||||
Accessible.onScrollDownAction: flickableItem.Accessible.onScrollDownAction
|
|
||||||
Accessible.onScrollUpAction: flickableItem.Accessible.onScrollUpAction
|
|
||||||
|
|
||||||
Keys.onUpPressed: scroll(Kirigami.Units.gridUnit * 2)
|
|
||||||
Keys.onDownPressed: scroll(-Kirigami.Units.gridUnit * 2)
|
|
||||||
|
|
||||||
function scroll(y) {
|
|
||||||
// Don't scroll if the view isn't scrollable!
|
|
||||||
if (flickableItem.contentHeight < flickableItem.height + flickableItem.contentY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const minYExtent = flickableItem.topMargin - flickableItem.originY;
|
|
||||||
const maxYExtent = flickableItem.height - (flickableItem.contentHeight + flickableItem.bottomMargin + flickableItem.originY);
|
|
||||||
|
|
||||||
flickableItem.contentY = Math.min(-maxYExtent, Math.max(-minYExtent, flickableItem.contentY - y));
|
|
||||||
}
|
|
||||||
|
|
||||||
focus: true
|
|
||||||
|
|
||||||
onPressed: mouse => { mouse.accepted = mouse.source !== Qt.MouseEventNotSynthesized; }
|
|
||||||
onVerticalScrollBarPolicyChanged: {
|
|
||||||
scrollBarCreationTimer.restart();
|
|
||||||
}
|
|
||||||
onHorizontalScrollBarPolicyChanged: {
|
|
||||||
scrollBarCreationTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onContentItemChanged: {
|
|
||||||
if (contentItem.hasOwnProperty("contentY")) {
|
|
||||||
flickableItem = contentItem;
|
|
||||||
if (typeof(flickableItem.keyNavigationEnabled) != "undefined") {
|
|
||||||
flickableItem.keyNavigationEnabled = true;
|
|
||||||
flickableItem.keyNavigationWraps = false;
|
|
||||||
}
|
|
||||||
contentItem.parent = flickableParent;
|
|
||||||
} else {
|
|
||||||
flickableItem = flickableComponent.createObject(flickableParent);
|
|
||||||
contentItem.parent = flickableItem.contentItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
flickableItem.anchors.fill = flickableParent;
|
|
||||||
|
|
||||||
scrollBarCreationTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
Binding {
|
|
||||||
when: !root.canFlickWithMouse
|
|
||||||
target: root.flickableItem
|
|
||||||
property: "interactive"
|
|
||||||
value: Kirigami.Settings.hasTransientTouchInput
|
|
||||||
restoreMode: Binding.RestoreBinding
|
|
||||||
}
|
|
||||||
Timer {
|
|
||||||
id: scrollBarCreationTimer
|
|
||||||
interval: 0
|
|
||||||
onTriggered: {
|
|
||||||
//create or destroy the vertical scrollbar
|
|
||||||
if ((!flickableItem.ScrollBar.vertical) &&
|
|
||||||
verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff) {
|
|
||||||
flickableItem.ScrollBar.vertical = verticalScrollComponent.createObject(root);
|
|
||||||
} else if (flickableItem.ScrollBar.vertical &&
|
|
||||||
verticalScrollBarPolicy == Qt.ScrollBarAlwaysOff) {
|
|
||||||
flickableItem.ScrollBar.vertical.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
//create or destroy the horizontal scrollbar
|
|
||||||
if ((!flickableItem.ScrollBar.horizontal) &&
|
|
||||||
horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff) {
|
|
||||||
flickableItem.ScrollBar.horizontal = horizontalScrollComponent.createObject(root);
|
|
||||||
} else if (flickableItem.ScrollBar.horizontal &&
|
|
||||||
horizontalScrollBarPolicy == Qt.ScrollBarAlwaysOff) {
|
|
||||||
flickableItem.ScrollBar.horizontal.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Kirigami.WheelHandler {
|
|
||||||
id: wheelHandler
|
|
||||||
target: root.flickableItem
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
id: flickableParent
|
|
||||||
clip: true
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
leftMargin: root.leftPadding
|
|
||||||
topMargin: root.topPadding
|
|
||||||
rightMargin: root.rightPadding + root.rightSpacing
|
|
||||||
bottomMargin: root.bottomPadding + root.bottomSpacing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: flickableComponent
|
|
||||||
Flickable {
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
}
|
|
||||||
contentWidth: root.contentItem ? root.contentItem.width : 0
|
|
||||||
contentHeight: root.contentItem ? root.contentItem.height : 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: verticalScrollComponent
|
|
||||||
ScrollBar {
|
|
||||||
z: flickableParent.z + 1
|
|
||||||
visible: root.verticalScrollBarPolicy != Qt.ScrollBarAlwaysOff && root.contentItem.visible && size < 1
|
|
||||||
interactive: !Kirigami.Settings.hasTransientTouchInput
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
right: parent.right
|
|
||||||
top: parent.top
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: root.bottomSpacing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Component {
|
|
||||||
id: horizontalScrollComponent
|
|
||||||
ScrollBar {
|
|
||||||
z: flickableParent.z + 1
|
|
||||||
visible: root.horizontalScrollBarPolicy != Qt.ScrollBarAlwaysOff && root.contentItem.visible && size < 1
|
|
||||||
interactive: !Kirigami.Settings.hasTransientTouchInput
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
bottom: parent.bottom
|
|
||||||
rightMargin: root.rightSpacing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue