panelview.cpp 42.1 KB
Newer Older
Sebastian Kügler's avatar
Sebastian Kügler committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 *  Copyright 2013 Marco Martin <mart@kde.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

Marco Martin's avatar
Marco Martin committed
19
#include <config-plasma.h>
Aurélien Gâteau's avatar
Aurélien Gâteau committed
20

Sebastian Kügler's avatar
Sebastian Kügler committed
21
#include "panelview.h"
22
#include "shellcorona.h"
Marco Martin's avatar
Marco Martin committed
23
#include "panelshadows_p.h"
24
#include "panelconfigview.h"
25
#include "screenpool.h"
Sebastian Kügler's avatar
Sebastian Kügler committed
26

Marco Martin's avatar
Marco Martin committed
27
#include <QAction>
Marco Martin's avatar
Marco Martin committed
28
#include <QApplication>
29
#include <QDebug>
30
#include <QScreen>
31
32
#include <QQmlEngine>
#include <QQmlContext>
33
#include <QGuiApplication>
Marco Martin's avatar
Marco Martin committed
34
#include <QTimer>
Sebastian Kügler's avatar
Sebastian Kügler committed
35

Sebastian Kügler's avatar
Sebastian Kügler committed
36
37
#include <kactioncollection.h>
#include <kwindowsystem.h>
Marco Martin's avatar
Marco Martin committed
38
39
#include <kwindoweffects.h>

40
#include <plasma_version.h>
Marco Martin's avatar
Marco Martin committed
41
#include <Plasma/Containment>
42
#include <Plasma/Package>
Sebastian Kügler's avatar
Sebastian Kügler committed
43

44
45
46
#include <KWayland/Client/plasmashell.h>
#include <KWayland/Client/surface.h>

47
48
#if HAVE_X11
#include <xcb/xcb.h>
49
#include <NETWM>
50
#include <QX11Info>
Marco Martin's avatar
Marco Martin committed
51
#include <QtPlatformHeaders/QXcbWindowFunctions>
52
53
#endif

54
55
static const int MINSIZE = 10;

56
PanelView::PanelView(ShellCorona *corona, QScreen *targetScreen, QWindow *parent)
Marco Martin's avatar
Marco Martin committed
57
    : PlasmaQuick::ContainmentView(corona, parent),
Marco Martin's avatar
Marco Martin committed
58
59
       m_offset(0),
       m_maxLength(0),
Marco Martin's avatar
Marco Martin committed
60
       m_minLength(0),
61
       m_contentLength(0),
Marco Martin's avatar
Marco Martin committed
62
       m_distance(0),
63
       m_thickness(30),
64
       m_initCompleted(false),
65
       m_alignment(Qt::AlignLeft),
66
       m_corona(corona),
Marco Martin's avatar
Marco Martin committed
67
       m_visibilityMode(NormalPanel),
68
       m_backgroundHints(Plasma::Types::StandardBackground),
69
       m_shellSurface(nullptr)
Sebastian Kügler's avatar
Sebastian Kügler committed
70
{
71
    if (targetScreen) {
72
        setPosition(targetScreen->geometry().center());
73
        setScreenToFollow(targetScreen);
74
75
        setScreen(targetScreen);
    }
Marco Martin's avatar
Marco Martin committed
76
    setResizeMode(QuickViewSharedEngine::SizeRootObjectToView);
Sebastian Kügler's avatar
Sebastian Kügler committed
77
    setClearBeforeRendering(true);
Marco Martin's avatar
Marco Martin committed
78
    setColor(QColor(Qt::transparent));
79
    setFlags(Qt::FramelessWindowHint|Qt::WindowDoesNotAcceptFocus);
Marco Martin's avatar
Marco Martin committed
80

81
82
    connect(&m_theme, &Plasma::Theme::themeChanged, this, &PanelView::updateMask);
    connect(this, &PanelView::backgroundHintsChanged, this, &PanelView::updateMask);
83
    connect(this, &PanelView::backgroundHintsChanged, this, &PanelView::updateEnabledBorders);
84
85
86
87
    // TODO: add finished/componentComplete signal to QuickViewSharedEngine,
    // so we exactly know when rootobject is available
    connect(this, &QuickViewSharedEngine::statusChanged,
            this, &PanelView::handleQmlStatusChange);
88

89
90
    m_positionPaneltimer.setSingleShot(true);
    m_positionPaneltimer.setInterval(150);
91
    connect(&m_positionPaneltimer, &QTimer::timeout, this, &PanelView::restore);
92

93
94
95
    m_unhideTimer.setSingleShot(true);
    m_unhideTimer.setInterval(500);
    connect(&m_unhideTimer, &QTimer::timeout,
96
            this, &PanelView::restoreAutoHide);
97

98
    m_lastScreen = targetScreen;
99
100
    connect(this, SIGNAL(locationChanged(Plasma::Types::Location)),
            &m_positionPaneltimer, SLOT(start()));
101
102
    connect(this, SIGNAL(containmentChanged()),
            this, SLOT(containmentChanged()));
103

David Edmundson's avatar
David Edmundson committed
104
    if (!m_corona->kPackage().isValid()) {
Marco Martin's avatar
Marco Martin committed
105
106
107
        qWarning() << "Invalid home screen package";
    }

108
109
    m_strutsTimer.setSingleShot(true);
    connect(&m_strutsTimer, &QTimer::timeout,
Marco Martin's avatar
Marco Martin committed
110
            this, &PanelView::updateStruts);
111

Marco Martin's avatar
Marco Martin committed
112
    qmlRegisterType<QScreen>();
113
    rootContext()->setContextProperty(QStringLiteral("panel"), this);
114
    setSource(m_corona->kPackage().fileUrl("views", QStringLiteral("Panel.qml")));
Sebastian Kügler's avatar
Sebastian Kügler committed
115
116
117
118
}

