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
This commit is contained in:
Thomas Lübking 2011-04-25 17:04:04 +02:00
parent 4b81841268
commit a2d9ff72aa
9 changed files with 172 additions and 34 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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();
}

View file

@ -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;

View file

@ -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);

View file

@ -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
*/

View file

@ -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.

View file

@ -105,6 +105,10 @@ enum TabBoxMode {
TabBoxWindowsAlternativeMode // Secondary window switching mode
};
enum KWinOption {
CloseButtonCorner
};
inline
KWIN_EXPORT Display* display()
{

View file

@ -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()) {