diff --git a/CMakeLists.txt b/CMakeLists.txt index 4406312..e885629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,65 +1,66 @@ project(decoration-api) set(PROJECT_VERSION "5.12.80") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) find_package(ECM 0.0.11 REQUIRED NO_MODULE) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(FeatureSummary) include(GenerateExportHeader) include(ECMPackageConfigHelpers) include(ECMSetupVersion) include(ECMGenerateHeaders) ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX KDECORATION2 VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kdecoration2_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KDecoration2ConfigVersion.cmake" SOVERSION 5) #dependencies set(REQUIRED_QT_VERSION 5.4.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED COMPONENTS Core Gui Test ) include(KDEInstallDirs) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) # require at least gcc 4.8 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if ("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "4.8") message(SEND_ERROR "Version ${CMAKE_CXX_COMPILER_VERSION} of the ${CMAKE_CXX_COMPILER_ID} C++ compiler is not supported. Please use version 4.8 or later.") endif() endif() set(KDECORATION2_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/KDecoration2") +find_package(KF5I18n CONFIG REQUIRED) # Subdirectories add_subdirectory(src) add_subdirectory(autotests) # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KDecoration2") ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KDecoration2Config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KDecoration2Config.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KDecoration2Config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KDecoration2ConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KDecoration2Targets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KDecoration2Targets.cmake NAMESPACE KDecoration2:: ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kdecoration2_version.h DESTINATION ${KF5_INCLUDE_INSTALL_DIR} COMPONENT Devel ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/mockclient.cpp b/autotests/mockclient.cpp index f855b3d..4300a24 100644 --- a/autotests/mockclient.cpp +++ b/autotests/mockclient.cpp @@ -1,290 +1,300 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "mockclient.h" #include #include MockClient::MockClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration) : QObject() , ApplicationMenuEnabledDecoratedClientPrivate(client, decoration) { } Qt::Edges MockClient::adjacentScreenEdges() const { return Qt::Edges(); } QString MockClient::caption() const { return QString(); } WId MockClient::decorationId() const { return 0; } int MockClient::desktop() const { return 1; } int MockClient::height() const { return m_height; } QIcon MockClient::icon() const { return QIcon(); } bool MockClient::isActive() const { return false; } bool MockClient::isCloseable() const { return m_closeable; } bool MockClient::isKeepAbove() const { return m_keepAbove; } bool MockClient::isKeepBelow() const { return m_keepBelow; } bool MockClient::isMaximizeable() const { return m_maximizable; } bool MockClient::isMaximized() const { return isMaximizedHorizontally() && isMaximizedVertically(); } bool MockClient::isMaximizedHorizontally() const { return m_maximizedHorizontally; } bool MockClient::isMaximizedVertically() const { return m_maximizedVertically; } bool MockClient::isMinimizeable() const { return m_minimizable; } bool MockClient::isModal() const { return false; } bool MockClient::isMoveable() const { return false; } bool MockClient::isOnAllDesktops() const { return m_onAllDesktops; } bool MockClient::isResizeable() const { return false; } bool MockClient::isShadeable() const { return m_shadeable; } bool MockClient::isShaded() const { return m_shaded; } QPalette MockClient::palette() const { return QPalette(); } bool MockClient::hasApplicationMenu() const { return true; } bool MockClient::isApplicationMenuActive() const { return false; } bool MockClient::providesContextHelp() const { return m_contextHelp; } void MockClient::requestClose() { emit closeRequested(); } void MockClient::requestContextHelp() { emit quickHelpRequested(); } void MockClient::requestToggleMaximization(Qt::MouseButtons buttons) { bool maximizedHorizontally = m_maximizedHorizontally; bool maximizedVertically = m_maximizedVertically; if (buttons.testFlag(Qt::LeftButton)) { maximizedHorizontally = !m_maximizedHorizontally; maximizedVertically = !m_maximizedVertically; } if (buttons.testFlag(Qt::MiddleButton)) { maximizedHorizontally = !m_maximizedHorizontally; } if (buttons.testFlag(Qt::RightButton)) { maximizedVertically = !m_maximizedVertically; } const bool wasMaximized = isMaximized(); if (m_maximizedHorizontally != maximizedHorizontally) { m_maximizedHorizontally = maximizedHorizontally; emit client()->maximizedHorizontallyChanged(m_maximizedHorizontally); } if (m_maximizedVertically != maximizedVertically) { m_maximizedVertically = maximizedVertically; emit client()->maximizedVerticallyChanged(m_maximizedVertically); } if (wasMaximized != isMaximized()) { emit client()->maximizedChanged(isMaximized()); } } void MockClient::requestMinimize() { emit minimizeRequested(); } void MockClient::requestShowWindowMenu() { emit menuRequested(); } void MockClient::requestShowApplicationMenu(const QRect &rect, int actionId) { Q_UNUSED(rect); Q_UNUSED(actionId); emit applicationMenuRequested(); // FIXME TODO pass geometry } void MockClient::requestToggleKeepAbove() { m_keepAbove = !m_keepAbove; emit client()->keepAboveChanged(m_keepAbove); } void MockClient::requestToggleKeepBelow() { m_keepBelow = !m_keepBelow; emit client()->keepBelowChanged(m_keepBelow); } void MockClient::requestToggleOnAllDesktops() { m_onAllDesktops = !m_onAllDesktops; emit client()->onAllDesktopsChanged(m_onAllDesktops); } void MockClient::requestToggleShade() { m_shaded = !m_shaded; emit client()->shadedChanged(m_shaded); } +void MockClient::requestShowToolTip(const QString &text) +{ + Q_UNUSED(text); +} + +void MockClient::requestHideToolTip() +{ + +} + int MockClient::width() const { return m_width; } WId MockClient::windowId() const { return 0; } void MockClient::setCloseable(bool set) { m_closeable = set; emit client()->closeableChanged(set); } void MockClient::setMinimizable(bool set) { m_minimizable = set; emit client()->minimizeableChanged(set); } void MockClient::setProvidesContextHelp(bool set) { m_contextHelp = set; emit client()->providesContextHelpChanged(set); } void MockClient::setShadeable(bool set) { m_shadeable = set; emit client()->shadeableChanged(set); } void MockClient::setMaximizable(bool set) { m_maximizable = set; emit client()->maximizeableChanged(set); } void MockClient::setWidth(int w) { m_width = w; emit client()->widthChanged(w); } void MockClient::setHeight(int h) { m_height = h; emit client()->heightChanged(h); } void MockClient::showApplicationMenu(int actionId) { Q_UNUSED(actionId) } diff --git a/autotests/mockclient.h b/autotests/mockclient.h index 8755589..c9fc5b0 100644 --- a/autotests/mockclient.h +++ b/autotests/mockclient.h @@ -1,105 +1,107 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef MOCK_CLIENT_H #define MOCK_CLIENT_H #include "../src/private/decoratedclientprivate.h" #include class MockClient : public QObject, public KDecoration2::ApplicationMenuEnabledDecoratedClientPrivate { Q_OBJECT public: explicit MockClient(KDecoration2::DecoratedClient *client, KDecoration2::Decoration *decoration); Qt::Edges adjacentScreenEdges() const override; QString caption() const override; WId decorationId() const override; int desktop() const override; int height() const override; QIcon icon() const override; bool isActive() const override; bool isCloseable() const override; bool isKeepAbove() const override; bool isKeepBelow() const override; bool isMaximizeable() const override; bool isMaximized() const override; bool isMaximizedHorizontally() const override; bool isMaximizedVertically() const override; bool isMinimizeable() const override; bool isModal() const override; bool isMoveable() const override; bool isOnAllDesktops() const override; bool isResizeable() const override; bool isShadeable() const override; bool isShaded() const override; QPalette palette() const override; bool hasApplicationMenu() const override; bool isApplicationMenuActive() const override; bool providesContextHelp() const override; void requestClose() override; void requestContextHelp() override; void requestToggleMaximization(Qt::MouseButtons buttons) override; void requestMinimize() override; void requestShowWindowMenu() override; void requestShowApplicationMenu(const QRect &rect, int actionId) override; void requestToggleKeepAbove() override; void requestToggleKeepBelow() override; void requestToggleOnAllDesktops() override; void requestToggleShade() override; + void requestShowToolTip(const QString &text) override; + void requestHideToolTip() override; int width() const override; WId windowId() const override; void showApplicationMenu(int actionId) override; void setCloseable(bool set); void setMinimizable(bool set); void setProvidesContextHelp(bool set); void setShadeable(bool set); void setMaximizable(bool set); void setWidth(int w); void setHeight(int h); Q_SIGNALS: void closeRequested(); void minimizeRequested(); void quickHelpRequested(); void menuRequested(); void applicationMenuRequested(); private: bool m_closeable = false; bool m_minimizable = false; bool m_contextHelp = false; bool m_keepAbove = false; bool m_keepBelow = false; bool m_shadeable = false; bool m_shaded = false; bool m_maximizable = false; bool m_maximizedVertically = false; bool m_maximizedHorizontally = false; bool m_onAllDesktops = false; int m_width = 0; int m_height = 0; }; #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 309a784..e209eb1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,58 +1,59 @@ add_subdirectory(private) set(libkdecoration2_SRCS decoratedclient.cpp decoration.cpp decorationbutton.cpp decorationbuttongroup.cpp decorationsettings.cpp decorationshadow.cpp ) add_library(kdecorations2 SHARED ${libkdecoration2_SRCS}) generate_export_header(kdecorations2 EXPORT_FILE_NAME kdecoration2/kdecoration2_export.h) add_library(KDecoration2::KDecoration ALIAS kdecorations2) target_link_libraries(kdecorations2 PUBLIC Qt5::Core Qt5::Gui PRIVATE kdecorations2private + KF5::I18n ) target_include_directories(kdecorations2 INTERFACE "$" ) set_target_properties(kdecorations2 PROPERTIES VERSION ${KDECORATION2_VERSION_STRING} SOVERSION ${KDECORATION2_SOVERSION} EXPORT_NAME KDecoration ) ecm_generate_headers(KDecoration2_CamelCase_HEADERS HEADER_NAMES DecoratedClient Decoration DecorationButton DecorationButtonGroup DecorationSettings DecorationShadow PREFIX KDecoration2 REQUIRED_HEADERS KDecoration2_HEADERS ) install(FILES ${KDecoration2_CamelCase_HEADERS} DESTINATION ${KDECORATION2_INCLUDEDIR}/KDecoration2 COMPONENT Devel) install(TARGETS kdecorations2 EXPORT KDecoration2Targets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kdecoration2/kdecoration2_export.h ${KDecoration2_HEADERS} decorationdefines.h DESTINATION ${KDECORATION2_INCLUDEDIR}/kdecoration2 COMPONENT Devel ) diff --git a/src/decoration.cpp b/src/decoration.cpp index 3e24f36..25bf3fc 100644 --- a/src/decoration.cpp +++ b/src/decoration.cpp @@ -1,403 +1,413 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "decoration.h" #include "decoration_p.h" #include "decoratedclient.h" #include "private/decoratedclientprivate.h" #include "private/decorationbridge.h" #include "decorationbutton.h" #include "decorationsettings.h" #include "decorationshadow.h" #include #include namespace KDecoration2 { namespace { DecorationBridge *findBridge(const QVariantList &args) { for (const auto &arg: args) { if (auto bridge = arg.toMap().value(QStringLiteral("bridge")).value()) { return bridge; } } Q_UNREACHABLE(); } } Decoration::Private::Private(Decoration *deco, const QVariantList &args) : sectionUnderMouse(Qt::NoSection) , bridge(findBridge(args)) , client(QSharedPointer(new DecoratedClient(deco, bridge))) , opaque(false) , q(deco) { Q_UNUSED(args) } void Decoration::Private::setSectionUnderMouse(Qt::WindowFrameSection section) { if (sectionUnderMouse == section) { return; } sectionUnderMouse = section; emit q->sectionUnderMouseChanged(sectionUnderMouse); } void Decoration::Private::updateSectionUnderMouse(const QPoint &mousePosition) { if (titleBar.contains(mousePosition)) { setSectionUnderMouse(Qt::TitleBarArea); return; } const QSize size = q->size(); const int corner = 2*settings->largeSpacing(); const bool left = mousePosition.x() < borders.left(); const bool top = mousePosition.y() < borders.top(); const bool bottom = size.height() - mousePosition.y() <= borders.bottom(); const bool right = size.width() - mousePosition.x() <= borders.right(); if (left) { if (top && mousePosition.y() < titleBar.top() + corner) { setSectionUnderMouse(Qt::TopLeftSection); } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) { setSectionUnderMouse(Qt::BottomLeftSection); } else { setSectionUnderMouse(Qt::LeftSection); } return; } if (right) { if (top && mousePosition.y() < titleBar.top() + corner) { setSectionUnderMouse(Qt::TopRightSection); } else if (size.height() - mousePosition.y() <= borders.bottom() + corner && mousePosition.y() > titleBar.bottom()) { setSectionUnderMouse(Qt::BottomRightSection); } else { setSectionUnderMouse(Qt::RightSection); } return; } if (bottom) { if (mousePosition.y() > titleBar.bottom()) { if (mousePosition.x() < borders.left() + corner) { setSectionUnderMouse(Qt::BottomLeftSection); } else if (size.width() - mousePosition.x() <= borders.right() + corner) { setSectionUnderMouse(Qt::BottomRightSection); } else { setSectionUnderMouse(Qt::BottomSection); } } else { setSectionUnderMouse(Qt::TitleBarArea); } return; } if (top) { if (mousePosition.y() < titleBar.top()) { if (mousePosition.x() < borders.left() + corner) { setSectionUnderMouse(Qt::TopLeftSection); } else if (size.width() - mousePosition.x() <= borders.right() + corner) { setSectionUnderMouse(Qt::TopRightSection); } else { setSectionUnderMouse(Qt::TopSection); } } else { setSectionUnderMouse(Qt::TitleBarArea); } return; } setSectionUnderMouse(Qt::NoSection); } void Decoration::Private::addButton(DecorationButton *button) { Q_ASSERT(!buttons.contains(button)); buttons << button; QObject::connect(button, &QObject::destroyed, q, [this](QObject *o) { auto it = buttons.begin(); while (it != buttons.end()) { if (*it == static_cast(o)) { it = buttons.erase(it); } else { it++; } } } ); } Decoration::Decoration(QObject *parent, const QVariantList &args) : QObject(parent) , d(new Private(this, args)) { connect(this, &Decoration::bordersChanged, this, [this]{ update(); }); } Decoration::~Decoration() = default; void Decoration::init() { Q_ASSERT(!d->settings.isNull()); } QWeakPointer Decoration::client() const { return d->client.toWeakRef(); } #define DELEGATE(name) \ void Decoration::name() \ { \ d->client->d->name(); \ } DELEGATE(requestClose) DELEGATE(requestContextHelp) DELEGATE(requestMinimize) DELEGATE(requestToggleOnAllDesktops) DELEGATE(requestToggleShade) DELEGATE(requestToggleKeepAbove) DELEGATE(requestToggleKeepBelow) DELEGATE(requestShowWindowMenu) #undef DELEGATE +void Decoration::requestShowToolTip(const QString &text) +{ + d->client->d->requestShowToolTip(text); +} + +void Decoration::requestHideToolTip() +{ + d->client->d->requestHideToolTip(); +} + void Decoration::requestToggleMaximization(Qt::MouseButtons buttons) { d->client->d->requestToggleMaximization(buttons); } void Decoration::showApplicationMenu(int actionId) { auto it = std::find_if(d->buttons.constBegin(), d->buttons.constEnd(), [](DecorationButton *button) { return button->type() == DecorationButtonType::ApplicationMenu; }); if (it != d->buttons.constEnd()) { requestShowApplicationMenu((*it)->geometry().toRect(), actionId); } } void Decoration::requestShowApplicationMenu(const QRect &rect, int actionId) { if (auto *appMenuEnabledPrivate = dynamic_cast(d->client->d.get())) { appMenuEnabledPrivate->requestShowApplicationMenu(rect, actionId); } } #define DELEGATE(name, variableName, type, emitValue) \ void Decoration::name(type a) \ { \ if (d->variableName == a) { \ return; \ } \ d->variableName = a; \ emit variableName##Changed(emitValue); \ } DELEGATE(setBorders, borders, const QMargins&, ) DELEGATE(setResizeOnlyBorders, resizeOnlyBorders, const QMargins&, ) DELEGATE(setTitleBar, titleBar, const QRect&, ) DELEGATE(setOpaque, opaque, bool, d->opaque) DELEGATE(setShadow, shadow, const QSharedPointer &, d->shadow) #undef DELEGATE #define DELEGATE(name, type) \ type Decoration::name() const \ { \ return d->name; \ } DELEGATE(borders, QMargins) DELEGATE(resizeOnlyBorders, QMargins) DELEGATE(titleBar, QRect) DELEGATE(sectionUnderMouse, Qt::WindowFrameSection) DELEGATE(shadow, QSharedPointer) #undef DELEGATE bool Decoration::isOpaque() const { return d->opaque; } #define BORDER(name, Name) \ int Decoration::border##Name() const \ { \ return d->borders.name(); \ } \ int Decoration::resizeOnlyBorder##Name() const \ { \ return d->resizeOnlyBorders.name(); \ } BORDER(left, Left) BORDER(right, Right) BORDER(top, Top) BORDER(bottom, Bottom) #undef BORDER QSize Decoration::size() const { const QMargins &b = d->borders; return QSize(d->client->width() + b.left() + b.right(), (d->client->isShaded() ? 0 : d->client->height()) + b.top() + b.bottom()); } QRect Decoration::rect() const { return QRect(QPoint(0, 0), size()); } bool Decoration::event(QEvent *event) { switch (event->type()) { case QEvent::HoverEnter: hoverEnterEvent(static_cast(event)); return true; case QEvent::HoverLeave: hoverLeaveEvent(static_cast(event)); return true; case QEvent::HoverMove: hoverMoveEvent(static_cast(event)); return true; case QEvent::MouseButtonPress: mousePressEvent(static_cast(event)); return true; case QEvent::MouseButtonRelease: mouseReleaseEvent(static_cast(event)); return true; case QEvent::MouseMove: mouseMoveEvent(static_cast(event)); return true; case QEvent::Wheel: wheelEvent(static_cast(event)); return true; default: return QObject::event(event); } } void Decoration::hoverEnterEvent(QHoverEvent *event) { for (DecorationButton *button : d->buttons) { QCoreApplication::instance()->sendEvent(button, event); } d->updateSectionUnderMouse(event->pos()); } void Decoration::hoverLeaveEvent(QHoverEvent *event) { for (DecorationButton *button : d->buttons) { QCoreApplication::instance()->sendEvent(button, event); } d->setSectionUnderMouse(Qt::NoSection); } void Decoration::hoverMoveEvent(QHoverEvent *event) { for (DecorationButton *button : d->buttons) { if (!button->isEnabled() || !button->isVisible()) { continue; } const bool hovered = button->isHovered(); const bool contains = button->geometry().contains(event->pos()); if (!hovered && contains) { QHoverEvent e(QEvent::HoverEnter, event->posF(), event->oldPosF(), event->modifiers()); QCoreApplication::instance()->sendEvent(button, &e); } else if (hovered && !contains) { QHoverEvent e(QEvent::HoverLeave, event->posF(), event->oldPosF(), event->modifiers()); QCoreApplication::instance()->sendEvent(button, &e); } else if (hovered && contains) { QCoreApplication::instance()->sendEvent(button, event); } } d->updateSectionUnderMouse(event->pos()); } void Decoration::mouseMoveEvent(QMouseEvent *event) { for (DecorationButton *button : d->buttons) { if (button->isPressed()) { QCoreApplication::instance()->sendEvent(button, event); return; } } // not handled, take care ourselves } void Decoration::mousePressEvent(QMouseEvent *event) { for (DecorationButton *button : d->buttons) { if (button->isHovered()) { if (button->acceptedButtons().testFlag(event->button())) { QCoreApplication::instance()->sendEvent(button, event); } event->setAccepted(true); return; } } } void Decoration::mouseReleaseEvent(QMouseEvent *event) { for (DecorationButton *button : d->buttons) { if (button->isPressed() && button->acceptedButtons().testFlag(event->button())) { QCoreApplication::instance()->sendEvent(button, event); return; } } // not handled, take care ourselves d->updateSectionUnderMouse(event->pos()); } void Decoration::wheelEvent(QWheelEvent *event) { for (DecorationButton *button : d->buttons) { if (button->geometry().contains(event->posF())) { QCoreApplication::instance()->sendEvent(button, event); event->setAccepted(true); } } } void Decoration::update(const QRect &r) { d->bridge->update(this, r.isNull() ? rect() : r); } void Decoration::update() { update(QRect()); } void Decoration::setSettings(const QSharedPointer< DecorationSettings > &settings) { d->settings = settings; } QSharedPointer< DecorationSettings > Decoration::settings() const { return d->settings; } } // namespace diff --git a/src/decoration.h b/src/decoration.h index 95b0b7c..14d31d3 100644 --- a/src/decoration.h +++ b/src/decoration.h @@ -1,244 +1,246 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KDECORATION2_DECORATION_H #define KDECORATION2_DECORATION_H #include #include "decorationshadow.h" #include #include #include #include class QHoverEvent; class QMouseEvent; class QPainter; class QWheelEvent; /** * @brief Framework for creating window decorations. * **/ namespace KDecoration2 { class DecorationPrivate; class DecoratedClient; class DecorationButton; class DecorationSettings; /** * @brief Base class for the Decoration. * * To provide a Decoration one needs to inherit from this class. The framework will instantiate * an instance of the inherited class for each DecoratedClient. * * The main tasks of the Decoration is to provide borders around the DecoratedClient. For this * the Deocration provides border sizes: those should be adjusted depending on the state of the * DecoratedClient. E.g. commonly a maximized DecoratedClient does not have borders on the side, * only the title bar. * * Whenever the visual representation of the Decoration changes the slot @link Decoration::update @endlink * should be invoked to request a repaint. The framework will in return invoke the * @link Decoration::paint @endlink method. This method needs to be implemented by inheriting * classes. * * A Decoration commonly provides buttons for interaction. E.g. a close button to close the * DecoratedClient. For such actions the Decoration provides slots which should be connected to * the clicked signals of the buttons. For convenience the framework provides the @link DecorationButton @endlink * and the @link DecorationButtonGroup @endlink for easier layout. It is not required to use those, * if one uses different ways to represent the actions one needs to filter the events accordingly. * * @see DecoratedClient * @see DecorationButton * @see DecorationButtonGroup **/ class KDECORATIONS2_EXPORT Decoration : public QObject { Q_OBJECT Q_PROPERTY(QMargins borders READ borders NOTIFY bordersChanged) Q_PROPERTY(int borderLeft READ borderLeft NOTIFY bordersChanged) Q_PROPERTY(int borderRight READ borderRight NOTIFY bordersChanged) Q_PROPERTY(int borderTop READ borderTop NOTIFY bordersChanged) Q_PROPERTY(int borderBottom READ borderBottom NOTIFY bordersChanged) Q_PROPERTY(QMargins resizeOnlyBorders READ resizeOnlyBorders NOTIFY resizeOnlyBordersChanged) Q_PROPERTY(int resizeOnlyBorderLeft READ resizeOnlyBorderLeft NOTIFY resizeOnlyBordersChanged) Q_PROPERTY(int resizeOnlyBorderRight READ resizeOnlyBorderRight NOTIFY resizeOnlyBordersChanged) Q_PROPERTY(int resizeOnlyBorderTop READ resizeOnlyBorderTop NOTIFY resizeOnlyBordersChanged) Q_PROPERTY(int resizeOnlyBorderBottom READ resizeOnlyBorderBottom NOTIFY resizeOnlyBordersChanged) /** * This property denotes the part of the Decoration which is currently under the mouse pointer. * It gets automatically updated whenever a QMouseEvent or QHoverEvent gets processed. **/ Q_PROPERTY(Qt::WindowFrameSection sectionUnderMouse READ sectionUnderMouse NOTIFY sectionUnderMouseChanged) /** * The titleBar is the area inside the Decoration containing all controls (e.g. Buttons) * and the caption. The titleBar is the main interaction area, while all other areas of the * Decoration are normally used as resize areas. **/ Q_PROPERTY(QRect titleBar READ titleBar NOTIFY titleBarChanged) /** * Whether the Decoration is fully opaque. By default a Decoration is considered to * use the alpha channel and this property has the value @c false. But for e.g. a maximized * DecoratedClient it is possible that the Decoration is fully opaque. In this case the * Decoration should set this property to @c true. **/ Q_PROPERTY(bool opaque READ isOpaque NOTIFY opaqueChanged) public: virtual ~Decoration(); /** * The DecoratedClient for this Decoration. * As long as the Decoration is alive it is guaranteed that the object is not * deleted. Thus it is save to access using QWeakPointer::data in e.g. a sublcass * of Decoration without promoting to QSharedPointer. **/ QWeakPointer client() const; QMargins borders() const; int borderLeft() const; int borderRight() const; int borderTop() const; int borderBottom() const; QMargins resizeOnlyBorders() const; int resizeOnlyBorderLeft() const; int resizeOnlyBorderRight() const; int resizeOnlyBorderTop() const; int resizeOnlyBorderBottom() const; Qt::WindowFrameSection sectionUnderMouse() const; QRect titleBar() const; bool isOpaque() const; /** * DecorationShadow for this Decoration. It is recommended that multiple Decorations share * the same DecorationShadow. E.g one DecorationShadow for all inactive Decorations and one * for the active Decoration. **/ QSharedPointer shadow() const; /** * The decoration's geometry in local coordinates. * * Basically the size of the DecoratedClient combined with the borders. **/ QRect rect() const; QSize size() const; /** * Invoked by the framework to set the Settings for this Decoration before * init is invoked. * @internal **/ void setSettings(const QSharedPointer &settings); /** * @returns The DecorationSettings used for this Decoration. **/ QSharedPointer settings() const; /** * Implement this method in inheriting classes to provide the rendering. * * The @p painter is set up to paint on an internal QPaintDevice. The painting is * implicitly double buffered. * * @param painter The painter which needs to be used for rendering * @param repaintArea The region which needs to be repainted. **/ virtual void paint(QPainter *painter, const QRect &repaintArea) = 0; virtual bool event(QEvent *event) override; public Q_SLOTS: void requestClose(); void requestToggleMaximization(Qt::MouseButtons buttons); void requestMinimize(); void requestContextHelp(); void requestToggleOnAllDesktops(); void requestToggleShade(); void requestToggleKeepAbove(); void requestToggleKeepBelow(); void requestShowWindowMenu(); + void requestShowToolTip(const QString &text); + void requestHideToolTip(); void showApplicationMenu(int actionId); void requestShowApplicationMenu(const QRect &rect, int actionId); void update(const QRect &rect); void update(); /** * This method gets invoked from the framework once the Decoration is created and * completely setup. * * An inheriting class should override this method and perform all initialization in * this method instead of the constructor. **/ virtual void init(); Q_SIGNALS: void bordersChanged(); void resizeOnlyBordersChanged(); void sectionUnderMouseChanged(Qt::WindowFrameSection); void titleBarChanged(); void opaqueChanged(bool); void shadowChanged(const QSharedPointer &shadow); protected: /** * Constructor for the Decoration. * * The @p args are used by the decoration framework to pass meta information * to the Decoration. An inheriting class is supposed to pass the args to the * parent class. * * @param parent The parent of the Decoration * @param args Additional arguments passed in from the framework **/ explicit Decoration(QObject *parent, const QVariantList &args); void setBorders(const QMargins &borders); void setResizeOnlyBorders(const QMargins &borders); /** * An implementation has to invoke this method whenever the area * containing the controls and caption changes. * @param rect The new geometry of the titleBar in Decoration coordinates **/ void setTitleBar(const QRect &rect); void setOpaque(bool opaque); void setShadow(const QSharedPointer &shadow); virtual void hoverEnterEvent(QHoverEvent *event); virtual void hoverLeaveEvent(QHoverEvent *event); virtual void hoverMoveEvent(QHoverEvent *event); virtual void mouseMoveEvent(QMouseEvent *event); virtual void mousePressEvent(QMouseEvent *event); virtual void mouseReleaseEvent(QMouseEvent *event); virtual void wheelEvent(QWheelEvent *event); private: friend class DecorationButton; class Private; QScopedPointer d; }; } // namespace Q_DECLARE_METATYPE(KDecoration2::Decoration*) #endif diff --git a/src/decorationbutton.cpp b/src/decorationbutton.cpp index 5d3c2f9..480fcbe 100644 --- a/src/decorationbutton.cpp +++ b/src/decorationbutton.cpp @@ -1,499 +1,556 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "decorationbutton.h" #include "decorationbutton_p.h" #include "decoration.h" #include "decoration_p.h" #include "decoratedclient.h" #include "decorationsettings.h" +#include + +#include #include #include #include #include #include namespace KDecoration2 { #ifndef DOXYGEN_SHOULD_SKIP_THIS uint qHash(const DecorationButtonType &type) { return static_cast(type); } #endif DecorationButton::Private::Private(DecorationButtonType type, const QPointer &decoration, DecorationButton *parent) : decoration(decoration) , type(type) , hovered(false) , enabled(true) , checkable(false) , checked(false) , visible(true) , acceptedButtons(Qt::LeftButton) , doubleClickEnabled(false) , pressAndHold(false) , q(parent) , m_pressed(Qt::NoButton) { init(); } DecorationButton::Private::~Private() = default; void DecorationButton::Private::init() { auto c = decoration->client().data(); auto settings = decoration->settings(); switch (type) { case DecorationButtonType::Menu: QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestShowWindowMenu, Qt::QueuedConnection); QObject::connect(q, &DecorationButton::doubleClicked, decoration.data(), &Decoration::requestClose, Qt::QueuedConnection); QObject::connect(settings.data(), &DecorationSettings::closeOnDoubleClickOnMenuChanged, q, [this](bool enabled) { doubleClickEnabled = enabled; setPressAndHold(enabled); }, Qt::QueuedConnection ); doubleClickEnabled = settings->isCloseOnDoubleClickOnMenu(); setPressAndHold(settings->isCloseOnDoubleClickOnMenu()); setAcceptedButtons(Qt::LeftButton | Qt::RightButton); break; case DecorationButtonType::ApplicationMenu: setVisible(c->hasApplicationMenu()); setCheckable(true); // will be "checked" whilst the menu is opened // FIXME TODO connect directly and figure out the button geometry/offset stuff QObject::connect(q, &DecorationButton::clicked, decoration.data(), [this] { decoration->requestShowApplicationMenu(q->geometry().toRect(), 0 /* actionId */); }, Qt::QueuedConnection); //&Decoration::requestShowApplicationMenu, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::hasApplicationMenuChanged, q, &DecorationButton::setVisible); QObject::connect(c, &DecoratedClient::applicationMenuActiveChanged, q, &DecorationButton::setChecked); break; case DecorationButtonType::OnAllDesktops: setVisible(settings->isOnAllDesktopsAvailable()); setCheckable(true); setChecked(c->isOnAllDesktops()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleOnAllDesktops, Qt::QueuedConnection); QObject::connect(settings.data(), &DecorationSettings::onAllDesktopsAvailableChanged, q, &DecorationButton::setVisible); QObject::connect(c, &DecoratedClient::onAllDesktopsChanged, q, &DecorationButton::setChecked); break; case DecorationButtonType::Minimize: setEnabled(c->isMinimizeable()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestMinimize, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::minimizeableChanged, q, &DecorationButton::setEnabled); break; case DecorationButtonType::Maximize: setEnabled(c->isMaximizeable()); setCheckable(true); setChecked(c->isMaximized()); setAcceptedButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleMaximization, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::maximizeableChanged, q, &DecorationButton::setEnabled); QObject::connect(c, &DecoratedClient::maximizedChanged, q, &DecorationButton::setChecked); break; case DecorationButtonType::Close: setEnabled(c->isCloseable()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestClose, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::closeableChanged, q, &DecorationButton::setEnabled); break; case DecorationButtonType::ContextHelp: setVisible(c->providesContextHelp()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestContextHelp, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::providesContextHelpChanged, q, &DecorationButton::setVisible); break; case DecorationButtonType::KeepAbove: setCheckable(true); setChecked(c->isKeepAbove()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleKeepAbove, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::keepAboveChanged, q, &DecorationButton::setChecked); break; case DecorationButtonType::KeepBelow: setCheckable(true); setChecked(c->isKeepBelow()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleKeepBelow, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::keepBelowChanged, q, &DecorationButton::setChecked); break; case DecorationButtonType::Shade: setEnabled(c->isShadeable()); setCheckable(true); setChecked(c->isShaded()); QObject::connect(q, &DecorationButton::clicked, decoration.data(), &Decoration::requestToggleShade, Qt::QueuedConnection); QObject::connect(c, &DecoratedClient::shadedChanged, q, &DecorationButton::setChecked); QObject::connect(c, &DecoratedClient::shadeableChanged, q, &DecorationButton::setEnabled); break; default: // nothing break; } } void DecorationButton::Private::setHovered(bool set) { if (hovered == set) { return; } hovered = set; emit q->hoveredChanged(hovered); } void DecorationButton::Private::setEnabled(bool set) { if (enabled == set) { return; } enabled = set; emit q->enabledChanged(enabled); if (!enabled) { setHovered(false); if (isPressed()) { m_pressed = Qt::NoButton; emit q->pressedChanged(false); } } } void DecorationButton::Private::setVisible(bool set) { if (visible == set) { return; } visible = set; emit q->visibilityChanged(set); if (!visible) { setHovered(false); if (isPressed()) { m_pressed = Qt::NoButton; emit q->pressedChanged(false); } } } void DecorationButton::Private::setChecked(bool set) { if (!checkable || checked == set) { return; } checked = set; emit q->checkedChanged(checked); } void DecorationButton::Private::setCheckable(bool set) { if (checkable == set) { return; } if (!set) { setChecked(false); } checkable = set; emit q->checkableChanged(checkable); } void DecorationButton::Private::setPressed(Qt::MouseButton button, bool pressed) { if (pressed) { m_pressed = m_pressed | button; } else { m_pressed = m_pressed & ~button; } emit q->pressedChanged(isPressed()); } void DecorationButton::Private::setAcceptedButtons(Qt::MouseButtons buttons) { if (acceptedButtons == buttons) { return; } acceptedButtons = buttons; emit q->acceptedButtonsChanged(acceptedButtons); } void DecorationButton::Private::startDoubleClickTimer() { if (!doubleClickEnabled) { return; } if (m_doubleClickTimer.isNull()) { m_doubleClickTimer.reset(new QElapsedTimer()); } m_doubleClickTimer->start(); } void DecorationButton::Private::invalidateDoubleClickTimer() { if (m_doubleClickTimer.isNull()) { return; } m_doubleClickTimer->invalidate(); } bool DecorationButton::Private::wasDoubleClick() const { if (m_doubleClickTimer.isNull() || !m_doubleClickTimer->isValid()) { return false; } return !m_doubleClickTimer->hasExpired(QGuiApplication::styleHints()->mouseDoubleClickInterval()); } void DecorationButton::Private::setPressAndHold(bool enable) { if (pressAndHold == enable) { return; } pressAndHold = enable; if (!pressAndHold) { m_pressAndHoldTimer.reset(); } } void DecorationButton::Private::startPressAndHold() { if (!pressAndHold) { return; } if (m_pressAndHoldTimer.isNull()) { m_pressAndHoldTimer.reset(new QTimer()); m_pressAndHoldTimer->setSingleShot(true); QObject::connect(m_pressAndHoldTimer.data(), &QTimer::timeout, q, [this]() { q->clicked(Qt::LeftButton); } ); } m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval()); } void DecorationButton::Private::stopPressAndHold() { if (!m_pressAndHoldTimer.isNull()) { m_pressAndHoldTimer->stop(); } } +QString DecorationButton::Private::typeToString(DecorationButtonType type) +{ + switch (type) { + case DecorationButtonType::Menu: + return i18n("Menu"); + case DecorationButtonType::ApplicationMenu: + return i18n("Application menu"); + case DecorationButtonType::OnAllDesktops: + if ( this->q->isChecked() ) + return i18n("On one desktop"); + else + return i18n("On all desktops"); + case DecorationButtonType::Minimize: + return i18n("Minimize"); + case DecorationButtonType::Maximize: + if ( this->q->isChecked() ) + return i18n("Restore"); + else + return i18n("Maximize"); + case DecorationButtonType::Close: + return i18n("Close"); + case DecorationButtonType::ContextHelp: + return i18n("Context help"); + case DecorationButtonType::Shade: + if ( this->q->isChecked() ) + return i18n("Unshade"); + else + return i18n("Shade"); + case DecorationButtonType::KeepBelow: + if ( this->q->isChecked() ) + return i18n("Don't keep below"); + else + return i18n("Keep below"); + case DecorationButtonType::KeepAbove: + if ( this->q->isChecked() ) + return i18n("Don't keep above"); + else + return i18n("Keep above"); + default: + return QString(); + } +} + DecorationButton::DecorationButton(DecorationButtonType type, const QPointer &decoration, QObject *parent) : QObject(parent) , d(new Private(type, decoration, this)) { decoration->d->addButton(this); connect(this, &DecorationButton::geometryChanged, this, static_cast(&DecorationButton::update)); auto updateSlot = static_cast(&DecorationButton::update); connect(this, &DecorationButton::hoveredChanged, this, updateSlot); + connect(this, &DecorationButton::hoveredChanged, this, + [this](bool hovered) { + if (hovered) { + //TODO: show tooltip if hovered and hide if not + const QString type = this->d->typeToString(this->type()); + this->decoration()->requestShowToolTip(type); + } else { + this->decoration()->requestHideToolTip(); + } + } + ); connect(this, &DecorationButton::pressedChanged, this, updateSlot); connect(this, &DecorationButton::checkedChanged, this, updateSlot); connect(this, &DecorationButton::enabledChanged, this, updateSlot); connect(this, &DecorationButton::visibilityChanged, this, updateSlot); connect(this, &DecorationButton::hoveredChanged, this, [this](bool hovered) { if (hovered) { emit pointerEntered(); } else { emit pointerLeft(); } } ); connect(this, &DecorationButton::pressedChanged, this, [this](bool p) { if (p) { emit pressed(); } else { emit released(); } } ); } DecorationButton::~DecorationButton() = default; void DecorationButton::update(const QRectF &rect) { decoration()->update(rect.isNull() ? geometry().toRect() : rect.toRect()); } void DecorationButton::update() { update(QRectF()); } QSizeF DecorationButton::size() const { return d->geometry.size(); } bool DecorationButton::isPressed() const { return d->isPressed(); } #define DELEGATE(name, variableName, type) \ type DecorationButton::name() const \ { \ return d->variableName; \ } DELEGATE(isHovered, hovered, bool) DELEGATE(isEnabled, enabled, bool) DELEGATE(isChecked, checked, bool) DELEGATE(isCheckable, checkable, bool) DELEGATE(isVisible, visible, bool) #define DELEGATE2(name, type) DELEGATE(name, name, type) DELEGATE2(geometry, QRectF) DELEGATE2(decoration, QPointer) DELEGATE2(acceptedButtons, Qt::MouseButtons) DELEGATE2(type, DecorationButtonType) #undef DELEGATE2 #undef DELEGATE #define DELEGATE(name, type) \ void DecorationButton::name(type a) \ { \ d->name(a); \ } DELEGATE(setAcceptedButtons, Qt::MouseButtons) DELEGATE(setEnabled, bool) DELEGATE(setChecked, bool) DELEGATE(setCheckable, bool) DELEGATE(setVisible, bool) #undef DELEGATE #define DELEGATE(name, variableName, type) \ void DecorationButton::name(type a) \ { \ if (d->variableName == a) { \ return; \ } \ d->variableName = a; \ emit variableName##Changed(d->variableName); \ } DELEGATE(setGeometry, geometry, const QRectF &) #undef DELEGATE bool DecorationButton::event(QEvent *event) { switch (event->type()) { case QEvent::HoverEnter: hoverEnterEvent(static_cast(event)); return true; case QEvent::HoverLeave: hoverLeaveEvent(static_cast(event)); return true; case QEvent::HoverMove: hoverMoveEvent(static_cast(event)); return true; case QEvent::MouseButtonPress: mousePressEvent(static_cast(event)); return true; case QEvent::MouseButtonRelease: mouseReleaseEvent(static_cast(event)); return true; case QEvent::MouseMove: mouseMoveEvent(static_cast(event)); return true; case QEvent::Wheel: wheelEvent(static_cast(event)); return true; default: return QObject::event(event); } } void DecorationButton::hoverEnterEvent(QHoverEvent *event) { if (!d->enabled || !d->visible || !d->geometry.contains(event->posF())) { return; } d->setHovered(true); event->setAccepted(true); } void DecorationButton::hoverLeaveEvent(QHoverEvent *event) { if (!d->enabled || !d->visible || !d->hovered || d->geometry.contains(event->posF())) { return; } d->setHovered(false); event->setAccepted(true); } void DecorationButton::hoverMoveEvent(QHoverEvent *event) { Q_UNUSED(event) } void DecorationButton::mouseMoveEvent(QMouseEvent *event) { if (!d->enabled || !d->visible || !d->hovered) { return; } if (!d->geometry.contains(event->localPos())) { d->setHovered(false); event->setAccepted(true); } } void DecorationButton::mousePressEvent(QMouseEvent *event) { if (!d->enabled || !d->visible || !d->geometry.contains(event->localPos()) || !d->acceptedButtons.testFlag(event->button())) { return; } d->setPressed(event->button(), true); event->setAccepted(true); if (d->doubleClickEnabled && event->button() == Qt::LeftButton) { // check for double click if (d->wasDoubleClick()) { event->setAccepted(true); emit doubleClicked(); } d->invalidateDoubleClickTimer(); } if (d->pressAndHold && event->button() == Qt::LeftButton) { d->startPressAndHold(); } } void DecorationButton::mouseReleaseEvent(QMouseEvent *event) { if (!d->enabled || !d->visible || !d->isPressed(event->button())) { return; } if (d->geometry.contains(event->localPos())) { if (!d->pressAndHold || event->button() != Qt::LeftButton) { emit clicked(event->button()); } else { d->stopPressAndHold(); } } d->setPressed(event->button(), false); event->setAccepted(true); if (d->doubleClickEnabled && event->button() == Qt::LeftButton) { d->startDoubleClickTimer(); } } void DecorationButton::wheelEvent(QWheelEvent *event) { Q_UNUSED(event) } } diff --git a/src/decorationbutton_p.h b/src/decorationbutton_p.h index 19f4d34..2315883 100644 --- a/src/decorationbutton_p.h +++ b/src/decorationbutton_p.h @@ -1,91 +1,93 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KDECORATION2_DECORATIONBUTTON_P_H #define KDECORATION2_DECORATIONBUTTON_P_H #include "decorationbutton.h" class QElapsedTimer; class QTimer; // // W A R N I N G // ------------- // // This file is not part of the KDecoration2 API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // namespace KDecoration2 { class DecorationButton::Private { public: explicit Private(DecorationButtonType type, const QPointer &decoration, DecorationButton *parent); ~Private(); bool isPressed() const { return m_pressed != Qt::NoButton; } bool isPressed(Qt::MouseButton button) const { return m_pressed.testFlag(button); } void setHovered(bool hovered); void setPressed(Qt::MouseButton, bool pressed); void setAcceptedButtons(Qt::MouseButtons buttons); void setEnabled(bool enabled); void setChecked(bool checked); void setCheckable(bool checkable); void setVisible(bool visible); void startDoubleClickTimer(); void invalidateDoubleClickTimer(); bool wasDoubleClick() const; void setPressAndHold(bool enable); void startPressAndHold(); void stopPressAndHold(); + QString typeToString(DecorationButtonType type); + QPointer decoration; DecorationButtonType type; QRectF geometry; bool hovered; bool enabled; bool checkable; bool checked; bool visible; Qt::MouseButtons acceptedButtons; bool doubleClickEnabled; bool pressAndHold; private: void init(); DecorationButton *q; Qt::MouseButtons m_pressed; QScopedPointer m_doubleClickTimer; QScopedPointer m_pressAndHoldTimer; }; } #endif diff --git a/src/private/CMakeLists.txt b/src/private/CMakeLists.txt index 2d75392..9e6e590 100644 --- a/src/private/CMakeLists.txt +++ b/src/private/CMakeLists.txt @@ -1,55 +1,55 @@ set(libkdecoration2Private_SRCS decoratedclientprivate.cpp decorationbridge.cpp decorationsettingsprivate.cpp ) add_library(kdecorations2private SHARED ${libkdecoration2Private_SRCS}) generate_export_header( kdecorations2private BASE_NAME KDECORATIONS_PRIVATE EXPORT_FILE_NAME kdecoration2/private/kdecoration2_private_export.h ) add_library(KDecoration2::KDecorationPrivate ALIAS kdecorations2private) target_link_libraries(kdecorations2private PUBLIC Qt5::Core Qt5::Gui ) target_include_directories(kdecorations2private INTERFACE "$" ) set_target_properties(kdecorations2private PROPERTIES VERSION ${KDECORATION2_VERSION_STRING} - SOVERSION ${KDECORATION2_SOVERSION} + SOVERSION 6 EXPORT_NAME KDecoration2Private ) ecm_generate_headers(KDecoration2Private_CamelCase_HEADERS HEADER_NAMES DecoratedClientPrivate DecorationBridge DecorationSettingsPrivate PREFIX KDecoration2/Private REQUIRED_HEADERS KDecoration2Private_HEADERS ) install(FILES ${KDecoration2Private_CamelCase_HEADERS} DESTINATION ${KDECORATION2_INCLUDEDIR}/KDecoration2/Private COMPONENT Devel) install(TARGETS kdecorations2private EXPORT KDecoration2Targets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kdecoration2/private/kdecoration2_private_export.h ${KDecoration2Private_HEADERS} DESTINATION ${KDECORATION2_INCLUDEDIR}/kdecoration2/private COMPONENT Devel ) diff --git a/src/private/decoratedclientprivate.h b/src/private/decoratedclientprivate.h index 51bcead..645fb7b 100644 --- a/src/private/decoratedclientprivate.h +++ b/src/private/decoratedclientprivate.h @@ -1,120 +1,122 @@ /* * Copyright 2014 Martin Gräßlin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KDECORATION2_DECORATED_CLIENT_PRIVATE_H #define KDECORATION2_DECORATED_CLIENT_PRIVATE_H #include #include "../decorationdefines.h" #include #include // // W A R N I N G // ------------- // // This file is not part of the KDecoration2 API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // namespace KDecoration2 { class Decoration; class DecoratedClient; class KDECORATIONS_PRIVATE_EXPORT DecoratedClientPrivate { public: virtual ~DecoratedClientPrivate(); virtual bool isActive() const = 0; virtual QString caption() const = 0; virtual int desktop() const = 0; virtual bool isOnAllDesktops() const = 0; virtual bool isShaded() const = 0; virtual QIcon icon() const = 0; virtual bool isMaximized() const = 0; virtual bool isMaximizedHorizontally() const = 0; virtual bool isMaximizedVertically() const = 0; virtual bool isKeepAbove() const = 0; virtual bool isKeepBelow() const = 0; virtual bool isCloseable() const = 0; virtual bool isMaximizeable() const = 0; virtual bool isMinimizeable() const = 0; virtual bool providesContextHelp() const = 0; virtual bool isModal() const = 0; virtual bool isShadeable() const = 0; virtual bool isMoveable() const = 0; virtual bool isResizeable() const = 0; virtual WId windowId() const = 0; virtual WId decorationId() const = 0; virtual int width() const = 0; virtual int height() const = 0; virtual QPalette palette() const = 0; virtual Qt::Edges adjacentScreenEdges() const = 0; + virtual void requestShowToolTip(const QString &text) = 0; + virtual void requestHideToolTip() = 0; virtual void requestClose() = 0; virtual void requestToggleMaximization(Qt::MouseButtons buttons) = 0; virtual void requestMinimize() = 0; virtual void requestContextHelp() = 0; virtual void requestToggleOnAllDesktops() = 0; virtual void requestToggleShade() = 0; virtual void requestToggleKeepAbove() = 0; virtual void requestToggleKeepBelow() = 0; virtual void requestShowWindowMenu() = 0; Decoration *decoration(); Decoration *decoration() const; virtual QColor color(ColorGroup group, ColorRole role) const; protected: explicit DecoratedClientPrivate(DecoratedClient *client, Decoration *decoration); DecoratedClient *client(); private: class Private; const QScopedPointer d; }; class KDECORATIONS_PRIVATE_EXPORT ApplicationMenuEnabledDecoratedClientPrivate : public DecoratedClientPrivate { public: ~ApplicationMenuEnabledDecoratedClientPrivate() override; virtual bool hasApplicationMenu() const = 0; virtual bool isApplicationMenuActive() const = 0; virtual void showApplicationMenu(int actionId) = 0; virtual void requestShowApplicationMenu(const QRect &rect, int actionId) = 0; protected: explicit ApplicationMenuEnabledDecoratedClientPrivate(DecoratedClient *client, Decoration *decoration); }; } // namespace #endif