前言
基于QPushButton实现的悬浮按钮功能,并且加入了一些动画,目前实现了基本的功能,满足可用,其他功能细节正在完善中。实现思路计算坐标然后绘制,尽可能减少贴图的使用。 效果图如下,代码自取,请放心食用。有好的想法建议欢迎留言交流,会不定期回复。
效果图
源码
懒得写描述了,自己看吧,直接拿去用也不是不行。
fr_floating_action_button.h
#ifndef FR_FLOATING_ACTION_BUTTON_H
#define FR_FLOATING_ACTION_BUTTON_H
#include <stdint.h>
#include <qpushbutton.h>
#include <qanimationgroup.h>
#include <qpropertyanimation.h>
class FrFloatingActionButton : public QPushButton {
Q_OBJECT
public:
explicit FrFloatingActionButton(QWidget *parent, uint8_t index);
virtual ~FrFloatingActionButton();
void set_path(const QString &path);
void set_text(const QString &text);
void set_icon(const QString &icon);
void Show(bool is);
void ReCalAnimation();
protected:
virtual void paintEvent(QPaintEvent *event);
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
virtual void enterEvent(QEnterEvent *event);
#else
virtual void enterEvent(QEvent *event);
#endif
virtual void leaveEvent(QEvent *event);
private:
void draw_border(QPainter& painter);
void draw_text(QPainter& painter);
private slots:
void transpond_click_signal();
signals:
void Clicked(QString text, QString path, quint8 index);
void ButtonActived(quint8 index, bool is);
private:
const uint8_t index_;
QString path_;
QString text_;
QString icon_;
QPropertyAnimation* zoom_in_;
QPropertyAnimation* zoom_out_;
QPropertyAnimation* float_out_;
QPropertyAnimation* float_back_;
};
#endif
fr_floating_action_button.cpp
#include "fr_floating_action_button.h"
#include <math.h>
#include <qevent.h>
#include <qpainter.h>
#define DURATION 100
FrFloatingActionButton::FrFloatingActionButton(QWidget *parent, uint8_t index)
: QPushButton(parent)
, index_(index)
, zoom_in_(new QPropertyAnimation(this, "geometry", this))
, zoom_out_(new QPropertyAnimation(this, "geometry", this))
, float_out_(new QPropertyAnimation(this, "pos", this))
, float_back_(new QPropertyAnimation(this, "pos", this)) {
connect(this, &FrFloatingActionButton::clicked, this, &FrFloatingActionButton::transpond_click_signal);
connect(zoom_out_, &QPropertyAnimation::finished, this, &FrFloatingActionButton::hide);
}
FrFloatingActionButton::~FrFloatingActionButton() {
}
void FrFloatingActionButton::set_path(const QString &path) {
path_ = path;
}
void FrFloatingActionButton::set_text(const QString &text) {
text_ = text;
}
void FrFloatingActionButton::set_icon(const QString &icon) {
icon_ = icon;
}
void FrFloatingActionButton::Show(bool is) {
if (index_ == 0) {
setVisible(is);
return;
}
if (path_.isEmpty()) {
setVisible(false);
return;
}
if (is) {
if (!isVisible()) {
setVisible(is);
zoom_in_->start();
}
} else {
zoom_out_->start();
}
}
void FrFloatingActionButton::ReCalAnimation() {
zoom_in_->setStartValue(QRect(pos() * 1.25 , size() / 2.0));
zoom_in_->setEndValue(QRect(pos(), size()));
zoom_in_->setDuration(DURATION);
zoom_out_->setStartValue(QRect(pos(), size()));
zoom_out_->setEndValue(QRect(pos() * 1.25 , size() / 2.0));
zoom_out_->setDuration(DURATION);
QPoint offset;
double step = width() * 0.025;
switch (index_) {
case 1:
offset = QPoint(0, -step * 2);
break;
case 2:
offset = QPoint(step * std::sqrt(3), -step);
break;
case 3:
offset = QPoint(step * std::sqrt(3), step);
break;
case 4:
offset = QPoint(0, step * 2);
break;
case 5:
offset = QPoint(-step * std::sqrt(3), step);
break;
case 6:
offset = QPoint(-step * std::sqrt(3), -step);
break;
default:
break;
}
float_out_->setStartValue(pos());
float_out_->setEndValue(pos() + offset);
float_out_->setDuration(DURATION);
float_back_->setStartValue(pos() + offset);
float_back_->setEndValue(pos());
float_back_->setDuration(DURATION);
}
void FrFloatingActionButton::paintEvent(QPaintEvent *event) {
double side = qMin(double(width()), double(height()) / (std::sqrt(3) / 2.0));
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(width() / 2, height() / 2);
painter.scale(side / 210.0, side / 210.0);
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::NoBrush);
draw_border(painter);
draw_text(painter);
}
void FrFloatingActionButton::draw_border(QPainter &painter) {
painter.save();
QPen pen(Qt::darkCyan, 2);
painter.setPen(pen);
painter.setBrush(QColor(78, 128, 196, 128));
static double sqtr3 = std::sqrt(3);
QPointF* points = new QPointF[6];
int i = 0;
points[i++] = QPointF(50.0, sqtr3 * 50.0);
points[i++] = QPointF(100, 0.0);
points[i++] = QPointF(50.0, -sqtr3 * 50.0);
points[i++] = QPointF(-50.0, -sqtr3 * 50.0);
points[i++] = QPointF(-100.0, 0.0);
points[i++] = QPointF(-50.0, sqtr3 * 50.0);
painter.drawPolygon(points, 6);
delete[] points;
painter.restore();
}
void FrFloatingActionButton::draw_text(QPainter &painter) {
painter.save();
painter.setPen(Qt::black);
painter.setFont(QFont("Times", 24));
painter.drawText(QRectF(-100, -100, 200, 200), Qt::AlignCenter, text_);
painter.restore();
}
void FrFloatingActionButton::transpond_click_signal() {
emit Clicked(text_, path_, index_);
}
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
void FrFloatingActionButton::enterEvent(QEnterEvent *event) {
#else
void FrFloatingActionButton::enterEvent(QEvent *event) {
#endif
float_out_->start();
emit ButtonActived(1 << index_, true);
}
void FrFloatingActionButton::leaveEvent(QEvent *event) {
float_back_->start();
emit ButtonActived(1 << index_, false);
}
fr_floating_action_button_panel.h
#ifndef FR_FLOATING_ACTION_BUTTON_PANEL_H
#define FR_FLOATING_ACTION_BUTTON_PANEL_H
#include <stdint.h>
#include <qwidget.h>
#include <qvariant.h>
#include <qjsondocument.h>
#include <qtimer.h>
class FrFloatingActionButton;
class FrFloatingActionButtonPanel : public QWidget
{
Q_OBJECT
public:
explicit FrFloatingActionButtonPanel(QWidget *parent = nullptr);
~FrFloatingActionButtonPanel();
protected:
virtual void resizeEvent(QResizeEvent *event);
virtual void showEvent(QShowEvent *event);
private:
bool load_configuer();
int load_buttons(const QString& path);
void adjust_buttons_size();
private slots:
void process_click(const QString& text, const QString& path, quint8 index);
void active_buttons(quint8 index, bool is);
void deactive_buttons();
void zoom_in_button();
void zoom_out_button();
signals:
void Clicked(QString text, QString path);
private:
QVariant values_;
FrFloatingActionButton** buttons_;
uint8_t actived_buttons_;
QTimer* keep_timer_;
QTimer* zoom_in_timer_;
};
#endif
fr_floating_action_button_panel.cpp
#include "fr_floating_action_button_panel.h"
#include <stdint.h>
#include <math.h>
#include <qpainter.h>
#include <qevent.h>
#include <qfile.h>
#include <qstring.h>
#include <fr_floating_action_button.h>
FrFloatingActionButtonPanel::FrFloatingActionButtonPanel(QWidget *parent)
: QWidget(parent)
, buttons_(nullptr)
, actived_buttons_(0)
, keep_timer_(new QTimer(this))
, zoom_in_timer_(new QTimer(this)) {
setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);
setAttribute(Qt::WA_TranslucentBackground);
buttons_ = new FrFloatingActionButton*[7];
for (int i = 0; i < 7; ++i) {
buttons_[i] = new FrFloatingActionButton(this, i);
connect(buttons_[i], &FrFloatingActionButton::Clicked, this, &FrFloatingActionButtonPanel::process_click);
connect(buttons_[i], &FrFloatingActionButton::ButtonActived, this, &FrFloatingActionButtonPanel::active_buttons);
buttons_[i]->setVisible(i == 0);
}
keep_timer_->setSingleShot(true);
connect(keep_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::deactive_buttons);
bool ok = load_configuer();
if (ok) {
load_buttons(QString());
} else {
;
}
}
FrFloatingActionButtonPanel::~FrFloatingActionButtonPanel() {
if (nullptr != buttons_) {
delete[] buttons_;
buttons_ = nullptr;
}
}
void FrFloatingActionButtonPanel::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
adjust_buttons_size();
}
void FrFloatingActionButtonPanel::showEvent(QShowEvent *event) {
QWidget::showEvent(event);
adjust_buttons_size();
}
bool FrFloatingActionButtonPanel::load_configuer() {
QFile file("system_buttons/config.json");
if(!file.open(QIODevice::ReadWrite)) {
return false;
}
QByteArray json = file.readAll();
QJsonParseError ok;
values_ = QJsonDocument::fromJson(json, &ok).toVariant();
return ok.error == QJsonParseError::NoError;
}
int FrFloatingActionButtonPanel::load_buttons(const QString &path) {
auto values = values_.toList();
auto tmp = path;
QVariantMap last;
last["index"] = QString();
last["text"] = QString("哦吼");
last["icon"] = QString("");
while (!tmp.isEmpty()) {
for (int i = 0; i < values.size(); ++i) {
auto map = values[i].toMap();
if(map.value("index").toString() == tmp.left(1)) {
last = map;
tmp.remove(0, 1);
values = map.value("children").toList();
continue;
}
}
}
if (values.isEmpty()) {
return 0;
}
for (int i = 1; i < 7; ++i) {
buttons_[i]->hide();
buttons_[i]->set_path(QString());
}
values.insert(0, last);
for (int i = 0; i < values.size() && i < 7; ++i) {
auto map = values[i].toMap();
if (i == 0) {
buttons_[i]->set_path(path);
} else {
buttons_[i]->set_path(path + map.value("index").toString());
}
buttons_[i]->set_text(map.value("text").toString());
buttons_[i]->set_icon(map.value("icon").toString());
buttons_[i]->setVisible((i == 0) | !path.isEmpty());
}
buttons_[0]->update();
return values.size() - 1;
}
void FrFloatingActionButtonPanel::adjust_buttons_size() {
int margin = 0;
bool by_witdh = double(width() - margin * 2) / 5.0 < double(height() - margin * 2) / (3 * std::sqrt(3));
int button_width = 0, button_height = 0;
if (by_witdh) {
margin = width() / 20.0;
button_width = double(width() - margin * 2) * 0.4;
button_height = double(width() - margin * 2) * 0.2 * sqrt(3);
} else {
margin = height() / 20.0;
button_width = double(height() - margin * 2) / (3.0 * sqrt(3)) * 2;
button_height = double(height() - margin * 2) / 3.0;
}
QList<QPoint> poses;
poses << QPoint(button_width * 0.75 + margin, button_height + margin)
<< QPoint(button_width * 0.75 + margin, margin)
<< QPoint(button_width * 1.50 + margin, button_height / 2 + margin)
<< QPoint(button_width * 1.50 + margin, button_height * 1.5 + margin)
<< QPoint(button_width * 0.75 + margin, button_height * 2.0 + margin)
<< QPoint(margin, button_height * 1.5 + margin)
<< QPoint(margin, button_height / 2 + margin);
for (int i = 0; i < 7; ++i) {
buttons_[i]->setGeometry(poses[i].x(), poses[i].y(), button_width, button_height);
buttons_[i]->ReCalAnimation();
}
}
void FrFloatingActionButtonPanel::process_click(const QString &text, const QString &path, quint8 index) {
if (index == 0) {
if (!path.isEmpty()) {
load_buttons(path.left(path.size() - 1));
} else {
}
} else {
if(load_buttons(path) == 0) {
emit Clicked(text, path);
}
}
}
void FrFloatingActionButtonPanel::active_buttons(quint8 index, bool is) {
if (is) {
if (index == 1) {
zoom_in_timer_->start(25);
connect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_in_button);
disconnect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_out_button);
}
keep_timer_->stop();
} else {
keep_timer_->start(200);
}
}
void FrFloatingActionButtonPanel::deactive_buttons() {
zoom_in_timer_->start(25);
disconnect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_in_button);
connect(zoom_in_timer_, &QTimer::timeout, this, &FrFloatingActionButtonPanel::zoom_out_button);
}
void FrFloatingActionButtonPanel::zoom_in_button() {
bool is = false;
for (int i = 1; i < 7; ++i) {
if (!buttons_[i]->isVisible()) {
buttons_[i]->Show(true);
is = true;
break;
}
}
if (!is) {
zoom_in_timer_->stop();
}
}
void FrFloatingActionButtonPanel::zoom_out_button() {
static int i = 1;
buttons_[i++]->Show(false);
i %= 7;
if (i == 0) {
zoom_in_timer_->stop();
i = 1;
}
}
|