Use custom-adjusted font metrics.

This commit is contained in:
John Preston 2024-05-02 11:32:44 +04:00
parent 4ddff63a9b
commit 86dc01e9bc
16 changed files with 362 additions and 202 deletions

View file

@ -23,12 +23,16 @@ public:
void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) { void drawTextLeft(int x, int y, int outerw, const QString &text, int textWidth = -1) {
QFontMetrics m(fontMetrics()); QFontMetrics m(fontMetrics());
if (style::RightToLeft() && textWidth < 0) textWidth = m.horizontalAdvance(text); if (style::RightToLeft() && textWidth < 0) textWidth = m.horizontalAdvance(text);
drawText(style::RightToLeft() ? (outerw - x - textWidth) : x, y + m.ascent(), text); const auto result = style::FindAdjustResult(font());
const auto ascent = result ? result->iascent : m.ascent();
drawText(style::RightToLeft() ? (outerw - x - textWidth) : x, y + ascent, text);
} }
void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1) { void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1) {
QFontMetrics m(fontMetrics()); QFontMetrics m(fontMetrics());
if (!style::RightToLeft() && textWidth < 0) textWidth = m.horizontalAdvance(text); if (!style::RightToLeft() && textWidth < 0) textWidth = m.horizontalAdvance(text);
drawText(style::RightToLeft() ? x : (outerw - x - textWidth), y + m.ascent(), text); const auto result = style::FindAdjustResult(font());
const auto ascent = result ? result->iascent : m.ascent();
drawText(style::RightToLeft() ? x : (outerw - x - textWidth), y + ascent, text);
} }
void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) { void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix, const QRect &from) {
drawPixmap(QPoint(style::RightToLeft() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from); drawPixmap(QPoint(style::RightToLeft() ? (outerw - x - (from.width() / pix.devicePixelRatio())) : x, y), pix, from);

View file

@ -107,13 +107,13 @@ void TitleWidget::init(int height) {
const auto apple = (family == u".AppleSystemUIFont"_q); const auto apple = (family == u".AppleSystemUIFont"_q);
setFromFont(style::font( setFromFont(style::font(
apple ? 13 : (height * 15) / 24, apple ? 13 : (height * 15) / 24,
apple ? style::internal::FontBold : 0, apple ? style::FontFlag::Bold : 0,
family)); family));
break; break;
} }
} }
if (!_textStyle) { if (!_textStyle) {
setFromFont(style::font(13, style::internal::FontSemibold, 0)); setFromFont(style::font(13, style::FontFlag::Semibold, 0));
} }
} }

View file

@ -29,12 +29,12 @@ auto PaletteVersion = 0;
auto ShortAnimationRunning = rpl::variable<bool>(false); auto ShortAnimationRunning = rpl::variable<bool>(false);
auto RunningShortAnimations = 0; auto RunningShortAnimations = 0;
std::vector<internal::ModuleBase*> &StyleModules() { [[nodiscard]] std::vector<internal::ModuleBase*> &StyleModules() {
static auto result = std::vector<internal::ModuleBase*>(); static auto result = std::vector<internal::ModuleBase*>();
return result; return result;
} }
void startModules(int scale) { void StartModules(int scale) {
for (const auto module : StyleModules()) { for (const auto module : StyleModules()) {
module->start(scale); module->start(scale);
} }
@ -60,14 +60,14 @@ void StopShortAnimation() {
} // namespace internal } // namespace internal
void startManager(int scale) { void StartManager(int scale) {
internal::registerFontFamily("Open Sans"); internal::RegisterFontFamily("Open Sans");
internal::startModules(scale); internal::StartModules(scale);
} }
void stopManager() { void StopManager() {
internal::destroyFonts(); internal::DestroyFonts();
internal::destroyIcons(); internal::DestroyIcons();
} }
rpl::producer<> PaletteChanged() { rpl::producer<> PaletteChanged() {

View file

@ -35,8 +35,8 @@ void StopShortAnimation();
} // namespace internal } // namespace internal
void startManager(int scale); void StartManager(int scale);
void stopManager(); void StopManager();
[[nodiscard]] rpl::producer<> PaletteChanged(); [[nodiscard]] rpl::producer<> PaletteChanged();
[[nodiscard]] int PaletteVersion(); [[nodiscard]] int PaletteVersion();

View file

@ -57,27 +57,28 @@ public:
_data->set(r, g, b, a); _data->set(r, g, b, a);
} }
operator const QBrush &() const { [[nodiscard]] operator const QBrush &() const {
return _data->b; return _data->b;
} }
operator const QPen &() const { [[nodiscard]] operator const QPen &() const {
return _data->p; return _data->p;
} }
ColorData *operator->() const { [[nodiscard]] ColorData *operator->() const {
return _data; return _data;
} }
ColorData *v() const { [[nodiscard]] ColorData *get() const {
return _data; return _data;
} }
explicit operator bool() const { [[nodiscard]] explicit operator bool() const {
return !!_data; return !!_data;
} }
class Proxy; class Proxy;
Proxy operator[](const style::palette &paletteOverride) const; [[nodiscard]] Proxy operator[](
const style::palette &paletteOverride) const;
private: private:
friend class OwnedColor; friend class OwnedColor;
@ -164,12 +165,12 @@ public:
} }
Proxy(const Proxy &other) = default; Proxy(const Proxy &other) = default;
operator const QBrush &() const { return _color; } [[nodiscard]] operator const QBrush &() const { return _color; }
operator const QPen &() const { return _color; } [[nodiscard]] operator const QPen &() const { return _color; }
ColorData *operator->() const { return _color.v(); } [[nodiscard]] ColorData *operator->() const { return _color.get(); }
ColorData *v() const { return _color.v(); } [[nodiscard]] ColorData *get() const { return _color.get(); }
explicit operator bool() const { return _color ? true : false; } [[nodiscard]] explicit operator bool() const { return !!_color; }
Color clone() const { return _color; } [[nodiscard]] Color clone() const { return _color; }
private: private:
Color _color; Color _color;

