/* SPDX-FileCopyrightText: 2014 Marco Martin SPDX-License-Identifier: GPL-2.0-or-later */ #include "view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "appadaptor.h" #include "x11windowscreenrelativepositioner.h" KCONFIGGROUP_DECLARE_ENUM_QOBJECT(View, HistoryBehavior) View::View(PlasmaQuick::SharedQmlEngine *engine, QWindow *) : PlasmaQuick::PlasmaWindow() , m_engine(engine) , m_floating(false) { KCrash::initialize(); qmlRegisterUncreatableType("org.kde.krunner.private.view", 1, 0, "HistoryBehavior", u"Only for enums"_s); if (KWindowSystem::isPlatformX11()) { m_x11Positioner = new X11WindowScreenRelativePositioner(this); } // used only by screen readers setTitle(i18n("KRunner")); m_config = KConfigGroup(KSharedConfig::openConfig(), u"General"_s); m_stateData = KSharedConfig::openConfig(u"krunnerstaterc"_s, // KConfig::NoGlobals, QStandardPaths::GenericDataLocation) ->group(u"General"_s); m_configWatcher = KConfigWatcher::create(KSharedConfig::openConfig()); connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group) { const QLatin1String pluginsGrp("Plugins"); if (group.name() == QLatin1String("General")) { loadConfig(); } else if (group.name() == pluginsGrp) { Q_EMIT helpEnabledChanged(); } }); connect(&m_consumer, &KActivities::Consumer::currentActivityChanged, this, &View::activityChanged); Q_EMIT activityChanged(m_consumer.currentActivity()); loadConfig(); new AppAdaptor(this); QDBusConnection::sessionBus().registerObject(u"/App"_s, this); connect(m_engine, &PlasmaQuick::SharedQmlEngine::finished, this, &View::objectIncubated); m_engine->engine()->rootContext()->setContextProperty(u"runnerWindow"_s, this); m_engine->setSource(QUrl(u"qrc:/krunner/RunCommand.qml"_s)); m_engine->completeInitialization(); auto screenRemoved = [this](QScreen *screen) { if (screen == this->screen()) { setScreen(qGuiApp->primaryScreen()); hide(); } }; connect(qGuiApp, &QGuiApplication::screenRemoved, this, screenRemoved); connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &View::slotFocusWindowChanged); } View::~View() { } void View::objectIncubated() { auto item = qobject_cast(m_engine->rootObject()); setMainItem(item); auto updateSize = [this]() { resize(QSize(mainItem()->implicitWidth(), mainItem()->implicitHeight()).grownBy(padding()).boundedTo(screen()->availableSize())); }; connect(item, &QQuickItem::implicitHeightChanged, this, updateSize); connect(this, &View::paddingChanged, this, updateSize); updateSize(); } void View::slotFocusWindowChanged() { if (QGuiApplication::focusWindow() && m_requestedClipboardSelection) { displayWithClipboardContents(); } if (!QGuiApplication::focusWindow() && !m_pinned) { setVisible(false); } } bool View::freeFloating() const { return m_floating; } void View::setFreeFloating(bool floating) { m_floating = floating; if (m_floating) { KWindowEffects::slideWindow(this, KWindowEffects::NoEdge); setBorders(Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge); } else { KWindowEffects::slideWindow(this, KWindowEffects::TopEdge); setBorders(Qt::LeftEdge | Qt::RightEdge | Qt::BottomEdge); } positionOnScreen(); } void View::loadConfig() { setFreeFloating(m_config.readEntry("FreeFloating", false)); setRetainPriorSearch(m_config.readEntry("RetainPriorSearch", true)); setPinned(m_stateData.readEntry("Pinned", false)); setHistoryBehavior(m_config.readEntry("historyBehavior", m_historyBehavior)); } void View::showEvent(QShowEvent *event) { if (KWindowSystem::isPlatformX11()) { KX11Extras::setOnAllDesktops(winId(), true); } QQuickWindow::showEvent(event); requestActivate(); if (KWindowSystem::isPlatformX11()) { KX11Extras::forceActiveWindow(winId()); } } void View::positionOnScreen() { const auto screens = QGuiApplication::screens(); auto screenIt = screens.cend(); if (KWindowSystem::isPlatformWayland() && m_floating) { auto message = QDBusMessage::createMethodCall(u"org.kde.KWin"_s, u"/KWin"_s, u"org.kde.KWin"_s, u"activeOutputName"_s); QDBusReply reply = QDBusConnection::sessionBus().call(message); if (reply.isValid()) { const QString activeOutputName = reply.value(); screenIt = std::find_if(screens.cbegin(), screens.cend(), [&activeOutputName](QScreen *screen) { return screen->name() == activeOutputName; }); } } else if (KWindowSystem::isPlatformX11()) { screenIt = std::find_if(screens.cbegin(), screens.cend(), [](QScreen *screen) { return screen->geometry().contains(QCursor::pos(screen)); }); } QScreen *const shownOnScreen = screenIt != screens.cend() ? *screenIt : QGuiApplication::primaryScreen(); setScreen(shownOnScreen); QMargins margins; if (m_floating) { const QRect r = shownOnScreen->availableGeometry(); margins = QMargins({0, r.height() / 3, 0, 0}); } if (KWindowSystem::isPlatformWayland()) { auto layerWindow = LayerShellQt::Window::get(this); layerWindow->setAnchors(LayerShellQt::Window::AnchorTop); layerWindow->setLayer(LayerShellQt::Window::LayerTop); layerWindow->setScope(u"krunner"_s); layerWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityOnDemand); layerWindow->setMargins(margins); layerWindow->setScreenConfiguration(m_floating ? LayerShellQt::Window::ScreenFromQWindow : LayerShellQt::Window::ScreenFromCompositor); } else if (KWindowSystem::isPlatformX11()) { m_x11Positioner->setAnchors(Qt::TopEdge); m_x11Positioner->setMargins(margins); if (m_floating) { KX11Extras::setOnDesktop(winId(), KX11Extras::currentDesktop()); } else { KX11Extras::setOnAllDesktops(winId(), true); } KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager); } } void View::toggleDisplay() { if (isVisible() && !QGuiApplication::focusWindow() && KWindowSystem::isPlatformX11()) { KX11Extras::forceActiveWindow(winId()); return; } if (isVisible()) { setVisible(false); } else { display(); } } void View::display() { positionOnScreen(); setVisible(true); } void View::displaySingleRunner(const QString &runnerName) { display(); m_engine->rootObject()->setProperty("singleRunner", runnerName); m_engine->rootObject()->setProperty("query", QString()); } void View::displayWithClipboardContents() { display(); // On Wayland we cannot retrieve the clipboard selection until we get the focus if (QGuiApplication::focusWindow()) { m_requestedClipboardSelection = false; m_engine->rootObject()->setProperty("singleRunner", QString()); m_engine->rootObject()->setProperty("query", QGuiApplication::clipboard()->text(QClipboard::Selection)); } else { m_requestedClipboardSelection = true; } } void View::query(const QString &term) { display(); m_engine->rootObject()->setProperty("singleRunner", QString()); m_engine->rootObject()->setProperty("query", term); } void View::querySingleRunner(const QString &runnerName, const QString &term) { display(); m_engine->rootObject()->setProperty("singleRunner", runnerName); m_engine->rootObject()->setProperty("query", term); } bool View::pinned() const { return m_pinned; } void View::setPinned(bool pinned) { if (m_pinned != pinned) { m_pinned = pinned; m_stateData.writeEntry("Pinned", pinned); Q_EMIT pinnedChanged(); } }