From a2d9ff72aa620ea11c22b93c312049d05946fabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20L=C3=BCbking?= Date: Mon, 25 Apr 2011 17:04:04 +0200 Subject: [PATCH] fix close button side for present windows effect BUG: 262543 pint desktop as background when including desktop in switcher BUG: 262137 zoom windows as hover/selection indicaton (1/8 of the screen or 105%) BUG: 215348 CCBUG: 175521 no closer on "show desktop" desktop show closer immediately but have it disabled for a short time to allow the user realize it REVIEW: 101318 --- effects.cpp | 11 ++ effects.h | 2 + effects/presentwindows/presentwindows.cpp | 130 +++++++++++++++++----- effects/presentwindows/presentwindows.h | 9 +- libkdecorations/kdecorationfactory.cpp | 23 +++- libkdecorations/kdecorationfactory.h | 15 +++ libkwineffects/kwineffects.h | 3 +- libkwineffects/kwinglobals.h | 4 + workspace.h | 9 ++ 9 files changed, 172 insertions(+), 34 deletions(-) diff --git a/effects.cpp b/effects.cpp index b23e5d8c64..d507855f16 100644 --- a/effects.cpp +++ b/effects.cpp @@ -1256,6 +1256,17 @@ EffectFrame* EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool static return new EffectFrameImpl(style, staticSize, position, alignment); } + +QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt) +{ + switch (kwopt) + { + case CloseButtonCorner: + return Workspace::self()->decorationCloseButtonCorner(); + } + return QVariant(); // an invalid one +} + void EffectsHandlerImpl::slotShowOutline(const QRect& geometry) { emit showOutline(geometry); diff --git a/effects.h b/effects.h index 75df0cc112..0d57339df1 100644 --- a/effects.h +++ b/effects.h @@ -151,6 +151,8 @@ public: virtual EffectFrame* effectFrame(EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment) const; + virtual QVariant kwinOption(KWinOption kwopt); + // internal (used by kwin core or compositing code) void startPaint(); bool borderActivated(ElectricBorder border); diff --git a/effects/presentwindows/presentwindows.cpp b/effects/presentwindows/presentwindows.cpp index 0dd07448de..6d6afbd8f3 100755 --- a/effects/presentwindows/presentwindows.cpp +++ b/effects/presentwindows/presentwindows.cpp @@ -295,9 +295,12 @@ void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &d } else if (winData->opacity != 1.0) data.setTranslucent(); + const bool isInMotion = m_motionManager.isManaging(w); // Calculate window's brightness if (w == m_highlightedWindow || w == m_closeWindow || !m_activated) winData->highlight = qMin(1.0, winData->highlight + time / m_fadeDuration); + else if (!isInMotion && w->isDesktop()) + winData->highlight = 0.3; else winData->highlight = qMax(0.0, winData->highlight - time / m_fadeDuration); @@ -317,7 +320,7 @@ void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &d if (w->isDesktop() && !w->isOnCurrentDesktop()) w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); - if (m_motionManager.isManaging(w)) + if (isInMotion) data.setTransformed(); // We will be moving this window } effects->prePaintWindow(w, data, time); @@ -333,15 +336,50 @@ void PresentWindowsEffect::paintWindow(EffectWindow *w, int mask, QRegion region return; } + mask |= PAINT_WINDOW_LANCZOS; // Apply opacity and brightness data.opacity *= winData->opacity; - data.brightness *= interpolate(0.7, 1.0, winData->highlight); + data.brightness *= interpolate(0.40, 1.0, winData->highlight); if (m_motionManager.isManaging(w)) { + if (w->isDesktop()) + effects->paintWindow(w, mask, region, data); m_motionManager.apply(w, data); + QRect rect = m_motionManager.transformedGeometry(w).toRect(); - if (!m_motionManager.areWindowsMoving()) { - mask |= PAINT_WINDOW_LANCZOS; + if (winData->highlight > 0.0) { + // scale the window (interpolated by the highlight level) to at least 105% or to cover 1/16 of the screen size - yet keep it in screen bounds + QRect area = effects->clientArea(FullScreenArea, w); + QSizeF effSize(w->width()*data.xScale, w->height()*data.yScale); + float tScale = sqrt((area.width()*area.height()) / (16.0*effSize.width()*effSize.height())); + if (tScale < 1.05) + tScale = 1.05; + if (effSize.width()*tScale > area.width()) + tScale = area.width() / effSize.width(); + if (effSize.height()*tScale > area.height()) + tScale = area.height() / effSize.height(); + const float scale = interpolate(1.0, tScale, winData->highlight); + if (scale > 1.0) { + if (scale < tScale) // don't use lanczos during transition + mask &= ~PAINT_WINDOW_LANCZOS; + + const QPoint ac = area.center(); + const QPoint wc = rect.center(); + + data.xScale *= scale; + data.yScale *= scale; + const int tx = -w->width()*data.xScale*(scale-1.0)*(0.5+(wc.x() - ac.x())/area.width()); + const int ty = -w->height()*data.yScale*(scale-1.0)*(0.5+(wc.y() - ac.y())/area.height()); + rect.translate(tx,ty); + rect.setWidth(rect.width()*scale); + rect.setHeight(rect.height()*scale); + data.xTranslate += tx; + data.yTranslate += ty; + } + } + + if (m_motionManager.areWindowsMoving()) { + mask &= ~PAINT_WINDOW_LANCZOS; } if (m_dragInProgress && m_dragWindow == w) { QPoint diff = cursorPos() - m_dragStart; @@ -350,7 +388,7 @@ void PresentWindowsEffect::paintWindow(EffectWindow *w, int mask, QRegion region } effects->paintWindow(w, mask, region, data); - QRect rect = m_motionManager.transformedGeometry(w).toRect(); + if (m_showIcons) { QPoint point(rect.x() + rect.width() * 0.95, rect.y() + rect.height() * 0.95); @@ -484,7 +522,7 @@ void PresentWindowsEffect::windowInputMouseEvent(Window w, QEvent *e) } if (m_closeView->isVisible()) { const QPoint widgetPos = m_closeView->mapFromGlobal(me->pos()); - const QPointF scenePos = m_closeView->mapToScene(widgetPos); +// const QPointF scenePos = m_closeView->mapToScene(widgetPos); QMouseEvent event(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers()); m_closeView->windowInputMouseEvent(&event); return; @@ -1546,6 +1584,7 @@ void PresentWindowsEffect::setActive(bool active, bool closingTab) } m_activated = active; if (m_activated) { + m_closeButtonCorner = (Qt::Corner)effects->kwinOption(KWin::CloseButtonCorner).toInt(); m_decalOpacity = 0.0; m_highlightedWindow = NULL; m_windowFilter.clear(); @@ -1648,6 +1687,8 @@ void PresentWindowsEffect::setActive(bool active, bool closingTab) } } } else { + if (m_highlightedWindow) + effects->setElevatedWindow(m_highlightedWindow, false); // Fade in/out all windows EffectWindow *activeWindow = effects->activeWindow(); if (m_tabBoxEnabled) @@ -1752,32 +1793,65 @@ void PresentWindowsEffect::setHighlightedWindow(EffectWindow *w) return; m_closeView->hide(); - if (m_highlightedWindow) + if (m_highlightedWindow) { + effects->setElevatedWindow(m_highlightedWindow, false); m_highlightedWindow->addRepaintFull(); // Trigger the first repaint + } m_highlightedWindow = w; - if (m_highlightedWindow) + if (m_highlightedWindow) { + effects->setElevatedWindow(m_highlightedWindow, true); m_highlightedWindow->addRepaintFull(); // Trigger the first repaint + } if (m_tabBoxEnabled && m_highlightedWindow) effects->setTabBoxWindow(w); updateCloseWindow(); } +void PresentWindowsEffect::elevateCloseWindow() +{ + if (EffectWindow *cw = effects->findWindow(m_closeView->winId())) + effects->setElevatedWindow(cw, true); +} + void PresentWindowsEffect::updateCloseWindow() { if (m_doNotCloseWindows) return; - if (m_closeView->isVisible()) - return; - if (!m_highlightedWindow) { + if (!m_highlightedWindow || m_highlightedWindow->isDesktop()) { m_closeView->hide(); return; } - const QRectF rect = m_motionManager.targetGeometry(m_highlightedWindow); - m_closeView->setGeometry(rect.x() + rect.width() - m_closeView->sceneRect().width(), rect.y(), - m_closeView->sceneRect().width(), m_closeView->sceneRect().height()); - if (rect.contains(effects->cursorPos())) - m_closeView->delayedShow(); + if (m_closeView->isVisible()) + return; + + const QRectF rect(m_motionManager.targetGeometry(m_highlightedWindow)); + if (2*m_closeView->sceneRect().width() > rect.width() && 2*m_closeView->sceneRect().height() > rect.height()) { + // not for tiny windows (eg. with many windows) - they might become unselectable + m_closeView->hide(); + return; + } + QRect cvr(QPoint(0,0), m_closeView->sceneRect().size().toSize()); + switch (m_closeButtonCorner) + { + case Qt::TopLeftCorner: + default: + cvr.moveTopLeft(rect.topLeft().toPoint()); break; + case Qt::TopRightCorner: + cvr.moveTopRight(rect.topRight().toPoint()); break; + case Qt::BottomLeftCorner: + cvr.moveBottomLeft(rect.bottomLeft().toPoint()); break; + case Qt::BottomRightCorner: + cvr.moveBottomRight(rect.bottomRight().toPoint()); break; + } + m_closeView->setGeometry(cvr); + if (rect.contains(effects->cursorPos())) { + m_closeView->show(); + m_closeView->disarm(); + // to wait for the next event cycle (or more if the show takes more time) + // TODO: make the closeWindow a graphicsviewitem? why should there be an extra scene to be used in an exiting scene?? + QTimer::singleShot(50, this, SLOT(elevateCloseWindow())); + } else m_closeView->hide(); } @@ -1972,7 +2046,7 @@ void PresentWindowsEffect::globalShortcutChangedClass(const QKeySequence& seq) ************************************************/ CloseWindowView::CloseWindowView(QWidget* parent) : QGraphicsView(parent) - , m_delayedShowTimer(new QTimer(this)) + , m_armTimer(new QTimer(this)) { setWindowFlags(Qt::X11BypassWindowManagerHint); setAttribute(Qt::WA_TranslucentBackground); @@ -2013,15 +2087,15 @@ CloseWindowView::CloseWindowView(QWidget* parent) scene->setSceneRect(QRectF(QPointF(0, 0), QSizeF(width, height))); setScene(scene); - // setup the timer - m_delayedShowTimer->setSingleShot(true); - m_delayedShowTimer->setInterval(500); - connect(m_delayedShowTimer, SIGNAL(timeout()), SLOT(show())); + // setup the timer - attempt to prevent accidental clicks + m_armTimer->setSingleShot(true); + m_armTimer->setInterval(350); // 50ms until the window is elevated (seen!) and 300ms more to be "realized" by the user. + connect(m_armTimer, SIGNAL(timeout()), SLOT(arm())); } void CloseWindowView::windowInputMouseEvent(QMouseEvent* e) { - if (m_delayedShowTimer->isActive()) + if (!isEnabled()) return; if (e->type() == QEvent::MouseMove) { mouseMoveEvent(e); @@ -2041,17 +2115,15 @@ void CloseWindowView::drawBackground(QPainter* painter, const QRectF& rect) m_frame->paintFrame(painter); } -void CloseWindowView::hide() +void CloseWindowView::arm() { - m_delayedShowTimer->stop(); - QWidget::hide(); + setEnabled(true); } -void CloseWindowView::delayedShow() +void CloseWindowView::disarm() { - if (isVisible() || m_delayedShowTimer->isActive()) - return; - m_delayedShowTimer->start(); + setEnabled(false); + m_armTimer->start(); } diff --git a/effects/presentwindows/presentwindows.h b/effects/presentwindows/presentwindows.h index 58c5266485..e0c982ef44 100644 --- a/effects/presentwindows/presentwindows.h +++ b/effects/presentwindows/presentwindows.h @@ -46,8 +46,9 @@ public: void windowInputMouseEvent(QMouseEvent* e); virtual void drawBackground(QPainter* painter, const QRectF& rect); - void delayedShow(); - void hide(); + void disarm(); +public slots: + void arm(); Q_SIGNALS: void close(); @@ -55,7 +56,7 @@ Q_SIGNALS: private: Plasma::PushButton* m_closeButton; Plasma::FrameSvg* m_frame; - QTimer* m_delayedShowTimer; + QTimer* m_armTimer; }; /** @@ -160,6 +161,7 @@ public slots: private slots: void closeWindow(); + void elevateCloseWindow(); protected: // Window rearranging @@ -265,6 +267,7 @@ private: CloseWindowView* m_closeView; EffectWindow* m_closeWindow; + Qt::Corner m_closeButtonCorner; // drag to close QPoint m_dragStart; diff --git a/libkdecorations/kdecorationfactory.cpp b/libkdecorations/kdecorationfactory.cpp index 05b33cf786..142add7a7b 100644 --- a/libkdecorations/kdecorationfactory.cpp +++ b/libkdecorations/kdecorationfactory.cpp @@ -28,12 +28,21 @@ DEALINGS IN THE SOFTWARE. #include "kdecorationbridge.h" -KDecorationFactory::KDecorationFactory() +class KDecorationFactoryPrivate { +public: + KDecorationFactoryPrivate() { + closeButtonCorner = (Qt::Corner)0; + } + Qt::Corner closeButtonCorner; +}; + +KDecorationFactory::KDecorationFactory() : d(new KDecorationFactoryPrivate) { } KDecorationFactory::~KDecorationFactory() { + delete d; assert(_decorations.count() == 0); } @@ -56,6 +65,18 @@ bool KDecorationFactory::exists(const KDecoration* deco) const return _decorations.contains(const_cast< KDecoration* >(deco)); } +Qt::Corner KDecorationFactory::closeButtonCorner() +{ + if (d->closeButtonCorner) + return d->closeButtonCorner; + return options()->titleButtonsLeft().contains('X') ? Qt::TopLeftCorner : Qt::TopRightCorner; +} + +void KDecorationFactory::setCloseButtonCorner(Qt::Corner cnr) +{ + d->closeButtonCorner = cnr; +} + void KDecorationFactory::addDecoration(KDecoration* deco) { _decorations.append(deco); diff --git a/libkdecorations/kdecorationfactory.h b/libkdecorations/kdecorationfactory.h index 978d4b2e50..44a5b7d447 100644 --- a/libkdecorations/kdecorationfactory.h +++ b/libkdecorations/kdecorationfactory.h @@ -88,6 +88,21 @@ public: * after such actions. */ bool exists(const KDecoration* deco) const; + + /** + * Set & get the position of the close button - most decorations don't have to call this ever. + * + * By default, the legacy position indicated by the options (top left or top right) will be + * returned. + * Only if you need to provide a bottom corner or your decoration does not respect those + * settings you will have to specify the exact corner (eg. used by the "present windows" + * closer) + * + * @since 4.8 + */ + Qt::Corner closeButtonCorner(); + void setCloseButtonCorner(Qt::Corner cnr); + /** * @internal */ diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index a93d398478..d75d64d8c3 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -166,7 +166,7 @@ X-KDE-Library=kwin4_effect_cooleffect #define KWIN_EFFECT_API_MAKE_VERSION( major, minor ) (( major ) << 8 | ( minor )) #define KWIN_EFFECT_API_VERSION_MAJOR 0 -#define KWIN_EFFECT_API_VERSION_MINOR 180 +#define KWIN_EFFECT_API_VERSION_MINOR 181 #define KWIN_EFFECT_API_VERSION KWIN_EFFECT_API_MAKE_VERSION( \ KWIN_EFFECT_API_VERSION_MAJOR, KWIN_EFFECT_API_VERSION_MINOR ) @@ -580,6 +580,7 @@ public: virtual void paintEffectFrame(EffectFrame* frame, QRegion region, double opacity, double frameOpacity) = 0; virtual void drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) = 0; virtual void buildQuads(EffectWindow* w, WindowQuadList& quadList) = 0; + virtual QVariant kwinOption(KWinOption kwopt) = 0; // Functions for handling input - e.g. when an Expose-like effect is shown, an input window // covering the whole screen is created and all mouse events will be intercepted by it. // The effect's windowInputMouseEvent() will get called with such events. diff --git a/libkwineffects/kwinglobals.h b/libkwineffects/kwinglobals.h index f34bdba5c5..4bf8153c8b 100644 --- a/libkwineffects/kwinglobals.h +++ b/libkwineffects/kwinglobals.h @@ -105,6 +105,10 @@ enum TabBoxMode { TabBoxWindowsAlternativeMode // Secondary window switching mode }; +enum KWinOption { + CloseButtonCorner +}; + inline KWIN_EXPORT Display* display() { diff --git a/workspace.h b/workspace.h index 492d1ca33b..2cba848416 100644 --- a/workspace.h +++ b/workspace.h @@ -431,6 +431,7 @@ public: bool rulesUpdatesDisabled() const; bool hasDecorationShadows() const; + Qt::Corner decorationCloseButtonCorner(); bool decorationHasAlpha() const; bool decorationSupportsClientGrouping() const; // Returns true if the decoration supports tabs. bool decorationSupportsFrameOverlap() const; @@ -1169,6 +1170,14 @@ inline bool Workspace::hasDecorationShadows() const return mgr->factory()->supports(AbilityProvidesShadow); } +inline Qt::Corner Workspace::decorationCloseButtonCorner() +{ + if (!hasDecorationPlugin()) { + return Qt::TopRightCorner; + } + return mgr->factory()->closeButtonCorner(); +} + inline bool Workspace::decorationHasAlpha() const { if (!hasDecorationPlugin()) {