From 7a6c484e306b1903e12bdf162be1205704a06acf Mon Sep 17 00:00:00 2001 From: Richard Kello Date: Wed, 22 Jul 2026 17:52:26 +0200 Subject: [PATCH 1/2] Add scrollbar for desktop devices --- app/inpututils.h | 2 +- app/qml/account/MMHowYouFoundUsPage.qml | 8 +- app/qml/components/MMListDrawer.qml | 8 +- .../components/MMListMultiselectDrawer.qml | 11 ++- app/qml/components/MMListView.qml | 1 + app/qml/components/MMScrollView.qml | 4 +- app/qml/components/MMToolbar.qml | 7 +- app/qml/form/MMFormPage.qml | 6 +- app/qml/form/MMPreviewDrawer.qml | 8 +- .../components/MMFeaturesListPageDrawer.qml | 8 +- app/qml/form/components/MMFormTabBar.qml | 63 +++++++++------- app/qml/form/editors/MMFormGalleryEditor.qml | 10 ++- app/qml/gps/MMPositionProviderPage.qml | 8 +- app/qml/layers/MMFeaturesListPage.qml | 12 +-- app/qml/layers/MMLayersList.qml | 60 ++++++++------- app/qml/project/MMProjectIssuesPage.qml | 8 +- app/qml/project/MMProjectList.qml | 74 ++++++++++--------- app/qml/project/MMProjectStatusPage.qml | 8 +- app/qml/project/MMProjectWizardPage.qml | 7 +- .../project/components/MMProjectDelegate.qml | 3 + app/qml/settings/MMChangelogPage.qml | 4 +- 21 files changed, 190 insertions(+), 130 deletions(-) diff --git a/app/inpututils.h b/app/inpututils.h index 122661a0f..e4d9c128f 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -222,7 +222,7 @@ class InputUtils: public QObject /** InputApp platform */ static QString appPlatform(); - static bool isMobilePlatform(); + Q_INVOKABLE static bool isMobilePlatform(); static QString appDataDir(); diff --git a/app/qml/account/MMHowYouFoundUsPage.qml b/app/qml/account/MMHowYouFoundUsPage.qml index d0d231503..96c7e7d1f 100644 --- a/app/qml/account/MMHowYouFoundUsPage.qml +++ b/app/qml/account/MMHowYouFoundUsPage.qml @@ -70,12 +70,13 @@ MMPage { width: parent.width height: parent.height - MMListView { - id: listView - + MMScrollView { width: parent.width height: parent.height + MMListView { + id: listView + spacing: __style.spacing12 topMargin: __style.margin40 @@ -160,6 +161,7 @@ MMPage { } } } + } MMButton { id: footerButton diff --git a/app/qml/components/MMListDrawer.qml b/app/qml/components/MMListDrawer.qml index 4a6a388fd..b22c96a30 100644 --- a/app/qml/components/MMListDrawer.qml +++ b/app/qml/components/MMListDrawer.qml @@ -43,16 +43,18 @@ MMDrawer { } } + MMScrollView { + width: parent.width + height: Math.min( root.drawerContentAvailableHeight, listViewComponent.contentHeight ) + MMListView { id: listViewComponent - width: parent.width - height: Math.min( root.drawerContentAvailableHeight, contentHeight ) - interactive: contentHeight > height clip: true maximumFlickVelocity: __androidUtils.isAndroid ? __style.scrollVelocityAndroid : maximumFlickVelocity } + } } } diff --git a/app/qml/components/MMListMultiselectDrawer.qml b/app/qml/components/MMListMultiselectDrawer.qml index ae9b35e4a..02837cd34 100644 --- a/app/qml/components/MMListMultiselectDrawer.qml +++ b/app/qml/components/MMListMultiselectDrawer.qml @@ -83,13 +83,15 @@ MMDrawer { sourceComponent: defaultEmptyStateComponent } + MMScrollView { + width: parent.width + height: Math.min( listViewComponent.contentHeight, root.drawerContentAvailableHeight - internal.searchBarVerticalSpace ) + + visible: listViewComponent.count > 0 + MMListView { id: listViewComponent - width: parent.width - height: Math.min( contentHeight, root.drawerContentAvailableHeight - internal.searchBarVerticalSpace ) - - visible: count > 0 interactive: contentHeight > height clip: true @@ -122,6 +124,7 @@ MMDrawer { footer: MMListSpacer { height: __style.safeAreaBottom + __style.margin8 + ( root.multiSelect ? confirmButton.height : 0 ) } } + } } } diff --git a/app/qml/components/MMListView.qml b/app/qml/components/MMListView.qml index d1d26e95b..280412870 100644 --- a/app/qml/components/MMListView.qml +++ b/app/qml/components/MMListView.qml @@ -12,6 +12,7 @@ import QtQuick ListView { id: root + // when flicking up really fast, we should go back to the first item onVerticalOvershootChanged: { if (verticalOvershoot < -200) { diff --git a/app/qml/components/MMScrollView.qml b/app/qml/components/MMScrollView.qml index 5ea4c2dea..01b60682e 100644 --- a/app/qml/components/MMScrollView.qml +++ b/app/qml/components/MMScrollView.qml @@ -16,8 +16,10 @@ import QtQuick.Controls ScrollView { id: root + rightPadding: ScrollBar.vertical.visible ? ScrollBar.vertical.width * 2 : 0 + contentWidth: availableWidth // to only scroll vertically - ScrollBar.vertical.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: !__inputUtils.isMobilePlatform() && root.contentHeight > root.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff ScrollBar.horizontal.policy: ScrollBar.AlwaysOff } diff --git a/app/qml/components/MMToolbar.qml b/app/qml/components/MMToolbar.qml index 3e1568fca..41e8c6847 100644 --- a/app/qml/components/MMToolbar.qml +++ b/app/qml/components/MMToolbar.qml @@ -41,17 +41,22 @@ Rectangle { // center the content x: __style.safeAreaLeft + MMScrollView { + anchors.fill: parent + + contentWidth: toolbar.contentWidth + MMListView { id: toolbar onWidthChanged: root.recalculate() model: toolbarModel delegate: ( model.count === 1 ) && ( menuModel.count === 0 ) ? longBtnComp : shortBtnComp - anchors.fill: parent orientation: ListView.Horizontal interactive: false Component.onCompleted: root.initialize() } + } } // buttons shown inside main toolbar diff --git a/app/qml/form/MMFormPage.qml b/app/qml/form/MMFormPage.qml index efb111df5..4cca54500 100644 --- a/app/qml/form/MMFormPage.qml +++ b/app/qml/form/MMFormPage.qml @@ -175,14 +175,15 @@ Page { property int tabIndex: model.TabIndex // from the repeater - MMComponents.MMListView { - + MMComponents.MMScrollView { anchors { fill: parent leftMargin: __style.pageMargins rightMargin: __style.pageMargins } + MMComponents.MMListView { + model: swipeViewRepeater.model.attributeFormProxyModel( pageDelegate.tabIndex ) clip: true @@ -206,6 +207,7 @@ Page { height: 20 * __dp } } + } } } } diff --git a/app/qml/form/MMPreviewDrawer.qml b/app/qml/form/MMPreviewDrawer.qml index 5c0891313..f5b4b6805 100644 --- a/app/qml/form/MMPreviewDrawer.qml +++ b/app/qml/form/MMPreviewDrawer.qml @@ -188,9 +188,12 @@ Item { visible: internal.showFields - MMComponents.MMListView { + MMComponents.MMScrollView { width: parent.width - height: contentHeight + height: listView.contentHeight + + MMComponents.MMListView { + id: listView spacing: __style.margin8 interactive: false @@ -238,6 +241,7 @@ Item { } } } + } } // HTML diff --git a/app/qml/form/components/MMFeaturesListPageDrawer.qml b/app/qml/form/components/MMFeaturesListPageDrawer.qml index b463a687f..7a7c8d3e2 100644 --- a/app/qml/form/components/MMFeaturesListPageDrawer.qml +++ b/app/qml/form/components/MMFeaturesListPageDrawer.qml @@ -96,12 +96,13 @@ Drawer { Layout.preferredHeight: __style.spacing20 } - MMComponents.MMListView { - id: listView - + MMComponents.MMScrollView { Layout.fillWidth: true Layout.fillHeight: true + MMComponents.MMListView { + id: listView + clip: true delegate: MMComponents.MMListDelegate { @@ -114,6 +115,7 @@ Drawer { footer: MMComponents.MMListSpacer { height: __style.safeAreaBottom + __style.margin8 + ( primarybtn.visible ? primarybtn.height : 0 ) } } + } } MMComponents.MMButton { diff --git a/app/qml/form/components/MMFormTabBar.qml b/app/qml/form/components/MMFormTabBar.qml index d7989e412..ac583efea 100644 --- a/app/qml/form/components/MMFormTabBar.qml +++ b/app/qml/form/components/MMFormTabBar.qml @@ -11,49 +11,58 @@ import QtQuick import QtQuick.Controls import "../../components" as MMComponents -MMComponents.MMListView { +MMComponents.MMScrollView { id: root implicitHeight: __style.row45 - spacing: 0 + property alias model: tabList.model + property alias currentIndex: tabList.currentIndex - clip: true - orientation: ListView.Horizontal + contentWidth: tabList.contentWidth - interactive: contentWidth > width + MMComponents.MMListView { + id: tabList - header: MMComponents.MMListSpacer { width: __style.margin20 } - footer: MMComponents.MMListSpacer { width: __style.margin20 } + spacing: 0 - delegate: Control { - id: tabDelegate + clip: true + orientation: ListView.Horizontal - property bool isSelected: ListView.isCurrentItem + interactive: contentWidth > width - height: __style.row45 - width: contentItem.implicitWidth + header: MMComponents.MMListSpacer { width: __style.margin20 } + footer: MMComponents.MMListSpacer { width: __style.margin20 } - focusPolicy: Qt.NoFocus + delegate: Control { + id: tabDelegate - background: Rectangle { - visible: tabDelegate.isSelected - color: __style.grassColor - radius: __style.radius30 - } + property bool isSelected: ListView.isCurrentItem - contentItem: MMComponents.MMText { - text: model.Name + height: __style.row45 + width: contentItem.implicitWidth - font: __style.t4 + focusPolicy: Qt.NoFocus - leftPadding: __style.margin20 - rightPadding: __style.margin20 - } + background: Rectangle { + visible: tabDelegate.isSelected + color: __style.grassColor + radius: __style.radius30 + } + + contentItem: MMComponents.MMText { + text: model.Name + + font: __style.t4 + + leftPadding: __style.margin20 + rightPadding: __style.margin20 + } - MouseArea { - anchors.fill: parent - onClicked: root.currentIndex = index + MouseArea { + anchors.fill: parent + onClicked: root.currentIndex = index + } } } } diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index 37221230b..9812317c6 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -35,12 +35,15 @@ MMPrivateComponents.MMBaseInput { ? qsTr( "Some features may be hidden by active filters" ) : "" - inputContent: MMComponents.MMListView { - id: rowView - + inputContent: MMComponents.MMScrollView { width: parent.width height: __style.row120 + contentWidth: rowView.contentWidth + + MMComponents.MMListView { + id: rowView + clip: true spacing: __style.spacing12 orientation: ListView.Horizontal @@ -77,6 +80,7 @@ MMPrivateComponents.MMBaseInput { header: __activeProject.projectRole !== "reader" ? addFeatureComponent : null } + } Component { id: addFeatureComponent diff --git a/app/qml/gps/MMPositionProviderPage.qml b/app/qml/gps/MMPositionProviderPage.qml index 34ceb63ff..d519c2834 100644 --- a/app/qml/gps/MMPositionProviderPage.qml +++ b/app/qml/gps/MMPositionProviderPage.qml @@ -27,12 +27,13 @@ MMComponents.MMPage { width: parent.width height: parent.height - MMComponents.MMListView { - id: listview - + MMComponents.MMScrollView { width: parent.width height: parent.height + MMComponents.MMListView { + id: listview + clip: true model: PositionProvidersModel { @@ -118,6 +119,7 @@ MMComponents.MMPage { height: __style.safeAreaBottom + __style.margin8 + connectNewReceiverButton.height } } + } MMComponents.MMButton { id: connectNewReceiverButton diff --git a/app/qml/layers/MMFeaturesListPage.qml b/app/qml/layers/MMFeaturesListPage.qml index 3d8e03970..4f19f30a7 100644 --- a/app/qml/layers/MMFeaturesListPage.qml +++ b/app/qml/layers/MMFeaturesListPage.qml @@ -65,17 +65,18 @@ MMComponents.MMPage { } } - MMComponents.MMListView { - id: listView - - width: parent.width - + MMComponents.MMScrollView { anchors { top: filterBanner.visible ? filterBanner.bottom : searchBar.bottom bottom: parent.bottom + left: parent.left + right: parent.right topMargin: filterBanner.visible ? __style.spacing10 : __style.spacing20 } + MMComponents.MMListView { + id: listView + model: MM.LayerFeaturesModel { id: featuresModel @@ -97,6 +98,7 @@ MMComponents.MMPage { height: __style.margin20 + ( root.hasToolbar ? 0 : __style.safeAreaBottom ) + ( addButton.visible ? addButton.height : 0 ) } } + } MMComponents.MMBusyIndicator { anchors.centerIn: parent diff --git a/app/qml/layers/MMLayersList.qml b/app/qml/layers/MMLayersList.qml index 0d2fc53e9..d6fddf39f 100644 --- a/app/qml/layers/MMLayersList.qml +++ b/app/qml/layers/MMLayersList.qml @@ -15,7 +15,7 @@ import QtQuick.Layouts import "../components" as MMComponents import "../inputs" -MMComponents.MMListView { +MMComponents.MMScrollView { id: root property var basemodel: null @@ -27,43 +27,49 @@ MMComponents.MMListView { signal nodeClicked( var node, string nodeType, string nodeName ) signal nodeVisibilityClicked( var node ) - model: DelegateModel { - id: delegatemodel + property alias footer: listView.footer - model: root.basemodel + MMComponents.MMListView { + id: listView - delegate: MMComponents.MMListDelegate { + model: DelegateModel { + id: delegatemodel - property bool secondaryTextVisible: root.showNodePath && model.nodePath + model: root.basemodel - text: model.display - secondaryText: secondaryTextVisible ? model.nodePath: "" + delegate: MMComponents.MMListDelegate { - leftContent: Image { - width: __style.icon24 - height: width - sourceSize: Qt.size( width, height) - cache: false // important! Otherwise pixmap providers would not be called for the same id again + property bool secondaryTextVisible: root.showNodePath && model.nodePath - source: root.imageProviderPath + model.serializedNode - } + text: model.display + secondaryText: secondaryTextVisible ? model.nodePath: "" + + leftContent: Image { + width: __style.icon24 + height: width + sourceSize: Qt.size( width, height) + cache: false // important! Otherwise pixmap providers would not be called for the same id again - rightContent: MMComponents.MMSwitch { - visible: model.nodeIsVisible !== "" - checked: model.nodeIsVisible === "yes" - onReleased: { - root.nodeVisibilityClicked( model.node ) + source: root.imageProviderPath + model.serializedNode + } + + rightContent: MMComponents.MMSwitch { + visible: model.nodeIsVisible !== "" + checked: model.nodeIsVisible === "yes" + onReleased: { + root.nodeVisibilityClicked( model.node ) + } } - } - onClicked: root.nodeClicked( model.node, model.nodeType, model.display ) + onClicked: root.nodeClicked( model.node, model.nodeType, model.display ) + } } - } - Component.onCompleted: { - if ( root.parentNodeIndex ) - { - delegatemodel.rootIndex = root.parentNodeIndex + Component.onCompleted: { + if ( root.parentNodeIndex ) + { + delegatemodel.rootIndex = root.parentNodeIndex + } } } } diff --git a/app/qml/project/MMProjectIssuesPage.qml b/app/qml/project/MMProjectIssuesPage.qml index 9812cc085..55546624a 100644 --- a/app/qml/project/MMProjectIssuesPage.qml +++ b/app/qml/project/MMProjectIssuesPage.qml @@ -30,11 +30,12 @@ MMComponents.MMPage { onBackClicked: root.visible = false pageHeader.title: qsTr("Project issues") - pageContent: MMComponents.MMListView { - id: mainList - + pageContent: MMComponents.MMScrollView { anchors.fill: parent + MMComponents.MMListView { + id: mainList + model: root.projectIssuesModel spacing: __style.margin12 @@ -93,4 +94,5 @@ MMComponents.MMPage { } } } + } } diff --git a/app/qml/project/MMProjectList.qml b/app/qml/project/MMProjectList.qml index 7636434b9..1a435c01c 100644 --- a/app/qml/project/MMProjectList.qml +++ b/app/qml/project/MMProjectList.qml @@ -47,55 +47,56 @@ Item { controllerModel.listProjects( searchText ) } - MMComponents.MMListView { - id: listview + MMComponents.MMScrollView { + anchors.fill: parent - Component.onCompleted: { - // set proper footer (add project / fetch more) - if ( root.projectModelType === MM.ProjectsModel.LocalProjectsModel ) { - listview.footer = addProjectComponent - } - else - { - listview.footer = loadingSpinnerComponent + MMComponents.MMListView { + id: listview + + Component.onCompleted: { + // set proper footer (add project / fetch more) + if ( root.projectModelType === MM.ProjectsModel.LocalProjectsModel ) { + listview.footer = addProjectComponent + } + else + { + listview.footer = loadingSpinnerComponent + } } - } - onAtYEndChanged: { - if ( atYEnd ) { // user reached end of the list - if ( controllerModel.hasMoreProjects && !controllerModel.isLoading ) { - controllerModel.fetchAnotherPage( viewModel.searchExpression ) + onAtYEndChanged: { + if ( atYEnd ) { // user reached end of the list + if ( controllerModel.hasMoreProjects && !controllerModel.isLoading ) { + controllerModel.fetchAnotherPage( viewModel.searchExpression ) + } } } - } - anchors.fill: parent - clip: true - spacing: root.spacing + clip: true + spacing: root.spacing - maximumFlickVelocity: __androidUtils.isAndroid ? __style.scrollVelocityAndroid : maximumFlickVelocity + maximumFlickVelocity: __androidUtils.isAndroid ? __style.scrollVelocityAndroid : maximumFlickVelocity - // Proxy model with source projects model - model: MM.ProjectsProxyModel { - id: viewModel + // Proxy model with source projects model + model: MM.ProjectsProxyModel { + id: viewModel - activeProjectAlwaysFirst: root.activeProjectAlwaysFirst - projectSourceModel: MM.ProjectsModel { - id: controllerModel + activeProjectAlwaysFirst: root.activeProjectAlwaysFirst + projectSourceModel: MM.ProjectsModel { + id: controllerModel - merginApi: __merginApi - localProjectsManager: __localProjectsManager - syncManager: __syncManager - modelType: root.projectModelType + merginApi: __merginApi + localProjectsManager: __localProjectsManager + syncManager: __syncManager + modelType: root.projectModelType + } } - } - // Project delegate - delegate: MMProjectComponents.MMProjectDelegate { - id: projectDelegate + // Project delegate + delegate: MMProjectComponents.MMProjectDelegate { + id: projectDelegate - width: ListView.view.width - height: visible ? implicitHeight : 0 + height: visible ? implicitHeight : 0 projectDisplayName: root.projectModelType === MM.ProjectsModel.WorkspaceProjectsModel ? model.ProjectName : model.ProjectFullName projectId: model.ProjectId ? model.ProjectId : "" @@ -178,6 +179,7 @@ Item { onShowChangesRequested: root.showLocalChangesRequested( projectId ) } } + } Component { id: loadingSpinnerComponent diff --git a/app/qml/project/MMProjectStatusPage.qml b/app/qml/project/MMProjectStatusPage.qml index 295d1be3e..702f60bc7 100644 --- a/app/qml/project/MMProjectStatusPage.qml +++ b/app/qml/project/MMProjectStatusPage.qml @@ -47,14 +47,15 @@ MMComponents.MMPage { MMComponents.MMListSpacer { id: spacer; height: __style.spacing40 } // With changes content - MMComponents.MMListView { - id: statusList - + MMComponents.MMScrollView { anchors.top: spacer.bottom anchors.bottom: parent.bottom width: parent.width visible: statusPanel.hasChanges + MMComponents.MMListView { + id: statusList + model: __merginProjectStatusModel spacing: __style.margin8 @@ -144,5 +145,6 @@ MMComponents.MMPage { } } } + } } } diff --git a/app/qml/project/MMProjectWizardPage.qml b/app/qml/project/MMProjectWizardPage.qml index 93a65a10b..f3e40aa1b 100644 --- a/app/qml/project/MMProjectWizardPage.qml +++ b/app/qml/project/MMProjectWizardPage.qml @@ -59,12 +59,14 @@ MMComponents.MMPage { verticalAlignment: Text.AlignVCenter } + MMComponents.MMScrollView { + width: parent.width + height: parent.height - __style.margin20 - projectNameField.height - __style.margin20 - attributesLabel.height + MMComponents.MMListView { id: fieldList model: fieldsModel - width: parent.width - height: parent.height - __style.margin20 - projectNameField.height - __style.margin20 - attributesLabel.height clip: true spacing: __style.margin20 @@ -115,6 +117,7 @@ MMComponents.MMPage { } } } + } } } diff --git a/app/qml/project/components/MMProjectDelegate.qml b/app/qml/project/components/MMProjectDelegate.qml index b858b0666..54d3bcbab 100644 --- a/app/qml/project/components/MMProjectDelegate.qml +++ b/app/qml/project/components/MMProjectDelegate.qml @@ -38,6 +38,9 @@ Control { height: implicitHeight + // shrink to leave room for the list's vertical scrollbar, so content isn't drawn underneath it + implicitWidth: ListView.view.width + topPadding: __style.margin20 rightPadding: __style.margin20 leftPadding: __style.margin20 diff --git a/app/qml/settings/MMChangelogPage.qml b/app/qml/settings/MMChangelogPage.qml index 92b9bf547..a2ffec0a0 100644 --- a/app/qml/settings/MMChangelogPage.qml +++ b/app/qml/settings/MMChangelogPage.qml @@ -21,10 +21,11 @@ MMPage { pageHeader.title: qsTr( "Changelog" ) pageBottomMarginPolicy: MMPage.BottomMarginPolicy.PaintBehindSystemBar - pageContent: MMListView { + pageContent: MMScrollView { width: parent.width height: parent.height + MMListView { spacing: __style.spacing20 model: root.model @@ -49,6 +50,7 @@ MMPage { footer: root.dataNotReady ? noItemDelegate : spacer } + } Component { id: spacer From 484f460a773aef0c2a924ad8025ecbd40451c955 Mon Sep 17 00:00:00 2001 From: Richard Kello Date: Tue, 25 Aug 2026 12:44:51 +0200 Subject: [PATCH 2/2] Make ListViews more reusable --- app/qml/account/MMHowYouFoundUsPage.qml | 8 +- app/qml/components/MMListDrawer.qml | 8 +- .../components/MMListMultiselectDrawer.qml | 11 +-- app/qml/components/MMListView.qml | 61 ++++++++++++--- app/qml/components/MMToolbar.qml | 7 +- app/qml/form/MMFormPage.qml | 6 +- app/qml/form/MMPreviewDrawer.qml | 8 +- .../components/MMFeaturesListPageDrawer.qml | 8 +- app/qml/form/components/MMFormTabBar.qml | 63 +++++++--------- app/qml/form/editors/MMFormGalleryEditor.qml | 10 +-- app/qml/gps/MMPositionProviderPage.qml | 8 +- app/qml/layers/MMFeaturesListPage.qml | 12 ++- app/qml/layers/MMLayersList.qml | 60 +++++++-------- app/qml/project/MMProjectIssuesPage.qml | 8 +- app/qml/project/MMProjectList.qml | 74 +++++++++---------- app/qml/project/MMProjectStatusPage.qml | 8 +- app/qml/project/MMProjectWizardPage.qml | 7 +- app/qml/settings/MMChangelogPage.qml | 4 +- 18 files changed, 180 insertions(+), 191 deletions(-) diff --git a/app/qml/account/MMHowYouFoundUsPage.qml b/app/qml/account/MMHowYouFoundUsPage.qml index 96c7e7d1f..d0d231503 100644 --- a/app/qml/account/MMHowYouFoundUsPage.qml +++ b/app/qml/account/MMHowYouFoundUsPage.qml @@ -70,13 +70,12 @@ MMPage { width: parent.width height: parent.height - MMScrollView { - width: parent.width - height: parent.height - MMListView { id: listView + width: parent.width + height: parent.height + spacing: __style.spacing12 topMargin: __style.margin40 @@ -161,7 +160,6 @@ MMPage { } } } - } MMButton { id: footerButton diff --git a/app/qml/components/MMListDrawer.qml b/app/qml/components/MMListDrawer.qml index b22c96a30..4a6a388fd 100644 --- a/app/qml/components/MMListDrawer.qml +++ b/app/qml/components/MMListDrawer.qml @@ -43,18 +43,16 @@ MMDrawer { } } - MMScrollView { - width: parent.width - height: Math.min( root.drawerContentAvailableHeight, listViewComponent.contentHeight ) - MMListView { id: listViewComponent + width: parent.width + height: Math.min( root.drawerContentAvailableHeight, contentHeight ) + interactive: contentHeight > height clip: true maximumFlickVelocity: __androidUtils.isAndroid ? __style.scrollVelocityAndroid : maximumFlickVelocity } - } } } diff --git a/app/qml/components/MMListMultiselectDrawer.qml b/app/qml/components/MMListMultiselectDrawer.qml index 02837cd34..ae9b35e4a 100644 --- a/app/qml/components/MMListMultiselectDrawer.qml +++ b/app/qml/components/MMListMultiselectDrawer.qml @@ -83,15 +83,13 @@ MMDrawer { sourceComponent: defaultEmptyStateComponent } - MMScrollView { - width: parent.width - height: Math.min( listViewComponent.contentHeight, root.drawerContentAvailableHeight - internal.searchBarVerticalSpace ) - - visible: listViewComponent.count > 0 - MMListView { id: listViewComponent + width: parent.width + height: Math.min( contentHeight, root.drawerContentAvailableHeight - internal.searchBarVerticalSpace ) + + visible: count > 0 interactive: contentHeight > height clip: true @@ -124,7 +122,6 @@ MMDrawer { footer: MMListSpacer { height: __style.safeAreaBottom + __style.margin8 + ( root.multiSelect ? confirmButton.height : 0 ) } } - } } } diff --git a/app/qml/components/MMListView.qml b/app/qml/components/MMListView.qml index 280412870..56e9c3883 100644 --- a/app/qml/components/MMListView.qml +++ b/app/qml/components/MMListView.qml @@ -2,23 +2,66 @@ * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ import QtQuick +import QtQuick.Controls -ListView { +// Drop-in replacement for ListView that also shows a scrollbar on desktop. +// Wraps a real ListView in a ScrollView internally so callers can keep using +// it exactly like a plain ListView, without knowing about the wrapping. +ScrollView { id: root - // when flicking up really fast, we should go back to the first item - onVerticalOvershootChanged: { - if (verticalOvershoot < -200) { - root.contentY= -root.topMargin - root.returnToBounds(); + property alias model: listView.model + property alias delegate: listView.delegate + property alias header: listView.header + property alias footer: listView.footer + property alias section: listView.section + property alias add: listView.add + property alias addDisplaced: listView.addDisplaced + + property alias orientation: listView.orientation + property alias interactive: listView.interactive + property alias maximumFlickVelocity: listView.maximumFlickVelocity + property alias topMargin: listView.topMargin + property alias bottomMargin: listView.bottomMargin + + property alias currentIndex: listView.currentIndex + property alias count: listView.count + + property alias contentY: listView.contentY + property alias atYEnd: listView.atYEnd + + function positionViewAtIndex( index, mode ) { + listView.positionViewAtIndex( index, mode ) + } + + function positionViewAtEnd() { + listView.positionViewAtEnd() + } + + rightPadding: ScrollBar.vertical.visible ? ScrollBar.vertical.width * 2 : 0 + bottomPadding: ScrollBar.horizontal.visible ? ScrollBar.horizontal.height * 2 : 0 + + ScrollBar.vertical.policy: !__inputUtils.isMobilePlatform() && listView.orientation === ListView.Vertical && listView.contentHeight > listView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff + ScrollBar.horizontal.policy: !__inputUtils.isMobilePlatform() && listView.orientation === ListView.Horizontal && listView.contentWidth > listView.width ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff + + ListView { + id: listView + + spacing: root.spacing + + // when flicking up really fast, we should go back to the first item + onVerticalOvershootChanged: { + if (verticalOvershoot < -200) { + listView.contentY = -listView.topMargin + listView.returnToBounds(); + } } + delegateModelAccess: DelegateModel.ReadWrite } - delegateModelAccess: DelegateModel.ReadWrite -} \ No newline at end of file +} diff --git a/app/qml/components/MMToolbar.qml b/app/qml/components/MMToolbar.qml index 41e8c6847..3e1568fca 100644 --- a/app/qml/components/MMToolbar.qml +++ b/app/qml/components/MMToolbar.qml @@ -41,22 +41,17 @@ Rectangle { // center the content x: __style.safeAreaLeft - MMScrollView { - anchors.fill: parent - - contentWidth: toolbar.contentWidth - MMListView { id: toolbar onWidthChanged: root.recalculate() model: toolbarModel delegate: ( model.count === 1 ) && ( menuModel.count === 0 ) ? longBtnComp : shortBtnComp + anchors.fill: parent orientation: ListView.Horizontal interactive: false Component.onCompleted: root.initialize() } - } } // buttons shown inside main toolbar diff --git a/app/qml/form/MMFormPage.qml b/app/qml/form/MMFormPage.qml index 4cca54500..efb111df5 100644 --- a/app/qml/form/MMFormPage.qml +++ b/app/qml/form/MMFormPage.qml @@ -175,15 +175,14 @@ Page { property int tabIndex: model.TabIndex // from the repeater - MMComponents.MMScrollView { + MMComponents.MMListView { + anchors { fill: parent leftMargin: __style.pageMargins rightMargin: __style.pageMargins } - MMComponents.MMListView { - model: swipeViewRepeater.model.attributeFormProxyModel( pageDelegate.tabIndex ) clip: true @@ -207,7 +206,6 @@ Page { height: 20 * __dp } } - } } } } diff --git a/app/qml/form/MMPreviewDrawer.qml b/app/qml/form/MMPreviewDrawer.qml index f5b4b6805..5c0891313 100644 --- a/app/qml/form/MMPreviewDrawer.qml +++ b/app/qml/form/MMPreviewDrawer.qml @@ -188,12 +188,9 @@ Item { visible: internal.showFields - MMComponents.MMScrollView { - width: parent.width - height: listView.contentHeight - MMComponents.MMListView { - id: listView + width: parent.width + height: contentHeight spacing: __style.margin8 interactive: false @@ -241,7 +238,6 @@ Item { } } } - } } // HTML diff --git a/app/qml/form/components/MMFeaturesListPageDrawer.qml b/app/qml/form/components/MMFeaturesListPageDrawer.qml index 7a7c8d3e2..b463a687f 100644 --- a/app/qml/form/components/MMFeaturesListPageDrawer.qml +++ b/app/qml/form/components/MMFeaturesListPageDrawer.qml @@ -96,13 +96,12 @@ Drawer { Layout.preferredHeight: __style.spacing20 } - MMComponents.MMScrollView { - Layout.fillWidth: true - Layout.fillHeight: true - MMComponents.MMListView { id: listView + Layout.fillWidth: true + Layout.fillHeight: true + clip: true delegate: MMComponents.MMListDelegate { @@ -115,7 +114,6 @@ Drawer { footer: MMComponents.MMListSpacer { height: __style.safeAreaBottom + __style.margin8 + ( primarybtn.visible ? primarybtn.height : 0 ) } } - } } MMComponents.MMButton { diff --git a/app/qml/form/components/MMFormTabBar.qml b/app/qml/form/components/MMFormTabBar.qml index ac583efea..d7989e412 100644 --- a/app/qml/form/components/MMFormTabBar.qml +++ b/app/qml/form/components/MMFormTabBar.qml @@ -11,58 +11,49 @@ import QtQuick import QtQuick.Controls import "../../components" as MMComponents -MMComponents.MMScrollView { +MMComponents.MMListView { id: root implicitHeight: __style.row45 - property alias model: tabList.model - property alias currentIndex: tabList.currentIndex + spacing: 0 - contentWidth: tabList.contentWidth + clip: true + orientation: ListView.Horizontal - MMComponents.MMListView { - id: tabList + interactive: contentWidth > width - spacing: 0 + header: MMComponents.MMListSpacer { width: __style.margin20 } + footer: MMComponents.MMListSpacer { width: __style.margin20 } - clip: true - orientation: ListView.Horizontal + delegate: Control { + id: tabDelegate - interactive: contentWidth > width + property bool isSelected: ListView.isCurrentItem - header: MMComponents.MMListSpacer { width: __style.margin20 } - footer: MMComponents.MMListSpacer { width: __style.margin20 } + height: __style.row45 + width: contentItem.implicitWidth - delegate: Control { - id: tabDelegate + focusPolicy: Qt.NoFocus - property bool isSelected: ListView.isCurrentItem - - height: __style.row45 - width: contentItem.implicitWidth - - focusPolicy: Qt.NoFocus - - background: Rectangle { - visible: tabDelegate.isSelected - color: __style.grassColor - radius: __style.radius30 - } + background: Rectangle { + visible: tabDelegate.isSelected + color: __style.grassColor + radius: __style.radius30 + } - contentItem: MMComponents.MMText { - text: model.Name + contentItem: MMComponents.MMText { + text: model.Name - font: __style.t4 + font: __style.t4 - leftPadding: __style.margin20 - rightPadding: __style.margin20 - } + leftPadding: __style.margin20 + rightPadding: __style.margin20 + } - MouseArea { - anchors.fill: parent - onClicked: root.currentIndex = index - } + MouseArea { + anchors.fill: parent + onClicked: root.currentIndex = index } } } diff --git a/app/qml/form/editors/MMFormGalleryEditor.qml b/app/qml/form/editors/MMFormGalleryEditor.qml index 9812317c6..37221230b 100644 --- a/app/qml/form/editors/MMFormGalleryEditor.qml +++ b/app/qml/form/editors/MMFormGalleryEditor.qml @@ -35,15 +35,12 @@ MMPrivateComponents.MMBaseInput { ? qsTr( "Some features may be hidden by active filters" ) : "" - inputContent: MMComponents.MMScrollView { + inputContent: MMComponents.MMListView { + id: rowView + width: parent.width height: __style.row120 - contentWidth: rowView.contentWidth - - MMComponents.MMListView { - id: rowView - clip: true spacing: __style.spacing12 orientation: ListView.Horizontal @@ -80,7 +77,6 @@ MMPrivateComponents.MMBaseInput { header: __activeProject.projectRole !== "reader" ? addFeatureComponent : null } - } Component { id: addFeatureComponent diff --git a/app/qml/gps/MMPositionProviderPage.qml b/app/qml/gps/MMPositionProviderPage.qml index d519c2834..34ceb63ff 100644 --- a/app/qml/gps/MMPositionProviderPage.qml +++ b/app/qml/gps/MMPositionProviderPage.qml @@ -27,13 +27,12 @@ MMComponents.MMPage { width: parent.width height: parent.height - MMComponents.MMScrollView { - width: parent.width - height: parent.height - MMComponents.MMListView { id: listview + width: parent.width + height: parent.height + clip: true model: PositionProvidersModel { @@ -119,7 +118,6 @@ MMComponents.MMPage { height: __style.safeAreaBottom + __style.margin8 + connectNewReceiverButton.height } } - } MMComponents.MMButton { id: connectNewReceiverButton diff --git a/app/qml/layers/MMFeaturesListPage.qml b/app/qml/layers/MMFeaturesListPage.qml index 4f19f30a7..3d8e03970 100644 --- a/app/qml/layers/MMFeaturesListPage.qml +++ b/app/qml/layers/MMFeaturesListPage.qml @@ -65,18 +65,17 @@ MMComponents.MMPage { } } - MMComponents.MMScrollView { + MMComponents.MMListView { + id: listView + + width: parent.width + anchors { top: filterBanner.visible ? filterBanner.bottom : searchBar.bottom bottom: parent.bottom - left: parent.left - right: parent.right topMargin: filterBanner.visible ? __style.spacing10 : __style.spacing20 } - MMComponents.MMListView { - id: listView - model: MM.LayerFeaturesModel { id: featuresModel @@ -98,7 +97,6 @@ MMComponents.MMPage { height: __style.margin20 + ( root.hasToolbar ? 0 : __style.safeAreaBottom ) + ( addButton.visible ? addButton.height : 0 ) } } - } MMComponents.MMBusyIndicator { anchors.centerIn: parent diff --git a/app/qml/layers/MMLayersList.qml b/app/qml/layers/MMLayersList.qml index d6fddf39f..0d2fc53e9 100644 --- a/app/qml/layers/MMLayersList.qml +++ b/app/qml/layers/MMLayersList.qml @@ -15,7 +15,7 @@ import QtQuick.Layouts import "../components" as MMComponents import "../inputs" -MMComponents.MMScrollView { +MMComponents.MMListView { id: root property var basemodel: null @@ -27,49 +27,43 @@ MMComponents.MMScrollView { signal nodeClicked( var node, string nodeType, string nodeName ) signal nodeVisibilityClicked( var node ) - property alias footer: listView.footer + model: DelegateModel { + id: delegatemodel - MMComponents.MMListView { - id: listView + model: root.basemodel - model: DelegateModel { - id: delegatemodel + delegate: MMComponents.MMListDelegate { - model: root.basemodel + property bool secondaryTextVisible: root.showNodePath && model.nodePath - delegate: MMComponents.MMListDelegate { + text: model.display + secondaryText: secondaryTextVisible ? model.nodePath: "" - property bool secondaryTextVisible: root.showNodePath && model.nodePath + leftContent: Image { + width: __style.icon24 + height: width + sourceSize: Qt.size( width, height) + cache: false // important! Otherwise pixmap providers would not be called for the same id again - text: model.display - secondaryText: secondaryTextVisible ? model.nodePath: "" - - leftContent: Image { - width: __style.icon24 - height: width - sourceSize: Qt.size( width, height) - cache: false // important! Otherwise pixmap providers would not be called for the same id again - - source: root.imageProviderPath + model.serializedNode - } + source: root.imageProviderPath + model.serializedNode + } - rightContent: MMComponents.MMSwitch { - visible: model.nodeIsVisible !== "" - checked: model.nodeIsVisible === "yes" - onReleased: { - root.nodeVisibilityClicked( model.node ) - } + rightContent: MMComponents.MMSwitch { + visible: model.nodeIsVisible !== "" + checked: model.nodeIsVisible === "yes" + onReleased: { + root.nodeVisibilityClicked( model.node ) } - - onClicked: root.nodeClicked( model.node, model.nodeType, model.display ) } + + onClicked: root.nodeClicked( model.node, model.nodeType, model.display ) } + } - Component.onCompleted: { - if ( root.parentNodeIndex ) - { - delegatemodel.rootIndex = root.parentNodeIndex - } + Component.onCompleted: { + if ( root.parentNodeIndex ) + { + delegatemodel.rootIndex = root.parentNodeIndex } } } diff --git a/app/qml/project/MMProjectIssuesPage.qml b/app/qml/project/MMProjectIssuesPage.qml index 55546624a..9812cc085 100644 --- a/app/qml/project/MMProjectIssuesPage.qml +++ b/app/qml/project/MMProjectIssuesPage.qml @@ -30,12 +30,11 @@ MMComponents.MMPage { onBackClicked: root.visible = false pageHeader.title: qsTr("Project issues") - pageContent: MMComponents.MMScrollView { - anchors.fill: parent - - MMComponents.MMListView { + pageContent: MMComponents.MMListView { id: mainList + anchors.fill: parent + model: root.projectIssuesModel spacing: __style.margin12 @@ -94,5 +93,4 @@ MMComponents.MMPage { } } } - } } diff --git a/app/qml/project/MMProjectList.qml b/app/qml/project/MMProjectList.qml index 1a435c01c..7636434b9 100644 --- a/app/qml/project/MMProjectList.qml +++ b/app/qml/project/MMProjectList.qml @@ -47,56 +47,55 @@ Item { controllerModel.listProjects( searchText ) } - MMComponents.MMScrollView { - anchors.fill: parent - - MMComponents.MMListView { - id: listview + MMComponents.MMListView { + id: listview - Component.onCompleted: { - // set proper footer (add project / fetch more) - if ( root.projectModelType === MM.ProjectsModel.LocalProjectsModel ) { - listview.footer = addProjectComponent - } - else - { - listview.footer = loadingSpinnerComponent - } + Component.onCompleted: { + // set proper footer (add project / fetch more) + if ( root.projectModelType === MM.ProjectsModel.LocalProjectsModel ) { + listview.footer = addProjectComponent + } + else + { + listview.footer = loadingSpinnerComponent } + } - onAtYEndChanged: { - if ( atYEnd ) { // user reached end of the list - if ( controllerModel.hasMoreProjects && !controllerModel.isLoading ) { - controllerModel.fetchAnotherPage( viewModel.searchExpression ) - } + onAtYEndChanged: { + if ( atYEnd ) { // user reached end of the list + if ( controllerModel.hasMoreProjects && !controllerModel.isLoading ) { + controllerModel.fetchAnotherPage( viewModel.searchExpression ) } } + } - clip: true - spacing: root.spacing + anchors.fill: parent + clip: true + spacing: root.spacing - maximumFlickVelocity: __androidUtils.isAndroid ? __style.scrollVelocityAndroid : maximumFlickVelocity + maximumFlickVelocity: __androidUtils.isAndroid ? __style.scrollVelocityAndroid : maximumFlickVelocity - // Proxy model with source projects model - model: MM.ProjectsProxyModel { - id: viewModel + // Proxy model with source projects model + model: MM.ProjectsProxyModel { + id: viewModel - activeProjectAlwaysFirst: root.activeProjectAlwaysFirst - projectSourceModel: MM.ProjectsModel { - id: controllerModel + activeProjectAlwaysFirst: root.activeProjectAlwaysFirst + projectSourceModel: MM.ProjectsModel { + id: controllerModel - merginApi: __merginApi - localProjectsManager: __localProjectsManager - syncManager: __syncManager - modelType: root.projectModelType - } + merginApi: __merginApi + localProjectsManager: __localProjectsManager + syncManager: __syncManager + modelType: root.projectModelType } + } - // Project delegate - delegate: MMProjectComponents.MMProjectDelegate { - id: projectDelegate + // Project delegate + delegate: MMProjectComponents.MMProjectDelegate { + id: projectDelegate - height: visible ? implicitHeight : 0 + width: ListView.view.width + height: visible ? implicitHeight : 0 projectDisplayName: root.projectModelType === MM.ProjectsModel.WorkspaceProjectsModel ? model.ProjectName : model.ProjectFullName projectId: model.ProjectId ? model.ProjectId : "" @@ -179,7 +178,6 @@ Item { onShowChangesRequested: root.showLocalChangesRequested( projectId ) } } - } Component { id: loadingSpinnerComponent diff --git a/app/qml/project/MMProjectStatusPage.qml b/app/qml/project/MMProjectStatusPage.qml index 702f60bc7..295d1be3e 100644 --- a/app/qml/project/MMProjectStatusPage.qml +++ b/app/qml/project/MMProjectStatusPage.qml @@ -47,15 +47,14 @@ MMComponents.MMPage { MMComponents.MMListSpacer { id: spacer; height: __style.spacing40 } // With changes content - MMComponents.MMScrollView { + MMComponents.MMListView { + id: statusList + anchors.top: spacer.bottom anchors.bottom: parent.bottom width: parent.width visible: statusPanel.hasChanges - MMComponents.MMListView { - id: statusList - model: __merginProjectStatusModel spacing: __style.margin8 @@ -145,6 +144,5 @@ MMComponents.MMPage { } } } - } } } diff --git a/app/qml/project/MMProjectWizardPage.qml b/app/qml/project/MMProjectWizardPage.qml index f3e40aa1b..93a65a10b 100644 --- a/app/qml/project/MMProjectWizardPage.qml +++ b/app/qml/project/MMProjectWizardPage.qml @@ -59,14 +59,12 @@ MMComponents.MMPage { verticalAlignment: Text.AlignVCenter } - MMComponents.MMScrollView { - width: parent.width - height: parent.height - __style.margin20 - projectNameField.height - __style.margin20 - attributesLabel.height - MMComponents.MMListView { id: fieldList model: fieldsModel + width: parent.width + height: parent.height - __style.margin20 - projectNameField.height - __style.margin20 - attributesLabel.height clip: true spacing: __style.margin20 @@ -117,7 +115,6 @@ MMComponents.MMPage { } } } - } } } diff --git a/app/qml/settings/MMChangelogPage.qml b/app/qml/settings/MMChangelogPage.qml index a2ffec0a0..92b9bf547 100644 --- a/app/qml/settings/MMChangelogPage.qml +++ b/app/qml/settings/MMChangelogPage.qml @@ -21,11 +21,10 @@ MMPage { pageHeader.title: qsTr( "Changelog" ) pageBottomMarginPolicy: MMPage.BottomMarginPolicy.PaintBehindSystemBar - pageContent: MMScrollView { + pageContent: MMListView { width: parent.width height: parent.height - MMListView { spacing: __style.spacing20 model: root.model @@ -50,7 +49,6 @@ MMPage { footer: root.dataNotReady ? noItemDelegate : spacer } - } Component { id: spacer