PanelView::~PanelView()
{
Marco Martin's avatar
Marco Martin committed
119
    if (containment()) {
120
        m_corona->requestApplicationConfigSync();
Marco Martin's avatar
Marco Martin committed
121
    }
Sebastian Kügler's avatar
Sebastian Kügler committed
122
123
}

124
KConfigGroup PanelView::panelConfig(ShellCorona *corona, Plasma::Containment *containment, QScreen *screen)
Marco Martin's avatar
Marco Martin committed
125
{
126
    if (!containment || !screen) {
Marco Martin's avatar
Marco Martin committed
127
128
        return KConfigGroup();
    }
129
130
    KConfigGroup views(corona->applicationConfig(), "PlasmaViews");
    views = KConfigGroup(&views, QStringLiteral("Panel %1").arg(containment->id()));
Marco Martin's avatar
Marco Martin committed
131

132
    if (containment->formFactor() == Plasma::Types::Vertical) {
Laurent Montel's avatar
Laurent Montel committed
133
        return KConfigGroup(&views, QStringLiteral("Vertical") + QString::number(screen->size().height()));
Marco Martin's avatar
Marco Martin committed
134
135
    //treat everything else as horizontal
    } else {
Laurent Montel's avatar
Laurent Montel committed
136
        return KConfigGroup(&views, QStringLiteral("Horizontal") + QString::number(screen->size().width()));
Marco Martin's avatar
Marco Martin committed
137
    }
Marco Martin's avatar
Marco Martin committed
138
139
}

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
KConfigGroup PanelView::panelConfigDefaults(ShellCorona *corona, Plasma::Containment *containment, QScreen *screen)
{
    if (!containment || !screen) {
        return KConfigGroup();
    }

    KConfigGroup views(corona->applicationConfig(), "PlasmaViews");
    views = KConfigGroup(&views, QStringLiteral("Panel %1").arg(containment->id()));

    return KConfigGroup(&views, QStringLiteral("Defaults"));
}

int PanelView::readConfigValueWithFallBack(const QString &key, int defaultValue)
{
    int value = config().readEntry(key, configDefaults().readEntry(key, defaultValue));
    return value;
}

158
159
KConfigGroup PanelView::config() const
{
160
    return panelConfig(m_corona, containment(), m_screenToFollow);
161
162
}

163
164
165
166
167
KConfigGroup PanelView::configDefaults() const
{
    return panelConfigDefaults(m_corona, containment(), m_screenToFollow);
}

168
169
170
171
172
Q_INVOKABLE QString PanelView::fileFromPackage(const QString &key, const QString &fileName)
{
    return corona()->kPackage().filePath(key.toUtf8(), fileName);
}

173
174
175
176
void PanelView::maximize()
{
    int length;
    if (containment()->formFactor() == Plasma::Types::Vertical) {
177
        length = m_screenToFollow->size().height();
178
    } else {
179
        length = m_screenToFollow->size().width();
180
181
182
183
184
185
    }
    setOffset(0);
    setMinimumLength(length);
    setMaximumLength(length);
}

186
187
188
189
190
191
192
193
194
195
196
197
Qt::Alignment PanelView::alignment() const
{
    return m_alignment;
}

void PanelView::setAlignment(Qt::Alignment alignment)
{
    if (m_alignment == alignment) {
        return;
    }

    m_alignment = alignment;
198
    //alignment is not resolution dependent, doesn't save to Defaults
199
    config().parent().writeEntry("alignment", (int)m_alignment);
200
    emit alignmentChanged();
201
202
203
    positionPanel();
}

204
205
206
207
208
209
210
211
212
213
214
int PanelView::offset() const
{
    return m_offset;
}

void PanelView::setOffset(int offset)
{
    if (m_offset == offset) {
        return;
    }

215
    if (formFactor() == Plasma::Types::Vertical) {
216
217
        if (offset + m_maxLength > m_screenToFollow->size().height()) {
            setMaximumLength( -m_offset + m_screenToFollow->size().height() );
218
219
        }
    } else {
220
221
        if (offset + m_maxLength > m_screenToFollow->size().width()) {
            setMaximumLength( -m_offset + m_screenToFollow->size().width() );
222
223
224
        }
    }

225
    m_offset = offset;
Marco Martin's avatar
Marco Martin committed
226
    config().writeEntry("offset", m_offset);
227
    configDefaults().writeEntry("offset", m_offset);
228
    positionPanel();
Marco Martin's avatar
Marco Martin committed
229
    emit offsetChanged();
230
    m_corona->requestApplicationConfigSync();
231
    emit m_corona->availableScreenRegionChanged();
Marco Martin's avatar
Marco Martin committed
232
233
}

