mirror of
https://github.com/clementine-player/Clementine
synced 2025-02-01 11:56:45 +01:00
Make the tooltip actions work properly by click and shortcut key, add a pretty mouseover animation to the tooltip actions, pass mouse events through to the proper widget when the tooltip is open.
This commit is contained in:
parent
7ec059dc13
commit
b45c5a866e
@ -34,7 +34,7 @@ const qreal GlobalSearchTooltip::kArrowWidth = 10.0;
|
||||
const qreal GlobalSearchTooltip::kArrowHeight = 10.0;
|
||||
|
||||
|
||||
GlobalSearchTooltip::GlobalSearchTooltip(QObject* event_target)
|
||||
GlobalSearchTooltip::GlobalSearchTooltip(QWidget* event_target)
|
||||
: QWidget(NULL),
|
||||
desktop_(qApp->desktop()),
|
||||
event_target_(event_target)
|
||||
@ -43,16 +43,6 @@ GlobalSearchTooltip::GlobalSearchTooltip(QObject* event_target)
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
|
||||
add_ = new QAction(tr("Add to playlist"), this);
|
||||
add_and_play_ = new QAction(tr("Add and play now"), this);
|
||||
add_and_queue_ = new QAction(tr("Queue track"), this);
|
||||
replace_ = new QAction(tr("Replace current playlist"), this);
|
||||
|
||||
add_->setShortcut(QKeySequence(Qt::Key_Return));
|
||||
add_and_play_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Return));
|
||||
add_and_queue_->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_Return));
|
||||
replace_->setShortcut(QKeySequence(Qt::ALT | Qt::Key_Return));
|
||||
}
|
||||
|
||||
void GlobalSearchTooltip::SetResults(const SearchProvider::ResultList& results) {
|
||||
@ -73,7 +63,7 @@ void GlobalSearchTooltip::SetResults(const SearchProvider::ResultList& results)
|
||||
|
||||
// Add the action widget
|
||||
QList<QAction*> actions;
|
||||
actions << add_ << add_and_play_ << add_and_queue_ << replace_;
|
||||
actions.append(common_actions_);
|
||||
|
||||
action_widget_ = new TooltipActionWidget(this);
|
||||
action_widget_->SetActions(actions);
|
||||
@ -119,14 +109,42 @@ void GlobalSearchTooltip::ShowAt(const QPoint& pointing_to) {
|
||||
show();
|
||||
}
|
||||
|
||||
void GlobalSearchTooltip::keyPressEvent(QKeyEvent* e) {
|
||||
// Copy the event to send to the target
|
||||
QKeyEvent copy(e->type(), e->key(), e->modifiers(), e->text(),
|
||||
e->isAutoRepeat(), e->count());
|
||||
bool GlobalSearchTooltip::event(QEvent* e) {
|
||||
switch (e->type()) {
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::InputMethod:
|
||||
case QEvent::Shortcut:
|
||||
case QEvent::ShortcutOverride:
|
||||
if (QApplication::sendEvent(event_target_, e)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
qApp->sendEvent(event_target_, ©);
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
if (!underMouse()) {
|
||||
QMouseEvent* me = static_cast<QMouseEvent*>(e);
|
||||
QMouseEvent c(me->type(), event_target_->mapFromGlobal(me->globalPos()),
|
||||
me->globalPos(), me->button(),
|
||||
me->buttons(), me->modifiers());
|
||||
|
||||
e->accept();
|
||||
QWidget* child = event_target_->childAt(c.pos());
|
||||
|
||||
if (child)
|
||||
child->setAttribute(Qt::WA_UnderMouse, true);
|
||||
|
||||
QApplication::sendEvent(child ? child : event_target_, &c);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QWidget::event(e);
|
||||
}
|
||||
|
||||
void GlobalSearchTooltip::paintEvent(QPaintEvent*) {
|
||||
|
@ -30,20 +30,22 @@ class GlobalSearchTooltip : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GlobalSearchTooltip(QObject* event_target);
|
||||
GlobalSearchTooltip(QWidget* event_target);
|
||||
|
||||
static const qreal kBorderRadius;
|
||||
static const qreal kBorderWidth;
|
||||
static const qreal kArrowWidth;
|
||||
static const qreal kArrowHeight;
|
||||
|
||||
void SetActions(const QList<QAction*>& actions) { common_actions_ = actions; }
|
||||
void SetResults(const SearchProvider::ResultList& results);
|
||||
void ShowAt(const QPoint& pointing_to);
|
||||
|
||||
qreal ArrowOffset() const;
|
||||
|
||||
bool event(QEvent* e);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* e);
|
||||
void paintEvent(QPaintEvent*);
|
||||
|
||||
private:
|
||||
@ -51,18 +53,14 @@ private:
|
||||
|
||||
private:
|
||||
QDesktopWidget* desktop_;
|
||||
|
||||
QAction* add_;
|
||||
QAction* add_and_play_;
|
||||
QAction* add_and_queue_;
|
||||
QAction* replace_;
|
||||
TooltipActionWidget* action_widget_;
|
||||
QList<QAction*> common_actions_;
|
||||
|
||||
SearchProvider::ResultList results_;
|
||||
qreal arrow_offset_;
|
||||
QRect inner_rect_;
|
||||
|
||||
QObject* event_target_;
|
||||
QWidget* event_target_;
|
||||
|
||||
QWidgetList widgets_;
|
||||
};
|
||||
|
@ -82,6 +82,24 @@ GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
|
||||
|
||||
ui_->search->installEventFilter(this);
|
||||
|
||||
// Actions
|
||||
add_ = new QAction(tr("Add to playlist"), this);
|
||||
add_and_play_ = new QAction(tr("Add and play now"), this);
|
||||
add_and_queue_ = new QAction(tr("Queue track"), this);
|
||||
replace_ = new QAction(tr("Replace current playlist"), this);
|
||||
|
||||
add_->setShortcut(QKeySequence(Qt::Key_Return));
|
||||
add_and_play_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_Return));
|
||||
add_and_queue_->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_Return));
|
||||
replace_->setShortcut(QKeySequence(Qt::ALT | Qt::Key_Return));
|
||||
|
||||
connect(add_, SIGNAL(triggered()), SLOT(AddCurrent()));
|
||||
connect(add_and_play_, SIGNAL(triggered()), SLOT(AddAndPlayCurrent()));
|
||||
connect(add_and_queue_, SIGNAL(triggered()), SLOT(AddAndQueueCurrent()));
|
||||
connect(replace_, SIGNAL(triggered()), SLOT(ReplaceCurrent()));
|
||||
|
||||
actions_ << add_ << add_and_play_ << add_and_queue_ << replace_;
|
||||
|
||||
// Load style sheets
|
||||
StyleSheetLoader* style_loader = new StyleSheetLoader(this);
|
||||
style_loader->SetStyleSheet(this, ":globalsearch.css");
|
||||
@ -89,10 +107,13 @@ GlobalSearchWidget::GlobalSearchWidget(QWidget* parent)
|
||||
connect(ui_->search, SIGNAL(textEdited(QString)), SLOT(TextEdited(QString)));
|
||||
connect(engine_, SIGNAL(ResultsAvailable(int,SearchProvider::ResultList)),
|
||||
SLOT(AddResults(int,SearchProvider::ResultList)));
|
||||
connect(engine_, SIGNAL(SearchFinished(int)), SLOT(SearchFinished(int)));
|
||||
connect(engine_, SIGNAL(ArtLoaded(int,QPixmap)), SLOT(ArtLoaded(int,QPixmap)));
|
||||
connect(engine_, SIGNAL(TracksLoaded(int,MimeData*)), SLOT(TracksLoaded(int,MimeData*)));
|
||||
connect(view_, SIGNAL(doubleClicked(QModelIndex)), SLOT(AddCurrent()));
|
||||
connect(engine_, SIGNAL(SearchFinished(int)), SLOT(SearchFinished(int)),
|
||||
Qt::QueuedConnection);
|
||||
connect(engine_, SIGNAL(ArtLoaded(int,QPixmap)), SLOT(ArtLoaded(int,QPixmap)),
|
||||
Qt::QueuedConnection);
|
||||
connect(engine_, SIGNAL(TracksLoaded(int,MimeData*)), SLOT(TracksLoaded(int,MimeData*)),
|
||||
Qt::QueuedConnection);
|
||||
connect(view_, SIGNAL(doubleClicked(QModelIndex)), SLOT(ResultDoubleClicked()));
|
||||
connect(view_->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
|
||||
SLOT(UpdateTooltip()));
|
||||
}
|
||||
@ -358,9 +379,15 @@ bool GlobalSearchWidget::EventFilterPopup(QObject*, QEvent* e) {
|
||||
switch (key) {
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Tab:
|
||||
HidePopup();
|
||||
AddCurrent();
|
||||
// Handle the QActions here - they don't activate when the tooltip is showing
|
||||
if (ke->modifiers() & Qt::AltModifier)
|
||||
replace_->trigger();
|
||||
else if (ke->modifiers() & Qt::ControlModifier)
|
||||
add_and_play_->trigger();
|
||||
else if (ke->modifiers() & Qt::ShiftModifier)
|
||||
add_and_queue_->trigger();
|
||||
else
|
||||
add_->trigger();
|
||||
break;
|
||||
|
||||
case Qt::Key_F4:
|
||||
@ -427,7 +454,27 @@ void GlobalSearchWidget::ArtLoaded(int id, const QPixmap& pixmap) {
|
||||
model_->itemFromIndex(index)->setData(pixmap, Qt::DecorationRole);
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::ResultDoubleClicked() {
|
||||
LoadTracks(NULL);
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::AddCurrent() {
|
||||
LoadTracks(add_);
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::AddAndPlayCurrent() {
|
||||
LoadTracks(add_and_play_);
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::AddAndQueueCurrent() {
|
||||
LoadTracks(add_and_queue_);
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::ReplaceCurrent() {
|
||||
LoadTracks(replace_);
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::LoadTracks(QAction* trigger) {
|
||||
QModelIndex index = view_->currentIndex();
|
||||
if (!index.isValid())
|
||||
index = proxy_->index(0, 0);
|
||||
@ -435,16 +482,31 @@ void GlobalSearchWidget::AddCurrent() {
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
engine_->LoadTracksAsync(index.data(Role_PrimaryResult).value<SearchProvider::Result>());
|
||||
int id = engine_->LoadTracksAsync(
|
||||
index.data(Role_PrimaryResult).value<SearchProvider::Result>());
|
||||
track_requests_[id] = trigger;
|
||||
}
|
||||
|
||||
void GlobalSearchWidget::TracksLoaded(int id, MimeData* mime_data) {
|
||||
Q_UNUSED(id);
|
||||
if (!track_requests_.contains(id))
|
||||
return;
|
||||
|
||||
QAction* trigger = track_requests_.take(id);
|
||||
|
||||
if (!mime_data)
|
||||
return;
|
||||
|
||||
mime_data->from_doubleclick_ = true;
|
||||
if (trigger == NULL) {
|
||||
mime_data->from_doubleclick_ = true;
|
||||
} else if (trigger == add_) {
|
||||
} else if (trigger == add_and_play_) {
|
||||
mime_data->play_now_ = true;
|
||||
} else if (trigger == add_and_queue_) {
|
||||
mime_data->enqueue_now_ = true;
|
||||
} else if (trigger == replace_) {
|
||||
mime_data->clear_first_= true;
|
||||
}
|
||||
|
||||
emit AddToPlaylist(mime_data);
|
||||
}
|
||||
|
||||
@ -528,6 +590,7 @@ void GlobalSearchWidget::UpdateTooltip() {
|
||||
tooltip_.reset(new GlobalSearchTooltip(view_));
|
||||
tooltip_->setFont(view_->font());
|
||||
tooltip_->setPalette(view_->palette());
|
||||
tooltip_->SetActions(actions_);
|
||||
}
|
||||
|
||||
const QRect item_rect = view_->visualRect(current);
|
||||
|
@ -81,7 +81,11 @@ private slots:
|
||||
|
||||
void TracksLoaded(int id, MimeData* mime_data);
|
||||
|
||||
void ResultDoubleClicked();
|
||||
void AddCurrent();
|
||||
void AddAndPlayCurrent();
|
||||
void AddAndQueueCurrent();
|
||||
void ReplaceCurrent();
|
||||
|
||||
void HidePopup();
|
||||
void UpdateTooltip();
|
||||
@ -102,6 +106,8 @@ private:
|
||||
bool EventFilterSearchWidget(QObject* o, QEvent* e);
|
||||
bool EventFilterPopup(QObject* o, QEvent* e);
|
||||
|
||||
void LoadTracks(QAction* trigger);
|
||||
|
||||
private:
|
||||
Ui_GlobalSearchWidget* ui_;
|
||||
|
||||
@ -110,6 +116,7 @@ private:
|
||||
bool clear_model_on_next_result_;
|
||||
|
||||
QMap<int, QModelIndex> art_requests_;
|
||||
QMap<int, QAction*> track_requests_;
|
||||
|
||||
QStandardItemModel* model_;
|
||||
QSortFilterProxyModel* proxy_;
|
||||
@ -125,6 +132,12 @@ private:
|
||||
QStringList provider_order_;
|
||||
|
||||
QScopedPointer<GlobalSearchTooltip> tooltip_;
|
||||
|
||||
QAction* add_;
|
||||
QAction* add_and_play_;
|
||||
QAction* add_and_queue_;
|
||||
QAction* replace_;
|
||||
QList<QAction*> actions_;
|
||||
};
|
||||
|
||||
#endif // GLOBALSEARCHWIDGET_H
|
||||
|
@ -19,10 +19,13 @@
|
||||
#include "core/logging.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
const int TooltipActionWidget::kBorder = 16;
|
||||
const int TooltipActionWidget::kSpacing = 6;
|
||||
const int TooltipActionWidget::kTopPadding = 3;
|
||||
const int TooltipActionWidget::kFadeDurationMsec = 200;
|
||||
|
||||
TooltipActionWidget::TooltipActionWidget(QWidget* parent)
|
||||
: QWidget(parent),
|
||||
@ -30,12 +33,14 @@ TooltipActionWidget::TooltipActionWidget(QWidget* parent)
|
||||
shortcut_width_(0),
|
||||
description_width_(0)
|
||||
{
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void TooltipActionWidget::SetActions(QList<QAction*> actions) {
|
||||
actions_ = actions;
|
||||
action_opacities_.clear();
|
||||
|
||||
int h = 3 + kTextHeight * actions.count();
|
||||
int h = kTopPadding + kTextHeight * actions.count();
|
||||
shortcut_width_ = 0;
|
||||
description_width_ = 0;
|
||||
|
||||
@ -45,6 +50,10 @@ void TooltipActionWidget::SetActions(QList<QAction*> actions) {
|
||||
fontMetrics().width(action->shortcut().toString(QKeySequence::NativeText)));
|
||||
description_width_ =
|
||||
qMax(description_width_, fontMetrics().width(action->text()));
|
||||
|
||||
QTimeLine* timeline = new QTimeLine(kFadeDurationMsec, this);
|
||||
connect(timeline, SIGNAL(valueChanged(qreal)), SLOT(update()));
|
||||
action_opacities_ << timeline;
|
||||
}
|
||||
|
||||
size_hint_ = QSize(
|
||||
@ -55,19 +64,22 @@ void TooltipActionWidget::SetActions(QList<QAction*> actions) {
|
||||
}
|
||||
|
||||
void TooltipActionWidget::paintEvent(QPaintEvent*) {
|
||||
int y = 3;
|
||||
|
||||
const qreal shortcut_opacity = 0.4;
|
||||
const qreal description_opacity = 0.7;
|
||||
int y = kTopPadding;
|
||||
|
||||
QPainter p(this);
|
||||
p.setPen(palette().color(QPalette::Text));
|
||||
|
||||
foreach (const QAction* action, actions_) {
|
||||
for (int i=0 ; i<actions_.count() ; ++i) {
|
||||
const QAction* action = actions_[i];
|
||||
const QTimeLine* timeline = action_opacities_[i];
|
||||
|
||||
const QRect shortcut_rect(kBorder, y, shortcut_width_, kTextHeight);
|
||||
const QRect description_rect(shortcut_rect.right() + kSpacing, y,
|
||||
description_width_, kTextHeight);
|
||||
|
||||
const qreal shortcut_opacity = 0.4 + 0.3 * timeline->currentValue();
|
||||
const qreal description_opacity = 0.7 + 0.3 * timeline->currentValue();
|
||||
|
||||
p.setOpacity(shortcut_opacity);
|
||||
p.drawText(shortcut_rect, Qt::AlignRight | Qt::AlignVCenter,
|
||||
action->shortcut().toString(QKeySequence::NativeText));
|
||||
@ -79,6 +91,40 @@ void TooltipActionWidget::paintEvent(QPaintEvent*) {
|
||||
}
|
||||
}
|
||||
|
||||
void TooltipActionWidget::mousePressEvent(QMouseEvent* e) {
|
||||
|
||||
int TooltipActionWidget::ActionAt(const QPoint& pos) const {
|
||||
return (pos.y() - kTopPadding) / kTextHeight;
|
||||
}
|
||||
|
||||
void TooltipActionWidget::mouseMoveEvent(QMouseEvent* e) {
|
||||
const int action = ActionAt(e->pos());
|
||||
|
||||
for (int i=0 ; i<actions_.count() ; ++i) {
|
||||
if (i == action) {
|
||||
StartAnimation(i, QTimeLine::Forward);
|
||||
} else {
|
||||
StartAnimation(i, QTimeLine::Backward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TooltipActionWidget::leaveEvent(QEvent* e) {
|
||||
for (int i=0 ; i<actions_.count() ; ++i) {
|
||||
StartAnimation(i, QTimeLine::Backward);
|
||||
}
|
||||
}
|
||||
|
||||
void TooltipActionWidget::StartAnimation(int i, QTimeLine::Direction direction) {
|
||||
QTimeLine* timeline = action_opacities_[i];
|
||||
if (timeline->direction() != direction) {
|
||||
timeline->setDirection(direction);
|
||||
if (timeline->state() != QTimeLine::Running)
|
||||
timeline->resume();
|
||||
}
|
||||
}
|
||||
|
||||
void TooltipActionWidget::mousePressEvent(QMouseEvent* e) {
|
||||
const int action = ActionAt(e->pos());
|
||||
if (action >= 0 && action < actions_.count()) {
|
||||
actions_[action]->trigger();
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,10 @@
|
||||
#ifndef TOOLTIPACTIONWIDGET_H
|
||||
#define TOOLTIPACTIONWIDGET_H
|
||||
|
||||
#include <QTimeLine>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
class TooltipActionWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@ -28,6 +30,8 @@ public:
|
||||
|
||||
static const int kBorder;
|
||||
static const int kSpacing;
|
||||
static const int kTopPadding;
|
||||
static const int kFadeDurationMsec;
|
||||
|
||||
void SetActions(QList<QAction*> actions);
|
||||
|
||||
@ -35,12 +39,20 @@ public:
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*);
|
||||
void mouseMoveEvent(QMouseEvent* e);
|
||||
void leaveEvent(QEvent* e);
|
||||
void mousePressEvent(QMouseEvent* e);
|
||||
|
||||
private:
|
||||
int ActionAt(const QPoint& pos) const;
|
||||
void StartAnimation(int i, QTimeLine::Direction direction);
|
||||
|
||||
private:
|
||||
const int kTextHeight;
|
||||
|
||||
QList<QAction*> actions_;
|
||||
QList<QTimeLine*> action_opacities_;
|
||||
|
||||
QSize size_hint_;
|
||||
int shortcut_width_;
|
||||
int description_width_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user