geopro/src/app/panels/DescriptionPanel.cpp

174 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "panels/DescriptionPanel.hpp"
#include <QColorDialog>
#include <QComboBox>
#include "EmptyAwareComboBox.hpp"
#include <QHBoxLayout>
#include <QPushButton>
#include <QTextCharFormat>
#include <QTextEdit>
#include <QToolBar>
#include <QToolButton>
#include <QVBoxLayout>
#include "Theme.hpp"
#include "panels/QuillDelta.hpp"
namespace geopro::app {
namespace {
// 字号下拉选项px——对照原版 ql-size 的 12~32px。
const int kFontSizesPx[] = {12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
// 字体族——对照原版 ql-font显示名 + Qt family。
struct FontOption { const char* label; const char* family; };
const FontOption kFontFamilies[] = {
{"微软雅黑", "Microsoft YaHei"}, {"宋体", "SimSun"}, {"仿宋", "FangSong"},
{"楷体", "KaiTi"}, {"黑体", "SimHei"}, {"仿宋_GB2312", "FangSong"},
};
} // namespace
DescriptionPanel::DescriptionPanel(QWidget* parent) : QWidget(parent) {
auto* lay = new QVBoxLayout(this);
lay->setContentsMargins(geopro::app::space::kMd, geopro::app::space::kMd,
geopro::app::space::kMd, geopro::app::space::kMd);
lay->setSpacing(geopro::app::space::kSm);
auto* tb = new QToolBar(this);
buildToolbar(tb);
lay->addWidget(tb);
edit_ = new QTextEdit(this);
edit_->setAcceptRichText(true);
edit_->setPlaceholderText(QStringLiteral("暂无描述"));
lay->addWidget(edit_, 1);
auto* btnLay = new QHBoxLayout();
btnLay->addStretch();
saveBtn_ = new QPushButton(QStringLiteral("保存"), this);
saveBtn_->setEnabled(false); // 注入 cmdRepo 后启用
btnLay->addWidget(saveBtn_);
lay->addLayout(btnLay);
connect(saveBtn_, &QPushButton::clicked, this, [this]() { emit saveRequested(); });
}
void DescriptionPanel::buildToolbar(QToolBar* tb) {
// 粗体 / 斜体 / 下划线:可勾选按钮,作用于选区当前字符格式。
auto addToggle = [this, tb](const QString& label, auto applier) {
auto* btn = new QToolButton(tb);
btn->setText(label);
btn->setCheckable(true);
tb->addWidget(btn);
connect(btn, &QToolButton::toggled, this, applier);
return btn;
};
// 对照原版 Quill 工具栏:粗/斜 + 字色/背景色 + 对齐 + 标题 + 字号 + 字体族。
// (原版无下划线/列表,故此处不设,以贴近原版。)
addToggle(QStringLiteral("B"), [this](bool on) {
QTextCharFormat f;
f.setFontWeight(on ? QFont::Bold : QFont::Normal);
edit_->mergeCurrentCharFormat(f);
});
addToggle(QStringLiteral("I"), [this](bool on) {
QTextCharFormat f;
f.setFontItalic(on);
edit_->mergeCurrentCharFormat(f);
});
// 字色:弹色板,作用于选区前景色。
auto* colorBtn = new QToolButton(tb);
colorBtn->setText(QStringLiteral("A"));
colorBtn->setToolTip(QStringLiteral("字体颜色"));
tb->addWidget(colorBtn);
connect(colorBtn, &QToolButton::clicked, this, [this]() {
const QColor c = QColorDialog::getColor(Qt::black, this, QStringLiteral("字体颜色"));
if (!c.isValid()) return;
QTextCharFormat f;
f.setForeground(c);
edit_->mergeCurrentCharFormat(f);
});
// 背景色:弹色板,作用于选区背景色(对照原版 ql-background
auto* bgBtn = new QToolButton(tb);
bgBtn->setText(QStringLiteral(""));
bgBtn->setToolTip(QStringLiteral("背景颜色"));
tb->addWidget(bgBtn);
connect(bgBtn, &QToolButton::clicked, this, [this]() {
const QColor c = QColorDialog::getColor(Qt::yellow, this, QStringLiteral("背景颜色"));
if (!c.isValid()) return;
QTextCharFormat f;
f.setBackground(c);
edit_->mergeCurrentCharFormat(f);
});
// 对齐:左/中/右/两端(对照原版 ql-align——块级。
auto addAlignBtn = [this, tb](const QString& label, Qt::Alignment align) {
auto* btn = new QToolButton(tb);
btn->setText(label);
tb->addWidget(btn);
connect(btn, &QToolButton::clicked, this, [this, align]() {
edit_->setAlignment(align);
});
};
addAlignBtn(QStringLiteral(""), Qt::AlignLeft);
addAlignBtn(QStringLiteral(""), Qt::AlignHCenter);
addAlignBtn(QStringLiteral(""), Qt::AlignRight);
addAlignBtn(QStringLiteral(""), Qt::AlignJustify);
// 标题下拉(正文 / H1~H4——块级作用于当前段。
auto* headerBox = new EmptyAwareComboBox(tb);
headerBox->addItem(QStringLiteral("正文"), 0);
for (int h = 1; h <= 4; ++h) headerBox->addItem(QStringLiteral("标题%1").arg(h), h);
tb->addWidget(headerBox);
connect(headerBox, &QComboBox::currentIndexChanged, this, [this, headerBox](int) {
const int h = headerBox->currentData().toInt();
QTextCursor cur = edit_->textCursor();
QTextBlockFormat bf = cur.blockFormat();
bf.setHeadingLevel(h); // 0 表示正文。
cur.mergeBlockFormat(bf);
edit_->setTextCursor(cur);
});
// 字号下拉px
auto* sizeBox = new EmptyAwareComboBox(tb);
for (int px : kFontSizesPx) sizeBox->addItem(QStringLiteral("%1px").arg(px), px);
sizeBox->setCurrentIndex(2); // 默认 16px与原版一致
tb->addWidget(sizeBox);
connect(sizeBox, &QComboBox::currentIndexChanged, this, [this, sizeBox](int) {
const int px = sizeBox->currentData().toInt();
QTextCharFormat f;
f.setFontPointSize(px * 3.0 / 4.0); // px→pt。
edit_->mergeCurrentCharFormat(f);
});
// 字体族下拉(对照原版 ql-font
auto* fontBox = new EmptyAwareComboBox(tb);
for (const auto& fo : kFontFamilies)
fontBox->addItem(QString::fromUtf8(fo.label), QString::fromUtf8(fo.family));
tb->addWidget(fontBox);
connect(fontBox, &QComboBox::currentIndexChanged, this, [this, fontBox](int) {
const QString family = fontBox->currentData().toString();
QTextCharFormat f;
f.setFontFamilies({family});
edit_->mergeCurrentCharFormat(f);
});
}
void DescriptionPanel::setDelta(const QJsonArray& ops) {
if (ops.isEmpty()) return;
deltaToDocument(ops, *edit_->document());
}
void DescriptionPanel::setPlainText(const QString& text) { edit_->setPlainText(text); }
QJsonArray DescriptionPanel::delta() const { return documentToDelta(*edit_->document()); }
QString DescriptionPanel::plainText() const { return edit_->toPlainText(); }
void DescriptionPanel::setSaveEnabled(bool on) { saveBtn_->setEnabled(on); }
} // namespace geopro::app