Marco Martin's avatar
Marco Martin committed
234
235
int PanelView::thickness() const
{
236
    return m_thickness;
Marco Martin's avatar
Marco Martin committed
237
238
239
240
241
}

void PanelView::setThickness(int value)
{
    if (value == thickness()) {
Marco Martin's avatar
Marco Martin committed
242
        return;
Marco Martin's avatar
Marco Martin committed
243
244
    }

245
    m_thickness = value;
David Edmundson's avatar
David Edmundson committed
246
247
    emit thicknessChanged();

Marco Martin's avatar
Marco Martin committed
248
    config().writeEntry("thickness", value);
249
    configDefaults().writeEntry("thickness", value);
250
    m_corona->requestApplicationConfigSync();
251
    resizePanel();
Marco Martin's avatar
Marco Martin committed
252
253
}

254
255
int PanelView::length() const
{
256
    return qMax(1, m_contentLength);
257
258
259
260
}

void PanelView::setLength(int value)
{
261
    if (value == m_contentLength) {
262
263
        return;
    }
Marco Martin's avatar
Marco Martin committed
264

265
    m_contentLength = value;
266

267
    resizePanel();
268
269
}

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
int PanelView::maximumLength() const
{
    return m_maxLength;
}

void PanelView::setMaximumLength(int length)
{
    if (length == m_maxLength) {
        return;
    }

    if (m_minLength > length) {
        setMinimumLength(length);
    }

    config().writeEntry("maxLength", length);
286
    configDefaults().writeEntry("maxLength", length);
287
288
    m_maxLength = length;
    emit maximumLengthChanged();
289
    m_corona->requestApplicationConfigSync();
Marco Martin's avatar
Marco Martin committed
290

291
    resizePanel();
292
293
294
}

int PanelView::minimumLength() const
Marco Martin's avatar
Marco Martin committed
295
{
296
    return m_minLength;
Marco Martin's avatar
Marco Martin committed
297
298
}

299
void PanelView::setMinimumLength(int length)
Marco Martin's avatar
Marco Martin committed
300
{
301
302
303
304
305
306
    if (length == m_minLength) {
        return;
    }

    if (m_maxLength < length) {
        setMaximumLength(length);
Marco Martin's avatar
Marco Martin committed
307
    }
308
309

    config().writeEntry("minLength", length);
310
    configDefaults().writeEntry("minLength", length);
311
312
    m_minLength = length;
    emit minimumLengthChanged();
313
    m_corona->requestApplicationConfigSync();
Marco Martin's avatar
Marco Martin committed
314

315
    resizePanel();
316
317
}

Marco Martin's avatar
Marco Martin committed
318
319
320
321
322
323
324
325
326
327
328
329
330
int PanelView::distance() const
{
    return m_distance;
}

void PanelView::setDistance(int dist)
{
    if (m_distance == dist) {
        return;
    }

    m_distance = dist;
    emit distanceChanged();
331
    positionPanel();
Marco Martin's avatar
Marco Martin committed
332
333
}

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
Plasma::Types::BackgroundHints PanelView::backgroundHints() const
{
    return m_backgroundHints;
}

void PanelView::setBackgroundHints(Plasma::Types::BackgroundHints hint)
{
    if (m_backgroundHints == hint) {
       return;
    }
 
    m_backgroundHints = hint;
     
    emit backgroundHintsChanged();
}

350
351
352
353
354
Plasma::FrameSvg::EnabledBorders PanelView::enabledBorders() const
{
    return m_enabledBorders;
}

Marco Martin's avatar
Marco Martin committed
355
356
void PanelView::setVisibilityMode(PanelView::VisibilityMode mode)
{
357
358
359
360
    if (m_visibilityMode == mode) {
        return;
    }

Marco Martin's avatar
Marco Martin committed
361
362
    m_visibilityMode = mode;

363
    disconnect(containment(), &Plasma::Applet::activated, this, &PanelView::showTemporarily);
364
    if (edgeActivated()) {
365
        connect(containment(), &Plasma::Applet::activated, this, &PanelView::showTemporarily);
Marco Martin's avatar
Marco Martin committed
366
367
    }

368
    if (config().isValid() && config().parent().isValid()) {
369
        //panelVisibility is not resolution dependent, don't write to Defaults
370
        config().parent().writeEntry("panelVisibility", (int)mode);
371
372
        m_corona->requestApplicationConfigSync();
    }
Marco Martin's avatar
Marco Martin committed
373

374
    visibilityModeToWayland();
Marco Martin's avatar
Marco Martin committed
375
376
    updateStruts();

377
    emit visibilityModeChanged();
378

379
    restoreAutoHide();
Marco Martin's avatar
Marco Martin committed
380
381
}

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
void PanelView::visibilityModeToWayland()
{
    if (!m_shellSurface) {
        return;
    }
    KWayland::Client::PlasmaShellSurface::PanelBehavior behavior;
    switch (m_visibilityMode) {
    case NormalPanel:
        behavior = KWayland::Client::PlasmaShellSurface::PanelBehavior::AlwaysVisible;
        break;
    case AutoHide:
        behavior = KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide;
        break;
    case LetWindowsCover:
        behavior = KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsCanCover;
        break;
    case WindowsGoBelow:
        behavior = KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow;
        break;
    default:
        Q_UNREACHABLE();
        return;
    }
    m_shellSurface->setPanelBehavior(behavior);
}