View file

@ -52,6 +52,19 @@ void SetCustomFont(const QString &font) {
} }
namespace internal { namespace internal {
struct ResolvedFont {
ResolvedFont(FontResolveResult result, FontVariants *modified);
FontResolveResult result;
FontData data;
};
ResolvedFont::ResolvedFont(FontResolveResult result, FontVariants *modified)
: result(std::move(result))
, data(this->result, modified) {
}
namespace { namespace {
#ifndef LIB_UI_USE_PACKAGED_FONTS #ifndef LIB_UI_USE_PACKAGED_FONTS
@ -69,12 +82,33 @@ const auto PersianFontTypes = std::array{
bool Started = false; bool Started = false;
QMap<QString, int> fontFamilyMap; base::flat_map<QString, int> FontFamilyIndices;
QVector<QString> fontFamilies; std::vector<QString> FontFamilies;
QMap<uint32, FontData*> fontsMap; base::flat_map<uint32, std::unique_ptr<ResolvedFont>> FontsByKey;
base::flat_map<uint64, uint32> QtFontsKeys;
uint32 fontKey(int size, uint32 flags, int family) { [[nodiscard]] uint32 FontKey(int size, FontFlags flags, int family) {
return (((uint32(family) << 12) | uint32(size)) << 6) | flags; return (uint32(family) << 18)
| (uint32(size) << 6)
| uint32(flags.value());
}
[[nodiscard]] uint64 QtFontKey(const QFont &font) {
static auto Families = base::flat_map<QString, int>();
const auto pixelSize = font.pixelSize();
const auto family = font.family();
auto i = Families.find(family);
if (i == end(Families)) {
i = Families.emplace(family, Families.size()).first;
}
return (uint64(i->second) << 24)
| (uint64(font.weight()) << 16)
| (uint64(font.bold() ? 1 : 0) << 15)
| (uint64(font.italic() ? 1 : 0) << 14)
| (uint64(font.underline() ? 1 : 0) << 13)
| (uint64(font.strikeOut() ? 1 : 0) << 12)
| (uint64(font.pixelSize()));
} }
#ifndef LIB_UI_USE_PACKAGED_FONTS #ifndef LIB_UI_USE_PACKAGED_FONTS
@ -135,106 +169,161 @@ bool LoadCustomFont(const QString &filePath) {
return family; return family;
} }
[[nodiscard]] int ComputePixelSize(QFont font, uint32 flags, int size) { struct Metrics {
int pixelSize = 0;
float64 ascent = 0.;
float64 height = 0.;
};
[[nodiscard]] Metrics ComputeMetrics(QFont font, bool adjust) {
constexpr auto kMaxSizeShift = 8; constexpr auto kMaxSizeShift = 8;
const auto startSize = font.pixelSize();
const auto metrics = QFontMetricsF(font);
const auto simple = [&] {
return Metrics{
.pixelSize = startSize,
.ascent = metrics.ascent(),
.height = metrics.height(),
};
};
const auto family = font.family(); const auto family = font.family();
const auto basic = u"Open Sans"_q; const auto basic = u"Open Sans"_q;
if (family == basic) { if (family == basic) {
return size; return simple();
} }
font.setPixelSize(size);
auto copy = font; auto copy = font;
copy.setFamily(basic); copy.setFamily(basic);
const auto basicMetrics = QFontMetricsF(copy); const auto basicMetrics = QFontMetricsF(copy);
//static const auto Test = u"bdfghijklpqtyBDFGHIJKLPQTY1234567890[]{}()"_q; static const auto Full = u"bdfghijklpqtyBDFGHIJKLPQTY1234567890[]{}()"_q;
static const auto Test = u"acemnorsuvwxz"_q; static const auto Test = u"acemnorsuvwxz"_q;
const auto desired = basicMetrics.tightBoundingRect(Test).height(); const auto desired = basicMetrics.tightBoundingRect(Test).height();
//const auto tightAscent = -tightRect.y(); const auto desiredFull = basicMetrics.tightBoundingRect(Full);
//const auto tightDescent = tightRect.y() + tightRect.height(); if (desired < 1. || desiredFull.height() < desired) {
return simple();
if (desired < 1.) {
return size;
} }
const auto currentMetrics = QFontMetricsF(font);
auto current = currentMetrics.tightBoundingRect(Test).height(); const auto adjusted = [&](int size, const QFontMetricsF &metrics) {
//const auto tightAscent = -tightRect.y(); const auto full = metrics.tightBoundingRect(Full);
//const auto tightDescent = tightRect.y() + tightRect.height(); const auto desiredTightAscent = -desiredFull.y();
const auto desiredTightHeight = desiredFull.height();
const auto ascentAdd = basicMetrics.ascent() - desiredTightAscent;
const auto heightAdd = basicMetrics.height() - desiredTightHeight;
const auto tightAscent = -full.y();
const auto tightHeight = full.height();
return Metrics{
.pixelSize = size,
.ascent = tightAscent + ascentAdd,
.height = tightHeight + heightAdd,
};
};
const auto max = std::min(kMaxSizeShift, size - 1); auto current = metrics.tightBoundingRect(Test).height();
if (current < 1. || std::abs(current - desired) < 0.2) { if (current < 1.) {
return size; return simple();
} else if (current < desired) { } else if (std::abs(current - desired) < 0.2) {
return adjusted(startSize, metrics);
}
const auto adjustedByFont = [&](const QFont &font) {
return adjusted(font.pixelSize(), QFontMetricsF(font));
};
const auto max = std::min(kMaxSizeShift, startSize - 1);
if (current < desired) {
for (auto i = 0; i != max; ++i) { for (auto i = 0; i != max; ++i) {
const auto shift = i + 1; const auto shift = i + 1;
font.setPixelSize(size + shift); font.setPixelSize(startSize + shift);
const auto metrics = QFontMetricsF(font); const auto metrics = QFontMetricsF(font);
const auto now = metrics.tightBoundingRect(Test).height(); const auto now = metrics.tightBoundingRect(Test).height();
if (now > desired) { if (now > desired) {
const auto better = (now - desired) < (desired - current); const auto better = (now - desired) < (desired - current);
return better ? (size + shift) : (size + shift - 1); if (better) {
return adjusted(startSize + shift, metrics);
}
font.setPixelSize(startSize + shift - 1);
return adjustedByFont(font);
} }
current = now; current = now;
} }
return size + max; font.setPixelSize(startSize + max);
return adjustedByFont(font);
} else { } else {
for (auto i = 0; i != max; ++i) { for (auto i = 0; i != max; ++i) {
const auto shift = i + 1; const auto shift = i + 1;
font.setPixelSize(size - shift); font.setPixelSize(startSize - shift);
const auto metrics = QFontMetricsF(font); const auto metrics = QFontMetricsF(font);
const auto now = metrics.tightBoundingRect(Test).height(); const auto now = metrics.tightBoundingRect(Test).height();
if (now < desired) { if (now < desired) {
const auto better = (desired - now) < (current - desired); const auto better = (desired - now) < (current - desired);
return better ? (size - shift) : (size - shift + 1); if (better) {
return adjusted(startSize - shift, metrics);
}
font.setPixelSize(startSize - shift + 1);
return adjustedByFont(font);
} }
current = now; current = now;
} }
return size - max; font.setPixelSize(startSize - max);
return adjustedByFont(font);
} }
} }
[[nodiscard]] QFont ResolveFont( [[nodiscard]] FontResolveResult ResolveFont(
const QString &family, const QString &family,
uint32 flags, FontFlags flags,
int size, int size,
bool skipSizeAdjustment) { bool skipSizeAdjustment) {
auto result = QFont(); auto font = QFont();
const auto monospace = (flags & FontMonospace) != 0;
const auto monospace = (flags & FontFlag::Monospace) != 0;
const auto system = !monospace && (family == SystemFontTag()); const auto system = !monospace && (family == SystemFontTag());
const auto overriden = !monospace && !system && !family.isEmpty(); const auto overriden = !monospace && !system && !family.isEmpty();
if (monospace) { if (monospace) {
result.setFamily(MonospaceFont()); font.setFamily(MonospaceFont());
} else if (system) { } else if (system) {
} else if (overriden) { } else if (overriden) {
result.setFamily(family); font.setFamily(family);
} else { } else {
result.setFamily("Open Sans"_q); font.setFamily("Open Sans"_q);
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
result.setFeature("ss03", true); font.setFeature("ss03", true);
#endif // Qt >= 6.7.0 #endif // Qt >= 6.7.0
} }
result.setWeight(((flags & FontBold) || (flags & FontSemibold)) font.setPixelSize(size);
font.setWeight((flags & (FontFlag::Bold | FontFlag::Semibold))
? QFont::DemiBold ? QFont::DemiBold
: QFont::Normal); : QFont::Normal);
if (result.bold()) { if (font.bold()) {
const auto style = QFontInfo(result).styleName(); const auto style = QFontInfo(font).styleName();
if (!style.isEmpty() && !style.startsWith( if (!style.isEmpty() && !style.startsWith(
"Semibold", "Semibold",
Qt::CaseInsensitive)) { Qt::CaseInsensitive)) {
result.setBold(true); font.setBold(true);
} }
} }
result.setItalic(flags & FontItalic);
result.setUnderline(flags & FontUnderline); font.setItalic(flags & FontFlag::Italic);
result.setStrikeOut(flags & FontStrikeOut); font.setUnderline(flags & FontFlag::Underline);
result.setPixelSize((monospace || !overriden || skipSizeAdjustment) font.setStrikeOut(flags & FontFlag::StrikeOut);
? size
: ComputePixelSize(result, flags, size)); const auto adjust = (overriden && !skipSizeAdjustment);
return result; const auto metrics = ComputeMetrics(font, adjust);
font.setPixelSize(metrics.pixelSize);
return {
.font = font,
.ascent = metrics.ascent,
.height = metrics.height,
.iascent = int(base::SafeRound(metrics.ascent)),
.iheight = int(base::SafeRound(metrics.height)),
.requestedFamily = RegisterFontFamily(family),
.requestedSize = size,
.requestedFlags = flags,
};
} }
} // namespace } // namespace
@ -276,74 +365,66 @@ void StartFonts() {
QApplication::setFont(appFont); QApplication::setFont(appFont);
} }
void destroyFonts() { void DestroyFonts() {
for (auto fontData : std::as_const(fontsMap)) { base::take(FontsByKey);
delete fontData;
}
fontsMap.clear();
} }
int registerFontFamily(const QString &family) { int RegisterFontFamily(const QString &family) {
auto result = fontFamilyMap.value(family, -1); auto i = FontFamilyIndices.find(family);
if (result < 0) { if (i == end(FontFamilyIndices)) {
result = fontFamilies.size(); i = FontFamilyIndices.emplace(family, FontFamilies.size()).first;
fontFamilyMap.insert(family, result); FontFamilies.push_back(family);
fontFamilies.push_back(family);
} }
return result; return i->second;
} }
FontData::FontData(int size, uint32 flags, int family, Font *other) FontData::FontData(const FontResolveResult &result, FontVariants *modified)
: f(ResolveFont( : f(result.font)
family ? fontFamilies[family] : Custom,
flags,
size,
family != 0))
, _m(f) , _m(f)
, _size(size) , _size(result.requestedSize)
, _flags(flags) , _family(result.requestedFamily)
, _family(family) { , _flags(result.requestedFlags) {
if (other) { if (modified) {
memcpy(_modified, other, sizeof(_modified)); memcpy(&_modified, modified, sizeof(_modified));
} }
_modified[_flags] = Font(this); _modified[int(_flags)] = Font(this);
height = int(base::SafeRound(_m.height())); height = int(base::SafeRound(result.height));
ascent = int(base::SafeRound(_m.ascent())); ascent = int(base::SafeRound(result.ascent));
descent = int(base::SafeRound(_m.descent())); descent = height - ascent;
spacew = width(QLatin1Char(' ')); spacew = width(QLatin1Char(' '));
elidew = width(u"..."_q); elidew = width(u"..."_q);
} }
Font FontData::bold(bool set) const { Font FontData::bold(bool set) const {
return otherFlagsFont(FontBold, set); return otherFlagsFont(FontFlag::Bold, set);
} }
Font FontData::italic(bool set) const { Font FontData::italic(bool set) const {
return otherFlagsFont(FontItalic, set); return otherFlagsFont(FontFlag::Italic, set);
} }
Font FontData::underline(bool set) const { Font FontData::underline(bool set) const {
return otherFlagsFont(FontUnderline, set); return otherFlagsFont(FontFlag::Underline, set);
} }
Font FontData::strikeout(bool set) const { Font FontData::strikeout(bool set) const {
return otherFlagsFont(FontStrikeOut, set); return otherFlagsFont(FontFlag::StrikeOut, set);
} }
Font FontData::semibold(bool set) const { Font FontData::semibold(bool set) const {
return otherFlagsFont(FontSemibold, set); return otherFlagsFont(FontFlag::Semibold, set);
} }
Font FontData::monospace(bool set) const { Font FontData::monospace(bool set) const {
return otherFlagsFont(FontMonospace, set); return otherFlagsFont(FontFlag::Monospace, set);
} }
int FontData::size() const { int FontData::size() const {
return _size; return _size;
} }
uint32 FontData::flags() const { FontFlags FontData::flags() const {
return _flags; return _flags;
} }
@ -351,50 +432,61 @@ int FontData::family() const {
return _family; return _family;
} }
Font FontData::otherFlagsFont(uint32 flag, bool set) const { Font FontData::otherFlagsFont(FontFlag flag, bool set) const {
int32 newFlags = set ? (_flags | flag) : (_flags & ~flag); const auto newFlags = set ? (_flags | flag) : (_flags & ~flag);
if (!_modified[newFlags].v()) { if (!_modified[newFlags]) {
_modified[newFlags] = Font(_size, newFlags, _family, _modified); _modified[newFlags] = Font(_size, newFlags, _family, &_modified);
} }
return _modified[newFlags]; return _modified[newFlags];
} }
Font::Font(int size, uint32 flags, const QString &family) { Font::Font(int size, FontFlags flags, const QString &family) {
if (fontFamilyMap.isEmpty()) { init(size, flags, RegisterFontFamily(family), 0);
for (uint32 i = 0, s = fontFamilies.size(); i != s; ++i) {
fontFamilyMap.insert(fontFamilies.at(i), i);
}
} }
auto i = fontFamilyMap.constFind(family); Font::Font(int size, FontFlags flags, int family) {
if (i == fontFamilyMap.cend()) {
fontFamilies.push_back(family);
i = fontFamilyMap.insert(family, fontFamilies.size() - 1);
}
init(size, flags, i.value(), 0);
}
Font::Font(int size, uint32 flags, int family) {
init(size, flags, family, 0); init(size, flags, family, 0);
} }
Font::Font(int size, uint32 flags, int family, Font *modified) { Font::Font(int size, FontFlags flags, int family, FontVariants *modified) {
init(size, flags, family, modified); init(size, flags, family, modified);
} }
void Font::init(int size, uint32 flags, int family, Font *modified) { void Font::init(
uint32 key = fontKey(size, flags, family); int size,
auto i = fontsMap.constFind(key); FontFlags flags,
if (i == fontsMap.cend()) { int family,
i = fontsMap.insert(key, new FontData(size, flags, family, modified)); FontVariants *modified) {
const auto key = FontKey(size, flags, family);
auto i = FontsByKey.find(key);
if (i == end(FontsByKey)) {
i = FontsByKey.emplace(
key,
std::make_unique<ResolvedFont>(
ResolveFont(
family ? FontFamilies[family] : Custom,
flags,
size,
family != 0),
modified)).first;
QtFontsKeys.emplace(QtFontKey(i->second->data.f), key);
} }
ptr = i.value(); _data = &i->second->data;
}
OwnedFont::OwnedFont(const QString &custom, FontFlags flags, int size)
: _data(ResolveFont(custom, flags, size, false), nullptr) {
_font._data = &_data;
} }
} // namespace internal } // namespace internal
QFont ResolveFont(const QString &custom, uint32 flags, int size) { const FontResolveResult *FindAdjustResult(const QFont &font) {
return internal::ResolveFont(custom, flags, size, false); const auto key = internal::QtFontKey(font);
const auto i = internal::QtFontsKeys.find(key);
return (i != end(internal::QtFontsKeys))
? &internal::FontsByKey[i->second]->result
: nullptr;
} }
} // namespace style } // namespace style

