Skip to content
This repository has been archived by the owner on Apr 29, 2023. It is now read-only.

Commit

Permalink
Set specific edge cursor shape when resizing
Browse files Browse the repository at this point in the history
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
  • Loading branch information
davidedmundson committed Jun 11, 2018
1 parent fcfe876 commit 5b4eb80
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 67 deletions.
18 changes: 13 additions & 5 deletions abstract_client.cpp
Expand Up @@ -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())
Expand Down
7 changes: 4 additions & 3 deletions abstract_client.h
Expand Up @@ -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>

Expand Down Expand Up @@ -602,7 +603,7 @@ class KWIN_EXPORT AbstractClient : public Toplevel
/**
* Cursor shape for move/resize mode.
**/
Qt::CursorShape cursor() const {
CursorShape cursor() const {
return m_moveResize.cursor;
}

Expand Down Expand Up @@ -747,7 +748,7 @@ public Q_SLOTS:
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*);
Expand Down Expand Up @@ -1122,7 +1123,7 @@ public Q_SLOTS:
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;
Expand Down
24 changes: 12 additions & 12 deletions autotests/integration/decoration_input_test.cpp
Expand Up @@ -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()
Expand Down Expand Up @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion client.cpp
Expand Up @@ -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())
Expand Down
107 changes: 81 additions & 26 deletions cursor.cpp
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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()) {
Expand All @@ -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();
}
Expand Down
55 changes: 45 additions & 10 deletions cursor.h
Expand Up @@ -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.
*
Expand Down Expand Up @@ -93,13 +135,6 @@ class KWIN_EXPORT Cursor : public QObject
* @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
**/
Expand All @@ -118,9 +153,9 @@ class KWIN_EXPORT Cursor : public QObject
**/
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);
Expand All @@ -147,7 +182,7 @@ class KWIN_EXPORT Cursor : public QObject
* 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
Expand Down
4 changes: 2 additions & 2 deletions plugins/platforms/x11/standalone/x11cursor.cpp
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion plugins/platforms/x11/standalone/x11cursor.h
Expand Up @@ -46,7 +46,7 @@ class KWIN_EXPORT X11Cursor : public Cursor
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();
Expand Down

0 comments on commit 5b4eb80

Please sign in to comment.