Marco Martin's avatar
Marco Martin committed
408
409
410
411
412
PanelView::VisibilityMode PanelView::visibilityMode() const
{
    return m_visibilityMode;
}

413
414
415
416
417
418
void PanelView::positionPanel()
{
    if (!containment()) {
        return;
    }

419
420
421
422
    if (!m_initCompleted) {
        return;
    }

423
    KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge;
424

425
    switch (containment()->location()) {
426
427
    case Plasma::Types::TopEdge:
        containment()->setFormFactor(Plasma::Types::Horizontal);
428
        slideLocation = KWindowEffects::TopEdge;
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
        break;

    case Plasma::Types::LeftEdge:
        containment()->setFormFactor(Plasma::Types::Vertical);
        slideLocation = KWindowEffects::LeftEdge;
        break;

    case Plasma::Types::RightEdge:
        containment()->setFormFactor(Plasma::Types::Vertical);
        slideLocation = KWindowEffects::RightEdge;
        break;

    case Plasma::Types::BottomEdge:
    default:
        containment()->setFormFactor(Plasma::Types::Horizontal);
        slideLocation = KWindowEffects::BottomEdge;
        break;
    }
447
448
    const QPoint pos = geometryByDistance(m_distance).topLeft();
    setPosition(pos);
449

450
451
452
    if (m_shellSurface) {
        m_shellSurface->setPosition(pos);
    }
Marco Martin's avatar
Marco Martin committed
453

454
455
456
457
458
    KWindowEffects::slideWindow(winId(), slideLocation, -1);
}

QRect PanelView::geometryByDistance(int distance) const
{
459
    QScreen *s = m_screenToFollow;
460
    QPoint position;
461
462
    const QRect screenGeometry = s->geometry();

463
464
    switch (containment()->location()) {
    case Plasma::Types::TopEdge:
Marco Martin's avatar
Marco Martin committed
465
466
        switch (m_alignment) {
        case Qt::AlignCenter:
467
            position = QPoint(QPoint(screenGeometry.center().x(), screenGeometry.top()) + QPoint(m_offset - width()/2, distance));
Marco Martin's avatar
Marco Martin committed
468
469
            break;
        case Qt::AlignRight:
470
            position = QPoint(QPoint(screenGeometry.x() + screenGeometry.width(), screenGeometry.y()) - QPoint(m_offset + width(), distance));
Marco Martin's avatar
Marco Martin committed
471
472
473
            break;
        case Qt::AlignLeft:
        default:
474
            position = QPoint(screenGeometry.topLeft() + QPoint(m_offset, distance));
Marco Martin's avatar
Marco Martin committed
475
        }
476
        break;
Marco Martin's avatar
Marco Martin committed
477

478
    case Plasma::Types::LeftEdge:
Marco Martin's avatar
Marco Martin committed
479
480
        switch (m_alignment) {
        case Qt::AlignCenter:
481
            position = QPoint(QPoint(screenGeometry.left(), screenGeometry.center().y()) + QPoint(distance, m_offset - height()/2));
Marco Martin's avatar
Marco Martin committed
482
483
            break;
        case Qt::AlignRight:
484
            position = QPoint(QPoint(screenGeometry.left(), screenGeometry.y() + screenGeometry.height()) - QPoint(distance, m_offset + height()));
Marco Martin's avatar
Marco Martin committed
485
486
487
            break;
        case Qt::AlignLeft:
        default:
488
            position = QPoint(screenGeometry.topLeft() + QPoint(distance, m_offset));
Marco Martin's avatar
Marco Martin committed
489
        }
490
        break;
Marco Martin's avatar
Marco Martin committed
491

492
    case Plasma::Types::RightEdge:
Marco Martin's avatar
Marco Martin committed
493
494
        switch (m_alignment) {
        case Qt::AlignCenter:
495
            // Never use rect.right(); for historical reasons it returns left() + width() - 1; see http://doc.qt.io/qt-5/qrect.html#right
496
            position = QPoint(QPoint(screenGeometry.x() + screenGeometry.width(), screenGeometry.center().y()) - QPoint(thickness() + distance, 0) + QPoint(0, m_offset - height()/2));
Marco Martin's avatar
Marco Martin committed
497
498
            break;
        case Qt::AlignRight:
499
            position = QPoint(QPoint(screenGeometry.x() + screenGeometry.width(), screenGeometry.y() + screenGeometry.height()) - QPoint(thickness() + distance, 0) - QPoint(0, m_offset + height()));
Marco Martin's avatar
Marco Martin committed
500
501
502
            break;
        case Qt::AlignLeft:
        default:
503
            position = QPoint(QPoint(screenGeometry.x() + screenGeometry.width(), screenGeometry.y()) - QPoint(thickness() + distance, 0) + QPoint(0, m_offset));
Marco Martin's avatar
Marco Martin committed
504
        }
505
        break;
Marco Martin's avatar
Marco Martin committed
506

507
    case Plasma::Types::BottomEdge:
508
    default:
Marco Martin's avatar
Marco Martin committed
509
510
        switch (m_alignment) {
        case Qt::AlignCenter:
511
            position = QPoint(QPoint(screenGeometry.center().x(), screenGeometry.bottom() - thickness() - distance) + QPoint(m_offset - width()/2, 1));
Marco Martin's avatar
Marco Martin committed
512
513
            break;
        case Qt::AlignRight:
514
            position = QPoint(screenGeometry.bottomRight() - QPoint(0, thickness() + distance) - QPoint(m_offset + width(), -1));
Marco Martin's avatar
Marco Martin committed
515
516
517
            break;
        case Qt::AlignLeft:
        default:
518
            position = QPoint(screenGeometry.bottomLeft() - QPoint(0, thickness() + distance) + QPoint(m_offset, 1));
Marco Martin's avatar
Marco Martin committed
519
        }
520
    }
521
    QRect ret = formFactor() == Plasma::Types::Vertical ? QRect(position, QSize(thickness(), height())) : QRect(position, QSize(width(), thickness()));
522
    ret = ret.intersected(screenGeometry);
523
    return ret;
524
}
Sebastian Kügler's avatar
Sebastian Kügler committed
525

