omarchy-ui 0.0.1-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ControlNode.qml ADDED
@@ -0,0 +1,745 @@
1
+ import QtQuick
2
+ import QtQuick.Controls as QQC
3
+ import qs.Commons
4
+ import qs.Ui as OmarchyUi
5
+
6
+ Loader {
7
+ id: root
8
+
9
+ property var bridge: null
10
+ property string surfaceName: ""
11
+ property string controlId: ""
12
+ property color foreground: Color.foreground
13
+ property string fontFamily: Style.font.family
14
+
15
+ readonly property var node: {
16
+ var currentRevision = bridge ? bridge.revision : 0
17
+ return bridge ? bridge.nodeFor(controlId) : null
18
+ }
19
+
20
+ readonly property bool builtIn: ["text", "icon", "button", "row", "column", "container", "image", "spacer",
21
+ "grid", "stack", "scroll", "rectangle", "action_button", "toggle", "toggle_switch", "text_field",
22
+ "number_field", "slider", "dropdown", "multi_select", "button_group", "progress", "separator",
23
+ "section_header", "searchable_dropdown", "confirm_dialog", "panel_hero", "optical_glyph",
24
+ "cursor_surface", "widget_button", "list_view"].indexOf(node ? node.type : "") >= 0
25
+
26
+ function prop(name, fallback) {
27
+ var props = node && node.props ? node.props : null
28
+ var value = props ? props[name] : undefined
29
+ return value === undefined || value === null ? fallback : value
30
+ }
31
+
32
+ function iconGlyph(name) {
33
+ var icons = {
34
+ ruby: "\ue23e",
35
+ phone: "\uf3cd",
36
+ plus: "\uf067",
37
+ reset: "\uf2f9"
38
+ }
39
+ var key = String(name || "")
40
+ return icons[key] || key
41
+ }
42
+
43
+ function easingType(name) {
44
+ var easings = {
45
+ linear: Easing.Linear,
46
+ in_quad: Easing.InQuad, out_quad: Easing.OutQuad, in_out_quad: Easing.InOutQuad,
47
+ in_cubic: Easing.InCubic, out_cubic: Easing.OutCubic, in_out_cubic: Easing.InOutCubic,
48
+ in_back: Easing.InBack, out_back: Easing.OutBack, in_out_back: Easing.InOutBack,
49
+ in_elastic: Easing.InElastic, out_elastic: Easing.OutElastic, in_out_elastic: Easing.InOutElastic,
50
+ in_bounce: Easing.InBounce, out_bounce: Easing.OutBounce, in_out_bounce: Easing.InOutBounce
51
+ }
52
+ return easings[String(name || "")] === undefined ? Easing.InOutQuad : easings[String(name)]
53
+ }
54
+
55
+ function subscribed(eventName) {
56
+ return node && Array.isArray(node.events) && node.events.indexOf(eventName) >= 0
57
+ }
58
+
59
+ function nativeDefinition() {
60
+ return bridge && node ? bridge.componentDefinition(node.type) : null
61
+ }
62
+
63
+ function syncNativeProperties() {
64
+ var definition = nativeDefinition()
65
+ if (!item || !definition || !definition.autoBind) return
66
+ var props = node && node.props ? node.props : ({})
67
+ var common = { visible: true, enabled: true, opacity: true, scale: true, rotation: true, z: true, width: true, height: true }
68
+ for (var protocolName in props) {
69
+ if (common[protocolName]) continue
70
+ var qmlName = definition.propertyMap[protocolName] || protocolName
71
+ if (item.hasOwnProperty(qmlName)) item[qmlName] = props[protocolName]
72
+ }
73
+ }
74
+
75
+ function nativeEventPayload(args) {
76
+ if (args.length === 0) return ({})
77
+ if (args.length === 1 && args[0] !== null && typeof args[0] === "object" && !Array.isArray(args[0])) return args[0]
78
+ var values = []
79
+ for (var i = 0; i < args.length; i++) {
80
+ var value = args[i]
81
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean"
82
+ || Array.isArray(value)) values.push(value)
83
+ else values.push(String(value))
84
+ }
85
+ return values.length === 1 ? { value: values[0] } : { arguments: values }
86
+ }
87
+
88
+ function connectNativeEvents() {
89
+ var definition = nativeDefinition()
90
+ if (!item || !definition || !definition.autoBind || !node || !Array.isArray(node.events)) return
91
+ for (let i = 0; i < node.events.length; i++) {
92
+ let protocolName = String(node.events[i])
93
+ if (protocolName === "mount" || protocolName === "unmount") continue
94
+ let qmlName = definition.eventMap[protocolName] || protocolName
95
+ let signal = item[qmlName]
96
+ if (signal && typeof signal.connect === "function") {
97
+ signal.connect(function() {
98
+ root.bridge.sendEvent(root.surfaceName, root.controlId, protocolName, root.nativeEventPayload(arguments))
99
+ })
100
+ }
101
+ }
102
+ }
103
+
104
+ function runTransition() {
105
+ var transitions = node && Array.isArray(node.transitions)
106
+ ? node.transitions
107
+ : (node && node.transition ? [node.transition] : [])
108
+ if (!item || transitions.length === 0) return
109
+ for (var transitionIndex = 0; transitionIndex < transitions.length; transitionIndex++)
110
+ runTrack(transitions[transitionIndex])
111
+ }
112
+
113
+ function runTrack(transition) {
114
+ var commonProperties = ["opacity", "scale", "rotation", "z", "width", "height"]
115
+ var definition = nativeDefinition()
116
+ var animationProperty = definition && definition.propertyMap[transition.property]
117
+ ? definition.propertyMap[transition.property] : transition.property
118
+ var animationTarget = commonProperties.indexOf(transition.property) >= 0 ? root : item
119
+ if (!animationTarget.hasOwnProperty(animationProperty)) return
120
+ if (transition.from === undefined || transition.from === null) return
121
+ var animation = propertyAnimationFactory.createObject(root, {
122
+ target: animationTarget,
123
+ property: String(animationProperty),
124
+ from: transition.from,
125
+ to: transition.to,
126
+ duration: Number(transition.duration)
127
+ })
128
+ animation.easing.type = easingType(transition.easing)
129
+ var delay = Number(transition.delay || 0)
130
+ if (delay > 0) delayedAnimationFactory.createObject(root, { interval: delay, animation: animation }).start()
131
+ else animation.start()
132
+ }
133
+
134
+ visible: node !== null && prop("visible", true) !== false
135
+ enabled: prop("enabled", true) !== false
136
+ opacity: Number(prop("opacity", 1))
137
+ scale: Number(prop("scale", 1))
138
+ rotation: Number(prop("rotation", 0))
139
+ z: Number(prop("z", 0))
140
+ sourceComponent: {
141
+ if (!node) return null
142
+ if (node.type === "text") return textComponent
143
+ if (node.type === "icon") return iconComponent
144
+ if (node.type === "button") return buttonComponent
145
+ if (node.type === "row") return rowComponent
146
+ if (node.type === "column") return columnComponent
147
+ if (node.type === "container") return containerComponent
148
+ if (node.type === "image") return imageComponent
149
+ if (node.type === "spacer") return spacerComponent
150
+ if (node.type === "grid") return gridComponent
151
+ if (node.type === "stack") return stackComponent
152
+ if (node.type === "scroll") return scrollComponent
153
+ if (node.type === "rectangle") return rectangleComponent
154
+ if (node.type === "action_button") return actionButtonComponent
155
+ if (node.type === "toggle") return toggleComponent
156
+ if (node.type === "toggle_switch") return toggleSwitchComponent
157
+ if (node.type === "text_field") return textFieldComponent
158
+ if (node.type === "number_field") return numberFieldComponent
159
+ if (node.type === "slider") return sliderComponent
160
+ if (node.type === "dropdown") return dropdownComponent
161
+ if (node.type === "multi_select") return multiSelectComponent
162
+ if (node.type === "button_group") return buttonGroupComponent
163
+ if (node.type === "progress") return progressComponent
164
+ if (node.type === "separator") return separatorComponent
165
+ if (node.type === "section_header") return sectionHeaderComponent
166
+ if (node.type === "searchable_dropdown") return searchableDropdownComponent
167
+ if (node.type === "confirm_dialog") return confirmDialogComponent
168
+ if (node.type === "panel_hero") return panelHeroComponent
169
+ if (node.type === "optical_glyph") return opticalGlyphComponent
170
+ if (node.type === "cursor_surface") return cursorSurfaceComponent
171
+ if (node.type === "widget_button") return widgetButtonComponent
172
+ if (node.type === "list_view") return listViewComponent
173
+ return null
174
+ }
175
+ source: node && !builtIn ? bridge.componentSource(node.type) : ""
176
+ onLoaded: {
177
+ if (!item) return
178
+ if (!builtIn) {
179
+ if (item.hasOwnProperty("bridge")) item.bridge = bridge
180
+ if (item.hasOwnProperty("surfaceName")) item.surfaceName = surfaceName
181
+ if (item.hasOwnProperty("controlId")) item.controlId = controlId
182
+ if (item.hasOwnProperty("node")) item.node = node
183
+ syncNativeProperties()
184
+ connectNativeEvents()
185
+ }
186
+ runTransition()
187
+ if (subscribed("mount")) bridge.sendEvent(surfaceName, controlId, "mount", {})
188
+ }
189
+ onNodeChanged: {
190
+ if (item && !builtIn && item.hasOwnProperty("node")) item.node = node
191
+ if (!builtIn) syncNativeProperties()
192
+ Qt.callLater(runTransition)
193
+ }
194
+
195
+ Repeater {
196
+ id: nativeChildren
197
+ parent: root.item && root.item.hasOwnProperty("contentHost") && root.item.contentHost
198
+ ? root.item.contentHost
199
+ : root
200
+ model: !root.builtIn && root.node && root.nativeDefinition() && root.nativeDefinition().container
201
+ && Array.isArray(root.node.children) ? root.node.children : []
202
+ delegate: childDelegate
203
+ }
204
+
205
+ Component {
206
+ id: propertyAnimationFactory
207
+ PropertyAnimation { onStopped: destroy() }
208
+ }
209
+
210
+ Component {
211
+ id: delayedAnimationFactory
212
+ Timer {
213
+ required property var animation
214
+ repeat: false
215
+ onTriggered: { animation.start(); destroy() }
216
+ }
217
+ }
218
+
219
+ Component.onDestruction: {
220
+ if (bridge && subscribed("unmount")) bridge.sendEvent(surfaceName, controlId, "unmount", {})
221
+ }
222
+ Component {
223
+ id: textComponent
224
+
225
+ Text {
226
+ text: String(root.prop("text", ""))
227
+ color: root.prop("color", root.foreground)
228
+ font.family: root.fontFamily
229
+ font.pixelSize: {
230
+ var style = String(root.prop("style", "body"))
231
+ if (style === "heading") return Style.font.heading
232
+ if (style === "caption") return Style.font.caption
233
+ return Number(root.prop("size", Style.font.body))
234
+ }
235
+ font.bold: root.prop("bold", false) || String(root.prop("style", "body")) === "heading"
236
+ wrapMode: root.prop("wrap", true) ? Text.Wrap : Text.NoWrap
237
+ width: Number(root.prop("width", implicitWidth))
238
+ }
239
+ }
240
+
241
+ Component {
242
+ id: iconComponent
243
+
244
+ Text {
245
+ text: root.iconGlyph(root.prop("name", root.prop("text", "")))
246
+ color: root.foreground
247
+ font.family: root.fontFamily
248
+ font.pixelSize: Number(root.prop("size", Style.font.icon))
249
+ }
250
+ }
251
+
252
+ Component {
253
+ id: buttonComponent
254
+
255
+ OmarchyUi.Button {
256
+ text: String(root.prop("text", ""))
257
+ iconText: root.iconGlyph(root.prop("icon", ""))
258
+ tooltipText: String(root.prop("tooltip", ""))
259
+ selected: root.prop("selected", false) === true
260
+ active: root.prop("active", false) === true
261
+ hasCursor: root.prop("cursor", false) === true
262
+ focusable: root.prop("focusable", true) !== false
263
+ bordered: root.prop("bordered", true) !== false
264
+ foreground: root.prop("foreground", root.foreground)
265
+ background: root.prop("background", "transparent")
266
+ accent: root.prop("accent", Color.accent)
267
+ fontFamily: String(root.prop("font_family", root.fontFamily))
268
+ fontSize: Number(root.prop("font_size", Style.font.body))
269
+ iconSize: Number(root.prop("icon_size", Style.font.icon))
270
+ iconRotation: Number(root.prop("icon_rotation", 0))
271
+ iconSpinning: root.prop("icon_spinning", false) === true
272
+ horizontalPadding: Number(root.prop("horizontal_padding", Style.spacing.controlPaddingX))
273
+ verticalPadding: Number(root.prop("vertical_padding", Style.spacing.controlPaddingY))
274
+ leftAlign: root.prop("left_align", false) === true
275
+ tooltipBackground: root.prop("tooltip_background", Color.tooltip.background)
276
+ tooltipForeground: root.prop("tooltip_foreground", Color.tooltip.text)
277
+ tooltipBorder: root.prop("tooltip_border", Color.tooltip.border)
278
+ onClicked: root.bridge.sendEvent(root.surfaceName, root.controlId, "click", {})
279
+ onRightClicked: root.bridge.sendEvent(root.surfaceName, root.controlId, "right_click", {})
280
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
281
+ }
282
+ }
283
+
284
+ Component {
285
+ id: rowComponent
286
+
287
+ Row {
288
+ spacing: Number(root.prop("spacing", Style.spacing.controlGap))
289
+
290
+ Repeater {
291
+ model: root.node && Array.isArray(root.node.children) ? root.node.children : []
292
+ delegate: rowChildDelegate
293
+ }
294
+ }
295
+ }
296
+
297
+ Component {
298
+ id: columnComponent
299
+
300
+ Column {
301
+ spacing: Number(root.prop("spacing", Style.spacing.panelGap))
302
+
303
+ Repeater {
304
+ model: root.node && Array.isArray(root.node.children) ? root.node.children : []
305
+ delegate: columnChildDelegate
306
+ }
307
+ }
308
+ }
309
+
310
+ Component {
311
+ id: containerComponent
312
+
313
+ OmarchyUi.BorderSurface {
314
+ readonly property int innerPadding: Number(root.prop("padding", 0))
315
+ implicitWidth: content.implicitWidth + innerPadding * 2
316
+ implicitHeight: content.implicitHeight + innerPadding * 2
317
+ color: "transparent"
318
+ borderSpec: root.prop("bordered", false)
319
+ ? Border.controlSpec("normal", root.foreground, Color.accent)
320
+ : Border.none()
321
+ radius: Style.cornerRadius
322
+
323
+ Column {
324
+ id: content
325
+ anchors.centerIn: parent
326
+ spacing: Number(root.prop("spacing", Style.spacing.panelGap))
327
+
328
+ Repeater {
329
+ model: root.node && Array.isArray(root.node.children) ? root.node.children : []
330
+ delegate: childDelegate
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ Component {
337
+ id: imageComponent
338
+
339
+ Image {
340
+ source: String(root.prop("source", ""))
341
+ width: Number(root.prop("width", 120))
342
+ height: Number(root.prop("height", 120))
343
+ asynchronous: true
344
+ fillMode: Image.PreserveAspectFit
345
+ }
346
+ }
347
+
348
+ Component {
349
+ id: spacerComponent
350
+
351
+ Item {
352
+ implicitWidth: Number(root.prop("width", 0))
353
+ implicitHeight: Number(root.prop("height", Style.space(8)))
354
+ }
355
+ }
356
+
357
+ Component {
358
+ id: gridComponent
359
+ Grid {
360
+ columns: Number(root.prop("columns", 2))
361
+ rows: Number(root.prop("rows", -1))
362
+ rowSpacing: Number(root.prop("row_spacing", root.prop("spacing", Style.spacing.controlGap)))
363
+ columnSpacing: Number(root.prop("column_spacing", root.prop("spacing", Style.spacing.controlGap)))
364
+ Repeater { model: root.node.children || []; delegate: childDelegate }
365
+ }
366
+ }
367
+
368
+ Component {
369
+ id: stackComponent
370
+ Item {
371
+ implicitWidth: childrenRect.width
372
+ implicitHeight: childrenRect.height
373
+ Repeater { model: root.node.children || []; delegate: childDelegate }
374
+ }
375
+ }
376
+
377
+ Component {
378
+ id: scrollComponent
379
+ QQC.ScrollView {
380
+ implicitWidth: Number(root.prop("width", 320))
381
+ implicitHeight: Number(root.prop("height", 240))
382
+ clip: root.prop("clip", true) !== false
383
+ Column {
384
+ spacing: Style.spacing.panelGap
385
+ Repeater { model: root.node.children || []; delegate: childDelegate }
386
+ }
387
+ }
388
+ }
389
+
390
+ Component {
391
+ id: rectangleComponent
392
+ Rectangle {
393
+ readonly property int pad: Number(root.prop("padding", 0))
394
+ implicitWidth: Number(root.prop("width", contentColumn.implicitWidth + pad * 2))
395
+ implicitHeight: Number(root.prop("height", contentColumn.implicitHeight + pad * 2))
396
+ color: root.prop("color", "transparent")
397
+ radius: Number(root.prop("radius", 0))
398
+ border.color: root.prop("border_color", "transparent")
399
+ border.width: Number(root.prop("border_width", 0))
400
+ Column {
401
+ id: contentColumn
402
+ anchors.centerIn: parent
403
+ Repeater { model: root.node.children || []; delegate: childDelegate }
404
+ }
405
+ }
406
+ }
407
+
408
+ Component {
409
+ id: childDelegate
410
+ Loader {
411
+ required property var modelData
412
+ source: Qt.resolvedUrl("ControlNode.qml")
413
+ onLoaded: {
414
+ item.bridge = root.bridge
415
+ item.surfaceName = root.surfaceName
416
+ item.controlId = String(modelData.id)
417
+ item.foreground = root.foreground
418
+ item.fontFamily = root.fontFamily
419
+ }
420
+ }
421
+ }
422
+
423
+ Component {
424
+ id: rowChildDelegate
425
+ Loader {
426
+ required property var modelData
427
+ readonly property string crossAlignment: String(root.prop("alignment", "center"))
428
+ anchors.top: crossAlignment === "start" || crossAlignment === "top" ? parent.top : undefined
429
+ anchors.verticalCenter: crossAlignment === "center" ? parent.verticalCenter : undefined
430
+ anchors.bottom: crossAlignment === "end" || crossAlignment === "bottom" ? parent.bottom : undefined
431
+ source: Qt.resolvedUrl("ControlNode.qml")
432
+ onLoaded: {
433
+ item.bridge = root.bridge
434
+ item.surfaceName = root.surfaceName
435
+ item.controlId = String(modelData.id)
436
+ item.foreground = root.foreground
437
+ item.fontFamily = root.fontFamily
438
+ }
439
+ }
440
+ }
441
+
442
+ Component {
443
+ id: columnChildDelegate
444
+ Loader {
445
+ required property var modelData
446
+ readonly property string crossAlignment: String(root.prop("alignment", "start"))
447
+ anchors.left: crossAlignment === "start" || crossAlignment === "left" ? parent.left : undefined
448
+ anchors.horizontalCenter: crossAlignment === "center" ? parent.horizontalCenter : undefined
449
+ anchors.right: crossAlignment === "end" || crossAlignment === "right" ? parent.right : undefined
450
+ source: Qt.resolvedUrl("ControlNode.qml")
451
+ onLoaded: {
452
+ item.bridge = root.bridge
453
+ item.surfaceName = root.surfaceName
454
+ item.controlId = String(modelData.id)
455
+ item.foreground = root.foreground
456
+ item.fontFamily = root.fontFamily
457
+ }
458
+ }
459
+ }
460
+
461
+ Component {
462
+ id: actionButtonComponent
463
+ OmarchyUi.PanelActionButton {
464
+ iconText: root.iconGlyph(root.prop("icon", "")); tooltipText: String(root.prop("tooltip", ""))
465
+ bordered: root.prop("bordered", false) === true; focusable: root.prop("focusable", false) === true
466
+ hasCursor: root.prop("cursor", false) === true
467
+ size: Number(root.prop("size", 28)); foreground: root.prop("foreground", root.foreground)
468
+ hoverColor: root.prop("hover_color", foreground); fontFamily: String(root.prop("font_family", root.fontFamily))
469
+ fontSize: Number(root.prop("font_size", Style.font.icon))
470
+ onClicked: root.bridge.sendEvent(root.surfaceName, root.controlId, "click", {})
471
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
472
+ }
473
+ }
474
+
475
+ Component {
476
+ id: toggleComponent
477
+ OmarchyUi.Toggle {
478
+ label: String(root.prop("label", "")); description: String(root.prop("description", ""))
479
+ checked: root.prop("checked", false) === true; hasCursor: root.prop("cursor", false) === true
480
+ rounded: root.prop("rounded", Style.cornerRadius > 0) === true
481
+ foreground: root.prop("foreground", root.foreground); accent: root.prop("accent", Color.accent)
482
+ fontFamily: String(root.prop("font_family", root.fontFamily)); titleSize: Number(root.prop("title_size", Style.font.subtitle))
483
+ descriptionSize: Number(root.prop("description_size", Style.font.caption))
484
+ onClicked: root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: !checked })
485
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
486
+ }
487
+ }
488
+
489
+ Component {
490
+ id: toggleSwitchComponent
491
+ OmarchyUi.ToggleSwitch {
492
+ checked: root.prop("checked", false) === true; busy: root.prop("busy", false) === true
493
+ interactive: root.prop("interactive", root.prop("enabled", true)) !== false
494
+ hasCursor: root.prop("cursor", false) === true; cursorRing: root.prop("cursor_ring", true) !== false
495
+ cursorPad: Number(root.prop("cursor_pad", Style.space(6))); rounded: root.prop("rounded", Style.cornerRadius > 0) === true
496
+ foreground: root.prop("foreground", root.foreground); accent: root.prop("accent", Color.accent)
497
+ trackHeight: Number(root.prop("track_height", Math.max(22, Math.round(Style.spacing.controlHeight * 0.55))))
498
+ trackWidth: Number(root.prop("track_width", Math.round(trackHeight * 1.9)))
499
+ knobSize: Number(root.prop("knob_size", Math.max(6, Math.round(trackHeight * 0.72))))
500
+ knobInset: Number(root.prop("knob_inset", Math.max(1, Math.round((trackHeight - knobSize) / 2))))
501
+ onToggled: root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: !checked })
502
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
503
+ }
504
+ }
505
+
506
+ Component {
507
+ id: textFieldComponent
508
+ OmarchyUi.TextField {
509
+ text: String(root.prop("text", "")); placeholderText: String(root.prop("placeholder", ""))
510
+ password: root.prop("password", false) === true
511
+ implicitWidth: Number(root.prop("width", 240)); foreground: root.prop("foreground", root.foreground)
512
+ accent: root.prop("accent", Color.accent); selectionTint: root.prop("selection_tint", Style.selectionFillFor(foreground, accent))
513
+ horizontalPadding: Number(root.prop("horizontal_padding", Style.spacing.controlPaddingX))
514
+ verticalPadding: Number(root.prop("vertical_padding", Style.spacing.inputPaddingY)); hasCursor: root.prop("cursor", false) === true
515
+ onTextEdited: root.bridge.sendEvent(root.surfaceName, root.controlId, "input", { value: text })
516
+ onEditingFinished: root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: text })
517
+ onAccepted: root.bridge.sendEvent(root.surfaceName, root.controlId, "submit", { value: text })
518
+ onActiveFocusChanged: root.bridge.sendEvent(root.surfaceName, root.controlId, activeFocus ? "focus" : "blur", { value: text })
519
+ }
520
+ }
521
+
522
+ Component {
523
+ id: numberFieldComponent
524
+ OmarchyUi.NumberField {
525
+ label: String(root.prop("label", "")); value: Number(root.prop("value", 0))
526
+ from: Number(root.prop("from", 0)); to: Number(root.prop("to", 100)); stepSize: Number(root.prop("step", 1))
527
+ foreground: root.prop("foreground", root.foreground); accent: root.prop("accent", Color.accent)
528
+ fontFamily: String(root.prop("font_family", root.fontFamily)); fontSize: Number(root.prop("font_size", Style.font.body))
529
+ fieldWidth: Number(root.prop("field_width", Style.spacing.numberFieldWidth)); hasCursor: root.prop("cursor", false) === true
530
+ onModified: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: value }) }
531
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
532
+ }
533
+ }
534
+
535
+ Component {
536
+ id: sliderComponent
537
+ OmarchyUi.PanelSlider {
538
+ value: Number(root.prop("value", 0)); minimum: Number(root.prop("minimum", 0)); maximum: Number(root.prop("maximum", 1))
539
+ step: Number(root.prop("step", 0.05)); integer: root.prop("integer", false) === true; tickCount: Number(root.prop("ticks", 0))
540
+ implicitWidth: Number(root.prop("width", 200)); trackColor: root.prop("track_color", "#333")
541
+ fillColor: root.prop("fill_color", root.foreground); knobColor: root.prop("knob_color", root.foreground)
542
+ trackHeight: Number(root.prop("track_height", Math.max(4, Math.round(Style.spacing.controlHeight * 0.11))))
543
+ knobSize: Number(root.prop("knob_size", Math.max(14, Math.round(Style.spacing.controlHeight * 0.38))))
544
+ tickColor: root.prop("tick_color", Color.background)
545
+ onReleased: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: value }) }
546
+ onMoved: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "input", { value: value }) }
547
+ onRightClicked: root.bridge.sendEvent(root.surfaceName, root.controlId, "right_click", {})
548
+ }
549
+ }
550
+
551
+ Component {
552
+ id: dropdownComponent
553
+ OmarchyUi.Dropdown {
554
+ label: String(root.prop("label", "")); value: String(root.prop("value", "")); options: root.prop("options", [])
555
+ implicitWidth: Number(root.prop("width", 240)); foreground: root.prop("foreground", root.foreground)
556
+ background: root.prop("background", Color.popups.background); popupBorder: root.prop("popup_border", Color.popups.border)
557
+ accent: root.prop("accent", Color.accent); fontFamily: String(root.prop("font_family", root.fontFamily))
558
+ rowHeight: Number(root.prop("row_height", Style.spacing.controlHeight)); popupRowHeight: Number(root.prop("popup_row_height", Style.spacing.popupRowHeight))
559
+ showLabel: root.prop("show_label", true) !== false; hasCursor: root.prop("cursor", false) === true
560
+ onChanged: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: value }) }
561
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
562
+ }
563
+ }
564
+
565
+ Component {
566
+ id: multiSelectComponent
567
+ OmarchyUi.MultiSelect {
568
+ label: String(root.prop("label", "")); values: root.prop("values", []); options: root.prop("options", [])
569
+ placeholderText: String(root.prop("placeholder", "Search...")); enabled: root.prop("enabled", true) !== false
570
+ optionsCommand: root.prop("options_command", []); optionsCommandCwd: String(root.prop("options_command_cwd", ""))
571
+ emptyText: String(root.prop("empty_text", "No options")); noSelectionText: String(root.prop("no_selection_text", "None selected"))
572
+ triggerLabel: String(root.prop("trigger_label", "")); showLabel: root.prop("show_label", true) !== false
573
+ implicitWidth: Number(root.prop("width", 240)); foreground: root.prop("foreground", root.foreground)
574
+ background: root.prop("background", Color.popups.background); popupBorder: root.prop("popup_border", Color.popups.border)
575
+ accent: root.prop("accent", Color.accent); fontFamily: String(root.prop("font_family", root.fontFamily))
576
+ rowHeight: Number(root.prop("row_height", Style.spacing.controlHeight)); popupRowHeight: Number(root.prop("popup_row_height", Style.spacing.popupRowHeight))
577
+ popupMinHeight: Number(root.prop("popup_min_height", Style.spacing.searchablePopupMinHeight)); hasCursor: root.prop("cursor", false) === true
578
+ onChanged: function(values) { root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: values }) }
579
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
580
+ }
581
+ }
582
+
583
+ Component {
584
+ id: buttonGroupComponent
585
+ OmarchyUi.ButtonGroup {
586
+ value: String(root.prop("value", "")); options: root.prop("options", [])
587
+ foreground: root.prop("foreground", root.foreground); background: root.prop("background", Color.background)
588
+ accent: root.prop("accent", Color.accent); fontFamily: String(root.prop("font_family", root.fontFamily))
589
+ fontSize: Number(root.prop("font_size", Style.font.body)); focusable: root.prop("focusable", true) !== false
590
+ cursorIndex: Number(root.prop("cursor_index", -1))
591
+ onChanged: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: value }) }
592
+ onHovered: function(index, value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { index: index, value: value }) }
593
+ }
594
+ }
595
+
596
+ Component {
597
+ id: progressComponent
598
+ Rectangle {
599
+ property real minimum: Number(root.prop("minimum", 0)); property real maximum: Number(root.prop("maximum", 1))
600
+ implicitWidth: Number(root.prop("width", 200)); implicitHeight: Number(root.prop("height", 6)); radius: height / 2
601
+ color: Qt.rgba(root.foreground.r, root.foreground.g, root.foreground.b, 0.18)
602
+ Rectangle { width: parent.width * Math.max(0, Math.min(1, (Number(root.prop("value", 0)) - parent.minimum) / Math.max(0.000001, parent.maximum - parent.minimum))); height: parent.height; radius: parent.radius; color: root.prop("color", root.foreground) }
603
+ }
604
+ }
605
+
606
+ Component { id: separatorComponent; OmarchyUi.PanelSeparator { foreground: root.foreground; strength: Number(root.prop("strength", 0.12)) } }
607
+ Component { id: sectionHeaderComponent; OmarchyUi.PanelSectionHeader { text: String(root.prop("text", "")); foreground: root.foreground; fontFamily: root.fontFamily } }
608
+
609
+ Component {
610
+ id: searchableDropdownComponent
611
+ OmarchyUi.SearchableDropdown {
612
+ label: String(root.prop("label", "")); value: String(root.prop("value", "")); options: root.prop("options", [])
613
+ placeholderText: String(root.prop("placeholder", "Search...")); emptyText: String(root.prop("empty_text", "No matches"))
614
+ triggerLabel: String(root.prop("trigger_label", "")); implicitWidth: Number(root.prop("width", 240))
615
+ foreground: root.prop("foreground", root.foreground); background: root.prop("background", Color.popups.background)
616
+ popupBorder: root.prop("popup_border", Color.popups.border); accent: root.prop("accent", Color.accent)
617
+ fontFamily: String(root.prop("font_family", root.fontFamily)); rowHeight: Number(root.prop("row_height", Style.spacing.controlHeight))
618
+ popupRowHeight: Number(root.prop("popup_row_height", Style.spacing.popupRowHeight)); popupMinHeight: Number(root.prop("popup_min_height", Style.spacing.searchablePopupMinHeight))
619
+ showLabel: root.prop("show_label", true) !== false; hasCursor: root.prop("cursor", false) === true
620
+ onChanged: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: value }) }
621
+ onHovered: function(value) { root.bridge.sendEvent(root.surfaceName, root.controlId, "hover", { value: value }) }
622
+ }
623
+ }
624
+
625
+ Component {
626
+ id: confirmDialogComponent
627
+ OmarchyUi.ConfirmDialog {
628
+ opened: root.prop("opened", false) === true; message: String(root.prop("message", ""))
629
+ cancelText: String(root.prop("cancel_text", "Cancel")); confirmText: String(root.prop("confirm_text", "Confirm"))
630
+ selectedIndex: Number(root.prop("selected_index", 1)); visible: root.prop("visible", true) !== false
631
+ background: root.prop("background", Color.background); foreground: root.prop("foreground", root.foreground)
632
+ scrim: root.prop("scrim", Util.alpha(Color.background, 0.7)); selectedBackground: root.prop("selected_background", Util.alpha(root.foreground, 0.08))
633
+ selectedText: root.prop("selected_text", Color.accent); fontFamily: String(root.prop("font_family", root.fontFamily))
634
+ cornerRadius: Number(root.prop("corner_radius", Style.cornerRadius))
635
+ onCanceled: root.bridge.sendEvent(root.surfaceName, root.controlId, "cancel", {})
636
+ onConfirmed: root.bridge.sendEvent(root.surfaceName, root.controlId, "confirm", {})
637
+ }
638
+ }
639
+
640
+ Component {
641
+ id: panelHeroComponent
642
+ OmarchyUi.PanelHero {
643
+ title: String(root.prop("title", "")); meta: String(root.prop("meta", "")); detail: String(root.prop("detail", ""))
644
+ iconSize: Number(root.prop("icon_size", Style.font.display)); iconOpacity: Number(root.prop("icon_opacity", 1))
645
+ metaOpacity: Number(root.prop("meta_opacity", 1)); foreground: root.prop("foreground", root.foreground)
646
+ fontFamily: String(root.prop("font_family", root.fontFamily))
647
+ }
648
+ }
649
+
650
+ Component { id: opticalGlyphComponent; OmarchyUi.OpticalGlyph { text: root.iconGlyph(root.prop("text", "")); fontSize: Number(root.prop("size", Style.font.body)); color: root.prop("color", root.foreground); debugBounds: root.prop("debug_bounds", false) === true; fontFamily: root.fontFamily } }
651
+
652
+ Component {
653
+ id: cursorSurfaceComponent
654
+ OmarchyUi.CursorSurface {
655
+ implicitWidth: Number(root.prop("width", contentItem.implicitWidth)); implicitHeight: Number(root.prop("height", contentItem.implicitHeight))
656
+ current: root.prop("current", false) === true; outline: root.prop("outline", false) === true; bordered: root.prop("bordered", false) === true
657
+ hasCursor: root.prop("cursor", false) === true; foreground: root.prop("foreground", root.foreground)
658
+ accent: root.prop("accent", Color.accent); fill: root.prop("fill", Style.hoverFillFor(foreground, accent))
659
+ currentFill: root.prop("current_fill", Style.selectedFillFor(foreground, accent))
660
+ Column { id: contentItem; anchors.centerIn: parent; Repeater { model: root.node.children || []; delegate: childDelegate } }
661
+ MouseArea { anchors.fill: parent; onClicked: root.bridge.sendEvent(root.surfaceName, root.controlId, "click", {}) }
662
+ }
663
+ }
664
+
665
+ Component {
666
+ id: widgetButtonComponent
667
+ OmarchyUi.WidgetButton {
668
+ text: String(root.prop("text", "")); tooltipText: String(root.prop("tooltip", "")); active: root.prop("active", false) === true
669
+ dimmed: root.prop("dimmed", false) === true; concealed: root.prop("concealed", false) === true
670
+ interactive: root.prop("interactive", true) !== false; pressable: root.prop("pressable", true) !== false
671
+ fontFamily: String(root.prop("font_family", root.fontFamily)); fontSize: Number(root.prop("font_size", Style.font.body))
672
+ foreground: root.prop("foreground", root.foreground); activeColor: root.prop("active_color", Color.urgent)
673
+ horizontalMargin: Number(root.prop("horizontal_margin", 8.5)); verticalPadding: Number(root.prop("vertical_padding", 6))
674
+ fixedWidth: Number(root.prop("fixed_width", -1)); fixedHeight: Number(root.prop("fixed_height", -1)); textRotation: Number(root.prop("text_rotation", 0))
675
+ keepSpace: root.prop("keep_space", false) === true; useActiveColor: root.prop("use_active_color", true) !== false
676
+ maintainIndicatorReveal: root.prop("maintain_indicator_reveal", false) === true
677
+ labelVisible: root.prop("label_visible", true) !== false; hasVisualContent: root.prop("has_visual_content", text !== "") === true
678
+ onPressed: function(button) {
679
+ var eventName = button === Qt.RightButton ? "right_click" : (button === Qt.MiddleButton ? "middle_click" : "click")
680
+ root.bridge.sendEvent(root.surfaceName, root.controlId, eventName, { button: button })
681
+ }
682
+ onWheelMoved: function(delta) { root.bridge.sendEvent(root.surfaceName, root.controlId, "wheel", { delta: delta }) }
683
+ }
684
+ }
685
+
686
+ Component {
687
+ id: listViewComponent
688
+ ListView {
689
+ id: listControl
690
+ readonly property var sourceItems: root.prop("items", [])
691
+ readonly property string keyField: String(root.prop("key_field", "id"))
692
+ readonly property string labelField: String(root.prop("label_field", "label"))
693
+ readonly property string descriptionField: String(root.prop("description_field", "description"))
694
+ readonly property string iconField: String(root.prop("icon_field", "icon"))
695
+ implicitWidth: Number(root.prop("width", 280)); implicitHeight: Number(root.prop("height", 240))
696
+ orientation: String(root.prop("orientation", "vertical")) === "horizontal" ? ListView.Horizontal : ListView.Vertical
697
+ spacing: Number(root.prop("spacing", Style.spacing.labelGap)); clip: true; model: sourceItems
698
+ currentIndex: {
699
+ var selected = root.prop("selected", null)
700
+ for (var i = 0; i < sourceItems.length; i++) {
701
+ var item = sourceItems[i]
702
+ var key = typeof item === "object" ? item[keyField] : item
703
+ if (key === selected) return i
704
+ }
705
+ return -1
706
+ }
707
+ onContentXChanged: if (moving) root.bridge.sendEvent(root.surfaceName, root.controlId, "scroll", { x: contentX, y: contentY })
708
+ onContentYChanged: if (moving) root.bridge.sendEvent(root.surfaceName, root.controlId, "scroll", { x: contentX, y: contentY })
709
+
710
+ delegate: OmarchyUi.CursorSurface {
711
+ required property var modelData
712
+ required property int index
713
+ readonly property var value: typeof modelData === "object" ? modelData[listControl.keyField] : modelData
714
+ width: listControl.orientation === ListView.Vertical ? listControl.width : implicitWidth
715
+ implicitWidth: rowContent.implicitWidth + Style.spacing.rowPaddingX * 2
716
+ implicitHeight: Math.max(Style.spacing.controlHeight, rowContent.implicitHeight + Style.spacing.controlPaddingY * 2)
717
+ current: index === listControl.currentIndex
718
+ foreground: root.foreground
719
+ Row {
720
+ id: rowContent
721
+ anchors.centerIn: parent
722
+ spacing: Style.spacing.controlGap
723
+ Text { visible: text !== ""; text: root.iconGlyph(typeof modelData === "object" ? modelData[listControl.iconField] : ""); color: root.foreground; font.family: root.fontFamily }
724
+ Column {
725
+ Text { text: String(typeof modelData === "object" ? (modelData[listControl.labelField] ?? value) : modelData); color: root.foreground; font.family: root.fontFamily }
726
+ Text { visible: text !== ""; text: String(typeof modelData === "object" ? (modelData[listControl.descriptionField] || "") : ""); color: Qt.darker(root.foreground, 1.4); font.family: root.fontFamily; font.pixelSize: Style.font.caption }
727
+ }
728
+ }
729
+ MouseArea {
730
+ anchors.fill: parent
731
+ onClicked: {
732
+ listControl.currentIndex = index
733
+ root.bridge.sendEvent(root.surfaceName, root.controlId, "change", { value: parent.value, index: index, item: modelData })
734
+ root.bridge.sendEvent(root.surfaceName, root.controlId, "activate", { value: parent.value, index: index, item: modelData })
735
+ }
736
+ }
737
+ }
738
+
739
+ Text {
740
+ anchors.centerIn: parent; visible: listControl.count === 0
741
+ text: String(root.prop("empty_text", "No items")); color: Qt.darker(root.foreground, 1.4); font.family: root.fontFamily
742
+ }
743
+ }
744
+ }
745
+ }