Slight refactor of ExpoLayout
ExpoCell is a QQuickItem which manages the geometry of window thumbnails, movig a bit of the logic in the c++ part. The partialActivationFactor property switches between the geometry of the window and the geometry it will have in the overview grid Get rid of the complicated, 2-stage state machine that the delegate had
This commit is contained in:
parent
e48b7b77ec
commit
01d7ddff68
7 changed files with 509 additions and 503 deletions
|
@ -177,7 +177,9 @@ Item {
|
|||
DropArea {
|
||||
id: dropArea
|
||||
anchors.fill: parent
|
||||
|
||||
onEntered: (drag) => {
|
||||
drag.accepted = true;
|
||||
}
|
||||
onDropped: drop => {
|
||||
drop.accepted = true;
|
||||
// dragging a KWin::Window
|
||||
|
|
|
@ -453,6 +453,9 @@ FocusScope {
|
|||
property real deltaColumn: column - allDesktopHeaps.currentBackgroundItem.column - deltaX
|
||||
property real deltaRow: row - allDesktopHeaps.currentBackgroundItem.row - deltaY
|
||||
|
||||
onDeltaColumnChanged: heap.layout.updateCellsMapping()
|
||||
onDeltaRowChanged: heap.layout.updateCellsMapping()
|
||||
|
||||
Behavior on deltaColumn {
|
||||
enabled: overviewVal > 0 && !container.desktopJustCreated
|
||||
NumberAnimation {
|
||||
|
@ -499,6 +502,7 @@ FocusScope {
|
|||
// Initially places transition desktops in a grid around the current one,
|
||||
// and moves them slighly to avoid overlapping the UI
|
||||
Translate {
|
||||
id: desktopTranslation
|
||||
x: minX * 0.5 * overviewVal + deltaColumn * width * (1 - gridVal)
|
||||
y: minY * 0.5 * overviewVal + deltaRow * height * (1 - gridVal)
|
||||
}
|
||||
|
@ -645,6 +649,12 @@ FocusScope {
|
|||
}
|
||||
delegate: WindowHeapDelegate {
|
||||
windowHeap: heap
|
||||
offsetX: mainBackground.deltaColumn * container.width * (1 - gridVal) + (dragHandler.active ? (dragHandler.centroid.pressPosition.x - dragHandler.centroid.position.x) : 0)
|
||||
offsetY: mainBackground.deltaRow * container.height * (1 - gridVal) + (dragHandler.active ? (dragHandler.centroid.pressPosition.y - dragHandler.centroid.position.y) : 0)
|
||||
|
||||
partialActivationFactor: container.overviewVal + container.gridVal * effect.organizedGrid
|
||||
// Parent switch needed for the option "organize windows in gridview" to work correctly
|
||||
contentItemParent: container.gridVal > 0 ? mainBackground : container
|
||||
|
||||
// This is preferable over using gestureInProgress values since gridVal and
|
||||
// overviewVal are animated even after the gesture ends, and since the partial
|
||||
|
@ -652,8 +662,6 @@ FocusScope {
|
|||
// fluent animation.
|
||||
gestureInProgress: !Number.isInteger(gridVal) || !Number.isInteger(overviewVal)
|
||||
|
||||
partialActivationFactor: container.overviewVal + container.gridVal * effect.organizedGrid
|
||||
|
||||
targetScale: {
|
||||
if (!container.anyDesktopBar) return targetScale;
|
||||
if (overviewVal != 1) return targetScale;
|
||||
|
|
|
@ -11,9 +11,26 @@
|
|||
#include <deque>
|
||||
#include <tuple>
|
||||
|
||||
ExpoCell::ExpoCell(QObject *parent)
|
||||
: QObject(parent)
|
||||
|
||||
ExpoCell::ExpoCell(QQuickItem *parent)
|
||||
: QQuickItem(parent)
|
||||
{
|
||||
connect(this, &ExpoCell::visibleChanged, this, [this]() {
|
||||
if (m_contentItem) {
|
||||
m_contentItem->setVisible(isVisible());
|
||||
}
|
||||
});
|
||||
|
||||
// This only works for a static visual tree hierarchy.
|
||||
// TODO: Make it work with reparenting or warn if any parent in the tree changes?
|
||||
QQuickItem *ancestor = this;
|
||||
while (ancestor) {
|
||||
connect(ancestor, &QQuickItem::xChanged, this, &ExpoCell::polish);
|
||||
connect(ancestor, &QQuickItem::yChanged, this, &ExpoCell::polish);
|
||||
connect(ancestor, &QQuickItem::widthChanged, this, &ExpoCell::polish);
|
||||
connect(ancestor, &QQuickItem::heightChanged, this, &ExpoCell::polish);
|
||||
ancestor = ancestor->parentItem();
|
||||
}
|
||||
}
|
||||
|
||||
ExpoCell::~ExpoCell()
|
||||
|
@ -21,6 +38,12 @@ ExpoCell::~ExpoCell()
|
|||
setLayout(nullptr);
|
||||
}
|
||||
|
||||
void ExpoCell::componentComplete()
|
||||
{
|
||||
QQuickItem::componentComplete();
|
||||
updateContentItemGeometry();
|
||||
}
|
||||
|
||||
ExpoLayout *ExpoCell::layout() const
|
||||
{
|
||||
return m_layout;
|
||||
|
@ -35,159 +58,177 @@ void ExpoCell::setLayout(ExpoLayout *layout)
|
|||
m_layout->removeCell(this);
|
||||
}
|
||||
m_layout = layout;
|
||||
if (m_layout && m_enabled) {
|
||||
if (m_layout && m_shouldLayout) {
|
||||
m_layout->addCell(this);
|
||||
}
|
||||
updateContentItemGeometry();
|
||||
Q_EMIT layoutChanged();
|
||||
}
|
||||
|
||||
bool ExpoCell::isEnabled() const
|
||||
bool ExpoCell::shouldLayout() const
|
||||
{
|
||||
return m_enabled;
|
||||
return m_shouldLayout;
|
||||
}
|
||||
|
||||
void ExpoCell::setEnabled(bool enabled)
|
||||
void ExpoCell::setShouldLayout(bool shouldLayout)
|
||||
{
|
||||
if (m_enabled != enabled) {
|
||||
m_enabled = enabled;
|
||||
if (enabled) {
|
||||
if (m_layout) {
|
||||
m_layout->addCell(this);
|
||||
if (shouldLayout == m_shouldLayout) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
m_shouldLayout = shouldLayout;
|
||||
|
||||
if (m_layout) {
|
||||
if (m_shouldLayout) {
|
||||
m_layout->addCell(this);
|
||||
} else {
|
||||
m_layout->removeCell(this);
|
||||
}
|
||||
}
|
||||
Q_EMIT enabledChanged();
|
||||
}
|
||||
|
||||
Q_EMIT shouldLayoutChanged();
|
||||
}
|
||||
|
||||
void ExpoCell::update()
|
||||
QQuickItem *ExpoCell::contentItem() const
|
||||
{
|
||||
return m_contentItem;
|
||||
}
|
||||
|
||||
void ExpoCell::setContentItem(QQuickItem *item)
|
||||
{
|
||||
if (m_contentItem == item) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_contentItem = item;
|
||||
|
||||
if (m_contentItem) {
|
||||
m_contentItem->setVisible(isVisible());
|
||||
}
|
||||
|
||||
updateContentItemGeometry();
|
||||
Q_EMIT contentItemChanged();
|
||||
}
|
||||
|
||||
qreal ExpoCell::partialActivationFactor() const
|
||||
{
|
||||
return m_partialActivationFactor;
|
||||
}
|
||||
|
||||
void ExpoCell::setPartialActivationFactor(qreal factor)
|
||||
{
|
||||
if (m_partialActivationFactor == factor) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_partialActivationFactor = factor;
|
||||
// Since this is an animation controller we want it to have immediate effect
|
||||
updateContentItemGeometry();
|
||||
|
||||
Q_EMIT partialActivationFactorChanged();
|
||||
}
|
||||
|
||||
void ExpoCell::updateLayout()
|
||||
{
|
||||
if (m_layout) {
|
||||
m_layout->polish();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::naturalX() const
|
||||
qreal ExpoCell::offsetX() const
|
||||
{
|
||||
return m_offsetX;
|
||||
}
|
||||
|
||||
void ExpoCell::setOffsetX(qreal x)
|
||||
{
|
||||
if (m_offsetX != x) {
|
||||
m_offsetX = x;
|
||||
updateContentItemGeometry();
|
||||
Q_EMIT offsetXChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qreal ExpoCell::offsetY() const
|
||||
{
|
||||
return m_offsetY;
|
||||
}
|
||||
|
||||
void ExpoCell::setOffsetY(qreal y)
|
||||
{
|
||||
if (m_offsetY != y) {
|
||||
m_offsetY = y;
|
||||
updateContentItemGeometry();
|
||||
Q_EMIT offsetYChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qreal ExpoCell::naturalX() const
|
||||
{
|
||||
return m_naturalX;
|
||||
}
|
||||
|
||||
void ExpoCell::setNaturalX(int x)
|
||||
void ExpoCell::setNaturalX(qreal x)
|
||||
{
|
||||
if (m_naturalX != x) {
|
||||
m_naturalX = x;
|
||||
update();
|
||||
updateLayout();
|
||||
Q_EMIT naturalXChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::naturalY() const
|
||||
qreal ExpoCell::naturalY() const
|
||||
{
|
||||
return m_naturalY;
|
||||
}
|
||||
|
||||
void ExpoCell::setNaturalY(int y)
|
||||
void ExpoCell::setNaturalY(qreal y)
|
||||
{
|
||||
if (m_naturalY != y) {
|
||||
m_naturalY = y;
|
||||
update();
|
||||
updateLayout();
|
||||
Q_EMIT naturalYChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::naturalWidth() const
|
||||
qreal ExpoCell::naturalWidth() const
|
||||
{
|
||||
return m_naturalWidth;
|
||||
}
|
||||
|
||||
void ExpoCell::setNaturalWidth(int width)
|
||||
void ExpoCell::setNaturalWidth(qreal width)
|
||||
{
|
||||
if (m_naturalWidth != width) {
|
||||
m_naturalWidth = width;
|
||||
update();
|
||||
updateLayout();
|
||||
Q_EMIT naturalWidthChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::naturalHeight() const
|
||||
qreal ExpoCell::naturalHeight() const
|
||||
{
|
||||
return m_naturalHeight;
|
||||
}
|
||||
|
||||
void ExpoCell::setNaturalHeight(int height)
|
||||
void ExpoCell::setNaturalHeight(qreal height)
|
||||
{
|
||||
if (m_naturalHeight != height) {
|
||||
m_naturalHeight = height;
|
||||
update();
|
||||
updateLayout();
|
||||
Q_EMIT naturalHeightChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QRect ExpoCell::naturalRect() const
|
||||
QRectF ExpoCell::naturalRect() const
|
||||
{
|
||||
return QRect(naturalX(), naturalY(), naturalWidth(), naturalHeight());
|
||||
return QRectF(m_naturalX, m_naturalY, m_naturalWidth, m_naturalHeight);
|
||||
}
|
||||
|
||||
QMargins ExpoCell::margins() const
|
||||
QMarginsF ExpoCell::margins() const
|
||||
{
|
||||
return m_margins;
|
||||
}
|
||||
|
||||
int ExpoCell::x() const
|
||||
{
|
||||
return m_x.value_or(0);
|
||||
}
|
||||
|
||||
void ExpoCell::setX(int x)
|
||||
{
|
||||
if (m_x != x) {
|
||||
m_x = x;
|
||||
Q_EMIT xChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::y() const
|
||||
{
|
||||
return m_y.value_or(0);
|
||||
}
|
||||
|
||||
void ExpoCell::setY(int y)
|
||||
{
|
||||
if (m_y != y) {
|
||||
m_y = y;
|
||||
Q_EMIT yChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::width() const
|
||||
{
|
||||
return m_width.value_or(0);
|
||||
}
|
||||
|
||||
void ExpoCell::setWidth(int width)
|
||||
{
|
||||
if (m_width != width) {
|
||||
m_width = width;
|
||||
Q_EMIT widthChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::height() const
|
||||
{
|
||||
return m_height.value_or(0);
|
||||
}
|
||||
|
||||
void ExpoCell::setHeight(int height)
|
||||
{
|
||||
if (m_height != height) {
|
||||
m_height = height;
|
||||
Q_EMIT heightChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QString ExpoCell::persistentKey() const
|
||||
{
|
||||
return m_persistentKey;
|
||||
|
@ -197,17 +238,17 @@ void ExpoCell::setPersistentKey(const QString &key)
|
|||
{
|
||||
if (m_persistentKey != key) {
|
||||
m_persistentKey = key;
|
||||
update();
|
||||
updateLayout();
|
||||
Q_EMIT persistentKeyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int ExpoCell::bottomMargin() const
|
||||
qreal ExpoCell::bottomMargin() const
|
||||
{
|
||||
return m_margins.bottom();
|
||||
}
|
||||
|
||||
void ExpoCell::setBottomMargin(int margin)
|
||||
void ExpoCell::setBottomMargin(qreal margin)
|
||||
{
|
||||
if (m_margins.bottom() != margin) {
|
||||
m_margins.setBottom(margin);
|
||||
|
@ -216,6 +257,31 @@ void ExpoCell::setBottomMargin(int margin)
|
|||
}
|
||||
}
|
||||
|
||||
void ExpoCell::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
|
||||
{
|
||||
updateContentItemGeometry();
|
||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||
}
|
||||
|
||||
void ExpoCell::updateContentItemGeometry()
|
||||
{
|
||||
if (!m_contentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
QRectF rect = mapRectToItem(m_contentItem->parentItem(), boundingRect());
|
||||
|
||||
rect = {
|
||||
rect.x() * m_partialActivationFactor + (m_naturalX + m_offsetX) * (1.0 - m_partialActivationFactor),
|
||||
rect.y() * m_partialActivationFactor + (m_naturalY + m_offsetY) * (1.0 - m_partialActivationFactor),
|
||||
rect.width() * m_partialActivationFactor + m_naturalWidth * (1.0 - m_partialActivationFactor),
|
||||
rect.height() * m_partialActivationFactor + m_naturalHeight * (1.0 - m_partialActivationFactor)};
|
||||
|
||||
m_contentItem->setX(rect.x());
|
||||
m_contentItem->setY(rect.y());
|
||||
m_contentItem->setSize(rect.size());
|
||||
}
|
||||
|
||||
ExpoLayout::ExpoLayout(QQuickItem *parent)
|
||||
: QQuickItem(parent)
|
||||
{
|
||||
|
@ -253,6 +319,13 @@ void ExpoLayout::forceLayout()
|
|||
updatePolish();
|
||||
}
|
||||
|
||||
void ExpoLayout::updateCellsMapping()
|
||||
{
|
||||
for (ExpoCell *cell : m_cells) {
|
||||
cell->polish();
|
||||
}
|
||||
}
|
||||
|
||||
void ExpoLayout::addCell(ExpoCell *cell)
|
||||
{
|
||||
Q_ASSERT(!m_cells.contains(cell));
|
||||
|
@ -308,9 +381,9 @@ void ExpoLayout::updatePolish()
|
|||
|
||||
QList<QRectF> windowSizes;
|
||||
for (ExpoCell *cell : std::as_const(m_cells)) {
|
||||
const QMargins &margins = cell->margins();
|
||||
const QMarginsF &margins = cell->margins();
|
||||
const QMarginsF scaledMargins(margins.left() / scale, margins.top() / scale, margins.right() / scale, margins.bottom() / scale);
|
||||
windowSizes.emplace_back(cell->naturalRect().toRectF().marginsAdded(scaledMargins));
|
||||
windowSizes.emplace_back(cell->naturalRect().marginsAdded(scaledMargins));
|
||||
}
|
||||
auto windowLayouts = ExpoLayout::layout(area, windowSizes);
|
||||
for (int i = 0; i < windowLayouts.size(); ++i) {
|
||||
|
@ -324,11 +397,19 @@ void ExpoLayout::updatePolish()
|
|||
|
||||
QRectF rect = cell->naturalRect();
|
||||
moveToFit(rect, target);
|
||||
if (m_ready) {
|
||||
// Use setProperty so the QML side can animate with Behavior
|
||||
cell->setProperty("x", rect.x());
|
||||
cell->setProperty("y", rect.y());
|
||||
cell->setProperty("width", rect.width());
|
||||
cell->setProperty("height", rect.height());
|
||||
} else {
|
||||
cell->setX(rect.x());
|
||||
cell->setY(rect.y());
|
||||
cell->setWidth(rect.width());
|
||||
cell->setHeight(rect.height());
|
||||
}
|
||||
}
|
||||
setReady();
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ public:
|
|||
void setReady();
|
||||
|
||||
Q_INVOKABLE void forceLayout();
|
||||
Q_INVOKABLE void updateCellsMapping();
|
||||
|
||||
protected:
|
||||
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||
|
@ -185,95 +186,100 @@ private:
|
|||
qreal m_maxScale = 1.0;
|
||||
};
|
||||
|
||||
class ExpoCell : public QObject
|
||||
class ExpoCell : public QQuickItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(ExpoLayout *layout READ layout WRITE setLayout NOTIFY layoutChanged)
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
|
||||
Q_PROPERTY(int naturalX READ naturalX WRITE setNaturalX NOTIFY naturalXChanged)
|
||||
Q_PROPERTY(int naturalY READ naturalY WRITE setNaturalY NOTIFY naturalYChanged)
|
||||
Q_PROPERTY(int naturalWidth READ naturalWidth WRITE setNaturalWidth NOTIFY naturalWidthChanged)
|
||||
Q_PROPERTY(int naturalHeight READ naturalHeight WRITE setNaturalHeight NOTIFY naturalHeightChanged)
|
||||
Q_PROPERTY(int x READ x NOTIFY xChanged)
|
||||
Q_PROPERTY(int y READ y NOTIFY yChanged)
|
||||
Q_PROPERTY(int width READ width NOTIFY widthChanged)
|
||||
Q_PROPERTY(int height READ height NOTIFY heightChanged)
|
||||
Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged)
|
||||
Q_PROPERTY(qreal partialActivationFactor READ partialActivationFactor WRITE setPartialActivationFactor NOTIFY partialActivationFactorChanged)
|
||||
Q_PROPERTY(bool shouldLayout READ shouldLayout WRITE setShouldLayout NOTIFY shouldLayoutChanged)
|
||||
Q_PROPERTY(qreal offsetX READ offsetX WRITE setOffsetX NOTIFY offsetXChanged)
|
||||
Q_PROPERTY(qreal offsetY READ offsetY WRITE setOffsetY NOTIFY offsetYChanged)
|
||||
Q_PROPERTY(qreal naturalX READ naturalX WRITE setNaturalX NOTIFY naturalXChanged)
|
||||
Q_PROPERTY(qreal naturalY READ naturalY WRITE setNaturalY NOTIFY naturalYChanged)
|
||||
Q_PROPERTY(qreal naturalWidth READ naturalWidth WRITE setNaturalWidth NOTIFY naturalWidthChanged)
|
||||
Q_PROPERTY(qreal naturalHeight READ naturalHeight WRITE setNaturalHeight NOTIFY naturalHeightChanged)
|
||||
Q_PROPERTY(QString persistentKey READ persistentKey WRITE setPersistentKey NOTIFY persistentKeyChanged)
|
||||
Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
|
||||
Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
|
||||
|
||||
public:
|
||||
explicit ExpoCell(QObject *parent = nullptr);
|
||||
explicit ExpoCell(QQuickItem *parent = nullptr);
|
||||
~ExpoCell() override;
|
||||
|
||||
bool isEnabled() const;
|
||||
void setEnabled(bool enabled);
|
||||
void componentComplete() override;
|
||||
|
||||
ExpoLayout *layout() const;
|
||||
void setLayout(ExpoLayout *layout);
|
||||
|
||||
int naturalX() const;
|
||||
void setNaturalX(int x);
|
||||
bool shouldLayout() const;
|
||||
void setShouldLayout(bool layout);
|
||||
|
||||
int naturalY() const;
|
||||
void setNaturalY(int y);
|
||||
QQuickItem *contentItem() const;
|
||||
void setContentItem(QQuickItem *item);
|
||||
|
||||
int naturalWidth() const;
|
||||
void setNaturalWidth(int width);
|
||||
qreal partialActivationFactor() const;
|
||||
void setPartialActivationFactor(qreal factor);
|
||||
|
||||
int naturalHeight() const;
|
||||
void setNaturalHeight(int height);
|
||||
qreal offsetX() const;
|
||||
void setOffsetX(qreal x);
|
||||
|
||||
QRect naturalRect() const;
|
||||
QMargins margins() const;
|
||||
qreal offsetY() const;
|
||||
void setOffsetY(qreal y);
|
||||
|
||||
int x() const;
|
||||
void setX(int x);
|
||||
qreal naturalX() const;
|
||||
void setNaturalX(qreal x);
|
||||
|
||||
int y() const;
|
||||
void setY(int y);
|
||||
qreal naturalY() const;
|
||||
void setNaturalY(qreal y);
|
||||
|
||||
int width() const;
|
||||
void setWidth(int width);
|
||||
qreal naturalWidth() const;
|
||||
void setNaturalWidth(qreal width);
|
||||
|
||||
int height() const;
|
||||
void setHeight(int height);
|
||||
qreal naturalHeight() const;
|
||||
void setNaturalHeight(qreal height);
|
||||
|
||||
QRectF naturalRect() const;
|
||||
QMarginsF margins() const;
|
||||
|
||||
QString persistentKey() const;
|
||||
void setPersistentKey(const QString &key);
|
||||
|
||||
int bottomMargin() const;
|
||||
void setBottomMargin(int margin);
|
||||
qreal bottomMargin() const;
|
||||
void setBottomMargin(qreal margin);
|
||||
|
||||
public Q_SLOTS:
|
||||
void update();
|
||||
protected:
|
||||
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void layoutChanged();
|
||||
void enabledChanged();
|
||||
void shouldLayoutChanged();
|
||||
void contentItemChanged();
|
||||
void partialActivationFactorChanged();
|
||||
void offsetXChanged();
|
||||
void offsetYChanged();
|
||||
void naturalXChanged();
|
||||
void naturalYChanged();
|
||||
void naturalWidthChanged();
|
||||
void naturalHeightChanged();
|
||||
void xChanged();
|
||||
void yChanged();
|
||||
void widthChanged();
|
||||
void heightChanged();
|
||||
void persistentKeyChanged();
|
||||
void bottomMarginChanged();
|
||||
|
||||
private:
|
||||
void updateContentItemGeometry();
|
||||
void updateLayout();
|
||||
|
||||
QString m_persistentKey;
|
||||
bool m_enabled = true;
|
||||
int m_naturalX = 0;
|
||||
int m_naturalY = 0;
|
||||
int m_naturalWidth = 0;
|
||||
int m_naturalHeight = 0;
|
||||
QMargins m_margins;
|
||||
std::optional<int> m_x;
|
||||
std::optional<int> m_y;
|
||||
std::optional<int> m_width;
|
||||
std::optional<int> m_height;
|
||||
qreal m_offsetX = 0;
|
||||
qreal m_offsetY = 0;
|
||||
qreal m_naturalX = 0;
|
||||
qreal m_naturalY = 0;
|
||||
qreal m_naturalWidth = 0;
|
||||
qreal m_naturalHeight = 0;
|
||||
QMarginsF m_margins;
|
||||
QPointer<ExpoLayout> m_layout;
|
||||
QPointer<QQuickItem> m_contentItem;
|
||||
qreal m_partialActivationFactor = 1.0;
|
||||
bool m_shouldLayout = true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -136,6 +136,7 @@ FocusScope {
|
|||
|
||||
delegate: WindowHeapDelegate {
|
||||
windowHeap: heap
|
||||
layout: expoLayout
|
||||
}
|
||||
|
||||
onObjectRemoved: (index, object) => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
|
||||
SPDX-FileCopyrightText: 2024 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
@ -15,7 +16,7 @@ import org.kde.plasma.components 3.0 as PC3
|
|||
import org.kde.plasma.extras as PlasmaExtras
|
||||
import org.kde.ksvg 1.0 as KSvg
|
||||
|
||||
Item {
|
||||
ExpoCell {
|
||||
id: thumb
|
||||
|
||||
required property QtObject window
|
||||
|
@ -23,8 +24,9 @@ Item {
|
|||
required property Item windowHeap
|
||||
|
||||
readonly property bool selected: windowHeap.selectedIndex === index
|
||||
property real partialActivationFactor: effect.partialActivationFactor
|
||||
property bool gestureInProgress: effect.gestureInProgress
|
||||
// Where the internal contentItem will be parented to
|
||||
property Item contentItemParent: this
|
||||
|
||||
// no desktops is a special value which means "All Desktops"
|
||||
readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1
|
||||
|
@ -61,43 +63,74 @@ Item {
|
|||
readonly property alias downGestureProgress: touchDragHandler.downGestureProgress
|
||||
signal downGestureTriggered()
|
||||
|
||||
// "normal" | "pressed" | "drag" | "reparenting"
|
||||
property string substate: "normal"
|
||||
|
||||
state: {
|
||||
if (thumb.gestureInProgress) {
|
||||
return "partial";
|
||||
}
|
||||
if (thumb.partialActivationFactor > 0.5 && (cell.isReady || activeHidden)) {
|
||||
return activeHidden ? "active-hidden" : `active-${substate}`;
|
||||
}
|
||||
return initialHidden ? "initial-hidden" : "initial";
|
||||
}
|
||||
|
||||
visible: opacity > 0
|
||||
z: (activeDragHandler.active || returning.running) ? 1000
|
||||
: window.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001)
|
||||
property bool isReady: width !== 0 && height !== 0
|
||||
|
||||
function restoreDND(oldGlobalRect: rect) {
|
||||
thumbSource.restoreDND(oldGlobalRect);
|
||||
}
|
||||
|
||||
component TweenBehavior : Behavior {
|
||||
enabled: thumb.state === "active-normal" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active
|
||||
layout: windowHeap.layout
|
||||
shouldLayout: !thumb.activeHidden
|
||||
partialActivationFactor: effect.partialActivationFactor
|
||||
naturalX: thumb.window.x - thumb.window.output.geometry.x
|
||||
naturalY: thumb.window.y - thumb.window.output.geometry.y
|
||||
naturalWidth: thumb.window.width
|
||||
naturalHeight: thumb.window.height
|
||||
persistentKey: thumb.window.internalId
|
||||
bottomMargin: icon.height / 4 + (thumb.windowTitleVisible ? caption.height : 0)
|
||||
|
||||
Behavior on x {
|
||||
enabled: thumb.isReady
|
||||
NumberAnimation {
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
Behavior on y {
|
||||
enabled: thumb.isReady
|
||||
NumberAnimation {
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
Behavior on width {
|
||||
enabled: thumb.isReady
|
||||
NumberAnimation {
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
Behavior on height {
|
||||
enabled: thumb.isReady
|
||||
NumberAnimation {
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
|
||||
TweenBehavior on x {}
|
||||
TweenBehavior on y {}
|
||||
TweenBehavior on width {}
|
||||
TweenBehavior on height {}
|
||||
contentItem: Item {
|
||||
id: mainContent
|
||||
parent: contentItemParent
|
||||
visible: opacity > 0 && (!activeHidden || !initialHidden)
|
||||
opacity: (1 - downGestureProgress) * (initialHidden ? partialActivationFactor : 1)
|
||||
z: (activeDragHandler.active || returnAnimation.running) ? 1000
|
||||
: thumb.window.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001)
|
||||
|
||||
KWinComponents.WindowThumbnail {
|
||||
id: thumbSource
|
||||
wId: thumb.window.internalId
|
||||
scale: targetScale
|
||||
width: mainContent.width
|
||||
height: mainContent.height
|
||||
|
||||
Binding on width {
|
||||
value: mainContent.width
|
||||
when: !returnAnimation.active
|
||||
}
|
||||
Binding on height {
|
||||
value: mainContent.height
|
||||
when: !returnAnimation.active
|
||||
}
|
||||
|
||||
Drag.proposedAction: Qt.MoveAction
|
||||
Drag.supportedActions: Qt.MoveAction
|
||||
|
@ -115,16 +148,14 @@ Item {
|
|||
thumb.windowHeap.saveDND(thumb.window.internalId, oldGlobalRect);
|
||||
}
|
||||
function restoreDND(oldGlobalRect: rect) {
|
||||
thumb.substate = "reparenting";
|
||||
|
||||
const newGlobalRect = mapFromItem(null, oldGlobalRect);
|
||||
|
||||
// Disable bindings
|
||||
returnAnimation.active = true;
|
||||
x = newGlobalRect.x;
|
||||
y = newGlobalRect.y;
|
||||
width = newGlobalRect.width;
|
||||
height = newGlobalRect.height;
|
||||
|
||||
thumb.substate = "normal";
|
||||
returnAnimation.restart();
|
||||
}
|
||||
function deleteDND() {
|
||||
thumb.windowHeap.deleteDND(thumb.window.internalId);
|
||||
|
@ -146,6 +177,39 @@ Item {
|
|||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: thumb.activeDragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor
|
||||
}
|
||||
ParallelAnimation {
|
||||
id: returnAnimation
|
||||
property bool active: false
|
||||
onRunningChanged: active = running
|
||||
NumberAnimation {
|
||||
target: thumbSource
|
||||
properties: "x,y"
|
||||
to: 0
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
NumberAnimation {
|
||||
target: thumbSource
|
||||
property: "width"
|
||||
to: mainContent.width
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
NumberAnimation {
|
||||
target: thumbSource
|
||||
property: "height"
|
||||
to: mainContent.height
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
NumberAnimation {
|
||||
target: thumbSource
|
||||
property: "scale"
|
||||
to: 1
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
|
@ -153,23 +217,31 @@ Item {
|
|||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: i18nd("kwin", "Drag Down To Close")
|
||||
opacity: thumbSource.opacity
|
||||
visible: !thumb.activeHidden && touchDragHandler.active
|
||||
background: Rectangle {
|
||||
anchors.centerIn: parent
|
||||
height: parent.contentHeight + Kirigami.Units.smallSpacing
|
||||
width: parent.contentWidth + Kirigami.Units.smallSpacing
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
id: icon
|
||||
width: Kirigami.Units.iconSizes.large
|
||||
height: Kirigami.Units.iconSizes.large
|
||||
opacity: partialActivationFactor
|
||||
scale: Math.min(1.0, mainContent.width / Math.max(0.01, thumb.width))
|
||||
source: thumb.window.icon
|
||||
anchors.horizontalCenter: thumbSource.horizontalCenter
|
||||
anchors.bottom: thumbSource.bottom
|
||||
anchors.bottomMargin: -Math.round(height / 4)
|
||||
visible: !thumb.activeHidden && !activeDragHandler.active
|
||||
anchors.verticalCenter: thumbSource.bottom
|
||||
anchors.verticalCenterOffset: -Math.round(height / 4) * scale
|
||||
visible: !thumb.activeHidden && !activeDragHandler.active && !returnAnimation.running
|
||||
PC3.Label {
|
||||
id: caption
|
||||
visible: thumb.windowTitleVisible
|
||||
width: cell.width
|
||||
width: thumb.width
|
||||
maximumLineCount: 1
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Kirigami.Units.smallSpacing
|
||||
|
@ -190,165 +262,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
ExpoCell {
|
||||
id: cell
|
||||
layout: windowHeap.layout
|
||||
enabled: !thumb.activeHidden
|
||||
naturalX: thumb.window.x
|
||||
naturalY: thumb.window.y
|
||||
naturalWidth: thumb.window.width
|
||||
naturalHeight: thumb.window.height
|
||||
persistentKey: thumb.window.internalId
|
||||
bottomMargin: icon.height / 4 + (thumb.windowTitleVisible ? caption.height : 0)
|
||||
property bool isReady: width !== 0 && height !== 0
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "initial"
|
||||
PropertyChanges {
|
||||
target: thumb
|
||||
x: thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0)
|
||||
y: thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0)
|
||||
width: thumb.window.width
|
||||
height: thumb.window.height
|
||||
}
|
||||
PropertyChanges {
|
||||
target: thumbSource
|
||||
x: 0
|
||||
y: 0
|
||||
width: thumb.window.width
|
||||
height: thumb.window.height
|
||||
}
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
opacity: 0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeButton
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "partial"
|
||||
PropertyChanges {
|
||||
target: thumb
|
||||
x: (thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0)) * (thumb.activeHidden ? 1 : 1 - thumb.partialActivationFactor) + (thumb.activeHidden ? 0 : cell.x * thumb.partialActivationFactor)
|
||||
y: (thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0)) * (thumb.activeHidden ? 1 : 1 - thumb.partialActivationFactor) + (thumb.activeHidden ? 0 : cell.y * thumb.partialActivationFactor)
|
||||
width: thumb.window.width * (thumb.activeHidden ? 1 : 1 - thumb.partialActivationFactor) + cell.width * (thumb.activeHidden ? 0 : thumb.partialActivationFactor)
|
||||
height: thumb.window.height * (thumb.activeHidden ? 1 : 1 - thumb.partialActivationFactor) + cell.height * (thumb.activeHidden ? 0 : thumb.partialActivationFactor)
|
||||
opacity: thumb.initialHidden
|
||||
? (thumb.activeHidden ? 0 : thumb.partialActivationFactor)
|
||||
: (thumb.activeHidden ? 1 - thumb.partialActivationFactor : 1)
|
||||
}
|
||||
PropertyChanges {
|
||||
target: thumbSource
|
||||
x: 0
|
||||
y: 0
|
||||
width: thumb.width
|
||||
height: thumb.height
|
||||
}
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
opacity: thumb.partialActivationFactor
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeButton
|
||||
opacity: thumb.partialActivationFactor
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "initial-hidden"
|
||||
extend: "initial"
|
||||
PropertyChanges {
|
||||
target: thumb
|
||||
opacity: 0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
opacity: 0
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeButton
|
||||
opacity: 0
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "active-hidden"
|
||||
extend: "initial-hidden"
|
||||
},
|
||||
State {
|
||||
// this state is never directly used without a substate
|
||||
name: "active"
|
||||
PropertyChanges {
|
||||
target: thumb
|
||||
x: cell.x
|
||||
y: cell.y
|
||||
width: cell.width
|
||||
height: cell.height
|
||||
}
|
||||
PropertyChanges {
|
||||
target: icon
|
||||
opacity: 1
|
||||
}
|
||||
PropertyChanges {
|
||||
target: closeButton
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "active-normal"
|
||||
extend: "active"
|
||||
PropertyChanges {
|
||||
target: thumbSource
|
||||
x: 0
|
||||
y: 0
|
||||
width: thumb.width
|
||||
height: thumb.height
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "active-pressed"
|
||||
extend: "active"
|
||||
PropertyChanges {
|
||||
target: thumbSource
|
||||
width: thumb.width
|
||||
height: thumb.height
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "active-drag"
|
||||
extend: "active"
|
||||
PropertyChanges {
|
||||
target: thumbSource
|
||||
x: -thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale +
|
||||
thumb.activeDragHandler.centroid.position.x
|
||||
y: -thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale +
|
||||
thumb.activeDragHandler.centroid.position.y
|
||||
width: thumb.width * thumb.targetScale
|
||||
height: thumb.height * thumb.targetScale
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "active-reparenting"
|
||||
extend: "active"
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
id: returning
|
||||
from: "active-drag, active-reparenting"
|
||||
to: "active-normal"
|
||||
enabled: thumb.windowHeap.animationEnabled
|
||||
NumberAnimation {
|
||||
duration: thumb.windowHeap.animationDuration
|
||||
properties: "x, y, width, height"
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
onHoveredChanged: if (hovered !== selected) {
|
||||
|
@ -364,20 +277,15 @@ Item {
|
|||
}
|
||||
onPressedChanged: {
|
||||
if (pressed) {
|
||||
var saved = Qt.point(thumbSource.x, thumbSource.y);
|
||||
thumbSource.Drag.active = true;
|
||||
thumb.substate = "pressed";
|
||||
thumbSource.x = saved.x;
|
||||
thumbSource.y = saved.y;
|
||||
} else if (!thumb.activeDragHandler.active) {
|
||||
thumbSource.Drag.active = false;
|
||||
thumb.substate = "normal";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component DragManager : DragHandler {
|
||||
target: null
|
||||
target: thumbSource
|
||||
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||
// This does not work when moving pointer fast and pressing along the way
|
||||
// See also QTBUG-105903, QTBUG-105904
|
||||
|
@ -387,9 +295,9 @@ Item {
|
|||
thumb.windowHeap.dragActive = active;
|
||||
if (active) {
|
||||
thumb.activeDragHandler = this;
|
||||
thumb.substate = "drag";
|
||||
} else {
|
||||
thumbSource.saveDND();
|
||||
returnAnimation.restart();
|
||||
|
||||
var action = thumbSource.Drag.drop();
|
||||
if (action === Qt.MoveAction) {
|
||||
|
@ -398,7 +306,6 @@ Item {
|
|||
if (typeof thumbSource !== "undefined") {
|
||||
// Except the case when it was dropped on the same desktop which it's already on, so let's return to normal state anyway.
|
||||
thumbSource.deleteDND();
|
||||
thumb.substate = "normal";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -409,7 +316,6 @@ Item {
|
|||
if (typeof thumbSource !== "undefined") {
|
||||
// else, return to normal without reparenting
|
||||
thumbSource.deleteDND();
|
||||
thumb.substate = "normal";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -451,7 +357,7 @@ Item {
|
|||
top: thumbSource.top
|
||||
margins: Kirigami.Units.smallSpacing
|
||||
}
|
||||
active: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active
|
||||
active: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active && !returnAnimation.running
|
||||
|
||||
sourceComponent: PC3.Button {
|
||||
text: i18ndc("kwin", "@info:tooltip as in: 'close this window'", "Close window")
|
||||
|
@ -473,3 +379,4 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,6 +202,7 @@ Item {
|
|||
id: delegate
|
||||
windowHeap: heap
|
||||
partialActivationFactor: container.organized ? 1 : 0
|
||||
contentItemParent: container
|
||||
Behavior on partialActivationFactor {
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
|
|
Loading…
Reference in a new issue