526
527
void PanelView::resizePanel()
{
528
529
530
531
    if (!m_initCompleted) {
        return;
    }

532
533
534
535
    QSize targetSize;
    QSize targetMinSize;
    QSize targetMaxSize;

536
    if (formFactor() == Plasma::Types::Vertical) {
David Edmundson's avatar
David Edmundson committed
537
        const int minSize = qMax(MINSIZE, m_minLength);
538
        const int maxSize = qMin(m_maxLength, m_screenToFollow->size().height() - m_offset);
539
540
541
        targetMinSize = QSize(thickness(), minSize);
        targetMaxSize = QSize(thickness(), maxSize);
        targetSize = QSize(thickness(), qBound(minSize, m_contentLength, maxSize));
542
    } else {
David Edmundson's avatar
David Edmundson committed
543
        const int minSize = qMax(MINSIZE, m_minLength);
544
        const int maxSize = qMin(m_maxLength, m_screenToFollow->size().width() - m_offset);
545
546
547
548
549
550
551
552
553
        targetMinSize = QSize(minSize, thickness());
        targetMaxSize = QSize(maxSize, thickness());
        targetSize = QSize(qBound(minSize, m_contentLength, maxSize), thickness());
    }
    if (minimumSize() != targetMinSize) {
        setMinimumSize(targetMinSize);
    }
    if (maximumSize() != targetMaxSize) {
        setMaximumSize(targetMaxSize);
554
    }
555
556
557
558
    if (size() != targetSize) {
        resize(targetSize);
    }

559
    //position will be updated implicitly from resizeEvent
560
561
}

Marco Martin's avatar
Marco Martin committed
562
563
564
565
566
567
void PanelView::restore()
{
    if (!containment()) {
        return;
    }

568
    // All the defaults are based on whatever are the current values
569
    // so won't be weirdly reset after screen resolution change
570

571
572
573
    //alignment is not resolution dependent
    //but if fails read it from the resolution dependent one as
    //the place for this config key is changed in Plasma 5.9
574
575
576
577
578
579
    //Do NOT use readConfigValueWithFallBack
    setAlignment((Qt::Alignment)config().parent().readEntry<int>("alignment", config().readEntry<int>("alignment", m_alignment)));

    // All the other values are read from screen independent values,
    // but fallback on the screen independent section, as is the only place
    // is safe to directly write during plasma startup, as there can be 
580
    // resolution changes
581
    m_offset = readConfigValueWithFallBack("offset", m_offset);
Marco Martin's avatar
Marco Martin committed
582
583
584
585
    if (m_alignment != Qt::AlignCenter) {
        m_offset = qMax(0, m_offset);
    }

586
    setThickness(readConfigValueWithFallBack("thickness", m_thickness));
587

588
    const QSize screenSize = m_screenToFollow->size();
Marco Martin's avatar
Marco Martin committed
589
590
    setMinimumSize(QSize(-1, -1));
    //FIXME: an invalid size doesn't work with QWindows
591
    setMaximumSize(screenSize);
592

593
594
    const int side = containment()->formFactor() == Plasma::Types::Vertical ? screenSize.height() : screenSize.width();
    const int maxSize = side - m_offset;
595
596
    m_maxLength = qBound<int>(MINSIZE, readConfigValueWithFallBack("maxLength", side), maxSize);
    m_minLength = qBound<int>(MINSIZE, readConfigValueWithFallBack("minLength", side), maxSize);
597

598
599
600
    //panelVisibility is not resolution dependent
    //but if fails read it from the resolution dependent one as
    //the place for this config key is changed in Plasma 5.9
601
    //Do NOT use readConfigValueWithFallBack
602
    setVisibilityMode((VisibilityMode)config().parent().readEntry<int>("panelVisibility", config().readEntry<int>("panelVisibility", (int)NormalPanel)));
603
    m_initCompleted = true;
David Edmundson's avatar
David Edmundson committed
604
    resizePanel();
605
    positionPanel();
606

607
608
609
    emit maximumLengthChanged();
    emit minimumLengthChanged();
    emit offsetChanged();
Marco Martin's avatar
Marco Martin committed
610
    emit alignmentChanged();
611
612
613
614

    //::restore might have been called directly before the timer fires
    // at which point we don't still need the timer
    m_positionPaneltimer.stop();
Marco Martin's avatar
Marco Martin committed
615
616
}

