QlineEdit带小三角的悬浮提示窗口
tiplabel.h
// tiplabel.h
#ifndef TIPLABEL_H
#define TIPLABEL_H
/*************************************
* \brief 带有小三角的tip label
* \author NiceBlueChai
*************************************/
#include <QLabel>
//Triangle tip label
// TODO: 添加属性支持样式表设置样式
// TODO: 支持通过enum选择上下左右四个方向的小三角
class TipLabel : public QLabel
{
Q_OBJECT
public:
TipLabel(const QString& text, QWidget *parent = nullptr);
bool eventFilter(QObject*,QEvent*) override;
QSize sizeHint() const override;
void updatePos(const QPoint& pos);
using CallBackType = std::function<QPoint ()>;
void setCallBack( CallBackType updatePosCallBack);
void setTopWindow(QWidget* w);
protected:
void paintEvent(QPaintEvent*) override;
private:
CallBackType updatePosCallBack;
QWidget* top_widget{nullptr};
};
#endif // TIPLABEL_H
tiplabel.cpp
// tiplabel.cpp
#include "tiplabel.h"
#include <QApplication>
#include <QMouseEvent>
#include <QPainter>
#include <QPolygon>
namespace {
const int kTriangleHeight = 8;
const int kTriangleWidth = 20;
const int kTriangleLeftMargin = 30;
}
TipLabel::TipLabel(const QString &text, QWidget *parent)
:QLabel(parent, Qt::ToolTip | Qt::FramelessWindowHint | Qt::BypassGraphicsProxyWidget)
{
setText(text);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_TransparentForMouseEvents, true);
qApp->installEventFilter(this);
}
bool TipLabel::eventFilter(QObject *o, QEvent *e)
{
if(o == parent()){
switch(e->type()){
case QEvent::WindowDeactivate:
hide();
default:
break;
}
}
if(top_widget && this->updatePosCallBack && o == top_widget){
if(e->type() == QEvent::Move) {
updatePos(updatePosCallBack());
}
}
return false;
}
QSize TipLabel::sizeHint() const
{
return QSize(120, 34);
}
void TipLabel::updatePos(const QPoint &pos)
{
move(pos- QPoint(kTriangleLeftMargin + kTriangleWidth/2, 0));
}
void TipLabel::setCallBack(CallBackType updatePosCallBack)
{
this->updatePosCallBack = updatePosCallBack;
}
void TipLabel::setTopWindow(QWidget *w)
{
top_widget = w;
}
void TipLabel::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPolygon polygon_triangle, polygon;
polygon_triangle << QPoint(kTriangleLeftMargin, kTriangleHeight)
<< QPoint(kTriangleLeftMargin + kTriangleWidth/2, 0)
<< QPoint(kTriangleWidth + kTriangleLeftMargin, kTriangleHeight)<<
QPoint(width() - 1, kTriangleHeight)
<< QPoint(width() -1, height() -1)
<< QPoint(1, height() -1)
<< QPoint(1, kTriangleHeight) <<QPoint(kTriangleLeftMargin, kTriangleHeight);
QRectF text_rect = rect().adjusted(0, kTriangleHeight, -1, -1);
painter.fillRect(rect(), Qt::transparent);
painter.save();
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
painter.drawPolygon(polygon_triangle);
painter.drawRoundedRect(text_rect, 6.0, 5.0);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setPen(Qt::gray);
painter.setBrush(Qt::NoBrush);
painter.drawPolyline(polygon_triangle);
painter.restore();
QTextOption opt;
opt.setAlignment(Qt::AlignCenter);
painter.drawText(text_rect, text(), opt);
}
tiplabellineedit.h
// tiplabel-lineedit.h
#ifndef TIPLABELLINEEDIT_H
#define TIPLABELLINEEDIT_H
#include <QLineEdit>
class TipLabel;
class TipLabelLineedit : public QLineEdit
{
Q_OBJECT
public:
explicit TipLabelLineedit(QWidget *parent = nullptr);
void setTopWindow(QWidget* w);
void setTipVisible(bool v);
virtual QPoint tipPos();
// QWidget interface
protected:
void resizeEvent(QResizeEvent *event) override;
void focusInEvent(QFocusEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
private:
TipLabel* label;
bool tip_visible{false};
};
#endif // TIPLABELLINEEDIT_H
tiplabellineedit.cpp
#include "tiplabellineedit.h"
#include "tiplabel.h"
#include <QResizeEvent>
#include <Windows.h>
#if defined (Q_OS_LINUX)
#include <QX11Info>
#include <X11/Xlib.h>
#define XK_MISCELLANY
#include <X11/keysymdef.h>
#elif defined (Q_OS_WIN)
#include <windows.h>
#include <winuser.h>
#endif
bool getCapsLockToggled()
{
#if defined (Q_OS_LINUX)
auto dpy = QX11Info::display();
XKeyboardState x;
XGetKeyboardControl(dpy, &x);
if (x.led_mask & 0x01)
return true;
else
return false;
#elif defined (Q_OS_WIN)
auto ret = GetKeyState(VK_CAPITAL);
if (ret & 0x01)
return true;
else
return false;
#endif
// TODO: Q_OS_MAC(太穷没见过mac,暂时不写)
}
TipLabelLineedit::TipLabelLineedit(QWidget *parent)
: QLineEdit{parent}, label(new TipLabel("大写锁定已打开", this))
{
auto calback = [this]()->QPoint{
return tipPos();
};
label->setCallBack(calback);
}
void TipLabelLineedit::setTopWindow(QWidget *w)
{
label->setTopWindow(w);
}
void TipLabelLineedit::setTipVisible(bool v)
{
tip_visible = v;
}
QPoint TipLabelLineedit::tipPos()
{
auto pos = mapToGlobal(QPoint(10, size().height()/2));
return pos;
}
void TipLabelLineedit::resizeEvent(QResizeEvent *event)
{
auto pos = mapToGlobal(QPoint(6, event->size().height()/2));
label->updatePos(pos);
QLineEdit::resizeEvent(event);
}
void TipLabelLineedit::focusInEvent(QFocusEvent *event)
{
if(tip_visible && getCapsLockToggled()){
label->showNormal();
}
QLineEdit::focusInEvent(event);
}
void TipLabelLineedit::focusOutEvent(QFocusEvent *event)
{
label->hide();
QLineEdit::focusOutEvent(event);
}
void TipLabelLineedit::keyPressEvent(QKeyEvent *event)
{
if(tip_visible && getCapsLockToggled()){
label->showNormal();
}else {
label->hide();
}
QLineEdit::keyPressEvent(event);
}
ui提升为TipLabelLineedit,并设置tip_visible为true
ui->lineEdit->setTipVisible(true);