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