View file

@ -7,6 +7,7 @@
#pragma once #pragma once
#include "base/basic_types.h" #include "base/basic_types.h"
#include "base/flags.h"
#include <QtGui/QFont> #include <QtGui/QFont>
#include <QtGui/QFontMetrics> #include <QtGui/QFontMetrics>
@ -18,61 +19,75 @@ namespace style {
[[nodiscard]] const QString &SystemFontTag(); [[nodiscard]] const QString &SystemFontTag();
void SetCustomFont(const QString &font); void SetCustomFont(const QString &font);
[[nodiscard]] QFont ResolveFont( enum class FontFlag : uchar {
const QString &custom, Bold = 0x01,
uint32 flags, Italic = 0x02,
int size); Underline = 0x04,
StrikeOut = 0x08,
Semibold = 0x10,
Monospace = 0x20,
};
inline constexpr bool is_flag_type(FontFlag) { return true; }
using FontFlags = base::flags<FontFlag>;
struct FontResolveResult {
QFont font;
float64 ascent = 0.;
float64 height = 0.;
int iascent = 0;
int iheight = 0;
int requestedFamily = 0;
int requestedSize = 0;
FontFlags requestedFlags;
};
[[nodiscard]] const FontResolveResult *FindAdjustResult(const QFont &font);
namespace internal { namespace internal {
void StartFonts(); void StartFonts();
void destroyFonts(); void DestroyFonts();
int registerFontFamily(const QString &family); int RegisterFontFamily(const QString &family);
inline constexpr auto kFontVariants = 0x40;
class Font;
using FontVariants = std::array<Font, kFontVariants>;
class FontData; class FontData;
class Font { class Font final {
public: public:
Font(Qt::Initialization = Qt::Uninitialized) { Font(Qt::Initialization = Qt::Uninitialized) {
} }
Font(int size, uint32 flags, const QString &family); Font(int size, FontFlags flags, const QString &family);
Font(int size, uint32 flags, int family); Font(int size, FontFlags flags, int family);
FontData *operator->() const { [[nodiscard]] FontData *operator->() const {
return ptr; return _data;
} }
FontData *v() const { [[nodiscard]] FontData *get() const {
return ptr; return _data;
} }
operator bool() const { [[nodiscard]] operator bool() const {
return !!ptr; return _data != nullptr;
} }
operator const QFont &() const; [[nodiscard]] operator const QFont &() const;
private: private:
FontData *ptr = nullptr;
void init(int size, uint32 flags, int family, Font *modified);
friend void startManager();
Font(FontData *p) : ptr(p) {
}
Font(int size, uint32 flags, int family, Font *modified);
friend class FontData; friend class FontData;
friend class OwnedFont;
}; FontData *_data = nullptr;
enum FontFlags { void init(int size, FontFlags flags, int family, FontVariants *modified);
FontBold = 0x01, friend void StartManager();
FontItalic = 0x02,
FontUnderline = 0x04, explicit Font(FontData *data) : _data(data) {
FontStrikeOut = 0x08, }
FontSemibold = 0x10, Font(int size, FontFlags flags, int family, FontVariants *modified);
FontMonospace = 0x20,
FontDifferentFlags = 0x40,
}; };
class FontData { class FontData {
@ -100,37 +115,74 @@ public:
[[nodiscard]] Font semibold(bool set = true) const; [[nodiscard]] Font semibold(bool set = true) const;
[[nodiscard]] Font monospace(bool set = true) const; [[nodiscard]] Font monospace(bool set = true) const;
int size() const; [[nodiscard]] int size() const;
uint32 flags() const; [[nodiscard]] FontFlags flags() const;
int family() const; [[nodiscard]] int family() const;
QFont f; QFont f;
int32 height, ascent, descent, spacew, elidew; int height = 0;
int ascent = 0;
int descent = 0;
int spacew = 0;
int elidew = 0;
private: private:
mutable Font _modified[FontDifferentFlags]; friend class OwnedFont;
friend struct ResolvedFont;
Font otherFlagsFont(uint32 flag, bool set) const; mutable FontVariants _modified;
FontData(int size, uint32 flags, int family, Font *other);
[[nodiscard]] Font otherFlagsFont(FontFlag flag, bool set) const;
FontData(const FontResolveResult &data, FontVariants *modified);
friend class Font;
QFontMetricsF _m; QFontMetricsF _m;
int _size; int _size = 0;
uint32 _flags; int _family = 0;
int _family; FontFlags _flags = 0;
}; };
inline bool operator==(const Font &a, const Font &b) { inline bool operator==(const Font &a, const Font &b) {
return a.v() == b.v(); return a.get() == b.get();
} }
inline bool operator!=(const Font &a, const Font &b) { inline bool operator!=(const Font &a, const Font &b) {
return a.v() != b.v(); return a.get() != b.get();
} }
inline Font::operator const QFont &() const { inline Font::operator const QFont &() const {
return ptr->f; Expects(_data != nullptr);
return _data->f;
} }
class OwnedFont final {
public:
OwnedFont(const QString &custom, FontFlags flags, int size);
OwnedFont(const OwnedFont &other)
: _data(other._data) {
_font._data = &_data;
}
OwnedFont &operator=(const OwnedFont &other) {
_data = other._data;
return *this;
}
[[nodiscard]] const Font &font() const {
return _font;
}
[[nodiscard]] FontData *operator->() const {
return _font.get();
}
[[nodiscard]] FontData *get() const {
return _font.get();
}
private:
FontData _data;
Font _font;
};
} // namespace internal } // namespace internal
} // namespace style } // namespace style

View file

@ -393,14 +393,14 @@ Icon Icon::withPalette(const style::palette &palette) const {
return result; return result;
} }
void resetIcons() { void ResetIcons() {
iconPixmaps.clear(); iconPixmaps.clear();
for (const auto data : iconData) { for (const auto data : iconData) {
data->reset(); data->reset();
} }
} }
void destroyIcons() { void DestroyIcons() {
iconData.clear(); iconData.clear();
iconPixmaps.clear(); iconPixmaps.clear();

View file

@ -295,8 +295,8 @@ private:
}; };
void resetIcons(); void ResetIcons();
void destroyIcons(); void DestroyIcons();
} // namespace internal } // namespace internal
} // namespace style } // namespace style

