import { LineEdit, ScrollView } from "std-widgets.slint"; import { FontAwesomeIcons, IconButton, CompactComboBox, ImGuiCheckBox, MiniToggleSwitch, CompactButton, PrimaryButton, SecondaryButton, ToggleSwitch, SectionTitle, DialogSurface, ImGuiLineStyle, ImGuiFontStyle } from "common.slint"; export struct RecentConnection { remote-id: string, display-name: string, host-name: string, online: bool, thumbnail: image, } export struct ReleaseNoteBlock { content: styled-text, section-gap: bool, } export global UiStrings { in-out property local-desktop: "Local Desktop"; in-out property remote-desktop: "Remote Desktop"; in-out property recent-connections: "Recent Connections"; in-out property local-id: "Local ID"; in-out property device-name: "Device Name"; in-out property remote-id: "Remote ID"; in-out property online: "Online"; in-out property offline: "Offline"; in-out property password: "Password"; in-out property copied: "Local ID copied to clipboard"; in-out property connect: "Connect"; in-out property settings: "Settings"; in-out property about: "About"; in-out property ok: "OK"; in-out property cancel: "Cancel"; in-out property save: "Save"; in-out property new-password: "New password (6 characters)"; in-out property invalid-password: "Password must contain exactly 6 characters"; in-out property edit-alias: "Connection alias"; in-out property delete-connection: "Delete connection"; in-out property confirm-delete: "Delete this recent connection?"; in-out property language: "Language"; in-out property video-quality: "Video quality"; in-out property frame-rate: "Frame rate"; in-out property codec: "Video codec"; in-out property hardware-codec: "Hardware video codec"; in-out property turn-relay: "TURN relay"; in-out property srtp: "SRTP encryption"; in-out property self-hosted: "Self-hosted server"; in-out property autostart: "Start with system"; in-out property daemon: "Run in background"; in-out property minimize-to-tray: "Minimize to tray"; in-out property file-save-path: "File save path"; in-out property default-desktop: "Desktop"; in-out property server-host: "Server host"; in-out property server-port: "Signal port"; in-out property coturn-port: "TURN port"; in-out property version: "Version"; in-out property copyright: "© 2026 by JUNKUN DI. All right reserved."; in-out property license: "Licensed under GNU GPL v3."; in-out property signal-connected: "Signal server connected"; in-out property signal-disconnected: "Signal server disconnected"; in-out property tls-error: "Signal server TLS certificate error"; in-out property update-available: "A new version is available"; in-out property release-notes: "Release notes"; in-out property later: "Later"; in-out property download: "Download"; in-out property input-password: "Input password"; in-out property reinput-password: "Incorrect password, please try again"; in-out property remember-password: "Remember password"; in-out property validate-password: "Validating password"; in-out property request-permissions: "Request permissions"; in-out property permission-required: "The application requires the following permissions:"; in-out property screen-recording-permission: "Screen recording permission"; in-out property accessibility-permission: "Accessibility permission"; in-out property service-setup-title: "Install CrossDesk Service"; in-out property service-setup-message: "Administrator permission is required to control this device while it is locked."; in-out property service-settings-label: "Lock Screen Service:"; in-out property install-service: "Install"; in-out property service-installed: "Installed"; in-out property do-not-remind: "Do not remind again"; in-out property notification: "Notification"; in-out property service-suppressed-message: "You will not be reminded again. You can install the service later in Settings."; in-out property quality-low: "Low"; in-out property quality-medium: "Medium"; in-out property quality-high: "High"; in-out property codec-h264: "H.264"; in-out property codec-av1: "AV1"; in-out property self-hosted-settings: "Self-hosted server settings"; in-out property access-website: "Website: "; in-out property release-date-label: "Release Date: "; } // Fluent LineEdit draws a blue 2px focus underline. The ImGui-style field // already has its own border, so cover that underline and restore its bottom // edge without changing the editor's focus, selection, or clear-button logic. component LineEditFocusUnderlineMask inherits Rectangle { height: 3px; background: white; z: 2; Rectangle { y: parent.height - 1px; height: 1px; background: ImGuiLineStyle.border; } } // Prompt copy in modal dialogs sits slightly low in the legacy ImGui layout. // Offset only the rendered text so input fields and action buttons keep their // established geometry. component DialogPromptText inherits Rectangle { in property prompt; in property text-color: #30343b; in property text-size: ImGuiFontStyle.base; in property text-weight: 400; // Mirror Text's intrinsic layout constraints exactly. This makes the // wrapper invisible to VerticalLayout: siblings retain the same geometry // they had when the Text item was a direct child. min-height: prompt-label.min-height; preferred-height: prompt-label.preferred-height; max-height: prompt-label.max-height; vertical-stretch: prompt-label.vertical-stretch; prompt-label := Text { y: 10px; width: parent.width; text: root.prompt; color: root.text-color; font-size: root.text-size; font-weight: root.text-weight; wrap: word-wrap; horizontal-alignment: center; } } // Shared online-state artwork. Both the recent-connection card and the // server status bar use the same pure green center and proportional glow. component OnlineStatusIndicator inherits Rectangle { in property dot-diameter: 8px; width: root.dot-diameter * 2.2; height: self.width; border-radius: self.width / 2; background: @radial-gradient( circle, #00ff0030 0%, #00ff0018 48%, transparent 100% ); Rectangle { x: (parent.width - root.dot-diameter) / 2; y: (parent.height - root.dot-diameter) / 2; width: root.dot-diameter; height: root.dot-diameter; border-radius: root.dot-diameter / 2; background: #00ff00; } } // ImGui::Button under StyleColorsLight(), used by both actions in the legacy // connection-password dialog. The old dialog does not distinguish primary and // secondary actions visually. component ConnectionDialogButton inherits Rectangle { in property text; callback clicked; width: max(40px, button-label.preferred-width + 8px); height: 24px; background: button-touch.pressed ? #0f87fa : button-touch.has-hover ? #4296fa : #4296fa66; border-radius: 3px; button-label := Text { text: root.text; color: #000000de; font-size: ImGuiFontStyle.base; horizontal-alignment: center; vertical-alignment: center; } button-touch := TouchArea { clicked => { root.clicked(); } } } export component MainWindow inherits Window { title: "CrossDesk"; preferred-width: 640px; preferred-height: 450px + root.titlebar-height; min-width: 640px; max-width: 640px; min-height: 450px + root.titlebar-height; max-height: 450px + root.titlebar-height; no-frame: root.custom-titlebar; background: root.wayland-titlebar ? transparent : white; default-font-size: ImGuiFontStyle.base; in property custom-titlebar: false; in property wayland-titlebar: false; in property window-active: true; private property titlebar-height: root.custom-titlebar ? (root.wayland-titlebar ? 32px : 31px) : 0px; private property wayland-corner-radius: 12px; in property local-id: ""; in property local-password: ""; in-out property remote-id-input: ""; in property password-visible: true; in property signal-connected: false; in property signal-tls-error: false; in property <[RecentConnection]> recent-connections; in property update-available: false; in property current-version: ""; in property latest-version: ""; in property release-name: ""; in property <[ReleaseNoteBlock]> release-note-blocks; in property release-date: ""; in-out property offline-warning: ""; in property connection-dialog-open: false; in property connection-status-text: ""; in property connection-pending: false; in property connection-password-required: false; in property connection-validating: false; in-out property connection-password: ""; in-out property connection-remember-password: false; in property permission-dialog-open: false; in property screen-recording-granted: true; in property accessibility-granted: true; in property portable-service-settings-visible: false; in property portable-service-dialog-open: false; in property portable-service-suppressed-notice-open: false; in property portable-service-installed: false; in property portable-service-installing: false; in property portable-service-succeeded: false; in property portable-service-status: ""; in-out property portable-service-do-not-remind: false; in-out property language-index: 0; in-out property video-quality-index: 2; in-out property frame-rate-index: 1; in-out property codec-index: 0; in-out property hardware-codec-enabled: true; in-out property turn-enabled: true; in-out property srtp-enabled: false; in-out property self-hosted-enabled: false; in-out property autostart-enabled: false; in-out property daemon-enabled: false; in-out property minimize-to-tray-enabled: false; in-out property file-save-path: ""; in-out property server-host: ""; in-out property server-port: ""; in-out property coturn-port: ""; in property settings-session-active: false; // Keep the setting visible on every platform. Unsupported builds expose it // as disabled so the fixed settings layout never turns into a blank row. in property hardware-codec-available: true; callback copy-local-id; callback toggle-password-visibility; callback reset-password(string) -> bool; callback format-remote-id(string) -> string; callback connect-requested(string); callback recent-connect(string); callback recent-edit-alias(string, string); callback recent-delete(string); callback save-settings; callback cancel-settings; callback save-self-hosted-settings; callback cancel-self-hosted-settings; callback browse-save-path; callback open-download; callback connection-cancel; callback connection-acknowledge; callback connection-submit-password(string, bool); callback reset-remote-id; callback request-screen-recording-permission; callback request-accessibility-permission; callback install-portable-service; callback cancel-portable-service; callback acknowledge-portable-service; callback acknowledge-portable-service-suppressed; callback main-title-drag(int, length, length); callback minimize-main-window; callback close-main-window; private property menu-open: false; in-out property settings-open: false; in-out property self-host-settings-open: false; in-out property about-open: false; in-out property update-open: root.update-available; in-out property reset-password-open: false; in-out property reset-password-invalid: false; in-out property new-password-input: ""; in-out property alias-open: false; private property alias-remote-id: ""; in-out property alias-input: ""; in-out property delete-open: false; private property delete-remote-id: ""; private property copy-toast: false; private property compact-language: root.language-index == 0; private property update-icon-blink-on: root.update-available && mod(animation-tick(), 2s) < 1s; private property recent-tooltip-visible: false; private property recent-tooltip-name: ""; private property recent-tooltip-host: ""; private property recent-tooltip-id: ""; private property recent-tooltip-online: false; private property recent-tooltip-pointer-x: 0px; private property recent-tooltip-pointer-y: 0px; private property recent-tooltip-width: 260px; private property recent-tooltip-height: root.recent-tooltip-host != "" && root.recent-tooltip-host != root.recent-tooltip-name ? 80px : 64px; private property recent-tooltip-x: min( root.width - root.recent-tooltip-width - 8px, max(8px, root.recent-tooltip-pointer-x > root.width * 0.7 ? root.recent-tooltip-pointer-x - root.recent-tooltip-width - 12px : root.recent-tooltip-pointer-x + 12px)); private property recent-tooltip-y: min( root.height - root.titlebar-height - root.recent-tooltip-height - 8px, max(8px, root.recent-tooltip-pointer-y - root.recent-tooltip-height - 8px)); reset-remote-id => { root.remote-id-input = ""; remote-id-editor.focus(); } window-surface := Rectangle { width: root.width; height: root.height; background: root.wayland-titlebar ? #f6f5f4 : transparent; border-radius: root.wayland-titlebar ? root.wayland-corner-radius : 0px; clip: root.wayland-titlebar; if root.custom-titlebar && root.wayland-titlebar: Rectangle { x: 0px; y: 0px; width: parent.width; height: root.titlebar-height; background: #f6f5f4; z: 100; Text { x: 80px; width: parent.width - 160px; height: parent.height; text: "CrossDesk"; color: root.window-active ? #3d3d3d : #747474; font-size: 12px; font-weight: 600; horizontal-alignment: center; vertical-alignment: center; overflow: elide; } wayland-title-drag := TouchArea { width: parent.width - 80px; property phase: self.pressed ? 1 : 0; changed phase => { root.main-title-drag(phase, self.mouse-x, self.mouse-y); } moved => { if (self.pressed) { root.main-title-drag(2, self.mouse-x, self.mouse-y); } } } wayland-minimize-button := Rectangle { x: parent.width - 74px; y: 4px; width: 24px; height: 24px; border-radius: 12px; background: wayland-minimize-touch.pressed ? #c0bfbc : wayland-minimize-touch.has-hover ? #deddda : root.window-active ? #e6e6e6 : transparent; accessible-role: button; accessible-label: "Minimize"; accessible-action-default => { wayland-minimize-touch.clicked(); } Rectangle { x: 8px; y: 13px; width: 8px; height: 0.75px; background: root.window-active || wayland-minimize-touch.has-hover || wayland-minimize-touch.pressed ? #454545 : #898989; } wayland-minimize-touch := TouchArea { clicked => { root.minimize-main-window(); } } } wayland-close-button := Rectangle { x: parent.width - 34px; y: 4px; width: 24px; height: 24px; border-radius: 12px; background: wayland-close-touch.pressed ? #b2161d : wayland-close-touch.has-hover ? #e01b24 : root.window-active ? #e6e6e6 : transparent; accessible-role: button; accessible-label: "Close"; accessible-action-default => { wayland-close-touch.clicked(); } Path { width: parent.width; height: parent.height; viewbox-width: 24; viewbox-height: 24; commands: "M 14.75 14.75 L 8.25 8.25 M 14.75 8.25 L 8.25 14.75"; stroke: wayland-close-touch.has-hover || wayland-close-touch.pressed ? white : root.window-active ? #454545 : #898989; stroke-width: 1px; } wayland-close-touch := TouchArea { clicked => { root.close-main-window(); } } } Rectangle { y: parent.height - 1px; height: 1px; background: #d5d3d0; } } if root.custom-titlebar && !root.wayland-titlebar: Rectangle { x: 0px; y: 0px; width: root.width; height: 31px; background: #f9f9f9; z: 100; Image { x: 8px; y: 7px; width: 16px; height: 16px; source: @image-url("../../../icons/linux/crossdesk_16x16.png"); image-fit: contain; } Text { x: 30px; width: parent.width - 122px; height: parent.height; text: "CrossDesk"; color: #202124; font-size: 12px; vertical-alignment: center; overflow: elide; } title-drag := TouchArea { x: 0px; width: parent.width - 92px; property phase: self.pressed ? 1 : 0; changed phase => { root.main-title-drag(phase, self.mouse-x, self.mouse-y); } moved => { if (self.pressed) { root.main-title-drag(2, self.mouse-x, self.mouse-y); } } } minimize-button := Rectangle { x: parent.width - 92px; width: 46px; height: parent.height; background: minimize-touch.pressed ? #c7c7c7 : minimize-touch.has-hover ? #e5e5e5 : transparent; accessible-role: button; accessible-label: "Minimize"; accessible-action-default => { minimize-touch.clicked(); } Text { text: FontAwesomeIcons.minus; color: #202124; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 10px; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; } minimize-touch := TouchArea { clicked => { root.minimize-main-window(); } } } close-button := Rectangle { x: parent.width - 46px; width: 46px; height: parent.height; background: close-touch.pressed ? #b3251b : close-touch.has-hover ? #c42b1c : transparent; accessible-role: button; accessible-label: "Close"; accessible-action-default => { close-touch.clicked(); } Text { text: FontAwesomeIcons.xmark; color: close-touch.has-hover || close-touch.pressed ? white : #202124; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 12px; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; } close-touch := TouchArea { clicked => { root.close-main-window(); } } } Rectangle { y: parent.height - 1px; height: 1px; background: #d4d4d4; } } content-layer := Rectangle { x: 0px; y: root.titlebar-height; width: root.width; height: root.height - self.y; clip: true; Rectangle { width: parent.width; height: parent.height; background: white; border-width: 0px; border-radius: 0px; focus-sink := FocusScope { x: 0px; y: 0px; width: 0px; height: 0px; } TouchArea { clicked => { focus-sink.focus(); } } // 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. Rectangle { x: 0px; y: 0px; width: parent.width; height: 1px; background: ImGuiLineStyle.control-separator; z: 7; } // App actions live in the content header; window controls and dragging // remain in the native or Windows-specific title bar above it. menu-button := IconButton { x: parent.width - 40px; y: 4px; width: 32px; height: 32px; icon: FontAwesomeIcons.bars; icon-size: 14px; hover-background: #e1e5eb; pressed-background: #cbd1d9; z: 8; clicked => { root.menu-open = !root.menu-open; } } if root.update-available && !root.menu-open && root.update-icon-blink-on: Text { x: menu-button.x + 16px; y: menu-button.y + 1px; width: 16px; height: 16px; text: FontAwesomeIcons.circle-arrow-up; color: #24272d; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 13px; horizontal-alignment: center; vertical-alignment: center; z: 9; } // Local and remote desktop panels. Rectangle { y: 0px; height: 234px; background: white; SectionTitle { x: 29px; y: 9px; width: 256px; height: 31px; text: UiStrings.local-desktop; } Rectangle { x: 29px; y: 42px; width: 256px; height: 183px; background: #eff0f2; border-radius: 9px; border-width: 1px; border-color: ImGuiLineStyle.border; Text { x: 8px; y: 10px; width: 160px; height: 26px; text: UiStrings.local-id; color: #111111; font-size: ImGuiFontStyle.panel-label; } Rectangle { x: 8px; y: 42px; width: 160px; height: 38px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; Text { x: 8px; // Compensate for the font's ascender-heavy // metrics so digits sit at the optical center. y: 4px; width: parent.width - 16px; height: parent.height - 4px; text: root.local-id; color: #20242a; font-size: ImGuiFontStyle.field-value; letter-spacing: 1px; horizontal-alignment: left; vertical-alignment: center; } } IconButton { x: 174px; y: 46px; width: 29px; height: 30px; icon: FontAwesomeIcons.copy; icon-size: 16px; tooltip: UiStrings.copied; clicked => { root.copy-local-id(); root.copy-toast = true; copy-timer.restart(); } } Rectangle { x: 8px; y: 88px; width: 240px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 101px; width: 160px; height: 26px; text: UiStrings.password; color: #111111; font-size: ImGuiFontStyle.panel-label; } Rectangle { x: 8px; y: 129px; width: 160px; height: 39px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; Text { x: 8px; y: 4px; width: parent.width - 16px; height: parent.height - 4px; text: root.password-visible ? root.local-password : "••••••"; color: #20242a; font-size: ImGuiFontStyle.field-value; letter-spacing: 2px; horizontal-alignment: left; vertical-alignment: center; } } IconButton { x: 174px; y: 133px; width: 29px; height: 30px; icon: root.password-visible ? FontAwesomeIcons.eye : FontAwesomeIcons.eye-slash; icon-size: 17px; clicked => { root.toggle-password-visibility(); } } IconButton { x: 214px; y: 133px; width: 29px; height: 30px; icon: FontAwesomeIcons.pen; icon-size: 15px; clicked => { root.reset-password-open = true; } } } Rectangle { x: parent.width / 2; y: 18px; width: 1px; height: 206px; background: ImGuiLineStyle.divider; } SectionTitle { x: 356px; y: 9px; width: 256px; height: 31px; text: UiStrings.remote-desktop; } Rectangle { x: 356px; y: 42px; width: 256px; height: 183px; background: #eff0f2; border-radius: 9px; border-width: 1px; border-color: ImGuiLineStyle.border; Text { x: 8px; y: 10px; width: 160px; height: 26px; text: UiStrings.remote-id; color: #111111; font-size: ImGuiFontStyle.panel-label; } Rectangle { x: 8px; y: 42px; width: 160px; height: 38px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; remote-id-editor := TextInput { x: 10px; y: 1.5px; width: parent.width - 20px; height: parent.height - 3px; text <=> root.remote-id-input; font-size: ImGuiFontStyle.field-value; input-type: InputType.number; color: #202124; vertical-alignment: center; selection-foreground-color: white; selection-background-color: #2463c7; // Run formatting after the input event has // finished so the caret state isn't reset by // a synchronous two-way binding rewrite. edited => { remote-id-format-timer.restart(); } accepted => { root.connect-requested(root.remote-id-input); } } } IconButton { x: 175px; y: 42px; width: 64px; height: 38px; normal-background: #a7cefc; hover-background: #b8d8fc; pressed-background: #76aeea; border-radius: 4px; icon: FontAwesomeIcons.arrow-right-long; icon-size: 25px; clicked => { root.connect-requested(root.remote-id-input); } } } } // Recent connections: horizontal cards with thumbnail, state and actions. recent-panel := Rectangle { y: 234px; height: 192px; background: white; border-width: 0px; border-color: ImGuiLineStyle.border; Rectangle { y: 0px; height: 1px; background: ImGuiLineStyle.border; } SectionTitle { x: 29px; y: 7px; height: 28px; text: UiStrings.recent-connections; } recent-list := Rectangle { x: 29px; y: 42px; width: parent.width - 58px; height: 139px; background: #eff0f2; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 10px; clip: true; recent-scroll := ScrollView { width: parent.width; height: parent.height; horizontal-stretch: 1; HorizontalLayout { padding-left: 8px; padding-top: 8px; padding-bottom: 14px; spacing: 24px; alignment: start; for connection[index] in root.recent-connections: connection-card := Rectangle { width: 178px; height: 117px; background: transparent; private property content-width: self.width - 30px; private property toolbar-width: 79px; private property toolbar-right-padding: 3px; Image { x: 7px; y: 7px; width: connection-card.content-width; height: 84px; source: connection.thumbnail; image-fit: cover; } Rectangle { x: 7px; y: 91px; width: connection-card.content-width; height: 25px; background: #00000018; border-radius: 3px; Text { x: 4px; y: 1px; height: parent.height - self.y; width: max(0px, parent.width - self.x - 4px - (card-touch.has-hover ? connection-card.toolbar-width + connection-card.toolbar-right-padding : 0px)); text: connection.display-name; color: #30343b; // The ImGui card footer uses its own 0.52 font // scale and renders slightly larger than the // compact body text used in dialogs. font-size: ImGuiFontStyle.base; overflow: elide; vertical-alignment: center; } } if connection.online: OnlineStatusIndicator { x: 16px - self.width / 2; y: 16px - self.height / 2; dot-diameter: 9px; } if !connection.online: Rectangle { x: 11.5px; y: 11.5px; width: 9px; height: 9px; border-radius: 4.5px; background: #9ca3af; } card-touch := TouchArea { x: 7px; y: 7px; width: connection-card.content-width; height: 109px; clicked => { root.recent-connect(connection.remote-id); } image-touch := TouchArea { width: parent.width; height: 84px; clicked => { root.recent-connect(connection.remote-id); } changed has-hover => { if self.has-hover { root.recent-tooltip-name = connection.display-name; root.recent-tooltip-host = connection.host-name; root.recent-tooltip-id = connection.remote-id; root.recent-tooltip-online = connection.online; root.recent-tooltip-pointer-x = recent-panel.x + recent-list.x + recent-scroll.x + recent-scroll.viewport-x + connection-card.x + card-touch.x + self.mouse-x; root.recent-tooltip-pointer-y = recent-panel.y + recent-list.y + recent-scroll.y + recent-scroll.viewport-y + connection-card.y + card-touch.y + self.mouse-y; root.recent-tooltip-visible = true; } else if root.recent-tooltip-id == connection.remote-id { root.recent-tooltip-visible = false; } } moved => { if self.has-hover { root.recent-tooltip-pointer-x = recent-panel.x + recent-list.x + recent-scroll.x + recent-scroll.viewport-x + connection-card.x + card-touch.x + self.mouse-x; root.recent-tooltip-pointer-y = recent-panel.y + recent-list.y + recent-scroll.y + recent-scroll.viewport-y + connection-card.y + card-touch.y + self.mouse-y; } } } if card-touch.has-hover: Rectangle { x: parent.width - self.width - connection-card.toolbar-right-padding; y: 85px; width: connection-card.toolbar-width; height: 23px; background: #4a4e55dc; border-radius: 5px; HorizontalLayout { spacing: 0px; IconButton { width: 26px; height: 23px; icon: FontAwesomeIcons.pen; icon-size: 12px; icon-color: white; hover-background: #ffffff2e; pressed-background: #598cf273; clicked => { root.alias-remote-id = connection.remote-id; root.alias-input = connection.display-name; root.alias-open = true; } } IconButton { width: 26px; height: 23px; icon: FontAwesomeIcons.trash-can; icon-size: 12px; icon-color: white; hover-background: #ffffff2e; pressed-background: #598cf273; clicked => { root.delete-remote-id = connection.remote-id; root.delete-open = true; } } IconButton { width: 27px; height: 23px; icon: FontAwesomeIcons.arrow-right-long; icon-size: 13px; icon-color: white; hover-background: #ffffff2e; pressed-background: #598cf273; clicked => { root.recent-connect(connection.remote-id); } } } } } if index < root.recent-connections.length - 1: Rectangle { x: 183px; y: 7px; width: 1px; height: 109px; background: #0000007a; } } } } } } // Status bar. Rectangle { y: 426px; height: 24px; background: #fafbfc; Rectangle { y: 0px; height: 1px; background: ImGuiLineStyle.border; } if root.signal-connected && !root.signal-tls-error: OnlineStatusIndicator { x: 15px - self.width / 2; y: 12px - self.height / 2; dot-diameter: 8px; } if !root.signal-connected || root.signal-tls-error: Rectangle { x: 11px; y: 8px; width: 8px; height: 8px; border-radius: 4px; background: root.signal-tls-error ? #f59e0b : #ef4444; } Text { x: 28px; y: 3px; height: 18px; text: root.signal-tls-error ? UiStrings.tls-error : root.signal-connected ? UiStrings.signal-connected : UiStrings.signal-disconnected; color: #343941; font-size: ImGuiFontStyle.base; vertical-alignment: center; } } } // Match the ImGui recent-connection tooltip while keeping it outside the // clipped ScrollView. It is deliberately limited to the thumbnail area. if root.recent-tooltip-visible: Rectangle { x: root.recent-tooltip-x; y: root.recent-tooltip-y; width: root.recent-tooltip-width; height: root.recent-tooltip-height; background: #22252bf2; border-width: 1px; border-color: #ffffff38; border-radius: 5px; z: 50; VerticalLayout { padding: 8px; spacing: 2px; Text { text: UiStrings.device-name + ": " + root.recent-tooltip-name; color: white; font-size: ImGuiFontStyle.small; overflow: elide; } if root.recent-tooltip-host != "" && root.recent-tooltip-host != root.recent-tooltip-name: Text { text: root.recent-tooltip-host; color: #e1e4e8; font-size: ImGuiFontStyle.small; overflow: elide; } Text { text: UiStrings.remote-id + ": " + root.recent-tooltip-id; color: #e1e4e8; font-size: ImGuiFontStyle.small; overflow: elide; } Text { text: root.recent-tooltip-online ? UiStrings.online : UiStrings.offline; color: root.recent-tooltip-online ? #62d98b : #d1d5db; font-size: ImGuiFontStyle.small; } } } // Dismiss the title menu when clicking anywhere outside of it. if root.menu-open: TouchArea { x: 0px; y: 0px; width: parent.width; height: parent.height; z: 9; clicked => { root.menu-open = false; focus-sink.focus(); } } // Title menu. if root.menu-open: Rectangle { x: menu-button.x + menu-button.width - self.width; y: menu-button.y + menu-button.height + 2px; width: min(118px, max(menu-settings-label.preferred-width, menu-about-label.preferred-width + (root.update-available ? 25px : 0px)) + 28px); height: 64px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 5px; z: 10; TouchArea { } VerticalLayout { padding: 4px; spacing: 2px; menu-settings := Rectangle { height: 27px; background: settings-touch.pressed ? #d8dadd : settings-touch.has-hover ? #f0f1f2 : white; border-width: 0px; border-radius: 3px; accessible-role: button; accessible-label: UiStrings.settings; accessible-action-default => { settings-touch.clicked(); } menu-settings-label := Text { x: 9px; height: parent.height; text: UiStrings.settings; color: #2d3138; vertical-alignment: center; accessible-role: none; } settings-touch := TouchArea { mouse-cursor: pointer; clicked => { root.menu-open = false; root.settings-open = true; } } } menu-about := Rectangle { height: 27px; background: about-touch.pressed ? #d8dadd : about-touch.has-hover ? #f0f1f2 : white; border-width: 0px; border-radius: 3px; accessible-role: button; accessible-label: UiStrings.about; accessible-action-default => { about-touch.clicked(); } menu-about-label := Text { x: 9px; height: parent.height; text: UiStrings.about; color: #2d3138; vertical-alignment: center; accessible-role: none; } if root.update-available && root.update-icon-blink-on: Text { x: menu-about-label.x + menu-about-label.preferred-width + 7px; width: 18px; height: parent.height; text: FontAwesomeIcons.circle-arrow-up; color: #2d3138; font-family: "Font Awesome 6 Free"; font-weight: 900; font-size: 16px; horizontal-alignment: center; vertical-alignment: center; accessible-role: none; } about-touch := TouchArea { mouse-cursor: pointer; clicked => { root.menu-open = false; root.about-open = true; } } } } Rectangle { x: 4px; y: 31.5px; width: parent.width - 8px; height: 1px; background: ImGuiLineStyle.separator; z: 1; } } copy-timer := Timer { interval: 1000ms; triggered => { root.copy-toast = false; } } remote-id-format-timer := Timer { interval: 1ms; triggered => { let formatted = root.format-remote-id(root.remote-id-input); root.remote-id-input = formatted; // IDs are ASCII-only, so character-count is also the UTF-8 offset // expected by set-selection-offsets(). remote-id-editor.set-selection-offsets( formatted.character-count, formatted.character-count); } } if root.copy-toast: Rectangle { x: 180px; y: 170px; width: 280px; height: 68px; background: #fffffff2; border-radius: 8px; border-width: 1px; border-color: ImGuiLineStyle.border; z: 30; Text { text: UiStrings.copied; color: #333840; horizontal-alignment: center; vertical-alignment: center; } } // Reset password modal. if root.reset-password-open: Rectangle { background: #00000042; z: 40; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.compact-language ? 205px : 160px; y: 140px; width: root.compact-language ? 230px : 320px; height: 170px; VerticalLayout { padding: 20px; spacing: 12px; DialogPromptText { prompt: UiStrings.new-password; } Rectangle { height: 28px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.new-password-input; input-type: InputType.password; } LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; } } Text { height: root.reset-password-invalid ? 18px : 0px; visible: root.reset-password-invalid; text: UiStrings.invalid-password; color: #d92d20; font-size: ImGuiFontStyle.small; horizontal-alignment: center; } HorizontalLayout { alignment: center; spacing: 8px; PrimaryButton { text: UiStrings.ok; clicked => { if (root.reset-password(root.new-password-input)) { root.new-password-input = ""; root.reset-password-invalid = false; root.reset-password-open = false; } else { root.reset-password-invalid = true; } } } SecondaryButton { text: UiStrings.cancel; clicked => { root.new-password-input = ""; root.reset-password-invalid = false; root.reset-password-open = false; } } } } } } // Recent connection alias modal. if root.alias-open: Rectangle { background: #00000042; z: 40; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.compact-language ? 205px : 160px; y: 145px; width: root.compact-language ? 230px : 320px; height: 160px; VerticalLayout { padding: 20px; spacing: 14px; DialogPromptText { prompt: UiStrings.edit-alias; } Rectangle { height: 28px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.alias-input; accepted => { root.recent-edit-alias(root.alias-remote-id, root.alias-input); root.alias-open = false; } } LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; } } HorizontalLayout { alignment: center; spacing: 8px; PrimaryButton { text: UiStrings.ok; clicked => { root.recent-edit-alias(root.alias-remote-id, root.alias-input); root.alias-open = false; } } SecondaryButton { text: UiStrings.cancel; clicked => { root.alias-open = false; } } } } } } // Delete confirmation modal. if root.delete-open: Rectangle { background: #00000042; z: 40; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.compact-language ? 205px : 160px; y: 155px; width: root.compact-language ? 230px : 320px; height: 140px; VerticalLayout { padding: 20px; spacing: 18px; DialogPromptText { prompt: UiStrings.confirm-delete; } HorizontalLayout { alignment: center; spacing: 8px; PrimaryButton { text: UiStrings.ok; clicked => { root.recent-delete(root.delete-remote-id); root.delete-open = false; } } SecondaryButton { text: UiStrings.cancel; clicked => { root.delete-open = false; } } } } } } // Offline/error warning modal. if root.offline-warning != "": Rectangle { background: #00000042; z: 39; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: 185px; y: 150px; width: 270px; height: 150px; VerticalLayout { padding: 20px; spacing: 18px; DialogPromptText { prompt: root.offline-warning; } PrimaryButton { text: UiStrings.ok; clicked => { root.offline-warning = ""; } } } } } // Connection progress and password retry modal from the ImGui client. if root.connection-dialog-open: Rectangle { background: #00000042; z: 48; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.connection-password-required ? 211px : 190px; y: root.connection-password-required ? 143px : 130px; width: root.connection-password-required ? 211px : 260px; height: root.connection-password-required ? 158px : 145px; if root.connection-password-required: Rectangle { Text { y: 38px; height: 22px; text: root.connection-status-text; color: #30343b; horizontal-alignment: center; vertical-alignment: center; } if !root.connection-validating: Rectangle { x: 71px; y: 63px; width: 70px; height: 23px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 3px; connection-password-editor := TextInput { x: 5px; y: 1px; width: parent.width - 10px; height: parent.height - 2px; text <=> root.connection-password; // The legacy ImGui field uses CharsNoBlank without the // Password flag, so entered text remains visible. input-type: InputType.text; color: #202124; font-size: ImGuiFontStyle.base; vertical-alignment: center; selection-foreground-color: white; selection-background-color: #2463c7; accepted => { root.connection-submit-password(root.connection-password, root.connection-remember-password); } } Timer { interval: 1ms; running: true; triggered => { connection-password-editor.focus(); } } } if !root.connection-validating: Rectangle { x: 72px; y: 89px; width: 120px; height: 21px; ImGuiCheckBox { x: 0px; y: 0px; checked <=> root.connection-remember-password; } Text { x: 27px; y: 0px; width: parent.width - 27px; height: parent.height; text: UiStrings.remember-password; color: #30343b; font-size: ImGuiFontStyle.body; vertical-alignment: center; } TouchArea { x: 27px; y: 0px; width: parent.width - 27px; height: parent.height; clicked => { root.connection-remember-password = !root.connection-remember-password; } } } if !root.connection-validating: ConnectionDialogButton { x: 69px; y: 118px; text: UiStrings.ok; clicked => { root.connection-submit-password(root.connection-password, root.connection-remember-password); } } if !root.connection-validating: ConnectionDialogButton { x: 117px; y: 118px; text: UiStrings.cancel; clicked => { root.connection-cancel(); } } } if !root.connection-password-required: VerticalLayout { padding: 20px; spacing: 13px; DialogPromptText { prompt: root.connection-status-text; } HorizontalLayout { alignment: center; spacing: 8px; if root.connection-pending: SecondaryButton { text: UiStrings.cancel; clicked => { root.connection-cancel(); } } if !root.connection-pending: PrimaryButton { text: UiStrings.ok; clicked => { root.connection-acknowledge(); } } } } } } // macOS permission gate. On other platforms this remains hidden. if root.permission-dialog-open: Rectangle { background: transparent; z: 50; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.compact-language ? 255px : 190px; y: 162px; width: root.compact-language ? 130px : 260px; height: 125px; border-radius: 7px; Rectangle { x: 0px; y: 0px; width: parent.width; height: 21px; background: #d3d3d3; border-radius: 7px; z: 5; Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; } Text { x: 6px; text: UiStrings.request-permissions; font-size: ImGuiFontStyle.base; font-weight: 600; color: #202124; vertical-alignment: center; } } Text { x: 10px; y: 32px; width: root.compact-language ? 110px : 240px; height: 24px; text: UiStrings.permission-required; color: #202124; font-size: 8px; wrap: word-wrap; } Text { x: 10px; y: 61px; width: root.compact-language ? 78px : 198px; height: 18px; text: "1. " + UiStrings.accessibility-permission + ":"; color: #202124; font-size: 8px; overflow: elide; vertical-alignment: center; } MiniToggleSwitch { x: root.compact-language ? 90px : 210px; y: 61px; checked: root.accessibility-granted; enabled: !root.accessibility-granted; toggled(value) => { if (value) { root.request-accessibility-permission(); } } } Text { x: 10px; y: 85px; width: root.compact-language ? 78px : 198px; height: 18px; text: "2. " + UiStrings.screen-recording-permission + ":"; color: #202124; font-size: 8px; overflow: elide; vertical-alignment: center; } MiniToggleSwitch { x: root.compact-language ? 90px : 210px; y: 85px; checked: root.screen-recording-granted; enabled: !root.screen-recording-granted; toggled(value) => { if (value) { root.request-screen-recording-permission(); } } } } } // Settings modal. if root.settings-open: Rectangle { background: transparent; z: 45; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.compact-language ? 219px : 190px; y: 9px; width: root.compact-language ? 202px : 269px; height: 432px; border-radius: 7px; Rectangle { x: 0px; y: 0px; width: parent.width; height: 21px; background: #d3d3d3; border-radius: 7px; z: 5; Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; } Text { x: 6px; text: UiStrings.settings; font-size: ImGuiFontStyle.base; font-weight: 600; color: #202124; vertical-alignment: center; } } Text { x: 8px; y: 31px; width: root.compact-language ? 108px : 176px; height: 24px; text: UiStrings.language; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } CompactComboBox { x: root.compact-language ? 120px : 188px; y: 30px; model: ["中文", "English", "Русский"]; current-index <=> root.language-index; } Rectangle { x: 7px; y: 57px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 61px; width: root.compact-language ? 108px : 176px; height: 24px; text: UiStrings.video-quality; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } CompactComboBox { x: root.compact-language ? 120px : 188px; y: 60px; enabled: !root.settings-session-active; model: [UiStrings.quality-low, UiStrings.quality-medium, UiStrings.quality-high]; current-index <=> root.video-quality-index; } Rectangle { x: 7px; y: 87px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 91px; width: root.compact-language ? 108px : 176px; height: 24px; text: UiStrings.frame-rate; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } CompactComboBox { x: root.compact-language ? 120px : 188px; y: 90px; enabled: !root.settings-session-active; model: ["30 fps", "60 fps"]; current-index <=> root.frame-rate-index; } Rectangle { x: 7px; y: 117px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 121px; width: root.compact-language ? 108px : 176px; height: 24px; text: UiStrings.codec; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } CompactComboBox { x: root.compact-language ? 120px : 188px; y: 120px; enabled: !root.settings-session-active; model: [UiStrings.codec-h264, UiStrings.codec-av1]; current-index <=> root.codec-index; } Rectangle { x: 7px; y: 147px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 151px; width: root.compact-language ? 155px : 225px; height: 24px; text: UiStrings.hardware-codec; color: root.hardware-codec-available ? #202124 : #8b8f95; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 151px; checked <=> root.hardware-codec-enabled; enabled: root.hardware-codec-available && !root.settings-session-active; } Rectangle { x: 7px; y: 177px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 181px; width: root.compact-language ? 155px : 225px; height: 24px; text: UiStrings.turn-relay; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 181px; checked <=> root.turn-enabled; enabled: !root.settings-session-active; } Rectangle { x: 7px; y: 207px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 211px; width: root.compact-language ? 155px : 225px; height: 24px; text: UiStrings.srtp; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 211px; checked <=> root.srtp-enabled; enabled: !root.settings-session-active; } Rectangle { x: 7px; y: 237px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } CompactButton { x: 8px; y: 241px; width: root.compact-language ? 70px : 220px; text: UiStrings.self-hosted; enabled: !root.settings-session-active; clicked => { root.self-host-settings-open = true; } } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 241px; checked <=> root.self-hosted-enabled; enabled: !root.settings-session-active; } Rectangle { x: 7px; y: 267px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 271px; width: root.compact-language ? 155px : 225px; height: 24px; text: UiStrings.autostart; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 271px; checked <=> root.autostart-enabled; enabled: !root.settings-session-active; } Rectangle { x: 7px; y: 297px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 301px; width: root.compact-language ? 155px : 225px; height: 24px; text: UiStrings.daemon; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 301px; checked <=> root.daemon-enabled; enabled: !root.settings-session-active; } Rectangle { x: 7px; y: 327px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 331px; width: root.compact-language ? 155px : 225px; height: 24px; text: UiStrings.minimize-to-tray; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } ImGuiCheckBox { x: root.compact-language ? 171px : 238px; y: 331px; checked <=> root.minimize-to-tray-enabled; enabled: !root.settings-session-active; } Rectangle { x: 7px; y: 357px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 361px; width: root.compact-language ? 108px : 168px; height: 24px; text: UiStrings.file-save-path; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } CompactButton { x: root.compact-language ? 120px : 180px; y: 361px; width: root.compact-language ? 73px : 80px; text: root.file-save-path == "" ? UiStrings.default-desktop : root.file-save-path; enabled: !root.settings-session-active; clicked => { root.browse-save-path(); } } CompactButton { x: root.compact-language ? 63px : 89px; y: 396px; primary: true; text: UiStrings.ok; clicked => { root.save-settings(); root.settings-open = false; root.self-host-settings-open = false; } } CompactButton { x: root.compact-language ? 104px : 130px; y: 396px; width: root.compact-language ? 34px : 56px; text: UiStrings.cancel; clicked => { root.cancel-settings(); root.settings-open = false; root.self-host-settings-open = false; } } } } if root.self-host-settings-open: Rectangle { background: transparent; z: 49; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: root.compact-language ? 190px : 140px; y: 105px; width: root.compact-language ? 260px : 360px; height: 168px; border-radius: 7px; Rectangle { x: 0px; y: 0px; width: parent.width; height: 21px; background: #d3d3d3; border-radius: 7px; z: 5; Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; } Text { x: 6px; text: UiStrings.self-hosted-settings; font-size: ImGuiFontStyle.base; font-weight: 600; color: #202124; vertical-alignment: center; } } Text { x: 8px; y: 31px; width: root.compact-language ? 88px : 165px; height: 24px; text: UiStrings.server-host; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } Rectangle { x: root.compact-language ? 100px : 173px; y: 30px; width: root.compact-language ? 152px : 179px; height: 24px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.server-host; font-size: ImGuiFontStyle.body; } LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; } } Rectangle { x: 7px; y: 57px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 61px; width: root.compact-language ? 88px : 165px; height: 24px; text: UiStrings.server-port; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } Rectangle { x: root.compact-language ? 100px : 173px; y: 60px; width: root.compact-language ? 152px : 179px; height: 24px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.server-port; input-type: InputType.number; font-size: ImGuiFontStyle.body; } LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; } } Rectangle { x: 7px; y: 87px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } Text { x: 8px; y: 91px; width: root.compact-language ? 88px : 165px; height: 24px; text: UiStrings.coturn-port; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; } Rectangle { x: root.compact-language ? 100px : 173px; y: 90px; width: root.compact-language ? 152px : 179px; height: 24px; background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 4px; LineEdit { x: 1px; y: 1.5px; width: parent.width - 2px; height: parent.height - 2px; text <=> root.coturn-port; input-type: InputType.number; font-size: ImGuiFontStyle.body; } LineEditFocusUnderlineMask { x: 5px; y: parent.height - 3px; width: parent.width - 10px; } } Rectangle { x: 7px; y: 117px; width: parent.width - 14px; height: 1px; background: ImGuiLineStyle.separator; } CompactButton { x: root.compact-language ? 93px : 135px; y: 130px; primary: true; text: UiStrings.ok; clicked => { root.save-self-hosted-settings(); root.self-host-settings-open = false; } } CompactButton { x: root.compact-language ? 134px : 176px; y: 130px; width: root.compact-language ? 34px : 56px; text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } } } } // About modal. if root.about-open: Rectangle { background: transparent; z: 46; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: 170px; y: 127px; width: 300px; height: 196px; border-radius: 7px; Rectangle { x: 0px; y: 0px; width: parent.width; height: 21px; background: #d3d3d3; border-radius: 7px; z: 5; Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; } Text { x: 6px; text: UiStrings.about; font-size: ImGuiFontStyle.base; font-weight: 600; color: #202124; vertical-alignment: center; } } Text { x: 30px; y: 40px; width: 240px; height: 20px; text: UiStrings.version + ": CrossDesk " + root.current-version; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; } if root.update-available: Text { x: 30px; y: 60px; width: 240px; height: 20px; text: UiStrings.update-available + ":"; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; } if root.update-available: Rectangle { x: 30px; y: 60px; width: 240px; height: 20px; version-link := Text { text: root.latest-version; color: version-touch.has-hover ? #174ea6 : #334fd1; font-size: ImGuiFontStyle.body; horizontal-alignment: center; vertical-alignment: center; } version-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-download(); } } } Text { x: 30px; y: 85px; width: 240px; height: 20px; text: UiStrings.copyright; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; } Text { x: 30px; y: 105px; width: 240px; height: 20px; text: UiStrings.license; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; } CompactButton { x: 133px; y: 145px; primary: true; text: UiStrings.ok; clicked => { root.about-open = false; } } } } // Update notification modal. if root.update-open && root.update-available: Rectangle { background: transparent; z: 47; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: 120px; y: 52px; width: 400px; height: 346px; border-radius: 7px; Text { x: 30px; y: 22px; width: 320px; height: 24px; text: UiStrings.update-available + ": " + root.latest-version; color: #202124; font-size: ImGuiFontStyle.prominent; vertical-alignment: center; } Rectangle { x: 30px; y: 47px; width: 320px; height: 22px; Text { x: 0px; width: parent.width; height: parent.height; text: UiStrings.access-website + "https://crossdesk.cn"; color: website-touch.has-hover ? #174ea6 : #334fd1; font-size: ImGuiFontStyle.body; horizontal-alignment: left; vertical-alignment: center; } website-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-download(); } } } Text { x: 30px; y: 75px; width: 320px; height: 20px; text: UiStrings.release-notes; color: #202124; font-size: 12px; vertical-alignment: center; } Rectangle { x: 30px; y: 105px; width: 360px; height: 190px; background: white; border-width: 1px; border-color: #b9bdc3; ScrollView { x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px; VerticalLayout { width: 340px; padding-left: 8px; padding-right: 8px; padding-top: 8px; padding-bottom: 8px; spacing: 7px; VerticalLayout { width: 312px; spacing: 10px; for block in root.release-note-blocks: VerticalLayout { padding-top: block.section-gap ? 10px : 0px; StyledText { width: 312px; text: block.content; default-color: #202124; default-font-size: ImGuiFontStyle.body; } } } } } } CompactButton { x: root.compact-language ? 163px : 130px; y: 309px; width: root.compact-language ? 34px : 70px; primary: true; text: UiStrings.download; clicked => { root.open-download(); root.update-open = false; } } CompactButton { x: root.compact-language ? 204px : 207px; y: 309px; width: root.compact-language ? 34px : 56px; text: UiStrings.later; clicked => { root.update-open = false; } } } } // Portable Windows builds prompt for the optional lock-screen service. if root.portable-service-dialog-open: Rectangle { background: #00000052; z: 50; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: 80px; y: 75px; width: 480px; height: 300px; VerticalLayout { padding: 24px; spacing: 14px; DialogPromptText { prompt: UiStrings.service-setup-title; text-color: #242832; text-size: ImGuiFontStyle.prominent; text-weight: 600; } Rectangle { background: #f7f8fa; border-color: #d9dde4; border-width: 1px; border-radius: 6px; 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 { text: root.portable-service-status; color: root.portable-service-succeeded ? #25874b : root.portable-service-installing ? #68707d : #b23b3b; horizontal-alignment: center; wrap: word-wrap; } if !root.portable-service-succeeded: HorizontalLayout { height: 26px; alignment: center; spacing: 8px; Text { text: UiStrings.do-not-remind; vertical-alignment: center; color: #4a505a; } ToggleSwitch { checked <=> root.portable-service-do-not-remind; enabled: !root.portable-service-installing; } } HorizontalLayout { height: 34px; alignment: center; spacing: 8px; if root.portable-service-succeeded: PrimaryButton { text: UiStrings.ok; clicked => { root.acknowledge-portable-service(); } } if !root.portable-service-succeeded: PrimaryButton { text: UiStrings.install-service; enabled: !root.portable-service-installing; clicked => { root.install-portable-service(); } } if !root.portable-service-succeeded: SecondaryButton { text: UiStrings.cancel; enabled: !root.portable-service-installing; clicked => { root.cancel-portable-service(); } } } } } } if root.portable-service-suppressed-notice-open: Rectangle { background: #00000052; z: 51; TouchArea { clicked => { focus-sink.focus(); } } DialogSurface { x: 100px; y: 130px; width: 440px; height: 190px; VerticalLayout { padding: 24px; spacing: 16px; DialogPromptText { prompt: UiStrings.notification; text-color: #242832; text-size: ImGuiFontStyle.prominent; text-weight: 600; } DialogPromptText { prompt: UiStrings.service-suppressed-message; text-color: #3d434d; } PrimaryButton { text: UiStrings.ok; clicked => { root.acknowledge-portable-service-suppressed(); } } } } } } if root.custom-titlebar: Rectangle { x: 0px; y: 0px; width: root.width; height: root.height; background: transparent; border-width: 1px; border-color: root.wayland-titlebar ? #aaa8a6 : #8b8b8b; border-radius: root.wayland-titlebar ? root.wayland-corner-radius : 0px; z: 200; } } }