File indexing completed on 2024-05-05 05:38:21
0001 /* 0002 SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "view.h" 0008 0009 #include <QAction> 0010 #include <QClipboard> 0011 #include <QDebug> 0012 #include <QGuiApplication> 0013 #include <QPlatformSurfaceEvent> 0014 #include <QQmlContext> 0015 #include <QQmlEngine> 0016 #include <QQuickItem> 0017 #include <QScreen> 0018 0019 #include <KAuthorized> 0020 #include <KCrash> 0021 #include <KIO/CommandLauncherJob> 0022 #include <KLocalizedString> 0023 #include <KService> 0024 #include <KWindowEffects> 0025 #include <KWindowSystem> 0026 #include <KX11Extras> 0027 0028 #include <LayerShellQt/Window> 0029 #include <qnamespace.h> 0030 0031 #include "appadaptor.h" 0032 #include "x11windowscreenrelativepositioner.h" 0033 0034 KCONFIGGROUP_DECLARE_ENUM_QOBJECT(View, HistoryBehavior) 0035 0036 View::View(PlasmaQuick::SharedQmlEngine *engine, QWindow *) 0037 : PlasmaQuick::PlasmaWindow() 0038 , m_engine(engine) 0039 , m_floating(false) 0040 { 0041 KCrash::initialize(); 0042 qmlRegisterUncreatableType<View>("org.kde.krunner.private.view", 1, 0, "HistoryBehavior", u"Only for enums"_s); 0043 0044 if (KWindowSystem::isPlatformX11()) { 0045 m_x11Positioner = new X11WindowScreenRelativePositioner(this); 0046 } 0047 0048 // used only by screen readers 0049 setTitle(i18n("KRunner")); 0050 0051 m_config = KConfigGroup(KSharedConfig::openConfig(), u"General"_s); 0052 m_stateData = KSharedConfig::openConfig(u"krunnerstaterc"_s, // 0053 KConfig::NoGlobals, 0054 QStandardPaths::GenericDataLocation) 0055 ->group(u"General"_s); 0056 m_configWatcher = KConfigWatcher::create(KSharedConfig::openConfig()); 0057 connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group) { 0058 const QLatin1String pluginsGrp("Plugins"); 0059 if (group.name() == QLatin1String("General")) { 0060 loadConfig(); 0061 } else if (group.name() == pluginsGrp) { 0062 Q_EMIT helpEnabledChanged(); 0063 } else if (group.name() == QLatin1String("Favorites") && group.parent().name() == pluginsGrp) { 0064 assignFavoriteIds(); 0065 } 0066 }); 0067 connect(&m_consumer, &KActivities::Consumer::currentActivityChanged, this, &View::activityChanged); 0068 Q_EMIT activityChanged(m_consumer.currentActivity()); 0069 0070 loadConfig(); 0071 0072 new AppAdaptor(this); 0073 QDBusConnection::sessionBus().registerObject(u"/App"_s, this); 0074 0075 connect(m_engine, &PlasmaQuick::SharedQmlEngine::finished, this, &View::objectIncubated); 0076 m_engine->engine()->rootContext()->setContextProperty(u"runnerWindow"_s, this); 0077 m_engine->setSource(QUrl(u"qrc:/krunner/RunCommand.qml"_s)); 0078 m_engine->completeInitialization(); 0079 0080 auto screenRemoved = [this](QScreen *screen) { 0081 if (screen == this->screen()) { 0082 setScreen(qGuiApp->primaryScreen()); 0083 hide(); 0084 } 0085 }; 0086 0087 connect(qGuiApp, &QGuiApplication::screenRemoved, this, screenRemoved); 0088 connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &View::slotFocusWindowChanged); 0089 } 0090 0091 View::~View() 0092 { 0093 } 0094 0095 void View::objectIncubated() 0096 { 0097 auto item = qobject_cast<QQuickItem *>(m_engine->rootObject()); 0098 setMainItem(item); 0099 0100 auto updateSize = [this]() { 0101 resize(QSize(mainItem()->implicitWidth(), mainItem()->implicitHeight()).grownBy(padding()).boundedTo(screen()->availableSize())); 0102 }; 0103 0104 connect(item, &QQuickItem::implicitHeightChanged, this, updateSize); 0105 connect(this, &View::paddingChanged, this, updateSize); 0106 updateSize(); 0107 } 0108 0109 void View::slotFocusWindowChanged() 0110 { 0111 if (QGuiApplication::focusWindow() && m_requestedClipboardSelection) { 0112 displayWithClipboardContents(); 0113 } 0114 0115 if (!QGuiApplication::focusWindow() && !m_pinned) { 0116 setVisible(false); 0117 } 0118 } 0119 0120 bool View::freeFloating() const 0121 { 0122 return m_floating; 0123 } 0124 0125 void View::setFreeFloating(bool floating) 0126 { 0127 m_floating = floating; 0128 if (m_floating) { 0129 KWindowEffects::slideWindow(this, KWindowEffects::NoEdge); 0130 setBorders(Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge); 0131 } else { 0132 KWindowEffects::slideWindow(this, KWindowEffects::TopEdge); 0133 setBorders(Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge); 0134 } 0135 0136 positionOnScreen(); 0137 } 0138 0139 void View::loadConfig() 0140 { 0141 setFreeFloating(m_config.readEntry("FreeFloating", false)); 0142 setRetainPriorSearch(m_config.readEntry("RetainPriorSearch", true)); 0143 setPinned(m_stateData.readEntry("Pinned", false)); 0144 setHistoryBehavior(m_config.readEntry("historyBehavior", m_historyBehavior)); 0145 assignFavoriteIds(); 0146 } 0147 0148 void View::showEvent(QShowEvent *event) 0149 { 0150 if (KWindowSystem::isPlatformX11()) { 0151 KX11Extras::setOnAllDesktops(winId(), true); 0152 } 0153 QQuickWindow::showEvent(event); 0154 requestActivate(); 0155 if (KWindowSystem::isPlatformX11()) { 0156 KX11Extras::forceActiveWindow(winId()); 0157 } 0158 } 0159 0160 void View::positionOnScreen() 0161 { 0162 const auto screens = QGuiApplication::screens(); 0163 auto screenIt = screens.cend(); 0164 if (KWindowSystem::isPlatformWayland() && m_floating) { 0165 auto message = QDBusMessage::createMethodCall("org.kde.KWin", "/KWin", "org.kde.KWin", "activeOutputName"); 0166 QDBusReply<QString> reply = QDBusConnection::sessionBus().call(message); 0167 if (reply.isValid()) { 0168 const QString activeOutputName = reply.value(); 0169 screenIt = std::find_if(screens.cbegin(), screens.cend(), [&activeOutputName](QScreen *screen) { 0170 return screen->name() == activeOutputName; 0171 }); 0172 } 0173 } else if (KWindowSystem::isPlatformX11()) { 0174 screenIt = std::find_if(screens.cbegin(), screens.cend(), [](QScreen *screen) { 0175 return screen->geometry().contains(QCursor::pos(screen)); 0176 }); 0177 } 0178 0179 QScreen *const shownOnScreen = screenIt != screens.cend() ? *screenIt : QGuiApplication::primaryScreen(); 0180 setScreen(shownOnScreen); 0181 0182 QMargins margins; 0183 if (m_floating) { 0184 const QRect r = shownOnScreen->availableGeometry(); 0185 margins = QMargins({0, r.height() / 3, 0, 0}); 0186 } 0187 0188 if (KWindowSystem::isPlatformWayland()) { 0189 auto layerWindow = LayerShellQt::Window::get(this); 0190 layerWindow->setAnchors(LayerShellQt::Window::AnchorTop); 0191 layerWindow->setLayer(LayerShellQt::Window::LayerTop); 0192 layerWindow->setScope(u"krunner"_s); 0193 layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand); 0194 layerWindow->setMargins(margins); 0195 layerWindow->setScreenConfiguration(m_floating ? LayerShellQt::Window::ScreenFromQWindow : LayerShellQt::Window::ScreenFromCompositor); 0196 } else if (KWindowSystem::isPlatformX11()) { 0197 m_x11Positioner->setAnchors(Qt::TopEdge); 0198 m_x11Positioner->setMargins(margins); 0199 if (m_floating) { 0200 KX11Extras::setOnDesktop(winId(), KX11Extras::currentDesktop()); 0201 } else { 0202 KX11Extras::setOnAllDesktops(winId(), true); 0203 } 0204 KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager); 0205 } 0206 } 0207 0208 void View::toggleDisplay() 0209 { 0210 if (isVisible() && !QGuiApplication::focusWindow() && KWindowSystem::isPlatformX11()) { 0211 KX11Extras::forceActiveWindow(winId()); 0212 return; 0213 } 0214 if (isVisible()) { 0215 setVisible(false); 0216 } else { 0217 display(); 0218 } 0219 } 0220 0221 void View::display() 0222 { 0223 positionOnScreen(); 0224 setVisible(true); 0225 } 0226 0227 void View::displaySingleRunner(const QString &runnerName) 0228 { 0229 display(); 0230 0231 m_engine->rootObject()->setProperty("singleRunner", runnerName); 0232 m_engine->rootObject()->setProperty("query", QString()); 0233 } 0234 0235 void View::displayWithClipboardContents() 0236 { 0237 display(); 0238 0239 // On Wayland we cannot retrieve the clipboard selection until we get the focus 0240 if (QGuiApplication::focusWindow()) { 0241 m_requestedClipboardSelection = false; 0242 m_engine->rootObject()->setProperty("singleRunner", QString()); 0243 m_engine->rootObject()->setProperty("query", QGuiApplication::clipboard()->text(QClipboard::Selection)); 0244 } else { 0245 m_requestedClipboardSelection = true; 0246 } 0247 } 0248 0249 void View::query(const QString &term) 0250 { 0251 display(); 0252 0253 m_engine->rootObject()->setProperty("singleRunner", QString()); 0254 m_engine->rootObject()->setProperty("query", term); 0255 } 0256 0257 void View::querySingleRunner(const QString &runnerName, const QString &term) 0258 { 0259 display(); 0260 0261 m_engine->rootObject()->setProperty("singleRunner", runnerName); 0262 m_engine->rootObject()->setProperty("query", term); 0263 } 0264 0265 bool View::pinned() const 0266 { 0267 return m_pinned; 0268 } 0269 0270 void View::setPinned(bool pinned) 0271 { 0272 if (m_pinned != pinned) { 0273 m_pinned = pinned; 0274 m_stateData.writeEntry("Pinned", pinned); 0275 Q_EMIT pinnedChanged(); 0276 } 0277 }