EvanZhu0721 / 公开仓库
SBMS
SBMS complete the calculation rules for the default logical desktop topology relationship and the physical dimensions of the displays in the Windows multi-screen collaboration system.
文件
../ pagescontrols.slint16.0 KBlaunch-intro.slint7.9 KBquick-access.slint13.9 KBremove-group-dialog.slint3.2 KBseven-segment.slint4.4 KBtheme.slint2.1 KBtray-icon.svg314 Bui/controls.slint16.0 KB
1
import { Button, LineEdit } from "std-widgets.slint";2
import { MdTokens } from "theme.slint";3
4
export component ResolutionLineEdit inherits Rectangle {5
in-out property <string> text;6
in property <string> placeholder-text;7
in property <string> field-label;8
in property <string> suggestion;9
in property <string> suggestion-suffix;10
callback edited();11
12
height: MdTokens.target;13
background: transparent;14
clip: true;15
16
editor := LineEdit {17
width: parent.width;18
height: parent.height;19
text <=> root.text;20
placeholder-text: root.placeholder-text;21
input-type: InputType.number;22
font-size: MdTokens.type-body-large;23
accessible-label: root.field-label;24
accessible-description: root.suggestion == ""25
? ""26
: "Suggested " + root.suggestion + ". Press Tab to accept.";27
edited => { root.edited(); }28
key-pressed(event) => {29
if event.text == Key.Tab && root.suggestion != "" {30
root.text = root.suggestion;31
root.edited();32
}33
// Let Tab keep its normal focus-navigation behavior.34
return reject;35
}36
}37
38
prefix-measure := Text {39
x: 16px;40
width: self.preferred-width;41
height: parent.height;42
text: root.text;43
color: transparent;44
font-size: MdTokens.type-body-large;45
vertical-alignment: center;46
accessible-role: none;47
}48
49
if editor.has-focus && root.suggestion-suffix != "" : Text {50
x: 16px + prefix-measure.preferred-width;51
width: parent.width - self.x - 40px;52
height: parent.height;53
text: root.suggestion-suffix;54
color: MdTokens.on-surface-variant;55
opacity: 0.55;56
font-size: MdTokens.type-body-large;57
vertical-alignment: center;58
overflow: clip;59
accessible-role: none;60
}61
}62
63
export component CompactComboBox inherits Rectangle {64
in property <[string]> model;65
in property <[string]> collapsed-model: root.model;66
in-out property <int> current-index: -1;67
in property <bool> control-enabled: true;68
in property <string> field-label;69
in property <length> popup-width: root.width;70
callback selected();71
72
property <string> current-value: root.current-index >= 073
&& root.current-index < root.collapsed-model.length74
? root.collapsed-model[root.current-index]75
: "";76
property <length> popup-height: root.model.length * MdTokens.target + 8px;77
78
height: MdTokens.target;79
background: selector-touch.pressed80
? MdTokens.surface-container81
: (selector-touch.has-hover ? MdTokens.surface-container : MdTokens.surface);82
border-width: combo-focus.has-focus || popup.is-open ? 2px : 1px;83
border-color: combo-focus.has-focus || popup.is-open84
? MdTokens.primary85
: MdTokens.outline-variant;86
border-radius: MdTokens.space-1;87
opacity: root.control-enabled ? 1 : 0.38;88
forward-focus: combo-focus;89
90
accessible-role: combobox;91
accessible-label: root.field-label;92
accessible-value: root.current-value;93
accessible-enabled: root.control-enabled;94
accessible-expandable: true;95
accessible-expanded: popup.is-open;96
accessible-action-expand => {97
if root.control-enabled && root.model.length > 0 {98
popup.show();99
}100
}101
102
Text {103
x: MdTokens.space-2;104
width: parent.width - 52px;105
height: parent.height;106
text: root.current-value;107
color: MdTokens.on-surface;108
font-size: MdTokens.type-body-large;109
vertical-alignment: center;110
overflow: clip;111
accessible-role: none;112
}113
114
Path {115
x: parent.width - 36px;116
y: 12px;117
width: 24px;118
height: 24px;119
viewbox-width: 24;120
viewbox-height: 24;121
commands: "M7 9.5L12 14.5L17 9.5";122
fill: transparent;123
stroke: MdTokens.on-surface;124
stroke-width: 2.5px;125
accessible-role: none;126
}127
128
combo-focus := FocusScope {129
key-pressed(event) => {130
if !root.control-enabled || root.model.length == 0 {131
return reject;132
}133
if event.text == Key.UpArrow {134
root.current-index = Math.max(root.current-index - 1, 0);135
root.selected();136
return accept;137
} else if event.text == Key.DownArrow {138
root.current-index = Math.min(root.current-index + 1, root.model.length - 1);139
root.selected();140
return accept;141
} else if event.text == Key.Return {142
popup.show();143
return accept;144
}145
return reject;146
}147
148
selector-touch := TouchArea {149
enabled: root.control-enabled && root.model.length > 0;150
clicked => {151
root.focus();152
popup.show();153
}154
}155
}156
157
popup := PopupWindow {158
x: 0;159
y: -root.popup-height - 4px;160
width: root.popup-width;161
height: root.popup-height;162
close-policy: close-on-click-outside;163
forward-focus: popup-focus;164
165
popup-focus := FocusScope {166
key-pressed(event) => {167
if event.text == Key.UpArrow {168
root.current-index = Math.max(root.current-index - 1, 0);169
root.selected();170
return accept;171
} else if event.text == Key.DownArrow {172
root.current-index = Math.min(root.current-index + 1, root.model.length - 1);173
root.selected();174
return accept;175
} else if event.text == Key.Return || event.text == Key.Escape {176
popup.close();177
return accept;178
}179
return reject;180
}181
182
Rectangle {183
background: MdTokens.surface;184
border-width: 1px;185
border-color: MdTokens.outline-variant;186
border-radius: MdTokens.shape-medium;187
clip: true;188
189
VerticalLayout {190
padding: 4px;191
spacing: 0px;192
193
for option[index] in root.model : Rectangle {194
height: MdTokens.target;195
border-radius: MdTokens.space-1;196
background: index == root.current-index197
? MdTokens.primary-container198
: (option-touch.has-hover ? MdTokens.surface-container : transparent);199
200
Text {201
x: MdTokens.space-2;202
width: parent.width - 2 * MdTokens.space-2;203
height: parent.height;204
text: option;205
color: index == root.current-index206
? MdTokens.on-primary-container207
: MdTokens.on-surface;208
font-size: MdTokens.type-body-large;209
vertical-alignment: center;210
overflow: clip;211
accessible-role: none;212
}213
214
option-touch := TouchArea {215
clicked => {216
root.current-index = index;217
root.selected();218
popup.close();219
}220
}221
}222
}223
}224
}225
}226
227
animate background { duration: 120ms; easing: ease-in-out; }228
}229
230
// Text buttons use three deliberate width tiers. This keeps their geometry231
// stable when labels or translations change.232
export component BackButton inherits Button {233
text: "Back";234
width: 80px;235
height: MdTokens.target;236
}237
238
export component PageActionButton inherits Button {239
width: 96px;240
height: MdTokens.target;241
}242
243
export component FullWidthPrimaryButton inherits Button {244
height: MdTokens.target;245
primary: true;246
}247
248
// A full-width icon action shares the primary action's pill geometry while249
// retaining the lower-emphasis surface treatment of an auxiliary action.250
export component FullWidthSettingsButton inherits Rectangle {251
in property <bool> control-enabled: true;252
in property <string> field-label;253
callback clicked();254
255
height: MdTokens.target;256
background: MdTokens.surface-container;257
border-width: 1px;258
border-color: MdTokens.outline-variant;259
border-radius: MdTokens.button-radius;260
opacity: root.control-enabled ? 1 : 0.38;261
262
Rectangle {263
width: parent.width;264
height: parent.height;265
border-radius: root.border-radius;266
background: MdTokens.primary;267
opacity: settings-touch.pressed ? 0.12 : (settings-touch.has-hover ? 0.08 : 0);268
animate opacity { duration: 100ms; easing: ease-in-out; }269
}270
271
Path {272
x: (parent.width - self.width) / 2;273
y: (parent.height - self.height) / 2;274
width: 22px;275
height: 22px;276
viewbox-width: 24;277
viewbox-height: 24;278
commands: "M19.43 12.98C19.47 12.66 19.5 12.34 19.5 12C19.5 11.66 19.47 11.34 19.42 11.02L21.54 9.37L19.54 5.91L17.04 6.92C16.54 6.52 16 6.18 15.4 5.93L15 3.27H11L10.6 5.93C10 6.18 9.45 6.53 8.96 6.92L6.46 5.91L4.46 9.37L6.58 11.02C6.53 11.34 6.5 11.67 6.5 12C6.5 12.33 6.53 12.66 6.58 12.98L4.46 14.63L6.46 18.09L8.96 17.08C9.45 17.47 10 17.82 10.6 18.07L11 20.73H15L15.4 18.07C16 17.82 16.54 17.48 17.04 17.08L19.54 18.09L21.54 14.63L19.43 12.98ZM13 15.5C11.07 15.5 9.5 13.93 9.5 12C9.5 10.07 11.07 8.5 13 8.5C14.93 8.5 16.5 10.07 16.5 12C16.5 13.93 14.93 15.5 13 15.5Z";279
fill: MdTokens.on-surface;280
accessible-role: none;281
}282
283
settings-touch := TouchArea {284
enabled: root.control-enabled;285
accessible-role: button;286
accessible-label: root.field-label;287
accessible-enabled: root.control-enabled;288
clicked => { root.clicked(); }289
}290
}291
292
// Dialog actions share one geometry and state-layer implementation.293
// Only the semantic color role changes for destructive actions.294
export component DialogActionButton inherits Rectangle {295
in property <string> text;296
in property <string> field-label;297
in property <bool> destructive: false;298
callback clicked();299
300
width: 96px;301
height: MdTokens.target;302
background: root.destructive ? MdTokens.error-container : MdTokens.surface-container;303
border-radius: MdTokens.button-radius;304
305
Rectangle {306
width: parent.width;307
height: parent.height;308
border-radius: parent.border-radius;309
background: root.destructive ? MdTokens.on-error-container : MdTokens.on-surface;310
opacity: action-touch.pressed ? 0.12 : (action-touch.has-hover ? 0.08 : 0);311
animate opacity { duration: 100ms; easing: ease-in-out; }312
}313
314
Text {315
width: parent.width;316
height: parent.height;317
text: root.text;318
color: root.destructive ? MdTokens.on-error-container : MdTokens.on-surface;319
font-size: MdTokens.type-body-medium;320
font-weight: MdTokens.weight-medium;321
horizontal-alignment: center;322
vertical-alignment: center;323
accessible-role: none;324
}325
326
action-touch := TouchArea {327
accessible-role: button;328
accessible-label: root.field-label;329
clicked => { root.clicked(); }330
}331
}332
333
// A single geometry owns the container, state layer, icon and hit target.334
// This avoids the text baseline and native button padding shifting small icons.335
export component MaterialIconButton inherits Rectangle {336
in property <int> icon-kind; // 0 add, 1 refresh, 2 tune, 3 edit, 4 more, 5 calculate337
in property <bool> compact: false;338
in property <bool> control-enabled: true;339
in property <string> field-label;340
callback clicked();341
342
width: root.compact ? 40px : MdTokens.target;343
height: root.compact ? 40px : MdTokens.target;344
background: MdTokens.surface-container;345
border-width: 1px;346
border-color: MdTokens.outline-variant;347
border-radius: MdTokens.shape-medium;348
opacity: root.control-enabled ? 1 : 0.38;349
350
// Material state layer. It deliberately shares the exact outer geometry.351
Rectangle {352
width: parent.width;353
height: parent.height;354
border-radius: root.border-radius;355
background: MdTokens.primary;356
opacity: icon-touch.pressed ? 0.12 : (icon-touch.has-hover ? 0.08 : 0);357
animate opacity { duration: 100ms; easing: ease-in-out; }358
}359
360
if root.icon-kind == 0 : Path {361
x: (parent.width - self.width) / 2;362
y: (parent.height - self.height) / 2;363
width: 20px;364
height: 20px;365
viewbox-width: 24;366
viewbox-height: 24;367
commands: "M19 13H13V19H11V13H5V11H11V5H13V11H19V13Z";368
fill: MdTokens.on-surface;369
accessible-role: none;370
}371
372
// Google Material Icons refresh, adapted to Slint; see NOTICE.md and LICENSES/Apache-2.0.txt.373
// https://github.com/google/material-design-icons/blob/master/src/navigation/refresh/materialicons/24px.svg374
if root.icon-kind == 1 : Path {375
x: (parent.width - self.width) / 2;376
y: (parent.height - self.height) / 2;377
width: 22px;378
height: 22px;379
viewbox-width: 24;380
viewbox-height: 24;381
commands: "M17.65 6.35C16.2 4.9 14.21 4 12 4C7.58 4 4.01 7.58 4.01 12C4.01 16.42 7.58 20 12 20C15.73 20 18.84 17.45 19.73 14H17.65C16.83 16.33 14.61 18 12 18C8.69 18 6 15.31 6 12C6 8.69 8.69 6 12 6C13.66 6 15.14 6.69 16.22 7.78L13 11H20V4L17.65 6.35Z";382
fill: MdTokens.on-surface;383
accessible-role: none;384
}385
386
if root.icon-kind == 2 : Path {387
x: (parent.width - self.width) / 2;388
y: (parent.height - self.height) / 2;389
width: 22px;390
height: 22px;391
viewbox-width: 24;392
viewbox-height: 24;393
commands: "M3 17V19H9V17H3ZM3 5V7H13V5H3ZM13 21V19H21V17H13V15H11V21H13ZM7 9V11H3V13H7V15H9V9H7ZM21 13V11H11V13H21ZM15 9H17V7H21V5H17V3H15V9Z";394
fill: MdTokens.on-surface;395
accessible-role: none;396
}397
398
// Google Material Icons edit, adapted to Slint; see NOTICE.md and LICENSES/Apache-2.0.txt.399
// https://github.com/google/material-design-icons/blob/master/src/image/edit/materialicons/24px.svg400
if root.icon-kind == 3 : Path {401
x: (parent.width - self.width) / 2;402
y: (parent.height - self.height) / 2;403
width: 22px;404
height: 22px;405
viewbox-width: 24;406
viewbox-height: 24;407
commands: "M3 17.25V21H6.75L17.81 9.94L14.06 6.19L3 17.25ZM20.71 7.04C21.1 6.65 21.1 6.02 20.71 5.63L18.37 3.29C17.98 2.9 17.35 2.9 16.96 3.29L15.13 5.12L18.88 8.87L20.71 7.04Z";408
fill: MdTokens.on-surface;409
accessible-role: none;410
}411
412
if root.icon-kind == 4 : Path {413
x: (parent.width - self.width) / 2;414
y: (parent.height - self.height) / 2;415
width: 22px;416
height: 22px;417
viewbox-width: 24;418
viewbox-height: 24;419
commands: "M6 10C4.9 10 4 10.9 4 12C4 13.1 4.9 14 6 14C7.1 14 8 13.1 8 12C8 10.9 7.1 10 6 10ZM12 10C10.9 10 10 10.9 10 12C10 13.1 10.9 14 12 14C13.1 14 14 13.1 14 12C14 10.9 13.1 10 12 10ZM18 10C16.9 10 16 10.9 16 12C16 13.1 16.9 14 18 14C19.1 14 20 13.1 20 12C20 10.9 19.1 10 18 10Z";420
fill: MdTokens.on-surface;421
accessible-role: none;422
}423
424
if root.icon-kind == 5 : Path {425
x: (parent.width - self.width) / 2;426
y: (parent.height - self.height) / 2;427
width: 22px;428
height: 22px;429
viewbox-width: 24;430
viewbox-height: 24;431
commands: "M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3ZM19 19H5V5H19V19ZM7 7H17V11H7V7ZM7 13H9V15H7V13ZM11 13H13V15H11V13ZM15 13H17V15H15V13ZM7 17H9V19H7V17ZM11 17H13V19H11V17ZM15 17H17V19H15V17Z";432
fill: MdTokens.on-surface;433
accessible-role: none;434
}435
436
icon-touch := TouchArea {437
enabled: root.control-enabled;438
accessible-role: button;439
accessible-label: root.field-label;440
clicked => { root.clicked(); }441
}442
}443