617
618
619
620
621
622
623
624
void PanelView::showConfigurationInterface(Plasma::Applet *applet)
{
    if (!applet || !applet->containment()) {
        return;
    }

    Plasma::Containment *cont = qobject_cast<Plasma::Containment *>(applet);

625
626
627
    const bool isPanelConfig = (cont && cont == containment() && cont->isContainment());

    if (m_panelConfigView) {
628
        if (m_panelConfigView->applet() == applet) {
629
630
631
632
633
634
635
636
637
            if (isPanelConfig) { // toggles panel controller, whereas applet config is always brought to front
                if (m_panelConfigView->isVisible()) {
                    m_panelConfigView->hide();
                } else {
                    m_panelConfigView->show();
                }
                return;
            }

638
            m_panelConfigView->show();
639
            m_panelConfigView->requestActivate();
640
641
            return;
        }
642
643
644

        m_panelConfigView->hide();
        m_panelConfigView->deleteLater();
645
646
    }

647
    if (isPanelConfig) {
648
649
        m_panelConfigView = new PanelConfigView(cont, this);
    } else {
Marco Martin's avatar
Marco Martin committed
650
        m_panelConfigView = new PlasmaQuick::ConfigView(applet);
651
    }
652

653
654
    m_panelConfigView.data()->init();
    m_panelConfigView.data()->show();
655
    m_panelConfigView.data()->requestActivate();
656

657
    if (isPanelConfig) {
658
659
        KWindowSystem::setState(m_panelConfigView.data()->winId(), NET::SkipTaskbar | NET::SkipPager);
    }
660
661
}

662
663
void PanelView::restoreAutoHide()
{
664
    bool autoHide = true;
665
    disconnect(m_transientWindowVisibleWatcher);
666
667
668
669

    if (!edgeActivated()) {
        autoHide = false;
    }
670
    else if (m_containsMouse) {
671
672
673
674
675
676
677
678
        autoHide = false;
    }
    else if (containment() && containment()->isUserConfiguring()) {
        autoHide = false;
    }
    else if (containment() && containment()->status() >= Plasma::Types::NeedsAttentionStatus &&
             containment()->status() != Plasma::Types::HiddenStatus) {
        autoHide = false;
679
680
681
682
683
684
685
686
    } else {
        for(QWindow *window: qApp->topLevelWindows()) {
            if (window->transientParent() == this && window->isVisible()) {
                m_transientWindowVisibleWatcher = connect(window, &QWindow::visibleChanged, this, &PanelView::restoreAutoHide);
                autoHide = false;
                break; //if multiple are open, we will re-evaluate this expression after the first closes
            }
        }
687
688
689
    }

    setAutoHideEnabled(autoHide);
690
691
692
}

void PanelView::setAutoHideEnabled(bool enabled)
693
694
{
#if HAVE_X11
695
    if (KWindowSystem::isPlatformX11()) {
696
        xcb_connection_t *c = QX11Info::connection();
697

698
699
        const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW");
        xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
700

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
701
        QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
702

703
704
705
        if (!atom) {
            return;
        }
706

707
708
709
710
        if (!enabled) {
            xcb_delete_property(c, winId(), atom->atom);
            return;
        }
711

712
713
        KWindowEffects::SlideFromLocation slideLocation = KWindowEffects::NoEdge;
        uint32_t value = 0;
714

715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
        switch (location()) {
        case Plasma::Types::TopEdge:
            value = 0;
            slideLocation = KWindowEffects::TopEdge;
            break;
        case Plasma::Types::RightEdge:
            value = 1;
            slideLocation = KWindowEffects::RightEdge;
            break;
        case Plasma::Types::BottomEdge:
            value = 2;
            slideLocation = KWindowEffects::BottomEdge;
            break;
        case Plasma::Types::LeftEdge:
            value = 3;
            slideLocation = KWindowEffects::LeftEdge;
            break;
        case Plasma::Types::Floating:
        default:
            value = 4;
            break;
        }
737

738
739
740
741
742
        int hideType = 0;
        if (m_visibilityMode == LetWindowsCover) {
            hideType = 1;
        }
        value |= hideType << 8;
743

744
745
746
        xcb_change_property(c, XCB_PROP_MODE_REPLACE, winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value);
        KWindowEffects::slideWindow(winId(), slideLocation, -1);
    }
747
#endif
748
749
750
751
752
753
754
    if (m_shellSurface && m_visibilityMode == PanelView::AutoHide) {
        if (enabled) {
            m_shellSurface->requestHideAutoHidingPanel();
        } else {
            m_shellSurface->requestShowAutoHidingPanel();
        }
    }
755
756
}

