feat(staging): 删除动画与拖动排序
- 删除:右移淡出 200ms(OutCubic)后移除 - 拖动排序:允许把条目拖回列表,按落点移动并持久化 - 自身拖拽不再被当作外部拖入
This commit is contained in:
+2
-1
@@ -6,6 +6,7 @@
|
||||
|
||||
- 传送门面板:DBlur 双栏、layer-shell 定位、拖拽自动弹出、位置/高度可调并持久化
|
||||
- 暂存区:文件/文字/图片、去重、上限、文件消失清理、固定/消耗语义、全选/反选/删除选中
|
||||
- 暂存区动画:hover 高亮、删除右移淡出、拖动排序
|
||||
- 内容工厂:按类型给出动作,动作按钮可直接接收 drop
|
||||
- http(s) 链接按扩展名分类,支持下载/另存为/复制/Markdown/待办/问小埃
|
||||
- thunder:// 解析真实地址,magnet 解析名称,非标准 scheme 尝试打开
|
||||
@@ -16,6 +17,6 @@
|
||||
编辑/重试/分支、上下文条数与自动压缩、关于
|
||||
- 合成器 portal(flake-wm / gxde-wlcom):org.gxde.Portal、缩略图坐标映射、
|
||||
原生 drop 到目标窗口
|
||||
- gxde-k9 开机自启与保活开关
|
||||
- gxde-k9 开机自启与保活开关;不支持环境只提示一次并保持待命
|
||||
|
||||
-- shenmo <shenmo@spark-app.store>
|
||||
|
||||
@@ -306,6 +306,23 @@ void StagingModel::setChecked(int row, bool checked)
|
||||
save();
|
||||
}
|
||||
|
||||
void StagingModel::moveItem(int from, int to)
|
||||
{
|
||||
if (from < 0 || from >= m_items.size() || to < 0 || to >= m_items.size() || from == to) {
|
||||
return;
|
||||
}
|
||||
if (to > from) {
|
||||
beginMoveRows(QModelIndex(), from, from, QModelIndex(), to + 1);
|
||||
m_items.move(from, to);
|
||||
endMoveRows();
|
||||
} else {
|
||||
beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
|
||||
m_items.move(from, to);
|
||||
endMoveRows();
|
||||
}
|
||||
save();
|
||||
}
|
||||
|
||||
void StagingModel::setPinned(int row, bool pinned)
|
||||
{
|
||||
if (row < 0 || row >= m_items.size()) {
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
|
||||
void setChecked(int row, bool checked);
|
||||
void setPinned(int row, bool pinned);
|
||||
void moveItem(int from, int to);
|
||||
void clearAll();
|
||||
|
||||
const StagingItem *itemAt(int row) const;
|
||||
|
||||
+98
-8
@@ -48,6 +48,8 @@
|
||||
#include <QScreen>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTimer>
|
||||
#include <QVariantAnimation>
|
||||
#include <QEasingCurve>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
@@ -132,17 +134,28 @@ QString typeLabel(const QModelIndex &index)
|
||||
|
||||
class StagingDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StagingDelegate(QObject *parent = nullptr)
|
||||
: QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void setHoverRow(int row)
|
||||
void setHoverRow(int row) { m_hoverRow = row; }
|
||||
|
||||
void setRemoveState(int row, qreal progress)
|
||||
{
|
||||
m_hoverRow = row;
|
||||
m_removingRow = row;
|
||||
m_removeProgress = progress;
|
||||
}
|
||||
|
||||
signals:
|
||||
void removeRequested(int row);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
||||
{
|
||||
Q_UNUSED(option)
|
||||
@@ -156,6 +169,11 @@ public:
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
if (index.row() == m_removingRow && m_removeProgress > 0.0) {
|
||||
painter->setOpacity(1.0 - m_removeProgress);
|
||||
painter->translate(m_removeProgress * 48.0, 0.0);
|
||||
}
|
||||
|
||||
const QRect row = option.rect.adjusted(4, 2, -4, -2);
|
||||
const bool hovered = index.row() == m_hoverRow;
|
||||
|
||||
@@ -247,7 +265,7 @@ public:
|
||||
}
|
||||
|
||||
if (closeRect(option.rect).contains(pos)) {
|
||||
model->removeRow(index.row());
|
||||
Q_EMIT removeRequested(index.row());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -256,6 +274,8 @@ public:
|
||||
|
||||
private:
|
||||
int m_hoverRow = -1;
|
||||
int m_removingRow = -1;
|
||||
qreal m_removeProgress = 0.0;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@@ -285,6 +305,30 @@ public:
|
||||
viewport()->setAutoFillBackground(false);
|
||||
setStyleSheet(QStringLiteral("QListView { background: transparent; }"));
|
||||
setAcceptDrops(true);
|
||||
|
||||
m_removeAnim = new QVariantAnimation(this);
|
||||
m_removeAnim->setDuration(200);
|
||||
m_removeAnim->setEasingCurve(QEasingCurve::OutCubic);
|
||||
connect(m_removeAnim, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) {
|
||||
if (m_removingRow >= 0) {
|
||||
m_delegate->setRemoveState(m_removingRow, value.toReal());
|
||||
viewport()->update();
|
||||
}
|
||||
});
|
||||
connect(m_removeAnim, &QVariantAnimation::finished, this, [this]() {
|
||||
if (m_removingRow < 0) {
|
||||
return;
|
||||
}
|
||||
auto *staging = qobject_cast<StagingModel *>(this->model());
|
||||
const int row = m_removingRow;
|
||||
m_delegate->setRemoveState(-1, 0.0);
|
||||
m_removingRow = -1;
|
||||
if (staging) {
|
||||
staging->removeRow(row);
|
||||
}
|
||||
viewport()->update();
|
||||
});
|
||||
connect(m_delegate, &StagingDelegate::removeRequested, this, &StagingView::startRemove);
|
||||
}
|
||||
|
||||
signals:
|
||||
@@ -346,7 +390,7 @@ protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event) override
|
||||
{
|
||||
if (isOwnDrag(event->mimeData())) {
|
||||
event->ignore();
|
||||
event->acceptProposedAction();
|
||||
return;
|
||||
}
|
||||
if (mimeSupported(event->mimeData())) {
|
||||
@@ -360,7 +404,7 @@ protected:
|
||||
void dragMoveEvent(QDragMoveEvent *event) override
|
||||
{
|
||||
if (isOwnDrag(event->mimeData())) {
|
||||
event->ignore();
|
||||
event->acceptProposedAction();
|
||||
return;
|
||||
}
|
||||
if (mimeSupported(event->mimeData())) {
|
||||
@@ -374,12 +418,35 @@ protected:
|
||||
|
||||
void dropEvent(QDropEvent *event) override
|
||||
{
|
||||
if (isOwnDrag(event->mimeData()) || !mimeSupported(event->mimeData())) {
|
||||
if (isOwnDrag(event->mimeData())) {
|
||||
auto *staging = qobject_cast<StagingModel *>(this->model());
|
||||
const QString id = QString::fromUtf8(
|
||||
event->mimeData()->data(QStringLiteral("application/x-gxde-ai-see-staging")));
|
||||
int from = -1;
|
||||
if (staging) {
|
||||
for (int row = 0; row < staging->count(); ++row) {
|
||||
const StagingItem *item = staging->itemAt(row);
|
||||
if (item && item->id == id) {
|
||||
from = row;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const QModelIndex index = indexAt(event->position().toPoint());
|
||||
int to = index.isValid() ? index.row() : (staging ? staging->count() - 1 : -1);
|
||||
if (staging && from >= 0 && to >= 0 && from != to) {
|
||||
staging->moveItem(from, to);
|
||||
}
|
||||
m_internalMove = true;
|
||||
event->acceptProposedAction();
|
||||
return;
|
||||
}
|
||||
if (!mimeSupported(event->mimeData())) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
auto *staging = qobject_cast<StagingModel *>(model());
|
||||
auto *staging = qobject_cast<StagingModel *>(this->model());
|
||||
if (staging) {
|
||||
staging->addMimeData(event->mimeData());
|
||||
}
|
||||
@@ -410,6 +477,21 @@ private:
|
||||
return mime && mime->hasFormat(QStringLiteral("application/x-gxde-ai-see-staging"));
|
||||
}
|
||||
|
||||
void startRemove(int row)
|
||||
{
|
||||
if (m_removeAnim->state() == QAbstractAnimation::Running) {
|
||||
return;
|
||||
}
|
||||
if (row < 0 || !model() || row >= model()->rowCount()) {
|
||||
return;
|
||||
}
|
||||
m_removingRow = row;
|
||||
m_delegate->setRemoveState(row, 0.0);
|
||||
m_removeAnim->setStartValue(0.0);
|
||||
m_removeAnim->setEndValue(1.0);
|
||||
m_removeAnim->start();
|
||||
}
|
||||
|
||||
void startDragForIndex(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
@@ -449,10 +531,15 @@ private:
|
||||
const Qt::DropAction action =
|
||||
drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
|
||||
|
||||
if (m_internalMove) {
|
||||
m_internalMove = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Dragging an item out of the staging area consumes it unless it is
|
||||
* pinned. A cancelled drag (Esc / dropped on nothing) keeps it. */
|
||||
if (action != Qt::IgnoreAction && !pinned) {
|
||||
auto *staging = qobject_cast<StagingModel *>(model());
|
||||
auto *staging = qobject_cast<StagingModel *>(this->model());
|
||||
if (staging) {
|
||||
for (int row = 0; row < staging->count(); ++row) {
|
||||
const StagingItem *item = staging->itemAt(row);
|
||||
@@ -466,9 +553,12 @@ private:
|
||||
}
|
||||
|
||||
StagingDelegate *m_delegate = nullptr;
|
||||
QVariantAnimation *m_removeAnim = nullptr;
|
||||
QPoint m_pressPos;
|
||||
QModelIndex m_pressIndex;
|
||||
int m_hoverRow = -1;
|
||||
int m_removingRow = -1;
|
||||
bool m_internalMove = false;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
Reference in New Issue
Block a user