feat(portal): 接入合成器传送门,暂存区固定/消耗语义

- 新增 PortalClient:org.gxde.Portal 的 BeginPortal / CancelPortal 与 drag 信号
- 传送门区域悬停 150ms 后打开合成器 overview;无窗口/不支持时给出提示
- 拖拽开始时自动弹出面板(120ms 延迟,mime 过滤),拖拽结束自动收起
- 暂存区每行新增固定按钮;默认拖出后消耗,固定后保留;取消拖拽不消耗
- 拒绝自身拖拽落入暂存区,避免重复条目
This commit is contained in:
2026-10-30 19:55:25 +08:00
parent f9204a03d1
commit 68d3eb1a24
10 changed files with 314 additions and 6 deletions
+18
View File
@@ -82,6 +82,7 @@ QJsonObject StagingItem::toJson() const
obj.insert(QStringLiteral("text"), text);
obj.insert(QStringLiteral("name"), name);
obj.insert(QStringLiteral("checked"), checked);
obj.insert(QStringLiteral("pinned"), pinned);
obj.insert(QStringLiteral("createdAt"), createdAt);
return obj;
}
@@ -95,6 +96,7 @@ StagingItem StagingItem::fromJson(const QJsonObject &obj)
item.text = obj.value(QStringLiteral("text")).toString();
item.name = obj.value(QStringLiteral("name")).toString();
item.checked = obj.value(QStringLiteral("checked")).toBool();
item.pinned = obj.value(QStringLiteral("pinned")).toBool();
item.createdAt = static_cast<qint64>(obj.value(QStringLiteral("createdAt")).toDouble());
if (item.id.isEmpty()) {
item.id = newId();
@@ -144,6 +146,8 @@ QVariant StagingModel::data(const QModelIndex &index, int role) const
return item.text;
case CheckedRole:
return item.checked;
case PinnedRole:
return item.pinned;
case ExistsRole:
return item.exists();
case IconRole:
@@ -302,6 +306,20 @@ void StagingModel::setChecked(int row, bool checked)
save();
}
void StagingModel::setPinned(int row, bool pinned)
{
if (row < 0 || row >= m_items.size()) {
return;
}
if (m_items[row].pinned == pinned) {
return;
}
m_items[row].pinned = pinned;
const QModelIndex idx = index(row);
Q_EMIT dataChanged(idx, idx, {PinnedRole});
save();
}
void StagingModel::clearAll()
{
if (m_items.isEmpty()) {