decoration.cpp 11.1 KB
Newer Older
1
/*
2
 * SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5
6
7
8
9
10
11
 */
#include "decoration.h"
#include "decoration_p.h"
#include "decoratedclient.h"
#include "private/decoratedclientprivate.h"
#include "private/decorationbridge.h"
#include "decorationbutton.h"
12
#include "decorationsettings.h"
13

14
#include <QCoreApplication>
15
16
17
18
19
#include <QHoverEvent>

namespace KDecoration2
{

20
namespace {
21
22
DecorationBridge *findBridge(const QVariantList &args)
{
23
24
25
    for (const auto &arg: args) {
        if (auto bridge = arg.toMap().value(QStringLiteral("bridge")).value<DecorationBridge*>()) {
            return bridge;
26
27
        }
    }
28
29
    Q_UNREACHABLE();
}
30
31
32
}

Decoration::Private::Private(Decoration *deco, const QVariantList &args)
33
    : sectionUnderMouse(Qt::NoSection)
34
    , bridge(findBridge(args))
35
    , client(QSharedPointer<DecoratedClient>(new DecoratedClient(deco, bridge)))
36
    , opaque(false)
37
    , q(deco)
38
{
39
    Q_UNUSED(args)
40
41
}

42
void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section)
43
{
44
    if (sectionUnderMouse == section) {
45
46
        return;
    }
47
48
    sectionUnderMouse = section;
    emit q->sectionUnderMouseChanged(sectionUnderMouse);
49
50
}

51
void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition)
52
{
53
    if (titleBar.contains(mousePosition)) {
54
        setSectionUnderMouse(Qt::TitleBarArea);
55
56
57
        return;
    }
    const QSize size = q->size();
58
    const int corner = 2*settings->largeSpacing();
Martin Flöser's avatar
Martin Flöser committed
59
60
    const bool left   = mousePosition.x() < borders.left();
    const bool top    = mousePosition.y() < borders.top();
61
62
    const bool bottom = size.height() - mousePosition.y() <= borders.bottom();
    const bool right  = size.width() - mousePosition.x() <= borders.right();
63
    if (left) {
64
        if (top && mousePosition.y() < titleBar.top() + corner) {
65
            setSectionUnderMouse(Qt::TopLeftSection);
66
        } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
67
            setSectionUnderMouse(Qt::BottomLeftSection);
68
        } else {
69
            setSectionUnderMouse(Qt::LeftSection);
70
71
72
73
        }
        return;
    }
    if (right) {
74
        if (top && mousePosition.y() < titleBar.top() + corner) {
75
            setSectionUnderMouse(Qt::TopRightSection);
76
        } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) {
77
            setSectionUnderMouse(Qt::BottomRightSection);
78
        } else {
79
            setSectionUnderMouse(Qt::RightSection);
80
81
82
83
        }
        return;
    }
    if (bottom) {
84
        if (mousePosition.y() > titleBar.bottom()) {
85
86
87
88
89
90
91
            if (mousePosition.x() < borders.left() + corner) {
                setSectionUnderMouse(Qt::BottomLeftSection);
            } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
                setSectionUnderMouse(Qt::BottomRightSection);
            } else {
                setSectionUnderMouse(Qt::BottomSection);
            }
92
        } else {
93
            setSectionUnderMouse(Qt::TitleBarArea);
94
95
96
97
        }
        return;
    }
    if (top) {
98
        if (mousePosition.y() < titleBar.top()) {
99
100
101
102
103
104
105
            if (mousePosition.x() < borders.left() + corner) {
                setSectionUnderMouse(Qt::TopLeftSection);
            } else if (size.width() - mousePosition.x() <= borders.right() + corner) {
                setSectionUnderMouse(Qt::TopRightSection);
            } else {
                setSectionUnderMouse(Qt::TopSection);
            }
106
        } else {
107
            setSectionUnderMouse(Qt::TitleBarArea);
108
109
110
        }
        return;
    }
111
    setSectionUnderMouse(Qt::NoSection);
112
113
}

114
void Decoration::Private::addButton(DecorationButton *button)
115
{
116
117
    Q_ASSERT(!buttons.contains(button));
    buttons << button;
118
119
    QObject::connect(button, &QObject::destroyed, q,
        [this](QObject *o) {
120
121
122
123
124
125
126
127
            auto it = buttons.begin();
            while (it != buttons.end()) {
                if (*it == static_cast<DecorationButton*>(o)) {
                    it = buttons.erase(it);
                } else {
                    it++;
                }
            }
128
129
130
131
        }
    );
}

