Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit 5b4eb80c authored by David Edmundson's avatar David Edmundson

Set specific edge cursor shape when resizing

Summary:
Instead of seeing the cursor <--> on the left edge you now see an icon
that looks like |<-  .

This brings kwin decorations in line with GTK CSD icons.

In theory this is also useful to tell which window will resize in the
case of side-by-side windows (regardless of whether borders are on or
not). In practice with the adwaita icon theme I tested with it's not
very intuitive to realise which is which till you learn the icon.

Change is more involved than it should be as Qt::CursorShape doesn't
have these entries, and I don't want to shadow that enum internally or
have
to change kwin effect code.

Specifics depend on cursor icon theme if they are not present it will
fallback to the <--> icon. (Breeze does not have them currently)

Test Plan:
Resized some windows (on X and on Wayland)
Correct icon appeared on Adwaita
Existing icon appeared on Breeze

Reviewers: #plasma

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D13396
parent fcfe8763
......@@ -1291,23 +1291,31 @@ void AbstractClient::updateCursor()
Position m = moveResizePointerMode();
if (!isResizable() || isShade())
m = PositionCenter;
Qt::CursorShape c = Qt::ArrowCursor;
CursorShape c = Qt::ArrowCursor;
switch(m) {
case PositionTopLeft:
c = KWin::ExtendedCursor::SizeNorthWest;
break;
case PositionBottomRight:
c = Qt::SizeFDiagCursor;
c = KWin::ExtendedCursor::SizeSouthEast;
break;
case PositionBottomLeft:
c = KWin::ExtendedCursor::SizeSouthWest;
break;
case PositionTopRight:
c = Qt::SizeBDiagCursor;
c = KWin::ExtendedCursor::SizeNorthEast;
break;
case PositionTop:
c = KWin::ExtendedCursor::SizeNorth;
break;
case PositionBottom:
c = Qt::SizeVerCursor;
c = KWin::ExtendedCursor::SizeSouth;
break;
case PositionLeft:
c = KWin::ExtendedCursor::SizeWest;
break;
case PositionRight:
c = Qt::SizeHorCursor;
c = KWin::ExtendedCursor::SizeEast;
break;
default:
if (isMoveResize())
......
......@@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "options.h"
#include "rules.h"
#include "tabgroup.h"
#include "cursor.h"
#include <memory>
......@@ -602,7 +603,7 @@ public:
/**
* Cursor shape for move/resize mode.
**/
Qt::CursorShape cursor() const {
CursorShape cursor() const {
return m_moveResize.cursor;
}
......@@ -747,7 +748,7 @@ Q_SIGNALS:
void modalChanged();
void quickTileModeChanged();
void moveResizedChanged();
void moveResizeCursorChanged(Qt::CursorShape);
void moveResizeCursorChanged(CursorShape);
void clientStartUserMovedResized(KWin::AbstractClient*);
void clientStepUserMovedResized(KWin::AbstractClient *, const QRect&);
void clientFinishUserMovedResized(KWin::AbstractClient*);
......@@ -1122,7 +1123,7 @@ private:
QRect geometry;
Position pointer = PositionCenter;
bool buttonDown = false;
Qt::CursorShape cursor = Qt::ArrowCursor;
CursorShape cursor = Qt::ArrowCursor;
int startScreen = 0;
QTimer *delayedTimer = nullptr;
} m_moveResize;
......
......@@ -365,28 +365,28 @@ void DecorationInputTest::testHover()
quint32 timestamp = 1;
MOTION(QPoint(c->geometry().center().x(), c->clientPos().y() / 2));
QCOMPARE(c->cursor(), Qt::ArrowCursor);
QCOMPARE(c->cursor(), CursorShape(Qt::ArrowCursor));
MOTION(QPoint(20, 0));
QCOMPARE(c->cursor(), Qt::SizeFDiagCursor);
MOTION(QPoint(c->geometry().x(), 0));
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeNorthWest));
MOTION(QPoint(c->geometry().x() + c->geometry().width() / 2, 0));
QCOMPARE(c->cursor(), Qt::SizeVerCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeNorth));
MOTION(QPoint(c->geometry().x() + c->geometry().width() - 1, 0));
QCOMPARE(c->cursor(), Qt::SizeBDiagCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeNorthEast));
MOTION(QPoint(c->geometry().x() + c->geometry().width() - 1, c->height() / 2));
QCOMPARE(c->cursor(), Qt::SizeHorCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeEast));
MOTION(QPoint(c->geometry().x() + c->geometry().width() - 1, c->height() - 1));
QCOMPARE(c->cursor(), Qt::SizeFDiagCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeSouthEast));
MOTION(QPoint(c->geometry().x() + c->geometry().width() / 2, c->height() - 1));
QCOMPARE(c->cursor(), Qt::SizeVerCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeSouth));
MOTION(QPoint(c->geometry().x(), c->height() - 1));
QCOMPARE(c->cursor(), Qt::SizeBDiagCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeSouthWest));
MOTION(QPoint(c->geometry().x(), c->height() / 2));
QCOMPARE(c->cursor(), Qt::SizeHorCursor);
QCOMPARE(c->cursor(), CursorShape(KWin::ExtendedCursor::SizeWest));
MOTION(c->geometry().center());
QEXPECT_FAIL("", "Cursor not set back on leave", Continue);
QCOMPARE(c->cursor(), Qt::ArrowCursor);
QCOMPARE(c->cursor(), CursorShape(Qt::ArrowCursor));
}
void DecorationInputTest::testPressToMove_data()
......@@ -425,7 +425,7 @@ void DecorationInputTest::testPressToMove()
quint32 timestamp = 1;
MOTION(QPoint(c->geometry().center().x(), c->y() + c->clientPos().y() / 2));
QCOMPARE(c->cursor(), Qt::ArrowCursor);
QCOMPARE(c->cursor(), CursorShape(Qt::ArrowCursor));
PRESS;
QVERIFY(!c->isMove());
......
......@@ -154,7 +154,7 @@ Client::Client()
connect(clientMachine(), &ClientMachine::localhostChanged, this, &Client::updateCaption);
connect(options, &Options::condensedTitleChanged, this, &Client::updateCaption);
connect(this, &Client::moveResizeCursorChanged, this, [this] (Qt::CursorShape cursor) {
connect(this, &Client::moveResizeCursorChanged, this, [this] (CursorShape cursor) {
xcb_cursor_t nativeCursor = Cursor::x11Cursor(cursor);
m_frame.defineCursor(nativeCursor);
if (m_decoInputExtent.isValid())
......
......@@ -125,7 +125,7 @@ void Cursor::setPos(int x, int y)
Cursor::setPos(QPoint(x, y));
}
xcb_cursor_t Cursor::getX11Cursor(Qt::CursorShape shape)
xcb_cursor_t Cursor::getX11Cursor(CursorShape shape)
{
Q_UNUSED(shape)
return XCB_CURSOR_NONE;
......@@ -137,7 +137,7 @@ xcb_cursor_t Cursor::getX11Cursor(const QByteArray &name)
return XCB_CURSOR_NONE;
}
xcb_cursor_t Cursor::x11Cursor(Qt::CursorShape shape)
xcb_cursor_t Cursor::x11Cursor(CursorShape shape)
{
return s_self->getX11Cursor(shape);
}
......@@ -299,7 +299,46 @@ QVector<QByteArray> Cursor::cursorAlternativeNames(const QByteArray &name) const
QByteArrayLiteral("1081e37283d90000800003c07f3ef6bf"),
QByteArrayLiteral("6407b0e94181790501fd1e167b474872"),
QByteArrayLiteral("b66166c04f8c3109214a4fbd64a50fc8")}},
{QByteArrayLiteral("dnd-move"), {QByteArrayLiteral("move")}}
{QByteArrayLiteral("dnd-move"), {QByteArrayLiteral("move")}},
{QByteArrayLiteral("sw-resize"), {QByteArrayLiteral("size_bdiag"),
QByteArrayLiteral("fcf1c3c7cd4491d801f1e1c78f100000"),
QByteArrayLiteral("fd_double_arrow"),
QByteArrayLiteral("bottom_left_corner")}},
{QByteArrayLiteral("se-resize"), {QByteArrayLiteral("size_fdiag"),
QByteArrayLiteral("c7088f0f3e6c8088236ef8e1e3e70000"),
QByteArrayLiteral("bd_double_arrow"),
QByteArrayLiteral("bottom_right_corner")}},
{QByteArrayLiteral("ne-resize"), {QByteArrayLiteral("size_bdiag"),
QByteArrayLiteral("fcf1c3c7cd4491d801f1e1c78f100000"),
QByteArrayLiteral("fd_double_arrow"),
QByteArrayLiteral("top_right_corner")}},
{QByteArrayLiteral("nw-resize"), {QByteArrayLiteral("size_fdiag"),
QByteArrayLiteral("c7088f0f3e6c8088236ef8e1e3e70000"),
QByteArrayLiteral("bd_double_arrow"),
QByteArrayLiteral("top_left_corner")}},
{QByteArrayLiteral("n-resize"), {QByteArrayLiteral("size_ver"),
QByteArrayLiteral("00008160000006810000408080010102"),
QByteArrayLiteral("sb_v_double_arrow"),
QByteArrayLiteral("v_double_arrow"),
QByteArrayLiteral("col-resize"),
QByteArrayLiteral("top_side")}},
{QByteArrayLiteral("e-resize"), {QByteArrayLiteral("size_hor"),
QByteArrayLiteral("028006030e0e7ebffc7f7070c0600140"),
QByteArrayLiteral("sb_h_double_arrow"),
QByteArrayLiteral("h_double_arrow"),
QByteArrayLiteral("row-resize"),
QByteArrayLiteral("left_side")}},
{QByteArrayLiteral("s-resize"), {QByteArrayLiteral("size_ver"),
QByteArrayLiteral("00008160000006810000408080010102"),
QByteArrayLiteral("sb_v_double_arrow"),
QByteArrayLiteral("v_double_arrow"),
QByteArrayLiteral("col-resize"),
QByteArrayLiteral("bottom_side")}},
{QByteArrayLiteral("w-resize"), {QByteArrayLiteral("size_hor"),
QByteArrayLiteral("028006030e0e7ebffc7f7070c0600140"),
QByteArrayLiteral("sb_h_double_arrow"),
QByteArrayLiteral("h_double_arrow"),
QByteArrayLiteral("right_side")}}
};
auto it = alternatives.find(name);
if (it != alternatives.end()) {
......@@ -308,51 +347,67 @@ QVector<QByteArray> Cursor::cursorAlternativeNames(const QByteArray &name) const
return QVector<QByteArray>();
}
QByteArray Cursor::cursorName(Qt::CursorShape shape) const
QByteArray CursorShape::name() const
{
switch (shape) {
switch (m_shape) {
case Qt::ArrowCursor:
return QByteArray("left_ptr");
return QByteArrayLiteral("left_ptr");
case Qt::UpArrowCursor:
return QByteArray("up_arrow");
return QByteArrayLiteral("up_arrow");
case Qt::CrossCursor:
return QByteArray("cross");
return QByteArrayLiteral("cross");
case Qt::WaitCursor:
return QByteArray("wait");
return QByteArrayLiteral("wait");
case Qt::IBeamCursor:
return QByteArray("ibeam");
return QByteArrayLiteral("ibeam");
case Qt::SizeVerCursor:
return QByteArray("size_ver");
return QByteArrayLiteral("size_ver");
case Qt::SizeHorCursor:
return QByteArray("size_hor");
return QByteArrayLiteral("size_hor");
case Qt::SizeBDiagCursor:
return QByteArray("size_bdiag");
return QByteArrayLiteral("size_bdiag");
case Qt::SizeFDiagCursor:
return QByteArray("size_fdiag");
return QByteArrayLiteral("size_fdiag");
case Qt::SizeAllCursor:
return QByteArray("size_all");
return QByteArrayLiteral("size_all");
case Qt::SplitVCursor:
return QByteArray("split_v");
return QByteArrayLiteral("split_v");
case Qt::SplitHCursor:
return QByteArray("split_h");
return QByteArrayLiteral("split_h");
case Qt::PointingHandCursor:
return QByteArray("pointing_hand");
return QByteArrayLiteral("pointing_hand");
case Qt::ForbiddenCursor:
return QByteArray("forbidden");
return QByteArrayLiteral("forbidden");
case Qt::OpenHandCursor:
return QByteArray("openhand");
return QByteArrayLiteral("openhand");
case Qt::ClosedHandCursor:
return QByteArray("closedhand");
return QByteArrayLiteral("closedhand");
case Qt::WhatsThisCursor:
return QByteArray("whats_this");
return QByteArrayLiteral("whats_this");
case Qt::BusyCursor:
return QByteArray("left_ptr_watch");
return QByteArrayLiteral("left_ptr_watch");
case Qt::DragMoveCursor:
return QByteArray("dnd-move");
return QByteArrayLiteral("dnd-move");
case Qt::DragCopyCursor:
return QByteArray("dnd-copy");
return QByteArrayLiteral("dnd-copy");
case Qt::DragLinkCursor:
return QByteArray("dnd-link");
return QByteArrayLiteral("dnd-link");
case KWin::ExtendedCursor::SizeNorthEast:
return QByteArrayLiteral("ne-resize");
case KWin::ExtendedCursor::SizeNorth:
return QByteArrayLiteral("n-resize");
case KWin::ExtendedCursor::SizeNorthWest:
return QByteArrayLiteral("nw-resize");
case KWin::ExtendedCursor::SizeEast:
return QByteArrayLiteral("e-resize");
case KWin::ExtendedCursor::SizeWest:
return QByteArrayLiteral("w-resize");
case KWin::ExtendedCursor::SizeSouthEast:
return QByteArrayLiteral("se-resize");
case KWin::ExtendedCursor::SizeSouth:
return QByteArrayLiteral("s-resize");
case KWin::ExtendedCursor::SizeSouthWest:
return QByteArrayLiteral("sw-resize");
default:
return QByteArray();
}
......
......@@ -33,6 +33,48 @@ class QTimer;
namespace KWin
{
namespace ExtendedCursor {
enum Shape {
SizeNorthWest = 0x100 + 0,
SizeNorth = 0x100 + 1,
SizeNorthEast = 0x100 + 2,
SizeEast = 0x100 + 3,
SizeWest = 0x100 + 4,
SizeSouthEast = 0x100 + 5,
SizeSouth = 0x100 + 6,
SizeSouthWest = 0x100 + 7
};
}
/**
* Extension of Qt::CursorShape with values not currently present there
*/
/**
* @brief Wrapper round Qt::CursorShape with extensions enums into a single entity
*/
class KWIN_EXPORT CursorShape {
public:
CursorShape(Qt::CursorShape qtShape) {
m_shape = qtShape;
}
CursorShape(KWin::ExtendedCursor::Shape kwinShape) {
m_shape = kwinShape;
}
bool operator==(const CursorShape &o) const {
return m_shape == o.m_shape;
}
operator int() const {
return m_shape;
}
/**
* @brief The name of a cursor shape in the theme.
*/
QByteArray name() const;
private:
int m_shape = Qt::ArrowCursor;
};
/**
* @short Replacement for QCursor.
*
......@@ -93,13 +135,6 @@ public:
* @return int
*/
int themeSize() const;
/**
* @brief The name of a cursor shape in the theme.
*
* @param shape The cursor for which the name needs to be known.
* @return QByteArray
*/
QByteArray cursorName(Qt::CursorShape shape) const;
/**
* @return list of alternative names for the cursor with @p name
**/
......@@ -118,9 +153,9 @@ public:
**/
static void setPos(const QPoint &pos);
static void setPos(int x, int y);
static xcb_cursor_t x11Cursor(Qt::CursorShape shape);
static xcb_cursor_t x11Cursor(CursorShape shape);
/**
* Notice: if available always use the Qt::CursorShape variant to avoid cache duplicates for
* Notice: if available always use the CursorShape variant to avoid cache duplicates for
* ambiguous cursor names in the non existing cursor name spcification
**/
static xcb_cursor_t x11Cursor(const QByteArray &name);
......@@ -147,7 +182,7 @@ protected:
* a null cursor, an implementing subclass should implement this method if it can provide X11
* mouse cursors.
**/
virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
virtual xcb_cursor_t getX11Cursor(CursorShape shape);
/**
* Called from @link x11Cursor to actually retrieve the X11 cursor. Base implementation returns
* a null cursor, an implementing subclass should implement this method if it can provide X11
......
......@@ -144,9 +144,9 @@ void X11Cursor::mousePolled()
}
}
xcb_cursor_t X11Cursor::getX11Cursor(Qt::CursorShape shape)
xcb_cursor_t X11Cursor::getX11Cursor(CursorShape shape)
{
return getX11Cursor(cursorName(shape));
return getX11Cursor(shape.name());
}
xcb_cursor_t X11Cursor::getX11Cursor(const QByteArray &name)
......
......@@ -46,7 +46,7 @@ public:
void notifyCursorChanged();
protected:
virtual xcb_cursor_t getX11Cursor(Qt::CursorShape shape);
virtual xcb_cursor_t getX11Cursor(CursorShape shape);
xcb_cursor_t getX11Cursor(const QByteArray &name) override;
virtual void doSetPos();
virtual void doGetPos();
......
......@@ -1178,7 +1178,7 @@ void CursorImage::updateDragCursor()
// TODO: add the cursor image
}
void CursorImage::loadThemeCursor(Qt::CursorShape shape, Image *image)
void CursorImage::loadThemeCursor(CursorShape shape, Image *image)
{
loadThemeCursor(shape, m_cursors, image);
}
......
......@@ -39,11 +39,11 @@ class SurfaceInterface;
namespace KWin
{
class CursorImage;
class InputRedirection;
class Toplevel;
class WaylandCursorTheme;
class CursorShape;
namespace Decoration
{
......@@ -203,7 +203,7 @@ private:
QImage image;
QPoint hotSpot;
};
void loadThemeCursor(Qt::CursorShape shape, Image *image);
void loadThemeCursor(CursorShape shape, Image *image);
void loadThemeCursor(const QByteArray &shape, Image *image);
template <typename T>
void loadThemeCursor(const T &shape, QHash<T, Image> &cursors, Image *image);
......@@ -235,7 +235,7 @@ private:
Image m_fallbackCursor;
Image m_moveResizeCursor;
Image m_windowSelectionCursor;
QHash<Qt::CursorShape, Image> m_cursors;
QHash<CursorShape, Image> m_cursors;
QHash<QByteArray, Image> m_cursorsByName;
QElapsedTimer m_surfaceRenderedTimer;
struct {
......
......@@ -90,9 +90,9 @@ void WaylandCursorTheme::destroyTheme()
m_theme = nullptr;
}
wl_cursor_image *WaylandCursorTheme::get(Qt::CursorShape shape)
wl_cursor_image *WaylandCursorTheme::get(CursorShape shape)
{
return get(Cursor::self()->cursorName(shape));
return get(shape.name());
}
wl_cursor_image *WaylandCursorTheme::get(const QByteArray &name)
......
......@@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kwin_export.h>
#include <QObject>
#include "cursor.h"
struct wl_cursor_image;
struct wl_cursor_theme;
......@@ -45,7 +46,7 @@ public:
explicit WaylandCursorTheme(KWayland::Client::ShmPool *shm, QObject *parent = nullptr);
virtual ~WaylandCursorTheme();
wl_cursor_image *get(Qt::CursorShape shape);
wl_cursor_image *get(CursorShape shape);
wl_cursor_image *get(const QByteArray &name);
Q_SIGNALS:
......
Markdown is supported
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