考虑一个问题,QTextEdit如何实现类似微信和QQ聊天输入框中的“@xxx”效果,其内容作为一个整体,以突出颜色显示,并且不可以单独编辑修改,只能整体删除修改。
突出颜色显示有很多方式可以实现,例如
- 通过setTextColor接口,此接口可以设置当前字体颜色为指定颜色
//保存当前颜色
QColor _color = this->textColor();
//设置为红色
this->setTextColor(Qt::red);
//添加文字
this->append("hello world");
//恢复颜色
this->setTextColor(_color);
- 通过html格式实现
this->append("<font color=\"#FF0000\">红色字体</font> ");
- 通过QTextCharFormat实现
auto cursor = this->textCursor();
//备份格式
auto backFormat = cursor.charFormat();
//设置字体
auto _font_size = this->font().pointSize();
//构建格式
QTextCharFormat _format;
//设置文本颜色
_format.setForeground(Qt::red);
cursor.insertText(QString(QChar::ObjectReplacementCharacter),_format);
//恢复默认格式
this->setCurrentCharFormat(backFormat);
颜色搞定了,如何将指定的字符串设为整体呢?
通过面向百度编程和查阅文档(主要是面向百度编程,哈哈)找到一种方法。
众所周知,QTextEdit中的内容是有QTextDocument类实现渲染的,而QTextDocument的布局方式是由QAbstractTextDocumentLayout
实现的,可以通过QAbstractTextDocumentLayout *QTextDocument::documentLayout()
接口获取。而在QAbstractTextDocumentLayout
类中提供了
void registerHandler(int objectType, QObject *component)
接口可以注册自定义的Handler实现自定义绘制。因此可以通过此方式实现将多个文本作为整体。
先看效果
继承QTextObjectInterface
实现intrinsicSize
和drawObject
enum TextFormatRole
{
//字体大小
Format_FontSize = QTextFormat::UserProperty + 1,
//文本
Format_Text
};
class TextEditHander : public QObject,QTextObjectInterface
{
Q_OBJECT
Q_INTERFACES(QTextObjectInterface)
public:
TextEditHander(QObject* parent = nullptr);
~TextEditHander();
//计算绘制区域
QSizeF intrinsicSize(QTextDocument *doc, int posInDocument,const QTextFormat &format) override;
void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc,
int posInDocument, const QTextFormat &format) override;
};
TextEditHander::TextEditHander(QObject *parent):QObject(parent)
{
}
TextEditHander::~TextEditHander()
{
}
//计算绘制区域
QSizeF TextEditHander::intrinsicSize(QTextDocument *doc, int posInDocument,
const QTextFormat &format)
{
//获取字体大小
int font_size = format.property(Format_FontSize).toInt();
QString text = format.property(TextFormatRole::Format_Text).toString();
auto _font = doc->defaultFont();
_font.setPointSize(font_size);
QFontMetrics _metrics(_font);
int textWidth = _metrics.horizontalAdvance(text);
return QSizeF(textWidth,_metrics.height());
}
//绘制
void TextEditHander::drawObject(QPainter *painter, const QRectF &rect,
QTextDocument *doc, int posInDocument, const QTextFormat &format)
{
Q_UNUSED(doc);
Q_UNUSED(posInDocument);
QString text = format.property(Format_Text).toString();
int font_size = format.property(Format_FontSize).toInt();
//调整rect
// QRectF _drawRect = rect.adjusted(0,1,0,-1);
QRectF _drawRect = rect;
//绘制
painter->save();
//绘制背景
painter->fillRect(_drawRect,format.background());
//绘制文字
auto _font = doc->defaultFont();
_font.setPointSize(font_size);
painter->setFont(_font);
painter->setPen(format.foreground().color());
painter->drawText(_drawRect,Qt::AlignBaseline,text);
painter->restore();
}
继承QTextEdit
class TextEdit : public QTextEdit
{
Q_OBJECT
public:
explicit TextEdit(QWidget *parent = nullptr);
virtual ~TextEdit() = default;
protected slots:
//插入标签
void slot_addText();
};
TextEdit::TextEdit(QWidget *parent)
: QTextEdit{parent}
{
//注册handler
auto handler = new TextEditHander(this);
this->document()->documentLayout()->registerHandler(QTextFormat::UserObject+1,handler);
QFont _font = this->font();
_font.setPointSize(12);
this->setFont(_font);
//创建右键菜单
QAction *act = new QAction("插入标签",this);
connect(act,&QAction::triggered,this,&TextEdit::slot_addText);
this->addAction(act);
this->setContextMenuPolicy(Qt::ActionsContextMenu);
}
void TextEdit::slot_addText()
{
auto cursor = this->textCursor();
//备份格式
auto backFormat = cursor.charFormat();
//设置字体
auto _font_size = this->font().pointSize();
//构建格式
QTextCharFormat _format;
//设置格式使用自定义的Hander渲染,这步很重要
_format.setObjectType(QTextFormat::UserObject + 1);
//设置需要绘制的文本
_format.setProperty(TextFormatRole::Format_Text,"${123}");
//设置字体大小
_format.setProperty(TextFormatRole::Format_FontSize,_font_size);
//设置前景色
_format.setForeground(Qt::black);
//设置背景色
_format.setBackground(Qt::lightGray);
cursor.insertText(QString(QChar::ObjectReplacementCharacter),_format);
//恢复默认格式
this->setCurrentCharFormat(backFormat);
//添加一个空格
this->textCursor().insertText(" ");
}