132
Decoration::Decoration(QObject *parent, const QVariantList &args)
133
    : QObject(parent)
134
    , d(new Private(this, args))
135
{
136
    connect(this, &Decoration::bordersChanged, this, [this]{ update(); });
137
138
139
140
}

Decoration::~Decoration() = default;

141
142
void Decoration::init()
{
143
    Q_ASSERT(!d->settings.isNull());
144
145
}

146
QWeakPointer<DecoratedClient> Decoration::client() const
147
{
148
    return d->client.toWeakRef();
149
150
151
152
153
}

#define DELEGATE(name) \
void Decoration::name() \
{ \
154
    d->client->d->name(); \
155
156
157
158
159
160
161
162
163
164
165
166
167
}

DELEGATE(requestClose)
DELEGATE(requestContextHelp)
DELEGATE(requestMinimize)
DELEGATE(requestToggleOnAllDesktops)
DELEGATE(requestToggleShade)
DELEGATE(requestToggleKeepAbove)
DELEGATE(requestToggleKeepBelow)
DELEGATE(requestShowWindowMenu)

#undef DELEGATE

Martin Flöser's avatar
Martin Flöser committed
168
169
170
171
172
173
174
175
176
177
void Decoration::requestShowToolTip(const QString &text)
{
    d->client->d->requestShowToolTip(text);
}

void Decoration::requestHideToolTip()
{
    d->client->d->requestHideToolTip();
}

178
179
180
void Decoration::requestToggleMaximization(Qt::MouseButtons buttons)
{
    d->client->d->requestToggleMaximization(buttons);
181
182
}

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
void Decoration::showApplicationMenu(int actionId)
{
    auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) {
        return button->type() == DecorationButtonType::ApplicationMenu;
    });

    if (it != d->buttons.constEnd()) {
        requestShowApplicationMenu((*it)->geometry().toRect(), actionId);
    }
}

void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId)
{
    if (auto *appMenuEnabledPrivate = dynamic_cast<ApplicationMenuEnabledDecoratedClientPrivate *>(d->client->d.get())) {
        appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId);
    }
}

201
#define DELEGATE(name, variableName, type, emitValue) \
Martin Flöser's avatar
Martin Flöser committed
202
203
204
205
206
207
void Decoration::name(type a) \
{ \
    if (d->variableName == a) { \
        return; \
    } \
    d->variableName = a; \
208
    emit variableName##Changed(emitValue); \
Martin Flöser's avatar
Martin Flöser committed
209
210
}

211
212
213
214
DELEGATE(setBorders, borders, const QMargins&, )
DELEGATE(setResizeOnlyBorders, resizeOnlyBorders, const QMargins&, )
DELEGATE(setTitleBar, titleBar, const QRect&, )
DELEGATE(setOpaque, opaque, bool, d->opaque)
215
DELEGATE(setShadow, shadow, const QSharedPointer<DecorationShadow> &, d->shadow)
216
217
218

#undef DELEGATE

Martin Flöser's avatar
Martin Flöser committed
219
220
221
222
223
224
225
#define DELEGATE(name, type) \
type Decoration::name() const \
{ \
    return d->name; \
}

DELEGATE(borders, QMargins)
226
DELEGATE(resizeOnlyBorders, QMargins)
227
DELEGATE(titleBar, QRect)
228
DELEGATE(sectionUnderMouse, Qt::WindowFrameSection)
229
DELEGATE(shadow, QSharedPointer<DecorationShadow>)
Martin Flöser's avatar
Martin Flöser committed
230
231
232

#undef DELEGATE

233
234
235
236
237
bool Decoration::isOpaque() const
{
    return d->opaque;
}

Martin Flöser's avatar
Martin Flöser committed
238
239
240
241
242
#define BORDER(name, Name) \
int Decoration::border##Name() const \
{ \
    return d->borders.name(); \
} \
243
244
245
246
int Decoration::resizeOnlyBorder##Name() const \
{ \
    return d->resizeOnlyBorders.name(); \
}
Martin Flöser's avatar
Martin Flöser committed
247
248
249
250
251
252
253

BORDER(left, Left)
BORDER(right, Right)
BORDER(top, Top)
BORDER(bottom, Bottom)
#undef BORDER

