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 {
|
DropArea {
|
||||||
id: dropArea
|
id: dropArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
onEntered: (drag) => {
|
||||||
|
drag.accepted = true;
|
||||||
|
}
|
||||||
onDropped: drop => {
|
onDropped: drop => {
|
||||||
drop.accepted = true;
|
drop.accepted = true;
|
||||||
// dragging a KWin::Window
|
// dragging a KWin::Window
|
||||||
|
|
|
@ -453,6 +453,9 @@ FocusScope {
|
||||||
property real deltaColumn: column - allDesktopHeaps.currentBackgroundItem.column - deltaX
|
property real deltaColumn: column - allDesktopHeaps.currentBackgroundItem.column - deltaX
|
||||||
property real deltaRow: row - allDesktopHeaps.currentBackgroundItem.row - deltaY
|
property real deltaRow: row - allDesktopHeaps.currentBackgroundItem.row - deltaY
|
||||||
|
|
||||||
|
onDeltaColumnChanged: heap.layout.updateCellsMapping()
|
||||||
|
onDeltaRowChanged: heap.layout.updateCellsMapping()
|
||||||
|
|
||||||
Behavior on deltaColumn {
|
Behavior on deltaColumn {
|
||||||
enabled: overviewVal > 0 && !container.desktopJustCreated
|
enabled: overviewVal > 0 && !container.desktopJustCreated
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
@ -499,6 +502,7 @@ FocusScope {
|
||||||
// Initially places transition desktops in a grid around the current one,
|
// Initially places transition desktops in a grid around the current one,
|
||||||
// and moves them slighly to avoid overlapping the UI
|
// and moves them slighly to avoid overlapping the UI
|
||||||
Translate {
|
Translate {
|
||||||
|
id: desktopTranslation
|
||||||
x: minX * 0.5 * overviewVal + deltaColumn * width * (1 - gridVal)
|
x: minX * 0.5 * overviewVal + deltaColumn * width * (1 - gridVal)
|
||||||
y: minY * 0.5 * overviewVal + deltaRow * height * (1 - gridVal)
|
y: minY * 0.5 * overviewVal + deltaRow * height * (1 - gridVal)
|
||||||
}
|
}
|
||||||
|
@ -645,6 +649,12 @@ FocusScope {
|
||||||
}
|
}
|
||||||
delegate: WindowHeapDelegate {
|
delegate: WindowHeapDelegate {
|
||||||
windowHeap: heap
|
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
|
// This is preferable over using gestureInProgress values since gridVal and
|
||||||
// overviewVal are animated even after the gesture ends, and since the partial
|
// overviewVal are animated even after the gesture ends, and since the partial
|
||||||
|
@ -652,8 +662,6 @@ FocusScope {
|
||||||
// fluent animation.
|
// fluent animation.
|
||||||
gestureInProgress: !Number.isInteger(gridVal) || !Number.isInteger(overviewVal)
|
gestureInProgress: !Number.isInteger(gridVal) || !Number.isInteger(overviewVal)
|
||||||
|
|
||||||
partialActivationFactor: container.overviewVal + container.gridVal * effect.organizedGrid
|
|
||||||
|
|
||||||
targetScale: {
|
targetScale: {
|
||||||
if (!container.anyDesktopBar) return targetScale;
|
if (!container.anyDesktopBar) return targetScale;
|
||||||
if (overviewVal != 1) return targetScale;
|
if (overviewVal != 1) return targetScale;
|
||||||
|
|
|
@ -11,9 +11,26 @@
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <tuple>
|
#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()
|
ExpoCell::~ExpoCell()
|
||||||
|
@ -21,6 +38,12 @@ ExpoCell::~ExpoCell()
|
||||||
setLayout(nullptr);
|
setLayout(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExpoCell::componentComplete()
|
||||||
|
{
|
||||||
|
QQuickItem::componentComplete();
|
||||||
|
updateContentItemGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
ExpoLayout *ExpoCell::layout() const
|
ExpoLayout *ExpoCell::layout() const
|
||||||
{
|
{
|
||||||
return m_layout;
|
return m_layout;
|
||||||
|
@ -35,159 +58,177 @@ void ExpoCell::setLayout(ExpoLayout *layout)
|
||||||
m_layout->removeCell(this);
|
m_layout->removeCell(this);
|
||||||
}
|
}
|
||||||
m_layout = layout;
|
m_layout = layout;
|
||||||
if (m_layout && m_enabled) {
|
if (m_layout && m_shouldLayout) {
|
||||||
m_layout->addCell(this);
|
m_layout->addCell(this);
|
||||||
}
|
}
|
||||||
|
updateContentItemGeometry();
|
||||||
Q_EMIT layoutChanged();
|
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) {
|
if (shouldLayout == m_shouldLayout) {
|
||||||
m_enabled = enabled;
|
return;
|
||||||
if (enabled) {
|
|
||||||
if (m_layout) {
|
|
||||||
m_layout->addCell(this);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (m_layout) {
|
|
||||||
m_layout->removeCell(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Q_EMIT enabledChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_shouldLayout = shouldLayout;
|
||||||
|
|
||||||
|
if (m_layout) {
|
||||||
|
if (m_shouldLayout) {
|
||||||
|
m_layout->addCell(this);
|
||||||
|
} else {
|
||||||
|
m_layout->removeCell(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
if (m_layout) {
|
||||||
m_layout->polish();
|
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;
|
return m_naturalX;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpoCell::setNaturalX(int x)
|
void ExpoCell::setNaturalX(qreal x)
|
||||||
{
|
{
|
||||||
if (m_naturalX != x) {
|
if (m_naturalX != x) {
|
||||||
m_naturalX = x;
|
m_naturalX = x;
|
||||||
update();
|
updateLayout();
|
||||||
Q_EMIT naturalXChanged();
|
Q_EMIT naturalXChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExpoCell::naturalY() const
|
qreal ExpoCell::naturalY() const
|
||||||
{
|
{
|
||||||
return m_naturalY;
|
return m_naturalY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpoCell::setNaturalY(int y)
|
void ExpoCell::setNaturalY(qreal y)
|
||||||
{
|
{
|
||||||
if (m_naturalY != y) {
|
if (m_naturalY != y) {
|
||||||
m_naturalY = y;
|
m_naturalY = y;
|
||||||
update();
|
updateLayout();
|
||||||
Q_EMIT naturalYChanged();
|
Q_EMIT naturalYChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExpoCell::naturalWidth() const
|
qreal ExpoCell::naturalWidth() const
|
||||||
{
|
{
|
||||||
return m_naturalWidth;
|
return m_naturalWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpoCell::setNaturalWidth(int width)
|
void ExpoCell::setNaturalWidth(qreal width)
|
||||||
{
|
{
|
||||||
if (m_naturalWidth != width) {
|
if (m_naturalWidth != width) {
|
||||||
m_naturalWidth = width;
|
m_naturalWidth = width;
|
||||||
update();
|
updateLayout();
|
||||||
Q_EMIT naturalWidthChanged();
|
Q_EMIT naturalWidthChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExpoCell::naturalHeight() const
|
qreal ExpoCell::naturalHeight() const
|
||||||
{
|
{
|
||||||
return m_naturalHeight;
|
return m_naturalHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpoCell::setNaturalHeight(int height)
|
void ExpoCell::setNaturalHeight(qreal height)
|
||||||
{
|
{
|
||||||
if (m_naturalHeight != height) {
|
if (m_naturalHeight != height) {
|
||||||
m_naturalHeight = height;
|
m_naturalHeight = height;
|
||||||
update();
|
updateLayout();
|
||||||
Q_EMIT naturalHeightChanged();
|
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;
|
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
|
QString ExpoCell::persistentKey() const
|
||||||
{
|
{
|
||||||
return m_persistentKey;
|
return m_persistentKey;
|
||||||
|
@ -197,17 +238,17 @@ void ExpoCell::setPersistentKey(const QString &key)
|
||||||
{
|
{
|
||||||
if (m_persistentKey != key) {
|
if (m_persistentKey != key) {
|
||||||
m_persistentKey = key;
|
m_persistentKey = key;
|
||||||
update();
|
updateLayout();
|
||||||
Q_EMIT persistentKeyChanged();
|
Q_EMIT persistentKeyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExpoCell::bottomMargin() const
|
qreal ExpoCell::bottomMargin() const
|
||||||
{
|
{
|
||||||
return m_margins.bottom();
|
return m_margins.bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpoCell::setBottomMargin(int margin)
|
void ExpoCell::setBottomMargin(qreal margin)
|
||||||
{
|
{
|
||||||
if (m_margins.bottom() != margin) {
|
if (m_margins.bottom() != margin) {
|
||||||
m_margins.setBottom(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)
|
ExpoLayout::ExpoLayout(QQuickItem *parent)
|
||||||
: QQuickItem(parent)
|
: QQuickItem(parent)
|
||||||
{
|
{
|
||||||
|
@ -253,6 +319,13 @@ void ExpoLayout::forceLayout()
|
||||||
updatePolish();
|
updatePolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExpoLayout::updateCellsMapping()
|
||||||
|
{
|
||||||
|
for (ExpoCell *cell : m_cells) {
|
||||||
|
cell->polish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExpoLayout::addCell(ExpoCell *cell)
|
void ExpoLayout::addCell(ExpoCell *cell)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_cells.contains(cell));
|
Q_ASSERT(!m_cells.contains(cell));
|
||||||
|
@ -308,9 +381,9 @@ void ExpoLayout::updatePolish()
|
||||||
|
|
||||||
QList<QRectF> windowSizes;
|
QList<QRectF> windowSizes;
|
||||||
for (ExpoCell *cell : std::as_const(m_cells)) {
|
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);
|
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);
|
auto windowLayouts = ExpoLayout::layout(area, windowSizes);
|
||||||
for (int i = 0; i < windowLayouts.size(); ++i) {
|
for (int i = 0; i < windowLayouts.size(); ++i) {
|
||||||
|
@ -324,10 +397,18 @@ void ExpoLayout::updatePolish()
|
||||||
|
|
||||||
QRectF rect = cell->naturalRect();
|
QRectF rect = cell->naturalRect();
|
||||||
moveToFit(rect, target);
|
moveToFit(rect, target);
|
||||||
cell->setX(rect.x());
|
if (m_ready) {
|
||||||
cell->setY(rect.y());
|
// Use setProperty so the QML side can animate with Behavior
|
||||||
cell->setWidth(rect.width());
|
cell->setProperty("x", rect.x());
|
||||||
cell->setHeight(rect.height());
|
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();
|
setReady();
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ public:
|
||||||
void setReady();
|
void setReady();
|
||||||
|
|
||||||
Q_INVOKABLE void forceLayout();
|
Q_INVOKABLE void forceLayout();
|
||||||
|
Q_INVOKABLE void updateCellsMapping();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||||
|
@ -185,95 +186,100 @@ private:
|
||||||
qreal m_maxScale = 1.0;
|
qreal m_maxScale = 1.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExpoCell : public QObject
|
class ExpoCell : public QQuickItem
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(ExpoLayout *layout READ layout WRITE setLayout NOTIFY layoutChanged)
|
Q_PROPERTY(ExpoLayout *layout READ layout WRITE setLayout NOTIFY layoutChanged)
|
||||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
|
Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged)
|
||||||
Q_PROPERTY(int naturalX READ naturalX WRITE setNaturalX NOTIFY naturalXChanged)
|
Q_PROPERTY(qreal partialActivationFactor READ partialActivationFactor WRITE setPartialActivationFactor NOTIFY partialActivationFactorChanged)
|
||||||
Q_PROPERTY(int naturalY READ naturalY WRITE setNaturalY NOTIFY naturalYChanged)
|
Q_PROPERTY(bool shouldLayout READ shouldLayout WRITE setShouldLayout NOTIFY shouldLayoutChanged)
|
||||||
Q_PROPERTY(int naturalWidth READ naturalWidth WRITE setNaturalWidth NOTIFY naturalWidthChanged)
|
Q_PROPERTY(qreal offsetX READ offsetX WRITE setOffsetX NOTIFY offsetXChanged)
|
||||||
Q_PROPERTY(int naturalHeight READ naturalHeight WRITE setNaturalHeight NOTIFY naturalHeightChanged)
|
Q_PROPERTY(qreal offsetY READ offsetY WRITE setOffsetY NOTIFY offsetYChanged)
|
||||||
Q_PROPERTY(int x READ x NOTIFY xChanged)
|
Q_PROPERTY(qreal naturalX READ naturalX WRITE setNaturalX NOTIFY naturalXChanged)
|
||||||
Q_PROPERTY(int y READ y NOTIFY yChanged)
|
Q_PROPERTY(qreal naturalY READ naturalY WRITE setNaturalY NOTIFY naturalYChanged)
|
||||||
Q_PROPERTY(int width READ width NOTIFY widthChanged)
|
Q_PROPERTY(qreal naturalWidth READ naturalWidth WRITE setNaturalWidth NOTIFY naturalWidthChanged)
|
||||||
Q_PROPERTY(int height READ height NOTIFY heightChanged)
|
Q_PROPERTY(qreal naturalHeight READ naturalHeight WRITE setNaturalHeight NOTIFY naturalHeightChanged)
|
||||||
Q_PROPERTY(QString persistentKey READ persistentKey WRITE setPersistentKey NOTIFY persistentKeyChanged)
|
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:
|
public:
|
||||||
explicit ExpoCell(QObject *parent = nullptr);
|
explicit ExpoCell(QQuickItem *parent = nullptr);
|
||||||
~ExpoCell() override;
|
~ExpoCell() override;
|
||||||
|
|
||||||
bool isEnabled() const;
|
void componentComplete() override;
|
||||||
void setEnabled(bool enabled);
|
|
||||||
|
|
||||||
ExpoLayout *layout() const;
|
ExpoLayout *layout() const;
|
||||||
void setLayout(ExpoLayout *layout);
|
void setLayout(ExpoLayout *layout);
|
||||||
|
|
||||||
int naturalX() const;
|
bool shouldLayout() const;
|
||||||
void setNaturalX(int x);
|
void setShouldLayout(bool layout);
|
||||||
|
|
||||||
int naturalY() const;
|
QQuickItem *contentItem() const;
|
||||||
void setNaturalY(int y);
|
void setContentItem(QQuickItem *item);
|
||||||
|
|
||||||
int naturalWidth() const;
|
qreal partialActivationFactor() const;
|
||||||
void setNaturalWidth(int width);
|
void setPartialActivationFactor(qreal factor);
|
||||||
|
|
||||||
int naturalHeight() const;
|
qreal offsetX() const;
|
||||||
void setNaturalHeight(int height);
|
void setOffsetX(qreal x);
|
||||||
|
|
||||||
QRect naturalRect() const;
|
qreal offsetY() const;
|
||||||
QMargins margins() const;
|
void setOffsetY(qreal y);
|
||||||
|
|
||||||
int x() const;
|
qreal naturalX() const;
|
||||||
void setX(int x);
|
void setNaturalX(qreal x);
|
||||||
|
|
||||||
int y() const;
|
qreal naturalY() const;
|
||||||
void setY(int y);
|
void setNaturalY(qreal y);
|
||||||
|
|
||||||
int width() const;
|
qreal naturalWidth() const;
|
||||||
void setWidth(int width);
|
void setNaturalWidth(qreal width);
|
||||||
|
|
||||||
int height() const;
|
qreal naturalHeight() const;
|
||||||
void setHeight(int height);
|
void setNaturalHeight(qreal height);
|
||||||
|
|
||||||
|
QRectF naturalRect() const;
|
||||||
|
QMarginsF margins() const;
|
||||||
|
|
||||||
QString persistentKey() const;
|
QString persistentKey() const;
|
||||||
void setPersistentKey(const QString &key);
|
void setPersistentKey(const QString &key);
|
||||||
|
|
||||||
int bottomMargin() const;
|
qreal bottomMargin() const;
|
||||||
void setBottomMargin(int margin);
|
void setBottomMargin(qreal margin);
|
||||||
|
|
||||||
public Q_SLOTS:
|
protected:
|
||||||
void update();
|
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void layoutChanged();
|
void layoutChanged();
|
||||||
void enabledChanged();
|
void shouldLayoutChanged();
|
||||||
|
void contentItemChanged();
|
||||||
|
void partialActivationFactorChanged();
|
||||||
|
void offsetXChanged();
|
||||||
|
void offsetYChanged();
|
||||||
void naturalXChanged();
|
void naturalXChanged();
|
||||||
void naturalYChanged();
|
void naturalYChanged();
|
||||||
void naturalWidthChanged();
|
void naturalWidthChanged();
|
||||||
void naturalHeightChanged();
|
void naturalHeightChanged();
|
||||||
void xChanged();
|
|
||||||
void yChanged();
|
|
||||||
void widthChanged();
|
|
||||||
void heightChanged();
|
|
||||||
void persistentKeyChanged();
|
void persistentKeyChanged();
|
||||||
void bottomMarginChanged();
|
void bottomMarginChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateContentItemGeometry();
|
||||||
|
void updateLayout();
|
||||||
|
|
||||||
QString m_persistentKey;
|
QString m_persistentKey;
|
||||||
bool m_enabled = true;
|
qreal m_offsetX = 0;
|
||||||
int m_naturalX = 0;
|
qreal m_offsetY = 0;
|
||||||
int m_naturalY = 0;
|
qreal m_naturalX = 0;
|
||||||
int m_naturalWidth = 0;
|
qreal m_naturalY = 0;
|
||||||
int m_naturalHeight = 0;
|
qreal m_naturalWidth = 0;
|
||||||
QMargins m_margins;
|
qreal m_naturalHeight = 0;
|
||||||
std::optional<int> m_x;
|
QMarginsF m_margins;
|
||||||
std::optional<int> m_y;
|
|
||||||
std::optional<int> m_width;
|
|
||||||
std::optional<int> m_height;
|
|
||||||
QPointer<ExpoLayout> m_layout;
|
QPointer<ExpoLayout> m_layout;
|
||||||
|
QPointer<QQuickItem> m_contentItem;
|
||||||
|
qreal m_partialActivationFactor = 1.0;
|
||||||
|
bool m_shouldLayout = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -136,6 +136,7 @@ FocusScope {
|
||||||
|
|
||||||
delegate: WindowHeapDelegate {
|
delegate: WindowHeapDelegate {
|
||||||
windowHeap: heap
|
windowHeap: heap
|
||||||
|
layout: expoLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
onObjectRemoved: (index, object) => {
|
onObjectRemoved: (index, object) => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||||
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
|
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
|
||||||
|
SPDX-FileCopyrightText: 2024 Marco Martin <mart@kde.org>
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
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.plasma.extras as PlasmaExtras
|
||||||
import org.kde.ksvg 1.0 as KSvg
|
import org.kde.ksvg 1.0 as KSvg
|
||||||
|
|
||||||
Item {
|
ExpoCell {
|
||||||
id: thumb
|
id: thumb
|
||||||
|
|
||||||
required property QtObject window
|
required property QtObject window
|
||||||
|
@ -23,8 +24,9 @@ Item {
|
||||||
required property Item windowHeap
|
required property Item windowHeap
|
||||||
|
|
||||||
readonly property bool selected: windowHeap.selectedIndex === index
|
readonly property bool selected: windowHeap.selectedIndex === index
|
||||||
property real partialActivationFactor: effect.partialActivationFactor
|
|
||||||
property bool gestureInProgress: effect.gestureInProgress
|
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"
|
// no desktops is a special value which means "All Desktops"
|
||||||
readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1
|
readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1
|
||||||
|
@ -61,125 +63,161 @@ Item {
|
||||||
readonly property alias downGestureProgress: touchDragHandler.downGestureProgress
|
readonly property alias downGestureProgress: touchDragHandler.downGestureProgress
|
||||||
signal downGestureTriggered()
|
signal downGestureTriggered()
|
||||||
|
|
||||||
// "normal" | "pressed" | "drag" | "reparenting"
|
property bool isReady: width !== 0 && height !== 0
|
||||||
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)
|
|
||||||
|
|
||||||
function restoreDND(oldGlobalRect: rect) {
|
function restoreDND(oldGlobalRect: rect) {
|
||||||
thumbSource.restoreDND(oldGlobalRect);
|
thumbSource.restoreDND(oldGlobalRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
component TweenBehavior : Behavior {
|
layout: windowHeap.layout
|
||||||
enabled: thumb.state === "active-normal" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active
|
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 {
|
NumberAnimation {
|
||||||
duration: thumb.windowHeap.animationDuration
|
duration: thumb.windowHeap.animationDuration
|
||||||
easing.type: Easing.InOutCubic
|
easing.type: Easing.InOutCubic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TweenBehavior on x {}
|
contentItem: Item {
|
||||||
TweenBehavior on y {}
|
id: mainContent
|
||||||
TweenBehavior on width {}
|
parent: contentItemParent
|
||||||
TweenBehavior on height {}
|
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 {
|
KWinComponents.WindowThumbnail {
|
||||||
id: thumbSource
|
id: thumbSource
|
||||||
wId: thumb.window.internalId
|
wId: thumb.window.internalId
|
||||||
|
scale: targetScale
|
||||||
|
width: mainContent.width
|
||||||
|
height: mainContent.height
|
||||||
|
|
||||||
Drag.proposedAction: Qt.MoveAction
|
Binding on width {
|
||||||
Drag.supportedActions: Qt.MoveAction
|
value: mainContent.width
|
||||||
Drag.source: thumb.window
|
when: !returnAnimation.active
|
||||||
Drag.hotSpot: Qt.point(
|
}
|
||||||
thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale,
|
Binding on height {
|
||||||
thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale)
|
value: mainContent.height
|
||||||
Drag.keys: ["kwin-window"]
|
when: !returnAnimation.active
|
||||||
|
}
|
||||||
|
|
||||||
onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
|
Drag.proposedAction: Qt.MoveAction
|
||||||
onYChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
|
Drag.supportedActions: Qt.MoveAction
|
||||||
|
Drag.source: thumb.window
|
||||||
|
Drag.hotSpot: Qt.point(
|
||||||
|
thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale,
|
||||||
|
thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale)
|
||||||
|
Drag.keys: ["kwin-window"]
|
||||||
|
|
||||||
function saveDND() {
|
onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
|
||||||
const oldGlobalRect = mapToItem(null, 0, 0, width, height);
|
onYChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
|
||||||
thumb.windowHeap.saveDND(thumb.window.internalId, oldGlobalRect);
|
|
||||||
}
|
|
||||||
function restoreDND(oldGlobalRect: rect) {
|
|
||||||
thumb.substate = "reparenting";
|
|
||||||
|
|
||||||
const newGlobalRect = mapFromItem(null, oldGlobalRect);
|
function saveDND() {
|
||||||
|
const oldGlobalRect = mapToItem(null, 0, 0, width, height);
|
||||||
|
thumb.windowHeap.saveDND(thumb.window.internalId, oldGlobalRect);
|
||||||
|
}
|
||||||
|
function restoreDND(oldGlobalRect: rect) {
|
||||||
|
const newGlobalRect = mapFromItem(null, oldGlobalRect);
|
||||||
|
// Disable bindings
|
||||||
|
returnAnimation.active = true;
|
||||||
|
x = newGlobalRect.x;
|
||||||
|
y = newGlobalRect.y;
|
||||||
|
width = newGlobalRect.width;
|
||||||
|
height = newGlobalRect.height;
|
||||||
|
returnAnimation.restart();
|
||||||
|
}
|
||||||
|
function deleteDND() {
|
||||||
|
thumb.windowHeap.deleteDND(thumb.window.internalId);
|
||||||
|
}
|
||||||
|
|
||||||
x = newGlobalRect.x;
|
// Not using FrameSvg hover element intentionally for stylistic reasons
|
||||||
y = newGlobalRect.y;
|
Rectangle {
|
||||||
width = newGlobalRect.width;
|
border.width: Kirigami.Units.largeSpacing
|
||||||
height = newGlobalRect.height;
|
border.color: Kirigami.Theme.highlightColor
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: -border.width
|
||||||
|
radius: Kirigami.Units.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
visible: !thumb.windowHeap.dragActive && (hoverHandler.hovered || (thumb.selected && Window.window.activeFocusItem)) && windowHeap.effectiveOrganized
|
||||||
|
}
|
||||||
|
|
||||||
thumb.substate = "normal";
|
MouseArea {
|
||||||
}
|
anchors.fill: parent
|
||||||
function deleteDND() {
|
acceptedButtons: Qt.NoButton
|
||||||
thumb.windowHeap.deleteDND(thumb.window.internalId);
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not using FrameSvg hover element intentionally for stylistic reasons
|
|
||||||
Rectangle {
|
|
||||||
border.width: Kirigami.Units.largeSpacing
|
|
||||||
border.color: Kirigami.Theme.highlightColor
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: -border.width
|
|
||||||
radius: Kirigami.Units.cornerRadius
|
|
||||||
color: "transparent"
|
|
||||||
visible: !thumb.windowHeap.dragActive && (hoverHandler.hovered || (thumb.selected && Window.window.activeFocusItem)) && windowHeap.effectiveOrganized
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
cursorShape: thumb.activeDragHandler.active ? Qt.ClosedHandCursor : Qt.ArrowCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PC3.Label {
|
|
||||||
anchors.fill: thumbSource
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
text: i18nd("kwin", "Drag Down To Close")
|
|
||||||
opacity: thumbSource.opacity
|
|
||||||
visible: !thumb.activeHidden && touchDragHandler.active
|
|
||||||
}
|
|
||||||
|
|
||||||
Kirigami.Icon {
|
|
||||||
id: icon
|
|
||||||
width: Kirigami.Units.iconSizes.large
|
|
||||||
height: Kirigami.Units.iconSizes.large
|
|
||||||
source: thumb.window.icon
|
|
||||||
anchors.horizontalCenter: thumbSource.horizontalCenter
|
|
||||||
anchors.bottom: thumbSource.bottom
|
|
||||||
anchors.bottomMargin: -Math.round(height / 4)
|
|
||||||
visible: !thumb.activeHidden && !activeDragHandler.active
|
|
||||||
PC3.Label {
|
PC3.Label {
|
||||||
id: caption
|
anchors.fill: thumbSource
|
||||||
visible: thumb.windowTitleVisible
|
|
||||||
width: cell.width
|
|
||||||
maximumLineCount: 1
|
|
||||||
anchors.top: parent.bottom
|
|
||||||
anchors.topMargin: Kirigami.Units.smallSpacing
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
text: thumb.window.caption
|
|
||||||
color: Kirigami.Theme.textColor
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
text: i18nd("kwin", "Drag Down To Close")
|
||||||
|
visible: !thumb.activeHidden && touchDragHandler.active
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: parent.contentHeight + Kirigami.Units.smallSpacing
|
height: parent.contentHeight + Kirigami.Units.smallSpacing
|
||||||
|
@ -188,288 +226,157 @@ Item {
|
||||||
radius: Kirigami.Units.cornerRadius
|
radius: Kirigami.Units.cornerRadius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ExpoCell {
|
Kirigami.Icon {
|
||||||
id: cell
|
id: icon
|
||||||
layout: windowHeap.layout
|
width: Kirigami.Units.iconSizes.large
|
||||||
enabled: !thumb.activeHidden
|
height: Kirigami.Units.iconSizes.large
|
||||||
naturalX: thumb.window.x
|
opacity: partialActivationFactor
|
||||||
naturalY: thumb.window.y
|
scale: Math.min(1.0, mainContent.width / Math.max(0.01, thumb.width))
|
||||||
naturalWidth: thumb.window.width
|
source: thumb.window.icon
|
||||||
naturalHeight: thumb.window.height
|
anchors.horizontalCenter: thumbSource.horizontalCenter
|
||||||
persistentKey: thumb.window.internalId
|
anchors.verticalCenter: thumbSource.bottom
|
||||||
bottomMargin: icon.height / 4 + (thumb.windowTitleVisible ? caption.height : 0)
|
anchors.verticalCenterOffset: -Math.round(height / 4) * scale
|
||||||
property bool isReady: width !== 0 && height !== 0
|
visible: !thumb.activeHidden && !activeDragHandler.active && !returnAnimation.running
|
||||||
}
|
PC3.Label {
|
||||||
|
id: caption
|
||||||
states: [
|
visible: thumb.windowTitleVisible
|
||||||
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
|
width: thumb.width
|
||||||
height: thumb.height
|
maximumLineCount: 1
|
||||||
}
|
anchors.top: parent.bottom
|
||||||
PropertyChanges {
|
anchors.topMargin: Kirigami.Units.smallSpacing
|
||||||
target: icon
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
opacity: thumb.partialActivationFactor
|
elide: Text.ElideRight
|
||||||
}
|
text: thumb.window.caption
|
||||||
PropertyChanges {
|
color: Kirigami.Theme.textColor
|
||||||
target: closeButton
|
textFormat: Text.PlainText
|
||||||
opacity: thumb.partialActivationFactor
|
horizontalAlignment: Text.AlignHCenter
|
||||||
}
|
verticalAlignment: Text.AlignVCenter
|
||||||
},
|
background: Rectangle {
|
||||||
State {
|
anchors.centerIn: parent
|
||||||
name: "initial-hidden"
|
height: parent.contentHeight + Kirigami.Units.smallSpacing
|
||||||
extend: "initial"
|
width: parent.contentWidth + Kirigami.Units.smallSpacing
|
||||||
PropertyChanges {
|
color: Kirigami.Theme.backgroundColor
|
||||||
target: thumb
|
radius: Kirigami.Units.cornerRadius
|
||||||
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 {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
onHoveredChanged: if (hovered !== selected) {
|
onHoveredChanged: if (hovered !== selected) {
|
||||||
thumb.windowHeap.resetSelected();
|
thumb.windowHeap.resetSelected();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onTapped: {
|
|
||||||
KWinComponents.Workspace.activeWindow = thumb.window;
|
|
||||||
thumb.windowHeap.activated();
|
|
||||||
}
|
|
||||||
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 {
|
TapHandler {
|
||||||
target: null
|
acceptedButtons: Qt.LeftButton
|
||||||
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
onTapped: {
|
||||||
// This does not work when moving pointer fast and pressing along the way
|
KWinComponents.Workspace.activeWindow = thumb.window;
|
||||||
// See also QTBUG-105903, QTBUG-105904
|
thumb.windowHeap.activated();
|
||||||
// enabled: thumb.state !== "active-normal"
|
}
|
||||||
|
onPressedChanged: {
|
||||||
|
if (pressed) {
|
||||||
|
thumbSource.Drag.active = true;
|
||||||
|
} else if (!thumb.activeDragHandler.active) {
|
||||||
|
thumbSource.Drag.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onActiveChanged: {
|
component DragManager : DragHandler {
|
||||||
thumb.windowHeap.dragActive = active;
|
target: thumbSource
|
||||||
if (active) {
|
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||||
thumb.activeDragHandler = this;
|
// This does not work when moving pointer fast and pressing along the way
|
||||||
thumb.substate = "drag";
|
// See also QTBUG-105903, QTBUG-105904
|
||||||
} else {
|
// enabled: thumb.state !== "active-normal"
|
||||||
thumbSource.saveDND();
|
|
||||||
|
|
||||||
var action = thumbSource.Drag.drop();
|
onActiveChanged: {
|
||||||
if (action === Qt.MoveAction) {
|
thumb.windowHeap.dragActive = active;
|
||||||
// This whole component is in the process of being destroyed due to drop onto
|
if (active) {
|
||||||
// another virtual desktop (not another screen).
|
thumb.activeDragHandler = this;
|
||||||
if (typeof thumbSource !== "undefined") {
|
} else {
|
||||||
// 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.saveDND();
|
||||||
thumbSource.deleteDND();
|
returnAnimation.restart();
|
||||||
thumb.substate = "normal";
|
|
||||||
|
var action = thumbSource.Drag.drop();
|
||||||
|
if (action === Qt.MoveAction) {
|
||||||
|
// This whole component is in the process of being destroyed due to drop onto
|
||||||
|
// another virtual desktop (not another screen).
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
|
var globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
|
||||||
effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
|
effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
|
||||||
|
|
||||||
if (typeof thumbSource !== "undefined") {
|
if (typeof thumbSource !== "undefined") {
|
||||||
// else, return to normal without reparenting
|
// else, return to normal without reparenting
|
||||||
thumbSource.deleteDND();
|
thumbSource.deleteDND();
|
||||||
thumb.substate = "normal";
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DragManager {
|
DragManager {
|
||||||
id: dragHandler
|
id: dragHandler
|
||||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
|
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
|
||||||
}
|
|
||||||
|
|
||||||
DragManager {
|
|
||||||
id: touchDragHandler
|
|
||||||
acceptedDevices: PointerDevice.TouchScreen
|
|
||||||
readonly property double downGestureProgress: {
|
|
||||||
if (!active) {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const startDistance = thumb.windowHeap.Kirigami.ScenePosition.y + thumb.windowHeap.height - centroid.scenePressPosition.y;
|
|
||||||
const localPosition = thumb.windowHeap.Kirigami.ScenePosition.y + thumb.windowHeap.height - centroid.scenePosition.y;
|
|
||||||
return 1 - Math.min(localPosition/startDistance, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onActiveChanged: {
|
DragManager {
|
||||||
if (!active) {
|
id: touchDragHandler
|
||||||
if (downGestureProgress > 0.6) {
|
acceptedDevices: PointerDevice.TouchScreen
|
||||||
thumb.downGestureTriggered();
|
readonly property double downGestureProgress: {
|
||||||
|
if (!active) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDistance = thumb.windowHeap.Kirigami.ScenePosition.y + thumb.windowHeap.height - centroid.scenePressPosition.y;
|
||||||
|
const localPosition = thumb.windowHeap.Kirigami.ScenePosition.y + thumb.windowHeap.height - centroid.scenePosition.y;
|
||||||
|
return 1 - Math.min(localPosition/startDistance, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (!active) {
|
||||||
|
if (downGestureProgress > 0.6) {
|
||||||
|
thumb.downGestureTriggered();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: closeButton
|
id: closeButton
|
||||||
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
|
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
right: thumbSource.right
|
right: thumbSource.right
|
||||||
top: thumbSource.top
|
top: thumbSource.top
|
||||||
margins: Kirigami.Units.smallSpacing
|
margins: Kirigami.Units.smallSpacing
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
icon.name: "window-close"
|
||||||
|
display: PC3.AbstractButton.IconOnly
|
||||||
|
|
||||||
|
PC3.ToolTip.text: text
|
||||||
|
PC3.ToolTip.visible: hovered && display === PC3.AbstractButton.IconOnly
|
||||||
|
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||||
|
Accessible.name: text
|
||||||
|
|
||||||
|
onClicked: thumb.window.closeWindow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
active: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active
|
|
||||||
|
|
||||||
sourceComponent: PC3.Button {
|
Component.onDestruction: {
|
||||||
text: i18ndc("kwin", "@info:tooltip as in: 'close this window'", "Close window")
|
if (selected) {
|
||||||
icon.name: "window-close"
|
windowHeap.resetSelected();
|
||||||
display: PC3.AbstractButton.IconOnly
|
}
|
||||||
|
|
||||||
PC3.ToolTip.text: text
|
|
||||||
PC3.ToolTip.visible: hovered && display === PC3.AbstractButton.IconOnly
|
|
||||||
PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
||||||
Accessible.name: text
|
|
||||||
|
|
||||||
onClicked: thumb.window.closeWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onDestruction: {
|
|
||||||
if (selected) {
|
|
||||||
windowHeap.resetSelected();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,7 @@ Item {
|
||||||
id: delegate
|
id: delegate
|
||||||
windowHeap: heap
|
windowHeap: heap
|
||||||
partialActivationFactor: container.organized ? 1 : 0
|
partialActivationFactor: container.organized ? 1 : 0
|
||||||
|
contentItemParent: container
|
||||||
Behavior on partialActivationFactor {
|
Behavior on partialActivationFactor {
|
||||||
SequentialAnimation {
|
SequentialAnimation {
|
||||||
PropertyAction {
|
PropertyAction {
|
||||||
|
|
Loading…
Reference in a new issue