1 /*
2     SPDX-FileCopyrightText: 2021 Michail Vourlakos <mvourlakos@gmail.com>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "widgetexplorerview.h"
7 
8 // local
9 #include "../panelshadows_p.h"
10 #include "../view.h"
11 #include "../../lattecorona.h"
12 #include "../../wm/abstractwindowinterface.h"
13 
14 // Qt
15 #include <QQuickItem>
16 #include <QScreen>
17 
18 // KDE
19 #include <KWindowEffects>
20 #include <KWindowSystem>
21 #include <KWayland/Client/plasmashell.h>
22 
23 // Plasma
24 #include <Plasma/Package>
25 
26 namespace Latte {
27 namespace ViewPart {
28 
WidgetExplorerView(Latte::View * view)29 WidgetExplorerView::WidgetExplorerView(Latte::View *view)
30     : SubConfigView(view, QString("#widgetexplorerview#"), true)
31 {
32     setResizeMode(QQuickView::SizeRootObjectToView);
33     //!set flags early in order for wayland to initialize properly
34     setFlags(wFlags());
35 
36     connect(this, &QQuickView::widthChanged, this, &WidgetExplorerView::updateEffects);
37     connect(this, &QQuickView::heightChanged, this, &WidgetExplorerView::updateEffects);
38 
39     connect(this, &QQuickView::statusChanged, [&](QQuickView::Status status) {
40         if (status == QQuickView::Ready) {
41             updateEffects();
42         }
43     });
44 
45     setParentView(view);
46     init();
47 }
48 
init()49 void WidgetExplorerView::init()
50 {
51     SubConfigView::init();
52 
53     QByteArray tempFilePath = "widgetexplorerui";
54 
55     updateEnabledBorders();
56 
57     auto source = QUrl::fromLocalFile(m_latteView->containment()->corona()->kPackage().filePath(tempFilePath));
58     setSource(source);
59     syncGeometry();
60 }
61 
hideOnWindowDeactivate() const62 bool WidgetExplorerView::hideOnWindowDeactivate() const
63 {
64     return m_hideOnWindowDeactivate;
65 }
66 
setHideOnWindowDeactivate(bool hide)67 void WidgetExplorerView::setHideOnWindowDeactivate(bool hide)
68 {
69     if (m_hideOnWindowDeactivate == hide) {
70         return;
71     }
72 
73     m_hideOnWindowDeactivate = hide;
74     emit hideOnWindowDeactivateChanged();
75 }
76 
wFlags() const77 Qt::WindowFlags WidgetExplorerView::wFlags() const
78 {
79     return (flags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
80 }
81 
geometryWhenVisible() const82 QRect WidgetExplorerView::geometryWhenVisible() const
83 {
84     return m_geometryWhenVisible;
85 }
86 
initParentView(Latte::View * view)87 void WidgetExplorerView::initParentView(Latte::View *view)
88 {
89     SubConfigView::initParentView(view);
90 
91     rootContext()->setContextProperty(QStringLiteral("containmentFromView"), m_latteView->containment());
92 
93     updateEnabledBorders();
94     syncGeometry();
95 }
96 
availableScreenGeometry() const97 QRect WidgetExplorerView::availableScreenGeometry() const
98 {
99     int currentScrId = m_latteView->positioner()->currentScreenId();
100 
101     QList<Latte::Types::Visibility> ignoreModes{Latte::Types::SidebarOnDemand,Latte::Types::SidebarAutoHide};
102 
103     if (m_latteView->visibility() && m_latteView->visibility()->isSidebar()) {
104         ignoreModes.removeAll(Latte::Types::SidebarOnDemand);
105         ignoreModes.removeAll(Latte::Types::SidebarAutoHide);
106     }
107 
108     QString activityid = m_latteView->layout()->lastUsedActivity();
109 
110     return m_corona->availableScreenRectWithCriteria(currentScrId, activityid, ignoreModes, {}, false, true);
111 }
112 
syncGeometry()113 void WidgetExplorerView::syncGeometry()
114 {
115     if (!m_latteView || !m_latteView->layout() || !m_latteView->containment() || !rootObject()) {
116         return;
117     }
118     const QSize size(rootObject()->width(), rootObject()->height());
119     auto availGeometry = availableScreenGeometry();
120 
121     int margin = availGeometry.height() == m_latteView->screenGeometry().height() ? 100 : 0;
122     auto geometry = QRect(availGeometry.x(), availGeometry.y(), size.width(), availGeometry.height()-margin);
123 
124     updateEnabledBorders();
125 
126     if (m_geometryWhenVisible == geometry) {
127         return;
128     }
129 
130     m_geometryWhenVisible = geometry;
131 
132     setPosition(geometry.topLeft());
133 
134     if (m_shellSurface) {
135         m_shellSurface->setPosition(geometry.topLeft());
136     }
137 
138     setMaximumSize(geometry.size());
139     setMinimumSize(geometry.size());
140     resize(geometry.size());
141 }
142 
showEvent(QShowEvent * ev)143 void WidgetExplorerView::showEvent(QShowEvent *ev)
144 {
145     if (m_shellSurface) {
146         //! under wayland it needs to be set again after its hiding
147         m_shellSurface->setPosition(m_geometryWhenVisible.topLeft());
148     }
149 
150     SubConfigView::showEvent(ev);
151 
152     if (!m_latteView) {
153         return;
154     }
155 
156     syncGeometry();
157 
158     requestActivate();
159 
160     m_screenSyncTimer.start();
161     QTimer::singleShot(400, this, &WidgetExplorerView::syncGeometry);
162 
163     emit showSignal();
164 }
165 
focusOutEvent(QFocusEvent * ev)166 void WidgetExplorerView::focusOutEvent(QFocusEvent *ev)
167 {
168     Q_UNUSED(ev);
169 
170     if (!m_latteView) {
171         return;
172     }
173 
174     hideConfigWindow();
175 }
176 
updateEffects()177 void WidgetExplorerView::updateEffects()
178 {
179     //! Don't apply any effect before the wayland surface is created under wayland
180     //! https://bugs.kde.org/show_bug.cgi?id=392890
181     if (KWindowSystem::isPlatformWayland() && !m_shellSurface) {
182         return;
183     }
184 
185     if (!m_background) {
186         m_background = new Plasma::FrameSvg(this);
187     }
188 
189     if (m_background->imagePath() != "dialogs/background") {
190         m_background->setImagePath(QStringLiteral("dialogs/background"));
191     }
192 
193     m_background->setEnabledBorders(m_enabledBorders);
194     m_background->resizeFrame(size());
195 
196     QRegion mask = m_background->mask();
197 
198     QRegion fixedMask = mask.isNull() ? QRegion(QRect(0,0,width(),height())) : mask;
199 
200     if (!fixedMask.isEmpty()) {
201         setMask(fixedMask);
202     } else {
203         setMask(QRegion());
204     }
205 
206     if (KWindowSystem::compositingActive()) {
207         KWindowEffects::enableBlurBehind(winId(), true, fixedMask);
208     } else {
209         KWindowEffects::enableBlurBehind(winId(), false);
210     }
211 }
212 
hideConfigWindow()213 void WidgetExplorerView::hideConfigWindow()
214 {
215     if (!m_hideOnWindowDeactivate) {
216         return;
217     }
218 
219     deleteLater();
220 
221     /*QTimer::singleShot(100, [this]() {
222         //! avoid crashes under wayland because some mouse events are sended after the surface is destroyed
223 
224         if (m_shellSurface) {
225             //!NOTE: Avoid crash in wayland environment with qt5.9
226             close();
227         } else {
228             hide();
229         }
230     });*/
231 }
232 
syncSlideEffect()233 void WidgetExplorerView::syncSlideEffect()
234 {
235     if (!m_latteView || !m_latteView->containment()) {
236         return;
237     }
238 
239     auto slideLocation = WindowSystem::AbstractWindowInterface::Slide::Left;
240 
241     m_corona->wm()->slideWindow(*this, slideLocation);
242 }
243 
244 //!BEGIN borders
updateEnabledBorders()245 void WidgetExplorerView::updateEnabledBorders()
246 {
247     if (!this->screen()) {
248         return;
249     }
250 
251     Plasma::FrameSvg::EnabledBorders borders = Plasma::FrameSvg::AllBorders;
252 
253     if (!m_geometryWhenVisible.isEmpty()) {
254         if (m_geometryWhenVisible.x() == m_latteView->screenGeometry().x()) {
255             borders &= ~Plasma::FrameSvg::LeftBorder;
256         }
257 
258         if (m_geometryWhenVisible.y() == m_latteView->screenGeometry().y()) {
259             borders &= ~Plasma::FrameSvg::TopBorder;
260         }
261 
262         if (m_geometryWhenVisible.height() == m_latteView->screenGeometry().height()) {
263             borders &= ~Plasma::FrameSvg::BottomBorder;
264         }
265     }
266 
267     if (m_enabledBorders != borders) {
268         if (isVisible()) {
269             m_enabledBorders = borders;
270         }
271         m_corona->dialogShadows()->addWindow(this, m_enabledBorders);
272 
273         emit enabledBordersChanged();
274     }
275 }
276 
277 //!END borders
278 
279 }
280 }
281 
282