/* 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 #include #include #include "appadaptor.h" View::View(QWindow *) : PlasmaQuick::Dialog() , m_offset(.5) , m_floating(false) { setClearBeforeRendering(true); setColor(QColor(Qt::transparent)); setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); KCrash::initialize(); // used only by screen readers setTitle(i18n("KRunner")); m_config = KConfigGroup(KSharedConfig::openConfig(), "General"); m_stateData = KSharedConfig::openConfig(QStringLiteral("krunnerstaterc"), // KConfig::NoGlobals, QStandardPaths::GenericDataLocation) ->group("General"); m_configWatcher = KConfigWatcher::create(KSharedConfig::openConfig()); connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) { Q_UNUSED(names); if (group.name() == QLatin1String("General")) { loadConfig(); } }); loadConfig(); new AppAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/App"), this); m_qmlObj = new KDeclarative::QmlObject(this); m_qmlObj->setInitializationDelayed(true); connect(m_qmlObj, &KDeclarative::QmlObject::finished, this, &View::objectIncubated); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { package.setPath(packageName); } m_qmlObj->engine()->rootContext()->setContextProperty(QStringLiteral("runnerWindow"), this); m_qmlObj->setSource(package.fileUrl("runcommandmainscript")); m_qmlObj->completeInitialization(); auto screenRemoved = [this](QScreen *screen) { if (screen == this->screen()) { setScreen(qGuiApp->primaryScreen()); hide(); } }; auto screenAdded = [this](const QScreen *screen) { connect(screen, &QScreen::geometryChanged, this, &View::screenGeometryChanged); screenGeometryChanged(); }; const auto screens = QGuiApplication::screens(); for (QScreen *s : screens) { screenAdded(s); } connect(qGuiApp, &QGuiApplication::screenAdded, this, screenAdded); connect(qGuiApp, &QGuiApplication::screenRemoved, this, screenRemoved); connect(KWindowSystem::self(), &KWindowSystem::workAreaChanged, this, &View::resetScreenPos); connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &View::slotFocusWindowChanged); } View::~View() { } void View::objectIncubated() { auto mainItem = qobject_cast(m_qmlObj->rootObject()); connect(mainItem, &QQuickItem::widthChanged, this, &View::resetScreenPos); setMainItem(mainItem); } void View::slotFocusWindowChanged() { if (!QGuiApplication::focusWindow() && !m_pinned) { setVisible(false); } } bool View::freeFloating() const { return m_floating; } void View::setFreeFloating(bool floating) { if (m_floating == floating) { return; } m_floating = floating; if (m_floating) { setLocation(Plasma::Types::Floating); } else { setLocation(Plasma::Types::TopEdge); } positionOnScreen(); } void View::loadConfig() { setFreeFloating(m_config.readEntry("FreeFloating", false)); setPinned(m_stateData.readEntry("Pinned", false)); } bool View::event(QEvent *event) { if (KWindowSystem::isPlatformWayland() && event->type() == QEvent::Expose && !dynamic_cast(event)->region().isNull()) { auto surface = KWayland::Client::Surface::fromWindow(this); auto shellSurface = KWayland::Client::PlasmaShellSurface::get(surface); if (shellSurface && isVisible()) { shellSurface->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow); shellSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel); shellSurface->setPanelTakesFocus(true); } } const bool retval = Dialog::event(event); // QXcbWindow overwrites the state in its show event. There are plans // to fix this in 5.4, but till then we must explicitly overwrite it // each time. bool setState = event->type() == QEvent::Show; if (event->type() == QEvent::PlatformSurface) { setState = (static_cast(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated); } if (setState) { KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); } return retval; } void View::resizeEvent(QResizeEvent *event) { if (event->oldSize().width() != event->size().width()) { positionOnScreen(); } } void View::showEvent(QShowEvent *event) { KWindowSystem::setOnAllDesktops(winId(), true); Dialog::showEvent(event); positionOnScreen(); requestActivate(); } void View::screenGeometryChanged() { if (isVisible()) { positionOnScreen(); } } void View::resetScreenPos() { if (isVisible() && !m_floating) { positionOnScreen(); } } void View::positionOnScreen() { if (!m_requestedVisible) { return; } QScreen *shownOnScreen = QGuiApplication::primaryScreen(); const auto screens = QGuiApplication::screens(); for (QScreen *screen : screens) { if (screen->geometry().contains(QCursor::pos(screen))) { shownOnScreen = screen; break; } } // in wayland, QScreen::availableGeometry() returns QScreen::geometry() // we could get a better value from plasmashell // BUG: 386114 auto message = QDBusMessage::createMethodCall("org.kde.plasmashell", "/StrutManager", "org.kde.PlasmaShell.StrutManager", "availableScreenRect"); message.setArguments({shownOnScreen->name()}); QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, shownOnScreen]() { watcher->deleteLater(); QDBusPendingReply reply = *watcher; const QRect r = reply.isValid() ? reply.value() : shownOnScreen->availableGeometry(); if (m_floating && !m_customPos.isNull()) { int x = qBound(r.left(), m_customPos.x(), r.right() - width()); int y = qBound(r.top(), m_customPos.y(), r.bottom() - height()); setPosition(x, y); PlasmaQuick::Dialog::setVisible(true); return; } const int w = width(); int x = r.left() + (r.width() * m_offset) - (w / 2); int y = r.top(); if (m_floating) { y += r.height() / 3; } x = qBound(r.left(), x, r.right() - width()); y = qBound(r.top(), y, r.bottom() - height()); setPosition(x, y); PlasmaQuick::Dialog::setVisible(true); if (m_floating) { KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop()); KWindowSystem::setType(winId(), NET::Normal); // Turn the sliding effect off setLocation(Plasma::Types::Floating); } else { KWindowSystem::setOnAllDesktops(winId(), true); setLocation(Plasma::Types::TopEdge); } KWindowSystem::forceActiveWindow(winId()); }); } void View::toggleDisplay() { if (isVisible() && !QGuiApplication::focusWindow()) { KWindowSystem::forceActiveWindow(winId()); return; } setVisible(!isVisible()); } void View::display() { setVisible(true); } void View::displaySingleRunner(const QString &runnerName) { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", runnerName); m_qmlObj->rootObject()->setProperty("query", QString()); } void View::displayWithClipboardContents() { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", QString()); m_qmlObj->rootObject()->setProperty("query", QGuiApplication::clipboard()->text(QClipboard::Selection)); } void View::query(const QString &term) { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", QString()); m_qmlObj->rootObject()->setProperty("query", term); } void View::querySingleRunner(const QString &runnerName, const QString &term) { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", runnerName); m_qmlObj->rootObject()->setProperty("query", term); } void View::switchUser() { QDBusConnection::sessionBus().asyncCall(QDBusMessage::createMethodCall(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QStringLiteral("org.kde.KSMServerInterface"), QStringLiteral("openSwitchUserDialog"))); } void View::displayConfiguration() { const QString systemSettings = QStringLiteral("systemsettings"); const QStringList kcmToOpen = QStringList(QStringLiteral("kcm_plasmasearch")); KIO::CommandLauncherJob *job = nullptr; if (KService::serviceByDesktopName(systemSettings)) { job = new KIO::CommandLauncherJob(QStringLiteral("systemsettings5"), kcmToOpen); job->setDesktopName(systemSettings); } else { job = new KIO::CommandLauncherJob(QStringLiteral("kcmshell5"), kcmToOpen); } job->start(); } bool View::canConfigure() const { return KAuthorized::authorizeControlModule(QStringLiteral("kcm_plasmasearch.desktop")); } void View::setVisible(bool visible) { m_requestedVisible = visible; if (visible && !m_floating) { positionOnScreen(); } else { PlasmaQuick::Dialog::setVisible(visible); } } 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(); } } void View::removeFromHistory(int index) { if (m_manager) { m_manager->removeFromHistory(index); Q_EMIT historyChanged(); } } QStringList View::history() const { return m_manager ? m_manager->history() : QStringList(); }