implement search bar functionality in settings
This commit is contained in:
@@ -9,6 +9,9 @@ import qs.Config
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
// Enables a smooth scroll animation only for search jumps, so normal
|
||||
// flicking stays instant.
|
||||
property bool animateScroll: false
|
||||
readonly property int cappedWidth: Math.min(800, width)
|
||||
default property Item contentChild
|
||||
readonly property alias flickable: flickable
|
||||
@@ -16,8 +19,106 @@ ColumnLayout {
|
||||
required property SettingsState sState
|
||||
required property string title
|
||||
|
||||
function applySearchAnchor(): void {
|
||||
if (!sState.searchAnchor)
|
||||
return;
|
||||
scrollRetry.tries = 0;
|
||||
scrollRetry.lastHeight = -1;
|
||||
scrollRetry.stableFrames = 0;
|
||||
scrollRetry.restart();
|
||||
}
|
||||
|
||||
function findAnchor(item: Item, anchor: string): Item {
|
||||
if (!item)
|
||||
return null;
|
||||
if (item.settingAnchor !== undefined && item.settingAnchor === anchor) // qmllint disable missing-property
|
||||
return item;
|
||||
const kids = item.children;
|
||||
for (let i = 0; i < kids.length; i++) {
|
||||
const found = findAnchor(kids[i], anchor);
|
||||
if (found)
|
||||
return found;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Flash a row without scrolling (used when re-selecting the current setting).
|
||||
function highlightAnchor(anchor: string): void {
|
||||
const row = findAnchor(contentChild, anchor);
|
||||
if (row && row.flashHighlight !== undefined) // qmllint disable missing-property
|
||||
row.flashHighlight(); // qmllint disable missing-property
|
||||
}
|
||||
|
||||
// When the settings search jumps to this page, scroll to the matching row.
|
||||
function scrollToAnchor(anchor: string): bool {
|
||||
if (!anchor || !contentChild)
|
||||
return false;
|
||||
const row = findAnchor(contentChild, anchor);
|
||||
if (!row)
|
||||
return false;
|
||||
const pos = row.mapToItem(flickable.contentItem, 0, 0);
|
||||
// Land the row below the top fade so it isn't dimmed by the edge effect,
|
||||
// clamped to the flickable's real scroll range (which includes margins).
|
||||
const inset = flickable.height * flickable.fadeAmount + Appearance.padding.large;
|
||||
const minY = -flickable.topMargin;
|
||||
const maxY = Math.max(minY, flickable.contentHeight + flickable.bottomMargin - flickable.height);
|
||||
const target = Math.max(minY, Math.min(pos.y - inset, maxY));
|
||||
root.animateScroll = true;
|
||||
flickable.contentY = target;
|
||||
Qt.callLater(() => root.animateScroll = false);
|
||||
if (row.flashHighlight !== undefined) // qmllint disable missing-property
|
||||
row.flashHighlight(); // qmllint disable missing-property
|
||||
return true;
|
||||
}
|
||||
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
Component.onCompleted: applySearchAnchor()
|
||||
|
||||
Timer {
|
||||
id: scrollRetry
|
||||
|
||||
property real lastHeight: -1
|
||||
property int stableFrames: 0
|
||||
property int tries: 0
|
||||
|
||||
interval: 16
|
||||
repeat: true
|
||||
|
||||
onTriggered: {
|
||||
// Pages like the ethernet detail load their content asynchronously
|
||||
// (device info, IP config), so the layout keeps growing for a while.
|
||||
// Wait until contentHeight has held steady for a few frames (or we've
|
||||
// waited long enough) before scrolling, so the target doesn't drift.
|
||||
const h = flickable.contentHeight;
|
||||
if (h === lastHeight && h > flickable.height)
|
||||
stableFrames++;
|
||||
else
|
||||
stableFrames = 0;
|
||||
lastHeight = h;
|
||||
|
||||
const ready = stableFrames >= 3 || tries >= 30;
|
||||
if (ready) {
|
||||
if (root.scrollToAnchor(root.sState.searchAnchor))
|
||||
root.sState.searchAnchor = "";
|
||||
stop();
|
||||
}
|
||||
tries++;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onHighlightSetting(anchor: string): void {
|
||||
root.highlightAnchor(anchor);
|
||||
}
|
||||
|
||||
function onSearchAnchorChanged(): void {
|
||||
root.applySearchAnchor();
|
||||
}
|
||||
|
||||
target: root.sState
|
||||
}
|
||||
|
||||
MouseArea { // Prevent clicks from reaching flickable
|
||||
Layout.bottomMargin: -flickable.topMargin // Extra height to block clicks on flickable top margin
|
||||
|
||||
@@ -69,6 +170,14 @@ ColumnLayout {
|
||||
fadeAmount: 0.1
|
||||
topMargin: Appearance.padding.large
|
||||
|
||||
Behavior on contentY {
|
||||
enabled: root.animateScroll
|
||||
|
||||
Anim {
|
||||
type: Anim.DefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: flickable.focus = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user