/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see .
*/
#include "osdpretty.h"
#include "ui_osdpretty.h"
#include "qtwin.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef Q_WS_X11
# include
#endif
const char* OSDPretty::kSettingsGroup = "OSDPretty";
const int OSDPretty::kDropShadowSize = 13;
const int OSDPretty::kBorderRadius = 10;
const int OSDPretty::kMaxIconSize = 100;
const QRgb OSDPretty::kPresetBlue = qRgb(102, 150, 227);
const QRgb OSDPretty::kPresetOrange = qRgb(254, 156, 67);
OSDPretty::OSDPretty(Mode mode, QWidget *parent)
: QWidget(parent),
ui_(new Ui_OSDPretty),
mode_(mode),
background_color_(kPresetBlue),
background_opacity_(0.85),
popup_display_(0),
timeout_(new QTimer(this)),
fading_enabled_(false),
fader_(new QTimeLine(300, this))
{
Qt::WindowFlags flags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint |
Qt::X11BypassWindowManagerHint;
#ifdef Q_OS_WIN32
flags |= Qt::ToolTip;
#endif
setWindowFlags(flags);
setAttribute(Qt::WA_TranslucentBackground, true);
ui_->setupUi(this);
// Mode settings
switch (mode_) {
case Mode_Popup:
setCursor(QCursor(Qt::ArrowCursor));
break;
case Mode_Draggable:
setCursor(QCursor(Qt::OpenHandCursor));
break;
}
// Timeout
timeout_->setSingleShot(true);
timeout_->setInterval(5000);
connect(timeout_, SIGNAL(timeout()), SLOT(hide()));
ui_->icon->setMaximumSize(kMaxIconSize, kMaxIconSize);
// Fader
connect(fader_, SIGNAL(valueChanged(qreal)), SLOT(FaderValueChanged(qreal)));
connect(fader_, SIGNAL(finished()), SLOT(FaderFinished()));
#ifdef Q_OS_WIN32
set_fading_enabled(true);
#endif
// Load the show edges and corners
QImage shadow_edge(":osd_shadow_edge.png");
QImage shadow_corner(":osd_shadow_corner.png");
for (int i=0 ; i<4 ; ++i) {
QTransform rotation = QTransform().rotate(90 * i);
shadow_edge_[i] = QPixmap::fromImage(shadow_edge.transformed(rotation));
shadow_corner_[i] = QPixmap::fromImage(shadow_corner.transformed(rotation));
}
background_ = QPixmap(":osd_background.png");
// Set the margins to allow for the drop shadow
QBoxLayout* l = static_cast(layout());
int margin = l->margin() + kDropShadowSize;
l->setMargin(margin);
Load();
}
OSDPretty::~OSDPretty() {
delete ui_;
}
bool OSDPretty::IsTransparencyAvailable() {
#ifdef Q_WS_X11
return QX11Info::isCompositingManagerRunning();
#endif
return true;
}
void OSDPretty::Load() {
QSettings s;
s.beginGroup(kSettingsGroup);
foreground_color_ = QColor(s.value("foreground_color", 0).toInt());
background_color_ = QColor(s.value("background_color", kPresetBlue).toInt());
background_opacity_ = s.value("background_opacity", 0.85).toDouble();
popup_display_ = s.value("popup_display", -1).toInt();
popup_pos_ = s.value("popup_pos", QPoint(0, 0)).toPoint();
set_foreground_color(foreground_color());
}
void OSDPretty::ReloadSettings() {
Load();
if (isVisible())
update();
}
QRect OSDPretty::BoxBorder() const {
return rect().adjusted(kDropShadowSize, kDropShadowSize,
-kDropShadowSize, -kDropShadowSize);
}
void OSDPretty::paintEvent(QPaintEvent *) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::HighQualityAntialiasing);
QRect box(BoxBorder());
// Shadow corners
const int kShadowCornerSize = kDropShadowSize + kBorderRadius;
p.drawPixmap(0, 0, shadow_corner_[0]);
p.drawPixmap(width() - kShadowCornerSize, 0, shadow_corner_[1]);
p.drawPixmap(width() - kShadowCornerSize, height() - kShadowCornerSize, shadow_corner_[2]);
p.drawPixmap(0, height() - kShadowCornerSize, shadow_corner_[3]);
// Shadow edges
p.drawTiledPixmap(kShadowCornerSize, 0,
width() - kShadowCornerSize*2, kDropShadowSize,
shadow_edge_[0]);
p.drawTiledPixmap(width() - kDropShadowSize, kShadowCornerSize,
kDropShadowSize, height() - kShadowCornerSize*2,
shadow_edge_[1]);
p.drawTiledPixmap(kShadowCornerSize, height() - kDropShadowSize,
width() - kShadowCornerSize*2, kDropShadowSize,
shadow_edge_[2]);
p.drawTiledPixmap(0, kShadowCornerSize,
kDropShadowSize, height() - kShadowCornerSize*2,
shadow_edge_[3]);
// Box background
p.setBrush(background_color_);
p.setPen(QPen());
p.setOpacity(background_opacity_);
p.drawRoundedRect(box, kBorderRadius, kBorderRadius);
// Background pattern
QPainterPath background_path;
background_path.addRoundedRect(box, kBorderRadius, kBorderRadius);
p.setClipPath(background_path);
p.setOpacity(1.0);
p.drawPixmap(box.right() - background_.width(),
box.bottom() - background_.height(), background_);
p.setClipping(false);
// Gradient overlay
QLinearGradient gradient(0, 0, 0, height());
gradient.setColorAt(0, QColor(255, 255, 255, 130));
gradient.setColorAt(1, QColor(255, 255, 255, 50));
p.setBrush(gradient);
p.drawRoundedRect(box, kBorderRadius, kBorderRadius);
// Box border
p.setBrush(QBrush());
p.setPen(QPen(background_color_.darker(150), 2));
p.drawRoundedRect(box, kBorderRadius, kBorderRadius);
}
void OSDPretty::SetMessage(const QString& summary, const QString& message,
const QImage& image) {
if (!image.isNull()) {
QImage scaled_image =
image.scaled(kMaxIconSize, kMaxIconSize,
Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui_->icon->setPixmap(QPixmap::fromImage(scaled_image));
ui_->icon->show();
} else {
ui_->icon->hide();
}
ui_->summary->setText(summary);
ui_->message->setText(message);
if (isVisible())
Reposition();
if (isVisible() && mode_ == Mode_Popup)
timeout_->start(); // Restart the timer
}
void OSDPretty::showEvent(QShowEvent* e) {
setWindowOpacity(fading_enabled_ ? 0.0 : 1.0);
QWidget::showEvent(e);
Reposition();
if (fading_enabled_) {
fader_->setDirection(QTimeLine::Forward);
fader_->start(); // Timeout will be started in FaderFinished
}
else if (mode_ == Mode_Popup)
timeout_->start();
}
void OSDPretty::setVisible(bool visible) {
if (!visible && fading_enabled_ && fader_->direction() == QTimeLine::Forward) {
fader_->setDirection(QTimeLine::Backward);
fader_->start();
} else {
QWidget::setVisible(visible);
}
}
void OSDPretty::FaderFinished() {
if (fader_->direction() == QTimeLine::Backward)
hide();
else if (mode_ == Mode_Popup)
timeout_->start();
}
void OSDPretty::FaderValueChanged(qreal value) {
setWindowOpacity(value);
}
void OSDPretty::Reposition() {
QDesktopWidget* desktop = QApplication::desktop();
// Make the OSD the proper size
layout()->activate();
resize(sizeHint());
// Work out where to place the OSD. -1 for x or y means "on the right or
// bottom edge".
QRect geometry(desktop->availableGeometry(popup_display_));
int x = popup_pos_.x() < 0 ? geometry.right() - width()
: geometry.left() + popup_pos_.x();
int y = popup_pos_.y() < 0 ? geometry.bottom() - height()
: geometry.top() + popup_pos_.y();
move(qBound(0, x, geometry.right() - width()),
qBound(0, y, geometry.bottom() - height()));
// Create a mask for the actual area of the OSD
QBitmap mask(size());
mask.clear();
QPainter p(&mask);
p.setBrush(Qt::color1);
p.drawRoundedRect(BoxBorder().adjusted(-1, -1, 0, 0), kBorderRadius, kBorderRadius);
p.end();
// If there's no compositing window manager running then we have to set an
// XShape mask.
if (IsTransparencyAvailable())
clearMask();
else {
setMask(mask);
}
// On windows, enable blurbehind on the masked area
QtWin::enableBlurBehindWindow(this, true, QRegion(mask));
}
void OSDPretty::enterEvent(QEvent *) {
if (mode_ == Mode_Popup)
setWindowOpacity(0.25);
}
void OSDPretty::leaveEvent(QEvent *) {
setWindowOpacity(1.0);
}
void OSDPretty::mousePressEvent(QMouseEvent* e) {
if (mode_ == Mode_Popup)
hide();
else {
original_window_pos_ = pos();
drag_start_pos_ = e->globalPos();
}
}
void OSDPretty::mouseMoveEvent(QMouseEvent* e) {
if (mode_ == Mode_Draggable) {
QPoint delta = e->globalPos() - drag_start_pos_;
QPoint new_pos = original_window_pos_ + delta;
// Keep it to the bounds of the desktop
QDesktopWidget* desktop = QApplication::desktop();
QRect geometry(desktop->availableGeometry(e->globalPos()));
new_pos.setX(qBound(geometry.left(), new_pos.x(), geometry.right() - width()));
new_pos.setY(qBound(geometry.top(), new_pos.y(), geometry.bottom() - height()));
move(new_pos);
popup_display_ = current_display();
popup_pos_ = current_pos();
}
}
QPoint OSDPretty::current_pos() const {
QDesktopWidget* desktop = QApplication::desktop();
QRect geometry(desktop->availableGeometry(current_display()));
int x = pos().x() >= geometry.right() - width()
? -1 : pos().x() - geometry.left();
int y = pos().y() >= geometry.bottom() - height()
? -1 : pos().y() - geometry.top();
return QPoint(x, y);
}
int OSDPretty::current_display() const {
QDesktopWidget* desktop = QApplication::desktop();
return desktop->screenNumber(pos());
}
void OSDPretty::set_background_color(QRgb color) {
background_color_ = color;
if (isVisible())
update();
}
void OSDPretty::set_background_opacity(qreal opacity) {
background_opacity_ = opacity;
if (isVisible())
update();
}
void OSDPretty::set_foreground_color(QRgb color) {
foreground_color_ = QColor(color);
QPalette p;
p.setColor(QPalette::WindowText, foreground_color_);
ui_->summary->setPalette(p);
ui_->message->setPalette(p);
}
void OSDPretty::set_popup_duration(int msec) {
timeout_->setInterval(msec);
}
void OSDPretty::mouseReleaseEvent(QMouseEvent *) {
if (mode_ == Mode_Draggable) {
popup_display_ = current_display();
popup_pos_ = current_pos();
}
}