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);
最后修改:2023 年 06 月 19 日
如果觉得我的文章对你有用,请随意赞赏