2026-10-28 08:52:20 -04:00
|
|
|
|
#include "desktop_list_page.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QFont>
|
|
|
|
|
|
#include <QFrame>
|
|
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
|
#include <QScrollArea>
|
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
|
|
|
|
|
|
|
namespace vdi {
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
// 在信息行里加一个拉伸占位(独立函数避免在类外重复声明)
|
|
|
|
|
|
void addStretchToRow(QHBoxLayout* row) {
|
|
|
|
|
|
row->addStretch(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
DesktopListPage::DesktopListPage(QWidget* parent) : QWidget(parent) {
|
|
|
|
|
|
auto* root = new QVBoxLayout(this);
|
|
|
|
|
|
root->setContentsMargins(80, 50, 80, 40);
|
|
|
|
|
|
root->setSpacing(16);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 标题区 ----
|
|
|
|
|
|
auto* title = new QLabel(QStringLiteral("选择云桌面"), this);
|
|
|
|
|
|
QFont titleFont = title->font();
|
|
|
|
|
|
titleFont.setPointSize(26);
|
|
|
|
|
|
titleFont.setBold(true);
|
|
|
|
|
|
title->setFont(titleFont);
|
|
|
|
|
|
title->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
root->addWidget(title);
|
|
|
|
|
|
|
|
|
|
|
|
auto* infoRow = new QHBoxLayout();
|
|
|
|
|
|
labelUser_ = new QLabel(this);
|
|
|
|
|
|
labelUser_->setStyleSheet(QStringLiteral("color: #8a94a6;"));
|
2026-10-30 03:11:21 -04:00
|
|
|
|
// 无登录页演示模式下不设"退出登录"(用户身份由设备/平台承担,
|
|
|
|
|
|
// 正式认证接入后由 DesktopService 驱动恢复登录流程)
|
2026-10-28 08:52:20 -04:00
|
|
|
|
infoRow->addWidget(labelUser_);
|
|
|
|
|
|
addStretchToRow(infoRow);
|
|
|
|
|
|
root->addLayout(infoRow);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 卡片区 ----
|
|
|
|
|
|
auto* scroll = new QScrollArea(this);
|
|
|
|
|
|
scroll->setWidgetResizable(true);
|
|
|
|
|
|
scroll->setFrameShape(QFrame::NoFrame);
|
|
|
|
|
|
scroll->setStyleSheet(QStringLiteral(
|
|
|
|
|
|
"QScrollArea { background: transparent; }"
|
|
|
|
|
|
"QWidget { background: transparent; }"));
|
|
|
|
|
|
|
|
|
|
|
|
auto* cardsHost = new QWidget(scroll);
|
|
|
|
|
|
cardsLayout_ = new QVBoxLayout(cardsHost);
|
|
|
|
|
|
cardsLayout_->setSpacing(14);
|
|
|
|
|
|
cardsLayout_->addStretch(1);
|
|
|
|
|
|
scroll->setWidget(cardsHost);
|
|
|
|
|
|
root->addWidget(scroll, 1);
|
|
|
|
|
|
|
|
|
|
|
|
// ---- 状态条 ----
|
|
|
|
|
|
labelStatus_ = new QLabel(this);
|
|
|
|
|
|
labelStatus_->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
labelStatus_->setStyleSheet(QStringLiteral("color: #8a94a6;"));
|
|
|
|
|
|
root->addWidget(labelStatus_);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DesktopListPage::setDesktops(const std::vector<DesktopInfo>& desktops) {
|
|
|
|
|
|
desktops_ = desktops;
|
|
|
|
|
|
rebuildCards();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DesktopListPage::rebuildCards() {
|
2026-10-30 03:11:21 -04:00
|
|
|
|
actionButtons_.clear();
|
|
|
|
|
|
|
2026-10-28 08:52:20 -04:00
|
|
|
|
// 清空旧卡片(最后一项是 stretch)
|
|
|
|
|
|
while (cardsLayout_->count() > 1) {
|
|
|
|
|
|
QLayoutItem* item = cardsLayout_->takeAt(0);
|
|
|
|
|
|
if (item->widget()) {
|
|
|
|
|
|
item->widget()->deleteLater();
|
|
|
|
|
|
}
|
|
|
|
|
|
delete item;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const DesktopInfo& d : desktops_) {
|
|
|
|
|
|
auto* card = new QFrame(this);
|
|
|
|
|
|
card->setMinimumHeight(96);
|
|
|
|
|
|
card->setStyleSheet(QStringLiteral(
|
|
|
|
|
|
"QFrame { background: #1a2233; border: 1px solid #2b3654; border-radius: 10px; }"
|
|
|
|
|
|
"QFrame:hover { border: 1px solid #2f6feb; }"
|
|
|
|
|
|
"QLabel { border: none; background: transparent; }"));
|
|
|
|
|
|
|
|
|
|
|
|
auto* row = new QHBoxLayout(card);
|
|
|
|
|
|
row->setContentsMargins(24, 16, 24, 16);
|
|
|
|
|
|
row->setSpacing(18);
|
|
|
|
|
|
|
|
|
|
|
|
auto* textCol = new QVBoxLayout();
|
|
|
|
|
|
textCol->setSpacing(4);
|
|
|
|
|
|
auto* name = new QLabel(d.name, card);
|
|
|
|
|
|
QFont nameFont = name->font();
|
|
|
|
|
|
nameFont.setPointSize(15);
|
|
|
|
|
|
nameFont.setBold(true);
|
|
|
|
|
|
name->setFont(nameFont);
|
|
|
|
|
|
auto* osLine = new QLabel(d.os, card);
|
|
|
|
|
|
osLine->setStyleSheet(QStringLiteral("color: #8a94a6;"));
|
|
|
|
|
|
textCol->addWidget(name);
|
|
|
|
|
|
textCol->addWidget(osLine);
|
|
|
|
|
|
row->addLayout(textCol, 1);
|
|
|
|
|
|
|
2026-10-30 03:11:21 -04:00
|
|
|
|
// 状态标签(会话通道统一 Moonlight,无需按桌面标注协议)
|
2026-10-28 08:52:20 -04:00
|
|
|
|
QString stateText;
|
|
|
|
|
|
QString stateColor;
|
|
|
|
|
|
switch (d.state) {
|
|
|
|
|
|
case DesktopState::kAvailable:
|
2026-10-30 03:11:21 -04:00
|
|
|
|
stateText = QStringLiteral("可连接");
|
2026-10-28 08:52:20 -04:00
|
|
|
|
stateColor = QStringLiteral("#3fb950");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DesktopState::kPending:
|
|
|
|
|
|
stateText = QStringLiteral("任务进行中");
|
|
|
|
|
|
stateColor = QStringLiteral("#d29922");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case DesktopState::kUnassigned:
|
|
|
|
|
|
stateText = QStringLiteral("待分配");
|
|
|
|
|
|
stateColor = QStringLiteral("#8a94a6");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
auto* state = new QLabel(stateText, card);
|
|
|
|
|
|
state->setStyleSheet(QStringLiteral("color: %1; font-weight: bold;").arg(stateColor));
|
|
|
|
|
|
row->addWidget(state);
|
|
|
|
|
|
|
2026-10-29 02:10:06 -04:00
|
|
|
|
// “设置”:每张卡都可编辑连接参数(协议/地址/应用名/分辨率/帧率)
|
|
|
|
|
|
auto* editBtn = new QPushButton(QStringLiteral("设置"), card);
|
|
|
|
|
|
editBtn->setMinimumSize(88, 42);
|
2026-10-30 03:11:21 -04:00
|
|
|
|
editBtn->setEnabled(!busy_);
|
|
|
|
|
|
actionButtons_.push_back(editBtn);
|
2026-10-29 02:10:06 -04:00
|
|
|
|
connect(editBtn, &QPushButton::clicked, this, [this, id = d.id]() {
|
|
|
|
|
|
if (!busy_) {
|
|
|
|
|
|
emit editRequested(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
row->addWidget(editBtn);
|
|
|
|
|
|
|
2026-10-28 08:52:20 -04:00
|
|
|
|
if (d.state == DesktopState::kAvailable) {
|
|
|
|
|
|
auto* connectBtn = new QPushButton(QStringLiteral("连接"), card);
|
|
|
|
|
|
connectBtn->setMinimumSize(110, 42);
|
|
|
|
|
|
connectBtn->setStyleSheet(QStringLiteral(
|
|
|
|
|
|
"QPushButton { background: #2f6feb; color: white; border: none; "
|
|
|
|
|
|
"border-radius: 6px; font-weight: bold; }"
|
|
|
|
|
|
"QPushButton:disabled { background: #3a4a63; }"));
|
2026-10-30 03:11:21 -04:00
|
|
|
|
connectBtn->setEnabled(!busy_);
|
|
|
|
|
|
actionButtons_.push_back(connectBtn);
|
2026-10-28 08:52:20 -04:00
|
|
|
|
connect(connectBtn, &QPushButton::clicked, this, [this, id = d.id]() {
|
|
|
|
|
|
if (!busy_) {
|
2026-10-30 03:11:21 -04:00
|
|
|
|
setBusy(true);
|
2026-10-28 08:52:20 -04:00
|
|
|
|
setStatus(QStringLiteral("正在连接,请稍候…"));
|
|
|
|
|
|
emit desktopSelected(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
row->addWidget(connectBtn);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
auto* btn = new QPushButton(QStringLiteral("连接"), card);
|
|
|
|
|
|
btn->setMinimumSize(110, 42);
|
|
|
|
|
|
btn->setEnabled(false);
|
|
|
|
|
|
row->addWidget(btn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cardsLayout_->insertWidget(cardsLayout_->count() - 1, card);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DesktopListPage::setStatus(const QString& text, bool error) {
|
|
|
|
|
|
labelStatus_->setText(text);
|
|
|
|
|
|
labelStatus_->setStyleSheet(
|
|
|
|
|
|
QStringLiteral("color: %1;").arg(error ? QStringLiteral("#e05561")
|
|
|
|
|
|
: QStringLiteral("#8a94a6")));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DesktopListPage::setBusy(bool busy) {
|
|
|
|
|
|
busy_ = busy;
|
2026-10-30 03:11:21 -04:00
|
|
|
|
for (QPushButton* button : actionButtons_) {
|
|
|
|
|
|
if (button) {
|
|
|
|
|
|
button->setEnabled(!busy_);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-10-28 08:52:20 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QString DesktopListPage::currentUser() const {
|
|
|
|
|
|
return currentUser_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DesktopListPage::setCurrentUser(const QString& user) {
|
|
|
|
|
|
currentUser_ = user;
|
|
|
|
|
|
labelUser_->setText(user.isEmpty()
|
|
|
|
|
|
? QStringLiteral("当前用户:未登录")
|
|
|
|
|
|
: QStringLiteral("当前用户:%1").arg(user));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace vdi
|