757
758
void PanelView::resizeEvent(QResizeEvent *ev)
{
759
    updateEnabledBorders();
760
    //don't setGeometry() to make really sure we aren't doing a resize loop
761
762
763
764
765
    const QPoint pos = geometryByDistance(m_distance).topLeft();
    setPosition(pos);
    if (m_shellSurface) {
        m_shellSurface->setPosition(pos);
    }
766
    m_strutsTimer.start(STRUTSTIMERDELAY);
767
768
    emit m_corona->availableScreenRegionChanged();

Marco Martin's avatar
Marco Martin committed
769
    PlasmaQuick::ContainmentView::resizeEvent(ev);
770

771
#if PLASMA_VERSION < QT_VERSION_CHECK(5,59,0)
772
    updateMask();
773
#endif
774
775
}

Marco Martin's avatar
Marco Martin committed
776
777
void PanelView::moveEvent(QMoveEvent *ev)
{
778
    updateEnabledBorders();
779
    m_strutsTimer.start(STRUTSTIMERDELAY);
Marco Martin's avatar
Marco Martin committed
780
    PlasmaQuick::ContainmentView::moveEvent(ev);
781
#if PLASMA_VERSION < QT_VERSION_CHECK(5,59,0)
782
    updateMask();
783
#endif
Marco Martin's avatar
Marco Martin committed
784
785
}

786
787
void PanelView::integrateScreen()
{
Marco Martin's avatar
Marco Martin committed
788
    connect(m_screenToFollow.data(), &QScreen::geometryChanged, this, &PanelView::restore);
789
    updateMask();
790
791
    KWindowSystem::setOnAllDesktops(winId(), true);
    KWindowSystem::setType(winId(), NET::Dock);
Marco Martin's avatar
Marco Martin committed
792
#if HAVE_X11
793
    QXcbWindowFunctions::setWmWindowType(this, QXcbWindowFunctions::Dock);
Marco Martin's avatar
Marco Martin committed
794
#endif
795
796
    if (m_shellSurface) {
        m_shellSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
797
        m_shellSurface->setSkipTaskbar(true);
798
    }
799
    setVisibilityMode(m_visibilityMode);
800

801
802
803
    if (containment()) {
        containment()->reactToScreenChange();
    }
804
805
}

Marco Martin's avatar
Marco Martin committed
806
807
void PanelView::showEvent(QShowEvent *event)
{
Marco Martin's avatar
Marco Martin committed
808
    PlasmaQuick::ContainmentView::showEvent(event);
809

810
811
    integrateScreen();
}
812

813
void PanelView::setScreenToFollow(QScreen *screen)
814
{
815
816
817
818
    if (screen == m_screenToFollow) {
        return;
    }

819
820
821
822
    if (!screen) {
        return;
    }

823
824
825
826
827
828
    /*connect(screen, &QObject::destroyed, this, [this]() {
        if (PanelView::screen()) {
            m_screenToFollow = PanelView::screen();
            adaptToScreen();
        }
    });*/
829

830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
    m_screenToFollow = screen;
    setScreen(screen);
    adaptToScreen();
}

QScreen *PanelView::screenToFollow() const
{
    return m_screenToFollow;
}

void PanelView::adaptToScreen()
{
    emit screenToFollowChanged(m_screenToFollow);
    m_lastScreen = m_screenToFollow;

    if (!m_screenToFollow) {
846
        return;
847
848
    }

849
850
851
    integrateScreen();
    showTemporarily();
    m_positionPaneltimer.start();
Marco Martin's avatar
Marco Martin committed
852
853
}

