Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/code_style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,59 @@ jobs:
fi

echo "OK: No direct StackView usage found."

no_listview:
name: No direct ListView usage in QML
if: ( github.repository == 'MerginMaps/mobile' ) && (!contains(github.event.head_commit.message, 'Translate '))
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Check for direct ListView instantiation in QML files
run: |
# MMListView.qml is the approved wrapper — exclude it from the search.
# Any other QML file with a bare "ListView" is using the raw Qt component
# instead of the wrapper, which breaks Squish test support.
matches=$(grep -rn --include="*.qml" \
--exclude="MMListView.qml" \
'\<ListView[[:space:]]*{' \
app/qml/ || true)

if [ -n "$matches" ]; then
echo "ERROR: Direct ListView usage found. Use MMListView instead:"
echo ""
echo "$matches"
exit 1
fi

echo "OK: No direct ListView usage found."

no_scrollview:
name: No direct ScrollView usage in QML
if: ( github.repository == 'MerginMaps/mobile' ) && (!contains(github.event.head_commit.message, 'Translate '))
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Check for direct ScrollView instantiation in QML files
run: |
# MMScrollView.qml and MMListView.qml are the approved wrappers (the
# latter uses a plain ScrollView internally) — exclude both from the
# search. Any other QML file with a bare "ScrollView" is using the raw
# Qt component instead of the wrapper, which breaks Squish test support.
matches=$(grep -rn --include="*.qml" \
--exclude="MMScrollView.qml" \
--exclude="MMListView.qml" \
'\<ScrollView[[:space:]]*{' \
app/qml/ || true)

if [ -n "$matches" ]; then
echo "ERROR: Direct ScrollView usage found. Use MMScrollView instead:"
echo ""
echo "$matches"
exit 1
fi

echo "OK: No direct ScrollView usage found."
2 changes: 1 addition & 1 deletion app/inpututils.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class InputUtils: public QObject

/** InputApp platform */
static QString appPlatform();
static bool isMobilePlatform();
Q_INVOKABLE static bool isMobilePlatform();

static QString appDataDir();

Expand Down
4 changes: 2 additions & 2 deletions app/qml/components/MMColorPicker.qml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls

ScrollView {
MMScrollView {
id: root

required property list<color> colors
Expand All @@ -21,8 +21,8 @@ ScrollView {
signal activeColorChangeRequested( color newColor )

height: scrollRow.height
contentWidth: scrollRow.width
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff

Row {
id: scrollRow
Expand Down
74 changes: 66 additions & 8 deletions app/qml/components/MMListView.qml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about wrapping the ScrollView in another Item and expose just that, so if used people won't accidentally modify ScrollView property instead of expected Listview property

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about wrapping the ScrollView in another Item and expose just that, so if used people won't accidentally modify ScrollView property instead of expected Listview property

Yeah, you're right.

ScrollView already carries contentWidth/spacing/padding (inherited from Pane/Control), so exposing it directly on MMListView means someone could set one of those thinking they're touching the real ListView, and get silently routed to the wrapper's own copy instead. Already hit this with spacing - Control.spacing is FINAL so I couldn't even alias around it.

Fix: root MMListView in a plain Item instead of ScrollView. Item is the base class Control/Pane/ScrollView build on top of, so it doesn't carry any of that - only what we explicitly alias gets exposed.

Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,74 @@
***************************************************************************/

import QtQuick
import QtQuick.Controls

ListView {
// Drop-in replacement for ListView that also shows a scrollbar on desktop.
// Rooted in a plain Item rather than ScrollView, so only the properties
// aliased below are exposed - ScrollView/Pane/Control have their own
// contentWidth/spacing/padding that would otherwise shadow ListView's.

Item {
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 spacing: listView.spacing
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 contentWidth: listView.contentWidth
property alias contentHeight: listView.contentHeight
property alias atYEnd: listView.atYEnd

implicitWidth: scrollView.implicitWidth
implicitHeight: scrollView.implicitHeight

function positionViewAtIndex( index, mode ) {
listView.positionViewAtIndex( index, mode )
}

function positionViewAtEnd() {
listView.positionViewAtEnd()
}

ScrollView {
id: scrollView

anchors.fill: parent

// reserve room for the scrollbar so content isn't drawn under it
rightPadding: ScrollBar.vertical.visible ? ScrollBar.vertical.width * 2 : 0
bottomPadding: ScrollBar.horizontal.visible ? ScrollBar.horizontal.height * 2 : 0

// non-interactive lists can't be scrolled, so never show a scrollbar for them
ScrollBar.vertical.policy: listView.interactive && !__inputUtils.isMobilePlatform() && listView.orientation === ListView.Vertical && listView.contentHeight > listView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: listView.interactive && !__inputUtils.isMobilePlatform() && listView.orientation === ListView.Horizontal && listView.contentWidth > listView.width ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff

ListView {
id: listView

// 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
}
}
4 changes: 3 additions & 1 deletion app/qml/components/MMScrollView.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
5 changes: 5 additions & 0 deletions app/qml/components/MMToolbar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ Rectangle {
toolbarModel.clear()
menuModel.clear()

// defer so old delegates finish being removed before new ones are added
Qt.callLater( root.populateToolbar )
}

function populateToolbar() {
// find how many visible buttons we need to place to toolbar or menu
let visibleButtonsCount = 0
for ( let i = 0; i < root.model.count; ++i ) {
Expand Down
7 changes: 5 additions & 2 deletions app/qml/form/editors/MMFormGalleryEditor.qml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ MMPrivateComponents.MMBaseInput {
}

delegate: MMComponents.MMPhotoCard{
size: rowView.height
// fixed margin, not live scrollbar padding - these are square cards, so
// shrinking height also shrinks width, risking an infinite resize loop
size: rowView.height - __style.margin12

imageSource: {
let absolutePath = model.PhotoPath
Expand Down Expand Up @@ -82,9 +84,10 @@ MMPrivateComponents.MMBaseInput {
id: addFeatureComponent

Row {
height: rowView.height - __style.margin12

Rectangle {
height: rowView.height
height: parent.height
width: height

radius: __style.radius20
Expand Down
3 changes: 1 addition & 2 deletions app/qml/gps/MMSelectionDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,11 @@ MMComponents.MMDrawer {
}
}

ScrollView {
MMComponents.MMScrollView {
width: parent.width
height: scrollRow.height

ScrollBar.vertical.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.interactive: true

contentHeight: scrollRow.height
Expand Down
3 changes: 1 addition & 2 deletions app/qml/gps/MMStakeoutDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,10 @@ MMDrawer {
visible: distanceState.state === "closeRange"
}

ScrollView {
MMScrollView {
id: gpsScrollView

width: parent.width
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOff

visible: distanceState.state === "closeRange"
Expand Down
8 changes: 5 additions & 3 deletions app/qml/project/MMProjectStatusPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ MMComponents.MMPage {
/* Table name within single file */
id: mainText

Layout.fillWidth: true

text:itemText
font: __style.p6
color: __style.nightColor
Expand All @@ -117,7 +119,7 @@ MMComponents.MMPage {
/* Added rows for table */
id: addedItem

width: delegateItem.width
Layout.fillWidth: true
count: inserts
visible: inserts > 0
type: MM.MerginProjectStatusModel.Added
Expand All @@ -126,7 +128,7 @@ MMComponents.MMPage {
MMProjectComponents.MMProjectStatusItem {
/* Edited rows for table */
id: editedItem
width: delegateItem.width
Layout.fillWidth: true
count: updates
visible: updates > 0
type: MM.MerginProjectStatusModel.Updated
Expand All @@ -136,7 +138,7 @@ MMComponents.MMPage {
/* Deleted rows for table */
id: deletedItem

width: delegateItem.width
Layout.fillWidth: true
count: deletes
visible: deletes > 0
type: MM.MerginProjectStatusModel.Deleted
Expand Down
3 changes: 3 additions & 0 deletions app/qml/project/components/MMProjectDelegate.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions app/qml/settings/MMAboutPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ MMPage {

pageHeader.title: qsTr( "About" )

pageContent: ScrollView {
pageContent: MMScrollView {

width: parent.width
height: parent.height

contentWidth: availableWidth // to only scroll vertically
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff

ColumnLayout {
width: parent.width
Expand Down
Loading