mirror of
https://gitgud.io/wackyideas/aerothemeplasma.git
synced 2024-08-15 00:43:43 +00:00
363 lines
10 KiB
C++
363 lines
10 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "breezesplitterproxy.h"
|
|
|
|
#include "breezestyleconfigdata.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QDebug>
|
|
#include <QPainter>
|
|
|
|
// Q_FALLTHROUGH() for Qt < 5.8
|
|
#ifndef Q_FALLTHROUGH
|
|
#if defined(__has_cpp_attribute)
|
|
#if __has_cpp_attribute(fallthrough)
|
|
#define Q_FALLTHROUGH() [[fallthrough]]
|
|
#elif __has_cpp_attribute(clang::fallthrough)
|
|
#define Q_FALLTHROUGH() [[clang::fallthrough]]
|
|
#elif __has_cpp_attribute(gnu::fallthrough)
|
|
#define Q_FALLTHROUGH() [[gnu::fallthrough]]
|
|
#endif
|
|
#endif
|
|
#ifndef Q_FALLTHROUGH
|
|
#if __GNUC__ >= 7
|
|
#define Q_FALLTHROUGH() __attribute__((fallthrough))
|
|
#elif (__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 5)
|
|
#define Q_FALLTHROUGH() [[clang::fallthrough]]
|
|
#else
|
|
#define Q_FALLTHROUGH()
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
namespace Breeze
|
|
{
|
|
//____________________________________________________________________
|
|
void SplitterFactory::setEnabled(bool value)
|
|
{
|
|
if (_enabled != value) {
|
|
// store
|
|
_enabled = value;
|
|
|
|
// assign to existing splitters
|
|
for (WidgetMap::iterator iter = _widgets.begin(); iter != _widgets.end(); ++iter) {
|
|
if (iter.value()) {
|
|
iter.value().data()->setEnabled(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
bool SplitterFactory::registerWidget(QWidget *widget)
|
|
{
|
|
// check widget type
|
|
if (qobject_cast<QMainWindow *>(widget)) {
|
|
WidgetMap::iterator iter(_widgets.find(widget));
|
|
if (iter == _widgets.end() || !iter.value()) {
|
|
widget->installEventFilter(&_addEventFilter);
|
|
SplitterProxy *proxy(new SplitterProxy(widget, _enabled));
|
|
widget->removeEventFilter(&_addEventFilter);
|
|
|
|
widget->installEventFilter(proxy);
|
|
_widgets.insert(widget, proxy);
|
|
|
|
} else {
|
|
widget->removeEventFilter(iter.value().data());
|
|
widget->installEventFilter(iter.value().data());
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (qobject_cast<QSplitterHandle *>(widget)) {
|
|
QWidget *window(widget->window());
|
|
WidgetMap::iterator iter(_widgets.find(window));
|
|
if (iter == _widgets.end() || !iter.value()) {
|
|
window->installEventFilter(&_addEventFilter);
|
|
SplitterProxy *proxy(new SplitterProxy(window, _enabled));
|
|
window->removeEventFilter(&_addEventFilter);
|
|
|
|
widget->installEventFilter(proxy);
|
|
_widgets.insert(window, proxy);
|
|
|
|
} else {
|
|
widget->removeEventFilter(iter.value().data());
|
|
widget->installEventFilter(iter.value().data());
|
|
}
|
|
|
|
return true;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
void SplitterFactory::unregisterWidget(QWidget *widget)
|
|
{
|
|
WidgetMap::iterator iter(_widgets.find(widget));
|
|
if (iter != _widgets.end()) {
|
|
if (iter.value()) {
|
|
iter.value().data()->deleteLater();
|
|
}
|
|
_widgets.erase(iter);
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
SplitterProxy::SplitterProxy(QWidget *parent, bool enabled)
|
|
: QWidget(parent)
|
|
, _enabled(enabled)
|
|
, _timerId(0)
|
|
{
|
|
setAttribute(Qt::WA_TranslucentBackground, true);
|
|
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
|
hide();
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
void SplitterProxy::setEnabled(bool value)
|
|
{
|
|
// make sure status has changed
|
|
if (_enabled != value) {
|
|
_enabled = value;
|
|
if (!_enabled) {
|
|
clearSplitter();
|
|
}
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
bool SplitterProxy::eventFilter(QObject *object, QEvent *event)
|
|
{
|
|
// do nothing if disabled
|
|
if (!_enabled) {
|
|
return false;
|
|
}
|
|
|
|
// do nothing in case of mouse grab
|
|
if (mouseGrabber()) {
|
|
return false;
|
|
}
|
|
|
|
switch (event->type()) {
|
|
case QEvent::HoverEnter:
|
|
if (!isVisible()) {
|
|
// cast to splitter handle
|
|
if (QSplitterHandle *handle = qobject_cast<QSplitterHandle *>(object)) {
|
|
setSplitter(handle);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
case QEvent::HoverMove:
|
|
case QEvent::HoverLeave:
|
|
return isVisible() && object == _splitter.data();
|
|
|
|
case QEvent::MouseMove:
|
|
case QEvent::Timer:
|
|
case QEvent::Move:
|
|
return false;
|
|
|
|
case QEvent::CursorChange:
|
|
if (QWidget *window = qobject_cast<QMainWindow *>(object)) {
|
|
if (window->cursor().shape() == Qt::SplitHCursor || window->cursor().shape() == Qt::SplitVCursor) {
|
|
setSplitter(window);
|
|
}
|
|
}
|
|
return false;
|
|
|
|
case QEvent::WindowDeactivate:
|
|
case QEvent::MouseButtonRelease:
|
|
clearSplitter();
|
|
return false;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
bool SplitterProxy::event(QEvent *event)
|
|
{
|
|
switch (event->type()) {
|
|
#if 0
|
|
case QEvent::Paint:
|
|
{
|
|
QPainter painter( this );
|
|
painter.setClipRegion( static_cast<QPaintEvent*>( event )->region() );
|
|
painter.setRenderHints( QPainter::Antialiasing );
|
|
painter.setPen( Qt::red );
|
|
painter.drawRect( QRectF( rect() ).adjusted( 0.5, 0.5, -0.5, -0.5 ) );
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
case QEvent::MouseMove:
|
|
case QEvent::MouseButtonPress:
|
|
case QEvent::MouseButtonRelease: {
|
|
// check splitter
|
|
if (!_splitter) {
|
|
return false;
|
|
}
|
|
|
|
event->accept();
|
|
|
|
// grab on mouse press
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
grabMouse();
|
|
resize(1, 1);
|
|
}
|
|
|
|
// cast to mouse event
|
|
QMouseEvent *mouseEvent(static_cast<QMouseEvent *>(event));
|
|
|
|
// get relevant position to post mouse drag event to application
|
|
if (event->type() == QEvent::MouseButtonPress) {
|
|
// use hook, to make sure splitter is properly dragged
|
|
QMouseEvent copy(mouseEvent->type(),
|
|
_hook,
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
|
mouseEvent->globalPosition().toPoint(),
|
|
#endif
|
|
mouseEvent->button(),
|
|
mouseEvent->buttons(),
|
|
mouseEvent->modifiers());
|
|
|
|
QCoreApplication::sendEvent(_splitter.data(), ©);
|
|
|
|
} else {
|
|
// map event position to current splitter and post.
|
|
QMouseEvent copy(mouseEvent->type(),
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
_splitter.data()->mapFromGlobal(mouseEvent->globalPosition().toPoint()),
|
|
#else
|
|
_splitter.data()->mapFromGlobal(mouseEvent->globalPos()),
|
|
#endif
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
|
mouseEvent->globalPosition().toPoint(),
|
|
#endif
|
|
mouseEvent->button(),
|
|
mouseEvent->buttons(),
|
|
mouseEvent->modifiers());
|
|
|
|
QCoreApplication::sendEvent(_splitter.data(), ©);
|
|
}
|
|
|
|
// release grab on mouse-Release
|
|
if (event->type() == QEvent::MouseButtonRelease && mouseGrabber() == this) {
|
|
releaseMouse();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
case QEvent::Timer:
|
|
if (static_cast<QTimerEvent *>(event)->timerId() != _timerId) {
|
|
return QWidget::event(event);
|
|
}
|
|
|
|
/*
|
|
Fall through is intended.
|
|
We somehow lost a QEvent::Leave before timeout. We fix it from here
|
|
*/
|
|
|
|
Q_FALLTHROUGH();
|
|
|
|
case QEvent::HoverLeave:
|
|
case QEvent::Leave: {
|
|
if (mouseGrabber() == this) {
|
|
return true;
|
|
}
|
|
|
|
// reset splitter
|
|
if (isVisible() && !rect().contains(mapFromGlobal(QCursor::pos()))) {
|
|
clearSplitter();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
return QWidget::event(event);
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
void SplitterProxy::setSplitter(QWidget *widget)
|
|
{
|
|
// check if changed
|
|
if (_splitter.data() == widget) {
|
|
return;
|
|
}
|
|
|
|
// get cursor position
|
|
const QPoint position(QCursor::pos());
|
|
|
|
// store splitter and hook
|
|
_splitter = widget;
|
|
_hook = _splitter.data()->mapFromGlobal(position);
|
|
|
|
// adjust rect
|
|
QRect rect(0, 0, 2 * StyleConfigData::splitterProxyWidth(), 2 * StyleConfigData::splitterProxyWidth());
|
|
rect.moveCenter(parentWidget()->mapFromGlobal(position));
|
|
setGeometry(rect);
|
|
setCursor(_splitter.data()->cursor().shape());
|
|
|
|
// show
|
|
raise();
|
|
show();
|
|
|
|
// timer used to automatically hide proxy in case leave events are lost
|
|
if (!_timerId) {
|
|
_timerId = startTimer(150);
|
|
}
|
|
}
|
|
|
|
//____________________________________________________________________
|
|
void SplitterProxy::clearSplitter()
|
|
{
|
|
// check if changed
|
|
if (!_splitter) {
|
|
return;
|
|
}
|
|
|
|
// release mouse
|
|
if (mouseGrabber() == this) {
|
|
releaseMouse();
|
|
}
|
|
|
|
// send hover event
|
|
if (_splitter) {
|
|
// SplitterProxy intercepts HoverLeave/HoverMove events to _splitter,
|
|
// but this is meant to reach it directly. Unset _splitter to stop interception.
|
|
auto splitter = _splitter;
|
|
_splitter.clear();
|
|
QHoverEvent hoverEvent(qobject_cast<QSplitterHandle *>(splitter.data()) ? QEvent::HoverLeave : QEvent::HoverMove,
|
|
splitter.data()->mapFromGlobal(QCursor::pos()),
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
|
splitter.data()->mapFromGlobal(QCursor::pos()),
|
|
#endif
|
|
_hook);
|
|
QCoreApplication::sendEvent(splitter.data(), &hoverEvent);
|
|
}
|
|
|
|
// kill timer if any
|
|
if (_timerId) {
|
|
killTimer(_timerId);
|
|
_timerId = 0;
|
|
}
|
|
|
|
// hide
|
|
parentWidget()->setUpdatesEnabled(false);
|
|
// Note: This sends a synthetic mouse event to the widget below (to get focus), which might be
|
|
// another SplitterHandle, therefore enabling this SplitterProxy again!
|
|
hide();
|
|
parentWidget()->setUpdatesEnabled(true);
|
|
}
|
|
|
|
}
|