4pragma ComponentBehavior: Bound
7import QtQuick.Templates as T
63 enum SelectionMode { SingleSelection, MultiSelection }
66 property int selectionMode: SegmentedButtonGroup.SingleSelection
76 property bool selectionRequired: selectionMode === SegmentedButtonGroup.SingleSelection
85 property list<int> selectedIndexes: []
93 signal selectionChanged(list<int> selectedIndexes)
95 property bool __ready:
false
96 property bool __updatingSelection:
false
97 property bool __updatingFocus:
false
98 property bool __syncingChildren:
false
99 property var __effectiveSelection: []
100 property var __selectedObjects: []
101 property var __knownChildren: []
102 property list<MD.SegmentedButton> __visibleSegments: []
103 property real __naturalSegmentWidth: 0
104 property real __naturalHeight: 0
106 implicitWidth: __visibleSegments.length * __naturalSegmentWidth + leftPadding + rightPadding
107 implicitHeight: __naturalHeight + topPadding + bottomPadding
109 focusPolicy: Qt.NoFocus
110 Accessible.role: Accessible.Grouping
112 contentItem: SegmentedButtonLayout {
113 segments: control.__visibleSegments
114 mirrored: control.mirrored
117 function __arraysEqual(a, b) {
118 if (a.length !== b.length)
120 for (let i = 0; i < a.length; ++i) {
127 function __indexOf(item) {
128 for (let i = 0; i < count; ++i) {
129 if (itemAt(i) === item)
139 function __firstRequiredIndex(): int {
141 for (let i = 0; i < count; ++i) {
142 const segment = __segmentAt(i);
147 if (segment.enabled && segment.visible)
153 function __canonicalIndexes(candidate) {
155 for (let i = 0; i < candidate.length; ++i) {
156 const index = Number(candidate[i]);
157 if (Number.isInteger(index) && index >= 0 && index < count
158 && __segmentAt(index) && result.indexOf(index) < 0)
161 result.sort((a, b) => a - b);
162 if (selectionMode === SegmentedButtonGroup.SingleSelection && result.length > 1)
164 if (selectionRequired && result.length === 0) {
165 const fallback = __firstRequiredIndex();
167 result.push(fallback);
172 function __commitSelection(candidate) {
173 if (!__ready || __updatingSelection)
175 const canonical = __canonicalIndexes(candidate);
176 const changed = !__arraysEqual(canonical, __effectiveSelection);
177 __updatingSelection =
true;
178 if (!__arraysEqual(Array.from(selectedIndexes), canonical))
179 selectedIndexes = canonical;
180 for (let i = 0; i < count; ++i) {
181 const segment = __segmentAt(i);
183 segment.checked = canonical.indexOf(i) >= 0;
185 __effectiveSelection = canonical.slice();
186 __selectedObjects = canonical.map(index => itemAt(index));
187 __updatingSelection =
false;
189 selectionChanged(canonical);
193 if (!__ready || __updatingSelection)
195 const index = __indexOf(segment);
199 let candidate = __selectedObjects.map(item => __indexOf(item)).filter(value => value >= 0);
200 if (segment.checked) {
201 if (selectionMode === SegmentedButtonGroup.SingleSelection)
203 else if (candidate.indexOf(index) < 0)
204 candidate.push(index);
205 }
else if (!(selectionRequired && candidate.length === 1 && candidate[0] === index)) {
206 candidate = candidate.filter(value => value !== index);
208 __commitSelection(candidate);
212 if (!segment || segment.__segmentedButtonGroup !== control)
214 segment.__segmentedButtonGroup = null;
215 segment.__leftEnd =
true;
216 segment.__rightEnd =
true;
217 segment.focusPolicy = Qt.StrongFocus;
218 segment.width = Qt.binding(() => segment.implicitWidth);
219 segment.height = Qt.binding(() => segment.implicitHeight);
224 function __syncChildren() {
227 if (__syncingChildren) {
228 Qt.callLater(__syncChildren);
231 __syncingChildren =
true;
234 let naturalWidth = 0;
235 let naturalHeight = 0;
236 for (let i = 0; i < count; ++i) {
237 const segment = __segmentAt(i);
240 current.push(segment);
241 segment.__segmentedButtonGroup = control;
242 segment.checkable =
true;
243 segment.autoExclusive =
false;
244 if (segment.visible) {
245 visible.push(segment);
246 naturalWidth = Math.max(naturalWidth, segment.implicitWidth);
247 naturalHeight = Math.max(naturalHeight, segment.implicitHeight);
250 for (
const oldItem of __knownChildren) {
251 if (current.indexOf(oldItem) < 0)
252 __releaseChild(oldItem);
254 __knownChildren = current;
255 __naturalSegmentWidth = naturalWidth;
256 __naturalHeight = naturalHeight;
257 __visibleSegments = visible;
258 __commitSelection(__selectedObjects.map(item => __indexOf(item)).filter(index => index >= 0));
259 __updateFocusPolicies();
260 __syncingChildren =
false;
263 function __scheduleSync() {
265 Qt.callLater(__syncChildren);
268 function __focusableIndexes() {
270 for (let i = 0; i < count; ++i) {
271 const segment = __segmentAt(i);
272 if (segment && segment.enabled && segment.visible)
278 function __updateFocusPolicies() {
279 if (!__ready || __updatingFocus)
281 __updatingFocus =
true;
282 const indexes = __focusableIndexes();
283 const focusIndex = indexes.indexOf(currentIndex) >= 0 ? currentIndex
284 : (indexes.length > 0 ? indexes[0] : -1);
285 if (currentIndex !== focusIndex)
286 currentIndex = focusIndex;
290 for (let i = 0; i < count; ++i) {
291 const segment = __segmentAt(i);
292 if (segment && segment.activeFocus && i !== focusIndex) {
293 const next = __segmentAt(focusIndex);
295 next.forceActiveFocus(Qt.TabFocusReason);
297 segment.focus =
false;
301 for (let i = 0; i < count; ++i) {
302 const segment = __segmentAt(i);
304 segment.focusPolicy = i === focusIndex ? Qt.StrongFocus : Qt.ClickFocus;
306 __updatingFocus =
false;
310 const index = __indexOf(segment);
312 currentIndex = index;
316 const indexes = __focusableIndexes();
317 if (indexes.length === 0)
320 if (event.key === Qt.Key_Home)
322 else if (event.key === Qt.Key_End)
323 target = indexes[indexes.length - 1];
324 else if (event.key === Qt.Key_Left || event.key === Qt.Key_Right) {
325 const physical = mirrored ? indexes.slice().reverse() : indexes;
326 const position = physical.indexOf(__indexOf(segment));
327 const delta =
event.key === Qt.Key_Right ? 1 : -1;
328 target = physical[(position + delta + physical.length) % physical.length];
332 const next = __segmentAt(target);
334 next.forceActiveFocus(Qt.TabFocusReason);
335 currentIndex = target;
339 onSelectedIndexesChanged: {
340 if (__ready && !__updatingSelection)
341 __commitSelection(Array.from(selectedIndexes));
343 onSelectionModeChanged: __commitSelection(Array.from(selectedIndexes))
344 onSelectionRequiredChanged: __commitSelection(Array.from(selectedIndexes))
345 onCurrentIndexChanged: __updateFocusPolicies()
346 onContentChildrenChanged: __syncChildren()
347 onCountChanged: __syncChildren()
348 onVisibleChanged: __scheduleSync()
349 onEnabledChanged: __scheduleSync()
351 Component.onCompleted: {
353 __commitSelection(Array.from(selectedIndexes));