[ui] flatten desktop surfaces and controls

This commit is contained in:
dijunkun
2026-10-26 14:53:09 +08:00
parent 38d0ce7728
commit c3ea4eb8fc
18 changed files with 337 additions and 428 deletions
+8 -8
View File
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { ScrollView, TextEdit } from "std-widgets.slint"; import { ScrollView, TextEdit } from "std-widgets.slint";
import { SettingsDialog, SettingsButton } from "settings_controls.slint"; import { SettingsDialog, SettingsButton } from "settings_controls.slint";
import { WorkbenchStyle } from "workbench_controls.slint"; import { WorkbenchStyle } from "workbench_controls.slint";
@@ -27,11 +27,11 @@ export global AboutStrings {
out property <string> back: GalleryStrings.tr("返回", "Back", "Назад"); out property <string> back: GalleryStrings.tr("返回", "Back", "Назад");
} }
export component AboutNavigationGroup inherits ShadowSurface { export component AboutNavigationGroup inherits FlatSurface {
height: rows.preferred-height + 2px; height: rows.preferred-height + 2px;
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
border-radius: UiRadius.control; border-radius: UiRadius.control;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
clip: true; clip: true;
rows := VerticalLayout { rows := VerticalLayout {
x: 1px; y: 1px; width: parent.width - 2px; x: 1px; y: 1px; width: parent.width - 2px;
@@ -42,13 +42,13 @@ export component AboutNavigationGroup inherits ShadowSurface {
export component AboutRowSeparator inherits Rectangle { export component AboutRowSeparator inherits Rectangle {
height: 1px; height: 1px;
ShadowSeparator { SurfaceSeparator {
x: 14px; width: parent.width - 30px; height: 1px; x: 14px; width: parent.width - 30px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
export component AboutNavigationRow inherits ShadowSurface { export component AboutNavigationRow inherits FlatSurface {
in property <string> title; in property <string> title;
in property <string> subtitle; in property <string> subtitle;
in property <string> icon; in property <string> icon;
@@ -61,7 +61,7 @@ export component AboutNavigationRow inherits ShadowSurface {
background: touch.pressed ? WorkbenchStyle.pressed-surface background: touch.pressed ? WorkbenchStyle.pressed-surface
: touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface; : touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface;
border-radius: root.grouped ? 0px : UiRadius.control; border-radius: root.grouped ? 0px : UiRadius.control;
shadow-enabled: !root.grouped; shadow-color: UiShadow.edge; outline-enabled: !root.grouped; outline-color: UiSurface.edge;
accessible-role: button; accessible-role: button;
accessible-label: root.title + (root.subtitle != "" ? ", " + root.subtitle : "") accessible-label: root.title + (root.subtitle != "" ? ", " + root.subtitle : "")
+ (root.detail != "" ? ", " + root.detail : ""); + (root.detail != "" ? ", " + root.detail : "");
@@ -227,7 +227,7 @@ export component AboutPanel inherits SettingsDialog {
footer := VerticalLayout { footer := VerticalLayout {
x: 24px; y: parent.height - self.height - 24px; x: 24px; y: parent.height - self.height - 24px;
width: parent.width - 48px; height: self.preferred-height; spacing: 12px; width: parent.width - 48px; height: self.preferred-height; spacing: 12px;
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
HorizontalLayout { HorizontalLayout {
alignment: end; alignment: end;
SettingsButton { text: AboutStrings.back; clicked => { root.back(); } } SettingsButton { text: AboutStrings.back; clicked => { root.back(); } }
+29 -46
View File
@@ -1,4 +1,4 @@
import { ShadowSurface, UiShadow } from "ui_shadow.slint"; import { FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
export global FloatingBarStyle { export global FloatingBarStyle {
out property <length> corner-radius: UiRadius.menu; out property <length> corner-radius: UiRadius.menu;
@@ -199,15 +199,14 @@ export component ImageButton inherits Rectangle {
} }
} }
export component CompactComboBox inherits ShadowSurface { export component CompactComboBox inherits FlatSurface {
shadow-compact: true;
in property <[string]> model; in property <[string]> model;
in-out property <int> current-index: 0; in-out property <int> current-index: 0;
in property <bool> enabled: true; in property <bool> enabled: true;
in property <bool> subtle: false; in property <bool> subtle: false;
in property <bool> show-arrow: true; in property <bool> show-arrow: true;
in property <color> normal-shadow-color: UiShadow.edge; in property <color> normal-outline-color: ButtonStyle.border;
in property <color> hover-shadow-color: UiShadow.hover; in property <color> hover-outline-color: ButtonStyle.hover-border;
property <bool> popup-animation-enabled: false; property <bool> popup-animation-enabled: false;
property <float> popup-progress: 0.0; property <float> popup-progress: 0.0;
callback selected(int); callback selected(int);
@@ -234,17 +233,13 @@ export component CompactComboBox inherits ShadowSurface {
background: !root.enabled ? ControlStyle.disabled background: !root.enabled ? ControlStyle.disabled
: combo-touch.pressed ? ButtonStyle.pressed : combo-touch.pressed ? ButtonStyle.pressed
: combo-popup.is-open || combo-touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; : combo-popup.is-open || combo-touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface;
border-width: 1px; outline-focused: root.enabled && combo-popup.is-open;
border-color: !root.enabled ? ButtonStyle.disabled-border outline-color: !root.enabled ? ButtonStyle.disabled-border
: combo-popup.is-open ? UiShadow.focus : combo-popup.is-open ? UiSurface.focus
: combo-touch.pressed ? ButtonStyle.pressed-border : combo-touch.pressed ? ButtonStyle.pressed-border
: combo-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; : combo-touch.has-hover ? root.hover-outline-color : root.normal-outline-color;
shadow-enabled: root.enabled && !combo-touch.pressed;
shadow-focused: root.enabled && combo-popup.is-open;
shadow-color: combo-popup.is-open ? UiShadow.focus
: root.enabled && combo-touch.has-hover ? root.hover-shadow-color : root.normal-shadow-color;
border-radius: UiRadius.control; border-radius: UiRadius.control;
animate background, border-color, shadow-color { duration: 120ms; } animate background, outline-color { duration: 120ms; }
accessible-role: combobox; accessible-role: combobox;
accessible-enabled: root.enabled; accessible-enabled: root.enabled;
accessible-expandable: true; accessible-expandable: true;
@@ -267,7 +262,7 @@ export component CompactComboBox inherits ShadowSurface {
width: 18px; height: parent.height; width: 18px; height: parent.height;
// Switch glyphs so the software renderer also shows the open state. // Switch glyphs so the software renderer also shows the open state.
text: combo-popup.is-open ? FontAwesomeIcons.angle-up : FontAwesomeIcons.angle-down; text: combo-popup.is-open ? FontAwesomeIcons.angle-up : FontAwesomeIcons.angle-down;
color: !root.enabled ? #98a2b3 : combo-popup.is-open ? UiShadow.focus : ControlStyle.icon; color: !root.enabled ? #98a2b3 : combo-popup.is-open ? UiSurface.focus : ControlStyle.icon;
font-family: "Font Awesome 6 Free"; font-family: "Font Awesome 6 Free";
font-weight: 900; font-weight: 900;
font-size: 10px; font-size: 10px;
@@ -306,7 +301,7 @@ export component CompactComboBox inherits ShadowSurface {
height: root.model.length * 23px + 8px; height: root.model.length * 23px + 8px;
close-policy: close-on-click-outside; close-policy: close-on-click-outside;
ShadowSurface { FlatSurface {
// Keep the reveal anchored to the popup's top edge while the // Keep the reveal anchored to the popup's top edge while the
// content settles downward into its final position. // content settles downward into its final position.
y: (1.0 - root.popup-progress) * -8px; y: (1.0 - root.popup-progress) * -8px;
@@ -314,7 +309,7 @@ export component CompactComboBox inherits ShadowSurface {
height: parent.height * root.popup-progress; height: parent.height * root.popup-progress;
opacity: root.popup-progress; opacity: root.popup-progress;
background: white; background: white;
shadow-color: root.normal-shadow-color; outline-color: root.normal-outline-color;
border-radius: UiRadius.menu; border-radius: UiRadius.menu;
clip: true; clip: true;
for choice[index] in root.model: Rectangle { for choice[index] in root.model: Rectangle {
@@ -333,7 +328,7 @@ export component CompactComboBox inherits ShadowSurface {
} }
if index == root.current-index: Text { if index == root.current-index: Text {
x: parent.width - 20px; width: 14px; height: parent.height; x: parent.width - 20px; width: 14px; height: parent.height;
text: FontAwesomeIcons.check; color: UiShadow.focus; text: FontAwesomeIcons.check; color: UiSurface.focus;
font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 10px; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 10px;
horizontal-alignment: center; vertical-alignment: center; horizontal-alignment: center; vertical-alignment: center;
accessible-role: none; accessible-role: none;
@@ -353,23 +348,20 @@ export component CompactComboBox inherits ShadowSurface {
} }
} }
} }
ShadowSurface { FlatSurface {
width: parent.width; width: parent.width;
height: parent.height; height: parent.height;
background: transparent; background: transparent;
clip: true; clip: true;
shadow-color: root.normal-shadow-color; outline-color: root.normal-outline-color;
border-radius: UiRadius.menu; border-radius: UiRadius.menu;
border-width: 1px;
border-color: ButtonStyle.border;
z: 2; z: 2;
} }
} }
} }
} }
export component ImGuiCheckBox inherits ShadowSurface { export component ImGuiCheckBox inherits FlatSurface {
shadow-compact: true;
in-out property <bool> checked; in-out property <bool> checked;
in property <bool> enabled: true; in property <bool> enabled: true;
@@ -378,7 +370,7 @@ export component ImGuiCheckBox inherits ShadowSurface {
background: !root.enabled ? ControlStyle.disabled background: !root.enabled ? ControlStyle.disabled
: root.checked ? (check-touch.pressed ? #254cc0 : check-touch.has-hover ? #4574f0 : #3564e3) : root.checked ? (check-touch.pressed ? #254cc0 : check-touch.has-hover ? #4574f0 : #3564e3)
: check-touch.pressed ? ControlStyle.pressed : check-touch.has-hover ? ControlStyle.hover : ControlStyle.surface; : check-touch.pressed ? ControlStyle.pressed : check-touch.has-hover ? ControlStyle.hover : ControlStyle.surface;
shadow-color: check-touch.has-hover && root.enabled ? UiShadow.focus : UiShadow.edge; outline-color: check-touch.has-hover && root.enabled ? UiSurface.focus : UiSurface.edge;
border-radius: 4px; border-radius: 4px;
if root.checked: Text { if root.checked: Text {
text: FontAwesomeIcons.check; text: FontAwesomeIcons.check;
@@ -419,8 +411,7 @@ export component MiniToggleSwitch inherits Rectangle {
} }
} }
export component CompactButton inherits ShadowSurface { export component CompactButton inherits FlatSurface {
shadow-compact: true;
in property <string> text; in property <string> text;
in property <length> text-size: ImGuiFontStyle.body; in property <length> text-size: ImGuiFontStyle.body;
in property <bool> primary: false; in property <bool> primary: false;
@@ -434,15 +425,13 @@ export component CompactButton inherits ShadowSurface {
: button-touch.pressed ? (root.primary ? #82b8f2 : ButtonStyle.pressed) : button-touch.pressed ? (root.primary ? #82b8f2 : ButtonStyle.pressed)
: button-touch.has-hover ? (root.primary ? #b8d9fb : ButtonStyle.hover) : button-touch.has-hover ? (root.primary ? #b8d9fb : ButtonStyle.hover)
: root.primary ? #a8d0fa : root.quiet ? transparent : ButtonStyle.surface; : root.primary ? #a8d0fa : root.quiet ? transparent : ButtonStyle.surface;
border-width: root.quiet ? 0px : 1px; outline-enabled: !root.quiet;
border-color: !root.enabled ? ButtonStyle.disabled-border outline-color: !root.enabled ? ButtonStyle.disabled-border
: root.primary ? UiShadow.focus : root.primary ? UiSurface.focus
: button-touch.pressed ? ButtonStyle.pressed-border : button-touch.pressed ? ButtonStyle.pressed-border
: button-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; : button-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border;
shadow-enabled: root.enabled && !root.quiet && !button-touch.pressed;
shadow-color: root.primary ? UiShadow.focus : button-touch.has-hover ? UiShadow.hover : UiShadow.edge;
border-radius: UiRadius.control; border-radius: UiRadius.control;
animate background, border-color { duration: 100ms; } animate background, outline-color { duration: 100ms; }
Text { Text {
text: root.text; text: root.text;
color: root.enabled ? #24272d : #8e9298; color: root.enabled ? #24272d : #8e9298;
@@ -454,7 +443,7 @@ export component CompactButton inherits ShadowSurface {
button-touch := TouchArea { enabled: root.enabled; mouse-cursor: pointer; clicked => { root.clicked(); } } button-touch := TouchArea { enabled: root.enabled; mouse-cursor: pointer; clicked => { root.clicked(); } }
} }
export component PrimaryButton inherits ShadowSurface { export component PrimaryButton inherits FlatSurface {
in property <string> text; in property <string> text;
in property <length> text-size: 12px; in property <length> text-size: 12px;
in property <bool> enabled: true; in property <bool> enabled: true;
@@ -466,9 +455,7 @@ export component PrimaryButton inherits ShadowSurface {
: touch.pressed ? #254cc0 : touch.pressed ? #254cc0
: touch.has-hover ? #4574f0 : touch.has-hover ? #4574f0
: #3564e3; : #3564e3;
shadow-compact: true; outline-enabled: false;
shadow-enabled: root.enabled && !touch.pressed;
shadow-color: UiShadow.focus;
border-radius: UiRadius.control; border-radius: UiRadius.control;
animate background { duration: 100ms; } animate background { duration: 100ms; }
@@ -487,7 +474,7 @@ export component PrimaryButton inherits ShadowSurface {
} }
} }
export component SecondaryButton inherits ShadowSurface { export component SecondaryButton inherits FlatSurface {
in property <string> text; in property <string> text;
in property <length> text-size: 12px; in property <length> text-size: 12px;
in property <bool> enabled: true; in property <bool> enabled: true;
@@ -498,15 +485,11 @@ export component SecondaryButton inherits ShadowSurface {
background: !root.enabled ? ControlStyle.disabled background: !root.enabled ? ControlStyle.disabled
: touch.pressed ? ButtonStyle.pressed : touch.pressed ? ButtonStyle.pressed
: touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; : touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface;
border-width: 1px; outline-color: !root.enabled ? ButtonStyle.disabled-border
border-color: !root.enabled ? ButtonStyle.disabled-border
: touch.pressed ? ButtonStyle.pressed-border : touch.pressed ? ButtonStyle.pressed-border
: touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; : touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border;
shadow-color: touch.has-hover ? UiShadow.hover : UiShadow.edge;
shadow-compact: true;
shadow-enabled: root.enabled && !touch.pressed;
border-radius: UiRadius.control; border-radius: UiRadius.control;
animate background, border-color { duration: 100ms; } animate background, outline-color { duration: 100ms; }
Text { Text {
text: root.text; text: root.text;
@@ -565,8 +548,8 @@ export component SectionTitle inherits Text {
vertical-alignment: center; vertical-alignment: center;
} }
export component DialogSurface inherits ShadowSurface { export component DialogSurface inherits FlatSurface {
background: white; background: white;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
} }
@@ -1,4 +1,4 @@
import { ShadowSurface, UiShadow } from "ui_shadow.slint"; import { FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ScrollView } from "std-widgets.slint"; import { ScrollView } from "std-widgets.slint";
import { RoundedLineEdit } from "rounded_line_edit.slint"; import { RoundedLineEdit } from "rounded_line_edit.slint";
@@ -21,13 +21,13 @@ export component DeviceGroupDialog inherits Rectangle {
callback cancel(); callback cancel();
background: #00000040; background: #00000040;
TouchArea {} TouchArea {}
dialog := ShadowSurface { dialog := FlatSurface {
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
width: min(360px, parent.width - 40px); width: min(360px, parent.width - 40px);
height: 208px + group-list.height + (root.error == "" ? 0px : 32px); height: 208px + group-list.height + (root.error == "" ? 0px : 32px);
background: white; border-radius: UiRadius.surface; background: white; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
TouchArea {} TouchArea {}
Text { Text {
x: 18px; y: 16px; height: 24px; x: 18px; y: 16px; height: 24px;
@@ -42,10 +42,10 @@ export component DeviceGroupDialog inherits Rectangle {
x: 18px; y: 67px; width: parent.width - 36px; height: 16px; x: 18px; y: 67px; width: parent.width - 36px; height: 16px;
text: "ID " + root.device-id; color: #858c97; font-size: 11px; text: "ID " + root.device-id; color: #858c97; font-size: 11px;
} }
group-list := ShadowSurface { group-list := FlatSurface {
x: 18px; y: 95px; width: parent.width - 36px; x: 18px; y: 95px; width: parent.width - 36px;
height: min(5, max(1, root.options.length)) * 32px + 2px; height: min(5, max(1, root.options.length)) * 32px + 2px;
background: #fbfcfd; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; background: #fbfcfd; outline-color: UiSurface.edge; border-radius: UiRadius.surface;
clip: true; clip: true;
ScrollView { ScrollView {
x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px; x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px;
@@ -81,10 +81,10 @@ export component DeviceGroupDialog inherits Rectangle {
border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface; border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface;
border-color: white; border-color: white;
} }
ShadowSurface { FlatSurface {
width: parent.width; height: parent.height; width: parent.width; height: parent.height;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
} }
} }
RoundedLineEdit { RoundedLineEdit {
@@ -1,4 +1,4 @@
import { ShadowSeparator, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, UiSurface } from "ui_surface.slint";
import { FileTransferEntry } from "stream_window.slint"; import { FileTransferEntry } from "stream_window.slint";
import { DialogSurface, FontAwesomeIcons, IconButton, ImGuiFontStyle } from "common.slint"; import { DialogSurface, FontAwesomeIcons, IconButton, ImGuiFontStyle } from "common.slint";
import { WorkbenchStyle, WorkbenchButton } from "workbench_controls.slint"; import { WorkbenchStyle, WorkbenchButton } from "workbench_controls.slint";
@@ -27,7 +27,7 @@ export component FileTransferNotification inherits Window {
DialogSurface { DialogSurface {
width: parent.width; height: parent.height; width: parent.width; height: parent.height;
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
Text { Text {
x: 16px; y: 8px; x: 16px; y: 8px;
width: parent.width - 64px - (root.remote-id == "" ? 0px : 108px); width: parent.width - 64px - (root.remote-id == "" ? 0px : 108px);
@@ -51,9 +51,9 @@ export component FileTransferNotification inherits Window {
pressed-background: WorkbenchStyle.pressed-surface; pressed-background: WorkbenchStyle.pressed-surface;
clicked => { root.dismissed(); } clicked => { root.dismissed(); }
} }
ShadowSeparator { SurfaceSeparator {
x: 16px; y: 40px; width: parent.width - 32px; height: 1px; x: 16px; y: 40px; width: parent.width - 32px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
Text { Text {
x: 16px; y: 47px; width: parent.width - 32px; height: 18px; x: 16px; y: 47px; width: parent.width - 32px; height: 18px;
@@ -97,7 +97,7 @@ export component FileTransferNotification inherits Window {
text: root.cancel-text; text: root.cancel-text;
text-size: ImGuiFontStyle.base; text-size: ImGuiFontStyle.base;
text-color: WorkbenchStyle.muted; text-color: WorkbenchStyle.muted;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
clicked => { root.cancel-requested(); } clicked => { root.cancel-requested(); }
} }
} }
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { CompactButton, FontAwesomeIcons, ControlStyle } from "common.slint"; import { CompactButton, FontAwesomeIcons, ControlStyle } from "common.slint";
@@ -89,11 +89,11 @@ export component GalleryMenuButton inherits Rectangle {
color: root.enabled ? #62666d : #8e9298; color: root.enabled ? #62666d : #8e9298;
horizontal-alignment: center; vertical-alignment: center; horizontal-alignment: center; vertical-alignment: center;
} }
if root.compact-trigger: ShadowSurface { if root.compact-trigger: FlatSurface {
background: more-touch.pressed || popup.is-open ? ControlStyle.selected : more-touch.has-hover ? ControlStyle.hover : white; background: more-touch.pressed || popup.is-open ? ControlStyle.selected : more-touch.has-hover ? ControlStyle.hover : white;
border-radius: UiRadius.menu; border-radius: UiRadius.menu;
shadow-color: trigger-focus.has-focus ? UiShadow.focus : UiShadow.edge; outline-color: trigger-focus.has-focus ? UiSurface.focus : UiSurface.edge;
animate background, shadow-color { duration: 120ms; } animate background, outline-color { duration: 120ms; }
for dot in 3: Rectangle { for dot in 3: Rectangle {
x: parent.width / 2 - 6px + dot * 5px; x: parent.width / 2 - 6px + dot * 5px;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
@@ -117,12 +117,12 @@ export component GalleryMenuButton inherits Rectangle {
forward-focus: popup-focus; forward-focus: popup-focus;
popup-focus := FocusScope { popup-focus := FocusScope {
key-pressed(event) => { return root.handle-key(event); } key-pressed(event) => { return root.handle-key(event); }
ShadowSurface { FlatSurface {
background: white; shadow-color: UiShadow.edge; border-radius: UiRadius.menu; background: white; outline-color: UiSurface.edge; border-radius: UiRadius.menu;
clip: true; clip: true;
if root.destructive-index > 0: ShadowSeparator { if root.destructive-index > 0: SurfaceSeparator {
x: 10px; y: root.menu-padding + root.destructive-index * root.row-height + (root.divider-spacing - 1px) / 2; x: 10px; y: root.menu-padding + root.destructive-index * root.row-height + (root.divider-spacing - 1px) / 2;
width: parent.width - 20px; height: 1px; shadow-color: UiShadow.separator; width: parent.width - 20px; height: 1px; outline-color: UiSurface.separator;
} }
for item[index] in root.items: Rectangle { for item[index] in root.items: Rectangle {
private property <bool> destructive: index == root.destructive-index; private property <bool> destructive: index == root.destructive-index;
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { GalleryStrings } from "device_strings.slint"; import { GalleryStrings } from "device_strings.slint";
import { CompactButton } from "common.slint"; import { CompactButton } from "common.slint";
@@ -191,13 +191,13 @@ export component GalleryNetworkDialog inherits Rectangle {
callback close(); callback close();
background: #00000042; background: #00000042;
TouchArea { clicked => { root.close(); } } TouchArea { clicked => { root.close(); } }
ShadowSurface { FlatSurface {
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
width: min(760px, parent.width - 32px); width: min(760px, parent.width - 32px);
height: min(120px + root.list-height, parent.height - 32px); height: min(120px + root.list-height, parent.height - 32px);
background: white; border-radius: UiRadius.surface; background: white; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
TouchArea {} TouchArea {}
FocusScope { FocusScope {
init => { self.focus(); } init => { self.focus(); }
@@ -238,7 +238,7 @@ export component GalleryNetworkDialog inherits Rectangle {
for device[index] in root.devices: NetworkRow { for device[index] in root.devices: NetworkRow {
y: index * root.row-height; width: list.viewport-width; height: root.row-height; y: index * root.row-height; width: list.viewport-width; height: root.row-height;
device: device; background: mod(index, 2) == 0 ? white : #f8fafc; device: device; background: mod(index, 2) == 0 ? white : #f8fafc;
ShadowSeparator { y: parent.height - 1px; height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { y: parent.height - 1px; height: 1px; outline-color: UiSurface.separator; }
} }
} }
if root.devices.length == 0: NetworkEmptyState { if root.devices.length == 0: NetworkEmptyState {
+40 -40
View File
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { GalleryStrings } from "device_strings.slint"; import { GalleryStrings } from "device_strings.slint";
import { RemoteCursor } from "remote_cursor.slint"; import { RemoteCursor } from "remote_cursor.slint";
@@ -492,13 +492,13 @@ export component GalleryView inherits Rectangle {
width: parent.width - self.x - (tile-touch.has-hover && !root.batch ? DeviceCardStyle.toolbar-width + 12px : root.batch ? 35px : 9px); width: parent.width - self.x - (tile-touch.has-hover && !root.batch ? DeviceCardStyle.toolbar-width + 12px : root.batch ? 35px : 9px);
height: 24px; text: tile.name; color: tile.online ? #24272d : DeviceCardStyle.offline-text; font-size: 12px; overflow: elide; vertical-alignment: center; height: 24px; text: tile.name; color: tile.online ? #24272d : DeviceCardStyle.offline-text; font-size: 12px; overflow: elide; vertical-alignment: center;
} }
if root.batch && !root.simplified: ShadowSurface { if root.batch && !root.simplified: FlatSurface {
x: parent.width - 27px; y: parent.height - 27px; x: parent.width - 27px; y: parent.height - 27px;
width: 18px; height: 18px; border-radius: 4px; width: 18px; height: 18px; border-radius: 4px;
background: tile.selected ? (tile-touch.pressed ? #254cc0 : tile-touch.has-hover ? #4574f0 : #3564e3) background: tile.selected ? (tile-touch.pressed ? #254cc0 : tile-touch.has-hover ? #4574f0 : #3564e3)
: tile-touch.pressed ? ControlStyle.pressed : tile-touch.has-hover ? ControlStyle.hover : ControlStyle.surface; : tile-touch.pressed ? ControlStyle.pressed : tile-touch.has-hover ? ControlStyle.hover : ControlStyle.surface;
shadow-color: tile.selected ? UiShadow.focus : tile-touch.has-hover ? UiShadow.hover : UiShadow.edge; outline-color: tile.selected ? UiSurface.focus : tile-touch.has-hover ? UiSurface.hover : UiSurface.edge;
animate background, shadow-color { duration: 120ms; } animate background, outline-color { duration: 120ms; }
if tile.selected: Text { if tile.selected: Text {
text: FontAwesomeIcons.check; color: white; text: FontAwesomeIcons.check; color: white;
font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 11px; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 11px;
@@ -533,27 +533,27 @@ export component GalleryView inherits Rectangle {
} }
} }
// Draw each table line once, continuously across occupied and empty cells. // Draw each table line once, continuously across occupied and empty cells.
for column in root.columns + 1: ShadowSeparator { for column in root.columns + 1: SurfaceSeparator {
visible: root.count > 0; visible: root.count > 0;
x: min(column * root.card-width, grid.table-width - 1px); x: min(column * root.card-width, grid.table-width - 1px);
y: 0px; y: 0px;
width: 1px; height: grid.table-height; width: 1px; height: grid.table-height;
vertical: true; shadow-color: root.resizing || (grid.has-hover && grid.edge-x != 0 && grid.column-line == column) vertical: true; outline-color: root.resizing || (grid.has-hover && grid.edge-x != 0 && grid.column-line == column)
? UiShadow.focus : UiShadow.separator; ? UiSurface.focus : UiSurface.separator;
} }
for row in root.rows + 1: ShadowSeparator { for row in root.rows + 1: SurfaceSeparator {
visible: root.count > 0; visible: root.count > 0;
x: 0px; x: 0px;
y: min(row * root.card-height, grid.table-height - 1px); y: min(row * root.card-height, grid.table-height - 1px);
width: grid.table-width; height: 1px; width: grid.table-width; height: 1px;
shadow-color: root.resizing || (grid.has-hover && grid.edge-y != 0 && grid.row-line == row) outline-color: root.resizing || (grid.has-hover && grid.edge-y != 0 && grid.row-line == row)
? UiShadow.focus : UiShadow.separator; ? UiSurface.focus : UiSurface.separator;
} }
if root.drag-target >= 0: ShadowSurface { if root.drag-target >= 0: FlatSurface {
x: mod(root.drag-target, root.columns) * root.card-width + 1px; x: mod(root.drag-target, root.columns) * root.card-width + 1px;
y: floor(root.drag-target / root.columns) * root.card-height + 1px; y: floor(root.drag-target / root.columns) * root.card-height + 1px;
width: root.card-width - 2px; height: root.card-height - 2px; width: root.card-width - 2px; height: root.card-height - 2px;
background: #76aeea26; shadow-color: UiShadow.focus; background: #76aeea26; outline-color: UiSurface.focus;
} }
if root.count == 0: Text { if root.count == 0: Text {
text: root.simplified text: root.simplified
@@ -599,11 +599,11 @@ export component GalleryView inherits Rectangle {
} }
GalleryButton { width: 100px; primary: true; enabled: !root.focused.connected || root.controlling || (root.focused.has-frame && !root.focused.stale); text: root.focused.connected ? (root.controlling ? GalleryStrings.tr("释放操控", "Release", "Отпустить") : GalleryStrings.tr("接管操控", "Control", "Управлять")) : GalleryStrings.tr("连接", "Connect", "Подключить"); clicked => { root.action(root.focused.connected ? (root.controlling ? "release" : "control") : "connect", root.focused.id); } } GalleryButton { width: 100px; primary: true; enabled: !root.focused.connected || root.controlling || (root.focused.has-frame && !root.focused.stale); text: root.focused.connected ? (root.controlling ? GalleryStrings.tr("释放操控", "Release", "Отпустить") : GalleryStrings.tr("接管操控", "Control", "Управлять")) : GalleryStrings.tr("连接", "Connect", "Подключить"); clicked => { root.action(root.focused.connected ? (root.controlling ? "release" : "control") : "connect", root.focused.id); } }
} }
ShadowSurface { FlatSurface {
x: 0px; y: rail.visible ? focus-divider.y + focus-divider.height : 36px; x: 0px; y: rail.visible ? focus-divider.y + focus-divider.height : 36px;
width: parent.width; height: parent.height - self.y; width: parent.width; height: parent.height - self.y;
background: root.focused.online ? #eff1f4 : DeviceCardStyle.offline-background; border-radius: UiRadius.surface; background: root.focused.online ? #eff1f4 : DeviceCardStyle.offline-background; border-radius: UiRadius.surface;
shadow-color: root.controlling ? UiShadow.focus : UiShadow.edge; outline-color: root.controlling ? UiSurface.focus : UiSurface.edge;
video := Rectangle { video := Rectangle {
x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px; x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px;
background: root.focused.online ? #24272d : DeviceCardStyle.offline-preview; background: root.focused.online ? #24272d : DeviceCardStyle.offline-preview;
@@ -670,12 +670,12 @@ export component GalleryView inherits Rectangle {
// Collapse the focused tile and its gap without changing the page model. // Collapse the focused tile and its gap without changing the page model.
width: tile.id == root.focused.id ? 0px : root.rail-slot-width; width: tile.id == root.focused.id ? 0px : root.rail-slot-width;
height: root.rail-card-height; visible: tile.id != root.focused.id; height: root.rail-card-height; visible: tile.id != root.focused.id;
rail-card := ShadowSurface { rail-card := FlatSurface {
private property <string> presence: tile.online ? GalleryStrings.tr("在线", "Online", "В сети") private property <string> presence: tile.online ? GalleryStrings.tr("在线", "Online", "В сети")
: tile.presence-known ? GalleryStrings.tr("离线", "Offline", "Не в сети") : GalleryStrings.tr("未知", "Unknown", "Неизвестно"); : tile.presence-known ? GalleryStrings.tr("离线", "Offline", "Не в сети") : GalleryStrings.tr("未知", "Unknown", "Неизвестно");
private property <string> details: tile.name + "\n" + tile.host-name + "\nID: " + tile.id + "\n" + tile.group-names + "\n" + self.presence; private property <string> details: tile.name + "\n" + tile.host-name + "\nID: " + tile.id + "\n" + tile.group-names + "\n" + self.presence;
width: root.rail-preview-width + 10px; height: root.rail-card-height; border-radius: UiRadius.surface; width: root.rail-preview-width + 10px; height: root.rail-card-height; border-radius: UiRadius.surface;
background: tile.online ? #f5f6f8 : DeviceCardStyle.offline-background; shadow-color: UiShadow.edge; background: tile.online ? #f5f6f8 : DeviceCardStyle.offline-background; outline-color: UiSurface.edge;
accessible-role: button; accessible-role: button;
accessible-label: self.details; accessible-label: self.details;
accessible-action-default => { root.action("focus", tile.id); } accessible-action-default => { root.action("focus", tile.id); }
@@ -716,9 +716,9 @@ export component GalleryView inherits Rectangle {
visible: root.focus-rail-visible; visible: root.focus-rail-visible;
x: 0px; y: rail.y + rail.height; width: parent.width; height: 12px; x: 0px; y: rail.y + rail.height; width: parent.width; height: 12px;
private property <bool> highlighted: divider-touch.has-hover || root.focus-resizing || divider-focus.has-focus; private property <bool> highlighted: divider-touch.has-hover || root.focus-resizing || divider-focus.has-focus;
ShadowSeparator { SurfaceSeparator {
x: 0px; y: 5px; width: parent.width; height: 1px; x: 0px; y: 5px; width: parent.width; height: 1px;
shadow-color: focus-divider.highlighted ? UiShadow.focus : UiShadow.separator; outline-color: focus-divider.highlighted ? UiSurface.focus : UiSurface.separator;
} }
Rectangle { Rectangle {
x: (parent.width - self.width) / 2; y: 4px; width: 36px; height: 3px; border-radius: 1.5px; x: (parent.width - self.width) / 2; y: 4px; width: 36px; height: 3px; border-radius: 1.5px;
@@ -777,7 +777,7 @@ export component GalleryView inherits Rectangle {
} }
Rectangle { Rectangle {
y: parent.height - 44px; height: 44px; background: #fafbfc; y: parent.height - 44px; height: 44px; background: #fafbfc;
ShadowSeparator { y: 0px; width: parent.width; height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { y: 0px; width: parent.width; height: 1px; outline-color: UiSurface.separator; }
} }
// Connection status and pagination remain available in every selection state. // Connection status and pagination remain available in every selection state.
footer := Rectangle { footer := Rectangle {
@@ -794,8 +794,8 @@ export component GalleryView inherits Rectangle {
} }
network-button := SecondaryButton { network-button := SecondaryButton {
x: connection-count.x + connection-count.width + 12px; x: connection-count.x + connection-count.width + 12px;
min-width: 64px; width: 64px; height: parent.height; width: 64px; height: parent.height;
shadow-color: UiShadow.edge; border-radius: UiRadius.control; outline-color: UiSurface.edge; border-radius: UiRadius.control;
text: GalleryStrings.tr("网络状态", "Network", "Сеть"); text: GalleryStrings.tr("网络状态", "Network", "Сеть");
accessible-role: button; accessible-role: button;
accessible-label: self.text; accessible-label: self.text;
@@ -843,11 +843,11 @@ export component GalleryView inherits Rectangle {
horizontal-alignment: center; vertical-alignment: center; horizontal-alignment: center; vertical-alignment: center;
} }
} }
if root.dragging: ShadowSurface { if root.dragging: FlatSurface {
x: clamp(grid.x + root.drag-x + 14px, 8px, parent.width - self.width - 8px); x: clamp(grid.x + root.drag-x + 14px, 8px, parent.width - self.width - 8px);
y: clamp(grid.y + root.drag-y + 14px, 8px, footer.y - self.height - 12px); y: clamp(grid.y + root.drag-y + 14px, 8px, footer.y - self.height - 12px);
width: 224px; height: max(54px, drag-hint.y + drag-hint.height + 9px); z: 10; width: 224px; height: max(54px, drag-hint.y + drag-hint.height + 9px); z: 10;
background: #fffffff2; shadow-color: UiShadow.focus; border-radius: UiRadius.surface; background: white; outline-color: UiSurface.focus; border-radius: UiRadius.surface;
Rectangle { Rectangle {
x: 8px; y: 9px; width: 64px; height: 36px; x: 8px; y: 9px; width: 64px; height: 36px;
background: root.dragged-tile.online ? #24272d : DeviceCardStyle.offline-preview; background: root.dragged-tile.online ? #24272d : DeviceCardStyle.offline-preview;
@@ -865,20 +865,20 @@ export component GalleryView inherits Rectangle {
font-size: 10px; color: #527ca8; wrap: word-wrap; font-size: 10px; color: #527ca8; wrap: word-wrap;
} }
} }
if root.swap-save-failed: ShadowSurface { if root.swap-save-failed: FlatSurface {
x: (parent.width - self.width) / 2; y: footer.y - self.height - 14px; x: (parent.width - self.width) / 2; y: footer.y - self.height - 14px;
width: min(420px, parent.width - 32px); height: 36px; z: 10; width: min(420px, parent.width - 32px); height: 36px; z: 10;
background: #fff2f0; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; background: #fff2f0; outline-color: UiSurface.edge; border-radius: UiRadius.surface;
Text { x: 12px; y: 0px; width: parent.width - 24px; height: parent.height; font-size: 12px; color: #9d4339; vertical-alignment: center; Text { x: 12px; y: 0px; width: parent.width - 24px; height: parent.height; font-size: 12px; color: #9d4339; vertical-alignment: center;
text: GalleryStrings.tr("换位未保存,请重试", "Couldn't save positions. Please try again.", "Не удалось сохранить порядок. Повторите попытку."); overflow: elide; text: GalleryStrings.tr("换位未保存,请重试", "Couldn't save positions. Please try again.", "Не удалось сохранить порядок. Повторите попытку."); overflow: elide;
} }
} }
// Device details are rendered by MainWindow; resize hints stay local. // Device details are rendered by MainWindow; resize hints stay local.
if root.device-tooltip-visible && (root.device-tooltip-id == "resize-grid" || root.device-tooltip-id == "resize-focus"): ShadowSurface { if root.device-tooltip-visible && (root.device-tooltip-id == "resize-grid" || root.device-tooltip-id == "resize-focus"): FlatSurface {
x: clamp(root.device-tooltip-x, 8px, parent.width - self.width - 8px); x: clamp(root.device-tooltip-x, 8px, parent.width - self.width - 8px);
y: clamp(root.device-tooltip-y + self.height > footer.y ? root.device-tooltip-y - self.height - 8px : root.device-tooltip-y, 4px, parent.height - self.height - 4px); y: clamp(root.device-tooltip-y + self.height > footer.y ? root.device-tooltip-y - self.height - 8px : root.device-tooltip-y, 4px, parent.height - self.height - 4px);
width: min(320px, parent.width - 16px); height: device-tooltip-label.preferred-height + 16px; width: min(320px, parent.width - 16px); height: device-tooltip-label.preferred-height + 16px;
background: #22252bf2; shadow-color: UiShadow.edge; border-radius: UiRadius.control; background: #22252bf2; outline-color: UiSurface.edge; border-radius: UiRadius.control;
device-tooltip-label := Text { device-tooltip-label := Text {
x: 8px; y: 8px; width: parent.width - 16px; x: 8px; y: 8px; width: parent.width - 16px;
text: root.device-tooltip-text; color: white; font-size: 11px; wrap: word-wrap; text: root.device-tooltip-text; color: white; font-size: 11px; wrap: word-wrap;
@@ -887,9 +887,9 @@ export component GalleryView inherits Rectangle {
if root.adding: Rectangle { if root.adding: Rectangle {
background: #00000042; background: #00000042;
TouchArea {} TouchArea {}
add-dialog := ShadowSurface { add-dialog := FlatSurface {
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 400px; height: 306px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 400px; height: 306px;
background: white; border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: white; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
function submit() { function submit() {
if root.add-device(device-id.text, password.text, group.text, remember.checked) { if root.add-device(device-id.text, password.text, group.text, remember.checked) {
password.text = ""; password.text = "";
@@ -934,18 +934,18 @@ export component GalleryView inherits Rectangle {
if root.importing: Rectangle { if root.importing: Rectangle {
background: #00000042; background: #00000042;
TouchArea {} TouchArea {}
ShadowSurface { FlatSurface {
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 440px; height: 378px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 440px; height: 378px;
background: white; border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: white; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.tr("从近期连接添加", "Add from recent connections", "Добавить из недавних"); font-size: 16px; } Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.tr("从近期连接添加", "Add from recent connections", "Добавить из недавних"); font-size: 16px; }
Text { x: 18px; y: 44px; width: 404px; height: 18px; text: GalleryStrings.tr("加入桌面墙后,可通过批量选择连接", "Add to DeskHub, then select devices to connect.", "Добавьте в DeskHub, затем выберите устройства для подключения."); color: #62666d; font-size: 11px; } Text { x: 18px; y: 44px; width: 404px; height: 18px; text: GalleryStrings.tr("加入桌面墙后,可通过批量选择连接", "Add to DeskHub, then select devices to connect.", "Добавьте в DeskHub, затем выберите устройства для подключения."); color: #62666d; font-size: 11px; }
ImGuiCheckBox { x: 18px; y: 74px; enabled: root.recent-available > 0; checked: root.recent-devices.length > 0 && root.recent-selected == root.recent-available; } ImGuiCheckBox { x: 18px; y: 74px; enabled: root.recent-available > 0; checked: root.recent-devices.length > 0 && root.recent-selected == root.recent-available; }
Text { x: 45px; y: 74px; height: 21px; text: GalleryStrings.tr("全选", "Select all", "Выбрать все"); color: root.recent-available > 0 ? #24272d : #92969e; font-size: 12px; vertical-alignment: center; } Text { x: 45px; y: 74px; height: 21px; text: GalleryStrings.tr("全选", "Select all", "Выбрать все"); color: root.recent-available > 0 ? #24272d : #92969e; font-size: 12px; vertical-alignment: center; }
TouchArea { x: 18px; y: 74px; width: 144px; height: 21px; enabled: root.recent-available > 0; clicked => { root.action("recent-all", ""); } } TouchArea { x: 18px; y: 74px; width: 144px; height: 21px; enabled: root.recent-available > 0; clicked => { root.action("recent-all", ""); } }
Text { x: 224px; y: 74px; width: 198px; height: 21px; text: GalleryStrings.tr("待添加 ", "To add ", "К добавлению ") + root.recent-selected + " / " + root.recent-available; horizontal-alignment: right; vertical-alignment: center; font-size: 12px; color: #62666d; } Text { x: 224px; y: 74px; width: 198px; height: 21px; text: GalleryStrings.tr("待添加 ", "To add ", "К добавлению ") + root.recent-selected + " / " + root.recent-available; horizontal-alignment: right; vertical-alignment: center; font-size: 12px; color: #62666d; }
ShadowSurface { FlatSurface {
x: 18px; y: 102px; width: 404px; height: 160px; x: 18px; y: 102px; width: 404px; height: 160px;
shadow-color: UiShadow.edge; border-radius: UiRadius.surface; clip: true; outline-color: UiSurface.edge; border-radius: UiRadius.surface; clip: true;
recent-list := ScrollView { recent-list := ScrollView {
x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px; x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px;
viewport-width: self.width - 12px; viewport-width: self.width - 12px;
@@ -975,10 +975,10 @@ export component GalleryView inherits Rectangle {
border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface; border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface;
border-color: white; border-color: white;
} }
ShadowSurface { FlatSurface {
width: parent.width; height: parent.height; width: parent.width; height: parent.height;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
} }
} }
Text { x: 18px; y: 274px; width: 60px; height: 30px; text: GalleryStrings.tr("分组", "Group", "Группа"); font-size: 12px; vertical-alignment: center; } Text { x: 18px; y: 274px; width: 60px; height: 30px; text: GalleryStrings.tr("分组", "Group", "Группа"); font-size: 12px; vertical-alignment: center; }
@@ -990,10 +990,10 @@ export component GalleryView inherits Rectangle {
} }
if root.confirming: Rectangle { if root.confirming: Rectangle {
background: #00000042; TouchArea {} background: #00000042; TouchArea {}
ShadowSurface { FlatSurface {
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2;
width: 360px; height: confirmation-list.height + 118px; width: 360px; height: confirmation-list.height + 118px;
background: white; border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: white; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
Text { Text {
x: 20px; y: 18px; width: parent.width - 132px; height: 24px; x: 20px; y: 18px; width: parent.width - 132px; height: 24px;
text: root.action-code == "disconnect" ? GalleryStrings.tr("断开连接", "Disconnect devices", "Отключение") : root.action-name; text: root.action-code == "disconnect" ? GalleryStrings.tr("断开连接", "Disconnect devices", "Отключение") : root.action-name;
@@ -1004,10 +1004,10 @@ export component GalleryView inherits Rectangle {
background: #eff3f8; border-radius: UiRadius.surface; background: #eff3f8; border-radius: UiRadius.surface;
Text { text: GalleryStrings.device-count(root.selected-count); color: #626e7d; font-size: 11px; horizontal-alignment: center; vertical-alignment: center; } Text { text: GalleryStrings.device-count(root.selected-count); color: #626e7d; font-size: 11px; horizontal-alignment: center; vertical-alignment: center; }
} }
confirmation-list := ShadowSurface { confirmation-list := FlatSurface {
x: 20px; y: 58px; width: parent.width - 40px; x: 20px; y: 58px; width: parent.width - 40px;
height: clamp(root.selected-devices.length * 52px + 2px, 54px, 210px); height: clamp(root.selected-devices.length * 52px + 2px, 54px, 210px);
background: #fcfdfe; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; clip: true; background: #fcfdfe; outline-color: UiSurface.edge; border-radius: UiRadius.surface; clip: true;
confirmation-scroll := ScrollView { confirmation-scroll := ScrollView {
x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px; x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px;
viewport-width: self.width - 12px; viewport-width: self.width - 12px;
@@ -1023,7 +1023,7 @@ export component GalleryView inherits Rectangle {
Rectangle { x: 9px; y: 7px; width: 6px; height: 6px; border-radius: 3px; background: device.connected ? #27a762 : #a0a6b0; } Rectangle { x: 9px; y: 7px; width: 6px; height: 6px; border-radius: 3px; background: device.connected ? #27a762 : #a0a6b0; }
Text { x: 21px; width: parent.width - 27px; height: parent.height; text: device.connected ? GalleryStrings.tr("已连接", "Connected", "Подключено") : GalleryStrings.tr("未连接", "Not connected", "Нет соединения"); color: device.connected ? #348259 : #858d98; font-size: 10px; vertical-alignment: center; } Text { x: 21px; width: parent.width - 27px; height: parent.height; text: device.connected ? GalleryStrings.tr("已连接", "Connected", "Подключено") : GalleryStrings.tr("未连接", "Not connected", "Нет соединения"); color: device.connected ? #348259 : #858d98; font-size: 10px; vertical-alignment: center; }
} }
if index + 1 < root.selected-devices.length: ShadowSeparator { x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; shadow-color: UiShadow.separator; } if index + 1 < root.selected-devices.length: SurfaceSeparator { x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; outline-color: UiSurface.separator; }
} }
} }
} }
+15 -15
View File
@@ -1,14 +1,14 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ScrollView } from "std-widgets.slint"; import { ScrollView } from "std-widgets.slint";
import { FontAwesomeIcons, ControlStyle } from "common.slint"; import { FontAwesomeIcons, ControlStyle } from "common.slint";
// Shared by history and DeskHub; system choices precede the named groups. // Shared by history and DeskHub; system choices precede the named groups.
export component GroupFilter inherits ShadowSurface { export component GroupFilter inherits FlatSurface {
in property <[string]> model; in property <[string]> model;
in property <int> system-count: 2; in property <int> system-count: 2;
in property <bool> enabled: true; in property <bool> enabled: true;
// An embedded picker shares its enclosing field's shadow and focus glow. // An embedded picker shares its enclosing field's outline and focus state.
in property <bool> embedded: false; in property <bool> embedded: false;
in property <bool> show-indicator: true; in property <bool> show-indicator: true;
in property <length> popup-min-width: 0px; in property <length> popup-min-width: 0px;
@@ -16,9 +16,9 @@ export component GroupFilter inherits ShadowSurface {
in property <int> text-weight: 400; in property <int> text-weight: 400;
in property <color> text-color: #30343b; in property <color> text-color: #30343b;
in property <color> normal-background: ControlStyle.surface; in property <color> normal-background: ControlStyle.surface;
in property <color> normal-shadow-color: UiShadow.edge; in property <color> normal-outline-color: UiSurface.edge;
in property <color> hover-shadow-color: UiShadow.hover; in property <color> hover-outline-color: UiSurface.hover;
in property <color> focus-shadow-color: UiShadow.focus; in property <color> focus-outline-color: UiSurface.focus;
in-out property <int> current-index: 0; in-out property <int> current-index: 0;
out property <bool> has-focus: trigger-focus.has-focus || popup.is-open; out property <bool> has-focus: trigger-focus.has-focus || popup.is-open;
out property <bool> has-hover: root.enabled && trigger-touch.has-hover && !popup.is-open; out property <bool> has-hover: root.enabled && trigger-touch.has-hover && !popup.is-open;
@@ -40,12 +40,12 @@ export component GroupFilter inherits ShadowSurface {
: !root.enabled ? ControlStyle.disabled : !root.enabled ? ControlStyle.disabled
: trigger-touch.pressed || popup.is-open ? ControlStyle.pressed : trigger-touch.pressed || popup.is-open ? ControlStyle.pressed
: trigger-touch.has-hover ? ControlStyle.hover : root.normal-background; : trigger-touch.has-hover ? ControlStyle.hover : root.normal-background;
shadow-enabled: !root.embedded; outline-enabled: !root.embedded;
shadow-focused: popup.is-open || trigger-focus.has-focus; outline-focused: popup.is-open || trigger-focus.has-focus;
shadow-color: popup.is-open || trigger-focus.has-focus ? root.focus-shadow-color outline-color: popup.is-open || trigger-focus.has-focus ? root.focus-outline-color
: trigger-touch.has-hover ? root.hover-shadow-color : root.normal-shadow-color; : trigger-touch.has-hover ? root.hover-outline-color : root.normal-outline-color;
border-radius: UiRadius.control; border-radius: UiRadius.control;
animate background, shadow-color { duration: 100ms; easing: ease-out; } animate background, outline-color { duration: 100ms; easing: ease-out; }
forward-focus: trigger-focus; forward-focus: trigger-focus;
accessible-role: combobox; accessible-role: combobox;
accessible-enabled: root.enabled; accessible-enabled: root.enabled;
@@ -140,13 +140,13 @@ export component GroupFilter inherits ShadowSurface {
forward-focus: popup-focus; forward-focus: popup-focus;
popup-focus := FocusScope { popup-focus := FocusScope {
key-pressed(event) => { return root.handle-key(event); } key-pressed(event) => { return root.handle-key(event); }
ShadowSurface { FlatSurface {
y: (1 - root.popup-progress) * -4px; y: (1 - root.popup-progress) * -4px;
width: parent.width; height: parent.height; width: parent.width; height: parent.height;
opacity: root.popup-progress; opacity: root.popup-progress;
background: white; background: white;
clip: true; clip: true;
shadow-color: root.normal-shadow-color; border-radius: UiRadius.menu; outline-color: root.normal-outline-color; border-radius: UiRadius.menu;
ScrollView { ScrollView {
x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px; x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px;
viewport-width: self.width; viewport-width: self.width;
@@ -154,9 +154,9 @@ export component GroupFilter inherits ShadowSurface {
viewport-y <=> root.scroll-y; viewport-y <=> root.scroll-y;
Rectangle { Rectangle {
width: parent.width; height: root.list-height; width: parent.width; height: root.list-height;
if root.has-divider: ShadowSeparator { if root.has-divider: SurfaceSeparator {
x: 6px; y: root.system-count * 26px + 3px; x: 6px; y: root.system-count * 26px + 3px;
width: parent.width - 12px; height: 1px; shadow-color: UiShadow.separator; width: parent.width - 12px; height: 1px; outline-color: UiSurface.separator;
} }
for choice[index] in root.model: Rectangle { for choice[index] in root.model: Rectangle {
y: index * 26px + (root.has-divider && index >= root.system-count ? 6px : 0px); y: index * 26px + (root.has-divider && index >= root.system-count ? 6px : 0px);
@@ -1,9 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"> <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<defs> <circle cx="32" cy="32" r="31" fill="white"/>
<radialGradient id="edge">
<stop offset="95%" stop-color="white"/>
<stop offset="100%" stop-color="white" stop-opacity="0"/>
</radialGradient>
</defs>
<circle cx="32" cy="32" r="32" fill="url(#edge)"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 139 B

+101 -122
View File
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { LanStrings } from "lan_strings.slint"; import { LanStrings } from "lan_strings.slint";
import { AboutPanel, AboutPage, AboutComponent, AboutStrings, AboutNavigationRow, AboutNavigationGroup, AboutRowSeparator } from "about_panel.slint"; import { AboutPanel, AboutPage, AboutComponent, AboutStrings, AboutNavigationRow, AboutNavigationGroup, AboutRowSeparator } from "about_panel.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
@@ -210,7 +210,7 @@ component DialogPromptText inherits Rectangle {
} }
// Use the same plain-text field for connection and password changes. // Use the same plain-text field for connection and password changes.
component PasswordInput inherits ShadowSurface { component PasswordInput inherits FlatSurface {
in-out property <string> text; in-out property <string> text;
in property <int> max-length: 0; in property <int> max-length: 0;
in property <length> text-size: ImGuiFontStyle.base; in property <length> text-size: ImGuiFontStyle.base;
@@ -221,9 +221,8 @@ component PasswordInput inherits ShadowSurface {
width: 70px; width: 70px;
height: 23px; height: 23px;
background: editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface; background: editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface;
shadow-color: root.error ? UiShadow.error : editor.has-focus ? UiShadow.focus : UiShadow.edge; outline-color: root.error ? UiSurface.error : editor.has-focus ? UiSurface.focus : UiSurface.edge;
shadow-focused: editor.has-focus || root.error; outline-focused: editor.has-focus || root.error;
shadow-compact: true;
border-radius: UiRadius.control; border-radius: UiRadius.control;
clip: true; clip: true;
forward-focus: editor; forward-focus: editor;
@@ -943,10 +942,10 @@ export component MainWindow inherits Window {
clicked => { root.close-main-window(); } clicked => { root.close-main-window(); }
} }
} }
ShadowSeparator { SurfaceSeparator {
y: parent.height - 1px; y: parent.height - 1px;
height: 1px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
@@ -1030,10 +1029,10 @@ export component MainWindow inherits Window {
} }
close-touch := TouchArea { clicked => { root.close-main-window(); } } close-touch := TouchArea { clicked => { root.close-main-window(); } }
} }
ShadowSeparator { SurfaceSeparator {
y: parent.height - 1px; y: parent.height - 1px;
height: 1px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
@@ -1096,12 +1095,12 @@ export component MainWindow inherits Window {
// Slint owns the client area below the native macOS title bar. Draw // Slint owns the client area below the native macOS title bar. Draw
// the boundary here because AppKit's titlebar separator is not shown // the boundary here because AppKit's titlebar separator is not shown
// reliably when winit creates a window without an NSToolbar. // reliably when winit creates a window without an NSToolbar.
ShadowSeparator { SurfaceSeparator {
x: 0px; x: 0px;
y: 0px; y: 0px;
width: parent.width; width: parent.width;
height: 1px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
z: 7; z: 7;
} }
@@ -1168,13 +1167,13 @@ export component MainWindow inherits Window {
clicked => { root.gallery-action("release", ""); root.menu-open = !root.menu-open; } clicked => { root.gallery-action("release", ""); root.menu-open = !root.menu-open; }
} }
// Unread messages take precedence so the menu has only one indicator. // Unread messages take precedence so the menu has only one indicator.
if !root.gallery-mode && root.update-available && root.announcement-unread <= 0: ShadowSurface { if !root.gallery-mode && root.update-available && root.announcement-unread <= 0: FlatSurface {
x: menu-button.x + menu-button.width - self.width + 4px; x: menu-button.x + menu-button.width - self.width + 4px;
y: menu-button.y - 1px; y: menu-button.y - 1px;
width: 16px; width: 16px;
height: 16px; height: 16px;
border-radius: 8px; border-radius: 8px;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
background: WorkbenchStyle.accent; background: WorkbenchStyle.accent;
opacity: root.menu-open || mod(animation-tick(), 2s) < 1s ? 1 : 0; opacity: root.menu-open || mod(animation-tick(), 2s) < 1s ? 1 : 0;
z: 9; z: 9;
@@ -1208,13 +1207,13 @@ export component MainWindow inherits Window {
clicked => { root.gallery-action("release", ""); root.menu-open = !root.menu-open; } clicked => { root.gallery-action("release", ""); root.menu-open = !root.menu-open; }
} }
} }
if !root.gallery-mode && root.announcement-unread > 0: ShadowSurface { if !root.gallery-mode && root.announcement-unread > 0: FlatSurface {
x: menu-button.x + menu-button.width - self.width + 4px; x: menu-button.x + menu-button.width - self.width + 4px;
y: menu-button.y - 1px; y: menu-button.y - 1px;
width: max(16px, unread-badge-label.preferred-width + 8px); width: max(16px, unread-badge-label.preferred-width + 8px);
height: 16px; height: 16px;
border-radius: 8px; border-radius: 8px;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
background: #d92d20; background: #d92d20;
z: 9; z: 9;
accessible-role: button; accessible-role: button;
@@ -1286,7 +1285,7 @@ export component MainWindow inherits Window {
lan-port-editor.select-all(); lan-port-editor.select-all();
} }
} }
local-address-field := ShadowSurface { local-address-field := FlatSurface {
x: 16px; y: 90px; width: parent.width - 32px; height: 40px; x: 16px; y: 90px; width: parent.width - 32px; height: 40px;
// Size the endpoint as one line; reserve five port digits so // Size the endpoint as one line; reserve five port digits so
// editing never shifts the IP or changes the font size. // editing never shifts the IP or changes the font size.
@@ -1305,14 +1304,13 @@ export component MainWindow inherits Window {
// parent's stacking order above the following password row. // parent's stacking order above the following password row.
z: 1; z: 1;
background: ControlStyle.input-surface; border-radius: UiRadius.control; background: ControlStyle.input-surface; border-radius: UiRadius.control;
shadow-compact: true; outline-color: !root.lan-mode ? UiSurface.edge
shadow-color: !root.lan-mode ? UiShadow.edge : root.lan-endpoint-error != "" ? UiSurface.error
: root.lan-endpoint-error != "" ? UiShadow.error : lan-ip-picker.has-focus || root.lan-port-editing ? UiSurface.focus
: lan-ip-picker.has-focus || root.lan-port-editing ? UiShadow.focus : UiSurface.edge;
: UiShadow.edge; animate outline-color { duration: 120ms; }
animate shadow-color { duration: 120ms; }
if !root.lan-mode: Text { x: 12px; width: parent.width - 64px; height: parent.height; text: root.local-id; font-size: 24px; font-weight: 600; letter-spacing: 1px; overflow: elide; color: WorkbenchStyle.text; vertical-alignment: center; } if !root.lan-mode: Text { x: 12px; width: parent.width - 64px; height: parent.height; text: root.local-id; font-size: 24px; font-weight: 600; letter-spacing: 1px; overflow: elide; color: WorkbenchStyle.text; vertical-alignment: center; }
ShadowSeparator { x: parent.width - 48px; y: 10px; width: 1px; height: parent.height - 20px; vertical: true; shadow-color: UiShadow.separator; } SurfaceSeparator { x: parent.width - 48px; y: 10px; width: 1px; height: parent.height - 20px; vertical: true; outline-color: UiSurface.separator; }
lan-ip-picker := GroupFilter { lan-ip-picker := GroupFilter {
visible: root.lan-mode; visible: root.lan-mode;
x: 4px; y: 2px; x: 4px; y: 2px;
@@ -1325,9 +1323,9 @@ export component MainWindow inherits Window {
model: root.lan-interfaces; system-count: 0; model: root.lan-interfaces; system-count: 0;
current-index <=> root.lan-interface-draft-index; current-index <=> root.lan-interface-draft-index;
enabled: root.lan-mode && !root.lan-password-pending && root.lan-interfaces.length > 0; enabled: root.lan-mode && !root.lan-password-pending && root.lan-interfaces.length > 0;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
hover-shadow-color: UiShadow.hover; hover-outline-color: UiSurface.hover;
focus-shadow-color: WorkbenchStyle.accent; focus-outline-color: WorkbenchStyle.accent;
accessible-label: GalleryStrings.tr("切换本机 IP", "Switch local IP", "Сменить локальный IP"); accessible-label: GalleryStrings.tr("切换本机 IP", "Switch local IP", "Сменить локальный IP");
selected(index) => { selected(index) => {
root.copy-feedback = CopyFeedback.idle; root.copy-feedback = CopyFeedback.idle;
@@ -1352,7 +1350,7 @@ export component MainWindow inherits Window {
font-weight: 600; color: WorkbenchStyle.text; font-weight: 600; color: WorkbenchStyle.text;
horizontal-alignment: center; vertical-alignment: center; horizontal-alignment: center; vertical-alignment: center;
} }
lan-port-field := ShadowSurface { lan-port-field := FlatSurface {
visible: root.lan-mode; visible: root.lan-mode;
x: lan-ip-picker.x + lan-ip-picker.width + 2px; y: 2px; x: lan-ip-picker.x + lan-ip-picker.width + 2px; y: 2px;
width: lan-port-metrics.preferred-width * local-address-field.lan-font-scale + 4px; width: lan-port-metrics.preferred-width * local-address-field.lan-font-scale + 4px;
@@ -1360,8 +1358,8 @@ export component MainWindow inherits Window {
background: lan-port-editor.has-focus ? ControlStyle.input-focused background: lan-port-editor.has-focus ? ControlStyle.input-focused
: lan-port-hover.has-hover ? ControlStyle.hover : transparent; : lan-port-hover.has-hover ? ControlStyle.hover : transparent;
border-radius: UiRadius.control; border-radius: UiRadius.control;
shadow-color: lan-port-editor.has-focus ? UiShadow.focus : transparent; outline-color: lan-port-editor.has-focus ? UiSurface.focus : transparent;
animate background, shadow-color { duration: 120ms; } animate background, outline-color { duration: 120ms; }
lan-port-hover := TouchArea { lan-port-hover := TouchArea {
enabled: root.lan-mode && !root.lan-password-pending; enabled: root.lan-mode && !root.lan-password-pending;
mouse-cursor: text; mouse-cursor: text;
@@ -1444,13 +1442,12 @@ export component MainWindow inherits Window {
} }
} }
Text { x: 16px; y: 138px; height: 18px; text: UiStrings.password; font-size: 12px; color: WorkbenchStyle.muted; } Text { x: 16px; y: 138px; height: 18px; text: UiStrings.password; font-size: 12px; color: WorkbenchStyle.muted; }
ShadowSurface { FlatSurface {
x: 16px; y: 158px; width: parent.width - 32px; height: 36px; x: 16px; y: 158px; width: parent.width - 32px; height: 36px;
background: ControlStyle.input-surface; border-radius: UiRadius.control; background: ControlStyle.input-surface; border-radius: UiRadius.control;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
shadow-compact: true;
Text { x: 12px; width: parent.width - 132px; height: parent.height; text: root.active-password == "" ? "—" : root.active-password-visible ? root.active-password : "••••••"; font-size: 20px; font-weight: 500; letter-spacing: 2px; color: WorkbenchStyle.text; vertical-alignment: center; } Text { x: 12px; width: parent.width - 132px; height: parent.height; text: root.active-password == "" ? "—" : root.active-password-visible ? root.active-password : "••••••"; font-size: 20px; font-weight: 500; letter-spacing: 2px; color: WorkbenchStyle.text; vertical-alignment: center; }
ShadowSeparator { x: parent.width - 118px; y: 8px; width: 1px; height: parent.height - 16px; vertical: true; shadow-color: UiShadow.separator; } SurfaceSeparator { x: parent.width - 118px; y: 8px; width: 1px; height: parent.height - 16px; vertical: true; outline-color: UiSurface.separator; }
WorkbenchButton { WorkbenchButton {
x: parent.width - 112px; y: 0px; width: 36px; quiet: true; x: parent.width - 112px; y: 0px; width: 36px; quiet: true;
icon: root.active-password-visible ? FontAwesomeIcons.eye : FontAwesomeIcons.eye-slash; icon: root.active-password-visible ? FontAwesomeIcons.eye : FontAwesomeIcons.eye-slash;
@@ -1477,14 +1474,13 @@ export component MainWindow inherits Window {
Text { x: 16px; y: 14px; width: parent.width - 32px; height: 24px; overflow: elide; text: UiStrings.remote-desktop; font-size: 16px; font-weight: 600; color: WorkbenchStyle.text; } Text { x: 16px; y: 14px; width: parent.width - 32px; height: 24px; overflow: elide; text: UiStrings.remote-desktop; font-size: 16px; font-weight: 600; color: WorkbenchStyle.text; }
Text { x: 16px; y: 38px; width: parent.width - 32px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("输入 IP:端口,通过局域网连接", "Enter IP:port to connect over LAN", "Введите IP:порт для подключения по LAN") : GalleryStrings.tr("输入设备 ID,连接远程桌面", "Enter a device ID to start a remote session", "Введите ID для удалённого подключения"); font-size: 11px; color: WorkbenchStyle.muted; overflow: elide; } Text { x: 16px; y: 38px; width: parent.width - 32px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("输入 IP:端口,通过局域网连接", "Enter IP:port to connect over LAN", "Введите IP:порт для подключения по LAN") : GalleryStrings.tr("输入设备 ID,连接远程桌面", "Enter a device ID to start a remote session", "Введите ID для удалённого подключения"); font-size: 11px; color: WorkbenchStyle.muted; overflow: elide; }
Text { x: 16px; y: 68px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("远端 IP / 端口", "Remote IP / Port", "Удалённый IP / Порт") : UiStrings.remote-id; font-size: 12px; color: WorkbenchStyle.muted; } Text { x: 16px; y: 68px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("远端 IP / 端口", "Remote IP / Port", "Удалённый IP / Порт") : UiStrings.remote-id; font-size: 12px; color: WorkbenchStyle.muted; }
ShadowSurface { FlatSurface {
x: 16px; y: 90px; width: parent.width - 32px; height: 40px; x: 16px; y: 90px; width: parent.width - 32px; height: 40px;
background: remote-id-editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface; background: remote-id-editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface;
shadow-compact: true;
border-radius: UiRadius.control; border-radius: UiRadius.control;
shadow-color: root.connection-input-error != "" ? UiShadow.error : remote-id-editor.has-focus ? UiShadow.focus : UiShadow.edge; outline-color: root.connection-input-error != "" ? UiSurface.error : remote-id-editor.has-focus ? UiSurface.focus : UiSurface.edge;
shadow-focused: remote-id-editor.has-focus || root.connection-input-error != ""; outline-focused: remote-id-editor.has-focus || root.connection-input-error != "";
animate shadow-color { duration: 120ms; } animate outline-color { duration: 120ms; }
if root.remote-id-input == "": Text { if root.remote-id-input == "": Text {
x: 12px; width: parent.width - 24px; height: parent.height; x: 12px; width: parent.width - 24px; height: parent.height;
text: root.lan-mode ? "192.168.1.20:52888" : GalleryStrings.tr("请输入 9 位设备 ID", "Enter a 9-digit device ID", "Введите ID из 9 цифр"); text: root.lan-mode ? "192.168.1.20:52888" : GalleryStrings.tr("请输入 9 位设备 ID", "Enter a 9-digit device ID", "Введите ID из 9 цифр");
@@ -1538,24 +1534,23 @@ export component MainWindow inherits Window {
x: parent.width - self.width - 20px; y: 0px; x: parent.width - self.width - 20px; y: 0px;
width: 155px; height: 32px; width: 155px; height: 32px;
normal-background: white; normal-background: white;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
hover-shadow-color: UiShadow.hover; hover-outline-color: UiSurface.hover;
focus-shadow-color: WorkbenchStyle.accent; focus-outline-color: WorkbenchStyle.accent;
model: root.recent-groups; model: root.recent-groups;
current-index <=> root.recent-group-index; current-index <=> root.recent-group-index;
selected(index) => { root.recent-group-selected(index); } selected(index) => { root.recent-group-selected(index); }
} }
recent-list := ShadowSurface { recent-list := FlatSurface {
x: 20px; x: 20px;
y: 36px; y: 36px;
width: parent.width - 40px; width: parent.width - 40px;
height: 142px; height: 142px;
// A continuous inset surface groups the cards into one // A light flat surface groups the cards and their scrollbar.
// horizontal scrolling lane, including its scrollbar. background: root.recent-connections.length > 0 ? #f5f7fb : transparent;
background: root.recent-connections.length > 0 ? #e6edf6 : transparent; outline-enabled: root.recent-connections.length > 0;
shadow-enabled: root.recent-connections.length > 0; outline-color: UiSurface.edge;
shadow-color: UiShadow.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
clip: true; clip: true;
@@ -1627,11 +1622,10 @@ export component MainWindow inherits Window {
width: parent.width; width: parent.width;
height: root.recent-preview-height; height: root.recent-preview-height;
background: !connection-card.available ? DeviceCardStyle.offline-preview : background: !connection-card.available ? DeviceCardStyle.offline-preview :
connection.platform == "windows" ? @linear-gradient(135deg, #1f61b3, #337ac4) : connection.platform == "windows" ? #296ebc :
connection.platform == "macos" ? @linear-gradient(135deg, #6e4aad, #8c63c4) : connection.platform == "macos" ? #7d57b9 :
connection.platform == "linux" ? @linear-gradient(135deg, #a14f14, #ba631f) : connection.platform == "linux" ? #ad591a :
connection.platform == "ios" ? @linear-gradient(135deg, #146e66, #1f8278) : connection.platform == "ios" ? #19786f : #667282;
@linear-gradient(135deg, #596675, #737d8c);
if connection.has-thumbnail: Image { if connection.has-thumbnail: Image {
width: parent.width; width: parent.width;
height: parent.height; height: parent.height;
@@ -1678,7 +1672,7 @@ export component MainWindow inherits Window {
width: parent.width; width: parent.width;
height: 44px; height: 44px;
background: white; background: white;
ShadowSeparator { x: 0px; y: 0px; width: parent.width; height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { x: 0px; y: 0px; width: parent.width; height: 1px; outline-color: UiSurface.separator; }
Text { Text {
x: 12px; y: 5px; height: 19px; x: 12px; y: 5px; height: 19px;
width: parent.width - 56px; width: parent.width - 56px;
@@ -1812,11 +1806,11 @@ export component MainWindow inherits Window {
} }
} }
} }
ShadowSurface { FlatSurface {
width: parent.width; height: 124px; width: parent.width; height: 124px;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: card-touch.has-hover || card-menu.menu-open ? UiShadow.hover : UiShadow.edge; outline-color: card-touch.has-hover || card-menu.menu-open ? UiSurface.hover : UiSurface.edge;
animate shadow-color { duration: 120ms; } animate outline-color { duration: 120ms; }
} }
} }
} }
@@ -1873,10 +1867,10 @@ export component MainWindow inherits Window {
y: parent.height - 28px; y: parent.height - 28px;
height: 28px; height: 28px;
background: white; background: white;
ShadowSeparator { SurfaceSeparator {
y: 0px; y: 0px;
height: 1px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
Rectangle { Rectangle {
x: 20px; y: 11px; width: 6px; height: 6px; border-radius: 3px; x: 20px; y: 11px; width: 6px; height: 6px; border-radius: 3px;
@@ -1927,7 +1921,7 @@ export component MainWindow inherits Window {
// Shared device details sit outside the clipped recent list and wall rail, // Shared device details sit outside the clipped recent list and wall rail,
// below the modal layer (z >= 39). // below the modal layer (z >= 39).
if (root.gallery-mode ? gallery.hovered-device.id != "" : root.recent-tooltip-visible) if (root.gallery-mode ? gallery.hovered-device.id != "" : root.recent-tooltip-visible)
&& !root.connection-dialog-open && !root.lan-open: device-tip := ShadowSurface { && !root.connection-dialog-open && !root.lan-open: device-tip := FlatSurface {
private property <string> device-name: root.gallery-mode ? gallery.hovered-device.name : root.recent-tooltip-name; private property <string> device-name: root.gallery-mode ? gallery.hovered-device.name : root.recent-tooltip-name;
private property <string> host-name: root.gallery-mode ? gallery.hovered-device.host-name : root.recent-tooltip-host; private property <string> host-name: root.gallery-mode ? gallery.hovered-device.host-name : root.recent-tooltip-host;
private property <string> device-id: root.gallery-mode ? gallery.hovered-device.id : root.recent-tooltip-id; private property <string> device-id: root.gallery-mode ? gallery.hovered-device.id : root.recent-tooltip-id;
@@ -1954,11 +1948,8 @@ export component MainWindow inherits Window {
self.label-width + max(tooltip-id.preferred-width, tooltip-groups.preferred-width) + 32px)); self.label-width + max(tooltip-id.preferred-width, tooltip-groups.preferred-width) + 32px));
height: tooltip-groups.y + tooltip-groups.height + 10px; height: tooltip-groups.y + tooltip-groups.height + 10px;
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.menu; border-radius: UiRadius.menu;
drop-shadow-color: #14294424;
drop-shadow-blur: 14px;
drop-shadow-offset-y: 4px;
z: 20; z: 20;
tooltip-name-label := Text { tooltip-name-label := Text {
@@ -1997,11 +1988,11 @@ export component MainWindow inherits Window {
color: WorkbenchStyle.muted; font-size: 10px; color: WorkbenchStyle.muted; font-size: 10px;
wrap: word-wrap; wrap: word-wrap;
} }
tooltip-divider := ShadowSeparator { tooltip-divider := SurfaceSeparator {
x: 12px; y: tooltip-name.y + tooltip-name.height x: 12px; y: tooltip-name.y + tooltip-name.height
+ (device-tip.has-host-name ? tooltip-host.height + 3px : 0px) + 8px; + (device-tip.has-host-name ? tooltip-host.height + 3px : 0px) + 8px;
width: parent.width - 24px; height: 1px; width: parent.width - 24px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
tooltip-id-label := Text { tooltip-id-label := Text {
x: 12px; y: tooltip-divider.y + 7px; x: 12px; y: tooltip-divider.y + 7px;
@@ -2048,7 +2039,7 @@ export component MainWindow inherits Window {
} }
// Expand downward from the application toolbar. // Expand downward from the application toolbar.
if !root.gallery-mode && root.menu-open: ShadowSurface { if !root.gallery-mode && root.menu-open: FlatSurface {
x: menu-button.x + menu-button.width - self.width; x: menu-button.x + menu-button.width - self.width;
y: menu-button.y + menu-button.height + 6px; y: menu-button.y + menu-button.height + 6px;
width: max(128px, max(menu-settings-label.preferred-width, width: max(128px, max(menu-settings-label.preferred-width,
@@ -2056,7 +2047,7 @@ export component MainWindow inherits Window {
menu-about-label.preferred-width + (root.update-available ? menu-update-badge.width + 8px : 0px))) + 28px); menu-about-label.preferred-width + (root.update-available ? menu-update-badge.width + 8px : 0px))) + 28px);
height: application-menu-content.preferred-height; height: application-menu-content.preferred-height;
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.menu; border-radius: UiRadius.menu;
z: 10; z: 10;
TouchArea { } TouchArea { }
@@ -2089,9 +2080,9 @@ export component MainWindow inherits Window {
} }
Rectangle { Rectangle {
height: 7px; height: 7px;
ShadowSeparator { SurfaceSeparator {
x: 8px; y: 3px; width: parent.width - 16px; height: 1px; x: 8px; y: 3px; width: parent.width - 16px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
menu-messages := Rectangle { menu-messages := Rectangle {
@@ -2138,9 +2129,9 @@ export component MainWindow inherits Window {
} }
Rectangle { Rectangle {
height: 7px; height: 7px;
ShadowSeparator { SurfaceSeparator {
x: 8px; y: 3px; width: parent.width - 16px; height: 1px; x: 8px; y: 3px; width: parent.width - 16px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
menu-about := Rectangle { menu-about := Rectangle {
@@ -2311,7 +2302,7 @@ export component MainWindow inherits Window {
DialogPromptText { prompt: UiStrings.edit-alias; } DialogPromptText { prompt: UiStrings.edit-alias; }
RoundedLineEdit { RoundedLineEdit {
height: 28px; height: 28px;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
text <=> root.alias-input; text <=> root.alias-input;
accepted => { accepted => {
root.recent-edit-alias(root.alias-remote-id, root.alias-input); root.recent-edit-alias(root.alias-remote-id, root.alias-input);
@@ -2386,7 +2377,7 @@ export component MainWindow inherits Window {
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
width: min(420px, parent.width - 40px); width: min(420px, parent.width - 40px);
height: disconnect-content.preferred-height; height: disconnect-content.preferred-height;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
TouchArea { scroll-event(event) => { accept } } TouchArea { scroll-event(event) => { accept } }
disconnect-content := VerticalLayout { disconnect-content := VerticalLayout {
padding: 24px; padding: 24px;
@@ -2535,8 +2526,7 @@ export component MainWindow inherits Window {
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
width: min(400px, parent.width - 40px); width: min(400px, parent.width - 40px);
height: permission-content.preferred-height; height: permission-content.preferred-height;
background: #f8fafc; shadow-color: UiShadow.edge; background: #f8fafc; outline-color: UiSurface.edge;
drop-shadow-color: #14294424; drop-shadow-blur: 24px; drop-shadow-offset-y: 8px;
TouchArea { clicked => { focus-sink.focus(); } } TouchArea { clicked => { focus-sink.focus(); } }
permission-content := VerticalLayout { permission-content := VerticalLayout {
padding: 24px; spacing: 20px; padding: 24px; spacing: 20px;
@@ -2607,7 +2597,7 @@ export component MainWindow inherits Window {
} }
VerticalLayout { VerticalLayout {
spacing: 12px; spacing: 12px;
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
Text { Text {
text: GalleryStrings.tr("请在系统设置中允许 CrossDesk 访问。如系统提示重新打开应用,请按提示操作。", text: GalleryStrings.tr("请在系统设置中允许 CrossDesk 访问。如系统提示重新打开应用,请按提示操作。",
"Allow CrossDesk access in System Settings. Reopen the app if macOS asks you to.", "Allow CrossDesk access in System Settings. Reopen the app if macOS asks you to.",
@@ -2628,13 +2618,12 @@ export component MainWindow inherits Window {
// Center every content section; the scrollbar sits in the right margin. // Center every content section; the scrollbar sits in the right margin.
private property <length> content-inset: 26px; private property <length> content-inset: 26px;
private property <length> scrollbar-gutter: 22px; private property <length> scrollbar-gutter: 22px;
private property <length> shadow-gutter: 6px;
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
width: min(400px, parent.width - 40px); width: min(400px, parent.width - 40px);
height: min(446px, parent.height - 16px); height: min(446px, parent.height - 16px);
background: #f8fafc; border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: #f8fafc; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
drop-shadow-color: #14294424; drop-shadow-blur: 24px; drop-shadow-offset-y: 8px;
TouchArea { clicked => { focus-sink.focus(); } } TouchArea { clicked => { focus-sink.focus(); } }
Text { Text {
x: settings-dialog.content-inset; y: 18px; x: settings-dialog.content-inset; y: 18px;
@@ -2642,12 +2631,12 @@ export component MainWindow inherits Window {
text: UiStrings.settings; font-size: 20px; font-weight: 600; color: #22324a; text: UiStrings.settings; font-size: 20px; font-weight: 600; color: #22324a;
} }
// Match the compact connection-mode switch, centered in the tab row. // Match the compact connection-mode switch, centered in the tab row.
ShadowSurface { FlatSurface {
x: settings-dialog.content-inset; y: 62px; x: settings-dialog.content-inset; y: 62px;
width: parent.width - 2 * settings-dialog.content-inset; height: 28px; width: parent.width - 2 * settings-dialog.content-inset; height: 28px;
background: WorkbenchStyle.field-surface; border-radius: UiRadius.control; background: WorkbenchStyle.field-surface; border-radius: UiRadius.control;
border-width: 1px; border-color: WorkbenchStyle.accent; border-width: 1px; border-color: WorkbenchStyle.accent;
shadow-compact: true; shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
Rectangle { Rectangle {
x: root.settings-tab == 1 ? parent.width / 2 + 1px : 2px; x: root.settings-tab == 1 ? parent.width / 2 + 1px : 2px;
y: 2px; width: (parent.width - 6px) / 2; height: parent.height - 4px; y: 2px; width: (parent.width - 6px) / 2; height: parent.height - 4px;
@@ -2672,9 +2661,9 @@ export component MainWindow inherits Window {
} }
} }
if root.settings-tab == 0: settings-scroll := ScrollView { if root.settings-tab == 0: settings-scroll := ScrollView {
x: settings-dialog.content-inset - settings-dialog.shadow-gutter; y: 108px; x: settings-dialog.content-inset; y: 108px;
width: parent.width - 2 * settings-dialog.content-inset width: parent.width - 2 * settings-dialog.content-inset
+ settings-dialog.scrollbar-gutter + settings-dialog.shadow-gutter; + settings-dialog.scrollbar-gutter;
height: parent.height - 174px - (root.settings-save-error == "" ? 0px : 28px); height: parent.height - 174px - (root.settings-save-error == "" ? 0px : 28px);
viewport-width: self.visible-width; viewport-width: self.visible-width;
viewport-height: settings-content.preferred-height; viewport-height: settings-content.preferred-height;
@@ -2688,9 +2677,7 @@ export component MainWindow inherits Window {
width: settings-scroll.viewport-width; width: settings-scroll.viewport-width;
height: settings-scroll.viewport-height; height: settings-scroll.viewport-height;
settings-content := VerticalLayout { settings-content := VerticalLayout {
// Keep the six outer shadow layers inside the scroll clip padding: 0px;
// without moving or narrowing the settings cards.
padding: 0px; padding-left: settings-dialog.shadow-gutter;
padding-right: settings-dialog.scrollbar-gutter; padding-bottom: 4px; padding-right: settings-dialog.scrollbar-gutter; padding-bottom: 4px;
spacing: 14px; spacing: 14px;
SettingsGroup { SettingsGroup {
@@ -2785,9 +2772,9 @@ export component MainWindow inherits Window {
width: parent.width - 2 * settings-dialog.content-inset; height: 24px; width: parent.width - 2 * settings-dialog.content-inset; height: 24px;
text: root.settings-save-error; font-size: 11px; color: #c53e3e; wrap: word-wrap; text: root.settings-save-error; font-size: 11px; color: #c53e3e; wrap: word-wrap;
} }
ShadowSeparator { SurfaceSeparator {
x: settings-dialog.content-inset; y: parent.height - 64px; x: settings-dialog.content-inset; y: parent.height - 64px;
width: parent.width - 2 * settings-dialog.content-inset; height: 1px; shadow-color: UiShadow.separator; width: parent.width - 2 * settings-dialog.content-inset; height: 1px; outline-color: UiSurface.separator;
} }
HorizontalLayout { HorizontalLayout {
x: settings-dialog.content-inset; y: parent.height - 50px; x: settings-dialog.content-inset; y: parent.height - 50px;
@@ -2813,8 +2800,7 @@ export component MainWindow inherits Window {
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2;
width: min(460px, parent.width - 32px); width: min(460px, parent.width - 32px);
height: min(380px, parent.height - 24px); height: min(380px, parent.height - 24px);
background: #f8fafc; shadow-color: UiShadow.edge; background: #f8fafc; outline-color: UiSurface.edge;
drop-shadow-color: #14294424; drop-shadow-blur: 18px; drop-shadow-offset-y: 6px;
TouchArea { clicked => { focus-sink.focus(); } } TouchArea { clicked => { focus-sink.focus(); } }
Text { Text {
x: 24px; y: 18px; width: parent.width - 48px; height: 26px; x: 24px; y: 18px; width: parent.width - 48px; height: 26px;
@@ -2867,8 +2853,7 @@ export component MainWindow inherits Window {
x: (parent.width - self.width) / 2; x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2; y: (parent.height - self.height) / 2;
width: 360px; height: root.self-host-settings-error == "" ? 266px : 322px; width: 360px; height: root.self-host-settings-error == "" ? 266px : 322px;
border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: #f8fafc; border-radius: UiRadius.surface; outline-color: UiSurface.edge; background: #f8fafc;
drop-shadow-color: #14294424; drop-shadow-blur: 18px; drop-shadow-offset-y: 6px;
TouchArea { clicked => { focus-sink.focus(); } } TouchArea { clicked => { focus-sink.focus(); } }
Text { Text {
x: 18px; y: 18px; width: parent.width - 36px; height: 24px; x: 18px; y: 18px; width: parent.width - 36px; height: 24px;
@@ -2878,21 +2863,21 @@ export component MainWindow inherits Window {
RoundedLineEdit { RoundedLineEdit {
x: 18px; y: 80px; width: parent.width - 36px; height: 30px; x: 18px; y: 80px; width: parent.width - 36px; height: 30px;
surrounding-background: #f8fafc; surrounding-background: #f8fafc;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
text <=> root.server-host; text <=> root.server-host;
} }
Text { x: 18px; y: 120px; width: parent.width - 36px; height: 20px; text: UiStrings.server-port; color: #34445a; font-size: 12px; overflow: elide; vertical-alignment: center; } Text { x: 18px; y: 120px; width: parent.width - 36px; height: 20px; text: UiStrings.server-port; color: #34445a; font-size: 12px; overflow: elide; vertical-alignment: center; }
RoundedLineEdit { RoundedLineEdit {
x: 18px; y: 144px; width: parent.width - 36px; height: 30px; x: 18px; y: 144px; width: parent.width - 36px; height: 30px;
surrounding-background: #f8fafc; surrounding-background: #f8fafc;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
text <=> root.server-port; input-type: InputType.number; text <=> root.server-port; input-type: InputType.number;
} }
if root.self-host-settings-error != "": Text { if root.self-host-settings-error != "": Text {
x: 18px; y: 186px; width: parent.width - 36px; height: 48px; x: 18px; y: 186px; width: parent.width - 36px; height: 48px;
text: root.self-host-settings-error; color: #c53e3e; font-size: 11px; wrap: word-wrap; text: root.self-host-settings-error; color: #c53e3e; font-size: 11px; wrap: word-wrap;
} }
ShadowSeparator { x: 18px; y: parent.height - 66px; width: parent.width - 36px; height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { x: 18px; y: parent.height - 66px; width: parent.width - 36px; height: 1px; outline-color: UiSurface.separator; }
SettingsButton { x: parent.width - 114px; y: parent.height - 50px; width: 92px; primary: true; text: UiStrings.ok; clicked => { if root.save-self-hosted-settings() { root.self-host-settings-open = false; } } } SettingsButton { x: parent.width - 114px; y: parent.height - 50px; width: 92px; primary: true; text: UiStrings.ok; clicked => { if root.save-self-hosted-settings() { root.self-host-settings-open = false; } } }
SettingsButton { x: parent.width - 204px; y: parent.height - 50px; width: 80px; text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } } SettingsButton { x: parent.width - 204px; y: parent.height - 50px; width: 80px; text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } }
} }
@@ -2930,7 +2915,7 @@ export component MainWindow inherits Window {
color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap; color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap;
font-family: "CrossDesk Message Spacing"; font-family: "CrossDesk Message Spacing";
} }
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
HorizontalLayout { HorizontalLayout {
alignment: end; alignment: end;
SettingsButton { SettingsButton {
@@ -2998,11 +2983,11 @@ export component MainWindow inherits Window {
width: about-scroll.width; height: self.preferred-height; width: about-scroll.width; height: self.preferred-height;
// Keep the expanding scrollbar outside the content. // Keep the expanding scrollbar outside the content.
padding: 0px; padding-right: 22px; spacing: 16px; padding: 0px; padding-right: 22px; spacing: 16px;
ShadowSurface { FlatSurface {
height: max(80px, product-info.preferred-height + 32px); height: max(80px, product-info.preferred-height + 32px);
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
Image { Image {
x: 16px; y: (parent.height - self.height) / 2; x: 16px; y: (parent.height - self.height) / 2;
width: 56px; height: 56px; width: 56px; height: 56px;
@@ -3019,11 +3004,11 @@ export component MainWindow inherits Window {
} }
} }
} }
if root.update-available: ShadowSurface { if root.update-available: FlatSurface {
height: update-summary.preferred-height + 24px; height: update-summary.preferred-height + 24px;
background: WorkbenchStyle.field-surface; background: WorkbenchStyle.field-surface;
border-radius: UiRadius.control; border-radius: UiRadius.control;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
update-summary := VerticalLayout { update-summary := VerticalLayout {
x: 12px; y: 12px; width: parent.width - 24px; x: 12px; y: 12px; width: parent.width - 24px;
height: self.preferred-height; height: self.preferred-height;
@@ -3093,7 +3078,7 @@ export component MainWindow inherits Window {
x: 24px; y: parent.height - self.height - 24px; x: 24px; y: parent.height - self.height - 24px;
width: parent.width - 48px; height: self.preferred-height; width: parent.width - 48px; height: self.preferred-height;
spacing: 16px; spacing: 16px;
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
if root.update-status-text != "": Text { if root.update-status-text != "": Text {
text: root.update-status-text; text: root.update-status-text;
color: root.update-check-status == UpdateCheckStatus.failed ? #b3261e color: root.update-check-status == UpdateCheckStatus.failed ? #b3261e
@@ -3137,10 +3122,7 @@ export component MainWindow inherits Window {
// Use the same content area for sizing and centering the dialog. // Use the same content area for sizing and centering the dialog.
height: min(488px, parent.height - 32px); height: min(488px, parent.height - 32px);
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
drop-shadow-color: #07142b24;
drop-shadow-blur: 24px;
drop-shadow-offset-y: 8px;
VerticalLayout { VerticalLayout {
padding: 24px; padding: 24px;
@@ -3193,9 +3175,9 @@ export component MainWindow inherits Window {
font-weight: 600; font-weight: 600;
vertical-alignment: center; vertical-alignment: center;
} }
ShadowSurface { FlatSurface {
background: WorkbenchStyle.field-surface; background: WorkbenchStyle.field-surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.control; border-radius: UiRadius.control;
// Inset the viewport so text and the scrollbar stay clear // Inset the viewport so text and the scrollbar stay clear
// of the rounded frame, including at either scroll limit. // of the rounded frame, including at either scroll limit.
@@ -3242,7 +3224,7 @@ export component MainWindow inherits Window {
VerticalLayout { VerticalLayout {
vertical-stretch: 0; vertical-stretch: 0;
spacing: 16px; spacing: 16px;
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
if root.update-install-status != "": Text { if root.update-install-status != "": Text {
text: root.update-install-status; text: root.update-install-status;
font-size: 12px; font-size: 12px;
@@ -3315,7 +3297,7 @@ export component MainWindow inherits Window {
width: min(440px, parent.width - 40px); width: min(440px, parent.width - 40px);
height: update-confirm-content.preferred-height; height: update-confirm-content.preferred-height;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
TouchArea {} TouchArea {}
update-confirm-content := VerticalLayout { update-confirm-content := VerticalLayout {
padding: 24px; padding: 24px;
@@ -3369,10 +3351,7 @@ export component MainWindow inherits Window {
width: 512px; width: 512px;
height: wayland-notice-content.preferred-height; height: wayland-notice-content.preferred-height;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
drop-shadow-color: #07142b30;
drop-shadow-blur: 28px;
drop-shadow-offset-y: 10px;
wayland-notice-content := VerticalLayout { wayland-notice-content := VerticalLayout {
padding: 24px; spacing: 18px; padding: 24px; spacing: 18px;
@@ -3393,9 +3372,9 @@ export component MainWindow inherits Window {
} }
} }
ShadowSurface { FlatSurface {
background: #fff8eb; background: #fff8eb;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
VerticalLayout { VerticalLayout {
@@ -3416,9 +3395,9 @@ export component MainWindow inherits Window {
} }
} }
ShadowSurface { FlatSurface {
background: #f3f7fd; background: #f3f7fd;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
VerticalLayout { VerticalLayout {
@@ -3439,7 +3418,7 @@ export component MainWindow inherits Window {
} }
} }
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
HorizontalLayout { HorizontalLayout {
height: 34px; alignment: end; height: 34px; alignment: end;
PrimaryButton { PrimaryButton {
@@ -3472,8 +3451,8 @@ export component MainWindow inherits Window {
text-size: ImGuiFontStyle.prominent; text-size: ImGuiFontStyle.prominent;
text-weight: 600; text-weight: 600;
} }
ShadowSurface { FlatSurface {
background: #f7f8fa; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; background: #f7f8fa; outline-color: UiSurface.edge; border-radius: UiRadius.surface;
Text { x: 14px; y: 22px; width: parent.width - 28px; height: parent.height - 24px; text: UiStrings.service-setup-message; color: #3d434d; wrap: word-wrap; vertical-alignment: center; } Text { x: 14px; y: 22px; width: parent.width - 28px; height: parent.height - 24px; text: UiStrings.service-setup-message; color: #3d434d; wrap: word-wrap; vertical-alignment: center; }
} }
if root.portable-service-status != "": Text { if root.portable-service-status != "": Text {
@@ -3544,13 +3523,13 @@ export component MainWindow inherits Window {
z: 250; z: 250;
} }
if root.custom-titlebar: ShadowSurface { if root.custom-titlebar: FlatSurface {
x: 0px; x: 0px;
y: 0px; y: 0px;
width: root.width; width: root.width;
height: root.height; height: root.height;
background: transparent; background: transparent;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: border-radius:
root.wayland-titlebar ? root.wayland-corner-radius : 0px; root.wayland-titlebar ? root.wayland-corner-radius : 0px;
z: 200; z: 200;
+8 -8
View File
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ScrollView } from "std-widgets.slint"; import { ScrollView } from "std-widgets.slint";
import { IconButton, FontAwesomeIcons, ControlStyle } from "common.slint"; import { IconButton, FontAwesomeIcons, ControlStyle } from "common.slint";
@@ -91,11 +91,11 @@ export component MessagePanel inherits SettingsDialog {
text: root.notice-text; font-size: 12px; wrap: word-wrap; color: #b3261e; text: root.notice-text; font-size: 12px; wrap: word-wrap; color: #b3261e;
} }
} }
message-content := ShadowSurface { message-content := FlatSurface {
x: 24px; y: root.content-top + (root.notice-text == "" ? 0px : notice.height + 12px); x: 24px; y: root.content-top + (root.notice-text == "" ? 0px : notice.height + 12px);
width: parent.width - 48px; height: parent.height - self.y - 24px; width: parent.width - 48px; height: parent.height - self.y - 24px;
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
border-radius: UiRadius.surface; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
if root.items.length > 0 || root.selected-id != "": Rectangle { if root.items.length > 0 || root.selected-id != "": Rectangle {
x: 1px; y: 1px; width: content-divider.x - 1px; height: parent.height - 2px; x: 1px; y: 1px; width: content-divider.x - 1px; height: parent.height - 2px;
background: WorkbenchStyle.field-surface; background: WorkbenchStyle.field-surface;
@@ -113,14 +113,14 @@ export component MessagePanel inherits SettingsDialog {
announcement-entries := VerticalLayout { announcement-entries := VerticalLayout {
width: announcements-scroll.viewport-width; spacing: 10px; padding: 0px; width: announcements-scroll.viewport-width; spacing: 10px; padding: 0px;
alignment: start; alignment: start;
for item in root.items: ShadowSurface { for item in root.items: FlatSurface {
height: 64px; height: 64px;
background: root.selected-id == item.id background: root.selected-id == item.id
? (entry-touch.pressed ? ControlStyle.selected-pressed : entry-touch.has-hover ? ControlStyle.selected-hover : ControlStyle.selected) ? (entry-touch.pressed ? ControlStyle.selected-pressed : entry-touch.has-hover ? ControlStyle.selected-hover : ControlStyle.selected)
: entry-touch.pressed ? WorkbenchStyle.pressed-surface : entry-touch.pressed ? WorkbenchStyle.pressed-surface
: entry-touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface; : entry-touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface;
border-radius: UiRadius.control; border-radius: UiRadius.control;
shadow-color: root.selected-id == item.id ? UiShadow.focus : UiShadow.edge; outline-color: root.selected-id == item.id ? UiSurface.focus : UiSurface.edge;
Text { Text {
x: 10px; y: 9px; width: parent.width - 30px; height: 20px; x: 10px; y: 9px; width: parent.width - 30px; height: 20px;
text: item.title; overflow: elide; font-size: 13px; text: item.title; overflow: elide; font-size: 13px;
@@ -155,10 +155,10 @@ export component MessagePanel inherits SettingsDialog {
color: WorkbenchStyle.muted; font-size: 13px; color: WorkbenchStyle.muted; font-size: 13px;
horizontal-alignment: center; vertical-alignment: center; horizontal-alignment: center; vertical-alignment: center;
} }
content-divider := ShadowSeparator { content-divider := SurfaceSeparator {
x: announcement-list.x + announcement-list.width + 8px; x: announcement-list.x + announcement-list.width + 8px;
y: 1px; width: 1px; height: parent.height - 2px; y: 1px; width: 1px; height: parent.height - 2px;
vertical: true; shadow-color: UiShadow.separator; vertical: true; outline-color: UiSurface.separator;
visible: root.items.length > 0 || root.selected-id != ""; visible: root.items.length > 0 || root.selected-id != "";
accessible-role: none; accessible-role: none;
} }
@@ -207,7 +207,7 @@ export component MessagePanel inherits SettingsDialog {
clicked => { root.request-delete(root.selected-id, root.selected-revision); } clicked => { root.request-delete(root.selected-id, root.selected-revision); }
} }
} }
ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
StyledText { StyledText {
vertical-stretch: 0; vertical-stretch: 0;
text: root.selected-body; text: root.selected-body;
@@ -1,4 +1,4 @@
import { ShadowSurface, UiShadow } from "ui_shadow.slint"; import { FlatSurface, UiSurface } from "ui_surface.slint";
import { LineEdit } from "std-widgets.slint"; import { LineEdit } from "std-widgets.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ControlStyle } from "common.slint"; import { ControlStyle } from "common.slint";
@@ -11,7 +11,7 @@ export component RoundedLineEdit inherits Rectangle {
in property <InputType> input-type: InputType.text; in property <InputType> input-type: InputType.text;
in property <bool> enabled: true; in property <bool> enabled: true;
in property <length> font-size: 12px; in property <length> font-size: 12px;
in property <color> normal-shadow-color: UiShadow.edge; in property <color> normal-outline-color: UiSurface.edge;
in property <brush> surrounding-background: white; in property <brush> surrounding-background: white;
callback accepted(); callback accepted();
callback edited(); callback edited();
@@ -40,7 +40,7 @@ export component RoundedLineEdit inherits Rectangle {
: editor.has-focus ? ControlStyle.input-focus-tint : ControlStyle.input-tint; : editor.has-focus ? ControlStyle.input-focus-tint : ControlStyle.input-tint;
} }
// The software renderer clips children to rectangular bounds. Mask the // The software renderer clips children to rectangular bounds. Mask the
// standard editor's corners before drawing our soft focus edge on top. // standard editor's corners before drawing the focus outline on top.
Rectangle { Rectangle {
x: -root.border-radius; y: -root.border-radius; x: -root.border-radius; y: -root.border-radius;
width: parent.width + 2 * root.border-radius; width: parent.width + 2 * root.border-radius;
@@ -49,13 +49,12 @@ export component RoundedLineEdit inherits Rectangle {
border-radius: 2 * root.border-radius; border-radius: 2 * root.border-radius;
border-color: root.surrounding-background; border-color: root.surrounding-background;
} }
ShadowSurface { FlatSurface {
width: parent.width; height: parent.height; width: parent.width; height: parent.height;
border-radius: root.border-radius; border-radius: root.border-radius;
clip: true; clip: true;
shadow-color: editor.has-focus ? UiShadow.focus : root.normal-shadow-color; outline-color: editor.has-focus ? UiSurface.focus : root.normal-outline-color;
shadow-focused: editor.has-focus; outline-focused: editor.has-focus;
shadow-compact: true;
background: transparent; background: transparent;
} }
} }
+17 -14
View File
@@ -1,7 +1,7 @@
import { ShadowSurface, UiShadow } from "ui_shadow.slint"; import { FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ScrollView } from "std-widgets.slint"; import { ScrollView } from "std-widgets.slint";
import { FontAwesomeIcons, FloatingBarStyle, ControlStyle } from "common.slint"; import { FontAwesomeIcons, FloatingBarStyle, ButtonStyle } from "common.slint";
export struct ControllerEntry { export struct ControllerEntry {
remote-id: string, remote-id: string,
@@ -20,7 +20,7 @@ export global ServerStrings {
in property <string> cancel: "取消"; in property <string> cancel: "取消";
} }
component SessionButton inherits ShadowSurface { component SessionButton inherits FlatSurface {
in property <string> text; in property <string> text;
in property <bool> danger: false; in property <bool> danger: false;
in property <bool> enabled: true; in property <bool> enabled: true;
@@ -30,10 +30,13 @@ component SessionButton inherits ShadowSurface {
width: label.preferred-width + 20px; width: label.preferred-width + 20px;
height: 28px; height: 28px;
border-radius: UiRadius.control; border-radius: UiRadius.control;
shadow-color: root.danger ? UiShadow.error : UiShadow.edge; outline-enabled: !root.danger;
outline-color: root.pointer-feedback && touch.pressed ? ButtonStyle.pressed-border
: root.pointer-feedback && touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border;
background: root.danger ? (root.pointer-feedback && touch.pressed ? #b42318 : root.pointer-feedback && touch.has-hover ? #f04438 : #d92d20) background: root.danger ? (root.pointer-feedback && touch.pressed ? #b42318 : root.pointer-feedback && touch.has-hover ? #f04438 : #d92d20)
: (root.pointer-feedback && touch.pressed ? ControlStyle.pressed : (root.pointer-feedback && touch.pressed ? ButtonStyle.pressed
: root.pointer-feedback && touch.has-hover ? ControlStyle.hover : ControlStyle.surface); : root.pointer-feedback && touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface);
animate background, outline-color { duration: 120ms; }
opacity: root.enabled ? 1 : 0.5; opacity: root.enabled ? 1 : 0.5;
accessible-role: button; accessible-role: button;
accessible-label: root.text; accessible-label: root.text;
@@ -73,9 +76,9 @@ component SessionButton inherits ShadowSurface {
// Restore hover feedback only after a fresh pointer move or press. // Restore hover feedback only after a fresh pointer move or press.
clicked => { root.pointer-feedback = false; root.keyboard-focus = false; keyboard.focus(); root.clicked(); } clicked => { root.pointer-feedback = false; root.keyboard-focus = false; keyboard.focus(); root.clicked(); }
} }
if root.enabled && keyboard.has-focus && root.keyboard-focus: ShadowSurface { if root.enabled && keyboard.has-focus && root.keyboard-focus: FlatSurface {
x: 2px; y: 2px; width: parent.width - 4px; height: parent.height - 4px; x: 2px; y: 2px; width: parent.width - 4px; height: parent.height - 4px;
border-radius: UiRadius.control - 2px; shadow-color: root.danger ? white : UiShadow.focus; border-radius: UiRadius.control - 2px; outline-color: root.danger ? white : UiSurface.focus;
} }
} }
@@ -108,10 +111,10 @@ export component ServerStatusBar inherits Window {
return reject; return reject;
} }
} }
ShadowSurface { FlatSurface {
x: 0px; y: 0px; width: parent.width; height: parent.height; x: 0px; y: 0px; width: parent.width; height: parent.height;
background: touch.pressed ? #dfe3e9 : touch.has-hover ? #f3f4f6 : white; background: touch.pressed ? #dfe3e9 : touch.has-hover ? #f3f4f6 : white;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: FloatingBarStyle.corner-radius; border-radius: FloatingBarStyle.corner-radius;
border-top-right-radius: 0px; border-top-right-radius: 0px;
border-bottom-right-radius: 0px; border-bottom-right-radius: 0px;
@@ -171,9 +174,9 @@ export component ServerWindow inherits Window {
if event.text == Key.Escape { root.dismiss-panel(); return accept; } if event.text == Key.Escape { root.dismiss-panel(); return accept; }
return reject; return reject;
} }
ShadowSurface { FlatSurface {
width: root.width; height: root.height; width: root.width; height: root.height;
background: white; shadow-color: UiShadow.edge; background: white; outline-color: UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
Text { Text {
x: 10px; y: 10px; width: parent.width - 20px; height: 24px; x: 10px; y: 10px; width: parent.width - 20px; height: 24px;
@@ -193,9 +196,9 @@ export component ServerWindow inherits Window {
width: parent.width - (root.controllers.length > 1 ? disconnect.width + 4px : status.preferred-width + 8px); width: parent.width - (root.controllers.length > 1 ? disconnect.width + 4px : status.preferred-width + 8px);
height: parent.height; border-radius: UiRadius.control; height: parent.height; border-radius: UiRadius.control;
background: root.controllers.length > 1 && index == root.selected-controller ? #eef5fd : select-touch.has-hover ? #f3f4f6 : transparent; background: root.controllers.length > 1 && index == root.selected-controller ? #eef5fd : select-touch.has-hover ? #f3f4f6 : transparent;
if root.controllers.length > 1: ShadowSurface { if root.controllers.length > 1: FlatSurface {
x: 4px; y: (parent.height - 13px) / 2; width: 13px; height: 13px; x: 4px; y: (parent.height - 13px) / 2; width: 13px; height: 13px;
border-radius: 7px; shadow-color: index == root.selected-controller ? UiShadow.focus : UiShadow.edge; border-radius: 7px; outline-color: index == root.selected-controller ? UiSurface.focus : UiSurface.edge;
if index == root.selected-controller: Rectangle { x: 3px; y: 3px; width: 7px; height: 7px; border-radius: 4px; background: #2463c7; } if index == root.selected-controller: Rectangle { x: 3px; y: 3px; width: 7px; height: 7px; border-radius: 4px; background: #2463c7; }
} }
name := Text { name := Text {
+12 -19
View File
@@ -1,11 +1,10 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { CompactComboBox, ToggleSwitch, DialogSurface, IconButton, FontAwesomeIcons, ControlStyle, ButtonStyle } from "common.slint"; import { CompactComboBox, ToggleSwitch, DialogSurface, IconButton, FontAwesomeIcons, ControlStyle, ButtonStyle } from "common.slint";
import { WorkbenchStyle } from "workbench_controls.slint"; import { WorkbenchStyle } from "workbench_controls.slint";
// Shared visual treatment for the global settings pages. // Shared visual treatment for the global settings pages.
export component SettingsButton inherits ShadowSurface { export component SettingsButton inherits FlatSurface {
shadow-compact: true;
in property <string> text; in property <string> text;
in property <image> icon; in property <image> icon;
in property <bool> primary: false; in property <bool> primary: false;
@@ -20,14 +19,11 @@ export component SettingsButton inherits ShadowSurface {
: root.primary ? (touch.pressed ? #1e4fa3 : touch.has-hover ? #2866c9 : #3275dc) : root.primary ? (touch.pressed ? #1e4fa3 : touch.has-hover ? #2866c9 : #3275dc)
: touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover
: root.quiet ? transparent : ButtonStyle.surface; : root.quiet ? transparent : ButtonStyle.surface;
border-width: root.quiet || root.primary ? 0px : 1px; outline-enabled: !root.quiet && !root.primary;
border-color: !root.enabled ? ButtonStyle.disabled-border outline-color: !root.enabled ? ButtonStyle.disabled-border
: touch.pressed ? ButtonStyle.pressed-border : touch.pressed ? ButtonStyle.pressed-border
: touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; : touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border;
shadow-enabled: root.enabled && !root.quiet && !touch.pressed; animate background, outline-color { duration: 120ms; }
shadow-color: root.primary ? UiShadow.focus
: touch.has-hover ? UiShadow.hover : UiShadow.edge;
animate background, border-color { duration: 120ms; }
accessible-role: button; accessible-label: root.text; accessible-role: button; accessible-label: root.text;
accessible-enabled: root.enabled; accessible-enabled: root.enabled;
accessible-action-default => { if root.enabled { root.clicked(); } } accessible-action-default => { if root.enabled { root.clicked(); } }
@@ -68,10 +64,7 @@ export component SettingsDialog inherits DialogSurface {
callback action(); callback action();
callback close(); callback close();
background: #f8fafc; background: #f8fafc;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
drop-shadow-color: #14294424;
drop-shadow-blur: 24px;
drop-shadow-offset-y: 8px;
TouchArea { } TouchArea { }
HorizontalLayout { HorizontalLayout {
x: 24px; y: 18px; width: parent.width - 48px; height: 28px; x: 24px; y: 18px; width: parent.width - 48px; height: 28px;
@@ -120,8 +113,8 @@ export component SettingsDialog inherits DialogSurface {
export component SettingsSelect inherits CompactComboBox { export component SettingsSelect inherits CompactComboBox {
subtle: true; subtle: true;
show-arrow: true; show-arrow: true;
normal-shadow-color: UiShadow.edge; normal-outline-color: UiSurface.edge;
hover-shadow-color: UiShadow.hover; hover-outline-color: UiSurface.hover;
height: 28px; height: 28px;
} }
@@ -133,9 +126,9 @@ export component SettingsGroup inherits Rectangle {
x: 2px; y: 0px; width: parent.width - 4px; height: 18px; text: root.title; x: 2px; y: 0px; width: parent.width - 4px; height: 18px; text: root.title;
font-size: 11px; font-weight: 600; color: #718096; vertical-alignment: center; font-size: 11px; font-weight: 600; color: #718096; vertical-alignment: center;
} }
ShadowSurface { FlatSurface {
y: 24px; width: parent.width; height: parent.height - self.y; y: 24px; width: parent.width; height: parent.height - self.y;
background: white; border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: white; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
VerticalLayout { padding: 0px; spacing: 0px; @children } VerticalLayout { padding: 0px; spacing: 0px; @children }
} }
} }
@@ -151,8 +144,8 @@ export component SettingsRow inherits Rectangle {
text: root.label; color: root.enabled ? #34445a : #9ba5b3; text: root.label; color: root.enabled ? #34445a : #9ba5b3;
font-size: 12px; overflow: elide; vertical-alignment: center; font-size: 12px; overflow: elide; vertical-alignment: center;
} }
if root.divider: ShadowSeparator { if root.divider: SurfaceSeparator {
x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; shadow-color: UiShadow.separator; x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; outline-color: UiSurface.separator;
} }
@children @children
} }
+24 -34
View File
@@ -1,4 +1,4 @@
import { ShadowSeparator, ShadowSurface, UiShadow } from "ui_shadow.slint"; import { SurfaceSeparator, FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ComboBox, ScrollView } from "std-widgets.slint"; import { ComboBox, ScrollView } from "std-widgets.slint";
import { RemoteCursor } from "remote_cursor.slint"; import { RemoteCursor } from "remote_cursor.slint";
@@ -85,13 +85,10 @@ struct ControlBarTooltip {
} }
// Shared surface treatment for the capsule and its detached detail cards. // Shared surface treatment for the capsule and its detached detail cards.
component StreamControlSurface inherits ShadowSurface { component StreamControlSurface inherits FlatSurface {
background: white; background: white;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: 16px; border-radius: 16px;
drop-shadow-color: #1d35571a;
drop-shadow-blur: 10px;
drop-shadow-offset-y: 2px;
} }
// Keep the compact 16px icons inside roomier circular targets. This remains // Keep the compact 16px icons inside roomier circular targets. This remains
@@ -246,22 +243,18 @@ component VideoSettingRow inherits VerticalLayout {
Text { text: root.label; color: #5c626c; font-size: 11px; height: 18px; } Text { text: root.label; color: #5c626c; font-size: 11px; height: 18px; }
HorizontalLayout { HorizontalLayout {
spacing: 4px; spacing: 4px;
for option[index] in root.options: ShadowSurface { for option[index] in root.options: FlatSurface {
height: 27px; height: 27px;
background: !root.enabled ? ControlStyle.disabled background: !root.enabled ? ControlStyle.disabled
: root.selected == index ? (option-touch.pressed ? ButtonStyle.selected-pressed : option-touch.has-hover ? ButtonStyle.selected-hover : ButtonStyle.selected) : root.selected == index ? (option-touch.pressed ? ButtonStyle.selected-pressed : option-touch.has-hover ? ButtonStyle.selected-hover : ButtonStyle.selected)
: option-touch.pressed ? ButtonStyle.pressed : option-touch.pressed ? ButtonStyle.pressed
: option-touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; : option-touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface;
border-radius: UiRadius.control; border-radius: UiRadius.control;
border-width: 1px; outline-color: !root.enabled ? ButtonStyle.disabled-border
border-color: !root.enabled ? ButtonStyle.disabled-border
: root.selected == index ? ButtonStyle.selected-pressed : root.selected == index ? ButtonStyle.selected-pressed
: option-touch.pressed ? ButtonStyle.pressed-border : option-touch.pressed ? ButtonStyle.pressed-border
: option-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; : option-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border;
shadow-compact: true; animate background, outline-color { duration: 120ms; }
shadow-enabled: root.enabled && !option-touch.pressed;
shadow-color: root.enabled && root.selected == index ? UiShadow.focus : UiShadow.edge;
animate background, border-color { duration: 120ms; }
Text { text: option; color: !root.enabled ? #8b8f95 Text { text: option; color: !root.enabled ? #8b8f95
: root.selected == index ? white : #30343b; : root.selected == index ? white : #30343b;
font-weight: root.selected == index ? 600 : 400; font-weight: root.selected == index ? 600 : 400;
@@ -914,10 +907,10 @@ export component StreamWindow inherits Window {
clicked => { root.close-stream-window(); } clicked => { root.close-stream-window(); }
} }
} }
ShadowSeparator { SurfaceSeparator {
y: parent.height - 1px; y: parent.height - 1px;
height: 1px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
@@ -937,11 +930,11 @@ export component StreamWindow inherits Window {
z: 10; z: 10;
HorizontalLayout { HorizontalLayout {
spacing: 1px; spacing: 1px;
for tab[index] in root.tabs: tab-item := ShadowSurface { for tab[index] in root.tabs: tab-item := FlatSurface {
width: min(190px, max(110px, (parent.width - 12px) / root.tabs.length)); width: min(190px, max(110px, (parent.width - 12px) / root.tabs.length));
background: index == root.selected-tab ? white : tab-touch.has-hover || root.dragging-tab == index ? #e5e7eb : #d8dbe0; background: index == root.selected-tab ? white : tab-touch.has-hover || root.dragging-tab == index ? #e5e7eb : #d8dbe0;
shadow-enabled: root.dragging-tab == index; outline-enabled: root.dragging-tab == index;
shadow-color: UiShadow.focus; outline-color: UiSurface.focus;
Rectangle { Rectangle {
width: parent.width - 27px; width: parent.width - 27px;
height: parent.height; height: parent.height;
@@ -1214,11 +1207,11 @@ export component StreamWindow inherits Window {
tooltip: root.fullscreen-enabled ? StreamStrings.exit-fullscreen : StreamStrings.fullscreen; tooltip: root.fullscreen-enabled ? StreamStrings.exit-fullscreen : StreamStrings.fullscreen;
clicked => { root.close-control-menus(); root.toggle-fullscreen(); } clicked => { root.close-control-menus(); root.toggle-fullscreen(); }
} }
if root.control-content-visible: ShadowSeparator { if root.control-content-visible: SurfaceSeparator {
opacity: root.control-content-progress; opacity: root.control-content-progress;
x: close-button.x - 6px; y: 12px; x: close-button.x - 6px; y: 12px;
width: 1px; height: 14px; width: 1px; height: 14px;
vertical: true; shadow-color: UiShadow.separator; vertical: true; outline-color: UiSurface.separator;
} }
close-button := ControlBarButton { close-button := ControlBarButton {
reveal-progress: root.control-content-progress; reveal-progress: root.control-content-progress;
@@ -1423,12 +1416,12 @@ export component StreamWindow inherits Window {
vertical-alignment: center; vertical-alignment: center;
overflow: elide; overflow: elide;
} }
if index < root.stats-rows.length - 2: ShadowSeparator { if index < root.stats-rows.length - 2: SurfaceSeparator {
x: 4px; x: 4px;
y: parent.height - 1px; y: parent.height - 1px;
width: parent.width - 8px; width: parent.width - 8px;
height: 1px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
} }
@@ -1437,16 +1430,16 @@ export component StreamWindow inherits Window {
text: StreamStrings.stats-encoder + "\n" + StreamStrings.stats-decoder; text: StreamStrings.stats-encoder + "\n" + StreamStrings.stats-decoder;
font-size: ImGuiFontStyle.small; font-size: ImGuiFontStyle.small;
} }
ShadowSurface { FlatSurface {
x: 0px; x: 0px;
y: root.stats-header-height + root.stats-rows.length * stats-table.row-height y: root.stats-header-height + root.stats-rows.length * stats-table.row-height
+ 70px; + 70px;
width: parent.width; height: 44px; width: parent.width; height: 44px;
background: #f7f9fc; border-radius: UiRadius.control; background: #f7f9fc; border-radius: UiRadius.control;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
ShadowSeparator { SurfaceSeparator {
x: 8px; y: 22px; width: parent.width - 16px; height: 1px; x: 8px; y: 22px; width: parent.width - 16px; height: 1px;
shadow-color: UiShadow.separator; outline-color: UiSurface.separator;
} }
for codec[index] in [ for codec[index] in [
{ label: StreamStrings.stats-encoder, value: root.stats-encoder }, { label: StreamStrings.stats-encoder, value: root.stats-encoder },
@@ -1762,11 +1755,8 @@ export component StreamWindow inherits Window {
y: min(max(64px, parent.height * 0.18), max(16px, parent.height - self.height - 16px)); y: min(max(64px, parent.height * 0.18), max(16px, parent.height - self.height - 16px));
width: min(400px, parent.width - 48px); width: min(400px, parent.width - 48px);
height: remote-update-message.y + remote-update-message.height + 84px; height: remote-update-message.y + remote-update-message.height + 84px;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
drop-shadow-color: #07142b30;
drop-shadow-blur: 32px;
drop-shadow-offset-y: 12px;
z: 14; z: 14;
TouchArea { TouchArea {
mouse-cursor: default; mouse-cursor: default;
@@ -1806,10 +1796,10 @@ export component StreamWindow inherits Window {
} }
// Transfer progress overlay at bottom-left. // Transfer progress overlay at bottom-left.
if root.file-transfer-visible: ShadowSurface { if root.file-transfer-visible: FlatSurface {
x: root.file-panel-x; y: root.file-panel-y; x: root.file-panel-x; y: root.file-panel-y;
width: root.file-panel-width; height: root.file-panel-height; width: root.file-panel-width; height: root.file-panel-height;
background: #fffffff0; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; z: 7; background: white; outline-color: UiSurface.edge; border-radius: UiRadius.surface; z: 7;
// Header/padding and drags leaving the panel belong to local UI. // Header/padding and drags leaving the panel belong to local UI.
TouchArea { TouchArea {
mouse-cursor: default; mouse-cursor: default;
@@ -1858,11 +1848,11 @@ export component StreamWindow inherits Window {
} }
} }
if root.custom-titlebar && !root.fullscreen-enabled: ShadowSurface { if root.custom-titlebar && !root.fullscreen-enabled: FlatSurface {
width: parent.width; width: parent.width;
height: parent.height; height: parent.height;
background: transparent; background: transparent;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: border-radius:
root.window-maximized ? 0px : root.window-corner-radius; root.window-maximized ? 0px : root.window-corner-radius;
z: 200; z: 200;
-63
View File
@@ -1,63 +0,0 @@
// Shared surface shadows use ordinary rectangles and gradients because Slint's
// software renderer does not draw drop-shadow-* effects. Keep layout and hit
// areas unchanged; only the decorative shadow extends beyond the surface.
export global UiShadow {
out property <color> edge: #20334e;
out property <color> hover: #315d92;
out property <color> focus: #3564e3;
out property <color> error: #d65252;
out property <color> separator: #20334e14;
}
export component ShadowSurface inherits Rectangle {
in property <color> shadow-color: UiShadow.edge;
in property <bool> shadow-enabled: true;
in property <bool> shadow-focused: false;
// Small controls need a tighter falloff than cards and dialogs.
in property <bool> shadow-compact: false;
border-width: 0px;
border-top-left-radius: root.border-radius;
border-top-right-radius: root.border-radius;
border-bottom-left-radius: root.border-radius;
border-bottom-right-radius: root.border-radius;
// Diffuse outwards, with more weight below the surface. A clipped surface
// keeps a faint inner falloff so native popup edges remain distinguishable.
for layer in (root.shadow-compact ? 3 : 6): Rectangle {
visible: root.shadow-enabled;
private property <length> inset: root.clip ? layer * 0.5px : -(layer + 1) * 1px;
private property <float> weight: (root.shadow-compact
? (layer == 0 ? 0.09 : layer == 1 ? 0.035 : 0.01)
: (layer == 0 ? 0.09 : layer == 1 ? 0.07
: layer == 2 ? 0.05 : layer == 3 ? 0.03 : layer == 4 ? 0.015 : 0.005))
* (root.shadow-focused ? 2.5 : 1);
x: self.inset; y: self.inset;
width: max(0px, root.width - 2 * self.inset);
height: max(0px, root.height - 2 * self.inset);
border-top-left-radius: max(0px, root.border-top-left-radius - self.inset);
border-top-right-radius: max(0px, root.border-top-right-radius - self.inset);
border-bottom-left-radius: max(0px, root.border-bottom-left-radius - self.inset);
border-bottom-right-radius: max(0px, root.border-bottom-right-radius - self.inset);
border-width: 1px;
border-color: @linear-gradient(180deg,
root.shadow-color.mix(transparent, self.weight * 0.45) 0%,
root.shadow-color.mix(transparent, self.weight) 100%);
}
@children
}
export component ShadowSeparator inherits Rectangle {
in property <bool> vertical: false;
in property <color> shadow-color: UiShadow.separator;
height: 1px;
Rectangle {
x: root.vertical ? -2px : 0px;
y: root.vertical ? 0px : -1px;
width: root.vertical ? 5px : root.width;
height: root.vertical ? root.height : 6px;
background: root.vertical
? @linear-gradient(90deg, transparent 0%, root.shadow-color 50%, transparent 100%)
: @linear-gradient(180deg, transparent 0%, root.shadow-color 50%, transparent 100%);
}
}
+35
View File
@@ -0,0 +1,35 @@
// Flat surfaces share solid outlines in software and GPU renderers.
// Interaction states use their actual colors, including focus and validation.
export global UiSurface {
out property <color> edge: #d7dfe9;
out property <color> hover: #8eabd2;
out property <color> focus: #3564e3;
out property <color> error: #d65252;
out property <color> separator: #e1e7ef;
}
export component FlatSurface inherits Rectangle {
in property <color> outline-color: UiSurface.edge;
in property <bool> outline-enabled: true;
in property <bool> outline-focused: false;
border-width: !root.outline-enabled ? 0px : root.outline-focused ? 2px : 1px;
border-color: root.outline-color;
border-top-left-radius: root.border-radius;
border-top-right-radius: root.border-radius;
border-bottom-left-radius: root.border-radius;
border-bottom-right-radius: root.border-radius;
@children
}
export component SurfaceSeparator inherits Rectangle {
in property <bool> vertical: false;
in property <color> outline-color: UiSurface.separator;
height: 1px;
Rectangle {
width: root.vertical ? 1px : root.width;
height: root.vertical ? root.height : 1px;
background: root.outline-color;
}
}
@@ -1,4 +1,4 @@
import { ShadowSurface, UiShadow } from "ui_shadow.slint"; import { FlatSurface, UiSurface } from "ui_surface.slint";
import { UiRadius } from "ui_radius.slint"; import { UiRadius } from "ui_radius.slint";
import { ControlStyle, ButtonStyle } from "common.slint"; import { ControlStyle, ButtonStyle } from "common.slint";
// Colors and spacing for the connection workbench; shapes are shared app-wide. // Colors and spacing for the connection workbench; shapes are shared app-wide.
@@ -15,12 +15,11 @@ export global WorkbenchStyle {
out property <color> success: #18835a; out property <color> success: #18835a;
} }
export component WorkbenchButton inherits ShadowSurface { export component WorkbenchButton inherits FlatSurface {
shadow-compact: true;
in property <string> text; in property <string> text;
in property <length> text-size: 13px; in property <length> text-size: 13px;
in property <color> text-color: root.destructive || root.primary ? white : WorkbenchStyle.text; in property <color> text-color: root.destructive || root.primary ? white : WorkbenchStyle.text;
in property <color> normal-shadow-color: UiShadow.edge; in property <color> normal-outline-color: ButtonStyle.border;
in property <color> normal-background: ButtonStyle.surface; in property <color> normal-background: ButtonStyle.surface;
in property <string> icon; in property <string> icon;
in property <string> tooltip; in property <string> tooltip;
@@ -46,17 +45,12 @@ export component WorkbenchButton inherits ShadowSurface {
width: 112px; width: 112px;
height: 36px; height: 36px;
border-radius: UiRadius.control; border-radius: UiRadius.control;
border-width: root.show-focus || (!root.quiet && !root.soft && !root.primary && !root.destructive) ? 1px : 0px; outline-enabled: root.show-focus || (!root.quiet && !root.soft && !root.primary && !root.destructive);
border-color: !root.enabled ? ButtonStyle.disabled-border outline-focused: root.show-focus;
outline-color: !root.enabled ? ButtonStyle.disabled-border
: root.show-focus ? WorkbenchStyle.accent : root.show-focus ? WorkbenchStyle.accent
: touch.pressed ? ButtonStyle.pressed-border : touch.pressed ? ButtonStyle.pressed-border
: touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; : touch.has-hover ? ButtonStyle.hover-border : root.normal-outline-color;
shadow-enabled: root.enabled && (root.show-focus || (!root.soft && !root.quiet && !touch.pressed));
shadow-focused: root.show-focus;
shadow-color: root.destructive ? UiShadow.error
: root.show-focus || root.primary ? UiShadow.focus
: touch.has-hover ? UiShadow.hover
: root.quiet ? transparent : root.normal-shadow-color;
background: root.destructive background: root.destructive
? (touch.pressed ? #912018 : touch.has-hover ? #b42318 : #d92d20) ? (touch.pressed ? #912018 : touch.has-hover ? #b42318 : #d92d20)
: root.soft : root.soft
@@ -67,7 +61,7 @@ export component WorkbenchButton inherits ShadowSurface {
? (touch.pressed ? #254cc0 : touch.has-hover ? #4574f0 : WorkbenchStyle.accent) ? (touch.pressed ? #254cc0 : touch.has-hover ? #4574f0 : WorkbenchStyle.accent)
: (touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : root.quiet ? transparent : root.normal-background); : (touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : root.quiet ? transparent : root.normal-background);
opacity: root.enabled ? 1 : 0.45; opacity: root.enabled ? 1 : 0.45;
animate background, border-color, shadow-color { duration: 120ms; } animate background, outline-color { duration: 120ms; }
forward-focus: button-focus; forward-focus: button-focus;
accessible-role: button; accessible-role: button;
accessible-label: root.tooltip != "" ? root.tooltip : root.text; accessible-label: root.tooltip != "" ? root.tooltip : root.text;
@@ -97,6 +91,11 @@ export component WorkbenchButton inherits ShadowSurface {
accessible-role: none; accessible-role: none;
} }
} }
if root.show-focus && (root.primary || root.destructive): Rectangle {
x: 3px; y: 3px; width: parent.width - 6px; height: parent.height - 6px;
border-radius: max(0px, root.border-radius - 3px);
border-width: 1px; border-color: white;
}
button-focus := FocusScope { button-focus := FocusScope {
enabled: root.enabled; enabled: root.enabled;
focus-gained(reason) => { focus-gained(reason) => {
@@ -155,7 +154,7 @@ export component WorkbenchButton inherits ShadowSurface {
} }
// A raised surface keeps transient action feedback distinct from the workbench. // A raised surface keeps transient action feedback distinct from the workbench.
export component WorkbenchNotice inherits ShadowSurface { export component WorkbenchNotice inherits FlatSurface {
in property <string> text; in property <string> text;
in property <string> icon; in property <string> icon;
in property <bool> error: false; in property <bool> error: false;
@@ -164,11 +163,8 @@ export component WorkbenchNotice inherits ShadowSurface {
width: min(root.width-limit, max(280px, message.preferred-width + 64px)); width: min(root.width-limit, max(280px, message.preferred-width + 64px));
height: 64px; height: 64px;
background: root.error ? #fff1f0 : #e9f6ef; background: root.error ? #fff1f0 : #e9f6ef;
shadow-color: root.error ? UiShadow.error : UiShadow.edge; outline-color: root.error ? UiSurface.error : UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
drop-shadow-color: #14294424;
drop-shadow-blur: 24px;
drop-shadow-offset-y: 6px;
accessible-role: AccessibleRole.text; accessible-role: AccessibleRole.text;
accessible-label: root.text; accessible-label: root.text;
Text { Text {
@@ -196,8 +192,8 @@ export component WorkbenchNotice inherits ShadowSurface {
} }
} }
export component WorkbenchCard inherits ShadowSurface { export component WorkbenchCard inherits FlatSurface {
background: WorkbenchStyle.surface; background: WorkbenchStyle.surface;
shadow-color: UiShadow.edge; outline-color: UiSurface.edge;
border-radius: UiRadius.surface; border-radius: UiRadius.surface;
} }