mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-16 17:10:20 +08:00
[fix] disable already added devices in DeskHub recent picker
This commit is contained in:
@@ -4628,7 +4628,9 @@ bool GuiApplication::ImportGalleryRecent(const std::string& input_group) {
|
||||
std::vector<std::string> ids;
|
||||
for (size_t i = 0; i < ui_->gallery_recent_model->row_count(); ++i) {
|
||||
const auto device = ui_->gallery_recent_model->row_data(i);
|
||||
if (device && device->selected) ids.emplace_back(device->id);
|
||||
if (device && device->selected && !device->added &&
|
||||
!ui_->gallery.Contains(std::string(device->id)))
|
||||
ids.emplace_back(device->id);
|
||||
}
|
||||
if (ids.empty()) return false;
|
||||
const auto group = Trim(input_group);
|
||||
@@ -4773,6 +4775,7 @@ void GuiApplication::GalleryAction(const std::string& action,
|
||||
? wall.groups[group_index - 1] : ""));
|
||||
std::vector<ui::GalleryRecentDevice> devices;
|
||||
std::set<std::string> seen;
|
||||
int available = 0;
|
||||
for (const auto& [_, recent] : recent_connections_) {
|
||||
if (!gallery::Workspace::ValidId(recent.remote_id) ||
|
||||
!seen.insert(recent.remote_id).second)
|
||||
@@ -4781,25 +4784,31 @@ void GuiApplication::GalleryAction(const std::string& action,
|
||||
device.id = UiText(recent.remote_id);
|
||||
device.name = UiText(settings_.RecentConnectionDisplayName(recent));
|
||||
device.online = device_presence_cache_.IsOnline(recent.remote_id);
|
||||
device.added = wall.Contains(recent.remote_id);
|
||||
device.selected = device.added;
|
||||
if (!device.added) ++available;
|
||||
devices.push_back(std::move(device));
|
||||
}
|
||||
ui_->gallery_recent_model->set_vector(std::move(devices));
|
||||
ui_->wall->set_gallery_recent_devices(ui_->gallery_recent_model);
|
||||
ui_->wall->set_gallery_recent_selected(0);
|
||||
ui_->wall->set_gallery_recent_available(available);
|
||||
return;
|
||||
}
|
||||
if (action == "recent-close") {
|
||||
ui_->gallery_recent_model->set_vector({});
|
||||
ui_->wall->set_gallery_recent_selected(0);
|
||||
ui_->wall->set_gallery_recent_available(0);
|
||||
ui_->wall->set_gallery_notice("");
|
||||
return;
|
||||
}
|
||||
if (action == "recent-select" || action == "recent-all") {
|
||||
const bool select_all = ui_->wall->get_gallery_recent_selected() <
|
||||
int(ui_->gallery_recent_model->row_count());
|
||||
ui_->wall->get_gallery_recent_available();
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < ui_->gallery_recent_model->row_count(); ++i) {
|
||||
auto device = *ui_->gallery_recent_model->row_data(i);
|
||||
if (device.added) continue;
|
||||
if (action == "recent-all" || std::string(device.id) == value) {
|
||||
device.selected = action == "recent-all" ? select_all : !device.selected;
|
||||
ui_->gallery_recent_model->set_row_data(i, device);
|
||||
@@ -5179,14 +5188,24 @@ void GuiApplication::SyncGallery() {
|
||||
}
|
||||
if (ui_->wall->get_gallery_importing()) {
|
||||
// Keep the picker selection and ordering stable when presence changes.
|
||||
int available = 0, selected = 0;
|
||||
for (size_t i = 0; i < ui_->gallery_recent_model->row_count(); ++i) {
|
||||
auto device = *ui_->gallery_recent_model->row_data(i);
|
||||
const bool online = device_presence_cache_.IsOnline(std::string(device.id));
|
||||
if (device.online != online) {
|
||||
const bool added = ui_->gallery.Contains(std::string(device.id));
|
||||
if (device.online != online || device.added != added) {
|
||||
device.online = online;
|
||||
if (device.added != added) device.selected = added;
|
||||
device.added = added;
|
||||
ui_->gallery_recent_model->set_row_data(i, device);
|
||||
}
|
||||
if (!device.added) {
|
||||
++available;
|
||||
if (device.selected) ++selected;
|
||||
}
|
||||
}
|
||||
ui_->wall->set_gallery_recent_available(available);
|
||||
ui_->wall->set_gallery_recent_selected(selected);
|
||||
}
|
||||
const auto tr = [this](const char* zh, const char* en,
|
||||
const char* ru) -> std::string {
|
||||
|
||||
@@ -62,6 +62,9 @@ struct Workspace {
|
||||
std::all_of(id.begin(), id.end(),
|
||||
[](unsigned char c) { return c >= '0' && c <= '9'; });
|
||||
}
|
||||
bool Contains(const std::string &id) const {
|
||||
return wall_ids_.count(id) != 0;
|
||||
}
|
||||
bool Add(const std::string &id, const std::string &group = {}) {
|
||||
if (!ValidId(id))
|
||||
return false;
|
||||
|
||||
@@ -33,6 +33,7 @@ export struct GalleryRecentDevice {
|
||||
id: string,
|
||||
name: string,
|
||||
selected: bool,
|
||||
added: bool,
|
||||
online: bool,
|
||||
}
|
||||
export struct GalleryActionDevice {
|
||||
@@ -159,6 +160,7 @@ export component GalleryView inherits Rectangle {
|
||||
in-out property <bool> importing: false;
|
||||
in property <[GalleryRecentDevice]> recent-devices;
|
||||
in property <int> recent-selected: 0;
|
||||
in property <int> recent-available: 0;
|
||||
in property <string> import-group;
|
||||
private property <bool> confirming: false;
|
||||
private property <string> action-name;
|
||||
@@ -922,10 +924,10 @@ export component GalleryView inherits Rectangle {
|
||||
background: white; border-radius: 8px; 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-devices.length > 0; checked: root.recent-devices.length > 0 && root.recent-selected == root.recent-devices.length; }
|
||||
Text { x: 45px; y: 74px; height: 21px; text: GalleryStrings.tr("全选", "Select all", "Выбрать все"); font-size: 12px; vertical-alignment: center; }
|
||||
TouchArea { x: 18px; y: 74px; width: 144px; height: 21px; enabled: root.recent-devices.length > 0; clicked => { root.action("recent-all", ""); } }
|
||||
Text { x: 224px; y: 74px; width: 198px; height: 21px; text: GalleryStrings.tr("已选 ", "Selected ", "Выбрано ") + root.recent-selected + " / " + root.recent-devices.length; horizontal-alignment: right; vertical-alignment: center; font-size: 12px; color: #62666d; }
|
||||
ImGuiCheckBox { x: 18px; y: 74px; enabled: root.recent-available > 0; checked: root.recent-devices.length > 0 && root.recent-selected == root.recent-available; }
|
||||
Text { x: 45px; y: 74px; height: 21px; text: GalleryStrings.tr("全选", "Select all", "Выбрать все"); color: root.recent-available > 0 ? #24272d : #92969e; font-size: 12px; vertical-alignment: center; }
|
||||
TouchArea { x: 18px; y: 74px; width: 144px; height: 21px; enabled: root.recent-available > 0; clicked => { root.action("recent-all", ""); } }
|
||||
Text { x: 224px; y: 74px; width: 198px; height: 21px; text: GalleryStrings.tr("待添加 ", "To add ", "К добавлению ") + root.recent-selected + " / " + root.recent-available; horizontal-alignment: right; vertical-alignment: center; font-size: 12px; color: #62666d; }
|
||||
Rectangle {
|
||||
x: 18px; y: 102px; width: 404px; height: 160px;
|
||||
border-width: 1px; border-color: #d8dadd; border-radius: 4px; clip: true;
|
||||
@@ -935,17 +937,17 @@ export component GalleryView inherits Rectangle {
|
||||
viewport-height: max(self.height, root.recent-devices.length * 44px);
|
||||
for device[index] in root.recent-devices: Rectangle {
|
||||
y: index * 44px; width: recent-list.viewport-width; height: 44px;
|
||||
background: device.selected ? #edf5fe : row-touch.has-hover ? #f4f5f6 : white;
|
||||
ImGuiCheckBox { x: 8px; y: 11px; checked: device.selected; }
|
||||
Text { x: 36px; y: 3px; width: parent.width - 126px; height: 20px; text: device.name; font-size: 12px; overflow: elide; vertical-alignment: center; }
|
||||
Text { x: 36px; y: 23px; width: parent.width - 126px; height: 16px; text: device.id; font-size: 11px; color: #808080; }
|
||||
Rectangle { x: parent.width - 74px; y: 19px; width: 6px; height: 6px; border-radius: 3px; background: device.online ? #21b567 : #92969e; }
|
||||
background: device.added ? #f1f2f4 : device.selected ? #edf5fe : row-touch.has-hover ? #f4f5f6 : white;
|
||||
ImGuiCheckBox { x: 8px; y: 11px; enabled: !device.added; checked: device.added || device.selected; }
|
||||
Text { x: 36px; y: 3px; width: parent.width - 126px; height: 20px; text: device.name; color: device.added ? #92969e : #24272d; font-size: 12px; overflow: elide; vertical-alignment: center; }
|
||||
Text { x: 36px; y: 23px; width: parent.width - 126px; height: 16px; text: device.id; font-size: 11px; color: device.added ? #92969e : #808080; }
|
||||
Rectangle { x: parent.width - 74px; y: 19px; width: 6px; height: 6px; border-radius: 3px; background: !device.added && device.online ? #21b567 : #92969e; }
|
||||
Text {
|
||||
x: parent.width - 61px; y: 12px; width: 57px; height: 20px;
|
||||
text: device.online ? GalleryStrings.tr("在线", "Online", "В сети") : GalleryStrings.tr("离线", "Offline", "Не в сети");
|
||||
color: device.online ? #17854c : #808080; font-size: 11px; vertical-alignment: center;
|
||||
text: device.added ? GalleryStrings.tr("已添加", "Added", "Добавлено") : device.online ? GalleryStrings.tr("在线", "Online", "В сети") : GalleryStrings.tr("离线", "Offline", "Не в сети");
|
||||
color: device.added ? #92969e : device.online ? #17854c : #808080; font-size: 11px; vertical-alignment: center;
|
||||
}
|
||||
row-touch := TouchArea { clicked => { root.action("recent-select", device.id); } }
|
||||
row-touch := TouchArea { enabled: !device.added; clicked => { root.action("recent-select", device.id); } }
|
||||
}
|
||||
}
|
||||
if root.recent-devices.length == 0: Text { text: GalleryStrings.tr("暂无最近连接", "No recent connections", "Нет недавних подключений"); color: #808080; font-size: 12px; horizontal-alignment: center; vertical-alignment: center; }
|
||||
|
||||
@@ -341,6 +341,7 @@ export component MainWindow inherits Window {
|
||||
in-out property <bool> gallery-importing: false;
|
||||
in property <[GalleryRecentDevice]> gallery-recent-devices;
|
||||
in property <int> gallery-recent-selected: 0;
|
||||
in property <int> gallery-recent-available: 0;
|
||||
in property <string> gallery-import-group;
|
||||
callback open-gallery();
|
||||
callback toggle-gallery-fullscreen();
|
||||
@@ -1337,7 +1338,8 @@ export component MainWindow inherits Window {
|
||||
selected-devices: root.gallery-selected-devices;
|
||||
focused: root.gallery-focused; batch <=> root.gallery-batch; adding <=> root.gallery-adding;
|
||||
importing <=> root.gallery-importing; recent-devices: root.gallery-recent-devices;
|
||||
recent-selected: root.gallery-recent-selected; import-group: root.gallery-import-group;
|
||||
recent-selected: root.gallery-recent-selected;
|
||||
recent-available: root.gallery-recent-available; import-group: root.gallery-import-group;
|
||||
action(code, value) => { root.gallery-action(code, value); }
|
||||
swap-devices(source, target) => { root.gallery-swap(source, target) }
|
||||
add-device(id, password, group, remember) => { root.gallery-add-device(id, password, group, remember) }
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
## 使用
|
||||
|
||||
- 在桌面墙中点“添加 → 通过设备 ID 添加”,填写设备 ID、密码和可选的分组名称,再点“添加并连接”。密码仅用于本次连接,勾选“记住密码”才会通过原有凭据机制保存;不会写入桌面墙配置。
|
||||
- “添加 → 从近期连接添加”支持多选、全选和统一指定分组。导入后只加入桌面墙,使用“选择 → 连接”再发起连接。同一分组内重复设备会跳过;不指定分组时,已在桌面墙中的设备不会重复加入。
|
||||
- “添加 → 从近期连接添加”支持多选、全选和统一指定分组。已加入桌面墙的设备显示为灰色、保持勾选并标注“已添加”,不可操作,不受当前分组筛选影响;全选和待添加数量只计算尚未加入的设备,全部已添加时禁用添加按钮。导入后只加入桌面墙,使用“选择 → 连接”再发起连接。
|
||||
- 连接成功后立即记录在连接页的“最近连接”中,无需等到断开,也不要求记住密码。失败的连接不会新增最近记录。
|
||||
- “全部设备”显示保存的成员及当前活动会话。分组成员与连接相互独立:断开后成员仍在,重启后不会自动连接。
|
||||
- 卡片名称栏只显示设备名,长名称省略,不再显示状态圆点。未在线的设备(离线或在线状态未知)整张卡片使用灰色,包括预览背景、边框和名称;在线但尚未连接的设备保持正常样式。选中边框、复选框和操作入口仍可用。悬停画面可查看设备名称、ID 和在线状态,精简模式也支持。在线状态订阅包含全部分组中的设备;尚未收到结果、结果过期或服务器断连时显示未知,已有活动连接的设备仍显示在线。
|
||||
|
||||
Reference in New Issue
Block a user