174 lines
6.5 KiB
C++
174 lines
6.5 KiB
C++
#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
|