DolphinQt: Add BalloonTip which is built off of an internal Qt class. It gives the ability to show a tooltip with an arrow!
This commit is contained in:
parent
a9845e0a3d
commit
c754b02aae
|
@ -81,6 +81,8 @@ add_executable(dolphin-emu
|
||||||
Config/GeckoCodeWidget.h
|
Config/GeckoCodeWidget.h
|
||||||
Config/Graphics/AdvancedWidget.cpp
|
Config/Graphics/AdvancedWidget.cpp
|
||||||
Config/Graphics/AdvancedWidget.h
|
Config/Graphics/AdvancedWidget.h
|
||||||
|
Config/Graphics/BalloonTip.cpp
|
||||||
|
Config/Graphics/BalloonTip.h
|
||||||
Config/Graphics/EnhancementsWidget.cpp
|
Config/Graphics/EnhancementsWidget.cpp
|
||||||
Config/Graphics/EnhancementsWidget.h
|
Config/Graphics/EnhancementsWidget.h
|
||||||
Config/Graphics/GeneralWidget.cpp
|
Config/Graphics/GeneralWidget.cpp
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
// Copyright 2020 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "DolphinQt/Config/Graphics/BalloonTip.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QBitmap>
|
||||||
|
#include <QGraphicsEffect>
|
||||||
|
#include <QGraphicsView>
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include <QPropertyAnimation>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
#include <QScreen>
|
||||||
|
#else
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <QToolTip>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::unique_ptr<BalloonTip> s_the_balloon_tip = nullptr;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void BalloonTip::ShowBalloon(const QIcon& icon, const QString& title, const QString& message,
|
||||||
|
const QPoint& pos, QWidget* parent, ShowArrow show_arrow)
|
||||||
|
{
|
||||||
|
HideBalloon();
|
||||||
|
if (message.isEmpty() && title.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
QString the_message = message;
|
||||||
|
the_message.replace(QStringLiteral("<dolphin_emphasis>"), QStringLiteral("<b>"));
|
||||||
|
the_message.replace(QStringLiteral("</dolphin_emphasis>"), QStringLiteral("</b>"));
|
||||||
|
QToolTip::showText(pos, the_message, parent);
|
||||||
|
#else
|
||||||
|
s_the_balloon_tip = std::make_unique<BalloonTip>(PrivateTag{}, icon, title, message, parent);
|
||||||
|
s_the_balloon_tip->UpdateBoundsAndRedraw(pos, show_arrow);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BalloonTip::HideBalloon()
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
QToolTip::hideText();
|
||||||
|
#else
|
||||||
|
if (!s_the_balloon_tip)
|
||||||
|
return;
|
||||||
|
s_the_balloon_tip->hide();
|
||||||
|
s_the_balloon_tip.reset();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
BalloonTip::BalloonTip(PrivateTag, const QIcon& icon, QString title, QString message,
|
||||||
|
QWidget* parent)
|
||||||
|
: QWidget(nullptr, Qt::ToolTip)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
|
||||||
|
const QPalette& pal = parent->palette();
|
||||||
|
|
||||||
|
const auto theme_window_color = pal.color(QPalette::Base);
|
||||||
|
const auto theme_window_hsv = theme_window_color.toHsv();
|
||||||
|
|
||||||
|
const auto brightness = theme_window_hsv.value();
|
||||||
|
|
||||||
|
QColor window_color;
|
||||||
|
QColor text_color;
|
||||||
|
QColor dolphin_emphasis;
|
||||||
|
if (brightness > 128)
|
||||||
|
{
|
||||||
|
// Our theme color is light, so make it darker
|
||||||
|
window_color = QColor(72, 72, 72);
|
||||||
|
text_color = Qt::white;
|
||||||
|
dolphin_emphasis = Qt::yellow;
|
||||||
|
m_border_color = palette().color(QPalette::Window).darker(160);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Our theme color is dark, so make it lighter
|
||||||
|
window_color = Qt::white;
|
||||||
|
text_color = Qt::black;
|
||||||
|
dolphin_emphasis = QColor(QStringLiteral("#0090ff"));
|
||||||
|
m_border_color = palette().color(QPalette::Window).darker(160);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto style_sheet = QStringLiteral("background-color: #%1; color: #%2;")
|
||||||
|
.arg(window_color.rgba(), 0, 16)
|
||||||
|
.arg(text_color.rgba(), 0, 16);
|
||||||
|
setStyleSheet(style_sheet);
|
||||||
|
|
||||||
|
// Replace text in our our message
|
||||||
|
// if specific "tags" are used
|
||||||
|
message.replace(QStringLiteral("<dolphin_emphasis>"),
|
||||||
|
QStringLiteral("<font color=\"#%1\"><b>").arg(dolphin_emphasis.rgba(), 0, 16));
|
||||||
|
message.replace(QStringLiteral("</dolphin_emphasis>"), QStringLiteral("</b></font>"));
|
||||||
|
|
||||||
|
auto* title_label = new QLabel;
|
||||||
|
title_label->installEventFilter(this);
|
||||||
|
title_label->setText(title);
|
||||||
|
QFont f = title_label->font();
|
||||||
|
f.setBold(true);
|
||||||
|
title_label->setFont(f);
|
||||||
|
title_label->setTextFormat(Qt::RichText);
|
||||||
|
title_label->setSizePolicy(QSizePolicy::Policy::MinimumExpanding,
|
||||||
|
QSizePolicy::Policy::MinimumExpanding);
|
||||||
|
|
||||||
|
auto* message_label = new QLabel;
|
||||||
|
message_label->installEventFilter(this);
|
||||||
|
message_label->setText(message);
|
||||||
|
message_label->setTextFormat(Qt::RichText);
|
||||||
|
message_label->setAlignment(Qt::AlignTop | Qt::AlignLeft);
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
const int limit = QApplication::desktop()->availableGeometry(message_label).width() / 3;
|
||||||
|
#else
|
||||||
|
const int limit = message_label->screen()->availableGeometry().width() / 3;
|
||||||
|
#endif
|
||||||
|
message_label->setMaximumWidth(limit);
|
||||||
|
message_label->setSizePolicy(QSizePolicy::Policy::MinimumExpanding,
|
||||||
|
QSizePolicy::Policy::MinimumExpanding);
|
||||||
|
if (message_label->sizeHint().width() > limit)
|
||||||
|
{
|
||||||
|
message_label->setWordWrap(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* layout = new QGridLayout;
|
||||||
|
layout->addWidget(title_label, 0, 0, 1, 2);
|
||||||
|
|
||||||
|
layout->addWidget(message_label, 1, 0, 1, 3);
|
||||||
|
layout->setSizeConstraint(QLayout::SetMinimumSize);
|
||||||
|
setLayout(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BalloonTip::paintEvent(QPaintEvent*)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
painter.drawPixmap(rect(), m_pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BalloonTip::UpdateBoundsAndRedraw(const QPoint& pos, ShowArrow show_arrow)
|
||||||
|
{
|
||||||
|
m_show_arrow = show_arrow == ShowArrow::Yes;
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
const QRect screen_rect = QApplication::desktop()->screenGeometry(pos);
|
||||||
|
#else
|
||||||
|
QScreen* screen = QGuiApplication::screenAt(pos);
|
||||||
|
if (!screen)
|
||||||
|
screen = QGuiApplication::primaryScreen();
|
||||||
|
const QRect screen_rect = screen->geometry();
|
||||||
|
#endif
|
||||||
|
QSize sh = sizeHint();
|
||||||
|
const int border = 1;
|
||||||
|
const int arrow_height = 18;
|
||||||
|
const int arrow_width = 18;
|
||||||
|
const int arrow_offset = 52;
|
||||||
|
const int rect_center = 7;
|
||||||
|
const bool arrow_at_bottom = (pos.y() - sh.height() - arrow_height > 0);
|
||||||
|
const bool arrow_at_left = (pos.x() + sh.width() - arrow_width < screen_rect.width());
|
||||||
|
const int default_padding = 10;
|
||||||
|
layout()->setContentsMargins(border + 3 + default_padding,
|
||||||
|
border + (arrow_at_bottom ? 0 : arrow_height) + 2 + default_padding,
|
||||||
|
border + 3 + default_padding,
|
||||||
|
border + (arrow_at_bottom ? arrow_height : 0) + 2 + default_padding);
|
||||||
|
updateGeometry();
|
||||||
|
sh = sizeHint();
|
||||||
|
|
||||||
|
int ml, mr, mt, mb;
|
||||||
|
QSize sz = sizeHint();
|
||||||
|
if (arrow_at_bottom)
|
||||||
|
{
|
||||||
|
ml = mt = 0;
|
||||||
|
mr = sz.width() - 1;
|
||||||
|
mb = sz.height() - arrow_height - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ml = 0;
|
||||||
|
mt = arrow_height;
|
||||||
|
mr = sz.width() - 1;
|
||||||
|
mb = sz.height() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPainterPath path;
|
||||||
|
path.moveTo(ml + rect_center, mt);
|
||||||
|
if (!arrow_at_bottom && arrow_at_left)
|
||||||
|
{
|
||||||
|
if (m_show_arrow)
|
||||||
|
{
|
||||||
|
path.lineTo(ml + arrow_offset - arrow_width, mt);
|
||||||
|
path.lineTo(ml + arrow_offset, mt - arrow_height);
|
||||||
|
path.lineTo(ml + arrow_offset + arrow_width, mt);
|
||||||
|
}
|
||||||
|
move(qMax(pos.x() - arrow_offset, screen_rect.left() + 2), pos.y());
|
||||||
|
}
|
||||||
|
else if (!arrow_at_bottom && !arrow_at_left)
|
||||||
|
{
|
||||||
|
if (m_show_arrow)
|
||||||
|
{
|
||||||
|
path.lineTo(mr - arrow_offset - arrow_width, mt);
|
||||||
|
path.lineTo(mr - arrow_offset, mt - arrow_height);
|
||||||
|
path.lineTo(mr - arrow_offset + arrow_width, mt);
|
||||||
|
}
|
||||||
|
move(qMin(pos.x() - sh.width() + arrow_offset, screen_rect.right() - sh.width() - 2), pos.y());
|
||||||
|
}
|
||||||
|
path.lineTo(mr - rect_center, mt);
|
||||||
|
path.arcTo(QRect(mr - rect_center * 2, mt, rect_center * 2, rect_center * 2), 90, -90);
|
||||||
|
path.lineTo(mr, mb - rect_center);
|
||||||
|
path.arcTo(QRect(mr - rect_center * 2, mb - rect_center * 2, rect_center * 2, rect_center * 2), 0,
|
||||||
|
-90);
|
||||||
|
if (arrow_at_bottom && !arrow_at_left)
|
||||||
|
{
|
||||||
|
if (m_show_arrow)
|
||||||
|
{
|
||||||
|
path.lineTo(mr - arrow_offset + arrow_width, mb);
|
||||||
|
path.lineTo(mr - arrow_offset, mb + arrow_height);
|
||||||
|
path.lineTo(mr - arrow_offset - arrow_width, mb);
|
||||||
|
}
|
||||||
|
move(qMin(pos.x() - sh.width() + arrow_offset, screen_rect.right() - sh.width() - 2),
|
||||||
|
pos.y() - sh.height());
|
||||||
|
}
|
||||||
|
else if (arrow_at_bottom && arrow_at_left)
|
||||||
|
{
|
||||||
|
if (m_show_arrow)
|
||||||
|
{
|
||||||
|
path.lineTo(arrow_offset + arrow_width, mb);
|
||||||
|
path.lineTo(arrow_offset, mb + arrow_height);
|
||||||
|
path.lineTo(arrow_offset - arrow_width, mb);
|
||||||
|
}
|
||||||
|
move(qMax(pos.x() - arrow_offset, screen_rect.x() + 2), pos.y() - sh.height());
|
||||||
|
}
|
||||||
|
path.lineTo(ml + rect_center, mb);
|
||||||
|
path.arcTo(QRect(ml, mb - rect_center * 2, rect_center * 2, rect_center * 2), -90, -90);
|
||||||
|
path.lineTo(ml, mt + rect_center);
|
||||||
|
path.arcTo(QRect(ml, mt, rect_center * 2, rect_center * 2), 180, -90);
|
||||||
|
|
||||||
|
// Set the mask
|
||||||
|
QBitmap bitmap(sizeHint());
|
||||||
|
bitmap.fill(Qt::color0);
|
||||||
|
QPainter painter1(&bitmap);
|
||||||
|
painter1.setPen(QPen(Qt::color1, border));
|
||||||
|
painter1.setBrush(QBrush(Qt::color1));
|
||||||
|
painter1.drawPath(path);
|
||||||
|
setMask(bitmap);
|
||||||
|
|
||||||
|
// Draw the border
|
||||||
|
m_pixmap = QPixmap(sz);
|
||||||
|
QPainter painter2(&m_pixmap);
|
||||||
|
painter2.setPen(QPen(m_border_color));
|
||||||
|
painter2.setBrush(palette().color(QPalette::Window));
|
||||||
|
painter2.drawPath(path);
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2020 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class BalloonTip : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
struct PrivateTag
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class ShowArrow
|
||||||
|
{
|
||||||
|
Yes,
|
||||||
|
No
|
||||||
|
};
|
||||||
|
static void ShowBalloon(const QIcon& icon, const QString& title, const QString& msg,
|
||||||
|
const QPoint& pos, QWidget* parent,
|
||||||
|
ShowArrow show_arrow = ShowArrow::Yes);
|
||||||
|
static void HideBalloon();
|
||||||
|
|
||||||
|
BalloonTip(PrivateTag, const QIcon& icon, QString title, QString msg, QWidget* parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateBoundsAndRedraw(const QPoint&, ShowArrow);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QColor m_border_color;
|
||||||
|
QPixmap m_pixmap;
|
||||||
|
bool m_show_arrow = true;
|
||||||
|
};
|
|
@ -60,6 +60,7 @@
|
||||||
<ClCompile Include="Config\GameConfigWidget.cpp" />
|
<ClCompile Include="Config\GameConfigWidget.cpp" />
|
||||||
<ClCompile Include="Config\GeckoCodeWidget.cpp" />
|
<ClCompile Include="Config\GeckoCodeWidget.cpp" />
|
||||||
<ClCompile Include="Config\Graphics\AdvancedWidget.cpp" />
|
<ClCompile Include="Config\Graphics\AdvancedWidget.cpp" />
|
||||||
|
<ClCompile Include="Config\Graphics\BalloonTip.cpp" />
|
||||||
<ClCompile Include="Config\Graphics\EnhancementsWidget.cpp" />
|
<ClCompile Include="Config\Graphics\EnhancementsWidget.cpp" />
|
||||||
<ClCompile Include="Config\Graphics\GeneralWidget.cpp" />
|
<ClCompile Include="Config\Graphics\GeneralWidget.cpp" />
|
||||||
<ClCompile Include="Config\Graphics\GraphicsBool.cpp" />
|
<ClCompile Include="Config\Graphics\GraphicsBool.cpp" />
|
||||||
|
@ -222,6 +223,7 @@
|
||||||
<QtMoc Include="Config\GameConfigWidget.h" />
|
<QtMoc Include="Config\GameConfigWidget.h" />
|
||||||
<QtMoc Include="Config\GeckoCodeWidget.h" />
|
<QtMoc Include="Config\GeckoCodeWidget.h" />
|
||||||
<QtMoc Include="Config\Graphics\AdvancedWidget.h" />
|
<QtMoc Include="Config\Graphics\AdvancedWidget.h" />
|
||||||
|
<QtMoc Include="Config\Graphics\BalloonTip.h" />
|
||||||
<QtMoc Include="Config\Graphics\EnhancementsWidget.h" />
|
<QtMoc Include="Config\Graphics\EnhancementsWidget.h" />
|
||||||
<QtMoc Include="Config\Graphics\GeneralWidget.h" />
|
<QtMoc Include="Config\Graphics\GeneralWidget.h" />
|
||||||
<QtMoc Include="Config\Graphics\GraphicsBool.h" />
|
<QtMoc Include="Config\Graphics\GraphicsBool.h" />
|
||||||
|
|
Loading…
Reference in New Issue