6import QGroundControl.Controls
7import QGroundControl.AppSettings
12 z: QGroundControl.zOrderTopMost
14 readonly property real _defaultTextHeight: ScreenTools.defaultFontPixelHeight
15 readonly property real _defaultTextWidth: ScreenTools.defaultFontPixelWidth
16 readonly property real _horizontalMargin: _defaultTextWidth / 2
17 readonly property real _verticalMargin: _defaultTextHeight / 2
19 property bool _first: true
20 property bool _commingFromRIDSettings: false
21 property int _selectedPageIndex: -1
22 property int _selectedSectionIndex: -1
23 property var _expandedPages: ({}) // pageIndex -> bool
24 property int _expandedRevision: 0 // bumped to trigger re-evaluation
25 property string _searchQuery: ""
27 function _setExpanded(pageIndex, value) {
28 _expandedPages[pageIndex] = value
32 function _isExpanded(pageIndex) {
33 void _expandedRevision // create binding dependency
34 return !!_expandedPages[pageIndex]
37 // Search: returns array of matching section indices for a page, or empty if no match
38 function _matchingSections(pageIndex) {
39 var query = _searchQuery.toLowerCase().trim()
40 if (query === "") return [] // empty = no filtering
42 var entry = settingsPagesModel.get(pageIndex)
45 // Check English search terms
46 var termsStr = entry.searchTerms
49 if (termsStr && termsStr !== "") {
51 var terms = JSON.parse(termsStr)
52 for (var i = 0; i < terms.length; i++) {
53 if (terms[i].terms.indexOf(query) !== -1) {
54 matched[terms[i].section] = true
55 matches.push(terms[i].section)
58 } catch(e) { console.warn("AppSettings: JSON parse error in searchTerms:", e) }
61 // Check translatable terms (translated at runtime)
62 var trStr = entry.translatableTerms
63 if (trStr && trStr !== "") {
65 var trTerms = JSON.parse(trStr)
66 for (var j = 0; j < trTerms.length; j++) {
67 if (matched[trTerms[j].section]) continue
68 var ctx = trTerms[j].context
69 var tList = trTerms[j].terms
70 for (var k = 0; k < tList.length; k++) {
71 if (qsTranslate(ctx, tList[k]).toLowerCase().indexOf(query) !== -1) {
72 matched[trTerms[j].section] = true
73 matches.push(trTerms[j].section)
78 } catch(e) { console.warn("AppSettings: JSON parse error in translatableTerms:", e) }
84 // Does this page have any search matches? (or is search empty = show all)
85 function _pageMatchesSearch(pageIndex) {
86 if (_searchQuery.trim() === "") return true
87 return _matchingSections(pageIndex).length > 0
90 function _navigateTo(pageIndex, sectionIndex) {
91 var entry = settingsPagesModel.get(pageIndex)
92 if (!entry || entry.name === "Divider") return
95 _selectedSectionIndex = sectionIndex
97 if (_selectedPageIndex !== pageIndex) {
98 _selectedPageIndex = pageIndex
99 rightPanel.source = url
102 // Apply section filter after the page is loaded
103 if (rightPanel.item && typeof rightPanel.item.sectionFilter !== "undefined") {
104 rightPanel.item.sectionFilter = sectionIndex
108 function showSettingsPage(settingsPage) {
109 for (var i = 0; i < settingsPagesModel.count; i++) {
110 var entry = settingsPagesModel.get(i)
111 if (entry && entry.name === settingsPage) {
118 // This need to block click event leakage to underlying map.
123 QGCPalette { id: qgcPal }
125 Component.onCompleted: {
126 // Find and select the default page
127 var targetUrl = globals.commingFromRIDIndicator
128 ? "qrc:/qml/QGroundControl/AppSettings/RemoteIDSettings.qml"
129 : "qrc:/qml/QGroundControl/AppSettings/GeneralSettings.qml"
130 globals.commingFromRIDIndicator = false
132 for (var i = 0; i < settingsPagesModel.count; i++) {
133 var entry = settingsPagesModel.get(i)
134 if (entry && entry.url === targetUrl) {
143 function onLoaded() {
144 if (rightPanel.item && typeof rightPanel.item.sectionFilter !== "undefined") {
145 rightPanel.item.sectionFilter = _selectedSectionIndex
150 SettingsPagesModel { id: settingsPagesModel }
154 width: Math.max(buttonColumn.implicitWidth + _horizontalMargin, ScreenTools.defaultFontPixelWidth * 22)
155 anchors.topMargin: _verticalMargin
156 anchors.top: parent.top
157 anchors.bottom: parent.bottom
158 anchors.leftMargin: _horizontalMargin
159 anchors.left: parent.left
160 spacing: _verticalMargin / 2
164 Layout.fillWidth: true
165 placeholderText: qsTr("Search settings...")
168 settingsView._searchQuery = text
174 objectName: "settings_buttonList"
175 Layout.fillWidth: true
176 Layout.fillHeight: true
177 contentHeight: buttonColumn.height + _verticalMargin
178 flickableDirection: Flickable.VerticalFlick
187 model: settingsPagesModel
192 Layout.fillWidth: true
194 required property int index
195 required property var model
197 property string pageName: model.name ?? ""
198 property string pageUrl: model.url ?? ""
199 property string pageIconUrl: model.iconUrl ?? ""
200 property var pageVisible: model.pageVisible ?? function() { return true }
201 property var pageSections: {
203 var trStr = model.translatableTerms
204 if (trStr && trStr !== "") {
205 var trTerms = JSON.parse(trStr)
206 return trTerms.map(function(t) {
207 if (!t.terms || t.terms.length === 0) return ""
208 return qsTranslate(t.context, t.terms[0])
209 }).filter(function(s) { return s !== "" })
211 var s = model.sections
212 return (s && s !== "") ? JSON.parse(s) : []
214 console.warn("AppSettings: JSON parse error in pageSections:", e)
218 property bool isSelected: settingsView._selectedPageIndex === index
219 property bool hasMultipleSections: pageSections.length > 1
220 property bool isSearching: settingsView._searchQuery.trim() !== ""
221 property bool matchesSearch: settingsView._pageMatchesSearch(index)
222 property bool isExpanded: hasMultipleSections && (isSearching ? matchesSearch : settingsView._isExpanded(index))
225 if (pageName === "Divider") return !isSearching
226 if (!pageVisible()) return false
227 if (isSearching) return matchesSearch
233 Layout.fillWidth: true
234 height: ScreenTools.defaultFontPixelHeight / 2
235 visible: pageName === "Divider"
240 Layout.fillWidth: true
241 objectName: "settingsButton_" + (model.nameKey ?? pageName)
243 icon.source: pageIconUrl
244 expandable: hasMultipleSections
246 checked: isSelected && settingsView._selectedSectionIndex === -1
247 visible: pageName !== "Divider" && pageVisible()
250 if (mainWindow.allowViewSwitch()) {
251 settingsView._navigateTo(index, -1)
252 if (hasMultipleSections) {
253 // Toggle expand/collapse when re-clicking the same page
254 if (isSelected && isExpanded) {
255 settingsView._setExpanded(index, false)
256 } else if (!isExpanded) {
257 settingsView._setExpanded(index, true)
264 if (!mainWindow.allowViewSwitch()) {
267 var expanding = !isExpanded
268 settingsView._setExpanded(index, expanding)
269 if (!expanding && isSelected) {
270 settingsView._navigateTo(index, -1)
275 // Section sub-items (indented, shown when page is expanded)
277 model: isExpanded ? pageSections : []
281 Layout.fillWidth: true
282 padding: ScreenTools.defaultFontPixelWidth * 0.75
283 leftPadding: ScreenTools.defaultFontPixelWidth * 3
284 hoverEnabled: !ScreenTools.isMobile
286 property int sectionIndex: index
287 property bool sectionChecked: pageColumn.isSelected && settingsView._selectedSectionIndex === sectionIndex
288 property bool sectionMatchesSearch: {
289 if (!pageColumn.isSearching) return true
290 var matches = settingsView._matchingSections(pageColumn.index)
291 return matches.indexOf(sectionIndex) !== -1
293 property bool sectionContentVisible: {
294 if (!pageColumn.isSelected) return true
295 if (!rightPanel.item) return true
296 if (typeof rightPanel.item.sectionVisible !== "function") return true
297 return rightPanel.item.sectionVisible(sectionIndex)
299 property color textColor: sectionChecked || pressed ? qgcPal.buttonHighlightText : qgcPal.buttonText
300 visible: sectionMatchesSearch && sectionContentVisible
302 background: Rectangle {
303 color: qgcPal.buttonHighlight
304 opacity: sectionBtn.sectionChecked || sectionBtn.pressed ? 1 : sectionBtn.enabled && sectionBtn.hovered ? 0.2 : 0
305 radius: ScreenTools.defaultFontPixelWidth / 2
308 contentItem: QGCLabel {
310 color: sectionBtn.textColor
311 font.pointSize: ScreenTools.defaultFontPointSize * 0.9
312 horizontalAlignment: Text.AlignLeft
316 if (mainWindow.allowViewSwitch()) {
317 settingsView._navigateTo(pageColumn.index, sectionIndex)
330 anchors.topMargin: _verticalMargin
331 anchors.bottomMargin: _verticalMargin
332 anchors.leftMargin: _horizontalMargin
333 anchors.left: leftPanel.right
334 anchors.top: parent.top
335 anchors.bottom: parent.bottom
337 color: qgcPal.windowShade
343 objectName: "settings_rightPanel"
344 anchors.leftMargin: _horizontalMargin
345 anchors.rightMargin: _horizontalMargin
346 anchors.topMargin: _verticalMargin
347 anchors.bottomMargin: _verticalMargin
348 anchors.left: divider.right
349 anchors.right: parent.right
350 anchors.top: parent.top
351 anchors.bottom: parent.bottom