mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-18 18:03:08 +08:00
[ui] refine desktop layouts and unify control styling
This commit is contained in:
@@ -4973,6 +4973,7 @@ void GuiApplication::RestoreWindowGeometry(bool before_show,
|
||||
auto& window = component->window();
|
||||
const auto current_size = window.size();
|
||||
const bool wall_mode = gallery_window;
|
||||
const float compact_width = component->get_minimum_window_width();
|
||||
const float compact_height = component->get_minimum_window_height();
|
||||
if (!before_show && ui_->loaded_window_scale[gallery_window] > 0) {
|
||||
const float ratio = window.scale_factor() / ui_->loaded_window_scale[gallery_window];
|
||||
@@ -4986,7 +4987,8 @@ void GuiApplication::RestoreWindowGeometry(bool before_show,
|
||||
}
|
||||
ui_->loaded_window_scale[gallery_window] = 0;
|
||||
}
|
||||
auto target_size = placement.RestoreSize(wall_mode, window.scale_factor(), compact_height);
|
||||
auto target_size = placement.RestoreSize(wall_mode, window.scale_factor(),
|
||||
compact_height, compact_width);
|
||||
const auto& position = gallery_window
|
||||
? placement.wall_position
|
||||
: placement.connection_position;
|
||||
@@ -5024,7 +5026,8 @@ void GuiApplication::RestoreWindowGeometry(bool before_show,
|
||||
// placement. Only recover coordinates when its display is unavailable.
|
||||
if (!found && available.w > 0 && available.h > 0) {
|
||||
const auto margin = uint32_t(32 * window.scale_factor());
|
||||
const auto minimum = placement.RestoreSize(false, window.scale_factor(), compact_height);
|
||||
const auto minimum = placement.RestoreSize(false, window.scale_factor(),
|
||||
compact_height, compact_width);
|
||||
safe_size.width = std::max(minimum.width,
|
||||
std::min(safe_size.width, uint32_t(available.w)));
|
||||
safe_size.height = std::max(minimum.height, std::min(
|
||||
@@ -5080,6 +5083,7 @@ void GuiApplication::SyncWindowGeometry(bool gallery_window) {
|
||||
auto& placement = gallery_window ? ui_->gallery_window : ui_->connection_window;
|
||||
const bool wall_mode = gallery_window;
|
||||
const bool transitioning = gallery_window && ui_->gallery_transition.Active();
|
||||
const float compact_width = component->get_minimum_window_width();
|
||||
const float compact_height = component->get_minimum_window_height();
|
||||
#if defined(__APPLE__)
|
||||
const bool fullscreen = gallery_window && IsGalleryWindowFullscreen();
|
||||
@@ -5091,16 +5095,17 @@ void GuiApplication::SyncWindowGeometry(bool gallery_window) {
|
||||
if (!transitioning && placement.RecoverUndersized(wall_mode, window.position(), current_size,
|
||||
window.scale_factor(), window.is_visible(),
|
||||
window.is_minimized(), fullscreen, maximized,
|
||||
compact_height)) {
|
||||
compact_height, compact_width)) {
|
||||
ui_->restore_ticks[gallery_window] = 0;
|
||||
const auto target = placement.RestoreSize(wall_mode, window.scale_factor(),
|
||||
compact_height);
|
||||
compact_height, compact_width);
|
||||
LOG_WARN("DeskHub undersized window: wall={} client={}x{} scale={}; restoring={}x{}",
|
||||
wall_mode, current_size.width, current_size.height,
|
||||
window.scale_factor(), target.width, target.height);
|
||||
}
|
||||
if (placement.Restoring() && ++ui_->restore_ticks[gallery_window] == 120) {
|
||||
const auto target = placement.RestoreSize(wall_mode, window.scale_factor(), compact_height);
|
||||
const auto target = placement.RestoreSize(wall_mode, window.scale_factor(),
|
||||
compact_height, compact_width);
|
||||
LOG_WARN("DeskHub window restore delayed: wall={} client={}x{} target={}x{} scale={} fullscreen={} maximized={}",
|
||||
wall_mode, current_size.width, current_size.height, target.width,
|
||||
target.height, window.scale_factor(), fullscreen, maximized);
|
||||
@@ -5114,7 +5119,7 @@ void GuiApplication::SyncWindowGeometry(bool gallery_window) {
|
||||
current_size.height, window.scale_factor(), fullscreen, maximized);
|
||||
} else if (window.is_visible() && !window.is_minimized() && !transitioning) {
|
||||
placement.Observe(wall_mode, window.position(), current_size, window.scale_factor(),
|
||||
fullscreen, maximized, compact_height);
|
||||
fullscreen, maximized, compact_height, compact_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,16 @@ struct WindowState {
|
||||
|
||||
void Observe(bool wall, slint::PhysicalPosition position,
|
||||
slint::PhysicalSize size, float scale, bool fullscreen,
|
||||
bool maximized, float compact_height = 486) {
|
||||
bool maximized, float compact_height = 486,
|
||||
float compact_width = 640) {
|
||||
if (Restoring())
|
||||
return;
|
||||
if (!wall) {
|
||||
connection_position = position;
|
||||
return;
|
||||
}
|
||||
if (!fullscreen && !maximized && IsUndersized(size, scale, compact_height))
|
||||
if (!fullscreen && !maximized &&
|
||||
IsUndersized(size, scale, compact_height, compact_width))
|
||||
return;
|
||||
scale = std::max(scale, 1.0f);
|
||||
wall_fullscreen = fullscreen;
|
||||
@@ -46,12 +48,13 @@ struct WindowState {
|
||||
bool RecoverUndersized(bool wall, slint::PhysicalPosition position,
|
||||
slint::PhysicalSize size, float scale, bool visible,
|
||||
bool minimized, bool fullscreen, bool maximized,
|
||||
float compact_height = 486) {
|
||||
float compact_height = 486,
|
||||
float compact_width = 640) {
|
||||
// Hidden/minimized/uninitialized windows can report temporary tiny bounds.
|
||||
// Leave those alone, and let an in-flight restoration retain its target.
|
||||
if (Restoring() || !visible || minimized || fullscreen || maximized ||
|
||||
size.width == 0 || size.height == 0 ||
|
||||
!IsUndersized(size, scale, compact_height))
|
||||
!IsUndersized(size, scale, compact_height, compact_width))
|
||||
return false;
|
||||
if (wall) {
|
||||
wall_position = position;
|
||||
@@ -66,9 +69,11 @@ struct WindowState {
|
||||
}
|
||||
|
||||
slint::PhysicalSize RestoreSize(bool wall, float scale,
|
||||
float compact_height = 486) const {
|
||||
float compact_height = 486,
|
||||
float compact_width = 640) const {
|
||||
const float width =
|
||||
wall && wall_size ? std::max(640.0f, wall_size->width) : 640;
|
||||
wall && wall_size ? std::max(compact_width, wall_size->width)
|
||||
: compact_width;
|
||||
const float height = wall && wall_size
|
||||
? std::max(compact_height, wall_size->height)
|
||||
: compact_height;
|
||||
@@ -157,9 +162,9 @@ private:
|
||||
std::optional<slint::PhysicalSize> target_, last_size_;
|
||||
int matches_ = 0;
|
||||
static bool IsUndersized(slint::PhysicalSize size, float scale,
|
||||
float compact_height) {
|
||||
float compact_height, float compact_width) {
|
||||
scale = std::max(scale, 1.0f);
|
||||
return double(size.width) + 1 < 640 * scale ||
|
||||
return double(size.width) + 1 < compact_width * scale ||
|
||||
double(size.height) + 1 < compact_height * scale;
|
||||
}
|
||||
static bool SameSize(slint::PhysicalSize a, slint::PhysicalSize b) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
// The legacy client uses ImGui::StyleColorsLight(). Keep the structural line
|
||||
// colors in one place so the Slint renderer produces the same visual weight.
|
||||
export global ImGuiLineStyle {
|
||||
@@ -8,7 +9,7 @@ export global ImGuiLineStyle {
|
||||
}
|
||||
|
||||
export global FloatingBarStyle {
|
||||
out property <length> corner-radius: 6px;
|
||||
out property <length> corner-radius: UiRadius.menu;
|
||||
}
|
||||
|
||||
// ImGui loads a 32px font atlas and applies per-window font scales. These
|
||||
@@ -76,7 +77,7 @@ export component IconButton inherits Rectangle {
|
||||
background: touch.pressed ? (danger ? #d92d20 : root.pressed-background)
|
||||
: touch.has-hover ? (danger ? #f04438 : root.hover-background)
|
||||
: root.normal-background;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
opacity: root.enabled ? 1 : 0.4;
|
||||
|
||||
Text {
|
||||
@@ -109,7 +110,7 @@ export component IconButton inherits Rectangle {
|
||||
width: tip.preferred-width + 12px;
|
||||
height: 24px;
|
||||
background: #22252be8;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
z: 20;
|
||||
tip := Text {
|
||||
text: root.tooltip;
|
||||
@@ -136,7 +137,7 @@ export component ImageButton inherits Rectangle {
|
||||
background: touch.pressed ? (danger ? #d92d20 : #d9dde5)
|
||||
: touch.has-hover ? (danger ? #f04438 : #edf0f5)
|
||||
: transparent;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
|
||||
Image {
|
||||
x: (parent.width - self.width) / 2;
|
||||
@@ -157,7 +158,7 @@ export component ImageButton inherits Rectangle {
|
||||
width: tip.preferred-width + 12px;
|
||||
height: 24px;
|
||||
background: #22252be8;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
z: 20;
|
||||
tip := Text {
|
||||
text: root.tooltip;
|
||||
@@ -175,6 +176,8 @@ export component CompactComboBox inherits Rectangle {
|
||||
in property <bool> enabled: true;
|
||||
in property <bool> subtle: false;
|
||||
in property <bool> show-arrow: true;
|
||||
in property <color> normal-border-color: root.subtle ? #dfe6ef : #bfc2c7;
|
||||
in property <color> hover-border-color: root.normal-border-color;
|
||||
property <bool> popup-animation-enabled: false;
|
||||
property <float> popup-progress: 0.0;
|
||||
callback selected(int);
|
||||
@@ -189,8 +192,8 @@ export component CompactComboBox inherits Rectangle {
|
||||
height: 24px;
|
||||
background: root.enabled ? (root.subtle ? #f8fafc : white) : root.subtle ? #f1f3f6 : #f1f1f1;
|
||||
border-width: 1px;
|
||||
border-color: root.subtle ? #dfe6ef : #bfc2c7;
|
||||
border-radius: root.subtle ? 6px : 3px;
|
||||
border-color: root.enabled && combo-touch.has-hover ? root.hover-border-color : root.normal-border-color;
|
||||
border-radius: UiRadius.control;
|
||||
|
||||
Text {
|
||||
x: root.subtle ? 10px : 5px;
|
||||
@@ -207,6 +210,8 @@ export component CompactComboBox inherits Rectangle {
|
||||
height: parent.height - 2px;
|
||||
y: 1px;
|
||||
background: root.subtle ? transparent : root.enabled ? #9bc9f8 : #d7d9dc;
|
||||
border-top-right-radius: UiRadius.control - 1px;
|
||||
border-bottom-right-radius: UiRadius.control - 1px;
|
||||
Text {
|
||||
text: FontAwesomeIcons.angle-down;
|
||||
color: root.subtle ? #79879a : #202124;
|
||||
@@ -223,7 +228,7 @@ export component CompactComboBox inherits Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
TouchArea {
|
||||
combo-touch := TouchArea {
|
||||
enabled: root.enabled; mouse-cursor: pointer;
|
||||
clicked => {
|
||||
root.popup-animation-enabled = false;
|
||||
@@ -263,8 +268,8 @@ export component CompactComboBox inherits Rectangle {
|
||||
opacity: root.popup-progress;
|
||||
background: white;
|
||||
border-width: 1px;
|
||||
border-color: root.subtle ? #dfe6ef : #bfc2c7;
|
||||
border-radius: root.subtle ? 6px : 3px;
|
||||
border-color: root.normal-border-color;
|
||||
border-radius: UiRadius.menu;
|
||||
clip: true;
|
||||
for choice[index] in root.model: Rectangle {
|
||||
y: index * 23px;
|
||||
@@ -288,8 +293,8 @@ export component CompactComboBox inherits Rectangle {
|
||||
height: parent.height;
|
||||
background: transparent;
|
||||
border-width: 1px;
|
||||
border-color: root.subtle ? #dfe6ef : #bfc2c7;
|
||||
border-radius: root.subtle ? 6px : 3px;
|
||||
border-color: root.normal-border-color;
|
||||
border-radius: UiRadius.menu;
|
||||
z: 2;
|
||||
}
|
||||
}
|
||||
@@ -361,7 +366,7 @@ export component CompactButton inherits Rectangle {
|
||||
: root.primary ? #a8d0fa : root.quiet ? transparent : #e2e4e7;
|
||||
border-width: 1px;
|
||||
border-color: root.primary ? #9bc3ed : root.quiet ? transparent : #c8cbd0;
|
||||
border-radius: 3px;
|
||||
border-radius: UiRadius.control;
|
||||
Text {
|
||||
text: root.text;
|
||||
color: root.enabled ? #24272d : #8e9298;
|
||||
@@ -385,7 +390,7 @@ export component PrimaryButton inherits Rectangle {
|
||||
: touch.pressed ? #1849a9
|
||||
: touch.has-hover ? #2970d6
|
||||
: #2463c7;
|
||||
border-radius: 5px;
|
||||
border-radius: UiRadius.control;
|
||||
|
||||
Text {
|
||||
text: root.text;
|
||||
@@ -414,7 +419,7 @@ export component SecondaryButton inherits Rectangle {
|
||||
: touch.has-hover ? #f3f4f6 : white;
|
||||
border-width: 1px;
|
||||
border-color: #c9ced7;
|
||||
border-radius: 5px;
|
||||
border-radius: UiRadius.control;
|
||||
clip: true;
|
||||
|
||||
Text {
|
||||
@@ -471,5 +476,5 @@ export component DialogSurface inherits Rectangle {
|
||||
background: white;
|
||||
border-width: 1px;
|
||||
border-color: ImGuiLineStyle.border;
|
||||
border-radius: 8px;
|
||||
border-radius: UiRadius.surface;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { IconButton, FontAwesomeIcons } from "common.slint";
|
||||
import { GalleryStrings } from "device_strings.slint";
|
||||
|
||||
@@ -12,6 +13,7 @@ export global DeviceCardStyle {
|
||||
|
||||
component CardAction inherits IconButton {
|
||||
width: 26px; height: 23px;
|
||||
border-radius: UiRadius.control;
|
||||
icon-size: 12px; icon-color: white;
|
||||
hover-background: #ffffff2e;
|
||||
pressed-background: #598cf273;
|
||||
@@ -28,7 +30,7 @@ export component DeviceCardActions inherits Rectangle {
|
||||
callback connect-device();
|
||||
callback disconnect-device();
|
||||
width: DeviceCardStyle.toolbar-width; height: 23px;
|
||||
background: #4a4e55dc; border-radius: 5px;
|
||||
background: #4a4e55dc; border-radius: UiRadius.control;
|
||||
HorizontalLayout {
|
||||
spacing: 0px;
|
||||
CardAction {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { ScrollView, LineEdit } from "std-widgets.slint";
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { ScrollView } from "std-widgets.slint";
|
||||
import { RoundedLineEdit } from "rounded_line_edit.slint";
|
||||
import { CompactButton, ImGuiCheckBox } from "common.slint";
|
||||
import { GalleryStrings } from "device_strings.slint";
|
||||
|
||||
@@ -23,7 +25,7 @@ export component DeviceGroupDialog inherits Rectangle {
|
||||
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: 8px;
|
||||
background: white; border-radius: UiRadius.surface;
|
||||
border-width: 1px; border-color: #b2b2b2;
|
||||
TouchArea {}
|
||||
Text {
|
||||
@@ -42,7 +44,8 @@ export component DeviceGroupDialog inherits Rectangle {
|
||||
group-list := Rectangle {
|
||||
x: 18px; y: 95px; width: parent.width - 36px;
|
||||
height: min(5, max(1, root.options.length)) * 32px + 2px;
|
||||
background: #fbfcfd; border-width: 1px; border-color: #e1e5eb; border-radius: 5px;
|
||||
background: #fbfcfd; border-width: 1px; border-color: #e1e5eb; border-radius: UiRadius.surface;
|
||||
clip: true;
|
||||
ScrollView {
|
||||
x: 1px; y: 1px; width: parent.width - 2px; height: parent.height - 2px;
|
||||
viewport-width: self.width;
|
||||
@@ -64,8 +67,21 @@ export component DeviceGroupDialog inherits Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Keep row hover backgrounds inside the rounded list on every renderer.
|
||||
Rectangle {
|
||||
x: -UiRadius.surface; y: -UiRadius.surface;
|
||||
width: parent.width + 2 * UiRadius.surface;
|
||||
height: parent.height + 2 * UiRadius.surface;
|
||||
border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface;
|
||||
border-color: white;
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width; height: parent.height;
|
||||
border-width: 1px; border-radius: UiRadius.surface;
|
||||
border-color: #e1e5eb;
|
||||
}
|
||||
}
|
||||
LineEdit {
|
||||
RoundedLineEdit {
|
||||
x: 18px; y: group-list.y + group-list.height + 12px;
|
||||
width: parent.width - 36px; height: 30px;
|
||||
text <=> root.new-group;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { CompactButton, FontAwesomeIcons } from "common.slint";
|
||||
|
||||
export struct GalleryMenuItem { label: string, enabled: bool }
|
||||
@@ -61,6 +62,7 @@ export component GalleryMenuButton inherits Rectangle {
|
||||
key-pressed(event) => { return root.handle-key(event); }
|
||||
CompactButton {
|
||||
width: parent.width; height: parent.height;
|
||||
border-radius: UiRadius.control;
|
||||
enabled: root.enabled; primary: root.primary;
|
||||
clicked => { trigger-focus.focus(); root.open-menu(); }
|
||||
}
|
||||
@@ -86,10 +88,10 @@ export component GalleryMenuButton inherits Rectangle {
|
||||
popup-focus := FocusScope {
|
||||
key-pressed(event) => { return root.handle-key(event); }
|
||||
Rectangle {
|
||||
background: white; border-width: 1px; border-color: #c9cdd2; border-radius: 4px;
|
||||
background: white; border-width: 1px; border-color: #c9cdd2; border-radius: UiRadius.menu;
|
||||
for item[index] in root.items: Rectangle {
|
||||
x: 4px; y: 4px + index * 30px; width: parent.width - 8px; height: 30px;
|
||||
border-radius: 3px;
|
||||
border-radius: UiRadius.control;
|
||||
background: item.enabled && index == root.current-index ? #edf4fc : transparent;
|
||||
accessible-role: button; accessible-label: item.label; accessible-enabled: item.enabled;
|
||||
accessible-action-default => { root.choose(index); }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { GalleryStrings } from "device_strings.slint";
|
||||
import { CompactButton } from "common.slint";
|
||||
import { ScrollView } from "std-widgets.slint";
|
||||
@@ -137,7 +138,7 @@ component NetworkRow inherits Rectangle {
|
||||
}
|
||||
if !root.heading && name-hover.has-hover && device-name.preferred-width > identity.width: Rectangle {
|
||||
x: 12px; y: 32px; width: min(name-tip.preferred-width + 20px, root.width - 24px);
|
||||
height: 26px; background: #293746; border-radius: 4px; z: 10;
|
||||
height: 26px; background: #293746; border-radius: UiRadius.control; z: 10;
|
||||
name-tip := Text {
|
||||
x: 10px; width: parent.width - 20px; height: parent.height;
|
||||
text: root.device.name; font-size: 11px; color: white;
|
||||
@@ -172,7 +173,7 @@ export component GalleryNetworkDialog inherits Rectangle {
|
||||
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: 8px;
|
||||
background: white; border-radius: UiRadius.surface;
|
||||
border-width: 1px; border-color: #c6cdd5;
|
||||
TouchArea {}
|
||||
FocusScope {
|
||||
@@ -188,6 +189,7 @@ export component GalleryNetworkDialog inherits Rectangle {
|
||||
font-size: 17px; font-weight: 600; color: #293746;
|
||||
}
|
||||
CompactButton {
|
||||
border-radius: UiRadius.control;
|
||||
x: parent.width - 80px; y: 17px; width: 60px; height: 26px;
|
||||
text: GalleryStrings.tr("关闭", "Close", "Закрыть"); clicked => { root.close(); }
|
||||
}
|
||||
@@ -199,7 +201,7 @@ export component GalleryNetworkDialog inherits Rectangle {
|
||||
}
|
||||
NetworkRow {
|
||||
x: 12px; y: 78px; width: parent.width - 38px; height: 30px;
|
||||
heading: true; background: #f3f5f8; border-radius: 4px;
|
||||
heading: true; background: #f3f5f8; border-radius: UiRadius.control;
|
||||
device: {
|
||||
name: GalleryStrings.tr("设备 / ID", "Device / ID", "Устройство / ID"),
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { GalleryStrings } from "device_strings.slint";
|
||||
import { SettingsGroup, SettingsRow, SettingsSelect } from "settings_controls.slint";
|
||||
|
||||
@@ -13,6 +14,7 @@ export component GallerySettings inherits Rectangle {
|
||||
label: GalleryStrings.tr("分辨率上限", "Resolution limit", "Разрешение");
|
||||
label-reserve: self.width - 112px;
|
||||
SettingsSelect {
|
||||
border-radius: UiRadius.control;
|
||||
x: 136px; y: 4px; width: parent.width - 150px;
|
||||
current-index <=> root.resolution-index;
|
||||
model: [GalleryStrings.tr("自动", "Auto", "Авто"),
|
||||
@@ -24,6 +26,7 @@ export component GallerySettings inherits Rectangle {
|
||||
label: GalleryStrings.tr("帧率", "Frame rate", "Частота кадров");
|
||||
label-reserve: self.width - 112px;
|
||||
SettingsSelect {
|
||||
border-radius: UiRadius.control;
|
||||
x: 136px; y: 4px; width: parent.width - 150px;
|
||||
current-index <=> root.frame-rate-index;
|
||||
model: [GalleryStrings.tr("跟随设备设置", "Follow device settings", "Настройки устройства"), "15 FPS", "30 FPS", "60 FPS"];
|
||||
@@ -33,22 +36,15 @@ export component GallerySettings inherits Rectangle {
|
||||
label: GalleryStrings.tr("视频总带宽", "Video budget", "Бюджет видео");
|
||||
label-reserve: self.width - 112px; divider: false;
|
||||
SettingsSelect {
|
||||
border-radius: UiRadius.control;
|
||||
x: 136px; y: 4px; width: parent.width - 150px;
|
||||
current-index <=> root.bandwidth-index;
|
||||
model: ["2 Mbps", "5 Mbps", "10 Mbps", "20 Mbps", "50 Mbps", "100 Mbps"];
|
||||
}
|
||||
}
|
||||
}
|
||||
Text {
|
||||
x: 2px; y: 144px; width: parent.width - 4px; height: 70px;
|
||||
text: GalleryStrings.tr(
|
||||
"可见设备共享视频预算,优先保障展开画面;不可见设备暂停视频。音频与文件传输不计入,旧版设备不支持限额。瞬时速率可能超过预算。",
|
||||
"Visible devices share the video budget, with priority for expanded views. Hidden video pauses. Audio, files and older devices are excluded; brief bursts may exceed the budget.",
|
||||
"Бюджет видео делится между видимыми устройствами с приоритетом крупного вида. Скрытое видео приостановлено. Звук, файлы и старые устройства не учитываются; возможны краткие превышения.");
|
||||
color: #718096; font-size: 11px; wrap: word-wrap;
|
||||
}
|
||||
if root.error != "": Text {
|
||||
x: 2px; y: 224px; width: parent.width - 4px; height: 32px;
|
||||
x: 2px; y: 144px; width: parent.width - 4px; height: 32px;
|
||||
text: root.error; color: #c53e3e; font-size: 11px; wrap: word-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { GalleryStrings } from "device_strings.slint";
|
||||
import { RemoteCursor } from "remote_cursor.slint";
|
||||
import { DeviceCardActions, DeviceCardStyle } from "device_card_actions.slint";
|
||||
import { GalleryMenuButton } from "gallery_menu_button.slint";
|
||||
import { GroupFilter } from "group_filter.slint";
|
||||
import { ScrollView, LineEdit } from "std-widgets.slint";
|
||||
import { ScrollView } from "std-widgets.slint";
|
||||
import { RoundedLineEdit } from "rounded_line_edit.slint";
|
||||
import { CompactButton, SecondaryButton, IconButton, FontAwesomeIcons, ImGuiCheckBox } from "common.slint";
|
||||
|
||||
component GalleryToolbarButton inherits CompactButton {
|
||||
component GalleryButton inherits CompactButton {
|
||||
border-radius: UiRadius.control;
|
||||
}
|
||||
|
||||
component GalleryToolbarButton inherits GalleryButton {
|
||||
in property <string> alternate-text;
|
||||
in property <length> horizontal-padding: 12px;
|
||||
// Reserve both labels so toggling a mode never shifts adjacent controls.
|
||||
@@ -487,7 +493,7 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
if root.batch && !root.simplified: Rectangle {
|
||||
x: parent.width - 27px; y: parent.height - 27px;
|
||||
width: 18px; height: 18px; border-radius: 5px; border-width: 1px;
|
||||
width: 18px; height: 18px; border-radius: 4px; border-width: 1px;
|
||||
background: tile.selected ? (tile-touch.pressed ? #5c9fdf : tile-touch.has-hover ? #659fe0 : #6dabea) : tile-touch.has-hover ? #f3f8fe : white;
|
||||
border-color: tile.selected ? #6dabea : tile-touch.has-hover ? #76aeea : #b7bec8;
|
||||
animate background, border-color { duration: 120ms; }
|
||||
@@ -560,14 +566,14 @@ export component GalleryView inherits Rectangle {
|
||||
height: footer.y - 12px - self.y;
|
||||
HorizontalLayout {
|
||||
x: 0px; y: 0px; width: parent.width - root.view-controls-space; height: 24px; spacing: 8px;
|
||||
CompactButton { width: 52px; text: GalleryStrings.tr("返回", "Back", "Назад"); clicked => { root.action("focus", ""); } }
|
||||
GalleryButton { width: 52px; text: GalleryStrings.tr("返回", "Back", "Назад"); clicked => { root.action("focus", ""); } }
|
||||
Rectangle {
|
||||
horizontal-stretch: 1; min-width: 0px;
|
||||
Text { x: 0px; width: parent.width; height: parent.height; text: root.focused.name; color: root.focused.online ? #303943 : DeviceCardStyle.offline-text; font-size: 12px; font-weight: 600; overflow: elide; horizontal-alignment: left; vertical-alignment: center; }
|
||||
}
|
||||
Rectangle {
|
||||
width: min(focus-state.preferred-width + 16px, 110px); height: 22px;
|
||||
border-radius: 5px;
|
||||
border-radius: UiRadius.control;
|
||||
background: root.controlling ? #e7f1fd : #f0f2f5;
|
||||
focus-state := Text {
|
||||
x: 8px; width: parent.width - 16px; height: parent.height;
|
||||
@@ -589,12 +595,12 @@ export component GalleryView inherits Rectangle {
|
||||
accessible-action-default => { self.clicked(); }
|
||||
clicked => { root.action(root.group-controlling ? "group-release" : "group-control", root.focused.id); }
|
||||
}
|
||||
CompactButton { width: 100px; primary: true; enabled: !root.focused.connected || root.controlling || (root.focused.has-frame && !root.focused.stale); text: root.focused.connected ? (root.controlling ? GalleryStrings.tr("释放操控", "Release", "Отпустить") : GalleryStrings.tr("接管操控", "Control", "Управлять")) : GalleryStrings.tr("连接", "Connect", "Подключить"); clicked => { root.action(root.focused.connected ? (root.controlling ? "release" : "control") : "connect", root.focused.id); } }
|
||||
GalleryButton { width: 100px; primary: true; enabled: !root.focused.connected || root.controlling || (root.focused.has-frame && !root.focused.stale); text: root.focused.connected ? (root.controlling ? GalleryStrings.tr("释放操控", "Release", "Отпустить") : GalleryStrings.tr("接管操控", "Control", "Управлять")) : GalleryStrings.tr("连接", "Connect", "Подключить"); clicked => { root.action(root.focused.connected ? (root.controlling ? "release" : "control") : "connect", root.focused.id); } }
|
||||
}
|
||||
Rectangle {
|
||||
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: 6px;
|
||||
background: root.focused.online ? #eff1f4 : DeviceCardStyle.offline-background; border-radius: UiRadius.surface;
|
||||
border-width: root.controlling ? 2px : 1px; border-color: root.controlling ? #76aeea : root.focused.online ? #d5dbe3 : DeviceCardStyle.offline-border;
|
||||
video := Rectangle {
|
||||
x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px;
|
||||
@@ -666,7 +672,7 @@ export component GalleryView inherits Rectangle {
|
||||
private property <string> presence: tile.online ? GalleryStrings.tr("在线", "Online", "В сети")
|
||||
: tile.presence-known ? GalleryStrings.tr("离线", "Offline", "Не в сети") : GalleryStrings.tr("未知", "Unknown", "Неизвестно");
|
||||
private property <string> details: tile.name + "\n" + tile.host-name + "\nID: " + tile.id + "\n" + tile.group-names + "\n" + self.presence;
|
||||
width: root.rail-preview-width + 10px; height: root.rail-card-height; border-radius: 5px;
|
||||
width: root.rail-preview-width + 10px; height: root.rail-card-height; border-radius: UiRadius.surface;
|
||||
background: tile.online ? #f5f6f8 : DeviceCardStyle.offline-background; border-width: 1px; border-color: tile.online ? #d5dbe3 : DeviceCardStyle.offline-border;
|
||||
accessible-role: button;
|
||||
accessible-label: self.details;
|
||||
@@ -696,7 +702,8 @@ export component GalleryView inherits Rectangle {
|
||||
if self.has-hover { self.show-details(); }
|
||||
else if root.device-tooltip-id == tile.id { root.device-tooltip-id = ""; }
|
||||
}
|
||||
moved => { if self.has-hover { self.show-details(); } }
|
||||
changed mouse-x => { if self.has-hover { self.show-details(); } }
|
||||
changed mouse-y => { if self.has-hover { self.show-details(); } }
|
||||
clicked => { root.device-tooltip-id = ""; root.action("focus", tile.id); }
|
||||
}
|
||||
}
|
||||
@@ -786,7 +793,7 @@ export component GalleryView inherits Rectangle {
|
||||
network-button := SecondaryButton {
|
||||
x: connection-count.x + connection-count.width + 12px;
|
||||
min-width: 64px; width: 64px; height: parent.height;
|
||||
border-color: #c8cbd0; border-radius: 3px;
|
||||
border-color: #c8cbd0; border-radius: UiRadius.control;
|
||||
text: GalleryStrings.tr("网络状态", "Network", "Сеть");
|
||||
accessible-role: button;
|
||||
accessible-label: self.text;
|
||||
@@ -796,9 +803,9 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
pager := HorizontalLayout {
|
||||
x: parent.width - self.width; y: 0px; width: 112px; height: 24px; spacing: 6px;
|
||||
previous-page := CompactButton { primary: root.drag-page-direction < 0; width: 24px; text: "‹"; text-size: 16px; enabled: root.page > 0; clicked => { root.action("page", "previous"); } }
|
||||
previous-page := GalleryButton { primary: root.drag-page-direction < 0; width: 24px; text: "‹"; text-size: 16px; enabled: root.page > 0; clicked => { root.action("page", "previous"); } }
|
||||
Text { text: (root.page + 1) + " / " + root.pages; min-width: 0px; overflow: elide; horizontal-alignment: center; vertical-alignment: center; }
|
||||
next-page := CompactButton { primary: root.drag-page-direction > 0; width: 24px; text: "›"; text-size: 16px; enabled: root.page + 1 < root.pages; clicked => { root.action("page", "next"); } }
|
||||
next-page := GalleryButton { primary: root.drag-page-direction > 0; width: 24px; text: "›"; text-size: 16px; enabled: root.page + 1 < root.pages; clicked => { root.action("page", "next"); } }
|
||||
}
|
||||
}
|
||||
// Paint the fullscreen tooltip above the grid/rail, below modal overlays.
|
||||
@@ -815,6 +822,7 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
IconButton {
|
||||
width: 24px; height: 24px;
|
||||
border-radius: UiRadius.control;
|
||||
icon: root.fullscreen ? FontAwesomeIcons.compress : FontAwesomeIcons.expand;
|
||||
tooltip: root.fullscreen ? GalleryStrings.tr("退出全屏", "Exit fullscreen", "Выйти из полноэкранного режима") : GalleryStrings.tr("全屏", "Fullscreen", "На весь экран");
|
||||
clicked => { root.toggle-fullscreen(); }
|
||||
@@ -836,7 +844,7 @@ export component GalleryView inherits Rectangle {
|
||||
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: 54px; z: 10;
|
||||
background: #fffffff2; border-width: 1px; border-color: #76aeea; border-radius: 4px;
|
||||
background: #fffffff2; border-width: 1px; border-color: #76aeea; border-radius: UiRadius.surface;
|
||||
Rectangle {
|
||||
x: 8px; y: 9px; width: 64px; height: 36px;
|
||||
background: root.dragged-tile.online ? #24272d : DeviceCardStyle.offline-preview;
|
||||
@@ -857,7 +865,7 @@ export component GalleryView inherits Rectangle {
|
||||
if root.swap-save-failed: Rectangle {
|
||||
x: (parent.width - self.width) / 2; y: footer.y - self.height - 14px;
|
||||
width: min(420px, parent.width - 32px); height: 36px; z: 10;
|
||||
background: #fff2f0; border-width: 1px; border-color: #e9b7b0; border-radius: 4px;
|
||||
background: #fff2f0; border-width: 1px; border-color: #e9b7b0; 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;
|
||||
}
|
||||
@@ -867,7 +875,7 @@ export component GalleryView inherits Rectangle {
|
||||
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; border-width: 1px; border-color: #ffffff38; border-radius: 5px;
|
||||
background: #22252bf2; border-width: 1px; border-color: #ffffff38; 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;
|
||||
@@ -878,7 +886,7 @@ export component GalleryView inherits Rectangle {
|
||||
TouchArea {}
|
||||
add-dialog := Rectangle {
|
||||
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 400px; height: 306px;
|
||||
background: white; border-radius: 8px; border-width: 1px; border-color: #b2b2b2;
|
||||
background: white; border-radius: UiRadius.surface; border-width: 1px; border-color: #b2b2b2;
|
||||
function submit() {
|
||||
if root.add-device(device-id.text, password.text, group.text, remember.checked) {
|
||||
password.text = "";
|
||||
@@ -887,21 +895,21 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.tr("添加设备", "Add device", "Добавить устройство"); font-size: 16px; }
|
||||
Text { x: 18px; y: 56px; width: 72px; height: 30px; text: GalleryStrings.tr("设备 ID", "Device ID", "ID"); vertical-alignment: center; font-size: 12px; }
|
||||
device-id := LineEdit {
|
||||
device-id := RoundedLineEdit {
|
||||
x: 96px; y: 56px; width: 286px; height: 30px;
|
||||
placeholder-text: GalleryStrings.tr("9 位设备 ID", "9-digit device ID", "ID из 9 цифр");
|
||||
input-type: InputType.number;
|
||||
accepted => { password.focus(); }
|
||||
}
|
||||
Text { x: 18px; y: 100px; width: 72px; height: 30px; text: GalleryStrings.tr("密码", "Password", "Пароль"); vertical-alignment: center; font-size: 12px; }
|
||||
password := LineEdit {
|
||||
password := RoundedLineEdit {
|
||||
x: 96px; y: 100px; width: 286px; height: 30px;
|
||||
placeholder-text: GalleryStrings.tr("设备连接密码", "Device password", "Пароль устройства");
|
||||
input-type: InputType.password;
|
||||
accepted => { group.focus(); }
|
||||
}
|
||||
Text { x: 18px; y: 144px; width: 72px; height: 30px; text: GalleryStrings.tr("分组", "Group", "Группа"); vertical-alignment: center; font-size: 12px; }
|
||||
group := LineEdit {
|
||||
group := RoundedLineEdit {
|
||||
x: 96px; y: 144px; width: 286px; height: 30px;
|
||||
placeholder-text: GalleryStrings.tr("分组名称(可选)", "Group name (optional)", "Группа (необязательно)");
|
||||
accepted => { add-dialog.submit(); }
|
||||
@@ -911,8 +919,8 @@ export component GalleryView inherits Rectangle {
|
||||
TouchArea { clicked => { remember.checked = !remember.checked; } }
|
||||
}
|
||||
Text { x: 18px; y: 216px; width: 364px; height: 28px; text: root.notice; color: #8e4740; font-size: 11px; wrap: word-wrap; overflow: elide; }
|
||||
CompactButton { x: 172px; y: 260px; width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { password.text = ""; root.adding = false; } }
|
||||
CompactButton {
|
||||
GalleryButton { x: 172px; y: 260px; width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { password.text = ""; root.adding = false; } }
|
||||
GalleryButton {
|
||||
x: 254px; y: 260px; width: 128px; height: 28px; primary: true;
|
||||
text: GalleryStrings.tr("添加并连接", "Add and connect", "Подключить");
|
||||
enabled: device-id.text != "" && password.text != "";
|
||||
@@ -925,7 +933,7 @@ export component GalleryView inherits Rectangle {
|
||||
TouchArea {}
|
||||
Rectangle {
|
||||
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 440px; height: 378px;
|
||||
background: white; border-radius: 8px; border-width: 1px; border-color: #b2b2b2;
|
||||
background: white; border-radius: UiRadius.surface; border-width: 1px; border-color: #b2b2b2;
|
||||
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 devices, then use Select to connect them.", "После добавления подключайтесь через выбор."); 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; }
|
||||
@@ -934,7 +942,7 @@ export component GalleryView inherits Rectangle {
|
||||
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; }
|
||||
Rectangle {
|
||||
x: 18px; y: 102px; width: 404px; height: 160px;
|
||||
border-width: 1px; border-color: #d8dadd; border-radius: 4px; clip: true;
|
||||
border-width: 1px; border-color: #d8dadd; 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;
|
||||
@@ -955,12 +963,24 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
}
|
||||
if root.recent-devices.length == 0: Text { text: GalleryStrings.tr("暂无最近连接", "No recent connections", "Нет недавних подключений"); color: #808080; font-size: 12px; horizontal-alignment: center; vertical-alignment: center; }
|
||||
Rectangle {
|
||||
x: -UiRadius.surface; y: -UiRadius.surface;
|
||||
width: parent.width + 2 * UiRadius.surface;
|
||||
height: parent.height + 2 * UiRadius.surface;
|
||||
border-width: UiRadius.surface; border-radius: 2 * UiRadius.surface;
|
||||
border-color: white;
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width; height: parent.height;
|
||||
border-width: 1px; border-radius: UiRadius.surface;
|
||||
border-color: #d8dadd;
|
||||
}
|
||||
}
|
||||
Text { x: 18px; y: 274px; width: 60px; height: 30px; text: GalleryStrings.tr("分组", "Group", "Группа"); font-size: 12px; vertical-alignment: center; }
|
||||
import-group := LineEdit { x: 84px; y: 274px; width: 338px; height: 30px; text: root.import-group; placeholder-text: GalleryStrings.tr("分组名称(可选)", "Group name (optional)", "Группа (необязательно)"); }
|
||||
import-group := RoundedLineEdit { x: 84px; y: 274px; width: 338px; height: 30px; text: root.import-group; placeholder-text: GalleryStrings.tr("分组名称(可选)", "Group name (optional)", "Группа (необязательно)"); }
|
||||
Text { x: 18px; y: 309px; width: 404px; height: 20px; text: root.notice; font-size: 11px; color: #8e4740; overflow: elide; }
|
||||
CompactButton { x: 192px; y: 336px; width: 72px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.importing = false; } }
|
||||
CompactButton { x: 274px; y: 336px; width: 148px; primary: true; text: GalleryStrings.tr("加入桌面墙", "Add selected", "Добавить"); enabled: root.recent-selected > 0; clicked => { if root.import-recent(import-group.text) { root.importing = false; } } }
|
||||
GalleryButton { x: 192px; y: 336px; width: 72px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.importing = false; } }
|
||||
GalleryButton { x: 274px; y: 336px; width: 148px; primary: true; text: GalleryStrings.tr("加入桌面墙", "Add selected", "Добавить"); enabled: root.recent-selected > 0; clicked => { if root.import-recent(import-group.text) { root.importing = false; } } }
|
||||
}
|
||||
}
|
||||
if root.confirming: Rectangle {
|
||||
@@ -968,7 +988,7 @@ export component GalleryView inherits Rectangle {
|
||||
Rectangle {
|
||||
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2;
|
||||
width: 360px; height: confirmation-list.height + 118px;
|
||||
background: white; border-radius: 8px; border-width: 1px; border-color: #b2b2b2;
|
||||
background: white; border-radius: UiRadius.surface; border-width: 1px; border-color: #b2b2b2;
|
||||
Text {
|
||||
x: 20px; y: 18px; width: parent.width - 132px; height: 24px;
|
||||
text: root.action-code == "disconnect" ? GalleryStrings.tr("断开连接", "Disconnect devices", "Отключение") : root.action-name;
|
||||
@@ -976,13 +996,13 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
Rectangle {
|
||||
x: parent.width - 108px; y: 20px; width: 88px; height: 22px;
|
||||
background: #eff3f8; border-radius: 11px;
|
||||
background: #eff3f8; border-radius: UiRadius.surface;
|
||||
Text { text: root.selected-count + GalleryStrings.tr(" 台设备", " devices", " устр."); color: #626e7d; font-size: 11px; horizontal-alignment: center; vertical-alignment: center; }
|
||||
}
|
||||
confirmation-list := Rectangle {
|
||||
x: 20px; y: 58px; width: parent.width - 40px;
|
||||
height: clamp(root.selected-devices.length * 52px + 2px, 54px, 210px);
|
||||
background: #fcfdfe; border-width: 1px; border-color: #e2e6eb; border-radius: 6px; clip: true;
|
||||
background: #fcfdfe; border-width: 1px; border-color: #e2e6eb; 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;
|
||||
@@ -1002,8 +1022,8 @@ export component GalleryView inherits Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
CompactButton { x: parent.width - 208px; y: parent.height - 46px; width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.confirming = false; } }
|
||||
CompactButton {
|
||||
GalleryButton { x: parent.width - 208px; y: parent.height - 46px; width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.confirming = false; } }
|
||||
GalleryButton {
|
||||
x: parent.width - 126px; y: parent.height - 46px; width: 108px; height: 28px; primary: true;
|
||||
text: root.action-code == "disconnect" ? GalleryStrings.tr("确认断开", "Disconnect", "Отключить") : GalleryStrings.tr("确认执行", "Continue", "Продолжить");
|
||||
clicked => { root.confirming = false; root.action(root.action-code, ""); }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { ScrollView } from "std-widgets.slint";
|
||||
import { FontAwesomeIcons } from "common.slint";
|
||||
|
||||
@@ -6,6 +7,9 @@ export component GroupFilter inherits Rectangle {
|
||||
in property <[string]> model;
|
||||
in property <int> system-count: 2;
|
||||
in property <bool> enabled: true;
|
||||
in property <color> normal-border-color: #c9cdd2;
|
||||
in property <color> hover-border-color: root.normal-border-color;
|
||||
in property <color> focus-border-color: #8db5df;
|
||||
in-out property <int> current-index: 0;
|
||||
callback selected(int);
|
||||
private property <bool> has-divider: root.system-count > 0 && root.model.length > root.system-count;
|
||||
@@ -23,8 +27,9 @@ export component GroupFilter inherits Rectangle {
|
||||
width: 155px; height: 28px;
|
||||
background: !root.enabled ? #f3f4f5 : trigger-touch.has-hover ? #f8f9fb : white;
|
||||
border-width: 1px;
|
||||
border-color: popup.is-open || trigger-focus.has-focus ? #8db5df : #c9cdd2;
|
||||
border-radius: 4px;
|
||||
border-color: popup.is-open || trigger-focus.has-focus ? root.focus-border-color
|
||||
: trigger-touch.has-hover ? root.hover-border-color : root.normal-border-color;
|
||||
border-radius: UiRadius.control;
|
||||
animate background, border-color { duration: 100ms; easing: ease-out; }
|
||||
forward-focus: trigger-focus;
|
||||
accessible-role: combobox;
|
||||
@@ -117,7 +122,7 @@ export component GroupFilter inherits Rectangle {
|
||||
width: parent.width; height: parent.height;
|
||||
opacity: root.popup-progress;
|
||||
background: white; border-width: 1px;
|
||||
border-color: #c9cdd2; border-radius: 5px;
|
||||
border-color: root.normal-border-color; border-radius: UiRadius.menu;
|
||||
ScrollView {
|
||||
x: 4px; y: 4px; width: parent.width - 8px; height: parent.height - 8px;
|
||||
viewport-width: self.width;
|
||||
@@ -131,7 +136,7 @@ export component GroupFilter inherits Rectangle {
|
||||
}
|
||||
for choice[index] in root.model: Rectangle {
|
||||
y: index * 26px + (root.has-divider && index >= root.system-count ? 6px : 0px);
|
||||
width: parent.width; height: 26px; border-radius: 3px;
|
||||
width: parent.width; height: 26px; border-radius: UiRadius.control;
|
||||
background: index == root.current-index ? #edf4fc : option-touch.has-hover ? #f1f3f5 : transparent;
|
||||
animate background { duration: 100ms; easing: ease-out; }
|
||||
accessible-role: list-item;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { ScrollView } from "std-widgets.slint";
|
||||
import { CompactButton, DialogSurface, IconButton, FontAwesomeIcons, ImGuiFontStyle } from "common.slint";
|
||||
|
||||
@@ -68,12 +69,11 @@ export component MessagePanel inherits DialogSurface {
|
||||
}
|
||||
}
|
||||
background: white;
|
||||
border-radius: 7px;
|
||||
border-radius: UiRadius.surface;
|
||||
TouchArea { }
|
||||
title-bar := Rectangle {
|
||||
x: 0px; y: 0px; width: parent.width; height: 21px;
|
||||
background: #d3d3d3; border-radius: 7px;
|
||||
Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; }
|
||||
background: #d3d3d3; border-top-left-radius: UiRadius.surface; border-top-right-radius: UiRadius.surface;
|
||||
message-heading := Text {
|
||||
x: 6px; width: parent.width - 100px; height: parent.height;
|
||||
text: MessageStrings.messages;
|
||||
@@ -101,7 +101,7 @@ export component MessagePanel inherits DialogSurface {
|
||||
}
|
||||
IconButton {
|
||||
x: parent.width - 25px; y: 2px; width: 19px; height: parent.height - 4px;
|
||||
border-radius: 3px;
|
||||
border-radius: UiRadius.control;
|
||||
icon: FontAwesomeIcons.xmark; icon-size: ImGuiFontStyle.base;
|
||||
icon-color: #202124;
|
||||
hover-background: #bfc2c7; pressed-background: #b2b2b2;
|
||||
@@ -113,7 +113,7 @@ export component MessagePanel inherits DialogSurface {
|
||||
}
|
||||
if root.notice-text != "": Rectangle {
|
||||
x: 16px; y: title-bar.height + 10px; width: parent.width - 32px; height: 28px;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.surface;
|
||||
background: #fef2f2;
|
||||
Text {
|
||||
x: 8px; width: parent.width - 16px; height: parent.height;
|
||||
@@ -133,7 +133,7 @@ export component MessagePanel inherits DialogSurface {
|
||||
for item[index] in root.items: Rectangle {
|
||||
height: 54px;
|
||||
background: root.selected-id == item.id ? #eaf2ff : entry-touch.has-hover ? #f0f1f2 : transparent;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
Text { x: 8px; y: 6px; width: parent.width - 32px; height: 18px; text: item.title; overflow: elide; font-size: ImGuiFontStyle.base; font-weight: item.read ? 400 : 600; color: #202124; vertical-alignment: center; }
|
||||
if !item.read: Rectangle {
|
||||
x: parent.width - 17px; y: 13px; width: 5px; height: 5px;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { LineEdit } from "std-widgets.slint";
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
|
||||
// Retain the standard editor's selection, clipboard, clear button and input
|
||||
// method support. Only its outer frame is replaced by the shared shape.
|
||||
export component RoundedLineEdit inherits Rectangle {
|
||||
in-out property <string> text;
|
||||
in property <string> placeholder-text;
|
||||
in property <InputType> input-type: InputType.text;
|
||||
in property <bool> enabled: true;
|
||||
in property <length> font-size: 12px;
|
||||
in property <color> normal-border-color: #dfe6ef;
|
||||
in property <brush> surrounding-background: white;
|
||||
callback accepted();
|
||||
callback edited();
|
||||
height: 30px;
|
||||
border-radius: UiRadius.control;
|
||||
background: white;
|
||||
clip: true;
|
||||
forward-focus: editor;
|
||||
|
||||
editor := LineEdit {
|
||||
x: -1px; y: -1px;
|
||||
width: parent.width + 2px; height: parent.height + 2px;
|
||||
text <=> root.text;
|
||||
placeholder-text: root.placeholder-text;
|
||||
input-type: root.input-type;
|
||||
enabled: root.enabled;
|
||||
font-size: root.font-size;
|
||||
accepted => { root.accepted(); }
|
||||
edited => { root.edited(); }
|
||||
}
|
||||
// The software renderer clips children to rectangular bounds. Mask the
|
||||
// standard editor's corners before drawing our rounded border on top.
|
||||
Rectangle {
|
||||
x: -root.border-radius; y: -root.border-radius;
|
||||
width: parent.width + 2 * root.border-radius;
|
||||
height: parent.height + 2 * root.border-radius;
|
||||
border-width: root.border-radius;
|
||||
border-radius: 2 * root.border-radius;
|
||||
border-color: root.surrounding-background;
|
||||
}
|
||||
Rectangle {
|
||||
width: parent.width; height: parent.height;
|
||||
border-radius: root.border-radius;
|
||||
border-width: editor.has-focus ? 2px : 1px;
|
||||
border-color: editor.has-focus ? #3564e3 : root.normal-border-color;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { ScrollView } from "std-widgets.slint";
|
||||
import { FontAwesomeIcons, ImGuiLineStyle, FloatingBarStyle } from "common.slint";
|
||||
|
||||
@@ -27,7 +28,7 @@ component SessionButton inherits Rectangle {
|
||||
callback clicked;
|
||||
width: label.preferred-width + 20px;
|
||||
height: 28px;
|
||||
border-radius: 5px;
|
||||
border-radius: UiRadius.control;
|
||||
border-width: 1px;
|
||||
border-color: root.danger ? #d92d20 : #c9ced7;
|
||||
background: root.danger ? (root.pointer-feedback && touch.pressed ? #b42318 : root.pointer-feedback && touch.has-hover ? #f04438 : #d92d20)
|
||||
@@ -73,7 +74,7 @@ component SessionButton inherits Rectangle {
|
||||
}
|
||||
if root.enabled && keyboard.has-focus && root.keyboard-focus: Rectangle {
|
||||
x: 2px; y: 2px; width: parent.width - 4px; height: parent.height - 4px;
|
||||
border-radius: 3px; border-width: 1px; border-color: root.danger ? white : #2463c7;
|
||||
border-radius: UiRadius.control - 2px; border-width: 1px; border-color: root.danger ? white : #2463c7;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +173,7 @@ export component ServerWindow inherits Window {
|
||||
Rectangle {
|
||||
width: root.width; height: root.height;
|
||||
background: white; border-width: 1px; border-color: ImGuiLineStyle.border;
|
||||
border-radius: 8px;
|
||||
border-radius: UiRadius.surface;
|
||||
Text {
|
||||
x: 10px; y: 10px; width: parent.width - 20px; height: 24px;
|
||||
text: ServerStrings.heading + (root.controllers.length > 1 ? " · " + root.controllers.length : "");
|
||||
@@ -189,7 +190,7 @@ export component ServerWindow inherits Window {
|
||||
select := Rectangle {
|
||||
x: 0px; y: 0px;
|
||||
width: parent.width - (root.controllers.length > 1 ? disconnect.width + 4px : status.preferred-width + 8px);
|
||||
height: parent.height; border-radius: 4px;
|
||||
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: Rectangle {
|
||||
x: 4px; y: (parent.height - 13px) / 2; width: 13px; height: 13px;
|
||||
@@ -274,7 +275,7 @@ export component ServerWindow inherits Window {
|
||||
// Draw outside the scrolling list so its last row can reveal its name.
|
||||
if root.hovered-name != "": Rectangle {
|
||||
x: 10px; y: root.tooltip-y; width: 205px; height: tip.preferred-height + 12px; z: 20;
|
||||
background: #24272d; border-radius: 5px;
|
||||
background: #24272d; border-radius: UiRadius.control;
|
||||
tip := Text { x: 6px; y: 6px; width: parent.width - 12px; text: root.hovered-name; color: white; font-size: 12px; wrap: word-wrap; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { CompactComboBox, ToggleSwitch } from "common.slint";
|
||||
import { WorkbenchStyle } from "workbench_controls.slint";
|
||||
|
||||
// Shared visual treatment for the global settings pages.
|
||||
export component SettingsButton inherits Rectangle {
|
||||
@@ -8,12 +10,13 @@ export component SettingsButton inherits Rectangle {
|
||||
in property <bool> align-left: false;
|
||||
in property <bool> enabled: true;
|
||||
callback clicked();
|
||||
width: 88px; height: 32px; border-radius: 7px;
|
||||
width: 88px; height: 32px; border-radius: UiRadius.control;
|
||||
background: !root.enabled ? #f0f3f7
|
||||
: root.primary ? (touch.pressed ? #1e4fa3 : touch.has-hover ? #2866c9 : #3275dc)
|
||||
: touch.pressed ? #e9eef5 : touch.has-hover ? #f0f4fa : root.quiet ? transparent : white;
|
||||
border-width: root.quiet || root.primary ? 0px : 1px;
|
||||
border-color: #dce3ec;
|
||||
border-color: !root.enabled ? WorkbenchStyle.border
|
||||
: touch.has-hover ? WorkbenchStyle.hover-border : WorkbenchStyle.control-border;
|
||||
accessible-role: button; accessible-label: root.text;
|
||||
accessible-action-default => { if root.enabled { root.clicked(); } }
|
||||
Text {
|
||||
@@ -28,6 +31,8 @@ export component SettingsButton inherits Rectangle {
|
||||
export component SettingsSelect inherits CompactComboBox {
|
||||
subtle: true;
|
||||
show-arrow: false;
|
||||
normal-border-color: WorkbenchStyle.control-border;
|
||||
hover-border-color: WorkbenchStyle.hover-border;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
@@ -41,7 +46,7 @@ export component SettingsGroup inherits Rectangle {
|
||||
}
|
||||
Rectangle {
|
||||
y: 24px; width: parent.width; height: parent.height - self.y;
|
||||
background: white; border-radius: 9px; border-color: #e5eaf1; border-width: 1px;
|
||||
background: white; border-radius: UiRadius.surface; border-color: WorkbenchStyle.border; border-width: 1px;
|
||||
VerticalLayout { padding: 0px; spacing: 0px; @children }
|
||||
}
|
||||
}
|
||||
@@ -58,7 +63,7 @@ export component SettingsRow inherits Rectangle {
|
||||
font-size: 12px; overflow: elide; vertical-alignment: center;
|
||||
}
|
||||
if root.divider: Rectangle {
|
||||
x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; background: #eff2f6;
|
||||
x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; background: #d9e1ec;
|
||||
}
|
||||
@children
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
import { ComboBox, ScrollView } from "std-widgets.slint";
|
||||
import { RemoteCursor } from "remote_cursor.slint";
|
||||
import { FontAwesomeIcons, IconButton, PrimaryButton, DialogSurface, ImGuiLineStyle, ImGuiFontStyle, FloatingBarStyle } from "common.slint";
|
||||
@@ -98,7 +99,7 @@ component ControlBarButton inherits Rectangle {
|
||||
background: root.muted ? #eef0f3 : touch.pressed && root.action-enabled ? #0f87fa
|
||||
: touch.has-hover ? #4296fa
|
||||
: root.selected ? #4296fa : #4296fa66;
|
||||
border-radius: 3px;
|
||||
border-radius: UiRadius.control;
|
||||
|
||||
Text {
|
||||
text: root.icon;
|
||||
@@ -159,7 +160,7 @@ component VideoSettingRow inherits VerticalLayout {
|
||||
height: 27px;
|
||||
background: !root.enabled ? #f1f1f1 : root.selected == index ? #d7eaff
|
||||
: option-touch.has-hover ? #eaf3ff : #f5f6f8;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
border-width: 1px;
|
||||
border-color: root.enabled && root.selected == index ? #4296fa : #dce0e6;
|
||||
Text { text: option; color: root.enabled ? #30343b : #8b8f95;
|
||||
@@ -967,7 +968,7 @@ export component StreamWindow inherits Window {
|
||||
width: parent.width;
|
||||
height: root.stats-header-height;
|
||||
background: #f3f5f8;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
Text {
|
||||
x: stats-table.label-column-width + 4px;
|
||||
width: stats-table.value-column-width - 8px;
|
||||
@@ -1004,7 +1005,7 @@ export component StreamWindow inherits Window {
|
||||
width: parent.width;
|
||||
height: stats-table.row-height;
|
||||
background: index == root.stats-rows.length - 1 ? #edf3fc : transparent;
|
||||
border-radius: 4px;
|
||||
border-radius: UiRadius.control;
|
||||
Text {
|
||||
x: 4px;
|
||||
width: stats-table.label-column-width - 8px;
|
||||
@@ -1133,7 +1134,7 @@ export component StreamWindow inherits Window {
|
||||
x: root.video-menu-x; y: root.video-menu-y;
|
||||
width: root.video-menu-width; height: root.video-menu-height;
|
||||
background: white; border-width: 1px; border-color: ImGuiLineStyle.border;
|
||||
border-radius: 5px; z: 11;
|
||||
border-radius: UiRadius.menu; z: 11;
|
||||
TouchArea {
|
||||
pointer-event(event) => { if event.kind == PointerEventKind.down { root.suppress-next-remote-pointer-release = true; } }
|
||||
scroll-event(event) => { accept }
|
||||
@@ -1183,7 +1184,7 @@ export component StreamWindow inherits Window {
|
||||
background: white;
|
||||
border-width: 1px;
|
||||
border-color: ImGuiLineStyle.border;
|
||||
border-radius: 5px;
|
||||
border-radius: UiRadius.menu;
|
||||
clip: true;
|
||||
z: 11;
|
||||
|
||||
@@ -1195,7 +1196,7 @@ export component StreamWindow inherits Window {
|
||||
: index == display-menu.hovered-index ? #d8e6f8
|
||||
: index == root.selected-display ? #e8eef9
|
||||
: transparent;
|
||||
border-radius: 3px;
|
||||
border-radius: UiRadius.control;
|
||||
Text {
|
||||
x: 8px;
|
||||
width: parent.width - 34px;
|
||||
@@ -1246,7 +1247,7 @@ export component StreamWindow inherits Window {
|
||||
|
||||
if root.shortcut-menu-open: Rectangle {
|
||||
x: control.x + shortcut-button.x; y: control.y + 40px; width: 180px; height: 68px;
|
||||
background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 5px; z: 11;
|
||||
background: white; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: UiRadius.menu; z: 11;
|
||||
VerticalLayout {
|
||||
padding: 4px; spacing: 1px;
|
||||
for shortcut in ["Ctrl+Alt+Del", "Win+L"]: Rectangle {
|
||||
@@ -1280,7 +1281,7 @@ export component StreamWindow inherits Window {
|
||||
// Anchor to the button row so expanded stats stay beneath the tooltip.
|
||||
y: control.y + root.control-toolbar-height + self.height + 8px <= video.height
|
||||
? control.y + root.control-toolbar-height + 4px : max(4px, control.y - self.height - 4px);
|
||||
background: #30343bf5; border-radius: 5px; z: 12;
|
||||
background: #30343bf5; border-radius: UiRadius.control; z: 12;
|
||||
control-tip := Text {
|
||||
x: 8px; y: 6px; width: parent.width - 16px;
|
||||
text: root.hovered-control.text;
|
||||
@@ -1304,7 +1305,7 @@ export component StreamWindow inherits Window {
|
||||
width: min(520px, parent.width - 48px);
|
||||
height: max(208px, remote-update-message.y + remote-update-message.height + 92px);
|
||||
border-color: #e5e9f0;
|
||||
border-radius: 14px;
|
||||
border-radius: UiRadius.surface;
|
||||
drop-shadow-color: #07142b30;
|
||||
drop-shadow-blur: 32px;
|
||||
drop-shadow-offset-y: 12px;
|
||||
@@ -1320,7 +1321,7 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
Rectangle {
|
||||
x: 32px; y: 32px; width: 48px; height: 48px;
|
||||
background: #edf4ff; border-radius: 12px;
|
||||
background: #edf4ff; border-radius: UiRadius.surface;
|
||||
Path {
|
||||
x: 12px; y: 12px; width: 24px; height: 24px;
|
||||
viewbox-width: 24; viewbox-height: 24;
|
||||
@@ -1355,7 +1356,7 @@ export component StreamWindow inherits Window {
|
||||
PrimaryButton {
|
||||
x: parent.width - 136px; y: parent.height - 68px;
|
||||
width: 104px; height: 36px;
|
||||
border-radius: 7px;
|
||||
border-radius: UiRadius.control;
|
||||
text: StreamStrings.remote-update-dismiss;
|
||||
text-size: 14px;
|
||||
clicked => { root.dismiss-remote-update(); }
|
||||
@@ -1365,7 +1366,7 @@ export component StreamWindow inherits Window {
|
||||
// Transfer progress overlay at bottom-left.
|
||||
if root.file-transfer-visible: Rectangle {
|
||||
x: 18px; y: parent.height - min(210px, parent.height * 0.35); width: min(430px, parent.width * 0.6); height: min(190px, parent.height * 0.32);
|
||||
background: #fffffff0; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: 6px; z: 7;
|
||||
background: #fffffff0; border-width: 1px; border-color: ImGuiLineStyle.border; border-radius: UiRadius.surface; z: 7;
|
||||
Text { x: 12px; y: 8px; text: StreamStrings.file-transfer; color: #2d3138; font-weight: 600; }
|
||||
IconButton { x: parent.width - 32px; y: 1px; icon: FontAwesomeIcons.xmark; icon-size: 13px; clicked => { root.close-file-transfer(); } }
|
||||
ScrollView {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Logical pixels: compact controls, popup menus, and larger surfaces.
|
||||
// Circular indicators, switches, progress bars, and native window chrome
|
||||
// retain radii derived from their own geometry.
|
||||
export global UiRadius {
|
||||
out property <length> control: 6px;
|
||||
out property <length> menu: 8px;
|
||||
out property <length> surface: 12px;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { UiRadius } from "ui_radius.slint";
|
||||
// Colors and spacing for the connection workbench; shapes are shared app-wide.
|
||||
export global WorkbenchStyle {
|
||||
out property <color> canvas: #f2f5f9;
|
||||
out property <color> surface: #ffffff;
|
||||
out property <color> border: #c6cfdd;
|
||||
out property <color> control-border: #b2bfd1;
|
||||
out property <color> hover-border: #7f97b8;
|
||||
out property <color> field-surface: #f5f7fb;
|
||||
out property <color> hover-surface: #eef3fb;
|
||||
out property <color> pressed-surface: #e4ebf6;
|
||||
out property <color> separator: #e1e7ef;
|
||||
out property <color> text: #1c2940;
|
||||
out property <color> muted: #69788e;
|
||||
out property <color> accent: #3564e3;
|
||||
out property <color> success: #18835a;
|
||||
}
|
||||
|
||||
export component WorkbenchButton inherits Rectangle {
|
||||
in property <string> text;
|
||||
in property <string> icon;
|
||||
in property <string> tooltip;
|
||||
in property <bool> primary: false;
|
||||
in property <bool> quiet: false;
|
||||
in property <bool> enabled: true;
|
||||
private property <bool> keyboard-focus: false;
|
||||
private property <bool> show-focus: button-focus.has-focus && root.keyboard-focus;
|
||||
callback clicked;
|
||||
width: 112px;
|
||||
height: 36px;
|
||||
border-radius: UiRadius.control;
|
||||
border-width: root.show-focus ? 2px : 1px;
|
||||
border-color: root.show-focus ? (root.primary ? #193da0 : WorkbenchStyle.accent)
|
||||
: root.primary ? WorkbenchStyle.accent
|
||||
: touch.has-hover ? WorkbenchStyle.hover-border
|
||||
: root.quiet ? transparent : WorkbenchStyle.control-border;
|
||||
background: root.primary
|
||||
? (touch.pressed ? #254cc0 : touch.has-hover ? #4574f0 : WorkbenchStyle.accent)
|
||||
: (touch.pressed ? WorkbenchStyle.pressed-surface : touch.has-hover ? WorkbenchStyle.hover-surface : root.quiet ? transparent : white);
|
||||
opacity: root.enabled ? 1 : 0.45;
|
||||
animate background, border-color { duration: 120ms; }
|
||||
forward-focus: button-focus;
|
||||
accessible-role: button;
|
||||
accessible-label: root.tooltip != "" ? root.tooltip : root.text;
|
||||
accessible-enabled: root.enabled;
|
||||
accessible-action-default => { if root.enabled { root.clicked(); } }
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
spacing: 8px;
|
||||
alignment: center;
|
||||
if root.icon != "": Text {
|
||||
text: root.icon;
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
font-size: 14px;
|
||||
color: root.primary ? white : WorkbenchStyle.muted;
|
||||
vertical-alignment: center;
|
||||
accessible-role: none;
|
||||
}
|
||||
if root.text != "": Text {
|
||||
text: root.text;
|
||||
font-size: 13px;
|
||||
font-weight: root.primary ? 600 : 500;
|
||||
color: root.primary ? white : WorkbenchStyle.text;
|
||||
vertical-alignment: center;
|
||||
accessible-role: none;
|
||||
}
|
||||
}
|
||||
button-focus := FocusScope {
|
||||
enabled: root.enabled;
|
||||
focus-gained(reason) => {
|
||||
if reason == FocusReason.tab-navigation {
|
||||
root.keyboard-focus = true;
|
||||
} else if reason == FocusReason.pointer-click {
|
||||
root.keyboard-focus = false;
|
||||
}
|
||||
}
|
||||
key-pressed(event) => {
|
||||
root.keyboard-focus = true;
|
||||
if event.text == Key.Return || event.text == " " {
|
||||
root.clicked();
|
||||
return accept;
|
||||
}
|
||||
return reject;
|
||||
}
|
||||
touch := TouchArea {
|
||||
enabled: root.enabled;
|
||||
mouse-cursor: pointer;
|
||||
pointer-event(event) => {
|
||||
if event.kind == PointerEventKind.down {
|
||||
root.keyboard-focus = false;
|
||||
}
|
||||
}
|
||||
// Keep focus for subsequent keyboard activation without leaving a
|
||||
// keyboard focus ring behind after a pointer click.
|
||||
clicked => { root.keyboard-focus = false; button-focus.focus(); root.clicked(); }
|
||||
}
|
||||
}
|
||||
if touch.has-hover && root.tooltip != "": Rectangle {
|
||||
x: min(0px, root.width - self.width);
|
||||
y: root.height + 6px;
|
||||
width: tip.preferred-width + 16px;
|
||||
height: 28px;
|
||||
background: #24324a;
|
||||
border-radius: UiRadius.control;
|
||||
z: 20;
|
||||
tip := Text {
|
||||
text: root.tooltip;
|
||||
font-size: 11px;
|
||||
color: white;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export component WorkbenchCard inherits Rectangle {
|
||||
background: WorkbenchStyle.surface;
|
||||
border-width: 1px;
|
||||
border-color: WorkbenchStyle.border;
|
||||
border-radius: UiRadius.surface;
|
||||
}
|
||||
Reference in New Issue
Block a user