1 /*
2     SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "subconfigview.h"
7 
8 //local
9 #include <config-latte.h>
10 #include "../view.h"
11 #include "../../lattecorona.h"
12 #include "../../layouts/manager.h"
13 #include "../../plasma/extended/theme.h"
14 #include "../../settings/universalsettings.h"
15 #include "../../shortcuts/globalshortcuts.h"
16 #include "../../shortcuts/shortcutstracker.h"
17 #include "../../wm/abstractwindowinterface.h"
18 
19 // KDE
20 #include <KLocalizedContext>
21 #include <KDeclarative/KDeclarative>
22 #include <KWayland/Client/plasmashell.h>
23 #include <KWayland/Client/surface.h>
24 #include <KWindowSystem>
25 
26 namespace Latte {
27 namespace ViewPart {
28 
SubConfigView(Latte::View * view,const QString & title,const bool & isNormalWindow)29 SubConfigView::SubConfigView(Latte::View *view, const QString &title, const bool &isNormalWindow)
30     : QQuickView(nullptr),
31       m_isNormalWindow(isNormalWindow)
32 {
33     m_corona = qobject_cast<Latte::Corona *>(view->containment()->corona());
34 
35     setupWaylandIntegration();
36 
37     if (KWindowSystem::isPlatformX11()) {
38         m_corona->wm()->registerIgnoredWindow(winId());
39     } else {
40         connect(this, &QWindow::windowTitleChanged, this, &SubConfigView::updateWaylandId);
41         connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, &SubConfigView::updateWaylandId);
42     }
43 
44     m_validTitle = title;
45     setTitle(m_validTitle);
46 
47     setScreen(view->screen());
48     setIcon(qGuiApp->windowIcon());
49 
50     if (!m_isNormalWindow) {
51         setFlags(wFlags());
52         m_corona->wm()->setViewExtraFlags(this, true);
53     }
54 
55     m_screenSyncTimer.setSingleShot(true);
56     m_screenSyncTimer.setInterval(100);
57 
58     connections << connect(&m_screenSyncTimer, &QTimer::timeout, this, [this]() {
59         if (!m_latteView) {
60             return;
61         }
62 
63         setScreen(m_latteView->screen());
64 
65         if (KWindowSystem::isPlatformX11()) {
66             if (m_isNormalWindow) {
67                 setFlags(wFlags());
68                 m_corona->wm()->setViewExtraFlags(this, false, Latte::Types::NormalWindow);
69             }
70         }
71 
72         syncGeometry();
73     });
74 
75     m_showTimer.setSingleShot(true);
76     m_showTimer.setInterval(0);
77 
78     connections << connect(&m_showTimer, &QTimer::timeout, this, [this]() {
79         syncSlideEffect();
80         show();
81     });
82 }
83 
~SubConfigView()84 SubConfigView::~SubConfigView()
85 {
86     qDebug() << validTitle() << " deleting...";
87 
88     m_corona->dialogShadows()->removeWindow(this);
89 
90     m_corona->wm()->unregisterIgnoredWindow(KWindowSystem::isPlatformX11() ? winId() : m_waylandWindowId);
91 
92     for (const auto &var : connections) {
93         QObject::disconnect(var);
94     }
95 
96     for (const auto &var : viewconnections) {
97         QObject::disconnect(var);
98     }
99 }
100 
init()101 void SubConfigView::init()
102 {
103     qDebug() << validTitle() << " : initialization started...";
104 
105     setDefaultAlphaBuffer(true);
106     setColor(Qt::transparent);
107 
108     rootContext()->setContextProperty(QStringLiteral("viewConfig"), this);
109     rootContext()->setContextProperty(QStringLiteral("shortcutsEngine"), m_corona->globalShortcuts()->shortcutsTracker());
110 
111     if (m_corona) {
112         rootContext()->setContextProperty(QStringLiteral("universalSettings"), m_corona->universalSettings());
113         rootContext()->setContextProperty(QStringLiteral("layoutsManager"), m_corona->layoutsManager());
114         rootContext()->setContextProperty(QStringLiteral("themeExtended"), m_corona->themeExtended());
115     }
116 
117     KDeclarative::KDeclarative kdeclarative;
118     kdeclarative.setDeclarativeEngine(engine());
119     kdeclarative.setTranslationDomain(QStringLiteral("latte-dock"));
120 #if KF5_VERSION_MINOR >= 45
121     kdeclarative.setupContext();
122     kdeclarative.setupEngine(engine());
123 #else
124     kdeclarative.setupBindings();
125 #endif
126 }
127 
wFlags() const128 Qt::WindowFlags SubConfigView::wFlags() const
129 {
130     return (flags() | Qt::FramelessWindowHint) & ~Qt::WindowDoesNotAcceptFocus;
131 }
132 
validTitle() const133 QString SubConfigView::validTitle() const
134 {
135     return m_validTitle;
136 }
137 
trackedWindowId()138 Latte::WindowSystem::WindowId SubConfigView::trackedWindowId()
139 {
140     if (KWindowSystem::isPlatformWayland() && m_waylandWindowId.toInt() <= 0) {
141         updateWaylandId();
142     }
143 
144     return !KWindowSystem::isPlatformWayland() ? winId() :  m_waylandWindowId;
145 }
146 
corona() const147 Latte::Corona *SubConfigView::corona() const
148 {
149     return m_corona;
150 }
151 
parentView() const152 Latte::View *SubConfigView::parentView() const
153 {
154     return m_latteView;
155 }
156 
setParentView(Latte::View * view,const bool & immediate)157 void SubConfigView::setParentView(Latte::View *view, const bool &immediate)
158 {
159     if (m_latteView == view) {
160         return;
161     }
162 
163     initParentView(view);
164 }
165 
initParentView(Latte::View * view)166 void SubConfigView::initParentView(Latte::View *view)
167 {
168     for (const auto &var : viewconnections) {
169         QObject::disconnect(var);
170     }
171 
172     m_latteView = view;
173 
174     viewconnections << connect(m_latteView->positioner(), &ViewPart::Positioner::canvasGeometryChanged, this, &SubConfigView::syncGeometry);
175 
176     //! Assign app interfaces in be accessible through containment graphic item
177     QQuickItem *containmentGraphicItem = qobject_cast<QQuickItem *>(m_latteView->containment()->property("_plasma_graphicObject").value<QObject *>());
178     rootContext()->setContextProperty(QStringLiteral("plasmoid"), containmentGraphicItem);
179     rootContext()->setContextProperty(QStringLiteral("latteView"), m_latteView);
180 }
181 
requestActivate()182 void SubConfigView::requestActivate()
183 {
184     if (KWindowSystem::isPlatformWayland() && m_shellSurface) {
185         updateWaylandId();
186         m_corona->wm()->requestActivate(m_waylandWindowId);
187     } else {
188         QQuickView::requestActivate();
189     }
190 }
191 
showAfter(int msecs)192 void SubConfigView::showAfter(int msecs)
193 {
194     if (isVisible()) {
195         return;
196     }
197 
198     m_showTimer.setInterval(msecs);
199     m_showTimer.start();
200 
201 }
202 
syncSlideEffect()203 void SubConfigView::syncSlideEffect()
204 {
205     if (!m_latteView || !m_latteView->containment()) {
206         return;
207     }
208 
209     auto slideLocation = WindowSystem::AbstractWindowInterface::Slide::None;
210 
211     switch (m_latteView->containment()->location()) {
212     case Plasma::Types::TopEdge:
213         slideLocation = WindowSystem::AbstractWindowInterface::Slide::Top;
214         break;
215 
216     case Plasma::Types::RightEdge:
217         slideLocation = WindowSystem::AbstractWindowInterface::Slide::Right;
218         break;
219 
220     case Plasma::Types::BottomEdge:
221         slideLocation = WindowSystem::AbstractWindowInterface::Slide::Bottom;
222         break;
223 
224     case Plasma::Types::LeftEdge:
225         slideLocation = WindowSystem::AbstractWindowInterface::Slide::Left;
226         break;
227 
228     default:
229         qDebug() << staticMetaObject.className() << "wrong location";
230         break;
231     }
232 
233     m_corona->wm()->slideWindow(*this, slideLocation);
234 }
235 
surface()236 KWayland::Client::PlasmaShellSurface *SubConfigView::surface()
237 {
238     return m_shellSurface;
239 }
240 
setupWaylandIntegration()241 void SubConfigView::setupWaylandIntegration()
242 {
243     if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) {
244         // already setup
245         return;
246     }
247 
248     if (m_corona) {
249         using namespace KWayland::Client;
250         PlasmaShell *interface = m_corona->waylandCoronaInterface();
251 
252         if (!interface) {
253             return;
254         }
255 
256         Surface *s = Surface::fromWindow(this);
257 
258         if (!s) {
259             return;
260         }
261 
262         qDebug() << "wayland " << title() <<  " surface was created...";
263 
264         m_shellSurface = interface->createSurface(s, this);
265 
266         if (m_isNormalWindow) {
267             m_corona->wm()->setViewExtraFlags(m_shellSurface, false);
268         } else {
269             m_corona->wm()->setViewExtraFlags(m_shellSurface, true);
270         }
271 
272         updateWaylandId();
273         syncGeometry();
274     }
275 }
276 
showEvent(QShowEvent * ev)277 void SubConfigView::showEvent(QShowEvent *ev)
278 {
279     QQuickView::showEvent(ev);
280 
281     if (m_shellSurface) {
282         //! readd shadows after hiding because the window shadows are not shown again after first showing
283         m_corona->dialogShadows()->addWindow(this, m_enabledBorders);
284     }
285 }
286 
event(QEvent * e)287 bool SubConfigView::event(QEvent *e)
288 {
289     if (e->type() == QEvent::PlatformSurface) {
290         if (auto pe = dynamic_cast<QPlatformSurfaceEvent *>(e)) {
291             switch (pe->surfaceEventType()) {
292             case QPlatformSurfaceEvent::SurfaceCreated:
293 
294                 if (m_shellSurface) {
295                     break;
296                 }
297 
298                 setupWaylandIntegration();
299                 break;
300 
301             case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
302                 if (m_shellSurface) {
303                     delete m_shellSurface;
304                     m_shellSurface = nullptr;
305                     qDebug() << "WAYLAND " << title() <<  " window surface was deleted...";
306                 }
307 
308                 break;
309             }
310         }
311     }
312 
313     return QQuickView::event(e);
314 }
315 
updateWaylandId()316 void SubConfigView::updateWaylandId()
317 {
318     Latte::WindowSystem::WindowId newId = m_corona->wm()->winIdFor("latte-dock", validTitle());
319 
320     if (m_waylandWindowId != newId) {
321         if (!m_waylandWindowId.isNull()) {
322             m_corona->wm()->unregisterIgnoredWindow(m_waylandWindowId);
323         }
324 
325         m_waylandWindowId = newId;
326         m_corona->wm()->registerIgnoredWindow(m_waylandWindowId);
327     }
328 }
329 
enabledBorders() const330 Plasma::FrameSvg::EnabledBorders SubConfigView::enabledBorders() const
331 {
332     return m_enabledBorders;
333 }
334 
335 }
336 }
337