854
855
bool PanelView::event(QEvent *e)
{
856
    switch (e->type()) {
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
        case QEvent::Enter:
            m_containsMouse = true;
            if (edgeActivated()) {
                m_unhideTimer.stop();
            }
            break;

        case QEvent::Leave:
            m_containsMouse = false;
            if (edgeActivated()) {
                m_unhideTimer.start();
            }
            break;

        /*Fitt's law: if the containment has margins, and the mouse cursor clicked
        * on the mouse edge, forward the click in the containment boundaries
        */

875
876
877
878
879
        case QEvent::MouseMove:
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease: {
            QMouseEvent *me = static_cast<QMouseEvent *>(e);

880
881
            //first, don't mess with position if the cursor is actually outside the view:
            //somebody is doing a click and drag that must not break when the cursor i outside
882
            if (geometry().contains(QCursor::pos(screenToFollow()))) {
883
884
885
886
887
888
889
890
891
892
                if (!containmentContainsPosition(me->windowPos())) {
                    auto me2 = new QMouseEvent(me->type(),
                                    positionAdjustedForContainment(me->windowPos()),
                                    positionAdjustedForContainment(me->windowPos()),
                                    positionAdjustedForContainment(me->windowPos()) + position(),
                                    me->button(), me->buttons(), me->modifiers());

                    QCoreApplication::postEvent(this, me2);
                    return true;
                }
893
            } else {
894
895
                // default handling if current mouse position is outside the panel
                return ContainmentView::event(e);
896
897
898
899
            }
            break;
        }

900
901
902
903
        case QEvent::Wheel: {
            QWheelEvent *we = static_cast<QWheelEvent *>(e);

            if (!containmentContainsPosition(we->pos())) {
904
                auto we2 = new QWheelEvent(positionAdjustedForContainment(we->pos()),
905
                                positionAdjustedForContainment(we->pos()) + position(),
Laurent Montel's avatar
Laurent Montel committed
906
                                we->pixelDelta(), we->angleDelta(), we->angleDelta().y(),
907
908
                                we->orientation(), we->buttons(), we->modifiers(), we->phase());

909
                QCoreApplication::postEvent(this, we2);
910
911
912
913
914
                return true;
            }
            break;
        }

915
916
        case QEvent::DragEnter: {
            QDragEnterEvent *de = static_cast<QDragEnterEvent *>(e);
917
            if (!containmentContainsPosition(de->pos())) {
918
                auto de2 = new QDragEnterEvent(positionAdjustedForContainment(de->pos()).toPoint(),
919
                                    de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers());
920

921
                QCoreApplication::postEvent(this, de2);
922
923
924
925
926
927
928
929
930
                return true;
            }
            break;
        }
        //DragLeave just works
        case QEvent::DragLeave:
            break;
        case QEvent::DragMove: {
            QDragMoveEvent *de = static_cast<QDragMoveEvent *>(e);
931
            if (!containmentContainsPosition(de->pos())) {
932
                auto de2 = new QDragMoveEvent(positionAdjustedForContainment(de->pos()).toPoint(),
933
                                   de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers());
934

935
                QCoreApplication::postEvent(this, de2);
936
937
938
939
940
941
                return true;
            }
            break;
        }
        case QEvent::Drop: {
            QDropEvent *de = static_cast<QDropEvent *>(e);
942
            if (!containmentContainsPosition(de->pos())) {
943
                auto de2 = new QDropEvent(positionAdjustedForContainment(de->pos()).toPoint(),
944
                               de->possibleActions(), de->mimeData(), de->mouseButtons(), de->keyboardModifiers());
945

946
                QCoreApplication::postEvent(this, de2);
947
948
949
950
951
                return true;
            }
            break;
        }

952
953
954
955
        case QEvent::Hide: {
            if (m_panelConfigView && m_panelConfigView.data()->isVisible()) {
                m_panelConfigView.data()->hide();
            }
956
            m_containsMouse = false;
957
958
            break;
        }
959
        case QEvent::PlatformSurface:
960
961
962
963
964
965
966
967
968
969
            switch (static_cast<QPlatformSurfaceEvent*>(e)->surfaceEventType()) {
            case QPlatformSurfaceEvent::SurfaceCreated:
                setupWaylandIntegration();
                PanelShadows::self()->addWindow(this, enabledBorders());
                break;
            case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
                delete m_shellSurface;
                m_shellSurface = nullptr;
                PanelShadows::self()->removeWindow(this);
                break;
970
971
            }
            break;
972
973
974
975
        default:
            break;
    }

Marco Martin's avatar
Marco Martin committed
976
    return ContainmentView::event(e);
977
978
}

David Edmundson's avatar
David Edmundson committed
979
bool PanelView::containmentContainsPosition(const QPointF &point) const
980
981
982
983
984
985
986
987
988
989
{
    QQuickItem *containmentItem = containment()->property("_plasma_graphicObject").value<QQuickItem *>();

    if (!containmentItem) {
        return false;
    }

    return QRectF(containmentItem->mapToScene(QPoint(0,0)), QSizeF(containmentItem->width(), containmentItem->height())).contains(point);
}

David Edmundson's avatar
David Edmundson committed
990
QPointF PanelView::positionAdjustedForContainment(const QPointF &point) const
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
{
    QQuickItem *containmentItem = containment()->property("_plasma_graphicObject").value<QQuickItem *>();

    if (!containmentItem) {
        return point;
    }

    QRectF containmentRect(containmentItem->mapToScene(QPoint(0,0)), QSizeF(containmentItem->width(), containmentItem->height()));

    return QPointF(qBound(containmentRect.left() + 2, point.x(), containmentRect.right() - 2),
                   qBound(containmentRect.top() + 2, point.y(), containmentRect.bottom() - 2));
}

Marco Martin's avatar
Marco Martin committed
1004
1005
void PanelView::updateMask()
{
1006
1007
1008
1009
1010
1011
    if (m_backgroundHints == Plasma::Types::NoBackground) {
        KWindowEffects::enableBlurBehind(winId(), false);
        KWindowEffects::enableBackgroundContrast(winId(), false);
        setMask(QRegion());
    } else {
        QRegion mask;
1012

1013
1014
1015
1016
1017
1018
        QQuickItem *rootObject = this->rootObject();
        if (rootObject) {
            const QVariant maskProperty = rootObject->property("panelMask");
            if (static_cast<QMetaType::Type>(maskProperty.type()) == QMetaType::QRegion) {
                mask = maskProperty.value<QRegion>();
            }