diff --git a/data/strings/en.ini b/data/strings/en.ini
index acb8657f1..5636bc57a 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -785,6 +785,11 @@ load = Load External Font
select_truetype_fonts = Select a Font File
empty_fonts = No system fonts were found
+[font_style]
+antialias = Antialias
+hinting = Hinting
+ligatures = Ligatures
+
[frame_combo]
all_frames = All frames
selected_frames = Selected frames
diff --git a/data/widgets/font_style.xml b/data/widgets/font_style.xml
new file mode 100644
index 000000000..92284caed
--- /dev/null
+++ b/data/widgets/font_style.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/fonts/font_data.cpp b/src/app/fonts/font_data.cpp
index 02d6e2851..9cad37bb0 100644
--- a/src/app/fonts/font_data.cpp
+++ b/src/app/fonts/font_data.cpp
@@ -32,17 +32,11 @@ text::FontRef FontData::getFont(text::FontMgrRef& fontMgr, float size)
{
ASSERT(fontMgr);
- // Use cache
- if (m_antialias) {
- auto it = m_antialiasFonts.find(size);
- if (it != m_antialiasFonts.end())
- return it->second;
- }
- else {
- auto it = m_fonts.find(size);
- if (it != m_fonts.end())
- return it->second;
- }
+ // Use cached fonts
+ const Cache::Key cacheKey{ size, m_antialias, m_hinting != text::FontHinting::None };
+ auto it = m_cache.fonts.find(cacheKey);
+ if (it != m_cache.fonts.end())
+ return it->second; // Cache hit
text::FontRef font = nullptr;
@@ -55,12 +49,18 @@ text::FontRef FontData::getFont(text::FontMgrRef& fontMgr, float size)
break;
case text::FontType::FreeType: {
font = fontMgr->loadTrueTypeFont(m_filename.c_str(), size);
- if (font)
+ if (font) {
font->setAntialias(m_antialias);
+ font->setHinting(m_hinting);
+ }
break;
}
}
+ // Cache this font
+ m_cache.fonts[cacheKey] = font;
+
+ // Load fallback
if (m_fallback) {
text::FontRef fallback = m_fallback->getFont(fontMgr, m_fallbackSize);
if (font)
@@ -68,12 +68,6 @@ text::FontRef FontData::getFont(text::FontMgrRef& fontMgr, float size)
else
return fallback; // Don't double-cache the fallback font
}
-
- // Cache this font
- if (m_antialias)
- m_antialiasFonts[size] = font;
- else
- m_fonts[size] = font;
return font;
}
diff --git a/src/app/fonts/font_data.h b/src/app/fonts/font_data.h
index 7e0e6d524..3e75d8ee5 100644
--- a/src/app/fonts/font_data.h
+++ b/src/app/fonts/font_data.h
@@ -39,11 +39,25 @@ public:
const std::string& filename() const { return m_filename; }
private:
+ // Cache of loaded fonts so we avoid re-loading them.
+ struct Cache {
+ struct Key {
+ float size;
+ bool antialias : 1;
+ bool hinting : 1;
+ bool operator<(const Key& b) const
+ {
+ return size < b.size || antialias < b.antialias || hinting < b.hinting;
+ }
+ };
+ std::map fonts;
+ };
+
text::FontType m_type;
std::string m_filename;
bool m_antialias;
- std::map m_fonts; // key=font size, value=real font
- std::map m_antialiasFonts;
+ text::FontHinting m_hinting = text::FontHinting::Normal;
+ Cache m_cache;
FontData* m_fallback;
float m_fallbackSize;
float m_descent = 0.0f;
diff --git a/src/app/fonts/font_info.cpp b/src/app/fonts/font_info.cpp
index 6355e9bd1..41b1896d0 100644
--- a/src/app/fonts/font_info.cpp
+++ b/src/app/fonts/font_info.cpp
@@ -27,24 +27,28 @@ FontInfo::FontInfo(Type type,
const std::string& name,
const float size,
const text::FontStyle style,
- const Flags flags)
+ const Flags flags,
+ const text::FontHinting hinting)
: m_type(type)
, m_name(name)
, m_size(size)
, m_style(style)
, m_flags(flags)
+ , m_hinting(hinting)
{
}
FontInfo::FontInfo(const FontInfo& other,
const float size,
const text::FontStyle style,
- const Flags flags)
+ const Flags flags,
+ text::FontHinting hinting)
: m_type(other.type())
, m_name(other.name())
, m_size(size)
, m_style(style)
, m_flags(flags)
+ , m_hinting(hinting)
{
}
@@ -130,6 +134,12 @@ std::string FontInfo::humanString() const
result += " Antialias";
if (ligatures())
result += " Ligatures";
+ switch (hinting()) {
+ case text::FontHinting::None: result += " No Hinting"; break;
+ case text::FontHinting::Slight: result += " Slight Hinting"; break;
+ case text::FontHinting::Normal: break;
+ case text::FontHinting::Full: result += " Full Hinting"; break;
+ }
}
return result;
}
@@ -150,6 +160,7 @@ app::FontInfo convert_to(const std::string& from)
bool bold = false;
bool italic = false;
app::FontInfo::Flags flags = app::FontInfo::Flags::None;
+ text::FontHinting hinting = text::FontHinting::Normal;
if (!parts.empty()) {
if (parts[0].compare(0, 5, "file=") == 0) {
@@ -176,6 +187,17 @@ app::FontInfo convert_to(const std::string& from)
else if (parts[i].compare(0, 5, "size=") == 0) {
size = std::strtof(parts[i].substr(5).c_str(), nullptr);
}
+ else if (parts[i].compare(0, 8, "hinting=") == 0) {
+ std::string hintingStr = parts[i].substr(8);
+ if (hintingStr == "none")
+ hinting = text::FontHinting::None;
+ else if (hintingStr == "slight")
+ hinting = text::FontHinting::Slight;
+ else if (hintingStr == "normal")
+ hinting = text::FontHinting::Normal;
+ else if (hintingStr == "full")
+ hinting = text::FontHinting::Full;
+ }
}
}
@@ -187,7 +209,7 @@ app::FontInfo convert_to(const std::string& from)
else if (italic)
style = text::FontStyle::Italic();
- return app::FontInfo(type, name, size, style, flags);
+ return app::FontInfo(type, name, size, style, flags, hinting);
}
template<>
@@ -213,6 +235,17 @@ std::string convert_to(const app::FontInfo& from)
result += ",antialias";
if (from.ligatures())
result += ",ligatures";
+ if (from.hinting() != text::FontHinting::Normal) {
+ result += ",hinting=";
+ switch (from.hinting()) {
+ case text::FontHinting::None: result += "none"; break;
+ case text::FontHinting::Slight: result += "slight"; break;
+ case text::FontHinting::Normal:
+ // Filtered out by above if
+ break;
+ case text::FontHinting::Full: result += "full"; break;
+ }
+ }
}
return result;
}
diff --git a/src/app/fonts/font_info.h b/src/app/fonts/font_info.h
index ed7ad0a10..d89463fe6 100644
--- a/src/app/fonts/font_info.h
+++ b/src/app/fonts/font_info.h
@@ -10,6 +10,7 @@
#include "base/convert_to.h"
#include "base/enum_flags.h"
+#include "text/font_hinting.h"
#include "text/font_style.h"
#include "text/font_type.h"
#include "text/fwd.h"
@@ -49,9 +50,14 @@ public:
const std::string& name = {},
float size = kDefaultSize,
text::FontStyle style = text::FontStyle(),
- Flags flags = Flags::None);
+ Flags flags = Flags::None,
+ text::FontHinting hinting = text::FontHinting::Normal);
- FontInfo(const FontInfo& other, float size, text::FontStyle style, Flags flags);
+ FontInfo(const FontInfo& other,
+ float size,
+ text::FontStyle style,
+ Flags flags,
+ text::FontHinting hinting);
bool isValid() const { return m_type != Type::Unknown; }
bool useDefaultSize() const { return m_size == kDefaultSize; }
@@ -72,6 +78,7 @@ public:
Flags flags() const { return m_flags; }
bool antialias() const;
bool ligatures() const;
+ text::FontHinting hinting() const { return m_hinting; }
text::TypefaceRef findTypeface(const text::FontMgrRef& fontMgr) const;
@@ -92,6 +99,7 @@ private:
float m_size = kDefaultSize;
text::FontStyle m_style;
Flags m_flags = Flags::None;
+ text::FontHinting m_hinting = text::FontHinting::Normal;
};
LAF_ENUM_FLAGS(FontInfo::Flags);
diff --git a/src/app/fonts/fonts.cpp b/src/app/fonts/fonts.cpp
index ecab39dba..b8b188a23 100644
--- a/src/app/fonts/fonts.cpp
+++ b/src/app/fonts/fonts.cpp
@@ -82,8 +82,10 @@ text::FontRef Fonts::fontFromInfo(const FontInfo& fontInfo)
}
}
- if (font)
+ if (font) {
font->setAntialias(fontInfo.antialias());
+ font->setHinting(fontInfo.hinting());
+ }
return font;
}
diff --git a/src/app/ui/font_entry.cpp b/src/app/ui/font_entry.cpp
index f3894c326..90a2ede03 100644
--- a/src/app/ui/font_entry.cpp
+++ b/src/app/ui/font_entry.cpp
@@ -19,10 +19,13 @@
#include "base/scoped_value.h"
#include "fmt/format.h"
#include "ui/display.h"
+#include "ui/fit_bounds.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/scale.h"
+#include "font_style.xml.h"
+
#include
#include
#include
@@ -248,31 +251,22 @@ void FontEntry::FontSize::onEntryChange()
Change();
}
-FontEntry::FontStyle::FontStyle() : ButtonSet(2, true)
+FontEntry::FontStyle::FontStyle() : ButtonSet(3, true)
{
addItem("B");
addItem("I");
+ addItem("...");
setMultiMode(MultiMode::Set);
}
-FontEntry::FontLigatures::FontLigatures() : ButtonSet(1, true)
-{
- addItem("fi");
- setMultiMode(MultiMode::Set);
-}
-
-FontEntry::FontEntry() : m_antialias("Antialias")
+FontEntry::FontEntry()
{
m_face.setExpansive(true);
m_size.setExpansive(false);
m_style.setExpansive(false);
- m_ligatures.setExpansive(false);
- m_antialias.setExpansive(false);
addChild(&m_face);
addChild(&m_size);
addChild(&m_style);
- addChild(&m_ligatures);
- addChild(&m_antialias);
m_face.setMinSize(gfx::Size(128 * guiscale(), 0));
@@ -280,52 +274,20 @@ FontEntry::FontEntry() : m_antialias("Antialias")
if (newTypeName.size() > 0)
setInfo(newTypeName, from);
else {
- setInfo(FontInfo(newTypeName, m_info.size(), m_info.style(), m_info.flags()), from);
+ setInfo(
+ FontInfo(newTypeName, m_info.size(), m_info.style(), m_info.flags(), m_info.hinting()),
+ from);
}
invalidate();
});
m_size.Change.connect([this]() {
const float newSize = std::strtof(m_size.getValue().c_str(), nullptr);
- setInfo(FontInfo(m_info, newSize, m_info.style(), m_info.flags()), From::Size);
+ setInfo(FontInfo(m_info, newSize, m_info.style(), m_info.flags(), m_info.hinting()),
+ From::Size);
});
- m_style.ItemChange.connect([this](ButtonSet::Item* item) {
- text::FontStyle style = m_info.style();
- switch (m_style.getItemIndex(item)) {
- // Bold button changed
- case 0: {
- const bool bold = m_style.getItem(0)->isSelected();
- style = text::FontStyle(
- bold ? text::FontStyle::Weight::Bold : text::FontStyle::Weight::Normal,
- style.width(),
- style.slant());
- break;
- }
- // Italic button changed
- case 1: {
- const bool italic = m_style.getItem(1)->isSelected();
- style = text::FontStyle(
- style.weight(),
- style.width(),
- italic ? text::FontStyle::Slant::Italic : text::FontStyle::Slant::Upright);
- break;
- }
- }
-
- setInfo(FontInfo(m_info, m_info.size(), style, m_info.flags()), From::Style);
- });
-
- auto flagsChange = [this]() {
- FontInfo::Flags flags = FontInfo::Flags::None;
- if (m_antialias.isSelected())
- flags |= FontInfo::Flags::Antialias;
- if (m_ligatures.getItem(0)->isSelected())
- flags |= FontInfo::Flags::Ligatures;
- setInfo(FontInfo(m_info, m_info.size(), m_info.style(), flags), From::Flags);
- };
- m_ligatures.ItemChange.connect(flagsChange);
- m_antialias.Click.connect(flagsChange);
+ m_style.ItemChange.connect(&FontEntry::onStyleItemClick, this);
}
// Defined here as FontPopup type is not fully defined in the header
@@ -351,12 +313,84 @@ void FontEntry::setInfo(const FontInfo& info, const From fromField)
m_style.getItem(1)->setSelected(info.style().slant() != text::FontStyle::Slant::Upright);
}
- if (fromField != From::Flags) {
- m_ligatures.getItem(0)->setSelected(info.ligatures());
- m_antialias.setSelected(info.antialias());
- }
-
FontChange(m_info, fromField);
}
+void FontEntry::onStyleItemClick(ButtonSet::Item* item)
+{
+ text::FontStyle style = m_info.style();
+
+ switch (m_style.getItemIndex(item)) {
+ // Bold button changed
+ case 0: {
+ const bool bold = m_style.getItem(0)->isSelected();
+ style = text::FontStyle(
+ bold ? text::FontStyle::Weight::Bold : text::FontStyle::Weight::Normal,
+ style.width(),
+ style.slant());
+
+ setInfo(FontInfo(m_info, m_info.size(), style, m_info.flags(), m_info.hinting()),
+ From::Style);
+ break;
+ }
+ // Italic button changed
+ case 1: {
+ const bool italic = m_style.getItem(1)->isSelected();
+ style = text::FontStyle(
+ style.weight(),
+ style.width(),
+ italic ? text::FontStyle::Slant::Italic : text::FontStyle::Slant::Upright);
+
+ setInfo(FontInfo(m_info, m_info.size(), style, m_info.flags(), m_info.hinting()),
+ From::Style);
+ break;
+ }
+ case 2: {
+ item->setSelected(false); // Unselect the "..." button
+
+ ui::PopupWindow popup;
+ app::gen::FontStyle content;
+
+ content.antialias()->setSelected(m_info.antialias());
+ content.ligatures()->setSelected(m_info.ligatures());
+ content.hinting()->setSelected(m_info.hinting() == text::FontHinting::Normal);
+
+ auto flagsChange = [this, &content]() {
+ FontInfo::Flags flags = FontInfo::Flags::None;
+ if (content.antialias()->isSelected())
+ flags |= FontInfo::Flags::Antialias;
+ if (content.ligatures()->isSelected())
+ flags |= FontInfo::Flags::Ligatures;
+ setInfo(FontInfo(m_info, m_info.size(), m_info.style(), flags, m_info.hinting()),
+ From::Flags);
+ };
+
+ auto hintingChange = [this, &content]() {
+ auto hinting = (content.hinting()->isSelected() ? text::FontHinting::Normal :
+ text::FontHinting::None);
+
+ setInfo(FontInfo(m_info, m_info.size(), m_info.style(), m_info.flags(), hinting),
+ From::Hinting);
+ };
+
+ content.antialias()->Click.connect(flagsChange);
+ content.ligatures()->Click.connect(flagsChange);
+ content.hinting()->Click.connect(hintingChange);
+
+ popup.addChild(&content);
+ popup.remapWindow();
+
+ gfx::Rect rc = item->bounds();
+ rc.y += rc.h - popup.border().bottom();
+
+ ui::fit_bounds(display(), &popup, gfx::Rect(rc.origin(), popup.sizeHint()));
+
+ popup.Open.connect([&popup] { popup.setHotRegion(gfx::Region(popup.boundsOnScreen())); });
+
+ popup.openWindowInForeground();
+ break;
+ }
+ }
+}
+
} // namespace app
diff --git a/src/app/ui/font_entry.h b/src/app/ui/font_entry.h
index 5481338ae..570accdec 100644
--- a/src/app/ui/font_entry.h
+++ b/src/app/ui/font_entry.h
@@ -28,6 +28,7 @@ public:
Size,
Style,
Flags,
+ Hinting,
Popup,
};
@@ -40,6 +41,8 @@ public:
obs::signal FontChange;
private:
+ void onStyleItemClick(ButtonSet::Item* item);
+
class FontFace : public SearchEntry {
public:
FontFace();
@@ -73,17 +76,10 @@ private:
FontStyle();
};
- class FontLigatures : public ButtonSet {
- public:
- FontLigatures();
- };
-
FontInfo m_info;
FontFace m_face;
FontSize m_size;
FontStyle m_style;
- FontLigatures m_ligatures;
- ui::CheckBox m_antialias;
bool m_lockFace = false;
};
diff --git a/src/app/ui/font_popup.cpp b/src/app/ui/font_popup.cpp
index ba22ddc52..7cfcf2cfb 100644
--- a/src/app/ui/font_popup.cpp
+++ b/src/app/ui/font_popup.cpp
@@ -181,7 +181,8 @@ private:
const FontInfo fontInfoDefSize(m_fontInfo,
FontInfo::kDefaultSize,
text::FontStyle(),
- FontInfo::Flags::Antialias);
+ FontInfo::Flags::Antialias,
+ text::FontHinting::Normal);
const text::FontRef font = fonts->fontFromInfo(fontInfoDefSize);
if (!font)
return;