From c3ea4eb8fca0577c6bf9eb591f0b55ce8216f6b8 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Mon, 26 Oct 2026 14:53:09 +0800 Subject: [PATCH] [ui] flatten desktop surfaces and controls --- apps/desktop/src/gui/ui/about_panel.slint | 16 +- apps/desktop/src/gui/ui/common.slint | 75 +++--- .../src/gui/ui/device_group_dialog.slint | 14 +- .../gui/ui/file_transfer_notification.slint | 10 +- .../src/gui/ui/gallery_menu_button.slint | 16 +- .../src/gui/ui/gallery_network_dialog.slint | 8 +- apps/desktop/src/gui/ui/gallery_view.slint | 80 +++---- apps/desktop/src/gui/ui/group_filter.slint | 30 +-- .../desktop/src/gui/ui/icons/circle-white.svg | 8 +- apps/desktop/src/gui/ui/main_window.slint | 223 ++++++++---------- apps/desktop/src/gui/ui/message_panel.slint | 16 +- .../src/gui/ui/rounded_line_edit.slint | 13 +- apps/desktop/src/gui/ui/server_window.slint | 31 +-- .../src/gui/ui/settings_controls.slint | 31 +-- apps/desktop/src/gui/ui/stream_window.slint | 58 ++--- apps/desktop/src/gui/ui/ui_shadow.slint | 63 ----- apps/desktop/src/gui/ui/ui_surface.slint | 35 +++ .../src/gui/ui/workbench_controls.slint | 38 ++- 18 files changed, 337 insertions(+), 428 deletions(-) delete mode 100644 apps/desktop/src/gui/ui/ui_shadow.slint create mode 100644 apps/desktop/src/gui/ui/ui_surface.slint diff --git a/apps/desktop/src/gui/ui/about_panel.slint b/apps/desktop/src/gui/ui/about_panel.slint index 9f87328e..6e4a17fe 100644 --- a/apps/desktop/src/gui/ui/about_panel.slint +++ b/apps/desktop/src/gui/ui/about_panel.slint @@ -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 { SettingsDialog, SettingsButton } from "settings_controls.slint"; import { WorkbenchStyle } from "workbench_controls.slint"; @@ -27,11 +27,11 @@ export global AboutStrings { out property back: GalleryStrings.tr("返回", "Back", "Назад"); } -export component AboutNavigationGroup inherits ShadowSurface { +export component AboutNavigationGroup inherits FlatSurface { height: rows.preferred-height + 2px; background: WorkbenchStyle.surface; border-radius: UiRadius.control; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; clip: true; rows := VerticalLayout { x: 1px; y: 1px; width: parent.width - 2px; @@ -42,13 +42,13 @@ export component AboutNavigationGroup inherits ShadowSurface { export component AboutRowSeparator inherits Rectangle { height: 1px; - ShadowSeparator { + SurfaceSeparator { 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 title; in property subtitle; in property icon; @@ -61,7 +61,7 @@ export component AboutNavigationRow inherits ShadowSurface { background: touch.pressed ? WorkbenchStyle.pressed-surface : touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface; 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-label: root.title + (root.subtitle != "" ? ", " + root.subtitle : "") + (root.detail != "" ? ", " + root.detail : ""); @@ -227,7 +227,7 @@ export component AboutPanel inherits SettingsDialog { footer := VerticalLayout { x: 24px; y: parent.height - self.height - 24px; 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 { alignment: end; SettingsButton { text: AboutStrings.back; clicked => { root.back(); } } diff --git a/apps/desktop/src/gui/ui/common.slint b/apps/desktop/src/gui/ui/common.slint index 12a58423..761cfc3d 100644 --- a/apps/desktop/src/gui/ui/common.slint +++ b/apps/desktop/src/gui/ui/common.slint @@ -1,4 +1,4 @@ -import { ShadowSurface, UiShadow } from "ui_shadow.slint"; +import { FlatSurface, UiSurface } from "ui_surface.slint"; import { UiRadius } from "ui_radius.slint"; export global FloatingBarStyle { out property corner-radius: UiRadius.menu; @@ -199,15 +199,14 @@ export component ImageButton inherits Rectangle { } } -export component CompactComboBox inherits ShadowSurface { - shadow-compact: true; +export component CompactComboBox inherits FlatSurface { in property <[string]> model; in-out property current-index: 0; in property enabled: true; in property subtle: false; in property show-arrow: true; - in property normal-shadow-color: UiShadow.edge; - in property hover-shadow-color: UiShadow.hover; + in property normal-outline-color: ButtonStyle.border; + in property hover-outline-color: ButtonStyle.hover-border; property popup-animation-enabled: false; property popup-progress: 0.0; callback selected(int); @@ -234,17 +233,13 @@ export component CompactComboBox inherits ShadowSurface { background: !root.enabled ? ControlStyle.disabled : combo-touch.pressed ? ButtonStyle.pressed : combo-popup.is-open || combo-touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; - border-width: 1px; - border-color: !root.enabled ? ButtonStyle.disabled-border - : combo-popup.is-open ? UiShadow.focus + outline-focused: root.enabled && combo-popup.is-open; + outline-color: !root.enabled ? ButtonStyle.disabled-border + : combo-popup.is-open ? UiSurface.focus : combo-touch.pressed ? ButtonStyle.pressed-border - : combo-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; - 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; + : combo-touch.has-hover ? root.hover-outline-color : root.normal-outline-color; border-radius: UiRadius.control; - animate background, border-color, shadow-color { duration: 120ms; } + animate background, outline-color { duration: 120ms; } accessible-role: combobox; accessible-enabled: root.enabled; accessible-expandable: true; @@ -267,7 +262,7 @@ export component CompactComboBox inherits ShadowSurface { width: 18px; height: parent.height; // Switch glyphs so the software renderer also shows the open state. 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-weight: 900; font-size: 10px; @@ -306,7 +301,7 @@ export component CompactComboBox inherits ShadowSurface { height: root.model.length * 23px + 8px; close-policy: close-on-click-outside; - ShadowSurface { + FlatSurface { // Keep the reveal anchored to the popup's top edge while the // content settles downward into its final position. y: (1.0 - root.popup-progress) * -8px; @@ -314,7 +309,7 @@ export component CompactComboBox inherits ShadowSurface { height: parent.height * root.popup-progress; opacity: root.popup-progress; background: white; - shadow-color: root.normal-shadow-color; + outline-color: root.normal-outline-color; border-radius: UiRadius.menu; clip: true; for choice[index] in root.model: Rectangle { @@ -333,7 +328,7 @@ export component CompactComboBox inherits ShadowSurface { } if index == root.current-index: Text { 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; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; @@ -353,23 +348,20 @@ export component CompactComboBox inherits ShadowSurface { } } } - ShadowSurface { + FlatSurface { width: parent.width; height: parent.height; background: transparent; clip: true; - shadow-color: root.normal-shadow-color; + outline-color: root.normal-outline-color; border-radius: UiRadius.menu; - border-width: 1px; - border-color: ButtonStyle.border; z: 2; } } } } -export component ImGuiCheckBox inherits ShadowSurface { - shadow-compact: true; +export component ImGuiCheckBox inherits FlatSurface { in-out property checked; in property enabled: true; @@ -378,7 +370,7 @@ export component ImGuiCheckBox inherits ShadowSurface { background: !root.enabled ? ControlStyle.disabled : 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; - 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; if root.checked: Text { text: FontAwesomeIcons.check; @@ -419,8 +411,7 @@ export component MiniToggleSwitch inherits Rectangle { } } -export component CompactButton inherits ShadowSurface { - shadow-compact: true; +export component CompactButton inherits FlatSurface { in property text; in property text-size: ImGuiFontStyle.body; in property primary: false; @@ -434,15 +425,13 @@ export component CompactButton inherits ShadowSurface { : button-touch.pressed ? (root.primary ? #82b8f2 : ButtonStyle.pressed) : button-touch.has-hover ? (root.primary ? #b8d9fb : ButtonStyle.hover) : root.primary ? #a8d0fa : root.quiet ? transparent : ButtonStyle.surface; - border-width: root.quiet ? 0px : 1px; - border-color: !root.enabled ? ButtonStyle.disabled-border - : root.primary ? UiShadow.focus + outline-enabled: !root.quiet; + outline-color: !root.enabled ? ButtonStyle.disabled-border + : root.primary ? UiSurface.focus : button-touch.pressed ? ButtonStyle.pressed-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; - animate background, border-color { duration: 100ms; } + animate background, outline-color { duration: 100ms; } Text { text: root.text; 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(); } } } -export component PrimaryButton inherits ShadowSurface { +export component PrimaryButton inherits FlatSurface { in property text; in property text-size: 12px; in property enabled: true; @@ -466,9 +455,7 @@ export component PrimaryButton inherits ShadowSurface { : touch.pressed ? #254cc0 : touch.has-hover ? #4574f0 : #3564e3; - shadow-compact: true; - shadow-enabled: root.enabled && !touch.pressed; - shadow-color: UiShadow.focus; + outline-enabled: false; border-radius: UiRadius.control; 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 text; in property text-size: 12px; in property enabled: true; @@ -498,15 +485,11 @@ export component SecondaryButton inherits ShadowSurface { background: !root.enabled ? ControlStyle.disabled : touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface; - border-width: 1px; - border-color: !root.enabled ? ButtonStyle.disabled-border + outline-color: !root.enabled ? ButtonStyle.disabled-border : touch.pressed ? ButtonStyle.pressed-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; - animate background, border-color { duration: 100ms; } + animate background, outline-color { duration: 100ms; } Text { text: root.text; @@ -565,8 +548,8 @@ export component SectionTitle inherits Text { vertical-alignment: center; } -export component DialogSurface inherits ShadowSurface { +export component DialogSurface inherits FlatSurface { background: white; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; } diff --git a/apps/desktop/src/gui/ui/device_group_dialog.slint b/apps/desktop/src/gui/ui/device_group_dialog.slint index 7d2d461a..6e1d15ef 100644 --- a/apps/desktop/src/gui/ui/device_group_dialog.slint +++ b/apps/desktop/src/gui/ui/device_group_dialog.slint @@ -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 { ScrollView } from "std-widgets.slint"; import { RoundedLineEdit } from "rounded_line_edit.slint"; @@ -21,13 +21,13 @@ export component DeviceGroupDialog inherits Rectangle { callback cancel(); background: #00000040; TouchArea {} - dialog := ShadowSurface { + dialog := FlatSurface { x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: min(360px, parent.width - 40px); height: 208px + group-list.height + (root.error == "" ? 0px : 32px); background: white; border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; TouchArea {} Text { 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; text: "ID " + root.device-id; color: #858c97; font-size: 11px; } - group-list := ShadowSurface { + group-list := FlatSurface { x: 18px; y: 95px; width: parent.width - 36px; 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; ScrollView { 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-color: white; } - ShadowSurface { + FlatSurface { width: parent.width; height: parent.height; border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; } } RoundedLineEdit { diff --git a/apps/desktop/src/gui/ui/file_transfer_notification.slint b/apps/desktop/src/gui/ui/file_transfer_notification.slint index ab505047..a2eea14a 100644 --- a/apps/desktop/src/gui/ui/file_transfer_notification.slint +++ b/apps/desktop/src/gui/ui/file_transfer_notification.slint @@ -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 { DialogSurface, FontAwesomeIcons, IconButton, ImGuiFontStyle } from "common.slint"; import { WorkbenchStyle, WorkbenchButton } from "workbench_controls.slint"; @@ -27,7 +27,7 @@ export component FileTransferNotification inherits Window { DialogSurface { width: parent.width; height: parent.height; background: WorkbenchStyle.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; Text { x: 16px; y: 8px; width: parent.width - 64px - (root.remote-id == "" ? 0px : 108px); @@ -51,9 +51,9 @@ export component FileTransferNotification inherits Window { pressed-background: WorkbenchStyle.pressed-surface; clicked => { root.dismissed(); } } - ShadowSeparator { + SurfaceSeparator { x: 16px; y: 40px; width: parent.width - 32px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } Text { x: 16px; y: 47px; width: parent.width - 32px; height: 18px; @@ -97,7 +97,7 @@ export component FileTransferNotification inherits Window { text: root.cancel-text; text-size: ImGuiFontStyle.base; text-color: WorkbenchStyle.muted; - normal-shadow-color: UiShadow.edge; + normal-outline-color: UiSurface.edge; clicked => { root.cancel-requested(); } } } diff --git a/apps/desktop/src/gui/ui/gallery_menu_button.slint b/apps/desktop/src/gui/ui/gallery_menu_button.slint index 40d1f304..e3d11da5 100644 --- a/apps/desktop/src/gui/ui/gallery_menu_button.slint +++ b/apps/desktop/src/gui/ui/gallery_menu_button.slint @@ -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 { CompactButton, FontAwesomeIcons, ControlStyle } from "common.slint"; @@ -89,11 +89,11 @@ export component GalleryMenuButton inherits Rectangle { color: root.enabled ? #62666d : #8e9298; 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; border-radius: UiRadius.menu; - shadow-color: trigger-focus.has-focus ? UiShadow.focus : UiShadow.edge; - animate background, shadow-color { duration: 120ms; } + outline-color: trigger-focus.has-focus ? UiSurface.focus : UiSurface.edge; + animate background, outline-color { duration: 120ms; } for dot in 3: Rectangle { x: parent.width / 2 - 6px + dot * 5px; y: (parent.height - self.height) / 2; @@ -117,12 +117,12 @@ export component GalleryMenuButton inherits Rectangle { forward-focus: popup-focus; popup-focus := FocusScope { key-pressed(event) => { return root.handle-key(event); } - ShadowSurface { - background: white; shadow-color: UiShadow.edge; border-radius: UiRadius.menu; + FlatSurface { + background: white; outline-color: UiSurface.edge; border-radius: UiRadius.menu; 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; - 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 { private property destructive: index == root.destructive-index; diff --git a/apps/desktop/src/gui/ui/gallery_network_dialog.slint b/apps/desktop/src/gui/ui/gallery_network_dialog.slint index 7b1c3182..0f98b7da 100644 --- a/apps/desktop/src/gui/ui/gallery_network_dialog.slint +++ b/apps/desktop/src/gui/ui/gallery_network_dialog.slint @@ -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 { GalleryStrings } from "device_strings.slint"; import { CompactButton } from "common.slint"; @@ -191,13 +191,13 @@ export component GalleryNetworkDialog inherits Rectangle { callback close(); background: #00000042; TouchArea { clicked => { root.close(); } } - ShadowSurface { + FlatSurface { x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: min(760px, parent.width - 32px); height: min(120px + root.list-height, parent.height - 32px); background: white; border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; TouchArea {} FocusScope { init => { self.focus(); } @@ -238,7 +238,7 @@ export component GalleryNetworkDialog inherits Rectangle { for device[index] in root.devices: NetworkRow { y: index * root.row-height; width: list.viewport-width; height: root.row-height; 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 { diff --git a/apps/desktop/src/gui/ui/gallery_view.slint b/apps/desktop/src/gui/ui/gallery_view.slint index aaaf5574..0f566306 100644 --- a/apps/desktop/src/gui/ui/gallery_view.slint +++ b/apps/desktop/src/gui/ui/gallery_view.slint @@ -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 { GalleryStrings } from "device_strings.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); 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; width: 18px; height: 18px; border-radius: 4px; 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; - shadow-color: tile.selected ? UiShadow.focus : tile-touch.has-hover ? UiShadow.hover : UiShadow.edge; - animate background, shadow-color { duration: 120ms; } + outline-color: tile.selected ? UiSurface.focus : tile-touch.has-hover ? UiSurface.hover : UiSurface.edge; + animate background, outline-color { duration: 120ms; } if tile.selected: Text { text: FontAwesomeIcons.check; color: white; 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. - for column in root.columns + 1: ShadowSeparator { + for column in root.columns + 1: SurfaceSeparator { visible: root.count > 0; x: min(column * root.card-width, grid.table-width - 1px); y: 0px; width: 1px; height: grid.table-height; - vertical: true; shadow-color: root.resizing || (grid.has-hover && grid.edge-x != 0 && grid.column-line == column) - ? UiShadow.focus : UiShadow.separator; + vertical: true; outline-color: root.resizing || (grid.has-hover && grid.edge-x != 0 && grid.column-line == column) + ? UiSurface.focus : UiSurface.separator; } - for row in root.rows + 1: ShadowSeparator { + for row in root.rows + 1: SurfaceSeparator { visible: root.count > 0; x: 0px; y: min(row * root.card-height, grid.table-height - 1px); width: grid.table-width; height: 1px; - shadow-color: root.resizing || (grid.has-hover && grid.edge-y != 0 && grid.row-line == row) - ? UiShadow.focus : UiShadow.separator; + outline-color: root.resizing || (grid.has-hover && grid.edge-y != 0 && grid.row-line == row) + ? 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; y: floor(root.drag-target / root.columns) * root.card-height + 1px; 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 { 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); } } } - ShadowSurface { + FlatSurface { x: 0px; y: rail.visible ? focus-divider.y + focus-divider.height : 36px; width: parent.width; height: parent.height - self.y; 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 { x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px; 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. width: tile.id == root.focused.id ? 0px : root.rail-slot-width; height: root.rail-card-height; visible: tile.id != root.focused.id; - rail-card := ShadowSurface { + rail-card := FlatSurface { private property presence: tile.online ? GalleryStrings.tr("在线", "Online", "В сети") : tile.presence-known ? GalleryStrings.tr("离线", "Offline", "Не в сети") : GalleryStrings.tr("未知", "Unknown", "Неизвестно"); private property 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; - 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-label: self.details; accessible-action-default => { root.action("focus", tile.id); } @@ -716,9 +716,9 @@ export component GalleryView inherits Rectangle { visible: root.focus-rail-visible; x: 0px; y: rail.y + rail.height; width: parent.width; height: 12px; private property highlighted: divider-touch.has-hover || root.focus-resizing || divider-focus.has-focus; - ShadowSeparator { + SurfaceSeparator { 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 { 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 { 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. footer := Rectangle { @@ -794,8 +794,8 @@ export component GalleryView inherits Rectangle { } network-button := SecondaryButton { x: connection-count.x + connection-count.width + 12px; - min-width: 64px; width: 64px; height: parent.height; - shadow-color: UiShadow.edge; border-radius: UiRadius.control; + width: 64px; height: parent.height; + outline-color: UiSurface.edge; border-radius: UiRadius.control; text: GalleryStrings.tr("网络状态", "Network", "Сеть"); accessible-role: button; accessible-label: self.text; @@ -843,11 +843,11 @@ export component GalleryView inherits Rectangle { 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); 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; - background: #fffffff2; shadow-color: UiShadow.focus; border-radius: UiRadius.surface; + background: white; outline-color: UiSurface.focus; border-radius: UiRadius.surface; Rectangle { x: 8px; y: 9px; width: 64px; height: 36px; 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; } } - if root.swap-save-failed: ShadowSurface { + if root.swap-save-failed: FlatSurface { x: (parent.width - self.width) / 2; y: footer.y - self.height - 14px; 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: GalleryStrings.tr("换位未保存,请重试", "Couldn't save positions. Please try again.", "Не удалось сохранить порядок. Повторите попытку."); overflow: elide; } } // 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); 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; - background: #22252bf2; shadow-color: UiShadow.edge; border-radius: UiRadius.control; + background: #22252bf2; outline-color: UiSurface.edge; border-radius: UiRadius.control; device-tooltip-label := Text { x: 8px; y: 8px; width: parent.width - 16px; 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 { background: #00000042; TouchArea {} - add-dialog := ShadowSurface { + add-dialog := FlatSurface { 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() { if root.add-device(device-id.text, password.text, group.text, remember.checked) { password.text = ""; @@ -934,18 +934,18 @@ export component GalleryView inherits Rectangle { if root.importing: Rectangle { background: #00000042; TouchArea {} - ShadowSurface { + FlatSurface { 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: 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; } 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", ""); } } 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; - shadow-color: UiShadow.edge; border-radius: UiRadius.surface; clip: true; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; clip: true; recent-list := ScrollView { x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px; viewport-width: self.width - 12px; @@ -975,10 +975,10 @@ export component GalleryView inherits Rectangle { border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface; border-color: white; } - ShadowSurface { + FlatSurface { width: parent.width; height: parent.height; 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; } @@ -990,10 +990,10 @@ export component GalleryView inherits Rectangle { } if root.confirming: Rectangle { background: #00000042; TouchArea {} - ShadowSurface { + FlatSurface { x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; 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 { x: 20px; y: 18px; width: parent.width - 132px; height: 24px; 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; 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; 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 { x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px; 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; } 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; } } } } diff --git a/apps/desktop/src/gui/ui/group_filter.slint b/apps/desktop/src/gui/ui/group_filter.slint index e025c02e..12f49d73 100644 --- a/apps/desktop/src/gui/ui/group_filter.slint +++ b/apps/desktop/src/gui/ui/group_filter.slint @@ -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 { ScrollView } from "std-widgets.slint"; import { FontAwesomeIcons, ControlStyle } from "common.slint"; // 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 system-count: 2; in property 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 embedded: false; in property show-indicator: true; in property popup-min-width: 0px; @@ -16,9 +16,9 @@ export component GroupFilter inherits ShadowSurface { in property text-weight: 400; in property text-color: #30343b; in property normal-background: ControlStyle.surface; - in property normal-shadow-color: UiShadow.edge; - in property hover-shadow-color: UiShadow.hover; - in property focus-shadow-color: UiShadow.focus; + in property normal-outline-color: UiSurface.edge; + in property hover-outline-color: UiSurface.hover; + in property focus-outline-color: UiSurface.focus; in-out property current-index: 0; out property has-focus: trigger-focus.has-focus || popup.is-open; out property has-hover: root.enabled && trigger-touch.has-hover && !popup.is-open; @@ -40,12 +40,12 @@ export component GroupFilter inherits ShadowSurface { : !root.enabled ? ControlStyle.disabled : trigger-touch.pressed || popup.is-open ? ControlStyle.pressed : trigger-touch.has-hover ? ControlStyle.hover : root.normal-background; - shadow-enabled: !root.embedded; - shadow-focused: popup.is-open || trigger-focus.has-focus; - shadow-color: popup.is-open || trigger-focus.has-focus ? root.focus-shadow-color - : trigger-touch.has-hover ? root.hover-shadow-color : root.normal-shadow-color; + outline-enabled: !root.embedded; + outline-focused: popup.is-open || trigger-focus.has-focus; + outline-color: popup.is-open || trigger-focus.has-focus ? root.focus-outline-color + : trigger-touch.has-hover ? root.hover-outline-color : root.normal-outline-color; 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; accessible-role: combobox; accessible-enabled: root.enabled; @@ -140,13 +140,13 @@ export component GroupFilter inherits ShadowSurface { forward-focus: popup-focus; popup-focus := FocusScope { key-pressed(event) => { return root.handle-key(event); } - ShadowSurface { + FlatSurface { y: (1 - root.popup-progress) * -4px; width: parent.width; height: parent.height; opacity: root.popup-progress; background: white; clip: true; - shadow-color: root.normal-shadow-color; border-radius: UiRadius.menu; + outline-color: root.normal-outline-color; border-radius: UiRadius.menu; ScrollView { x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px; viewport-width: self.width; @@ -154,9 +154,9 @@ export component GroupFilter inherits ShadowSurface { viewport-y <=> root.scroll-y; Rectangle { 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; - 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 { y: index * 26px + (root.has-divider && index >= root.system-count ? 6px : 0px); diff --git a/apps/desktop/src/gui/ui/icons/circle-white.svg b/apps/desktop/src/gui/ui/icons/circle-white.svg index 099460ca..172775e8 100644 --- a/apps/desktop/src/gui/ui/icons/circle-white.svg +++ b/apps/desktop/src/gui/ui/icons/circle-white.svg @@ -1,9 +1,3 @@ - - - - - - - + diff --git a/apps/desktop/src/gui/ui/main_window.slint b/apps/desktop/src/gui/ui/main_window.slint index 47bebad5..7b516274 100644 --- a/apps/desktop/src/gui/ui/main_window.slint +++ b/apps/desktop/src/gui/ui/main_window.slint @@ -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 { AboutPanel, AboutPage, AboutComponent, AboutStrings, AboutNavigationRow, AboutNavigationGroup, AboutRowSeparator } from "about_panel.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. -component PasswordInput inherits ShadowSurface { +component PasswordInput inherits FlatSurface { in-out property text; in property max-length: 0; in property text-size: ImGuiFontStyle.base; @@ -221,9 +221,8 @@ component PasswordInput inherits ShadowSurface { width: 70px; height: 23px; background: editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface; - shadow-color: root.error ? UiShadow.error : editor.has-focus ? UiShadow.focus : UiShadow.edge; - shadow-focused: editor.has-focus || root.error; - shadow-compact: true; + outline-color: root.error ? UiSurface.error : editor.has-focus ? UiSurface.focus : UiSurface.edge; + outline-focused: editor.has-focus || root.error; border-radius: UiRadius.control; clip: true; forward-focus: editor; @@ -943,10 +942,10 @@ export component MainWindow inherits Window { clicked => { root.close-main-window(); } } } - ShadowSeparator { + SurfaceSeparator { y: parent.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(); } } } - ShadowSeparator { + SurfaceSeparator { y: parent.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 // the boundary here because AppKit's titlebar separator is not shown // reliably when winit creates a window without an NSToolbar. - ShadowSeparator { + SurfaceSeparator { x: 0px; y: 0px; width: parent.width; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; z: 7; } @@ -1168,13 +1167,13 @@ export component MainWindow inherits Window { clicked => { root.gallery-action("release", ""); root.menu-open = !root.menu-open; } } // 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; y: menu-button.y - 1px; width: 16px; height: 16px; border-radius: 8px; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; background: WorkbenchStyle.accent; opacity: root.menu-open || mod(animation-tick(), 2s) < 1s ? 1 : 0; z: 9; @@ -1208,13 +1207,13 @@ export component MainWindow inherits Window { 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; y: menu-button.y - 1px; width: max(16px, unread-badge-label.preferred-width + 8px); height: 16px; border-radius: 8px; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; background: #d92d20; z: 9; accessible-role: button; @@ -1286,7 +1285,7 @@ export component MainWindow inherits Window { lan-port-editor.select-all(); } } - local-address-field := ShadowSurface { + local-address-field := FlatSurface { x: 16px; y: 90px; width: parent.width - 32px; height: 40px; // Size the endpoint as one line; reserve five port digits so // 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. z: 1; background: ControlStyle.input-surface; border-radius: UiRadius.control; - shadow-compact: true; - shadow-color: !root.lan-mode ? UiShadow.edge - : root.lan-endpoint-error != "" ? UiShadow.error - : lan-ip-picker.has-focus || root.lan-port-editing ? UiShadow.focus - : UiShadow.edge; - animate shadow-color { duration: 120ms; } + outline-color: !root.lan-mode ? UiSurface.edge + : root.lan-endpoint-error != "" ? UiSurface.error + : lan-ip-picker.has-focus || root.lan-port-editing ? UiSurface.focus + : UiSurface.edge; + animate outline-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; } - 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 { visible: root.lan-mode; x: 4px; y: 2px; @@ -1325,9 +1323,9 @@ export component MainWindow inherits Window { model: root.lan-interfaces; system-count: 0; current-index <=> root.lan-interface-draft-index; enabled: root.lan-mode && !root.lan-password-pending && root.lan-interfaces.length > 0; - normal-shadow-color: UiShadow.edge; - hover-shadow-color: UiShadow.hover; - focus-shadow-color: WorkbenchStyle.accent; + normal-outline-color: UiSurface.edge; + hover-outline-color: UiSurface.hover; + focus-outline-color: WorkbenchStyle.accent; accessible-label: GalleryStrings.tr("切换本机 IP", "Switch local IP", "Сменить локальный IP"); selected(index) => { root.copy-feedback = CopyFeedback.idle; @@ -1352,7 +1350,7 @@ export component MainWindow inherits Window { font-weight: 600; color: WorkbenchStyle.text; horizontal-alignment: center; vertical-alignment: center; } - lan-port-field := ShadowSurface { + lan-port-field := FlatSurface { visible: root.lan-mode; 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; @@ -1360,8 +1358,8 @@ export component MainWindow inherits Window { background: lan-port-editor.has-focus ? ControlStyle.input-focused : lan-port-hover.has-hover ? ControlStyle.hover : transparent; border-radius: UiRadius.control; - shadow-color: lan-port-editor.has-focus ? UiShadow.focus : transparent; - animate background, shadow-color { duration: 120ms; } + outline-color: lan-port-editor.has-focus ? UiSurface.focus : transparent; + animate background, outline-color { duration: 120ms; } lan-port-hover := TouchArea { enabled: root.lan-mode && !root.lan-password-pending; 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; } - ShadowSurface { + FlatSurface { x: 16px; y: 158px; width: parent.width - 32px; height: 36px; background: ControlStyle.input-surface; border-radius: UiRadius.control; - shadow-color: UiShadow.edge; - shadow-compact: true; + outline-color: UiSurface.edge; 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 { x: parent.width - 112px; y: 0px; width: 36px; quiet: true; 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: 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; } - ShadowSurface { + FlatSurface { x: 16px; y: 90px; width: parent.width - 32px; height: 40px; background: remote-id-editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface; - shadow-compact: true; border-radius: UiRadius.control; - shadow-color: root.connection-input-error != "" ? UiShadow.error : remote-id-editor.has-focus ? UiShadow.focus : UiShadow.edge; - shadow-focused: remote-id-editor.has-focus || root.connection-input-error != ""; - animate shadow-color { duration: 120ms; } + outline-color: root.connection-input-error != "" ? UiSurface.error : remote-id-editor.has-focus ? UiSurface.focus : UiSurface.edge; + outline-focused: remote-id-editor.has-focus || root.connection-input-error != ""; + animate outline-color { duration: 120ms; } if root.remote-id-input == "": Text { 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 цифр"); @@ -1538,24 +1534,23 @@ export component MainWindow inherits Window { x: parent.width - self.width - 20px; y: 0px; width: 155px; height: 32px; normal-background: white; - normal-shadow-color: UiShadow.edge; - hover-shadow-color: UiShadow.hover; - focus-shadow-color: WorkbenchStyle.accent; + normal-outline-color: UiSurface.edge; + hover-outline-color: UiSurface.hover; + focus-outline-color: WorkbenchStyle.accent; model: root.recent-groups; current-index <=> root.recent-group-index; selected(index) => { root.recent-group-selected(index); } } - recent-list := ShadowSurface { + recent-list := FlatSurface { x: 20px; y: 36px; width: parent.width - 40px; height: 142px; - // A continuous inset surface groups the cards into one - // horizontal scrolling lane, including its scrollbar. - background: root.recent-connections.length > 0 ? #e6edf6 : transparent; - shadow-enabled: root.recent-connections.length > 0; - shadow-color: UiShadow.edge; + // A light flat surface groups the cards and their scrollbar. + background: root.recent-connections.length > 0 ? #f5f7fb : transparent; + outline-enabled: root.recent-connections.length > 0; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; clip: true; @@ -1627,11 +1622,10 @@ export component MainWindow inherits Window { width: parent.width; height: root.recent-preview-height; background: !connection-card.available ? DeviceCardStyle.offline-preview : - connection.platform == "windows" ? @linear-gradient(135deg, #1f61b3, #337ac4) : - connection.platform == "macos" ? @linear-gradient(135deg, #6e4aad, #8c63c4) : - connection.platform == "linux" ? @linear-gradient(135deg, #a14f14, #ba631f) : - connection.platform == "ios" ? @linear-gradient(135deg, #146e66, #1f8278) : - @linear-gradient(135deg, #596675, #737d8c); + connection.platform == "windows" ? #296ebc : + connection.platform == "macos" ? #7d57b9 : + connection.platform == "linux" ? #ad591a : + connection.platform == "ios" ? #19786f : #667282; if connection.has-thumbnail: Image { width: parent.width; height: parent.height; @@ -1678,7 +1672,7 @@ export component MainWindow inherits Window { width: parent.width; height: 44px; 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 { x: 12px; y: 5px; height: 19px; width: parent.width - 56px; @@ -1812,11 +1806,11 @@ export component MainWindow inherits Window { } } } - ShadowSurface { + FlatSurface { width: parent.width; height: 124px; border-radius: UiRadius.surface; - shadow-color: card-touch.has-hover || card-menu.menu-open ? UiShadow.hover : UiShadow.edge; - animate shadow-color { duration: 120ms; } + outline-color: card-touch.has-hover || card-menu.menu-open ? UiSurface.hover : UiSurface.edge; + animate outline-color { duration: 120ms; } } } } @@ -1873,10 +1867,10 @@ export component MainWindow inherits Window { y: parent.height - 28px; height: 28px; background: white; - ShadowSeparator { + SurfaceSeparator { y: 0px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } Rectangle { 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, // below the modal layer (z >= 39). 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 device-name: root.gallery-mode ? gallery.hovered-device.name : root.recent-tooltip-name; private property host-name: root.gallery-mode ? gallery.hovered-device.host-name : root.recent-tooltip-host; private property 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)); height: tooltip-groups.y + tooltip-groups.height + 10px; background: WorkbenchStyle.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.menu; - drop-shadow-color: #14294424; - drop-shadow-blur: 14px; - drop-shadow-offset-y: 4px; z: 20; tooltip-name-label := Text { @@ -1997,11 +1988,11 @@ export component MainWindow inherits Window { color: WorkbenchStyle.muted; font-size: 10px; wrap: word-wrap; } - tooltip-divider := ShadowSeparator { + tooltip-divider := SurfaceSeparator { x: 12px; y: tooltip-name.y + tooltip-name.height + (device-tip.has-host-name ? tooltip-host.height + 3px : 0px) + 8px; width: parent.width - 24px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } tooltip-id-label := Text { x: 12px; y: tooltip-divider.y + 7px; @@ -2048,7 +2039,7 @@ export component MainWindow inherits Window { } // 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; y: menu-button.y + menu-button.height + 6px; 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); height: application-menu-content.preferred-height; background: WorkbenchStyle.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.menu; z: 10; TouchArea { } @@ -2089,9 +2080,9 @@ export component MainWindow inherits Window { } Rectangle { height: 7px; - ShadowSeparator { + SurfaceSeparator { x: 8px; y: 3px; width: parent.width - 16px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } } menu-messages := Rectangle { @@ -2138,9 +2129,9 @@ export component MainWindow inherits Window { } Rectangle { height: 7px; - ShadowSeparator { + SurfaceSeparator { x: 8px; y: 3px; width: parent.width - 16px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } } menu-about := Rectangle { @@ -2311,7 +2302,7 @@ export component MainWindow inherits Window { DialogPromptText { prompt: UiStrings.edit-alias; } RoundedLineEdit { height: 28px; - normal-shadow-color: UiShadow.edge; + normal-outline-color: UiSurface.edge; text <=> root.alias-input; accepted => { 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; width: min(420px, parent.width - 40px); height: disconnect-content.preferred-height; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; TouchArea { scroll-event(event) => { accept } } disconnect-content := VerticalLayout { padding: 24px; @@ -2535,8 +2526,7 @@ export component MainWindow inherits Window { y: (parent.height - self.height) / 2; width: min(400px, parent.width - 40px); height: permission-content.preferred-height; - background: #f8fafc; shadow-color: UiShadow.edge; - drop-shadow-color: #14294424; drop-shadow-blur: 24px; drop-shadow-offset-y: 8px; + background: #f8fafc; outline-color: UiSurface.edge; TouchArea { clicked => { focus-sink.focus(); } } permission-content := VerticalLayout { padding: 24px; spacing: 20px; @@ -2607,7 +2597,7 @@ export component MainWindow inherits Window { } VerticalLayout { spacing: 12px; - ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } + SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; } Text { text: GalleryStrings.tr("请在系统设置中允许 CrossDesk 访问。如系统提示重新打开应用,请按提示操作。", "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. private property content-inset: 26px; private property scrollbar-gutter: 22px; - private property shadow-gutter: 6px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: min(400px, parent.width - 40px); height: min(446px, parent.height - 16px); - background: #f8fafc; border-radius: UiRadius.surface; shadow-color: UiShadow.edge; - drop-shadow-color: #14294424; drop-shadow-blur: 24px; drop-shadow-offset-y: 8px; + background: #f8fafc; border-radius: UiRadius.surface; outline-color: UiSurface.edge; + TouchArea { clicked => { focus-sink.focus(); } } Text { 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; } // Match the compact connection-mode switch, centered in the tab row. - ShadowSurface { + FlatSurface { x: settings-dialog.content-inset; y: 62px; width: parent.width - 2 * settings-dialog.content-inset; height: 28px; background: WorkbenchStyle.field-surface; border-radius: UiRadius.control; border-width: 1px; border-color: WorkbenchStyle.accent; - shadow-compact: true; shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; Rectangle { x: root.settings-tab == 1 ? parent.width / 2 + 1px : 2px; 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 { - 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 - + settings-dialog.scrollbar-gutter + settings-dialog.shadow-gutter; + + settings-dialog.scrollbar-gutter; height: parent.height - 174px - (root.settings-save-error == "" ? 0px : 28px); viewport-width: self.visible-width; viewport-height: settings-content.preferred-height; @@ -2688,9 +2677,7 @@ export component MainWindow inherits Window { width: settings-scroll.viewport-width; height: settings-scroll.viewport-height; settings-content := VerticalLayout { - // Keep the six outer shadow layers inside the scroll clip - // without moving or narrowing the settings cards. - padding: 0px; padding-left: settings-dialog.shadow-gutter; + padding: 0px; padding-right: settings-dialog.scrollbar-gutter; padding-bottom: 4px; spacing: 14px; SettingsGroup { @@ -2785,9 +2772,9 @@ export component MainWindow inherits Window { width: parent.width - 2 * settings-dialog.content-inset; height: 24px; text: root.settings-save-error; font-size: 11px; color: #c53e3e; wrap: word-wrap; } - ShadowSeparator { + SurfaceSeparator { 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 { 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; width: min(460px, parent.width - 32px); height: min(380px, parent.height - 24px); - background: #f8fafc; shadow-color: UiShadow.edge; - drop-shadow-color: #14294424; drop-shadow-blur: 18px; drop-shadow-offset-y: 6px; + background: #f8fafc; outline-color: UiSurface.edge; TouchArea { clicked => { focus-sink.focus(); } } Text { 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; y: (parent.height - self.height) / 2; width: 360px; height: root.self-host-settings-error == "" ? 266px : 322px; - border-radius: UiRadius.surface; shadow-color: UiShadow.edge; background: #f8fafc; - drop-shadow-color: #14294424; drop-shadow-blur: 18px; drop-shadow-offset-y: 6px; + border-radius: UiRadius.surface; outline-color: UiSurface.edge; background: #f8fafc; TouchArea { clicked => { focus-sink.focus(); } } Text { x: 18px; y: 18px; width: parent.width - 36px; height: 24px; @@ -2878,21 +2863,21 @@ export component MainWindow inherits Window { RoundedLineEdit { x: 18px; y: 80px; width: parent.width - 36px; height: 30px; surrounding-background: #f8fafc; - normal-shadow-color: UiShadow.edge; + normal-outline-color: UiSurface.edge; 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; } RoundedLineEdit { x: 18px; y: 144px; width: parent.width - 36px; height: 30px; surrounding-background: #f8fafc; - normal-shadow-color: UiShadow.edge; + normal-outline-color: UiSurface.edge; text <=> root.server-port; input-type: InputType.number; } if root.self-host-settings-error != "": Text { x: 18px; y: 186px; width: parent.width - 36px; height: 48px; 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 - 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; font-family: "CrossDesk Message Spacing"; } - ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } + SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; } HorizontalLayout { alignment: end; SettingsButton { @@ -2998,11 +2983,11 @@ export component MainWindow inherits Window { width: about-scroll.width; height: self.preferred-height; // Keep the expanding scrollbar outside the content. padding: 0px; padding-right: 22px; spacing: 16px; - ShadowSurface { + FlatSurface { height: max(80px, product-info.preferred-height + 32px); background: WorkbenchStyle.surface; border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; Image { x: 16px; y: (parent.height - self.height) / 2; 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; background: WorkbenchStyle.field-surface; border-radius: UiRadius.control; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; update-summary := VerticalLayout { x: 12px; y: 12px; width: parent.width - 24px; height: self.preferred-height; @@ -3093,7 +3078,7 @@ export component MainWindow inherits Window { x: 24px; y: parent.height - self.height - 24px; width: parent.width - 48px; height: self.preferred-height; spacing: 16px; - ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } + SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; } if root.update-status-text != "": Text { text: root.update-status-text; 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. height: min(488px, parent.height - 32px); border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; - drop-shadow-color: #07142b24; - drop-shadow-blur: 24px; - drop-shadow-offset-y: 8px; + outline-color: UiSurface.edge; VerticalLayout { padding: 24px; @@ -3193,9 +3175,9 @@ export component MainWindow inherits Window { font-weight: 600; vertical-alignment: center; } - ShadowSurface { + FlatSurface { background: WorkbenchStyle.field-surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.control; // Inset the viewport so text and the scrollbar stay clear // of the rounded frame, including at either scroll limit. @@ -3242,7 +3224,7 @@ export component MainWindow inherits Window { VerticalLayout { vertical-stretch: 0; spacing: 16px; - ShadowSeparator { height: 1px; shadow-color: UiShadow.separator; } + SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; } if root.update-install-status != "": Text { text: root.update-install-status; font-size: 12px; @@ -3315,7 +3297,7 @@ export component MainWindow inherits Window { width: min(440px, parent.width - 40px); height: update-confirm-content.preferred-height; border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; TouchArea {} update-confirm-content := VerticalLayout { padding: 24px; @@ -3369,10 +3351,7 @@ export component MainWindow inherits Window { width: 512px; height: wayland-notice-content.preferred-height; border-radius: UiRadius.surface; - shadow-color: UiShadow.edge; - drop-shadow-color: #07142b30; - drop-shadow-blur: 28px; - drop-shadow-offset-y: 10px; + outline-color: UiSurface.edge; wayland-notice-content := VerticalLayout { padding: 24px; spacing: 18px; @@ -3393,9 +3372,9 @@ export component MainWindow inherits Window { } } - ShadowSurface { + FlatSurface { background: #fff8eb; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; VerticalLayout { @@ -3416,9 +3395,9 @@ export component MainWindow inherits Window { } } - ShadowSurface { + FlatSurface { background: #f3f7fd; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; 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 { height: 34px; alignment: end; PrimaryButton { @@ -3472,8 +3451,8 @@ export component MainWindow inherits Window { text-size: ImGuiFontStyle.prominent; text-weight: 600; } - ShadowSurface { - background: #f7f8fa; shadow-color: UiShadow.edge; border-radius: UiRadius.surface; + FlatSurface { + 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; } } if root.portable-service-status != "": Text { @@ -3544,13 +3523,13 @@ export component MainWindow inherits Window { z: 250; } - if root.custom-titlebar: ShadowSurface { + if root.custom-titlebar: FlatSurface { x: 0px; y: 0px; width: root.width; height: root.height; background: transparent; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: root.wayland-titlebar ? root.wayland-corner-radius : 0px; z: 200; diff --git a/apps/desktop/src/gui/ui/message_panel.slint b/apps/desktop/src/gui/ui/message_panel.slint index 2b5d8fc3..659624cd 100644 --- a/apps/desktop/src/gui/ui/message_panel.slint +++ b/apps/desktop/src/gui/ui/message_panel.slint @@ -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 { ScrollView } from "std-widgets.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; } } - message-content := ShadowSurface { + message-content := FlatSurface { x: 24px; y: root.content-top + (root.notice-text == "" ? 0px : notice.height + 12px); width: parent.width - 48px; height: parent.height - self.y - 24px; 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 { x: 1px; y: 1px; width: content-divider.x - 1px; height: parent.height - 2px; background: WorkbenchStyle.field-surface; @@ -113,14 +113,14 @@ export component MessagePanel inherits SettingsDialog { announcement-entries := VerticalLayout { width: announcements-scroll.viewport-width; spacing: 10px; padding: 0px; alignment: start; - for item in root.items: ShadowSurface { + for item in root.items: FlatSurface { height: 64px; background: root.selected-id == item.id ? (entry-touch.pressed ? ControlStyle.selected-pressed : entry-touch.has-hover ? ControlStyle.selected-hover : ControlStyle.selected) : entry-touch.pressed ? WorkbenchStyle.pressed-surface : entry-touch.has-hover ? WorkbenchStyle.hover-surface : WorkbenchStyle.surface; 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 { x: 10px; y: 9px; width: parent.width - 30px; height: 20px; text: item.title; overflow: elide; font-size: 13px; @@ -155,10 +155,10 @@ export component MessagePanel inherits SettingsDialog { color: WorkbenchStyle.muted; font-size: 13px; horizontal-alignment: center; vertical-alignment: center; } - content-divider := ShadowSeparator { + content-divider := SurfaceSeparator { x: announcement-list.x + announcement-list.width + 8px; 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 != ""; accessible-role: none; } @@ -207,7 +207,7 @@ export component MessagePanel inherits SettingsDialog { 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 { vertical-stretch: 0; text: root.selected-body; diff --git a/apps/desktop/src/gui/ui/rounded_line_edit.slint b/apps/desktop/src/gui/ui/rounded_line_edit.slint index 3ac9cc72..9a5deb7e 100644 --- a/apps/desktop/src/gui/ui/rounded_line_edit.slint +++ b/apps/desktop/src/gui/ui/rounded_line_edit.slint @@ -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 { UiRadius } from "ui_radius.slint"; import { ControlStyle } from "common.slint"; @@ -11,7 +11,7 @@ export component RoundedLineEdit inherits Rectangle { in property input-type: InputType.text; in property enabled: true; in property font-size: 12px; - in property normal-shadow-color: UiShadow.edge; + in property normal-outline-color: UiSurface.edge; in property surrounding-background: white; callback accepted(); callback edited(); @@ -40,7 +40,7 @@ export component RoundedLineEdit inherits Rectangle { : editor.has-focus ? ControlStyle.input-focus-tint : ControlStyle.input-tint; } // 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 { x: -root.border-radius; y: -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-color: root.surrounding-background; } - ShadowSurface { + FlatSurface { width: parent.width; height: parent.height; border-radius: root.border-radius; clip: true; - shadow-color: editor.has-focus ? UiShadow.focus : root.normal-shadow-color; - shadow-focused: editor.has-focus; - shadow-compact: true; + outline-color: editor.has-focus ? UiSurface.focus : root.normal-outline-color; + outline-focused: editor.has-focus; background: transparent; } } diff --git a/apps/desktop/src/gui/ui/server_window.slint b/apps/desktop/src/gui/ui/server_window.slint index fedb19bd..71fd9598 100644 --- a/apps/desktop/src/gui/ui/server_window.slint +++ b/apps/desktop/src/gui/ui/server_window.slint @@ -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 { ScrollView } from "std-widgets.slint"; -import { FontAwesomeIcons, FloatingBarStyle, ControlStyle } from "common.slint"; +import { FontAwesomeIcons, FloatingBarStyle, ButtonStyle } from "common.slint"; export struct ControllerEntry { remote-id: string, @@ -20,7 +20,7 @@ export global ServerStrings { in property cancel: "取消"; } -component SessionButton inherits ShadowSurface { +component SessionButton inherits FlatSurface { in property text; in property danger: false; in property enabled: true; @@ -30,10 +30,13 @@ component SessionButton inherits ShadowSurface { width: label.preferred-width + 20px; height: 28px; 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) - : (root.pointer-feedback && touch.pressed ? ControlStyle.pressed - : root.pointer-feedback && touch.has-hover ? ControlStyle.hover : ControlStyle.surface); + : (root.pointer-feedback && touch.pressed ? ButtonStyle.pressed + : root.pointer-feedback && touch.has-hover ? ButtonStyle.hover : ButtonStyle.surface); + animate background, outline-color { duration: 120ms; } opacity: root.enabled ? 1 : 0.5; accessible-role: button; accessible-label: root.text; @@ -73,9 +76,9 @@ component SessionButton inherits ShadowSurface { // Restore hover feedback only after a fresh pointer move or press. 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; - 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; } } - ShadowSurface { + FlatSurface { x: 0px; y: 0px; width: parent.width; height: parent.height; background: touch.pressed ? #dfe3e9 : touch.has-hover ? #f3f4f6 : white; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: FloatingBarStyle.corner-radius; border-top-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; } return reject; } - ShadowSurface { + FlatSurface { width: root.width; height: root.height; - background: white; shadow-color: UiShadow.edge; + background: white; outline-color: UiSurface.edge; border-radius: UiRadius.surface; Text { 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); height: parent.height; border-radius: UiRadius.control; 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; - 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; } } name := Text { diff --git a/apps/desktop/src/gui/ui/settings_controls.slint b/apps/desktop/src/gui/ui/settings_controls.slint index 5a053c61..84f56837 100644 --- a/apps/desktop/src/gui/ui/settings_controls.slint +++ b/apps/desktop/src/gui/ui/settings_controls.slint @@ -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 { CompactComboBox, ToggleSwitch, DialogSurface, IconButton, FontAwesomeIcons, ControlStyle, ButtonStyle } from "common.slint"; import { WorkbenchStyle } from "workbench_controls.slint"; // Shared visual treatment for the global settings pages. -export component SettingsButton inherits ShadowSurface { - shadow-compact: true; +export component SettingsButton inherits FlatSurface { in property text; in property icon; in property primary: false; @@ -20,14 +19,11 @@ export component SettingsButton inherits ShadowSurface { : root.primary ? (touch.pressed ? #1e4fa3 : touch.has-hover ? #2866c9 : #3275dc) : touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : root.quiet ? transparent : ButtonStyle.surface; - border-width: root.quiet || root.primary ? 0px : 1px; - border-color: !root.enabled ? ButtonStyle.disabled-border + outline-enabled: !root.quiet && !root.primary; + outline-color: !root.enabled ? ButtonStyle.disabled-border : touch.pressed ? ButtonStyle.pressed-border : touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; - shadow-enabled: root.enabled && !root.quiet && !touch.pressed; - shadow-color: root.primary ? UiShadow.focus - : touch.has-hover ? UiShadow.hover : UiShadow.edge; - animate background, border-color { duration: 120ms; } + animate background, outline-color { duration: 120ms; } accessible-role: button; accessible-label: root.text; accessible-enabled: root.enabled; accessible-action-default => { if root.enabled { root.clicked(); } } @@ -68,10 +64,7 @@ export component SettingsDialog inherits DialogSurface { callback action(); callback close(); background: #f8fafc; - shadow-color: UiShadow.edge; - drop-shadow-color: #14294424; - drop-shadow-blur: 24px; - drop-shadow-offset-y: 8px; + outline-color: UiSurface.edge; TouchArea { } HorizontalLayout { x: 24px; y: 18px; width: parent.width - 48px; height: 28px; @@ -120,8 +113,8 @@ export component SettingsDialog inherits DialogSurface { export component SettingsSelect inherits CompactComboBox { subtle: true; show-arrow: true; - normal-shadow-color: UiShadow.edge; - hover-shadow-color: UiShadow.hover; + normal-outline-color: UiSurface.edge; + hover-outline-color: UiSurface.hover; height: 28px; } @@ -133,9 +126,9 @@ export component SettingsGroup inherits Rectangle { x: 2px; y: 0px; width: parent.width - 4px; height: 18px; text: root.title; font-size: 11px; font-weight: 600; color: #718096; vertical-alignment: center; } - ShadowSurface { + FlatSurface { 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 } } } @@ -151,8 +144,8 @@ export component SettingsRow inherits Rectangle { text: root.label; color: root.enabled ? #34445a : #9ba5b3; font-size: 12px; overflow: elide; vertical-alignment: center; } - if root.divider: ShadowSeparator { - x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; shadow-color: UiShadow.separator; + if root.divider: SurfaceSeparator { + x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; outline-color: UiSurface.separator; } @children } diff --git a/apps/desktop/src/gui/ui/stream_window.slint b/apps/desktop/src/gui/ui/stream_window.slint index 0ccc416d..70c6193a 100644 --- a/apps/desktop/src/gui/ui/stream_window.slint +++ b/apps/desktop/src/gui/ui/stream_window.slint @@ -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 { ComboBox, ScrollView } from "std-widgets.slint"; import { RemoteCursor } from "remote_cursor.slint"; @@ -85,13 +85,10 @@ struct ControlBarTooltip { } // Shared surface treatment for the capsule and its detached detail cards. -component StreamControlSurface inherits ShadowSurface { +component StreamControlSurface inherits FlatSurface { background: white; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; 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 @@ -246,22 +243,18 @@ component VideoSettingRow inherits VerticalLayout { Text { text: root.label; color: #5c626c; font-size: 11px; height: 18px; } HorizontalLayout { spacing: 4px; - for option[index] in root.options: ShadowSurface { + for option[index] in root.options: FlatSurface { height: 27px; background: !root.enabled ? ControlStyle.disabled : 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.has-hover ? ButtonStyle.hover : ButtonStyle.surface; border-radius: UiRadius.control; - border-width: 1px; - border-color: !root.enabled ? ButtonStyle.disabled-border + outline-color: !root.enabled ? ButtonStyle.disabled-border : root.selected == index ? ButtonStyle.selected-pressed : option-touch.pressed ? ButtonStyle.pressed-border : option-touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; - shadow-compact: true; - shadow-enabled: root.enabled && !option-touch.pressed; - shadow-color: root.enabled && root.selected == index ? UiShadow.focus : UiShadow.edge; - animate background, border-color { duration: 120ms; } + animate background, outline-color { duration: 120ms; } Text { text: option; color: !root.enabled ? #8b8f95 : root.selected == index ? white : #30343b; font-weight: root.selected == index ? 600 : 400; @@ -914,10 +907,10 @@ export component StreamWindow inherits Window { clicked => { root.close-stream-window(); } } } - ShadowSeparator { + SurfaceSeparator { y: parent.height - 1px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } } @@ -937,11 +930,11 @@ export component StreamWindow inherits Window { z: 10; HorizontalLayout { 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)); background: index == root.selected-tab ? white : tab-touch.has-hover || root.dragging-tab == index ? #e5e7eb : #d8dbe0; - shadow-enabled: root.dragging-tab == index; - shadow-color: UiShadow.focus; + outline-enabled: root.dragging-tab == index; + outline-color: UiSurface.focus; Rectangle { width: parent.width - 27px; height: parent.height; @@ -1214,11 +1207,11 @@ export component StreamWindow inherits Window { tooltip: root.fullscreen-enabled ? StreamStrings.exit-fullscreen : StreamStrings.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; x: close-button.x - 6px; y: 12px; width: 1px; height: 14px; - vertical: true; shadow-color: UiShadow.separator; + vertical: true; outline-color: UiSurface.separator; } close-button := ControlBarButton { reveal-progress: root.control-content-progress; @@ -1423,12 +1416,12 @@ export component StreamWindow inherits Window { vertical-alignment: center; overflow: elide; } - if index < root.stats-rows.length - 2: ShadowSeparator { + if index < root.stats-rows.length - 2: SurfaceSeparator { x: 4px; y: parent.height - 1px; width: parent.width - 8px; 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; font-size: ImGuiFontStyle.small; } - ShadowSurface { + FlatSurface { x: 0px; y: root.stats-header-height + root.stats-rows.length * stats-table.row-height + 70px; width: parent.width; height: 44px; background: #f7f9fc; border-radius: UiRadius.control; - shadow-color: UiShadow.edge; - ShadowSeparator { + outline-color: UiSurface.edge; + SurfaceSeparator { x: 8px; y: 22px; width: parent.width - 16px; height: 1px; - shadow-color: UiShadow.separator; + outline-color: UiSurface.separator; } for codec[index] in [ { 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)); width: min(400px, parent.width - 48px); height: remote-update-message.y + remote-update-message.height + 84px; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; - drop-shadow-color: #07142b30; - drop-shadow-blur: 32px; - drop-shadow-offset-y: 12px; z: 14; TouchArea { mouse-cursor: default; @@ -1806,10 +1796,10 @@ export component StreamWindow inherits Window { } // 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; 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. TouchArea { 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; height: parent.height; background: transparent; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: root.window-maximized ? 0px : root.window-corner-radius; z: 200; diff --git a/apps/desktop/src/gui/ui/ui_shadow.slint b/apps/desktop/src/gui/ui/ui_shadow.slint deleted file mode 100644 index 48e45ca9..00000000 --- a/apps/desktop/src/gui/ui/ui_shadow.slint +++ /dev/null @@ -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 edge: #20334e; - out property hover: #315d92; - out property focus: #3564e3; - out property error: #d65252; - out property separator: #20334e14; -} - -export component ShadowSurface inherits Rectangle { - in property shadow-color: UiShadow.edge; - in property shadow-enabled: true; - in property shadow-focused: false; - // Small controls need a tighter falloff than cards and dialogs. - in property 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 inset: root.clip ? layer * 0.5px : -(layer + 1) * 1px; - private property 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 vertical: false; - in property 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%); - } -} diff --git a/apps/desktop/src/gui/ui/ui_surface.slint b/apps/desktop/src/gui/ui/ui_surface.slint new file mode 100644 index 00000000..bb84ea68 --- /dev/null +++ b/apps/desktop/src/gui/ui/ui_surface.slint @@ -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 edge: #d7dfe9; + out property hover: #8eabd2; + out property focus: #3564e3; + out property error: #d65252; + out property separator: #e1e7ef; +} + +export component FlatSurface inherits Rectangle { + in property outline-color: UiSurface.edge; + in property outline-enabled: true; + in property 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 vertical: false; + in property outline-color: UiSurface.separator; + height: 1px; + + Rectangle { + width: root.vertical ? 1px : root.width; + height: root.vertical ? root.height : 1px; + background: root.outline-color; + } +} diff --git a/apps/desktop/src/gui/ui/workbench_controls.slint b/apps/desktop/src/gui/ui/workbench_controls.slint index 00d08e8b..7be49dde 100644 --- a/apps/desktop/src/gui/ui/workbench_controls.slint +++ b/apps/desktop/src/gui/ui/workbench_controls.slint @@ -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 { ControlStyle, ButtonStyle } from "common.slint"; // Colors and spacing for the connection workbench; shapes are shared app-wide. @@ -15,12 +15,11 @@ export global WorkbenchStyle { out property success: #18835a; } -export component WorkbenchButton inherits ShadowSurface { - shadow-compact: true; +export component WorkbenchButton inherits FlatSurface { in property text; in property text-size: 13px; in property text-color: root.destructive || root.primary ? white : WorkbenchStyle.text; - in property normal-shadow-color: UiShadow.edge; + in property normal-outline-color: ButtonStyle.border; in property normal-background: ButtonStyle.surface; in property icon; in property tooltip; @@ -46,17 +45,12 @@ export component WorkbenchButton inherits ShadowSurface { width: 112px; height: 36px; border-radius: UiRadius.control; - border-width: root.show-focus || (!root.quiet && !root.soft && !root.primary && !root.destructive) ? 1px : 0px; - border-color: !root.enabled ? ButtonStyle.disabled-border + outline-enabled: root.show-focus || (!root.quiet && !root.soft && !root.primary && !root.destructive); + outline-focused: root.show-focus; + outline-color: !root.enabled ? ButtonStyle.disabled-border : root.show-focus ? WorkbenchStyle.accent : touch.pressed ? ButtonStyle.pressed-border - : touch.has-hover ? ButtonStyle.hover-border : ButtonStyle.border; - 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; + : touch.has-hover ? ButtonStyle.hover-border : root.normal-outline-color; background: root.destructive ? (touch.pressed ? #912018 : touch.has-hover ? #b42318 : #d92d20) : root.soft @@ -67,7 +61,7 @@ export component WorkbenchButton inherits ShadowSurface { ? (touch.pressed ? #254cc0 : touch.has-hover ? #4574f0 : WorkbenchStyle.accent) : (touch.pressed ? ButtonStyle.pressed : touch.has-hover ? ButtonStyle.hover : root.quiet ? transparent : root.normal-background); opacity: root.enabled ? 1 : 0.45; - animate background, border-color, shadow-color { duration: 120ms; } + animate background, outline-color { duration: 120ms; } forward-focus: button-focus; accessible-role: button; accessible-label: root.tooltip != "" ? root.tooltip : root.text; @@ -97,6 +91,11 @@ export component WorkbenchButton inherits ShadowSurface { 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 { enabled: root.enabled; focus-gained(reason) => { @@ -155,7 +154,7 @@ export component WorkbenchButton inherits ShadowSurface { } // A raised surface keeps transient action feedback distinct from the workbench. -export component WorkbenchNotice inherits ShadowSurface { +export component WorkbenchNotice inherits FlatSurface { in property text; in property icon; in property error: false; @@ -164,11 +163,8 @@ export component WorkbenchNotice inherits ShadowSurface { width: min(root.width-limit, max(280px, message.preferred-width + 64px)); height: 64px; 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; - drop-shadow-color: #14294424; - drop-shadow-blur: 24px; - drop-shadow-offset-y: 6px; accessible-role: AccessibleRole.text; accessible-label: root.text; Text { @@ -196,8 +192,8 @@ export component WorkbenchNotice inherits ShadowSurface { } } -export component WorkbenchCard inherits ShadowSurface { +export component WorkbenchCard inherits FlatSurface { background: WorkbenchStyle.surface; - shadow-color: UiShadow.edge; + outline-color: UiSurface.edge; border-radius: UiRadius.surface; }