mirror of
https://github.com/KDE/latte-dock.git
synced 2024-12-23 01:33:50 +03:00
introduce floating gap window
--this window responsibility is to check if the mouse is still inside the real floating gap and in such case to prevent the dock from hiding
This commit is contained in:
parent
35ce0496ec
commit
3f68665300
@ -3,6 +3,7 @@ set(lattedock-app_SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/containmentinterface.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/containmentinterface.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/contextmenu.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/contextmenu.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/effects.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/effects.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/floatinggapwindow.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/panelshadows.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/panelshadows.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/positioner.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/positioner.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/screenedgeghostwindow.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/screenedgeghostwindow.cpp
|
||||||
|
378
app/view/floatinggapwindow.cpp
Normal file
378
app/view/floatinggapwindow.cpp
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Michail Vourlakos <mvourlakos@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Latte-Dock
|
||||||
|
*
|
||||||
|
* Latte-Dock is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Latte-Dock is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "floatinggapwindow.h"
|
||||||
|
|
||||||
|
// local
|
||||||
|
#include "view.h"
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSurfaceFormat>
|
||||||
|
#include <QQuickView>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
// KDE
|
||||||
|
#include <KWayland/Client/plasmashell.h>
|
||||||
|
#include <KWayland/Client/surface.h>
|
||||||
|
#include <KWindowSystem>
|
||||||
|
|
||||||
|
// X11
|
||||||
|
#include <NETWM>
|
||||||
|
|
||||||
|
namespace Latte {
|
||||||
|
namespace ViewPart {
|
||||||
|
|
||||||
|
FloatingGapWindow::FloatingGapWindow(Latte::View *view) :
|
||||||
|
m_latteView(view)
|
||||||
|
{
|
||||||
|
m_corona = qobject_cast<Latte::Corona *>(view->corona());
|
||||||
|
|
||||||
|
m_debugMode = (qApp->arguments().contains("-d") && qApp->arguments().contains("--kwinedges"));
|
||||||
|
|
||||||
|
if (m_debugMode) {
|
||||||
|
m_showColor = QColor("green");
|
||||||
|
m_hideColor = QColor("red");
|
||||||
|
} else {
|
||||||
|
m_showColor = QColor(Qt::transparent);
|
||||||
|
m_hideColor = QColor(Qt::transparent);
|
||||||
|
|
||||||
|
m_showColor.setAlpha(0);
|
||||||
|
m_hideColor.setAlpha(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
setColor(m_showColor);
|
||||||
|
setDefaultAlphaBuffer(true);
|
||||||
|
|
||||||
|
setFlags(Qt::FramelessWindowHint
|
||||||
|
| Qt::WindowStaysOnTopHint
|
||||||
|
| Qt::NoDropShadowWindowHint
|
||||||
|
| Qt::WindowDoesNotAcceptFocus);
|
||||||
|
|
||||||
|
m_fixGeometryTimer.setSingleShot(true);
|
||||||
|
m_fixGeometryTimer.setInterval(500);
|
||||||
|
connect(&m_fixGeometryTimer, &QTimer::timeout, this, &FloatingGapWindow::fixGeometry);
|
||||||
|
|
||||||
|
//! this timer is used in order to identify if mouse is still present in sensitive floating
|
||||||
|
//! areas and in such case to prevent a real-floating view to hide itself
|
||||||
|
m_asyncMouseTimer.setSingleShot(true);
|
||||||
|
m_asyncMouseTimer.setInterval(200);
|
||||||
|
connect(&m_asyncMouseTimer, &QTimer::timeout, this, [this]() {
|
||||||
|
if (m_inAsyncContainsMouse && !m_containsMouse) {
|
||||||
|
emit asyncContainsMouseChanged(false);
|
||||||
|
hideWithMask();
|
||||||
|
m_inAsyncContainsMouse = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &QQuickView::xChanged, this, &FloatingGapWindow::startGeometryTimer);
|
||||||
|
connect(this, &QQuickView::yChanged, this, &FloatingGapWindow::startGeometryTimer);
|
||||||
|
connect(this, &QQuickView::widthChanged, this, &FloatingGapWindow::startGeometryTimer);
|
||||||
|
connect(this, &QQuickView::heightChanged, this, &FloatingGapWindow::startGeometryTimer);
|
||||||
|
|
||||||
|
connect(m_latteView, &Latte::View::absoluteGeometryChanged, this, &FloatingGapWindow::updateGeometry);
|
||||||
|
connect(m_latteView, &Latte::View::screenGeometryChanged, this, &FloatingGapWindow::updateGeometry);
|
||||||
|
connect(m_latteView, &Latte::View::locationChanged, this, &FloatingGapWindow::updateGeometry);
|
||||||
|
connect(m_latteView, &QQuickView::screenChanged, this, [this]() {
|
||||||
|
setScreen(m_latteView->screen());
|
||||||
|
updateGeometry();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!KWindowSystem::isPlatformWayland()) {
|
||||||
|
//! IMPORTANT!!! ::: This fixes a bug when closing an Activity all views from all Activities are
|
||||||
|
//! disappearing! With this code parts they reappear!!!
|
||||||
|
m_visibleHackTimer1.setInterval(400);
|
||||||
|
m_visibleHackTimer2.setInterval(2500);
|
||||||
|
m_visibleHackTimer1.setSingleShot(true);
|
||||||
|
m_visibleHackTimer2.setSingleShot(true);
|
||||||
|
|
||||||
|
connectionsHack << connect(this, &QWindow::visibleChanged, this, [&]() {
|
||||||
|
if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) {
|
||||||
|
m_visibleHackTimer1.start();
|
||||||
|
m_visibleHackTimer2.start();
|
||||||
|
} else if (!m_inDelete) {
|
||||||
|
//! For some reason when the window is hidden in the edge under X11 afterwards
|
||||||
|
//! is losing its window flags
|
||||||
|
m_corona->wm()->setViewExtraFlags(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connectionsHack << connect(&m_visibleHackTimer1, &QTimer::timeout, this, [&]() {
|
||||||
|
if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) {
|
||||||
|
show();
|
||||||
|
emit forcedShown();
|
||||||
|
//qDebug() << "Floating Gap:: Enforce reshow from timer 1...";
|
||||||
|
} else {
|
||||||
|
//qDebug() << "Floating Gap:: No needed reshow from timer 1...";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connectionsHack << connect(&m_visibleHackTimer2, &QTimer::timeout, this, [&]() {
|
||||||
|
if (!m_inDelete && m_latteView && m_latteView->layout() && !isVisible()) {
|
||||||
|
show();
|
||||||
|
emit forcedShown();
|
||||||
|
//qDebug() << "Floating Gap:: Enforce reshow from timer 2...";
|
||||||
|
} else {
|
||||||
|
//qDebug() << "Floating Gap:: No needed reshow from timer 2...";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connectionsHack << connect(this, &FloatingGapWindow::forcedShown, this, [&]() {
|
||||||
|
m_corona->wm()->unregisterIgnoredWindow(m_trackedWindowId);
|
||||||
|
m_trackedWindowId = winId();
|
||||||
|
m_corona->wm()->registerIgnoredWindow(m_trackedWindowId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupWaylandIntegration();
|
||||||
|
|
||||||
|
if (KWindowSystem::isPlatformX11()) {
|
||||||
|
m_trackedWindowId = winId();
|
||||||
|
m_corona->wm()->registerIgnoredWindow(m_trackedWindowId);
|
||||||
|
} else {
|
||||||
|
connect(m_corona->wm(), &WindowSystem::AbstractWindowInterface::latteWindowAdded, this, [&]() {
|
||||||
|
if (m_trackedWindowId.isNull()) {
|
||||||
|
m_trackedWindowId = m_corona->wm()->winIdFor("latte-dock", geometry());
|
||||||
|
m_corona->wm()->registerIgnoredWindow(m_trackedWindowId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setScreen(m_latteView->screen());
|
||||||
|
show();
|
||||||
|
updateGeometry();
|
||||||
|
hideWithMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingGapWindow::~FloatingGapWindow()
|
||||||
|
{
|
||||||
|
m_inDelete = true;
|
||||||
|
|
||||||
|
m_corona->wm()->unregisterIgnoredWindow(KWindowSystem::isPlatformX11() ? winId() : m_trackedWindowId);
|
||||||
|
|
||||||
|
m_latteView = nullptr;
|
||||||
|
|
||||||
|
// clear mode
|
||||||
|
m_visibleHackTimer1.stop();
|
||||||
|
m_visibleHackTimer2.stop();
|
||||||
|
for (auto &c : connectionsHack) {
|
||||||
|
disconnect(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_shellSurface) {
|
||||||
|
delete m_shellSurface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FloatingGapWindow::location()
|
||||||
|
{
|
||||||
|
return (int)m_latteView->location();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FloatingGapWindow::thickness() const
|
||||||
|
{
|
||||||
|
return m_thickness;
|
||||||
|
}
|
||||||
|
|
||||||
|
Latte::View *FloatingGapWindow::parentView()
|
||||||
|
{
|
||||||
|
return m_latteView;
|
||||||
|
}
|
||||||
|
|
||||||
|
KWayland::Client::PlasmaShellSurface *FloatingGapWindow::surface()
|
||||||
|
{
|
||||||
|
return m_shellSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::updateGeometry()
|
||||||
|
{
|
||||||
|
if (m_latteView->positioner()->slideOffset() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect newGeometry;
|
||||||
|
|
||||||
|
m_thickness = m_latteView->screenEdgeMargin();
|
||||||
|
|
||||||
|
int length = m_latteView->formFactor() == Plasma::Types::Horizontal ? m_latteView->absoluteGeometry().width() : m_latteView->absoluteGeometry().height();
|
||||||
|
|
||||||
|
if (m_latteView->location() == Plasma::Types::BottomEdge) {
|
||||||
|
int xF = qMax(m_latteView->screenGeometry().left(), m_latteView->absoluteGeometry().left());
|
||||||
|
newGeometry.setX(xF);
|
||||||
|
newGeometry.setY(m_latteView->screenGeometry().bottom() - m_thickness);
|
||||||
|
} else if (m_latteView->location() == Plasma::Types::TopEdge) {
|
||||||
|
int xF = qMax(m_latteView->screenGeometry().left(), m_latteView->absoluteGeometry().left());
|
||||||
|
newGeometry.setX(xF);
|
||||||
|
newGeometry.setY(m_latteView->screenGeometry().top());
|
||||||
|
} else if (m_latteView->location() == Plasma::Types::LeftEdge) {
|
||||||
|
int yF = qMax(m_latteView->screenGeometry().top(), m_latteView->absoluteGeometry().top());
|
||||||
|
newGeometry.setX(m_latteView->screenGeometry().left());
|
||||||
|
newGeometry.setY(yF);
|
||||||
|
} else if (m_latteView->location() == Plasma::Types::RightEdge) {
|
||||||
|
int yF = qMax(m_latteView->screenGeometry().top(), m_latteView->absoluteGeometry().top());
|
||||||
|
newGeometry.setX(m_latteView->screenGeometry().right() - m_thickness);
|
||||||
|
newGeometry.setY(yF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_latteView->formFactor() == Plasma::Types::Horizontal) {
|
||||||
|
newGeometry.setWidth(length);
|
||||||
|
newGeometry.setHeight(m_thickness + 1);
|
||||||
|
} else {
|
||||||
|
newGeometry.setWidth(m_thickness + 1);
|
||||||
|
newGeometry.setHeight(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_calculatedGeometry = newGeometry;
|
||||||
|
|
||||||
|
fixGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::fixGeometry()
|
||||||
|
{
|
||||||
|
if (!m_calculatedGeometry.isEmpty()
|
||||||
|
&& (m_calculatedGeometry.x() != x() || m_calculatedGeometry.y() != y()
|
||||||
|
|| m_calculatedGeometry.width() != width() || m_calculatedGeometry.height() != height())) {
|
||||||
|
setMinimumSize(m_calculatedGeometry.size());
|
||||||
|
setMaximumSize(m_calculatedGeometry.size());
|
||||||
|
resize(m_calculatedGeometry.size());
|
||||||
|
setPosition(m_calculatedGeometry.x(), m_calculatedGeometry.y());
|
||||||
|
|
||||||
|
if (m_shellSurface) {
|
||||||
|
m_shellSurface->setPosition(m_calculatedGeometry.topLeft());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::startGeometryTimer()
|
||||||
|
{
|
||||||
|
m_fixGeometryTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::setupWaylandIntegration()
|
||||||
|
{
|
||||||
|
if (m_shellSurface || !KWindowSystem::isPlatformWayland() || !m_latteView || !m_latteView->containment()) {
|
||||||
|
// already setup
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_corona) {
|
||||||
|
using namespace KWayland::Client;
|
||||||
|
|
||||||
|
PlasmaShell *interface = m_corona->waylandCoronaInterface();
|
||||||
|
|
||||||
|
if (!interface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Surface *s = Surface::fromWindow(this);
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "wayland screen edge ghost window surface was created...";
|
||||||
|
m_shellSurface = interface->createSurface(s, this);
|
||||||
|
m_corona->wm()->setViewExtraFlags(m_shellSurface);
|
||||||
|
|
||||||
|
m_shellSurface->setPanelTakesFocus(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FloatingGapWindow::containsMouse() const
|
||||||
|
{
|
||||||
|
return m_containsMouse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::setContainsMouse(bool contains)
|
||||||
|
{
|
||||||
|
if (m_containsMouse == contains) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_containsMouse = contains;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FloatingGapWindow::event(QEvent *e)
|
||||||
|
{
|
||||||
|
if (e->type() == QEvent::DragEnter || e->type() == QEvent::DragMove) {
|
||||||
|
setContainsMouse(true);
|
||||||
|
emit dragEntered();
|
||||||
|
} else if (e->type() == QEvent::Enter) {
|
||||||
|
setContainsMouse(true);
|
||||||
|
triggerAsyncContainsMouseSignals();
|
||||||
|
} else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave) {
|
||||||
|
setContainsMouse(false);
|
||||||
|
if (m_inAsyncContainsMouse) {
|
||||||
|
m_asyncMouseTimer.stop();
|
||||||
|
m_inAsyncContainsMouse = false;
|
||||||
|
emit asyncContainsMouseChanged(true);
|
||||||
|
}
|
||||||
|
} else if (e->type() == QEvent::Show) {
|
||||||
|
m_corona->wm()->setViewExtraFlags(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QQuickView::event(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::callAsyncContainsMouse()
|
||||||
|
{
|
||||||
|
m_inAsyncContainsMouse = true;
|
||||||
|
m_asyncMouseTimer.start();
|
||||||
|
showWithMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::triggerAsyncContainsMouseSignals()
|
||||||
|
{
|
||||||
|
if (!m_inAsyncContainsMouse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! this function is called QEvent::Enter
|
||||||
|
m_asyncMouseTimer.stop();
|
||||||
|
hideWithMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::hideWithMask()
|
||||||
|
{
|
||||||
|
if (m_debugMode) {
|
||||||
|
qDebug() << " Floating Gap Window :: MASK HIDE...";
|
||||||
|
}
|
||||||
|
|
||||||
|
//! old values: 0,0,1,1 were blocking the top-left corner of the window
|
||||||
|
QRect maskGeometry{-2, 0, 1, 1};
|
||||||
|
setMask(maskGeometry);
|
||||||
|
|
||||||
|
//! repaint in order to update mask immediately
|
||||||
|
setColor(m_hideColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FloatingGapWindow::showWithMask()
|
||||||
|
{
|
||||||
|
if (m_debugMode) {
|
||||||
|
qDebug() << " Floating Gap Window :: MAKS SHOW...";
|
||||||
|
}
|
||||||
|
|
||||||
|
setMask(QRegion());
|
||||||
|
|
||||||
|
//! repaint in order to update mask immediately
|
||||||
|
setColor(m_showColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
139
app/view/floatinggapwindow.h
Normal file
139
app/view/floatinggapwindow.h
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Michail Vourlakos <mvourlakos@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Latte-Dock
|
||||||
|
*
|
||||||
|
* Latte-Dock is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of
|
||||||
|
* the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Latte-Dock is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FLOATINGGAPWINDOW_H
|
||||||
|
#define FLOATINGWINDOW_H
|
||||||
|
|
||||||
|
// local
|
||||||
|
#include "../lattecorona.h"
|
||||||
|
#include "../wm/windowinfowrap.h"
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQuickView>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
namespace KWayland {
|
||||||
|
namespace Client {
|
||||||
|
class PlasmaShellSurface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Latte {
|
||||||
|
class Corona;
|
||||||
|
class View;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Latte {
|
||||||
|
namespace ViewPart {
|
||||||
|
|
||||||
|
//! What is the importance of this class?
|
||||||
|
//!
|
||||||
|
//! This window is responsible to identify if the mouse is still present
|
||||||
|
//! in the REAL FLOATING GAP between the VIEW and the SCREEN EDGE.
|
||||||
|
//! When VIEWS are REAL FLOATING then the VIEW Window is really placed
|
||||||
|
//! as it is shown to the user. In that case we need a way to check
|
||||||
|
//! where the mouse is even though it is OUTSIDE the VIEW or the
|
||||||
|
//! SCREENEDGEGHOSTWINDOW. The main functionality of FloatingGapWindow
|
||||||
|
//! is that it is TEMPORARILY shown/draw after a MUSTHIDE signal of
|
||||||
|
//! VisibilityManager was sent; in order to check if the mouse is still
|
||||||
|
//! inside the FLOATINGGAP. After it has really identified where mouse
|
||||||
|
//! is present, an FloatingGapWindow::asyncContainsMouse(contains) signal
|
||||||
|
//! is sent.
|
||||||
|
|
||||||
|
class FloatingGapWindow : public QQuickView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FloatingGapWindow(Latte::View *view);
|
||||||
|
~FloatingGapWindow() override;
|
||||||
|
|
||||||
|
int location();
|
||||||
|
int thickness() const;
|
||||||
|
|
||||||
|
void hideWithMask();
|
||||||
|
void showWithMask();
|
||||||
|
|
||||||
|
Latte::View *parentView();
|
||||||
|
|
||||||
|
KWayland::Client::PlasmaShellSurface *surface();
|
||||||
|
|
||||||
|
void callAsyncContainsMouse();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void asyncContainsMouseChanged(bool contains); //called from visibility to check if mouse is in the free sensitive floating area
|
||||||
|
void dragEntered();
|
||||||
|
void forcedShown(); //[workaround] forced shown to avoid a KWin issue that hides windows when activities are stopped
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent *ev) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void startGeometryTimer();
|
||||||
|
void updateGeometry();
|
||||||
|
void fixGeometry();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool containsMouse() const;
|
||||||
|
void setContainsMouse(bool contains);
|
||||||
|
void setupWaylandIntegration();
|
||||||
|
|
||||||
|
void triggerAsyncContainsMouseSignals();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_debugMode{false};
|
||||||
|
|
||||||
|
bool m_containsMouse{false};
|
||||||
|
bool m_inDelete{false};
|
||||||
|
|
||||||
|
bool m_inAsyncContainsMouse{false}; //called from visibility to check if mouse is in the free sensitive floating area
|
||||||
|
|
||||||
|
int m_thickness{2};
|
||||||
|
|
||||||
|
QRect m_calculatedGeometry;
|
||||||
|
|
||||||
|
//! [workaround] colors in order to help masking to apply immediately
|
||||||
|
//! for some reason when the window in with no content the mask is not
|
||||||
|
//! update immediately
|
||||||
|
QColor m_hideColor;
|
||||||
|
QColor m_showColor;
|
||||||
|
|
||||||
|
QTimer m_asyncMouseTimer; //called from visibility to check if mouse is in the free sensitive floating area
|
||||||
|
QTimer m_fixGeometryTimer;
|
||||||
|
|
||||||
|
//! HACK: Timers in order to handle KWin faulty
|
||||||
|
//! behavior that hides Views when closing Activities
|
||||||
|
//! with no actual reason
|
||||||
|
QTimer m_visibleHackTimer1;
|
||||||
|
QTimer m_visibleHackTimer2;
|
||||||
|
//! Connections for the KWin visibility hack
|
||||||
|
QList<QMetaObject::Connection> connectionsHack;
|
||||||
|
|
||||||
|
Latte::View *m_latteView{nullptr};
|
||||||
|
|
||||||
|
QPointer<Latte::Corona> m_corona;
|
||||||
|
|
||||||
|
Latte::WindowSystem::WindowId m_trackedWindowId;
|
||||||
|
KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -197,10 +197,7 @@ void ScreenEdgeGhostWindow::updateGeometry()
|
|||||||
|
|
||||||
QRect newGeometry;
|
QRect newGeometry;
|
||||||
|
|
||||||
if (m_latteView->screenEdgeMargin()>0 && m_latteView->behaveAsPlasmaPanel()) {
|
if (KWindowSystem::compositingActive()) {
|
||||||
//real floating
|
|
||||||
m_thickness = m_latteView->screenEdgeMargin();
|
|
||||||
} else if (KWindowSystem::compositingActive()) {
|
|
||||||
m_thickness = 6;
|
m_thickness = 6;
|
||||||
} else {
|
} else {
|
||||||
m_thickness = 2;
|
m_thickness = 2;
|
||||||
|
@ -480,6 +480,18 @@ void View::updateAbsoluteGeometry(bool bypassChecks)
|
|||||||
QRect absGeometry {x() + m_localGeometry.x(), y() + m_localGeometry.y()
|
QRect absGeometry {x() + m_localGeometry.x(), y() + m_localGeometry.y()
|
||||||
, m_localGeometry.width(), m_localGeometry.height()};
|
, m_localGeometry.width(), m_localGeometry.height()};
|
||||||
|
|
||||||
|
if (isFloatingWindow()) {
|
||||||
|
if (location() == Plasma::Types::BottomEdge) {
|
||||||
|
absGeometry.setY(screenGeometry().bottom() - m_screenEdgeMargin - m_normalThickness);
|
||||||
|
} else if (location() == Plasma::Types::TopEdge) {
|
||||||
|
absGeometry.setY(screenGeometry().top() + m_screenEdgeMargin);
|
||||||
|
} else if (location() == Plasma::Types::LeftEdge) {
|
||||||
|
absGeometry.setX(screenGeometry().left() + m_screenEdgeMargin);
|
||||||
|
} else if (location() == Plasma::Types::RightEdge) {
|
||||||
|
absGeometry.setX(screenGeometry().right() - m_screenEdgeMargin - m_normalThickness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_absoluteGeometry == absGeometry && !bypassChecks) {
|
if (m_absoluteGeometry == absGeometry && !bypassChecks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
// local
|
// local
|
||||||
#include "positioner.h"
|
#include "positioner.h"
|
||||||
|
#include "floatinggapwindow.h"
|
||||||
#include "screenedgeghostwindow.h"
|
#include "screenedgeghostwindow.h"
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
#include "windowstracker/currentscreentracker.h"
|
#include "windowstracker/currentscreentracker.h"
|
||||||
@ -58,25 +59,11 @@ VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view)
|
|||||||
m_wm = m_corona->wm();
|
m_wm = m_corona->wm();
|
||||||
|
|
||||||
connect(this, &VisibilityManager::slideOutFinished, this, &VisibilityManager::updateHiddenState);
|
connect(this, &VisibilityManager::slideOutFinished, this, &VisibilityManager::updateHiddenState);
|
||||||
connect(this, &VisibilityManager::slideInFinished, this, [&]() {
|
connect(this, &VisibilityManager::slideInFinished, this, &VisibilityManager::updateHiddenState);
|
||||||
if (m_latteView && !m_latteView->screenEdgeMarginEnabled()) {
|
|
||||||
//! after slide-out the real floating windows should ignore their criteria
|
|
||||||
//! until containsMouse from view has been set to true and false to afterwards
|
|
||||||
updateHiddenState();
|
|
||||||
} else {
|
|
||||||
m_timerHide.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::updateKWinEdgesSupport);
|
connect(this, &VisibilityManager::enableKWinEdgesChanged, this, &VisibilityManager::updateKWinEdgesSupport);
|
||||||
connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateKWinEdgesSupport);
|
connect(this, &VisibilityManager::modeChanged, this, &VisibilityManager::updateKWinEdgesSupport);
|
||||||
|
|
||||||
connect(this, &VisibilityManager::mustBeHide, this, [&]() {
|
|
||||||
if (supportsKWinEdges() && m_latteView->screenEdgeMargin()>0 && m_latteView->behaveAsPlasmaPanel()) {
|
|
||||||
m_edgeGhostWindow->showWithMask();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_latteView) {
|
if (m_latteView) {
|
||||||
connect(m_latteView, &Latte::View::eventTriggered, this, &VisibilityManager::viewEventManager);
|
connect(m_latteView, &Latte::View::eventTriggered, this, &VisibilityManager::viewEventManager);
|
||||||
connect(m_latteView, &Latte::View::byPassWMChanged, this, &VisibilityManager::updateKWinEdgesSupport);
|
connect(m_latteView, &Latte::View::byPassWMChanged, this, &VisibilityManager::updateKWinEdgesSupport);
|
||||||
@ -107,8 +94,13 @@ VisibilityManager::VisibilityManager(PlasmaQuick::ContainmentView *view)
|
|||||||
});
|
});
|
||||||
connect(&m_timerHide, &QTimer::timeout, this, [&]() {
|
connect(&m_timerHide, &QTimer::timeout, this, [&]() {
|
||||||
if (!m_blockHiding && !m_isHidden && !m_isBelowLayer && !m_dragEnter) {
|
if (!m_blockHiding && !m_isHidden && !m_isBelowLayer && !m_dragEnter) {
|
||||||
// qDebug() << "must be hide";
|
if (m_latteView->isFloatingWindow()) {
|
||||||
emit mustBeHide();
|
//! first check if mouse is inside the floating gap
|
||||||
|
checkMouseInFloatingArea();
|
||||||
|
} else {
|
||||||
|
//! immediate call
|
||||||
|
emit mustBeHide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,6 +115,10 @@ VisibilityManager::~VisibilityManager()
|
|||||||
if (m_edgeGhostWindow) {
|
if (m_edgeGhostWindow) {
|
||||||
m_edgeGhostWindow->deleteLater();
|
m_edgeGhostWindow->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_floatingGapWindow) {
|
||||||
|
m_floatingGapWindow->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Types::Visibility VisibilityManager::mode() const
|
Types::Visibility VisibilityManager::mode() const
|
||||||
@ -491,13 +487,7 @@ void VisibilityManager::updateGhostWindowState()
|
|||||||
if (m_mode == Latte::Types::WindowsCanCover) {
|
if (m_mode == Latte::Types::WindowsCanCover) {
|
||||||
m_wm->setActiveEdge(m_edgeGhostWindow, m_isBelowLayer && !m_containsMouse);
|
m_wm->setActiveEdge(m_edgeGhostWindow, m_isBelowLayer && !m_containsMouse);
|
||||||
} else {
|
} else {
|
||||||
/* bool viewIsFloatingAndContainsMouse =
|
bool activated = (m_isHidden && !windowContainsMouse());
|
||||||
m_latteView->behaveAsPlasmaPanel()
|
|
||||||
&& m_latteView->screenEdgeMarginEnabled()
|
|
||||||
&& m_latteView->screenEdgeMargin()>0
|
|
||||||
&& (m_edgeGhostWindow->containsMouse() || m_containsMouse);*/
|
|
||||||
|
|
||||||
bool activated = (m_isHidden && !m_containsMouse && !m_edgeGhostWindow->containsMouse());
|
|
||||||
|
|
||||||
m_wm->setActiveEdge(m_edgeGhostWindow, activated);
|
m_wm->setActiveEdge(m_edgeGhostWindow, activated);
|
||||||
}
|
}
|
||||||
@ -572,7 +562,7 @@ void VisibilityManager::updateHiddenState()
|
|||||||
switch (m_mode) {
|
switch (m_mode) {
|
||||||
case Types::AutoHide:
|
case Types::AutoHide:
|
||||||
case Types::WindowsCanCover:
|
case Types::WindowsCanCover:
|
||||||
raiseView(m_containsMouse || (m_edgeGhostWindow && m_edgeGhostWindow->containsMouse()));
|
raiseView(m_containsMouse);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Types::DodgeActive:
|
case Types::DodgeActive:
|
||||||
@ -597,6 +587,10 @@ void VisibilityManager::applyActivitiesToHiddenWindows(const QStringList &activi
|
|||||||
if (m_edgeGhostWindow) {
|
if (m_edgeGhostWindow) {
|
||||||
m_wm->setWindowOnActivities(*m_edgeGhostWindow, activities);
|
m_wm->setWindowOnActivities(*m_edgeGhostWindow, activities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_floatingGapWindow) {
|
||||||
|
m_wm->setWindowOnActivities(*m_floatingGapWindow, activities);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisibilityManager::dodgeActive()
|
void VisibilityManager::dodgeActive()
|
||||||
@ -634,6 +628,7 @@ void VisibilityManager::dodgeAllWindows()
|
|||||||
|
|
||||||
if (m_containsMouse) {
|
if (m_containsMouse) {
|
||||||
raiseView(true);
|
raiseView(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool windowIntersects{m_latteView->windowsTracker()->currentScreen()->activeWindowTouching() || m_latteView->windowsTracker()->currentScreen()->existsWindowTouching()};
|
bool windowIntersects{m_latteView->windowsTracker()->currentScreen()->activeWindowTouching() || m_latteView->windowsTracker()->currentScreen()->existsWindowTouching()};
|
||||||
@ -720,9 +715,17 @@ void VisibilityManager::setContainsMouse(bool contains)
|
|||||||
|
|
||||||
m_containsMouse = contains;
|
m_containsMouse = contains;
|
||||||
emit containsMouseChanged();
|
emit containsMouseChanged();
|
||||||
|
}
|
||||||
|
|
||||||
if (contains && m_mode != Types::AlwaysVisible) {
|
bool VisibilityManager::windowContainsMouse()
|
||||||
raiseView(true);
|
{
|
||||||
|
return m_containsMouse || (m_edgeGhostWindow && m_edgeGhostWindow->containsMouse());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisibilityManager::checkMouseInFloatingArea()
|
||||||
|
{
|
||||||
|
if (m_floatingGapWindow && m_latteView->isFloatingWindow()) {
|
||||||
|
m_floatingGapWindow->callAsyncContainsMouse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,14 +788,19 @@ void VisibilityManager::updateKWinEdgesSupport()
|
|||||||
|
|
||||||
if (m_enableKWinEdgesFromUser) {
|
if (m_enableKWinEdgesFromUser) {
|
||||||
createEdgeGhostWindow();
|
createEdgeGhostWindow();
|
||||||
|
if (m_latteView->isFloatingWindow()) {
|
||||||
|
createFloatingGapWindow();
|
||||||
|
}
|
||||||
} else if (!m_enableKWinEdgesFromUser) {
|
} else if (!m_enableKWinEdgesFromUser) {
|
||||||
deleteEdgeGhostWindow();
|
deleteEdgeGhostWindow();
|
||||||
|
deleteFloatingGapWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (m_mode == Types::WindowsCanCover) {
|
} else if (m_mode == Types::WindowsCanCover) {
|
||||||
createEdgeGhostWindow();
|
createEdgeGhostWindow();
|
||||||
} else {
|
} else {
|
||||||
deleteEdgeGhostWindow();
|
deleteEdgeGhostWindow();
|
||||||
|
deleteFloatingGapWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,23 +809,12 @@ void VisibilityManager::createEdgeGhostWindow()
|
|||||||
if (!m_edgeGhostWindow) {
|
if (!m_edgeGhostWindow) {
|
||||||
m_edgeGhostWindow = new ScreenEdgeGhostWindow(m_latteView);
|
m_edgeGhostWindow = new ScreenEdgeGhostWindow(m_latteView);
|
||||||
|
|
||||||
connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) {
|
connect(m_edgeGhostWindow, &ScreenEdgeGhostWindow::containsMouseChanged, this, [ = ](bool contains) {
|
||||||
if (contains) {
|
if (contains) {
|
||||||
if (!m_isHidden) {
|
raiseView(true);
|
||||||
//! immediate call
|
|
||||||
m_edgeGhostWindow->hideWithMask();
|
|
||||||
emit mustBeShown();
|
|
||||||
} else {
|
|
||||||
raiseView(true);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!m_isHidden) {
|
m_timerShow.stop();
|
||||||
//! immediate call
|
updateGhostWindowState();
|
||||||
updateHiddenState();
|
|
||||||
} else {
|
|
||||||
m_timerShow.stop();
|
|
||||||
updateGhostWindowState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -861,6 +858,41 @@ void VisibilityManager::deleteEdgeGhostWindow()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VisibilityManager::createFloatingGapWindow()
|
||||||
|
{
|
||||||
|
if (!m_floatingGapWindow) {
|
||||||
|
m_floatingGapWindow = new FloatingGapWindow(m_latteView);
|
||||||
|
|
||||||
|
connect(m_floatingGapWindow, &FloatingGapWindow::asyncContainsMouseChanged, this, [ = ](bool contains) {
|
||||||
|
if (contains) {
|
||||||
|
if (m_latteView->isFloatingWindow() && !m_isHidden) {
|
||||||
|
//! immediate call after contains mouse checks for mouse in sensitive floating areas
|
||||||
|
updateHiddenState();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_latteView->isFloatingWindow() && !m_isHidden) {
|
||||||
|
//! immediate call after contains mouse checks for mouse in sensitive floating areas
|
||||||
|
emit mustBeHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VisibilityManager::deleteFloatingGapWindow()
|
||||||
|
{
|
||||||
|
if (m_floatingGapWindow) {
|
||||||
|
m_floatingGapWindow->deleteLater();
|
||||||
|
m_floatingGapWindow = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VisibilityManager::supportsFloatingGap() const
|
||||||
|
{
|
||||||
|
return (m_floatingGapWindow != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//! END: VisibilityManager implementation
|
//! END: VisibilityManager implementation
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ namespace Latte {
|
|||||||
class Corona;
|
class Corona;
|
||||||
class View;
|
class View;
|
||||||
namespace ViewPart {
|
namespace ViewPart {
|
||||||
|
class FloatingGapWindow;
|
||||||
class ScreenEdgeGhostWindow;
|
class ScreenEdgeGhostWindow;
|
||||||
}
|
}
|
||||||
namespace WindowSystem {
|
namespace WindowSystem {
|
||||||
@ -154,9 +155,18 @@ private:
|
|||||||
void deleteEdgeGhostWindow();
|
void deleteEdgeGhostWindow();
|
||||||
void updateGhostWindowState();
|
void updateGhostWindowState();
|
||||||
|
|
||||||
|
//! Floating Gap Support functions
|
||||||
|
void createFloatingGapWindow();
|
||||||
|
void deleteFloatingGapWindow();
|
||||||
|
bool supportsFloatingGap() const;
|
||||||
|
|
||||||
void updateStrutsBasedOnLayoutsAndActivities(bool forceUpdate = false);
|
void updateStrutsBasedOnLayoutsAndActivities(bool forceUpdate = false);
|
||||||
void viewEventManager(QEvent *ev);
|
void viewEventManager(QEvent *ev);
|
||||||
|
|
||||||
|
void checkMouseInFloatingArea();
|
||||||
|
|
||||||
|
bool windowContainsMouse();
|
||||||
|
|
||||||
QRect acceptableStruts();
|
QRect acceptableStruts();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@ -191,6 +201,9 @@ private:
|
|||||||
std::array<QMetaObject::Connection, 1> m_connectionsKWinEdges;
|
std::array<QMetaObject::Connection, 1> m_connectionsKWinEdges;
|
||||||
ScreenEdgeGhostWindow *m_edgeGhostWindow{nullptr};
|
ScreenEdgeGhostWindow *m_edgeGhostWindow{nullptr};
|
||||||
|
|
||||||
|
//! Floating Gap
|
||||||
|
FloatingGapWindow *m_floatingGapWindow{nullptr};
|
||||||
|
|
||||||
Latte::Corona *m_corona{nullptr};
|
Latte::Corona *m_corona{nullptr};
|
||||||
Latte::View *m_latteView{nullptr};
|
Latte::View *m_latteView{nullptr};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user