View file

@ -226,7 +226,7 @@ QByteArray save() {
bool load(const QByteArray &cache) { bool load(const QByteArray &cache) {
if (GetMutable().load(cache)) { if (GetMutable().load(cache)) {
style::internal::resetIcons(); style::internal::ResetIcons();
return true; return true;
} }
return false; return false;
@ -242,17 +242,17 @@ palette::SetResult setColor(QLatin1String name, QLatin1String from) {
void apply(const palette &other) { void apply(const palette &other) {
GetMutable() = other; GetMutable() = other;
style::internal::resetIcons(); style::internal::ResetIcons();
} }
void reset() { void reset() {
GetMutable().reset(); GetMutable().reset();
style::internal::resetIcons(); style::internal::ResetIcons();
} }
void reset(const colorizer &with) { void reset(const colorizer &with) {
GetMutable().reset(with); GetMutable().reset(with);
style::internal::resetIcons(); style::internal::ResetIcons();
} }
int indexOfColor(color c) { int indexOfColor(color c) {

View file

@ -27,6 +27,7 @@ using cursor = Qt::CursorShape;
using align = Qt::Alignment; using align = Qt::Alignment;
using margins = QMargins; using margins = QMargins;
using font = internal::Font; using font = internal::Font;
using owned_font = internal::OwnedFont;
using color = internal::Color; using color = internal::Color;
using owned_color = internal::OwnedColor; using owned_color = internal::OwnedColor;
using complex_color = internal::ComplexColor; using complex_color = internal::ComplexColor;

View file

@ -373,28 +373,31 @@ bool BlockParser::isSpaceBreak(
style::font WithFlags( style::font WithFlags(
const style::font &font, const style::font &font,
TextBlockFlags flags, TextBlockFlags flags,
uint32 fontFlags) { style::FontFlags fontFlags) {
using namespace style::internal; using namespace style::internal;
using Flag = style::FontFlag;
if (!flags && !fontFlags) { if (!flags && !fontFlags) {
return font; return font;
} else if (IsMono(flags) || (fontFlags & FontMonospace)) { } else if (IsMono(flags) || (fontFlags & Flag::Monospace)) {
return font->monospace(); return font->monospace();
} }
auto result = font; auto result = font;
if ((flags & TextBlockFlag::Bold) || (fontFlags & FontBold)) { if ((flags & TextBlockFlag::Bold) || (fontFlags & Flag::Bold)) {
result = result->bold(); result = result->bold();
} else if ((flags & TextBlockFlag::Semibold) } else if ((flags & TextBlockFlag::Semibold)
|| (fontFlags & FontSemibold)) { || (fontFlags & Flag::Semibold)) {
result = result->semibold(); result = result->semibold();
} }
if ((flags & TextBlockFlag::Italic) || (fontFlags & FontItalic)) { if ((flags & TextBlockFlag::Italic) || (fontFlags & Flag::Italic)) {
result = result->italic(); result = result->italic();
} }
if ((flags & TextBlockFlag::Underline) || (fontFlags & FontUnderline)) { if ((flags & TextBlockFlag::Underline)
|| (fontFlags & Flag::Underline)) {
result = result->underline(); result = result->underline();
} }
if ((flags & TextBlockFlag::StrikeOut) || (fontFlags & FontStrikeOut)) { if ((flags & TextBlockFlag::StrikeOut)
|| (fontFlags & Flag::StrikeOut)) {
result = result->strikeout(); result = result->strikeout();
} }
if (flags & TextBlockFlag::Tilde) { // Tilde fix in OpenSans. if (flags & TextBlockFlag::Tilde) { // Tilde fix in OpenSans.

View file

@ -47,7 +47,7 @@ using TextBlockFlags = base::flags<TextBlockFlag>;
[[nodiscard]] style::font WithFlags( [[nodiscard]] style::font WithFlags(
const style::font &font, const style::font &font,
TextBlockFlags flags, TextBlockFlags flags,
uint32 fontFlags = 0); style::FontFlags fontFlags = 0);
[[nodiscard]] Qt::LayoutDirection UnpackParagraphDirection( [[nodiscard]] Qt::LayoutDirection UnpackParagraphDirection(
bool ltr, bool ltr,

View file

@ -3912,7 +3912,9 @@ void InputField::refreshPlaceholder(const QString &text) {
_placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth); _placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth);
_placeholderPath = QPainterPath(); _placeholderPath = QPainterPath();
if (!_placeholder.isEmpty()) { if (!_placeholder.isEmpty()) {
_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder); const auto result = style::FindAdjustResult(placeholderFont);
const auto ascent = result ? result->iascent : metrics.ascent();
_placeholderPath.addText(0, ascent, placeholderFont, _placeholder);
} }
} else { } else {
_placeholder = _st.placeholderFont->elided(text, availableWidth); _placeholder = _st.placeholderFont->elided(text, availableWidth);

View file

@ -390,7 +390,9 @@ void MaskedInputField::refreshPlaceholder(const QString &text) {
_placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth); _placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth);
_placeholderPath = QPainterPath(); _placeholderPath = QPainterPath();
if (!_placeholder.isEmpty()) { if (!_placeholder.isEmpty()) {
_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder); const auto result = style::FindAdjustResult(placeholderFont);
const auto ascent = result ? result->iascent : metrics.ascent();
_placeholderPath.addText(0, ascent, placeholderFont, _placeholder);
} }
} else { } else {
_placeholder = _st.placeholderFont->elided(text, availableWidth); _placeholder = _st.placeholderFont->elided(text, availableWidth);

View file

@ -20,10 +20,13 @@ namespace Ui {
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html
ScrollShadow::ScrollShadow(ScrollArea *parent, const style::ScrollArea *st) : QWidget(parent), _st(st) { ScrollShadow::ScrollShadow(ScrollArea *parent, const style::ScrollArea *st)
: QWidget(parent)
, _st(st) {
Expects(_st != nullptr);
Expects(_st->shColor.get() != nullptr);
setVisible(false); setVisible(false);
Assert(_st != nullptr);
Assert(_st->shColor.v() != nullptr);
} }
void ScrollShadow::paintEvent(QPaintEvent *e) { void ScrollShadow::paintEvent(QPaintEvent *e) {