Commit 8b054656 authored by David Redondo's avatar David Redondo 🏎 Committed by Nate Graham
Browse files

kcms/cursortheme: Make the cursor preview on hover animated again

I removed that feature when killing the X specific code just using
the pixmap setter in 9f4ad82c5b5c47f6e3a3ed78ce559de4db361c78 (plasma-desktop).
To make it work we can drive the animation ourselves.
parent 5795b25e
Pipeline #162819 passed with stage
in 5 minutes and 43 seconds
......@@ -9,6 +9,8 @@
#include <QHash>
#include <QPixmap>
#include <chrono>
/**
* This is the abstract base class for all cursor themes stored in a
* CursorThemeModel and previewed in a PreviewWidget.
......@@ -41,6 +43,11 @@ public:
PendingDeletionRole,
};
struct CursorImage {
QImage image;
std::chrono::milliseconds delay;
};
CursorTheme()
{
}
......@@ -97,6 +104,10 @@ public:
/// If the theme doesn't have the cursor @p name, it should return a null image.
virtual QImage loadImage(const QString &name, int size = 0) const = 0;
/// Loads the cursor images @p name, with the nominal size @p size.
/// The images should be autocropped to the smallest possible size.
/// If the theme doesn't have the cursor @p name, it should return an empty vector
virtual std::vector<CursorImage> loadImages(const QString &name, int size = 0) const = 0;
/// Loads the cursor @p name, with the nominal size @p size.
/// If the theme doesn't have the cursor @p name, it should return
......
......@@ -79,23 +79,25 @@ public:
{
return pixmap();
}
const std::vector<CursorTheme::CursorImage> &images() const
{
return m_images;
}
private:
int m_boundingSize;
QPixmap m_pixmap;
std::vector<CursorTheme::CursorImage> m_images;
QPoint m_pos;
};
PreviewCursor::PreviewCursor(const CursorTheme *theme, const QString &name, int size)
: m_boundingSize(size > 0 ? size : theme->defaultCursorSize())
, m_images(theme->loadImages(name, size))
{
// Create the preview pixmap
QImage image = theme->loadImage(name, size);
if (image.isNull())
if (m_images.empty())
return;
m_pixmap = QPixmap::fromImage(image);
m_pixmap = QPixmap::fromImage(m_images.front().image);
}
QRect PreviewCursor::rect() const
......@@ -112,6 +114,11 @@ PreviewWidget::PreviewWidget(QQuickItem *parent)
{
setAcceptHoverEvents(true);
current = nullptr;
connect(&m_animationTimer, &QTimer::timeout, this, [this] {
setCursor(QPixmap::fromImage(current->images().at(nextAnimationFrame).image));
m_animationTimer.setInterval(current->images().at(nextAnimationFrame).delay);
nextAnimationFrame = (nextAnimationFrame + 1) % current->images().size();
});
}
PreviewWidget::~PreviewWidget()
......@@ -259,23 +266,35 @@ void PreviewWidget::hoverMoveEvent(QHoverEvent *e)
if (needLayout)
layoutItems();
for (const PreviewCursor *c : qAsConst(list)) {
if (c->rect().contains(e->pos())) {
if (c != current) {
setCursor(QCursor(c->pixmap()));
current = c;
}
return;
}
auto it = std::find_if(list.cbegin(), list.cend(), [e](const PreviewCursor *c) {
return c->rect().contains(e->pos());
});
const PreviewCursor *cursor = it != list.cend() ? *it : nullptr;
if (cursor == std::exchange(current, cursor)) {
return;
}
m_animationTimer.stop();
setCursor(Qt::ArrowCursor);
current = nullptr;
if (current == nullptr) {
setCursor(Qt::ArrowCursor);
return;
}
if (current->images().size() <= 1) {
setCursor(current->pixmap());
return;
}
nextAnimationFrame = 0;
m_animationTimer.setInterval(0);
m_animationTimer.start();
}
void PreviewWidget::hoverLeaveEvent(QHoverEvent *e)
{
Q_UNUSED(e);
m_animationTimer.stop();
unsetCursor();
}
......
......@@ -9,6 +9,7 @@
#include "sortproxymodel.h"
#include <QPointer>
#include <QQuickPaintedItem>
#include <QTimer>
class CursorTheme;
class PreviewCursor;
......@@ -63,4 +64,6 @@ private:
QPointer<SortProxyModel> m_themeModel;
int m_currentIndex;
int m_currentSize;
QTimer m_animationTimer;
size_t nextAnimationFrame;
};
......@@ -209,3 +209,32 @@ QImage XCursorTheme::loadImage(const QString &name, int size) const
return image;
}
std::vector<CursorTheme::CursorImage> XCursorTheme::loadImages(const QString &name, int size) const
{
if (size <= 0)
size = defaultCursorSize();
// Load the images
XcursorImages *xcimages = xcLoadImages(name, size);
if (!xcimages)
xcimages = xcLoadImages(findAlternative(name), size);
if (!xcimages) {
return {};
}
std::vector<CursorImage> images;
images.reserve(xcimages->nimage);
for (int i = 0; i < xcimages->nimage; ++i) {
// Convert the XcursorImage to a QImage, and auto-crop it
const XcursorImage *xcimage = xcimages->images[i];
QImage image(reinterpret_cast<unsigned char *>(xcimage->pixels), xcimage->width, xcimage->height, QImage::Format_ARGB32_Premultiplied);
images.push_back(CursorImage{autoCropImage(image), std::chrono::milliseconds{xcimage->delay}});
}
XcursorImagesDestroy(xcimages);
return images;
}
......@@ -37,7 +37,9 @@ public:
{
return m_inherits;
}
QImage loadImage(const QString &name, int size = 0) const override;
std::vector<CursorImage> loadImages(const QString &name, int size = 0) const override;
qulonglong loadCursor(const QString &name, int size = 0) const override;
/** Returns the size that the XCursor library would use if no
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment