Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unversioned

- Minor: Fixed usercard resizing improperly without recent messages. (#6496)
- Minor: Added Markdown support to user notes. (#6490)
- Dev: Update release documentation. (#6498)
- Dev: Make code sanitizers opt in with the `CHATTERINO_SANITIZER_SUPPORT` CMake option. After that's enabled, use the `SANITIZE_*` flag to enable individual sanitizers. (#6493)
- Dev: Remove unused QTextCodec includes. (#6487)
Expand Down
6 changes: 6 additions & 0 deletions resources/buttons/bold-darkMode.svg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use (outlined?) fluent icons for new ones.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions resources/buttons/bold-lightMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions resources/buttons/bullet-list-darkMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions resources/buttons/bullet-list-lightMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions resources/buttons/heading-darkMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions resources/buttons/heading-lightMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions resources/buttons/italic-darkMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions resources/buttons/italic-lightMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions resources/buttons/link-darkMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions resources/buttons/link-lightMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions resources/buttons/quote-darkMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions resources/buttons/quote-lightMode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
214 changes: 186 additions & 28 deletions src/widgets/Label.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#include "singletons/Theme.hpp"
#include "widgets/Label.hpp"

#include "Application.hpp"

#include <QAbstractTextDocumentLayout>
#include <QDesktopServices>
#include <QMouseEvent>
#include <QPainter>
#include <QTextDocument>
#include <QUrl>

namespace chatterino {

Expand Down Expand Up @@ -39,6 +45,10 @@ void Label::setText(const QString &text)
this->updateElidedText(this->getFontMetrics(),
this->textRect().width());
}
if (this->markdownEnabled_ && this->markdownDocument_)
{
this->markdownDocument_->setMarkdown(text);
}
this->updateSize();
this->update();
}
Expand Down Expand Up @@ -84,6 +94,33 @@ void Label::setShouldElide(bool shouldElide)
this->update();
}

bool Label::getMarkdownEnabled() const
{
return this->markdownEnabled_;
}

void Label::setMarkdownEnabled(bool enabled)
{
this->markdownEnabled_ = enabled;
if (enabled)
{
if (!this->markdownDocument_)
{
this->markdownDocument_ = std::make_unique<QTextDocument>();
}
if (!this->text_.isEmpty())
{
this->markdownDocument_->setMarkdown(this->text_);
}
}
else
{
this->markdownDocument_.reset();
}
this->updateSize();
this->update();
}

void Label::setFontStyle(FontStyle style)
{
this->fontStyle_ = style;
Expand Down Expand Up @@ -117,32 +154,65 @@ void Label::paintEvent(QPaintEvent * /*event*/)
// draw text
QRectF textRect = this->textRect();

auto text = [this] {
if (this->shouldElide_)
{
return this->elidedText_;
}
if (this->markdownEnabled_ && this->markdownDocument_ &&
!this->text_.isEmpty())
{
QColor textColor = this->theme ? this->theme->messages.textColors.regular : Qt::black;

return this->text_;
}();
this->markdownDocument_->setTextWidth(textRect.width());
this->markdownDocument_->setDefaultFont(
getApp()->getFonts()->getFont(this->getFontStyle(), this->scale()));
this->markdownDocument_->setMarkdown(this->text_);

qreal width = metrics.horizontalAdvance(text);
Qt::Alignment alignment = !this->centered_ || width > textRect.width()
? Qt::AlignLeft | Qt::AlignVCenter
: Qt::AlignCenter;
QPalette docPalette = this->palette();
docPalette.setColor(QPalette::Text, textColor);
docPalette.setColor(QPalette::WindowText, textColor);

painter.setBrush(this->palette().windowText());
painter.setPen(textColor);

QTextOption option(alignment);
if (this->wordWrap_)
{
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
painter.save();
painter.translate(textRect.topLeft());

// create a rendering context using our text color and document palette
QAbstractTextDocumentLayout::PaintContext paintContext;
paintContext.palette = docPalette;
paintContext.clip = QRectF(0, 0, textRect.width(), textRect.height());
this->markdownDocument_->documentLayout()->draw(
&painter, paintContext);

painter.restore();
}
else
{
option.setWrapMode(QTextOption::NoWrap);
auto text = [this] {
if (this->shouldElide_)
{
return this->elidedText_;
}

return this->text_;
}();

qreal width = metrics.horizontalAdvance(text);
Qt::Alignment alignment = !this->centered_ || width > textRect.width()
? Qt::AlignLeft | Qt::AlignVCenter
: Qt::AlignCenter;

// Use theme color for text instead of palette
QColor textColor = this->theme ? this->theme->messages.textColors.regular : Qt::black;
painter.setBrush(textColor);

QTextOption option(alignment);
if (this->wordWrap_)
{
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
}
else
{
option.setWrapMode(QTextOption::NoWrap);
}
painter.drawText(textRect, text, option);
}
painter.drawText(textRect, text, option);

#if 0
painter.setPen(QColor(255, 0, 0));
Expand Down Expand Up @@ -178,21 +248,47 @@ void Label::updateSize()

auto yPadding =
this->currentPadding_.top() + this->currentPadding_.bottom();
auto height = metrics.height() + yPadding;
if (this->shouldElide_)
{
this->updateElidedText(metrics, this->textRect().width());
this->sizeHint_ = QSizeF(-1, height).toSize();
this->minimumSizeHint_ = this->sizeHint_;
}
else

if (this->markdownEnabled_ && this->markdownDocument_ &&
!this->text_.isEmpty())
{
auto width = metrics.horizontalAdvance(this->text_) +
this->markdownDocument_->setDefaultFont(
getApp()->getFonts()->getFont(this->getFontStyle(), this->scale()));

this->markdownDocument_->setMarkdown(this->text_);

// Use word wrap width if enabled, otherwise use a reasonable default
qreal testWidth = this->wordWrap_
? 400.0 * this->scale()
: this->markdownDocument_->idealWidth();
this->markdownDocument_->setTextWidth(testWidth);

auto height = this->markdownDocument_->size().height() + yPadding;
auto width = qMin(this->markdownDocument_->idealWidth(), testWidth) +
this->currentPadding_.left() +
this->currentPadding_.right();

this->sizeHint_ = QSizeF(width, height).toSize();
this->minimumSizeHint_ = this->sizeHint_;
}
else
{
auto height = metrics.height() + yPadding;
if (this->shouldElide_)
{
this->updateElidedText(metrics, this->textRect().width());
this->sizeHint_ = QSizeF(-1, height).toSize();
this->minimumSizeHint_ = this->sizeHint_;
}
else
{
auto width = metrics.horizontalAdvance(this->text_) +
this->currentPadding_.left() +
this->currentPadding_.right();
this->sizeHint_ = QSizeF(width, height).toSize();
this->minimumSizeHint_ = this->sizeHint_;
}
}

this->updateGeometry();
}
Expand All @@ -214,7 +310,69 @@ bool Label::updateElidedText(const QFontMetricsF &fontMetrics, qreal width)

QRectF Label::textRect() const
{
return this->rect().toRectF().marginsRemoved(this->currentPadding_);
return QRectF(this->rect()).marginsRemoved(this->currentPadding_);
}

void Label::mousePressEvent(QMouseEvent *event)
{
if (this->markdownEnabled_ && this->markdownDocument_ &&
event->button() == Qt::LeftButton)
{
QRectF textRect = this->textRect();
QPointF pos = event->pos() - textRect.topLeft();

QString anchor =
this->markdownDocument_->documentLayout()->anchorAt(pos);
if (!anchor.isEmpty())
{
QUrl url(anchor);

// Validate the URL and add scheme if missing
if (!url.isValid())
{
return;
}

// If the URL doesn't have a scheme, assume it's http
if (url.scheme().isEmpty())
{
url.setScheme("http");
}

// Only open URLs with safe schemes
QString scheme = url.scheme().toLower();
if (scheme == "http" || scheme == "https" || scheme == "ftp" ||
scheme == "file" || scheme == "mailto")
{
QDesktopServices::openUrl(url);
}
return;
}
}

BaseWidget::mousePressEvent(event);
}

void Label::mouseMoveEvent(QMouseEvent *event)
{
if (this->markdownEnabled_ && this->markdownDocument_)
{
QRectF textRect = this->textRect();
QPointF pos = event->pos() - textRect.topLeft();

QString anchor =
this->markdownDocument_->documentLayout()->anchorAt(pos);
if (!anchor.isEmpty())
{
this->setCursor(Qt::PointingHandCursor);
}
else
{
this->setCursor(Qt::ArrowCursor);
}
}

BaseWidget::mouseMoveEvent(event);
}

} // namespace chatterino
Loading