1 /*
2 SPDX-FileCopyrightText: 2016 Smith AR <audoban@openmailbox.org>
3 SPDX-FileCopyrightText: 2016 Michail Vourlakos <mvourlakos@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "view.h"
9
10 // local
11 #include "contextmenu.h"
12 #include "effects.h"
13 #include "positioner.h"
14 #include "visibilitymanager.h"
15 #include "settings/primaryconfigview.h"
16 #include "settings/secondaryconfigview.h"
17 #include "settings/viewsettingsfactory.h"
18 #include "settings/widgetexplorerview.h"
19 #include "../apptypes.h"
20 #include "../lattecorona.h"
21 #include "../data/layoutdata.h"
22 #include "../data/viewstable.h"
23 #include "../declarativeimports/interfaces.h"
24 #include "../indicator/factory.h"
25 #include "../layout/genericlayout.h"
26 #include "../layouts/manager.h"
27 #include "../layouts/storage.h"
28 #include "../plasma/extended/theme.h"
29 #include "../screenpool.h"
30 #include "../settings/universalsettings.h"
31 #include "../settings/exporttemplatedialog/exporttemplatedialog.h"
32 #include "../shortcuts/globalshortcuts.h"
33 #include "../shortcuts/shortcutstracker.h"
34
35 // Qt
36 #include <QAction>
37 #include <QDragEnterEvent>
38 #include <QDragMoveEvent>
39 #include <QDropEvent>
40 #include <QMouseEvent>
41 #include <QQmlContext>
42 #include <QQmlEngine>
43 #include <QQmlProperty>
44 #include <QQuickItem>
45 #include <QMenu>
46
47 // KDe
48 #include <KActionCollection>
49 #include <KActivities/Consumer>
50 #include <KWayland/Client/plasmashell.h>
51 #include <KWayland/Client/surface.h>
52 #include <KWindowSystem>
53
54 // Plasma
55 #include <Plasma/Containment>
56 #include <Plasma/ContainmentActions>
57 #include <PlasmaQuick/AppletQuickItem>
58
59 #define BLOCKHIDINGDRAGTYPE "View::ContainsDrag()"
60 #define BLOCKHIDINGNEEDSATTENTIONTYPE "View::Containment::NeedsAttentionState()"
61 #define BLOCKHIDINGREQUESTSINPUTTYPE "View::Containment::RequestsInputState()"
62
63 namespace Latte {
64
65 //! both alwaysVisible and byPassWMX11 are passed through corona because
66 //! during the view window creation containment hasn't been set, but these variables
67 //! are needed in order for window flags to be set correctly
View(Plasma::Corona * corona,QScreen * targetScreen,bool byPassX11WM)68 View::View(Plasma::Corona *corona, QScreen *targetScreen, bool byPassX11WM)
69 : PlasmaQuick::ContainmentView(corona),
70 m_contextMenu(new ViewPart::ContextMenu(this)),
71 m_effects(new ViewPart::Effects(this)),
72 m_interface(new ViewPart::ContainmentInterface(this)),
73 m_parabolic(new ViewPart::Parabolic(this)),
74 m_sink(new ViewPart::EventsSink(this))
75 {
76 //this is disabled because under wayland breaks Views positioning
77 //setVisible(false);
78
79 //! needs to be created after Effects because it catches some of its signals
80 //! and avoid a crash from View::winId() at the same time
81 m_positioner = new ViewPart::Positioner(this);
82
83 // setTitle(corona->kPackage().metadata().name());
84 setIcon(qGuiApp->windowIcon());
85 setResizeMode(QuickViewSharedEngine::SizeRootObjectToView);
86 setColor(QColor(Qt::transparent));
87 setClearBeforeRendering(true);
88
89 const auto flags = Qt::FramelessWindowHint
90 | Qt::NoDropShadowWindowHint
91 | Qt::WindowDoesNotAcceptFocus;
92
93 if (byPassX11WM) {
94 setFlags(flags | Qt::BypassWindowManagerHint);
95 //! needs to be set early enough
96 m_byPassWM = byPassX11WM;
97 } else {
98 setFlags(flags);
99 }
100
101 if (targetScreen)
102 m_positioner->setScreenToFollow(targetScreen);
103 else
104 m_positioner->setScreenToFollow(qGuiApp->primaryScreen());
105
106 m_releaseGrabTimer.setInterval(400);
107 m_releaseGrabTimer.setSingleShot(true);
108 connect(&m_releaseGrabTimer, &QTimer::timeout, this, &View::releaseGrab);
109
110 connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::updateTransientWindowsTracking);
111 connect(m_interface, &ViewPart::ContainmentInterface::hasExpandedAppletChanged, this, &View::updateTransientWindowsTracking);
112
113 connect(this, &View::containmentChanged
114 , this, [ &, byPassX11WM]() {
115 qDebug() << "dock view c++ containment changed 1...";
116
117 if (!this->containment())
118 return;
119
120 qDebug() << "dock view c++ containment changed 2...";
121
122 setTitle(validTitle());
123
124 //! First load default values from file
125 restoreConfig();
126
127 //! Afterwards override that values in case during creation something different is needed
128 setByPassWM(byPassX11WM);
129
130 //! Check the screen assigned to this dock
131 reconsiderScreen();
132
133 //! needs to be created before visibility creation because visibility uses it
134 if (!m_windowsTracker) {
135 m_windowsTracker = new ViewPart::WindowsTracker(this);
136 emit windowsTrackerChanged();
137 }
138
139 if (!m_visibility) {
140 m_visibility = new ViewPart::VisibilityManager(this);
141
142 connect(m_visibility, &ViewPart::VisibilityManager::isHiddenChanged, this, [&]() {
143 if (m_visibility->isHidden()) {
144 m_interface->deactivateApplets();
145 }
146 });
147
148 connect(m_visibility, &ViewPart::VisibilityManager::containsMouseChanged,
149 this, &View::updateTransientWindowsTracking);
150
151 //! Deprecated because with Plasma 5.19.3 the issue does not appear.
152 //! The issue was that when FrameExtents where zero strange behaviors were
153 //! occuring from KWin, e.g. the panels were moving outside of screen and
154 //! panel external shadows were positioned out of place.
155 /*connect(m_visibility, &ViewPart::VisibilityManager::frameExtentsCleared, this, [&]() {
156 if (behaveAsPlasmaPanel()) {
157 //! recreate view because otherwise compositor frame extents implementation
158 //! is triggering a crazy behavior of moving/hiding the view and freezing Latte
159 //! in some cases.
160 //reloadSource();
161 }
162 });*/
163
164 emit visibilityChanged();
165 }
166
167 if (!m_indicator) {
168 m_indicator = new ViewPart::Indicator(this);
169 emit indicatorChanged();
170 }
171
172 if (m_positioner) {
173 //! immediateSyncGeometry helps avoiding binding loops from containment qml side
174 m_positioner->immediateSyncGeometry();
175 }
176
177 connect(this->containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), SLOT(statusChanged(Plasma::Types::ItemStatus)));
178 connect(this->containment(), &Plasma::Containment::showAddWidgetsInterface, this, &View::showWidgetExplorer);
179 connect(this->containment(), &Plasma::Containment::userConfiguringChanged, this, [&]() {
180 emit inEditModeChanged();
181 });
182
183 if (m_corona->viewSettingsFactory()->hasOrphanSettings()
184 && m_corona->viewSettingsFactory()->hasVisibleSettings()
185 && m_corona->viewSettingsFactory()->lastContainment() == containment()) {
186 //! used mostly from view recreations in order to inform config windows that view has been updated
187 m_primaryConfigView = m_corona->viewSettingsFactory()->primaryConfigView();
188 m_primaryConfigView->setParentView(this, true);
189 }
190
191 emit containmentActionsChanged();
192 }, Qt::DirectConnection);
193
194 m_corona = qobject_cast<Latte::Corona *>(this->corona());
195
196 if (m_corona) {
197 connect(m_corona, &Latte::Corona::viewLocationChanged, this, &View::dockLocationChanged);
198 }
199 }
200
~View()201 View::~View()
202 {
203 m_inDelete = true;
204
205 //! clear Layout connections
206 m_visibleHackTimer1.stop();
207 m_visibleHackTimer2.stop();
208 for (auto &c : connectionsLayout) {
209 disconnect(c);
210 }
211
212 //! unload indicators
213 if (m_indicator) {
214 m_indicator->unloadIndicators();
215 }
216
217 disconnectSensitiveSignals();
218 disconnect(containment(), SIGNAL(statusChanged(Plasma::Types::ItemStatus)), this, SLOT(statusChanged(Plasma::Types::ItemStatus)));
219
220 qDebug() << "dock view deleting...";
221
222 //! this disconnect does not free up connections correctly when
223 //! latteView is deleted. A crash for this example is the following:
224 //! switch to Alternative Session and disable compositing,
225 //! the signal creating the crash was probably from deleted
226 //! windows.
227 //! this->disconnect();
228
229 if (m_primaryConfigView && m_corona->inQuit()) {
230 //! delete only when application is quitting
231 delete m_primaryConfigView;
232 }
233
234 if (m_appletConfigView) {
235 delete m_appletConfigView;
236 }
237
238 if (m_contextMenu) {
239 delete m_contextMenu;
240 }
241
242 //needs to be deleted before Effects because it catches some of its signals
243 if (m_positioner) {
244 delete m_positioner;
245 }
246
247 if (m_effects) {
248 delete m_effects;
249 }
250
251 if (m_indicator) {
252 delete m_indicator;
253 }
254
255 if (m_interface) {
256 delete m_interface;
257 }
258
259 if (m_visibility) {
260 delete m_visibility;
261 }
262
263 if (m_windowsTracker) {
264 delete m_windowsTracker;
265 }
266 }
267
init(Plasma::Containment * plasma_containment)268 void View::init(Plasma::Containment *plasma_containment)
269 {
270 connect(this, &QQuickWindow::xChanged, this, &View::geometryChanged);
271 connect(this, &QQuickWindow::yChanged, this, &View::geometryChanged);
272 connect(this, &QQuickWindow::widthChanged, this, &View::geometryChanged);
273 connect(this, &QQuickWindow::heightChanged, this, &View::geometryChanged);
274
275 connect(this, &QQuickWindow::xChanged, this, &View::xChanged);
276 connect(this, &QQuickWindow::xChanged, this, &View::updateAbsoluteGeometry);
277 connect(this, &QQuickWindow::yChanged, this, &View::yChanged);
278 connect(this, &QQuickWindow::yChanged, this, &View::updateAbsoluteGeometry);
279 connect(this, &QQuickWindow::widthChanged, this, &View::widthChanged);
280 connect(this, &QQuickWindow::widthChanged, this, &View::updateAbsoluteGeometry);
281 connect(this, &QQuickWindow::heightChanged, this, &View::heightChanged);
282 connect(this, &QQuickWindow::heightChanged, this, &View::updateAbsoluteGeometry);
283
284 connect(this, &View::fontPixelSizeChanged, this, &View::editThicknessChanged);
285 connect(this, &View::maxNormalThicknessChanged, this, &View::editThicknessChanged);
286
287 connect(this, &View::activitiesChanged, this, &View::applyActivitiesToWindows);
288 connect(m_positioner, &ViewPart::Positioner::winIdChanged, this, &View::applyActivitiesToWindows);
289
290 connect(this, &View::maxLengthChanged, this, [&]() {
291 if (m_inDelete) {
292 return;
293 }
294
295 emit availableScreenRectChangedFrom(this);
296 emit availableScreenRegionChangedFrom(this);
297 });
298
299 connect(this, &View::offsetChanged, this, [&]() {
300 if (m_inDelete ) {
301 return;
302 }
303
304 emit availableScreenRectChangedFrom(this);
305 emit availableScreenRegionChangedFrom(this);
306 });
307
308 connect(this, &View::localGeometryChanged, this, [&]() {
309 updateAbsoluteGeometry();
310 });
311 connect(this, &View::screenEdgeMarginEnabledChanged, this, [&]() {
312 updateAbsoluteGeometry();
313 });
314
315 //! used in order to disconnect it when it should NOT be called because it creates crashes
316 connect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom);
317 connect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom);
318 connect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot);
319 connect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop);
320
321 connect(this, &View::byPassWMChanged, this, &View::saveConfig);
322 connect(this, &View::isPreferredForShortcutsChanged, this, &View::saveConfig);
323 connect(this, &View::nameChanged, this, &View::saveConfig);
324 connect(this, &View::onPrimaryChanged, this, &View::saveConfig);
325 connect(this, &View::typeChanged, this, &View::saveConfig);
326
327 connect(this, &View::normalThicknessChanged, this, [&]() {
328 emit availableScreenRectChangedFrom(this);
329 });
330
331 connect(m_effects, &ViewPart::Effects::innerShadowChanged, this, [&]() {
332 emit availableScreenRectChangedFrom(this);
333 });
334 connect(m_positioner, &ViewPart::Positioner::onHideWindowsForSlidingOut, this, &View::hideWindowsForSlidingOut);
335 connect(m_positioner, &ViewPart::Positioner::screenGeometryChanged, this, &View::screenGeometryChanged);
336 connect(m_positioner, &ViewPart::Positioner::windowSizeChanged, this, [&]() {
337 emit availableScreenRectChangedFrom(this);
338 });
339
340 connect(m_contextMenu, &ViewPart::ContextMenu::menuChanged, this, &View::contextMenuIsShownChanged);
341
342 connect(m_interface, &ViewPart::ContainmentInterface::hasExpandedAppletChanged, this, &View::verticalUnityViewHasFocus);
343
344 //! View sends this signal in order to avoid crashes from ViewPart::Indicator when the view is recreated
345 connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorChanged, this, [&](const QString &indicatorId) {
346 emit indicatorPluginChanged(indicatorId);
347 });
348
349 connect(this, &View::indicatorPluginChanged, this, [&](const QString &indicatorId) {
350 if (m_indicator && m_indicator->isCustomIndicator() && m_indicator->type() == indicatorId) {
351 reloadSource();
352 }
353 });
354
355 connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::indicatorRemoved, this, &View::indicatorPluginRemoved);
356
357 //! Assign app interfaces in be accessible through containment graphic item
358 QQuickItem *containmentGraphicItem = qobject_cast<QQuickItem *>(plasma_containment->property("_plasma_graphicObject").value<QObject *>());
359
360 if (containmentGraphicItem) {
361 containmentGraphicItem->setProperty("_latte_globalShortcuts_object", QVariant::fromValue(m_corona->globalShortcuts()->shortcutsTracker()));
362 containmentGraphicItem->setProperty("_latte_layoutsManager_object", QVariant::fromValue(m_corona->layoutsManager()));
363 containmentGraphicItem->setProperty("_latte_themeExtended_object", QVariant::fromValue(m_corona->themeExtended()));
364 containmentGraphicItem->setProperty("_latte_universalSettings_object", QVariant::fromValue(m_corona->universalSettings()));
365 containmentGraphicItem->setProperty("_latte_view_object", QVariant::fromValue(this));
366
367 Latte::Interfaces *ifacesGraphicObject = qobject_cast<Latte::Interfaces *>(containmentGraphicItem->property("_latte_view_interfacesobject").value<QObject *>());
368
369 if (ifacesGraphicObject) {
370 ifacesGraphicObject->updateView();
371 setInterfacesGraphicObj(ifacesGraphicObject);
372 }
373 }
374
375 setSource(corona()->kPackage().filePath("lattedockui"));
376
377 //! immediateSyncGeometry helps avoiding binding loops from containment qml side
378 m_positioner->immediateSyncGeometry();
379
380 qDebug() << "SOURCE:" << source();
381 }
382
reloadSource()383 void View::reloadSource()
384 {
385 if (m_layout && containment()) {
386 // if (settingsWindowIsShown()) {
387 // m_configView->deleteLater();
388 // }
389
390 engine()->clearComponentCache();
391 m_layout->recreateView(containment(), settingsWindowIsShown());
392 }
393 }
394
inDelete() const395 bool View::inDelete() const
396 {
397 return m_inDelete;
398 }
399
inReadyState() const400 bool View::inReadyState() const
401 {
402 return (m_layout != nullptr);
403 }
404
disconnectSensitiveSignals()405 void View::disconnectSensitiveSignals()
406 {
407 m_initLayoutTimer.stop();
408
409 disconnect(this, &View::availableScreenRectChangedFrom, m_corona, &Latte::Corona::availableScreenRectChangedFrom);
410 disconnect(this, &View::availableScreenRegionChangedFrom, m_corona, &Latte::Corona::availableScreenRegionChangedFrom);
411 disconnect(m_corona, &Latte::Corona::availableScreenRectChangedFrom, this, &View::availableScreenRectChangedFromSlot);
412 disconnect(m_corona, &Latte::Corona::verticalUnityViewHasFocus, this, &View::topViewAlwaysOnTop);
413
414 setLayout(nullptr);
415 }
416
availableScreenRectChangedFromSlot(View * origin)417 void View::availableScreenRectChangedFromSlot(View *origin)
418 {
419 if (m_inDelete || origin == this || !origin) {
420 return;
421 }
422
423 if (formFactor() == Plasma::Types::Vertical
424 && origin->formFactor() == Plasma::Types::Horizontal //! accept only horizontal views
425 && !(origin->location() == Plasma::Types::TopEdge && m_positioner->isStickedOnTopEdge()) //! ignore signals in such case
426 && !(origin->location() == Plasma::Types::BottomEdge && m_positioner->isStickedOnBottomEdge()) //! ignore signals in such case
427 && origin->layout()
428 && m_layout
429 && origin->layout()->lastUsedActivity() == m_layout->lastUsedActivity()) {
430 //! must be in same activity
431 m_positioner->syncGeometry();
432 }
433 }
434
setupWaylandIntegration()435 void View::setupWaylandIntegration()
436 {
437 if (m_shellSurface)
438 return;
439
440 if (Latte::Corona *c = qobject_cast<Latte::Corona *>(corona())) {
441 using namespace KWayland::Client;
442 PlasmaShell *interface {c->waylandCoronaInterface()};
443
444 if (!interface)
445 return;
446
447 Surface *s{Surface::fromWindow(this)};
448
449 if (!s)
450 return;
451
452 m_shellSurface = interface->createSurface(s, this);
453 qDebug() << "WAYLAND dock window surface was created...";
454 if (m_visibility) {
455 m_visibility->initViewFlags();
456 }
457 if (m_positioner) {
458 m_positioner->updateWaylandId();
459 }
460 }
461 }
462
surface()463 KWayland::Client::PlasmaShellSurface *View::surface()
464 {
465 return m_shellSurface;
466 }
467
468 //! the main function which decides if this dock is at the
469 //! correct screen
reconsiderScreen()470 void View::reconsiderScreen()
471 {
472 m_positioner->reconsiderScreen();
473 }
474
duplicateView()475 void View::duplicateView()
476 {
477 QString storedTmpViewFilepath = m_layout->storedView(containment()->id());
478 newView(storedTmpViewFilepath);
479 }
480
exportTemplate()481 void View::exportTemplate()
482 {
483 Latte::Settings::Dialog::ExportTemplateDialog *exportDlg = new Latte::Settings::Dialog::ExportTemplateDialog(this);
484 exportDlg->show();
485 }
486
newView(const QString & templateFile)487 void View::newView(const QString &templateFile)
488 {
489 if (templateFile.isEmpty() || !m_layout) {
490 return;
491 }
492
493 Data::ViewsTable templateviews = Layouts::Storage::self()->views(templateFile);
494
495 if (templateviews.rowCount() <= 0) {
496 return;
497 }
498
499 Data::View nextdata = templateviews[0];
500 int scrId = onPrimary() ? m_corona->screenPool()->primaryScreenId() : m_positioner->currentScreenId();
501
502 QList<Plasma::Types::Location> freeedges = m_layout->freeEdges(scrId);
503
504 if (!freeedges.contains(nextdata.edge)) {
505 nextdata.edge = (freeedges.count() > 0 ? freeedges[0] : Plasma::Types::BottomEdge);
506 }
507
508 nextdata.setState(Data::View::OriginFromViewTemplate, templateFile);
509
510 m_layout->newView(nextdata);
511 }
512
removeView()513 void View::removeView()
514 {
515 if (m_layout) {
516 m_inDelete = true;
517
518 QAction *removeAct = action("remove");
519
520 if (removeAct) {
521 removeAct->trigger();
522 }
523 }
524 }
525
settingsWindowIsShown()526 bool View::settingsWindowIsShown()
527 {
528 return m_primaryConfigView && (m_primaryConfigView->parentView()==this) && m_primaryConfigView->isVisible();
529 }
530
showSettingsWindow()531 void View::showSettingsWindow()
532 {
533 if (!settingsWindowIsShown()) {
534 emit m_visibility->mustBeShown();
535 showConfigurationInterface(containment());
536 applyActivitiesToWindows();
537 }
538 }
539
configView()540 QQuickView *View::configView()
541 {
542 return m_primaryConfigView.data();
543 }
544
showConfigurationInterface(Plasma::Applet * applet)545 void View::showConfigurationInterface(Plasma::Applet *applet)
546 {
547 if (!applet || !applet->containment())
548 return;
549
550 Plasma::Containment *c = qobject_cast<Plasma::Containment *>(applet);
551
552 if (m_primaryConfigView && c && c->isContainment() && c == this->containment()) {
553 if (m_primaryConfigView->isVisible()) {
554 m_primaryConfigView->hideConfigWindow();
555 } else {
556 m_primaryConfigView->showConfigWindow();
557 applyActivitiesToWindows();
558 }
559
560 return;
561 } else if (m_appletConfigView) {
562 if (m_appletConfigView->applet() == applet) {
563 m_appletConfigView->show();
564
565 if (KWindowSystem::isPlatformX11()) {
566 m_appletConfigView->requestActivate();
567 }
568 return;
569 } else {
570 m_appletConfigView->hide();
571 }
572 }
573
574 bool delayConfigView = false;
575
576 if (c && containment() && c->isContainment() && c->id() == containment()->id()) {
577 m_primaryConfigView = m_corona->viewSettingsFactory()->primaryConfigView(this);
578 applyActivitiesToWindows();
579 } else {
580 m_appletConfigView = new PlasmaQuick::ConfigView(applet);
581 m_appletConfigView.data()->init();
582 m_appletConfigView->show();
583 }
584 }
585
showWidgetExplorer(const QPointF & point)586 void View::showWidgetExplorer(const QPointF &point)
587 {
588 auto widgetExplorerView = m_corona->viewSettingsFactory()->widgetExplorerView(this);
589
590 if (!widgetExplorerView->isVisible()) {
591 widgetExplorerView->showAfter(250);
592 }
593 }
594
localGeometry() const595 QRect View::localGeometry() const
596 {
597 return m_localGeometry;
598 }
599
setLocalGeometry(const QRect & geometry)600 void View::setLocalGeometry(const QRect &geometry)
601 {
602 if (m_localGeometry == geometry) {
603 return;
604 }
605
606 m_localGeometry = geometry;
607 emit localGeometryChanged();
608 }
609
610
name() const611 QString View::name() const
612 {
613 return m_name;
614 }
615
setName(const QString & newname)616 void View::setName(const QString &newname)
617 {
618 if (m_name == newname) {
619 return;
620 }
621
622 m_name = newname;
623 emit nameChanged();
624 }
625
validTitle() const626 QString View::validTitle() const
627 {
628 if (!containment()) {
629 return QString();
630 }
631
632 return QString("#view#" + QString::number(containment()->id()));
633 }
634
updateAbsoluteGeometry(bool bypassChecks)635 void View::updateAbsoluteGeometry(bool bypassChecks)
636 {
637 //! there was a -1 in height and width here. The reason of this
638 //! if I remember correctly was related to multi-screen but I cant
639 //! remember exactly the reason, something related to right edge in
640 //! multi screen environment. BUT this was breaking the entire AlwaysVisible
641 //! experience with struts. Removing them in order to restore correct
642 //! behavior and keeping this comment in order to check for
643 //! multi-screen breakage
644 QRect absGeometry = m_localGeometry;
645 absGeometry.moveLeft(x() + m_localGeometry.x());
646 absGeometry.moveTop(y() + m_localGeometry.y());
647
648 if (behaveAsPlasmaPanel()) {
649 int currentScreenEdgeMargin = m_screenEdgeMarginEnabled ? qMax(0, m_screenEdgeMargin) : 0;
650
651 if (location() == Plasma::Types::BottomEdge) {
652 absGeometry.moveTop(screenGeometry().bottom() - currentScreenEdgeMargin - m_normalThickness);
653 } else if (location() == Plasma::Types::TopEdge) {
654 absGeometry.moveTop(screenGeometry().top() + currentScreenEdgeMargin);
655 } else if (location() == Plasma::Types::LeftEdge) {
656 absGeometry.moveLeft(screenGeometry().left() + currentScreenEdgeMargin);
657 } else if (location() == Plasma::Types::RightEdge) {
658 absGeometry.moveLeft(screenGeometry().right() - currentScreenEdgeMargin - m_normalThickness);
659 }
660 }
661
662 if (KWindowSystem::isPlatformX11() && devicePixelRatio() != 1.0) {
663 //!Fix for X11 Global Scale, I dont think this could be pixel perfect accurate
664 auto factor = devicePixelRatio();
665 absGeometry = QRect(qRound(absGeometry.x() * factor),
666 qRound(absGeometry.y() * factor),
667 qRound(absGeometry.width() * factor),
668 qRound(absGeometry.height() * factor));
669 }
670
671 if (m_absoluteGeometry == absGeometry && !bypassChecks) {
672 return;
673 }
674
675 if (m_absoluteGeometry != absGeometry) {
676 m_absoluteGeometry = absGeometry;
677 emit absoluteGeometryChanged(m_absoluteGeometry);
678 }
679
680 if ((m_absoluteGeometry != absGeometry) || bypassChecks) {
681 //! inform others such as neighbour vertical views that new geometries are applied
682 //! main use of BYPASSCKECKS is from Positioner when the view changes screens
683 emit availableScreenRectChangedFrom(this);
684 emit availableScreenRegionChangedFrom(this);
685 }
686 }
687
statusChanged(Plasma::Types::ItemStatus status)688 void View::statusChanged(Plasma::Types::ItemStatus status)
689 {
690 if (!containment()) {
691 return;
692 }
693
694 //! Fix for #443236, following setFlags(...) need to be added at all three cases
695 //! but initViewFlags() should be called afterwards because setFlags(...) breaks
696 //! the Dock window default behavior under x11
697 if (status == Plasma::Types::NeedsAttentionStatus || status == Plasma::Types::RequiresAttentionStatus) {
698 m_visibility->addBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE);
699 setFlags(flags() | Qt::WindowDoesNotAcceptFocus);
700 m_visibility->initViewFlags();
701 } else if (status == Plasma::Types::AcceptingInputStatus) {
702 m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE);
703 setFlags(flags() & ~Qt::WindowDoesNotAcceptFocus);
704 m_visibility->initViewFlags();
705 KWindowSystem::forceActiveWindow(winId());
706 } else {
707 updateTransientWindowsTracking();
708 m_visibility->removeBlockHidingEvent(BLOCKHIDINGNEEDSATTENTIONTYPE);
709 setFlags(flags() | Qt::WindowDoesNotAcceptFocus);
710 m_visibility->initViewFlags();
711 }
712 }
713
addTransientWindow(QWindow * window)714 void View::addTransientWindow(QWindow *window)
715 {
716 if (!m_transientWindows.contains(window) && !window->flags().testFlag(Qt::ToolTip) && !window->title().startsWith("#debugwindow#")) {
717 m_transientWindows.append(window);
718
719 QString winPtrStr = "0x" + QString::number((qulonglong)window,16);
720 m_visibility->addBlockHidingEvent(winPtrStr);
721
722 if (m_visibility->hasBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE)) {
723 m_visibility->removeBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE);
724 }
725
726 connect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow);
727 }
728 }
729
removeTransientWindow(const bool & visible)730 void View::removeTransientWindow(const bool &visible)
731 {
732 QWindow *window = static_cast<QWindow *>(QObject::sender());
733
734 if (window && !visible) {
735 QString winPtrStr = "0x" + QString::number((qulonglong)window,16);
736 m_visibility->removeBlockHidingEvent(winPtrStr);
737 disconnect(window, &QWindow::visibleChanged, this, &View::removeTransientWindow);
738 m_transientWindows.removeAll(window);
739
740 if (m_visibility->hasBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE)) {
741 m_visibility->removeBlockHidingEvent(Latte::GlobalShortcuts::SHORTCUTBLOCKHIDINGTYPE);
742 }
743
744 updateTransientWindowsTracking();
745 }
746 }
747
updateTransientWindowsTracking()748 void View::updateTransientWindowsTracking()
749 {
750 for(QWindow *window: qApp->topLevelWindows()) {
751 if (window->transientParent() == this && window->isVisible()){
752 addTransientWindow(window);
753 break;
754 }
755 }
756 }
757
type() const758 Types::ViewType View::type() const
759 {
760 return m_type;
761 }
762
setType(Types::ViewType type)763 void View::setType(Types::ViewType type)
764 {
765 if (m_type == type) {
766 return;
767 }
768
769 m_type = type;
770 emit typeChanged();
771 }
772
alternativesIsShown() const773 bool View::alternativesIsShown() const
774 {
775 return m_alternativesIsShown;
776 }
777
setAlternativesIsShown(bool show)778 void View::setAlternativesIsShown(bool show)
779 {
780 if (m_alternativesIsShown == show) {
781 return;
782 }
783
784 m_alternativesIsShown = show;
785
786 emit alternativesIsShownChanged();
787 }
788
containsDrag() const789 bool View::containsDrag() const
790 {
791 return m_containsDrag;
792 }
793
setContainsDrag(bool contains)794 void View::setContainsDrag(bool contains)
795 {
796 if (m_containsDrag == contains) {
797 return;
798 }
799
800 m_containsDrag = contains;
801
802
803 if (m_containsDrag) {
804 m_visibility->addBlockHidingEvent(BLOCKHIDINGDRAGTYPE);
805 } else {
806 m_visibility->removeBlockHidingEvent(BLOCKHIDINGDRAGTYPE);
807 }
808
809 emit containsDragChanged();
810 }
811
containsMouse() const812 bool View::containsMouse() const
813 {
814 return m_containsMouse;
815 }
816
contextMenuIsShown() const817 bool View::contextMenuIsShown() const
818 {
819 if (!m_contextMenu) {
820 return false;
821 }
822
823 return m_contextMenu->menu();
824 }
825
normalThickness() const826 int View::normalThickness() const
827 {
828 return m_normalThickness;
829 }
830
setNormalThickness(int thickness)831 void View::setNormalThickness(int thickness)
832 {
833 if (m_normalThickness == thickness) {
834 return;
835 }
836
837 m_normalThickness = thickness;
838 emit normalThicknessChanged();
839 }
840
maxNormalThickness() const841 int View::maxNormalThickness() const
842 {
843 return m_maxNormalThickness;
844 }
845
setMaxNormalThickness(int thickness)846 void View::setMaxNormalThickness(int thickness)
847 {
848 if (m_maxNormalThickness == thickness) {
849 return;
850 }
851
852 m_maxNormalThickness = thickness;
853 emit maxNormalThicknessChanged();
854 }
855
headThicknessGap() const856 int View::headThicknessGap() const
857 {
858 return m_headThicknessGap;
859 }
860
setHeadThicknessGap(int thickness)861 void View::setHeadThicknessGap(int thickness)
862 {
863 if (m_headThicknessGap == thickness) {
864 return;
865 }
866
867 m_headThicknessGap = thickness;
868 emit headThicknessGapChanged();
869 }
870
byPassWM() const871 bool View::byPassWM() const
872 {
873 return m_byPassWM;
874 }
875
setByPassWM(bool bypass)876 void View::setByPassWM(bool bypass)
877 {
878 if (m_byPassWM == bypass) {
879 return;
880 }
881
882 m_byPassWM = bypass;
883 emit byPassWMChanged();
884 }
885
behaveAsPlasmaPanel() const886 bool View::behaveAsPlasmaPanel() const
887 {
888 return m_behaveAsPlasmaPanel;
889 }
890
setBehaveAsPlasmaPanel(bool behavior)891 void View::setBehaveAsPlasmaPanel(bool behavior)
892 {
893 if (m_behaveAsPlasmaPanel == behavior) {
894 return;
895 }
896
897 m_behaveAsPlasmaPanel = behavior;
898
899 emit behaveAsPlasmaPanelChanged();
900 }
901
inEditMode() const902 bool View::inEditMode() const
903 {
904 return containment() && containment()->isUserConfiguring();
905 }
906
isFloatingPanel() const907 bool View::isFloatingPanel() const
908 {
909 return m_behaveAsPlasmaPanel && m_screenEdgeMarginEnabled && (m_screenEdgeMargin>0);
910 }
911
isPreferredForShortcuts() const912 bool View::isPreferredForShortcuts() const
913 {
914 return m_isPreferredForShortcuts;
915 }
916
setIsPreferredForShortcuts(bool preferred)917 void View::setIsPreferredForShortcuts(bool preferred)
918 {
919 if (m_isPreferredForShortcuts == preferred) {
920 return;
921 }
922
923 m_isPreferredForShortcuts = preferred;
924
925 emit isPreferredForShortcutsChanged();
926
927 if (m_isPreferredForShortcuts && m_layout) {
928 emit m_layout->preferredViewForShortcutsChanged(this);
929 }
930 }
931
inSettingsAdvancedMode() const932 bool View::inSettingsAdvancedMode() const
933 {
934 return m_primaryConfigView && m_corona->universalSettings()->inAdvancedModeForEditSettings();
935 }
936
isTouchingBottomViewAndIsBusy() const937 bool View::isTouchingBottomViewAndIsBusy() const
938 {
939 return m_isTouchingBottomViewAndIsBusy;
940 }
941
setIsTouchingBottomViewAndIsBusy(bool touchAndBusy)942 void View::setIsTouchingBottomViewAndIsBusy(bool touchAndBusy)
943 {
944 if (m_isTouchingBottomViewAndIsBusy == touchAndBusy) {
945 return;
946 }
947
948 m_isTouchingBottomViewAndIsBusy = touchAndBusy;
949
950 emit isTouchingBottomViewAndIsBusyChanged();
951 }
952
isTouchingTopViewAndIsBusy() const953 bool View::isTouchingTopViewAndIsBusy() const
954 {
955 return m_isTouchingTopViewAndIsBusy;
956 }
957
setIsTouchingTopViewAndIsBusy(bool touchAndBusy)958 void View::setIsTouchingTopViewAndIsBusy(bool touchAndBusy)
959 {
960 if (m_isTouchingTopViewAndIsBusy == touchAndBusy) {
961 return;
962 }
963
964 m_isTouchingTopViewAndIsBusy = touchAndBusy;
965 emit isTouchingTopViewAndIsBusyChanged();
966 }
967
preferredViewForShortcutsChangedSlot(Latte::View * view)968 void View::preferredViewForShortcutsChangedSlot(Latte::View *view)
969 {
970 if (view != this) {
971 setIsPreferredForShortcuts(false);
972 }
973 }
974
onPrimary() const975 bool View::onPrimary() const
976 {
977 return m_onPrimary;
978 }
979
setOnPrimary(bool flag)980 void View::setOnPrimary(bool flag)
981 {
982 if (m_onPrimary == flag) {
983 return;
984 }
985
986 m_onPrimary = flag;
987 emit onPrimaryChanged();
988 }
989
maxLength() const990 float View::maxLength() const
991 {
992 return m_maxLength;
993 }
994
setMaxLength(float length)995 void View::setMaxLength(float length)
996 {
997 if (m_maxLength == length) {
998 return;
999 }
1000
1001 m_maxLength = length;
1002 emit maxLengthChanged();
1003 }
1004
editThickness() const1005 int View::editThickness() const
1006 {
1007 int smallspacing = 4;
1008 int ruler_height{m_fontPixelSize};
1009 int header_height{m_fontPixelSize + 2*smallspacing};
1010
1011 int edgeThickness = behaveAsPlasmaPanel() && screenEdgeMarginEnabled() ? m_screenEdgeMargin : 0;
1012
1013 return edgeThickness + m_maxNormalThickness + ruler_height + header_height + 6*smallspacing;
1014 }
1015
maxThickness() const1016 int View::maxThickness() const
1017 {
1018 return m_maxThickness;
1019 }
1020
setMaxThickness(int thickness)1021 void View::setMaxThickness(int thickness)
1022 {
1023 if (m_maxThickness == thickness)
1024 return;
1025
1026 m_maxThickness = thickness;
1027 emit maxThicknessChanged();
1028 }
1029
alignment() const1030 int View::alignment() const
1031 {
1032 return m_alignment;
1033 }
1034
setAlignment(int alignment)1035 void View::setAlignment(int alignment)
1036 {
1037 Types::Alignment align = static_cast<Types::Alignment>(alignment);
1038
1039 if (m_alignment == alignment) {
1040 return;
1041 }
1042
1043 m_alignment = align;
1044 emit alignmentChanged();
1045 }
1046
absoluteGeometry() const1047 QRect View::absoluteGeometry() const
1048 {
1049 return m_absoluteGeometry;
1050 }
1051
screenGeometry() const1052 QRect View::screenGeometry() const
1053 {
1054 if (this->screen()) {
1055 QRect geom = this->screen()->geometry();
1056 return geom;
1057 }
1058
1059 return QRect();
1060 }
1061
offset() const1062 float View::offset() const
1063 {
1064 return m_offset;
1065 }
1066
setOffset(float offset)1067 void View::setOffset(float offset)
1068 {
1069 if (m_offset == offset) {
1070 return;
1071 }
1072
1073 m_offset = offset;
1074 emit offsetChanged();
1075 }
1076
screenEdgeMarginEnabled() const1077 bool View::screenEdgeMarginEnabled() const
1078 {
1079 return m_screenEdgeMarginEnabled;
1080 }
1081
setScreenEdgeMarginEnabled(bool enabled)1082 void View::setScreenEdgeMarginEnabled(bool enabled)
1083 {
1084 if (m_screenEdgeMarginEnabled == enabled) {
1085 return;
1086 }
1087
1088 m_screenEdgeMarginEnabled = enabled;
1089 emit screenEdgeMarginEnabledChanged();
1090 }
1091
screenEdgeMargin() const1092 int View::screenEdgeMargin() const
1093 {
1094 return m_screenEdgeMargin;
1095 }
1096
setScreenEdgeMargin(int margin)1097 void View::setScreenEdgeMargin(int margin)
1098 {
1099 if (m_screenEdgeMargin == margin) {
1100 return;
1101 }
1102
1103
1104
1105 m_screenEdgeMargin = margin;
1106 emit screenEdgeMarginChanged();
1107 }
1108
fontPixelSize() const1109 int View::fontPixelSize() const
1110 {
1111 return m_fontPixelSize;
1112 }
1113
setFontPixelSize(int size)1114 void View::setFontPixelSize(int size)
1115 {
1116 if (m_fontPixelSize == size) {
1117 return;
1118 }
1119
1120 m_fontPixelSize = size;
1121
1122 emit fontPixelSizeChanged();
1123 }
1124
isOnAllActivities() const1125 bool View::isOnAllActivities() const
1126 {
1127 return m_activities.isEmpty() || m_activities[0] == Data::Layout::ALLACTIVITIESID;
1128 }
1129
isOnActivity(const QString & activity) const1130 bool View::isOnActivity(const QString &activity) const
1131 {
1132 return isOnAllActivities() || m_activities.contains(activity);
1133 }
1134
activities() const1135 QStringList View::activities() const
1136 {
1137 QStringList running;
1138
1139 QStringList runningAll = m_corona->activitiesConsumer()->runningActivities();
1140
1141 for(int i=0; i<m_activities.count(); ++i) {
1142 if (runningAll.contains(m_activities[i])) {
1143 running << m_activities[i];
1144 }
1145 }
1146
1147 return running;
1148 }
1149
setActivities(const QStringList & ids)1150 void View::setActivities(const QStringList &ids)
1151 {
1152 if (m_activities == ids) {
1153 return;
1154 }
1155
1156 m_activities = ids;
1157 emit activitiesChanged();
1158 }
1159
applyActivitiesToWindows()1160 void View::applyActivitiesToWindows()
1161 {
1162 if (m_visibility && m_positioner && m_layout) {
1163 QStringList runningActivities = activities();
1164
1165 m_positioner->setWindowOnActivities(m_positioner->trackedWindowId(), runningActivities);
1166
1167 //! config windows
1168 if (m_primaryConfigView) {
1169 m_primaryConfigView->setOnActivities(runningActivities);
1170 }
1171
1172 if (m_appletConfigView) {
1173 Latte::WindowSystem::WindowId appletconfigviewid;
1174
1175 if (KWindowSystem::isPlatformX11()) {
1176 appletconfigviewid = m_appletConfigView->winId();
1177 } else {
1178 appletconfigviewid = m_corona->wm()->winIdFor("latte-dock", m_appletConfigView->title());
1179 }
1180
1181 m_positioner->setWindowOnActivities(appletconfigviewid, runningActivities);
1182 }
1183
1184 //! hidden windows
1185 if (m_visibility->supportsKWinEdges()) {
1186 m_visibility->applyActivitiesToHiddenWindows(runningActivities);
1187 }
1188 }
1189 }
1190
showHiddenViewFromActivityStopping()1191 void View::showHiddenViewFromActivityStopping()
1192 {
1193 if (m_layout && m_visibility && !inDelete() && !isVisible() && !m_visibility->isHidden()) {
1194 show();
1195
1196 if (m_effects) {
1197 m_effects->updateEnabledBorders();
1198 }
1199
1200 //qDebug() << "View:: Enforce reshow from timer 1...";
1201 emit forcedShown();
1202 } else if (m_layout && isVisible()) {
1203 m_inDelete = false;
1204 //qDebug() << "View:: No needed reshow from timer 1...";
1205 }
1206 }
1207
layout() const1208 Layout::GenericLayout *View::layout() const
1209 {
1210 return m_layout;
1211 }
1212
setLayout(Layout::GenericLayout * layout)1213 void View::setLayout(Layout::GenericLayout *layout)
1214 {
1215 if (m_layout == layout) {
1216 return;
1217 }
1218
1219 // clear mode
1220 for (auto &c : connectionsLayout) {
1221 disconnect(c);
1222 }
1223
1224 m_layout = layout;
1225
1226 if (m_layout) {
1227 connectionsLayout << connect(containment(), &Plasma::Applet::destroyedChanged, m_layout, &Layout::GenericLayout::destroyedChanged);
1228 connectionsLayout << connect(containment(), &Plasma::Applet::locationChanged, m_corona, &Latte::Corona::viewLocationChanged);
1229 connectionsLayout << connect(containment(), &Plasma::Containment::appletAlternativesRequested, m_corona, &Latte::Corona::showAlternativesForApplet, Qt::QueuedConnection);
1230
1231 if (m_corona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1232 connectionsLayout << connect(containment(), &Plasma::Containment::appletCreated, m_layout, &Layout::GenericLayout::appletCreated);
1233 }
1234
1235 connectionsLayout << connect(m_positioner, &Latte::ViewPart::Positioner::edgeChanged, m_layout, &Layout::GenericLayout::viewEdgeChanged);
1236 connectionsLayout << connect(m_layout, &Layout::GenericLayout::popUpMarginChanged, m_effects, &Latte::ViewPart::Effects::popUpMarginChanged);
1237
1238 //! Sometimes the activity isnt completely ready, by adding a delay
1239 //! we try to catch up
1240 m_initLayoutTimer.setInterval(100);
1241 m_initLayoutTimer.setSingleShot(true);
1242 connectionsLayout << connect(&m_initLayoutTimer, &QTimer::timeout, this, [&]() {
1243 if (m_layout && m_visibility) {
1244 setActivities(m_layout->appliedActivities());
1245 qDebug() << "DOCK VIEW FROM LAYOUT ::: " << m_layout->name() << " - activities: " << m_activities;
1246 }
1247 });
1248 m_initLayoutTimer.start();
1249
1250 connectionsLayout << connect(m_layout, &Layout::GenericLayout::preferredViewForShortcutsChanged, this, &View::preferredViewForShortcutsChangedSlot);
1251
1252 Latte::Corona *latteCorona = qobject_cast<Latte::Corona *>(this->corona());
1253
1254 connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::currentActivityChanged, this, [&]() {
1255 if (m_layout && m_visibility) {
1256 setActivities(m_layout->appliedActivities());
1257 //! update activities in case KWin did its magic and assigned windows to faulty activities
1258 applyActivitiesToWindows();
1259 showHiddenViewFromActivityStopping();
1260 qDebug() << "DOCK VIEW FROM LAYOUT (currentActivityChanged) ::: " << m_layout->name() << " - activities: " << m_activities;
1261 }
1262 });
1263
1264 if (latteCorona->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
1265 connectionsLayout << connect(latteCorona->activitiesConsumer(), &KActivities::Consumer::runningActivitiesChanged, this, [&]() {
1266 if (m_layout && m_visibility) {
1267 setActivities(m_layout->appliedActivities());
1268 qDebug() << "DOCK VIEW FROM LAYOUT (runningActivitiesChanged) ::: " << m_layout->name()
1269 << " - activities: " << m_activities;
1270 }
1271 });
1272
1273 connectionsLayout << connect(m_layout, &Layout::GenericLayout::activitiesChanged, this, [&]() {
1274 if (m_layout) {
1275 setActivities(m_layout->appliedActivities());
1276 }
1277 });
1278
1279 connectionsLayout << connect(latteCorona->layoutsManager()->synchronizer(), &Layouts::Synchronizer::layoutsChanged, this, [&]() {
1280 if (m_layout) {
1281 setActivities(m_layout->appliedActivities());
1282 }
1283 });
1284
1285 //! BEGIN OF KWIN HACK
1286 //! IMPORTANT ::: Fixing KWin Faulty Behavior that KWin hides ALL Views when an Activity stops
1287 //! with no reason!!
1288
1289 m_visibleHackTimer1.setInterval(400);
1290 m_visibleHackTimer2.setInterval(2500);
1291 m_visibleHackTimer1.setSingleShot(true);
1292 m_visibleHackTimer2.setSingleShot(true);
1293
1294 connectionsLayout << connect(this, &QWindow::visibleChanged, this, [&]() {
1295 if (m_layout && !inDelete() && !isVisible() && !m_positioner->inLayoutUnloading()) {
1296 m_visibleHackTimer1.start();
1297 m_visibleHackTimer2.start();
1298 }
1299 });
1300
1301 connectionsLayout << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() {
1302 applyActivitiesToWindows();
1303 showHiddenViewFromActivityStopping();
1304 emit activitiesChanged();
1305 });
1306
1307 connectionsLayout << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() {
1308 applyActivitiesToWindows();
1309 showHiddenViewFromActivityStopping();
1310 emit activitiesChanged();
1311 });
1312
1313 //! END OF KWIN HACK
1314 }
1315
1316 emit layoutChanged();
1317 } else {
1318 m_activities.clear();
1319 }
1320 }
1321
hideWindowsForSlidingOut()1322 void View::hideWindowsForSlidingOut()
1323 {
1324 if (m_primaryConfigView) {
1325 m_primaryConfigView->hideConfigWindow();
1326 }
1327 }
1328
1329 //!check if the plasmoid with _name_ exists in the midedata
mimeContainsPlasmoid(QMimeData * mimeData,QString name)1330 bool View::mimeContainsPlasmoid(QMimeData *mimeData, QString name)
1331 {
1332 if (!mimeData) {
1333 return false;
1334 }
1335
1336 if (mimeData->hasFormat(QStringLiteral("text/x-plasmoidservicename"))) {
1337 QString data = mimeData->data(QStringLiteral("text/x-plasmoidservicename"));
1338 const QStringList appletNames = data.split('\n', QString::SkipEmptyParts);
1339
1340 for (const QString &appletName : appletNames) {
1341 if (appletName == name)
1342 return true;
1343 }
1344 }
1345
1346 return false;
1347 }
1348
data() const1349 Latte::Data::View View::data() const
1350 {
1351 Latte::Data::View vdata;
1352 vdata.id = QString::number(containment()->id());
1353 vdata.name = name();
1354 vdata.isActive = true;
1355 vdata.onPrimary = onPrimary();
1356
1357 vdata.screen = containment()->screen();
1358 if (!Layouts::Storage::isValid(vdata.screen)) {
1359 vdata.screen = containment()->lastScreen();
1360 }
1361
1362 //!screen edge margin can be more accurate in the config file
1363 vdata.screenEdgeMargin = m_screenEdgeMargin > 0 ? m_screenEdgeMargin : containment()->config().group("General").readEntry("screenEdgeMargin", (int)-1);
1364
1365 vdata.edge = location();
1366 vdata.maxLength = m_maxLength * 100;
1367 vdata.alignment = m_alignment;
1368 vdata.subcontainments = Layouts::Storage::self()->subcontainments(layout(), containment());
1369
1370 vdata.setState(Latte::Data::View::IsCreated);
1371 return vdata;
1372 }
1373
colorizer() const1374 QQuickItem *View::colorizer() const
1375 {
1376 return m_colorizer;
1377 }
1378
setColorizer(QQuickItem * colorizer)1379 void View::setColorizer(QQuickItem *colorizer)
1380 {
1381 if (m_colorizer == colorizer) {
1382 return;
1383 }
1384
1385 m_colorizer = colorizer;
1386 emit colorizerChanged();
1387 }
1388
metrics() const1389 QQuickItem *View::metrics() const
1390 {
1391 return m_metrics;
1392 }
1393
setMetrics(QQuickItem * metrics)1394 void View::setMetrics(QQuickItem *metrics)
1395 {
1396 if (m_metrics == metrics) {
1397 return;
1398 }
1399
1400 m_metrics = metrics;
1401 emit metricsChanged();
1402 }
1403
effects() const1404 ViewPart::Effects *View::effects() const
1405 {
1406 return m_effects;
1407 }
1408
indicator() const1409 ViewPart::Indicator *View::indicator() const
1410 {
1411 return m_indicator;
1412 }
1413
contextMenu() const1414 ViewPart::ContextMenu *View::contextMenu() const
1415 {
1416 return m_contextMenu;
1417 }
1418
extendedInterface() const1419 ViewPart::ContainmentInterface *View::extendedInterface() const
1420 {
1421 return m_interface;
1422 }
1423
parabolic() const1424 ViewPart::Parabolic *View::parabolic() const
1425 {
1426 return m_parabolic;
1427 }
1428
positioner() const1429 ViewPart::Positioner *View::positioner() const
1430 {
1431 return m_positioner;
1432 }
1433
sink() const1434 ViewPart::EventsSink *View::sink() const
1435 {
1436 return m_sink;
1437 }
1438
visibility() const1439 ViewPart::VisibilityManager *View::visibility() const
1440 {
1441 return m_visibility;
1442 }
1443
windowsTracker() const1444 ViewPart::WindowsTracker *View::windowsTracker() const
1445 {
1446 return m_windowsTracker;
1447 }
1448
interfacesGraphicObj() const1449 Latte::Interfaces *View::interfacesGraphicObj() const
1450 {
1451 return m_interfacesGraphicObj;
1452 }
1453
setInterfacesGraphicObj(Latte::Interfaces * ifaces)1454 void View::setInterfacesGraphicObj(Latte::Interfaces *ifaces)
1455 {
1456 if (m_interfacesGraphicObj == ifaces) {
1457 return;
1458 }
1459
1460 m_interfacesGraphicObj = ifaces;
1461
1462 if (containment()) {
1463 QQuickItem *containmentGraphicItem = qobject_cast<QQuickItem *>(containment()->property("_plasma_graphicObject").value<QObject *>());
1464
1465 if (containmentGraphicItem) {
1466 containmentGraphicItem->setProperty("_latte_view_interfacesobject", QVariant::fromValue(m_interfacesGraphicObj));
1467 }
1468 }
1469
1470 emit interfacesGraphicObjChanged();
1471 }
1472
event(QEvent * e)1473 bool View::event(QEvent *e)
1474 {
1475 QEvent *sunkevent = e;
1476
1477 if (!m_inDelete) {
1478 emit eventTriggered(e);
1479
1480 bool sinkableevent{false};
1481
1482 switch (e->type()) {
1483 case QEvent::Enter:
1484 m_containsMouse = true;
1485 break;
1486
1487 case QEvent::Leave:
1488 m_containsMouse = false;
1489 setContainsDrag(false);
1490 sinkableevent = true;
1491 break;
1492
1493 case QEvent::DragEnter:
1494 setContainsDrag(true);
1495 sinkableevent = true;
1496 break;
1497
1498 case QEvent::DragLeave:
1499 setContainsDrag(false);
1500 break;
1501
1502 case QEvent::DragMove:
1503 sinkableevent = true;
1504 break;
1505
1506 case QEvent::Drop:
1507 setContainsDrag(false);
1508 sinkableevent = true;
1509 break;
1510
1511 case QEvent::MouseMove:
1512 sinkableevent = true;
1513 break;
1514
1515 case QEvent::MouseButtonPress:
1516 if (auto me = dynamic_cast<QMouseEvent *>(e)) {
1517 emit mousePressed(me->pos(), me->button());
1518 sinkableevent = true;
1519 }
1520 break;
1521
1522 case QEvent::MouseButtonRelease:
1523 if (auto me = dynamic_cast<QMouseEvent *>(e)) {
1524 emit mouseReleased(me->pos(), me->button());
1525 sinkableevent = true;
1526 }
1527 break;
1528
1529 case QEvent::PlatformSurface:
1530 if (auto pe = dynamic_cast<QPlatformSurfaceEvent *>(e)) {
1531 switch (pe->surfaceEventType()) {
1532 case QPlatformSurfaceEvent::SurfaceCreated:
1533 setupWaylandIntegration();
1534
1535 if (m_shellSurface) {
1536 //! immediateSyncGeometry helps avoiding binding loops from containment qml side
1537 m_positioner->immediateSyncGeometry();
1538 m_effects->updateShadows();
1539 }
1540
1541 break;
1542
1543 case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
1544 if (m_shellSurface) {
1545 delete m_shellSurface;
1546 m_shellSurface = nullptr;
1547 qDebug() << "WAYLAND dock window surface was deleted...";
1548 m_effects->clearShadows();
1549 }
1550
1551 break;
1552 }
1553 }
1554
1555 break;
1556
1557 case QEvent::Show:
1558 if (m_visibility) {
1559 m_visibility->initViewFlags();
1560 }
1561 break;
1562
1563 case QEvent::Wheel:
1564 if (auto we = dynamic_cast<QWheelEvent *>(e)) {
1565 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
1566 QPoint pos = QPoint(we->x(), we->y());
1567 #else
1568 QPoint pos = we->position().toPoint();
1569 #endif
1570 emit wheelScrolled(pos, we->angleDelta(), we->buttons());
1571
1572 sinkableevent = true;
1573 }
1574 break;
1575 default:
1576 break;
1577 }
1578
1579 if (sinkableevent && m_sink->isActive()) {
1580 sunkevent = m_sink->onEvent(e);
1581 }
1582 }
1583
1584 return ContainmentView::event(sunkevent);
1585 }
1586
releaseConfigView()1587 void View::releaseConfigView()
1588 {
1589 m_primaryConfigView = nullptr;
1590 }
1591
1592 //! release grab and restore mouse state
unblockMouse(int x,int y)1593 void View::unblockMouse(int x, int y)
1594 {
1595 setMouseGrabEnabled(false);
1596
1597 m_releaseGrab_x = x;
1598 m_releaseGrab_y = y;
1599 m_releaseGrabTimer.start();
1600 }
1601
releaseGrab()1602 void View::releaseGrab()
1603 {
1604 //! ungrab mouse
1605 if (mouseGrabberItem()) {
1606 mouseGrabberItem()->ungrabMouse();
1607 }
1608
1609 //! properly release grabbed mouse in order to inform all views
1610 setMouseGrabEnabled(true);
1611 setMouseGrabEnabled(false);
1612
1613 //! Send a fake QEvent::Leave to inform applets for mouse leaving the view
1614 QHoverEvent e(QEvent::Leave, QPoint(-5,-5), QPoint(m_releaseGrab_x, m_releaseGrab_y));
1615 QCoreApplication::instance()->sendEvent(this, &e);
1616 }
1617
action(const QString & name)1618 QAction *View::action(const QString &name)
1619 {
1620 if (!containment()) {
1621 return nullptr;
1622 }
1623
1624 return this->containment()->actions()->action(name);
1625 }
1626
containmentActions() const1627 QVariantList View::containmentActions() const
1628 {
1629 QVariantList actions;
1630
1631 if (!containment()) {
1632 return actions;
1633 }
1634
1635 const QString trigger = "RightButton;NoModifier";
1636 Plasma::ContainmentActions *plugin = this->containment()->containmentActions().value(trigger);
1637
1638 if (!plugin) {
1639 return actions;
1640 }
1641
1642 if (plugin->containment() != this->containment()) {
1643 plugin->setContainment(this->containment());
1644 // now configure it
1645 KConfigGroup cfg(this->containment()->corona()->config(), "ActionPlugins");
1646 cfg = KConfigGroup(&cfg, QString::number(this->containment()->containmentType()));
1647 KConfigGroup pluginConfig = KConfigGroup(&cfg, trigger);
1648 plugin->restore(pluginConfig);
1649 }
1650
1651 for (QAction *ac : plugin->contextualActions()) {
1652 actions << QVariant::fromValue<QAction *>(ac);
1653 }
1654
1655 return actions;
1656 }
1657
isHighestPriorityView()1658 bool View::isHighestPriorityView() {
1659 if (m_layout) {
1660 return this == m_layout->highestPriorityView();
1661 }
1662
1663 return false;
1664 }
1665
1666 //! BEGIN: WORKAROUND order to force top panels always on top and above left/right panels
topViewAlwaysOnTop()1667 void View::topViewAlwaysOnTop()
1668 {
1669 if (!m_visibility) {
1670 return;
1671 }
1672
1673 if (location() == Plasma::Types::TopEdge
1674 && m_visibility->mode() != Latte::Types::WindowsCanCover
1675 && m_visibility->mode() != Latte::Types::WindowsAlwaysCover) {
1676 //! this is needed in order to preserve that the top dock will be above others.
1677 //! Unity layout paradigm is a good example for this. The top panel shadow
1678 //! should be always on top compared to left panel
1679 m_visibility->setViewOnFrontLayer();
1680 }
1681 }
1682
verticalUnityViewHasFocus()1683 void View::verticalUnityViewHasFocus()
1684 {
1685 if (formFactor() == Plasma::Types::Vertical
1686 && (y() != screenGeometry().y())
1687 && ( (m_alignment == Latte::Types::Justify && m_maxLength == 1.0)
1688 ||(m_alignment == Latte::Types::Top && m_offset == 0.0) )) {
1689 emit m_corona->verticalUnityViewHasFocus();
1690 }
1691 }
1692 //! END: WORKAROUND
1693
1694 //!BEGIN overriding context menus behavior
mousePressEvent(QMouseEvent * event)1695 void View::mousePressEvent(QMouseEvent *event)
1696 {
1697 bool result = m_contextMenu->mousePressEvent(event);
1698
1699 if (result) {
1700 PlasmaQuick::ContainmentView::mousePressEvent(event);
1701 updateTransientWindowsTracking();
1702 }
1703
1704 verticalUnityViewHasFocus();
1705 }
1706 //!END overriding context menus behavior
1707
1708 //!BEGIN configuration functions
saveConfig()1709 void View::saveConfig()
1710 {
1711 if (!this->containment())
1712 return;
1713
1714 auto config = this->containment()->config();
1715 config.writeEntry("onPrimary", onPrimary());
1716 config.writeEntry("byPassWM", byPassWM());
1717 config.writeEntry("isPreferredForShortcuts", isPreferredForShortcuts());
1718 config.writeEntry("name", m_name);
1719 config.writeEntry("viewType", (int)m_type);
1720 }
1721
restoreConfig()1722 void View::restoreConfig()
1723 {
1724 if (!this->containment())
1725 return;
1726
1727 auto config = this->containment()->config();
1728 m_onPrimary = config.readEntry("onPrimary", true);
1729 m_alignment = static_cast<Latte::Types::Alignment>(config.group("General").readEntry("alignment", (int)Latte::Types::Center));
1730 m_byPassWM = config.readEntry("byPassWM", false);
1731 m_isPreferredForShortcuts = config.readEntry("isPreferredForShortcuts", false);
1732 m_name = config.readEntry("name", QString());
1733
1734 //! Send changed signals at the end in order to be sure that saveConfig
1735 //! wont rewrite default/invalid values
1736 emit alignmentChanged();
1737 emit nameChanged();
1738 emit onPrimaryChanged();
1739 emit byPassWMChanged();
1740 }
1741 //!END configuration functions
1742
1743 }
1744 //!END namespace
1745