#include "panels/DescriptionPanel.hpp" #include #include #include #include #include #include #include #include #include #include #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}; } // 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; }; 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); }); addToggle(QStringLiteral("U"), [this](bool on) { QTextCharFormat f; f.setFontUnderline(on); edit_->mergeCurrentCharFormat(f); }); // 字色:弹色板,作用于选区前景色。 auto* colorBtn = new QToolButton(tb); colorBtn->setText(QStringLiteral("A")); 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); }); // 字号下拉(px)。 auto* sizeBox = new QComboBox(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); }); // 标题下拉(正文 / H1~H4)——块级,作用于当前段。 auto* headerBox = new QComboBox(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); }); // 有序 / 无序列表——块级。 auto addListBtn = [this, tb](const QString& label, QTextListFormat::Style style) { auto* btn = new QToolButton(tb); btn->setText(label); tb->addWidget(btn); connect(btn, &QToolButton::clicked, this, [this, style]() { edit_->textCursor().createList(style); }); }; addListBtn(QStringLiteral("1."), QTextListFormat::ListDecimal); addListBtn(QStringLiteral("•"), QTextListFormat::ListDisc); } 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