diff --git a/src/qml/BookmarkList.qml b/src/qml/BookmarkList.qml index 9edcd32389..44d8cbbd94 100644 --- a/src/qml/BookmarkList.qml +++ b/src/qml/BookmarkList.qml @@ -10,68 +10,27 @@ import Theme /** * \ingroup qml */ -Pane { +QfPaneDrawer { id: bookmarkList property alias model: bookmarksList.model property bool multiSelection: false - property bool fullScreenView: false - property bool isVertical: parent.width < parent.height || parent.width < 300 - property bool isDragging: false - property real dragHeightAdjustment: 0 - property real dragWidthAdjustment: 0 - property real lastWidth - - width: { - if (props.isVisible) { - if (dragWidthAdjustment != 0) { - return lastWidth - dragWidthAdjustment; - } else if (fullScreenView || parent.width <= parent.height || width >= 0.95 * parent.width) { - lastWidth = parent.width; - return parent.width; - } else { - const newWidth = Math.min(Math.max(200, parent.width / 2.25), parent.width); - lastWidth = newWidth; - return newWidth; - } - } else { - lastWidth = 0; - return 0; - } - } - property real lastHeight - - height: { - if (props.isVisible) { - if (dragHeightAdjustment != 0) { - return Math.min(lastHeight - dragHeightAdjustment, parent.height - mainWindow.sceneTopMargin); - } else if (fullScreenView || parent.width > parent.height || height >= 0.95 * parent.height) { - lastHeight = parent.height; - return parent.height; - } else { - const defaultMin = Math.min(Math.max(200, parent.height / 2), parent.height); - const minContentHeight = bookmarkListToolBar.height + (bookmarksList.contentHeight + bookmarksList.anchors.bottomMargin) + 25; - const newHeight = Math.min(minContentHeight, defaultMin); - lastHeight = newHeight; - return newHeight; - } - } else { - lastHeight = 0; - return 0; - } - } - topPadding: 0 - leftPadding: 0 - rightPadding: 0 - bottomPadding: 0 + contentVisible: props.isVisible + freezeKey: 'bookmarkresize' + headerHeight: bookmarkListToolBar.height + minContentHeight: bookmarkListToolBar.height + (bookmarksList.contentHeight + bookmarksList.anchors.bottomMargin) + 25 - visible: props.isVisible - clip: true + onCollapsed: bookmarkList.hide() - WheelHandler { - acceptedDevices: PointerDevice.AllDevices - onWheel: {} - } + states: [ + State { + name: "Hidden" + }, + State { + name: "Visible" + } + ] + state: "Hidden" QtObject { id: props @@ -443,50 +402,6 @@ Pane { } } - function setMultiSelection(active) { - multiSelection = active; - if (model) { - model.hideProjectBookmarks = active; - } - } - - function statusIndicatorDragged(deltaX, deltaY) { - fullScreenView = false; - if (isVertical) { - dragHeightAdjustment += deltaY; - } else { - dragWidthAdjustment += deltaX; - } - } - - function statusIndicatorDragReleased() { - isDragging = false; - if (isVertical) { - const minContentHeight = bookmarkListToolBar.height + 48 + 30; - if (bookmarkList.height < minContentHeight) { - if (fullScreenView) { - fullScreenView = false; - } else { - bookmarkList.hide(); - } - } else if (dragHeightAdjustment < -parent.height * 0.2) { - fullScreenView = true; - } - } else { - if (bookmarkList.width < bookmarkList.parent.width * 0.3) { - if (fullScreenView) { - fullScreenView = false; - } else { - bookmarkList.hide(); - } - } else if (dragWidthAdjustment < -parent.width * 0.2) { - fullScreenView = true; - } - } - dragHeightAdjustment = 0; - dragWidthAdjustment = 0; - } - Keys.onReleased: event => { if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) { if (Overlay.overlay && Overlay.overlay.visibleChildren.length > 1 || (Overlay.overlay.visibleChildren.length === 1 && !toast.visible)) { @@ -502,39 +417,17 @@ Pane { } } - Behavior on width { - enabled: !isDragging - PropertyAnimation { - duration: parent.width > parent.height ? 250 : 0 - easing.type: Easing.OutQuart - - onRunningChanged: { - if (running) - mapCanvasMap.freeze('bookmarkresize'); - else - mapCanvasMap.unfreeze('bookmarkresize'); - } - } - } - - Behavior on height { - enabled: !isDragging - PropertyAnimation { - duration: parent.width < parent.height ? 250 : 0 - easing.type: Easing.OutQuart - - onRunningChanged: { - if (running) - mapCanvasMap.freeze('bookmarkresize'); - else - mapCanvasMap.unfreeze('bookmarkresize'); - } - } - } - function show() { props.isVisible = true; focus = true; + state = "Visible"; + } + + function setMultiSelection(active) { + multiSelection = active; + if (model) { + model.hideProjectBookmarks = active; + } } function hide() { @@ -545,5 +438,6 @@ Pane { if (bookmarkList.model) { bookmarkList.model.clearSelection(); } + state = "Hidden"; } } diff --git a/src/qml/FeatureListForm.qml b/src/qml/FeatureListForm.qml index 648b79bcea..8199d99ba9 100644 --- a/src/qml/FeatureListForm.qml +++ b/src/qml/FeatureListForm.qml @@ -26,7 +26,7 @@ import Theme /** * \ingroup qml */ -Pane { +QfPaneDrawer { id: featureFormList property ProcessingAlgorithm algorithm: processingAlgorithm @@ -49,15 +49,22 @@ Pane { property bool allowDelete property bool multiSelection: false - property bool fullScreenView: qfieldSettings.fullScreenIdentifyView - property bool isVertical: parent.width < parent.height || parent.width < 300 - - property bool isDragging: false - property real dragHeightAdjustment: 0 - property real dragWidthAdjustment: 0 + fullScreenView: qfieldSettings.fullScreenIdentifyView property bool canvasOperationRequested: digitizingToolbar.geometryRequested || moveFeaturesToolbar.moveFeaturesRequested || rotateFeaturesToolbar.rotateFeaturesRequested + contentVisible: props.isVisible || featureFormList.canvasOperationRequested + freezeKey: 'formresize' + headerHeight: featureListToolBar.height + useDefaultMinHeight: featureFormList.state !== "FeatureList" + minContentHeight: featureListToolBar.height + (globalFeaturesList.contentHeight + globalFeaturesList.anchors.bottomMargin) + 25 + + onCollapsed: { + if (featureFormList.state !== 'FeatureFormEdit') { + featureFormList.state = 'Hidden'; + } + } + signal showMessage(string message) signal editGeometry signal requestJumpToPoint(var center, real scale, bool handleMargins) @@ -66,58 +73,11 @@ Pane { featureForm.requestCancel(); } - property real lastWidth - - width: { - if (props.isVisible || featureFormList.canvasOperationRequested) { - if (dragWidthAdjustment != 0) { - return lastWidth - dragWidthAdjustment; - } else if (fullScreenView || parent.width <= parent.height || width >= 0.95 * parent.width) { - lastWidth = parent.width; - return parent.width; - } else { - const newWidth = Math.min(Math.max(200, parent.width / 2.25), parent.width); - lastWidth = newWidth; - return newWidth; - } - } else { - lastWidth = 0; - return 0; - } - } - property real lastHeight - - height: { - if (props.isVisible || featureFormList.canvasOperationRequested) { - if (dragHeightAdjustment != 0) { - return Math.min(lastHeight - dragHeightAdjustment, parent.height - mainWindow.sceneTopMargin); - } else if (fullScreenView || parent.width > parent.height || height >= 0.95 * parent.height) { - lastHeight = parent.height; - return parent.height; - } else { - const defaultMin = Math.min(Math.max(200, parent.height / 2), parent.height); - var minContentHeight = featureFormList.state !== "FeatureList" ? defaultMin : featureListToolBar.height + (globalFeaturesList.contentHeight + globalFeaturesList.anchors.bottomMargin) + 25; - const newHeight = Math.min(minContentHeight, defaultMin); - lastHeight = newHeight; - return newHeight; - } - } else { - lastHeight = 0; - return 0; - } - } - anchors.bottomMargin: featureFormList.canvasOperationRequested ? featureFormList.height : 0 anchors.rightMargin: featureFormList.canvasOperationRequested ? -featureFormList.width : 0 opacity: featureFormList.canvasOperationRequested ? 0.5 : 1 - topPadding: 0 - leftPadding: 0 - rightPadding: 0 - bottomPadding: 0 - enabled: !featureFormList.canvasOperationRequested - visible: props.isVisible states: [ State { @@ -255,12 +215,6 @@ Pane { } ] state: "Hidden" - clip: true - - WheelHandler { - acceptedDevices: PointerDevice.AllDevices - onWheel: {} - } QtObject { id: props @@ -560,48 +514,15 @@ Pane { } onStatusIndicatorDragged: function (deltaX, deltaY) { - fullScreenView = false; - if (isVertical) { - dragHeightAdjustment += deltaY; - } else { - dragWidthAdjustment += deltaX; - } + featureFormList.statusIndicatorDragged(deltaX, deltaY); } onStatusIndicatorDragAcquired: { - isDragging = true; + featureFormList.isDragging = true; } onStatusIndicatorDragReleased: { - isDragging = false; - if (isVertical) { - const minContentHeight = featureListToolBar.height + 48 + 30; - if (featureFormList.height < minContentHeight) { - if (fullScreenView) { - fullScreenView = false; - } else { - if (featureFormList.state !== 'FeatureFormEdit') { - featureFormList.state = 'Hidden'; - } - } - } else if (dragHeightAdjustment < -parent.height * 0.2) { - fullScreenView = true; - } - } else { - if (featureFormList.width < featureFormList.parent.width * 0.3) { - if (fullScreenView) { - fullScreenView = false; - } else { - if (featureFormList.state != 'FeatureFormEdit') { - featureFormList.state = 'Hidden'; - } - } - } else if (dragWidthAdjustment < -parent.width * 0.2) { - fullScreenView = true; - } - } - dragHeightAdjustment = 0; - dragWidthAdjustment = 0; + featureFormList.statusIndicatorDragReleased(); } onEditAttributesButtonClicked: { @@ -806,36 +727,6 @@ Pane { } } - Behavior on width { - enabled: !isDragging - PropertyAnimation { - duration: parent.width > parent.height ? 250 : 0 - easing.type: Easing.OutQuart - - onRunningChanged: { - if (running) - mapCanvasMap.freeze('formresize'); - else - mapCanvasMap.unfreeze('formresize'); - } - } - } - - Behavior on height { - enabled: !isDragging - PropertyAnimation { - duration: parent.width < parent.height ? 250 : 0 - easing.type: Easing.OutQuart - - onRunningChanged: { - if (running) - mapCanvasMap.freeze('formresize'); - else - mapCanvasMap.unfreeze('formresize'); - } - } - } - Behavior on anchors.rightMargin { PropertyAnimation { duration: 250 diff --git a/src/qml/imports/Theme/QfPaneDrawer.qml b/src/qml/imports/Theme/QfPaneDrawer.qml new file mode 100644 index 0000000000..ef07afd723 --- /dev/null +++ b/src/qml/imports/Theme/QfPaneDrawer.qml @@ -0,0 +1,158 @@ +import QtQuick +import QtQuick.Controls + +/** + * \ingroup qml + */ +Pane { + id: paneDrawer + + //! TRUE when the pane is laid out vertically (portrait or narrow), driving the drag axis and resize animation direction + readonly property bool isVertical: parent.width < parent.height || parent.width < 300 + //! When TRUE the pane expands to fill the whole available area + property bool fullScreenView: false + //! TRUE while the header is being dragged to resize the pane + property bool isDragging: false + + //! When TRUE the pane is laid out at its resting size, when FALSE it collapses to zero + property bool contentVisible: false + //! Content driven minimum height the pane snaps to at rest, clamped to half the available height + property real minContentHeight: 0 + //! When TRUE the resting height ignores a minContentHeight and uses the default minimum + property bool useDefaultMinHeight: false + //! Height of the header, used to compute the collapse threshold + property real headerHeight: 0 + //! Key passed to mapCanvasMap freeze/unfreeze so concurrent panes does not clear each other + property string freezeKey + //! Emitted when a drag releases below the minimum size without entering fullscreen + signal collapsed + + //! Internal state driving the resize math, kept out of the public interface + QtObject { + id: props + + property real dragHeightAdjustment: 0 + property real dragWidthAdjustment: 0 + property real lastWidth + property real lastHeight + } + + width: { + if (contentVisible) { + if (props.dragWidthAdjustment != 0) { + return props.lastWidth - props.dragWidthAdjustment; + } else if (fullScreenView || parent.width <= parent.height || width >= 0.95 * parent.width) { + props.lastWidth = parent.width; + return parent.width; + } else { + const newWidth = Math.min(Math.max(200, parent.width / 2.25), parent.width); + props.lastWidth = newWidth; + return newWidth; + } + } else { + props.lastWidth = 0; + return 0; + } + } + + height: { + if (contentVisible) { + if (props.dragHeightAdjustment != 0) { + return Math.min(props.lastHeight - props.dragHeightAdjustment, parent.height - mainWindow.sceneTopMargin); + } else if (fullScreenView || parent.width > parent.height || height >= 0.95 * parent.height) { + props.lastHeight = parent.height; + return parent.height; + } else { + const defaultMin = Math.min(Math.max(200, parent.height / 2), parent.height); + const newHeight = useDefaultMinHeight ? defaultMin : Math.min(minContentHeight, defaultMin); + props.lastHeight = newHeight; + return newHeight; + } + } else { + props.lastHeight = 0; + return 0; + } + } + + topPadding: 0 + leftPadding: 0 + rightPadding: 0 + bottomPadding: 0 + + visible: contentVisible + clip: true + + WheelHandler { + acceptedDevices: PointerDevice.AllDevices + onWheel: {} + } + + function statusIndicatorDragged(deltaX, deltaY) { + fullScreenView = false; + if (isVertical) { + props.dragHeightAdjustment += deltaY; + } else { + props.dragWidthAdjustment += deltaX; + } + } + + function statusIndicatorDragReleased() { + isDragging = false; + if (isVertical) { + const minHeight = headerHeight + 48 + 30; + if (paneDrawer.height < minHeight) { + if (fullScreenView) { + fullScreenView = false; + } else { + paneDrawer.collapsed(); + } + } else if (props.dragHeightAdjustment < -parent.height * 0.2) { + fullScreenView = true; + } + } else { + if (paneDrawer.width < paneDrawer.parent.width * 0.3) { + if (fullScreenView) { + fullScreenView = false; + } else { + paneDrawer.collapsed(); + } + } else if (props.dragWidthAdjustment < -parent.width * 0.2) { + fullScreenView = true; + } + } + props.dragHeightAdjustment = 0; + props.dragWidthAdjustment = 0; + } + + Behavior on width { + enabled: !isDragging + PropertyAnimation { + duration: paneDrawer.parent.width > paneDrawer.parent.height ? 250 : 0 + easing.type: Easing.OutQuart + + onRunningChanged: { + if (running) { + mapCanvasMap.freeze(paneDrawer.freezeKey); + } else { + mapCanvasMap.unfreeze(paneDrawer.freezeKey); + } + } + } + } + + Behavior on height { + enabled: !isDragging + PropertyAnimation { + duration: paneDrawer.parent.width < paneDrawer.parent.height ? 250 : 0 + easing.type: Easing.OutQuart + + onRunningChanged: { + if (running) { + mapCanvasMap.freeze(paneDrawer.freezeKey); + } else { + mapCanvasMap.unfreeze(paneDrawer.freezeKey); + } + } + } + } +} diff --git a/src/qml/imports/Theme/qmldir b/src/qml/imports/Theme/qmldir index 650e3a2903..9264d3dcb4 100644 --- a/src/qml/imports/Theme/qmldir +++ b/src/qml/imports/Theme/qmldir @@ -13,6 +13,7 @@ QfMeterBar 1.0 QfMeterBar.qml QfOpacityMask 1.0 QfOpacityMask.qml QfOverlayContainer 1.0 QfOverlayContainer.qml QfPageHeader 1.0 QfPageHeader.qml +QfPaneDrawer 1.0 QfPaneDrawer.qml QfPopup 1.0 QfPopup.qml QfProgressRing 1.0 QfProgressRing.qml QfProjectThumbnail 1.0 QfProjectThumbnail.qml diff --git a/src/qml/qgismobileapp.qml b/src/qml/qgismobileapp.qml index 7934627fb5..d4bb48eb45 100644 --- a/src/qml/qgismobileapp.qml +++ b/src/qml/qgismobileapp.qml @@ -4659,8 +4659,8 @@ ApplicationWindow { bottom: parent.bottom } - onVisibleChanged: { - if (visible && featureListForm.visible) { + onStateChanged: { + if (state !== "Hidden" && featureListForm.visible) { featureListForm.hide(); } } diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index 7321fbc207..4081eba302 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -109,19 +109,20 @@ Nyuki.qml LayerLoginDialog.qml imports/Theme/QfActionButton.qml + imports/Theme/QfButton.qml imports/Theme/QfComboBox.qml imports/Theme/QfDropShadow.qml imports/Theme/QfExpandableGroupBox.qml imports/Theme/QfOpacityMask.qml imports/Theme/QfToolButton.qml imports/Theme/QfToolButtonDrawer.qml - imports/Theme/QfButton.qml imports/Theme/QfSwitch.qml imports/Theme/QfTextField.qml imports/Theme/QfCollapsibleMessage.qml imports/Theme/QfSlider.qml imports/Theme/QfCalendarPanel.qml imports/Theme/QfPageHeader.qml + imports/Theme/QfPaneDrawer.qml imports/Theme/QfOverlayContainer.qml imports/Theme/QfTabBar.qml imports/Theme/qmldir