Clementine-audio-player-Mac.../src/globalsearch/globalsearchtooltip.cpp

262 lines
7.3 KiB
C++

/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#include "globalsearch.h"
#include "globalsearchtooltip.h"
#include "tooltipactionwidget.h"
#include "tooltipresultwidget.h"
#include "core/logging.h"
#include "core/utilities.h"
#include <QAction>
#include <QApplication>
#include <QDesktopWidget>
#include <QKeyEvent>
#include <QLayoutItem>
#include <QPainter>
#include <QSettings>
#include <QVBoxLayout>
const qreal GlobalSearchTooltip::kBorderRadius = 8.0;
const qreal GlobalSearchTooltip::kTotalBorderWidth = 4.0;
const qreal GlobalSearchTooltip::kOuterBorderWidth = 0.5;
const qreal GlobalSearchTooltip::kArrowWidth = 10.0;
const qreal GlobalSearchTooltip::kArrowHeight = 10.0;
GlobalSearchTooltip::GlobalSearchTooltip(QWidget* event_target)
: QWidget(NULL),
desktop_(qApp->desktop()),
event_target_(event_target),
active_result_(0),
show_tooltip_help_(true)
{
setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
setFocusPolicy(Qt::NoFocus);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
switch_action_ = new QAction(tr("Switch provider"), this);
switch_action_->setShortcut(QKeySequence(Qt::Key_Tab));
connect(switch_action_, SIGNAL(triggered()), SLOT(SwitchProvider()));
ReloadSettings();
}
void GlobalSearchTooltip::SetResults(const SearchProvider::ResultList& results) {
results_ = results;
qDeleteAll(widgets_);
widgets_.clear();
result_buttons_.clear();
active_result_ = 0;
// Using a QVBoxLayout here made some weird flickering that I couldn't figure
// out how to fix, so do layout manually.
int w = 0;
int y = 9;
// Add a widget for each result
foreach (const SearchProvider::Result& result, results) {
TooltipResultWidget* widget = new TooltipResultWidget(result, this);
if (widgets_.isEmpty()) {
// If this is the first widget then mark it as selected
widget->setChecked(true);
}
AddWidget(widget, &w, &y);
result_buttons_ << widget;
}
if (show_tooltip_help_) {
// Add the action widget
QList<QAction*> actions;
if (results_.count() > 1) {
actions.append(switch_action_);
}
actions.append(common_actions_);
action_widget_ = new TooltipActionWidget(this);
action_widget_->SetActions(actions);
AddWidget(action_widget_, &w, &y);
}
// Set the width of each widget
foreach (QWidget* widget, widgets_) {
widget->resize(w, widget->sizeHint().height());
}
// Resize this widget
y += 9;
resize(w, y);
inner_rect_ = rect().adjusted(
kArrowWidth + kTotalBorderWidth, kTotalBorderWidth,
-kTotalBorderWidth, -kTotalBorderWidth);
foreach (QWidget* widget, widgets_) {
widget->setMask(inner_rect_);
}
}
void GlobalSearchTooltip::AddWidget(QWidget* widget, int* w, int* y) {
widget->move(0, *y);
widget->show();
widgets_ << widget;
QSize size_hint(widget->sizeHint());
*y += size_hint.height();
*w = qMax(*w, size_hint.width());
}
void GlobalSearchTooltip::ShowAt(const QPoint& pointing_to) {
const qreal min_arrow_offset = kBorderRadius + kArrowHeight;
const QRect screen = desktop_->screenGeometry(this);
arrow_offset_ = min_arrow_offset +
qMax(0, pointing_to.y() + height() - screen.bottom());
move(pointing_to.x(), pointing_to.y() - arrow_offset_);
if (!isVisible())
show();
}
bool GlobalSearchTooltip::event(QEvent* e) {
switch (e->type()) {
case QEvent::KeyPress: {
QKeyEvent* ke = static_cast<QKeyEvent*>(e);
if (ke->key() == Qt::Key_Tab && ke->modifiers() == Qt::NoModifier) {
SwitchProvider();
e->accept();
return true;
}
// fallthrough
}
case QEvent::KeyRelease:
case QEvent::InputMethod:
case QEvent::Shortcut:
case QEvent::ShortcutOverride:
if (QApplication::sendEvent(event_target_, e)) {
return true;
}
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
if (!underMouse()) {
const QMouseEvent* me = static_cast<QMouseEvent*>(e);
QWidget* child = event_target_->childAt(
event_target_->mapFromGlobal(me->globalPos()));
if (child)
child->setAttribute(Qt::WA_UnderMouse, true);
Utilities::ForwardMouseEvent(me, child ? child : event_target_);
return true;
}
break;
default:
break;
}
return QWidget::event(e);
}
void GlobalSearchTooltip::paintEvent(QPaintEvent*) {
QPainter p(this);
const QColor outer_color(0, 0, 0, 192);
const QColor inner_color = palette().color(QPalette::Highlight);
const QColor center_color = palette().color(QPalette::Base);
// Transparent background
p.fillRect(rect(), Qt::transparent);
QRect area(inner_rect_.adjusted(
-kTotalBorderWidth/2, -kTotalBorderWidth/2,
kTotalBorderWidth/2, kTotalBorderWidth/2));
// Draw the border
p.setCompositionMode(QPainter::CompositionMode_Source);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(QPen(outer_color, kTotalBorderWidth));
p.drawRoundedRect(area, kBorderRadius, kBorderRadius);
// Draw the arrow
QPolygonF arrow;
arrow << QPointF(kArrowWidth + 2, arrow_offset_ - kArrowHeight)
<< QPointF(0, arrow_offset_)
<< QPointF(kArrowWidth + 2, arrow_offset_ + kArrowHeight);
p.setBrush(outer_color);
p.setPen(outer_color);
p.drawPolygon(arrow);
// Now draw the inner shapes on top
const qreal inner_border_width = kTotalBorderWidth - kOuterBorderWidth;
QRect inner_area(inner_rect_.adjusted(
-inner_border_width/2, -inner_border_width/2,
inner_border_width/2, inner_border_width/2));
// Inner border
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.setBrush(center_color);
p.setPen(QPen(inner_color, inner_border_width));
p.drawRoundedRect(inner_area, kBorderRadius, kBorderRadius);
// Inner arrow
arrow[0].setY(arrow[0].y() + kOuterBorderWidth + 0.5);
arrow[1].setX(arrow[1].x() + kOuterBorderWidth + 1.5);
arrow[2].setY(arrow[2].y() - kOuterBorderWidth - 0.5);
p.setBrush(inner_color);
p.setPen(inner_color);
p.drawPolygon(arrow);
}
void GlobalSearchTooltip::SwitchProvider() {
// Find which one was checked before.
int old_index = -1;
for (int i=0 ; i<result_buttons_.count() ; ++i) {
if (result_buttons_[i]->isChecked()) {
old_index = i;
break;
}
}
if (old_index == -1)
return;
// Check the new one. The auto exclusive group will take care of unchecking
// the old one.
active_result_ = (old_index + 1) % result_buttons_.count();
result_buttons_[active_result_]->setChecked(true);
}
void GlobalSearchTooltip::ReloadSettings() {
QSettings s;
s.beginGroup(GlobalSearch::kSettingsGroup);
show_tooltip_help_ = s.value("tooltip_help", true).toBool();
}