no more popup fading (got rid of it)
[dyskinesia.git] / src / k8popups / k8popups.cpp
blob51f250d52e61d8bb075b425df181706ef84e01ff
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
9 */
10 #include <QDebug>
12 #include <QApplication>
13 #include <QBitmap>
14 #include <QDateTime>
15 #include <QDesktopWidget>
16 #include <QEvent>
17 #include <QMouseEvent>
18 #include <QPainter>
19 #include <QPixmap>
21 #include "k8popups.h"
24 namespace K8Popups {
26 const static QString sLt("&lt;");
27 const static QString sAmp("&amp;");
30 QString escapeStr (const QString &str) {
31 QString res;
32 int len = str.length();
33 res.reserve(len*11/10);
34 for (int f = 0; f < len; f++) {
35 QChar ch(str[f]);
36 switch (ch.unicode()) {
37 case '<': res += sLt; break;
38 case '&': res += sAmp; break;
39 default: res += ch; break;
42 return res;
48 ///////////////////////////////////////////////////////////////////////////////
49 QSize K8PopupLayout::sizeHint () const {
50 QDesktopWidget *desktop = QApplication::desktop();
51 QRect geometry = desktop->availableGeometry();
52 int minH = 64;
53 int maxH = (geometry.bottom()-geometry.top())*2/3;
54 QSize res = QVBoxLayout::sizeHint();
55 int h = res.height();
57 if (h < minH) h = minH;
58 if (h > maxH) h = maxH;
59 res.setHeight(h);
60 res.setWidth(mWidth);
61 return res;
65 ///////////////////////////////////////////////////////////////////////////////
66 #ifdef FADING
67 K8PopupFader::K8PopupFader (QWidget *parent, bool aIn) : QWidget(parent), mPParent(parent), mIn(aIn) {
68 mStartColor = parent ? parent->palette().window().color() : Qt::white;
69 mCurrentAlpha = 0;
70 mDuration = 150;
72 mTimer = new QTimer(this);
73 connect(mTimer, SIGNAL(timeout()), this, SLOT(update()));
75 setAttribute(Qt::WA_DeleteOnClose);
76 resize(parent->size());
80 void K8PopupFader::start () {
81 mCurrentAlpha = mIn ? 0 : 255;
82 mTimer->start(33);
83 //setMask(mPParent->mask());
84 show();
88 void K8PopupFader::paintEvent (QPaintEvent * /*event*/) {
89 bool end;
91 QPainter painter(this);
92 QColor semiTransparentColor = mStartColor;
94 semiTransparentColor.setAlpha(mCurrentAlpha);
95 painter.fillRect(rect(), semiTransparentColor);
96 if (mIn) {
97 mCurrentAlpha += 255*mTimer->interval()/mDuration;
98 end = (mCurrentAlpha >= 255);
99 } else {
100 mCurrentAlpha -= 255*mTimer->interval()/mDuration;
101 end = (mCurrentAlpha <= 0);
103 if (end) {
104 mTimer->stop();
105 close();
108 #endif
111 ///////////////////////////////////////////////////////////////////////////////
112 K8PopupWin::K8PopupWin (K8PopupManager *aMan, int aWidth, int aHeight, K8PopupType *aType,
113 const QString &aId, const QString &msg, QWidget *parent) :
114 QWidget(parent), mMan(aMan),
115 mType(aType), mShouldClose(false), mMouseInside(false), mShowTime(0),
116 mId(aId)
117 #ifdef FADING
118 , mFaderIn(0), mFaderOut(0), mFadingOut(false)
119 #endif
120 //mMaskSet(false)
122 initUI(aWidth, aHeight);
123 setData(msg);
127 K8PopupWin::~K8PopupWin () {
128 if (mMan) mMan->remove(this);
132 #ifdef FADING
133 void K8PopupWin::onFaderInDies (QObject * /*obj*/) {
134 mFaderIn = 0;
138 void K8PopupWin::onFaderOutDies (QObject * /*obj*/) {
139 mFaderOut = 0;
140 if (mFadingOut) close();
142 #endif
145 void K8PopupWin::initUI (int width, int height) {
146 setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::ToolTip);
147 setAttribute(Qt::WA_QuitOnClose, false);
148 setAttribute(Qt::WA_DeleteOnClose, true);
149 setAttribute(Qt::WA_KeyboardFocusChange, false);
150 setAttribute(Qt::WA_AlwaysShowToolTips, false);
151 setContextMenuPolicy(Qt::NoContextMenu);
153 resize(width, height);
155 if (!mMan || !mMan->floatHeight()) {
156 mLayout = new QVBoxLayout(this);
157 setFixedSize(QSize(width, height));
158 } else {
159 mLayout = new K8PopupLayout(width, this);
160 mLayout->setSizeConstraint(QLayout::SetFixedSize);
163 //mLayout = new QVBoxLayout(this);
164 mLayout->setSpacing(0);
165 mLayout->setContentsMargins(0, 0, 0, 0);
166 //verticalLayout->setSpacing(4);
167 //verticalLayout->setMargin(2);
169 mContentLabel = new QLabel(this);
170 mContentLabel->setMinimumSize(QSize(0, 24));
171 mContentLabel->setMaximumSize(QSize(16777215, 16777215));
172 QSizePolicy szp = mContentLabel->sizePolicy();
173 szp.setVerticalPolicy(QSizePolicy::Expanding);
174 mContentLabel->setSizePolicy(szp);
175 mContentLabel->setContextMenuPolicy(Qt::NoContextMenu);
176 mContentLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
177 //mContentLabel->setAlignment(Qt::AlignCenter);
178 mContentLabel->setWordWrap(true);
179 mContentLabel->setTextInteractionFlags(Qt::NoTextInteraction);
180 mContentLabel->setTextFormat(Qt::RichText);
181 mLayout->addWidget(mContentLabel);
183 mContentLabel->show();
185 mContentLabel->installEventFilter(this);
186 installEventFilter(this);
190 bool K8PopupWin::eventFilter (QObject * /*obj*/, QEvent *event) {
191 if (event->type() == QEvent::MouseButtonRelease) {
192 QMouseEvent *mevent = (QMouseEvent *)event;
194 if (mMan) mMan->emitClick(this, mevent->button()); else close();
195 return true;
197 return false;
201 void K8PopupWin::enterEvent (QEvent *event) {
202 mMouseInside = true;
203 QWidget::enterEvent(event);
207 void K8PopupWin::leaveEvent (QEvent *event) {
208 mMouseInside = false;
209 QWidget::leaveEvent(event);
210 if (mShouldClose) close();
214 void K8PopupWin::buildAndSetMask (QWidget *w) {
215 //qDebug() << "size:" << size();
216 QPixmap pixmap(size());
217 pixmap.fill(Qt::transparent);
218 render(&pixmap, QPoint(), QRegion(), QWidget::DrawChildren | QWidget::IgnoreMask);
219 w->clearMask();
220 w->setMask(pixmap.mask());
224 void K8PopupWin::rebuildMask () {
225 this->clearMask();
226 if (isHidden()) {
227 QPoint op = pos();
228 move(10000, 10000);
229 QWidget::show();
230 buildAndSetMask(this);
231 QWidget::hide();
232 move(op);
233 } else {
234 QWidget::hide();
235 QWidget::show();
236 buildAndSetMask(this);
237 QWidget::hide();
238 QWidget::show();
240 //mMaskSet = true;
244 void K8PopupWin::show () {
245 #ifdef FADING
246 if (mFadingOut) return;
247 #endif
248 // cut edges (if any)
249 rebuildMask();
251 #ifdef FADING
252 if (!mFaderIn) {
253 mFaderIn = new K8PopupFader(this, false);
254 connect(mFaderIn, SIGNAL(destroyed(QObject *)), this, SLOT(onFaderInDies(QObject *)));
255 mFaderIn->setFadeColor(Qt::white);
256 mFaderIn->start();
258 #endif
259 QWidget::show();
260 #ifdef FADING
261 if (mFaderIn) {
262 mFaderIn->resize(size());
263 buildAndSetMask(mFaderIn);
265 #endif
269 void K8PopupWin::getOut () {
270 #ifdef FADING
271 if (mFadingOut) return;
272 if (mFaderIn) mFaderIn->close();
273 if (!mFaderOut) {
274 mFaderOut = new K8PopupFader(this, true);
275 connect(mFaderOut, SIGNAL(destroyed(QObject *)), this, SLOT(onFaderOutDies(QObject *)));
276 mFaderOut->setFadeColor(QColor(255, 255, 255));
277 buildAndSetMask(mFaderOut);
278 mFaderOut->start();
280 mFadingOut = true;
281 #else
282 close();
283 #endif
287 void K8PopupWin::setData (const QString &msg) {
288 if (mType) {
289 if (!mType->style.isEmpty()) mContentLabel->setStyleSheet(mType->style);
291 mContentLabel->setText(msg);
292 rebuildMask();
296 ///////////////////////////////////////////////////////////////////////////////
297 K8PopupManager::K8PopupManager (int width, int height, uint timeout, Position pos) :
298 mPos(pos), mWidth(width), mHeight(height), mTimeout(timeout), mFloatHeight(false)
300 mCheckTimer = new QTimer(this);
301 connect(mCheckTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
303 if (mWidth < 64) mWidth = 64; else if (mWidth > 750) mWidth = 750;
304 if (mHeight < 62) mHeight = 64; else if (mHeight > 550) mHeight = 550;
305 if (mTimeout < 1) mTimeout = 1; else if (mTimeout > 30) mTimeout = 30;
309 K8PopupManager::~K8PopupManager () {
310 foreach (K8PopupWin *w, mList) {
311 if (!w) continue;
312 w->mMan = 0;
313 w->mType = 0;
314 delete w;
316 foreach (K8PopupType *t, mTypes) delete t;
320 void K8PopupManager::setTimeout (int timeout) {
321 mTimeout = timeout;
322 if (mTimeout < 1) mTimeout = 1; else if (mTimeout > 30) mTimeout = 30;
326 void K8PopupManager::addType (const QString &aName, const QString &aStyle) {
327 K8PopupType *t;
329 if (mTypes.contains(aName)) {
330 t = mTypes[aName];
331 t->style = aStyle;
332 } else {
333 t = new K8PopupType(aName, aStyle);
334 mTypes[aName] = t;
339 void K8PopupManager::delType (const QString &aName) {
340 if (!mTypes.contains(aName)) return;
341 K8PopupType *t = mTypes[aName];
342 foreach (K8PopupWin *w, mList) if (w && w->mType == t) w->mType = 0;
343 mTypes.remove(aName);
344 delete t;
348 bool K8PopupManager::hasType (const QString &aName) {
349 return mTypes.contains(aName);
353 void K8PopupManager::showMessage (const QString &type, const QString &id, const QString &msg) {
354 if (!mTypes.contains(type)) return;
355 K8PopupType *t = mTypes[type];
356 K8PopupWin *w = new K8PopupWin(this, mWidth, mHeight, t, id, msg);
357 allocSlot(w);
358 w->show();
359 w->mShowTime = QDateTime::currentDateTime().toTime_t();
360 if (mFloatHeight) rearrange();
364 void K8PopupManager::updateMessage (const QString &type, const QString &id, const QString &msg) {
365 bool wasChanged = false;
366 K8PopupType *t = 0;
367 if (!type.isEmpty()) {
368 if (!mTypes.contains(type)) return;
369 t = mTypes[type];
371 foreach (K8PopupWin *w, mList) {
372 if (!w->mMan || (t && w->mType != t) || w->mId != id) continue;
373 w->setData(msg);
374 w->mShowTime = QDateTime::currentDateTime().toTime_t();
375 wasChanged = true;
377 if (!t) return;
378 if (wasChanged) rearrange(); else showMessage(type, id, msg);
382 void K8PopupManager::onTimer () {
383 uint now = QDateTime::currentDateTime().toTime_t();
384 for (int f = mList.size()-1; f >= 0; f--) {
385 K8PopupWin *w = mList[f];
386 if (!w) continue;
387 if (w->mShowTime+mTimeout > now) continue;
388 if (w->mMouseInside) {
389 //w->mShouldClose = true;
390 w->mShowTime = QDateTime::currentDateTime().toTime_t();
391 } else w->getOut();
396 void K8PopupManager::allocSlot (K8PopupWin *w) {
397 QDesktopWidget *desktop = QApplication::desktop();
398 QRect geometry = desktop->availableGeometry();
399 int x = 0, y = 0;
401 switch (mPos) {
402 case LeftTop:
403 x = geometry.left();
404 y = geometry.top();
405 break;
406 case RightTop:
407 x = geometry.right()-mWidth;
408 y = geometry.top();
409 break;
410 case LeftBottom:
411 x = geometry.left();
412 y = geometry.bottom();
413 break;
414 case RightBottom:
415 x = geometry.right()-mWidth;
416 y = geometry.bottom();
417 break;
418 default: ;
420 if (w) {
421 int idx = mList.indexOf(w);
422 if (idx < 0) mList.append(w);
425 QPoint curpos(QCursor::pos());
427 foreach (K8PopupWin *ww, mList) {
428 int hh = ww->height()-1;
430 if (ww->mMouseInside) ww->mShowTime = QDateTime::currentDateTime().toTime_t();
431 switch (mPos) {
432 case LeftTop: case RightTop:
433 if (y+hh > geometry.bottom()) {
434 y = geometry.top();
435 if (mPos == LeftTop) x += mWidth; else x -= mWidth;
437 ww->move(x, y);
438 y += hh;
439 break;
440 case LeftBottom: case RightBottom:
441 y -= hh;
442 if (y < geometry.top()) {
443 y = geometry.bottom();
444 if (mPos == LeftBottom) x += mWidth; else x -= mWidth;
446 ww->move(x, y);
447 break;
448 default: ;
451 QPoint wp(ww->mapFromGlobal(curpos));
453 ww->mMouseInside = (wp.x() >= 0 && wp.y() >= 0 && wp.x() < ww->width() && wp.y() < ww->height());
454 if (ww->mMouseInside) ww->mShowTime = QDateTime::currentDateTime().toTime_t();
456 if (mList.size() == 0) {
457 if (mCheckTimer->isActive()) mCheckTimer->stop();
458 } else {
459 if (!mCheckTimer->isActive()) mCheckTimer->start(500);
464 void K8PopupManager::remove (K8PopupWin *wnd) {
465 if (wnd) {
466 int idx = mList.indexOf(wnd);
467 if (idx < 0) return;
468 mList.remove(idx);
470 rearrange();
474 void K8PopupManager::rearrange () {
475 allocSlot(0);
479 void K8PopupManager::emitClick (K8PopupWin *w, Qt::MouseButton button) {
480 if (!w || !w->mMan) return;
481 if (!w->mType) return;
482 #ifdef FADING
483 if (w->mFadingOut) return;
484 #endif
485 emit click(w->mType->name, w->mId, button);
486 w->getOut();
490 void K8PopupManager::removeAll () {
491 foreach (K8PopupWin *w, mList) {
492 w->mMan = 0;
493 w->close();
495 mList.clear();
496 if (mCheckTimer->isActive()) mCheckTimer->stop();
500 void K8PopupManager::removeById (const QString &type, const QString &id) {
501 K8PopupType *t = 0;
502 if (!type.isEmpty()) {
503 if (!mTypes.contains(type)) return;
504 t = mTypes[type];
507 for (int f = 0; f < mList.size(); ) {
508 K8PopupWin *w = mList[f];
509 if (!w->mMan || (t && w->mType != t) || w->mId != id) {
510 f++;
511 continue;
513 mList[f]->mMan = 0;
514 mList[f]->close();
515 mList.remove(f);
517 rearrange();