254
255
QSize Decoration::size() const
{
Martin Flöser's avatar
Martin Flöser committed
256
    const QMargins &b = d->borders;
257
    return QSize(d->client->width() + b.left() + b.right(),
258
                 (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom());
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
}

QRect Decoration::rect() const
{
    return QRect(QPoint(0, 0), size());
}

bool Decoration::event(QEvent *event)
{
    switch (event->type()) {
    case QEvent::HoverEnter:
        hoverEnterEvent(static_cast<QHoverEvent*>(event));
        return true;
    case QEvent::HoverLeave:
        hoverLeaveEvent(static_cast<QHoverEvent*>(event));
        return true;
    case QEvent::HoverMove:
        hoverMoveEvent(static_cast<QHoverEvent*>(event));
        return true;
    case QEvent::MouseButtonPress:
        mousePressEvent(static_cast<QMouseEvent*>(event));
        return true;
    case QEvent::MouseButtonRelease:
        mouseReleaseEvent(static_cast<QMouseEvent*>(event));
        return true;
    case QEvent::MouseMove:
        mouseMoveEvent(static_cast<QMouseEvent*>(event));
        return true;
Martin Flöser's avatar
Martin Flöser committed
287
288
289
    case QEvent::Wheel:
        wheelEvent(static_cast<QWheelEvent*>(event));
        return true;
290
291
292
293
294
295
296
    default:
        return QObject::event(event);
    }
}

void Decoration::hoverEnterEvent(QHoverEvent *event)
{
297
    for (DecorationButton *button : d->buttons) {
298
299
        QCoreApplication::instance()->sendEvent(button, event);
    }
300
    d->updateSectionUnderMouse(event->pos());
301
302
303
304
}

void Decoration::hoverLeaveEvent(QHoverEvent *event)
{
305
    for (DecorationButton *button : d->buttons) {
306
307
        QCoreApplication::instance()->sendEvent(button, event);
    }
308
    d->setSectionUnderMouse(Qt::NoSection);
309
310
311
312
}

void Decoration::hoverMoveEvent(QHoverEvent *event)
{
313
    for (DecorationButton *button : d->buttons) {
314
        if (!button->isEnabled() || !button->isVisible()) {
315
316
317
            continue;
        }
        const bool hovered = button->isHovered();
318
        const bool contains = button->contains(event->posF());
319
320
321
322
323
324
325
326
327
328
        if (!hovered && contains) {
            QHoverEvent e(QEvent::HoverEnter, event->posF(), event->oldPosF(), event->modifiers());
            QCoreApplication::instance()->sendEvent(button, &e);
        } else if (hovered && !contains) {
            QHoverEvent e(QEvent::HoverLeave, event->posF(), event->oldPosF(), event->modifiers());
            QCoreApplication::instance()->sendEvent(button, &e);
        } else if (hovered && contains) {
            QCoreApplication::instance()->sendEvent(button, event);
        }
    }
329
    d->updateSectionUnderMouse(event->pos());
330
331
332
333
}

void Decoration::mouseMoveEvent(QMouseEvent *event)
{
334
    for (DecorationButton *button : d->buttons) {
335
336
337
338
339
340
341
342
343
344
        if (button->isPressed()) {
            QCoreApplication::instance()->sendEvent(button, event);
            return;
        }
    }
    // not handled, take care ourselves
}

void Decoration::mousePressEvent(QMouseEvent *event)
{
345
    for (DecorationButton *button : d->buttons) {
346
347
348
349
350
        if (button->isHovered()) {
            if (button->acceptedButtons().testFlag(event->button())) {
                QCoreApplication::instance()->sendEvent(button, event);
            }
            event->setAccepted(true);
351
352
353
354
355
356
357
            return;
        }
    }
}

void Decoration::mouseReleaseEvent(QMouseEvent *event)
{
358
    for (DecorationButton *button : d->buttons) {
359
360
361
362
363
364
        if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) {
            QCoreApplication::instance()->sendEvent(button, event);
            return;
        }
    }
    // not handled, take care ourselves
365
    d->updateSectionUnderMouse(event->pos());
366
367
}

Martin Flöser's avatar
Martin Flöser committed
368
369
void Decoration::wheelEvent(QWheelEvent *event)
{
370
    for (DecorationButton *button : d->buttons) {
371
        if (button->contains(event->posF())) {
372
373
            QCoreApplication::instance()->sendEvent(button, event);
            event->setAccepted(true);
Martin Flöser's avatar
Martin Flöser committed
374
375
376
377
        }
    }
}

378
379
void Decoration::update(const QRect &r)
{
380
    d->bridge->update(this, r.isNull() ? rect() : r);
381
382
}

383
384
385
386
387
void Decoration::update()
{
    update(QRect());
}

388
389
390
391
392
393
394
395
396
397
void Decoration::setSettings(const QSharedPointer< DecorationSettings > &settings)
{
    d->settings = settings;
}

QSharedPointer< DecorationSettings > Decoration::settings() const
{
    return d->settings;
}

398
} // namespace