112 lines
4.1 KiB
C++
112 lines
4.1 KiB
C++
#include "CollapsibleSection.h"
|
||
#include <QVBoxLayout>
|
||
#include <QTextBrowser>
|
||
#include <QEvent>
|
||
#include <QCoreApplication>
|
||
#include <QDesktopServices>
|
||
#include <QRegularExpression>
|
||
#include <QApplication>
|
||
|
||
CollapsibleSection::CollapsibleSection(
|
||
const QString &title,
|
||
const QString &markdownContent,
|
||
QWidget *parent,
|
||
const bool expanded
|
||
): QWidget(parent) {
|
||
QColor bg = qApp->palette().color(QPalette::Window);
|
||
bool dark = bg.lightness() < 128;
|
||
|
||
// Кнопка заголовка с стрелкой
|
||
m_toggleButton = new QToolButton(this);
|
||
m_toggleButton->setText(title);
|
||
m_toggleButton->setCheckable(true);
|
||
m_toggleButton->setChecked(expanded);
|
||
m_toggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||
m_toggleButton->setArrowType(expanded ? Qt::DownArrow : Qt::RightArrow);
|
||
|
||
connect(m_toggleButton, &QToolButton::toggled, this, &CollapsibleSection::onToggled);
|
||
|
||
// Контент без собственного скрола, займёт доступное пространство
|
||
m_contentBrowser = new QTextBrowser(this);
|
||
m_contentBrowser->setSearchPaths({QCoreApplication::applicationDirPath() + "/tutorials"});
|
||
if (markdownContent.contains("<table") || markdownContent.contains("<img")) {
|
||
// Контент с HTML — конвертируем markdown-элементы вручную
|
||
QString html = markdownContent;
|
||
// ## заголовок → <h2>
|
||
html.replace(QRegularExpression(R"(^## (.+)$)", QRegularExpression::MultilineOption), R"(<h3>\1</h3>)");
|
||
// --- → <hr>
|
||
html.replace(QRegularExpression(R"(^---$)", QRegularExpression::MultilineOption), "<hr>");
|
||
// **text** → <b>text</b>
|
||
html.replace(QRegularExpression(R"(\*\*([^*]+)\*\*)"), R"(<b>\1</b>)");
|
||
m_contentBrowser->setHtml(html);
|
||
} else {
|
||
m_contentBrowser->setMarkdown(markdownContent);
|
||
}
|
||
m_contentBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||
m_contentBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||
m_contentBrowser->setFrameShape(QFrame::NoFrame);
|
||
m_contentBrowser->setVisible(expanded);
|
||
m_contentBrowser->document()->adjustSize();
|
||
m_contentBrowser->setReadOnly(true);
|
||
m_contentBrowser->setOpenLinks(false);
|
||
|
||
// 2) Подключаемся к клику по ссылке
|
||
connect(m_contentBrowser, &QTextBrowser::anchorClicked,
|
||
this, [](const QUrl &url) {
|
||
QDesktopServices::openUrl(url);
|
||
});
|
||
|
||
if (dark) {
|
||
m_contentBrowser->setStyleSheet(R"(
|
||
QTextBrowser {
|
||
background-color: #2b2b2b;
|
||
border: 1px solid #ccc;
|
||
color: #dddddd;
|
||
border-radius:3px;
|
||
}
|
||
)");
|
||
} else {
|
||
m_contentBrowser->setStyleSheet(R"(
|
||
QTextBrowser {
|
||
background-color: white;
|
||
border: 1px solid #ccc;
|
||
color: black;
|
||
border-radius: 3px;
|
||
}
|
||
)");
|
||
}
|
||
updateContentHeight();
|
||
m_contentBrowser->viewport()->installEventFilter(this);
|
||
|
||
auto *layout = new QVBoxLayout(this);
|
||
layout->setSpacing(0);
|
||
layout->setContentsMargins(0, 0, 0, 0);
|
||
layout->addWidget(m_toggleButton);
|
||
layout->addSpacing(5);
|
||
layout->addWidget(m_contentBrowser);
|
||
}
|
||
|
||
bool CollapsibleSection::eventFilter(QObject *watched, QEvent *event) {
|
||
if (watched == m_contentBrowser->viewport() && event->type() == QEvent::Resize) {
|
||
updateContentHeight();
|
||
}
|
||
return QWidget::eventFilter(watched, event);
|
||
}
|
||
|
||
void CollapsibleSection::updateContentHeight() const {
|
||
const qreal w = m_contentBrowser->viewport()->width();
|
||
m_contentBrowser->document()->setTextWidth(w);
|
||
const qreal h = m_contentBrowser->document()->size().height();
|
||
// Можно добавить +1 пиксел на погрешность:
|
||
m_contentBrowser->setFixedHeight(static_cast<int>(h) + 5);
|
||
}
|
||
|
||
void CollapsibleSection::onToggled(const bool checked) const {
|
||
m_toggleButton->setArrowType(checked ? Qt::DownArrow : Qt::RightArrow);
|
||
m_contentBrowser->setVisible(checked);
|
||
|
||
if (checked) {
|
||
updateContentHeight();
|
||
}
|
||
}
|