Add ligatures button to Text tool (#4679)

Doesn't fully fix the issue as we cannot move the cursor between the
ligature clusters, but at least we can disable the ligatures now.
This commit is contained in:
David Capello 2024-09-25 16:37:41 -03:00
parent e700cce987
commit 5c7a3a41f6
14 changed files with 121 additions and 47 deletions

2
laf

@ -1 +1 @@
Subproject commit 851bdbc2454aa381131cd0972781aa86e149504a
Subproject commit 61aba133e6de5197cdf490696ae7aba5f56273fd

View File

@ -26,24 +26,24 @@ FontInfo::FontInfo(Type type,
const std::string& name,
const float size,
const text::FontStyle style,
const bool antialias)
const Flags flags)
: m_type(type)
, m_name(name)
, m_size(size)
, m_style(style)
, m_antialias(antialias)
, m_flags(flags)
{
}
FontInfo::FontInfo(const FontInfo& other,
const float size,
const text::FontStyle style,
const bool antialias)
const Flags flags)
: m_type(other.type())
, m_name(other.name())
, m_size(size)
, m_style(style)
, m_antialias(antialias)
, m_flags(flags)
{
}
@ -89,7 +89,8 @@ FontInfo FontInfo::getFromPreferences()
pref.textTool.fontFace(),
pref.textTool.fontSize(),
text::FontStyle(),
pref.textTool.antialias());
pref.textTool.antialias() ? FontInfo::Flags::Antialias :
FontInfo::Flags::None);
}
// New configuration
if (!pref.textTool.fontInfo().empty()) {
@ -124,7 +125,7 @@ template<> app::FontInfo convert_to(const std::string& from)
float size = 0.0f;
bool bold = false;
bool italic = false;
bool antialias = false;
app::FontInfo::Flags flags = app::FontInfo::Flags::None;
if (!parts.empty()) {
if (parts[0].compare(0, 5, "file=") == 0) {
@ -141,7 +142,9 @@ template<> app::FontInfo convert_to(const std::string& from)
}
for (int i=1; i<parts.size(); ++i) {
if (parts[i] == "antialias")
antialias = true;
flags |= app::FontInfo::Flags::Antialias;
else if (parts[i] == "ligatures")
flags |= app::FontInfo::Flags::Ligatures;
else if (parts[i] == "bold")
bold = true;
else if (parts[i] == "italic")
@ -157,7 +160,7 @@ template<> app::FontInfo convert_to(const std::string& from)
else if (bold) style = text::FontStyle::Bold();
else if (italic) style = text::FontStyle::Italic();
return app::FontInfo(type, name, size, style, antialias);
return app::FontInfo(type, name, size, style, flags);
}
template<> std::string convert_to(const app::FontInfo& from)
@ -186,6 +189,8 @@ template<> std::string convert_to(const app::FontInfo& from)
result += ",italic";
if (from.antialias())
result += ",antialias";
if (from.ligatures())
result += ",ligatures";
}
return result;
}

View File

@ -9,6 +9,7 @@
#pragma once
#include "base/convert_to.h"
#include "base/enum_flags.h"
#include "text/font_style.h"
#include "text/fwd.h"
#include "text/typeface.h"
@ -28,18 +29,24 @@ namespace app {
System,
};
enum class Flags {
None = 0,
Antialias = 1,
Ligatures = 2,
};
static constexpr const float kDefaultSize = 0.0f;
FontInfo(Type type = Type::Unknown,
const std::string& name = {},
float size = kDefaultSize,
text::FontStyle style = text::FontStyle(),
bool antialias = false);
Flags flags = Flags::None);
FontInfo(const FontInfo& other,
float size,
text::FontStyle style,
bool antialias);
Flags flags);
bool isValid() const { return m_type != Type::Unknown; }
bool useDefaultSize() const { return m_size == kDefaultSize; }
@ -57,7 +64,9 @@ namespace app {
float size() const { return m_size; }
text::FontStyle style() const { return m_style; }
bool antialias() const { return m_antialias; }
Flags flags() const { return m_flags; }
bool antialias() const;
bool ligatures() const;
text::TypefaceRef findTypeface(const text::FontMgrRef& fontMgr) const;
@ -68,7 +77,7 @@ namespace app {
return (m_type == other.m_type &&
m_name == other.m_name &&
std::fabs(m_size-other.m_size) < 0.001f &&
m_antialias == other.m_antialias);
m_flags == other.m_flags);
}
private:
@ -76,9 +85,19 @@ namespace app {
std::string m_name;
float m_size = kDefaultSize;
text::FontStyle m_style;
bool m_antialias = false;
Flags m_flags = Flags::None;
};
LAF_ENUM_FLAGS(FontInfo::Flags);
inline bool FontInfo::antialias() const {
return (m_flags & Flags::Antialias) == Flags::Antialias;
}
inline bool FontInfo::ligatures() const {
return (m_flags & Flags::Ligatures) == Flags::Ligatures;
}
} // namespace app
namespace base {

View File

@ -47,9 +47,10 @@ TEST(FontInfo, ByFile)
TEST(FontInfo, BySystem)
{
FontInfo a(FontInfo::Type::System, "FreeMono");
FontInfo b(FontInfo::Type::System, "DejaVu Serif", 12, text::FontStyle(), true);
FontInfo c(FontInfo::Type::System, "Arial", 14, text::FontStyle::Bold(), false);
FontInfo d(FontInfo::Type::System, "Arial", 16, text::FontStyle::BoldItalic(), false);
FontInfo b(FontInfo::Type::System, "DejaVu Serif", 12, text::FontStyle(),
FontInfo::Flags::Antialias);
FontInfo c(FontInfo::Type::System, "Arial", 14, text::FontStyle::Bold());
FontInfo d(FontInfo::Type::System, "Arial", 16, text::FontStyle::BoldItalic());
EXPECT_EQ("system=FreeMono", base::convert_to<std::string>(a));
EXPECT_EQ("system=DejaVu Serif,size=12,antialias", base::convert_to<std::string>(b));
EXPECT_EQ("system=Arial,size=14,bold", base::convert_to<std::string>(c));

View File

@ -148,6 +148,13 @@ private:
onNewTextBlob();
}
text::ShaperFeatures onGetTextShaperFeatures() const override {
const FontInfo fontInfo = App::instance()->contextBar()->fontInfo();
text::ShaperFeatures features;
features.ligatures = fontInfo.ligatures();
return features;
}
void onNewTextBlob() {
text::TextBlobRef blob = textBlob();
if (!blob)

View File

@ -144,16 +144,25 @@ FontEntry::FontStyle::FontStyle()
setMultiMode(MultiMode::Set);
}
FontEntry::FontLigatures::FontLigatures()
: ButtonSet(1, true)
{
addItem("fi");
setMultiMode(MultiMode::Set);
}
FontEntry::FontEntry()
: m_antialias("Antialias")
{
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));
@ -162,7 +171,7 @@ FontEntry::FontEntry()
setInfo(FontInfo(newTypeName,
m_info.size(),
m_info.style(),
m_info.antialias()),
m_info.flags()),
From::Face);
invalidate();
});
@ -172,7 +181,7 @@ FontEntry::FontEntry()
setInfo(FontInfo(m_info,
newSize,
m_info.style(),
m_info.antialias()),
m_info.flags()),
From::Size);
});
@ -202,17 +211,21 @@ FontEntry::FontEntry()
setInfo(FontInfo(m_info,
m_info.size(),
style,
m_info.antialias()),
m_info.flags()),
From::Style);
});
m_antialias.Click.connect([this](){
setInfo(FontInfo(m_info,
m_info.size(),
m_info.style(),
m_antialias.isSelected()),
From::Antialias);
});
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);
}
// Defined here as FontPopup type is not fully defined in the header
@ -236,8 +249,10 @@ void FontEntry::setInfo(const FontInfo& info,
m_style.getItem(1)->setSelected(info.style().slant() != text::FontStyle::Slant::Upright);
}
if (fromField != From::Antialias)
if (fromField != From::Flags) {
m_ligatures.getItem(0)->setSelected(info.ligatures());
m_antialias.setSelected(info.antialias());
}
FontChange(m_info);
}

View File

@ -27,7 +27,7 @@ namespace app {
Face,
Size,
Style,
Antialias,
Flags,
};
FontEntry();
@ -63,10 +63,16 @@ namespace app {
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;
};

View File

@ -64,7 +64,8 @@ public:
: ListItem(name)
, m_fontInfo(FontInfo::Type::Name, name,
FontInfo::kDefaultSize,
text::FontStyle(), true) {
text::FontStyle(),
FontInfo::Flags::Antialias) {
getCachedThumbnail();
}
@ -72,7 +73,8 @@ public:
: ListItem(base::get_file_title(fn))
, m_fontInfo(FontInfo::Type::File, fn,
FontInfo::kDefaultSize,
text::FontStyle(), true) {
text::FontStyle(),
FontInfo::Flags::Antialias) {
getCachedThumbnail();
}
@ -81,7 +83,8 @@ public:
const text::FontStyleSetRef& set)
: ListItem(name)
, m_fontInfo(FontInfo::Type::System, name,
FontInfo::kDefaultSize, style, true)
FontInfo::kDefaultSize, style,
FontInfo::Flags::Antialias)
, m_set(set) {
getCachedThumbnail();
}

View File

@ -180,8 +180,10 @@ doc::ImageRef render_text(
// fonts (e.g. if the given font is not enough to shape other code
// points/languages).
MeasureHandler handler;
text::ShaperFeatures features;
features.ligatures = fontInfo.ligatures();
text::TextBlobRef blob =
text::TextBlob::MakeWithShaper(fontMgr, font, text, &handler);
text::TextBlob::MakeWithShaper(fontMgr, font, text, &handler, features);
if (!blob)
return nullptr;

View File

@ -958,7 +958,9 @@ void Entry::recalcCharBoxes(const std::string& text)
text::draw_text(nullptr,
theme()->fontMgr(),
base::AddRef(font()), text,
gfx::ColorNone, gfx::ColorNone, 0, 0, &delegate).w;
gfx::ColorNone, gfx::ColorNone, 0, 0,
&delegate,
onGetTextShaperFeatures()).w;
m_boxes = delegate.boxes();
if (!m_boxes.empty()) {

View File

@ -26,6 +26,7 @@
#include "text/draw_text.h"
#include "text/font.h"
#include "text/font_metrics.h"
#include "text/shaper_features.h"
#include "ui/display.h"
#include "ui/scale.h"
#include "ui/theme.h"
@ -338,7 +339,8 @@ void Graphics::setFont(const text::FontRef& font)
void Graphics::drawText(const std::string& str,
gfx::Color fg, gfx::Color bg,
const gfx::Point& origPt,
text::DrawTextDelegate* delegate)
text::DrawTextDelegate* delegate,
text::ShaperFeatures features)
{
if (str.empty())
return;
@ -348,7 +350,7 @@ void Graphics::drawText(const std::string& str,
os::SurfaceLock lock(m_surface.get());
gfx::Rect textBounds =
text::draw_text(m_surface.get(), get_theme()->fontMgr(),
m_font, str, fg, bg, pt.x, pt.y, delegate);
m_font, str, fg, bg, pt.x, pt.y, delegate, features);
dirty(gfx::Rect(pt.x, pt.y, textBounds.w, textBounds.h));
}

View File

@ -17,6 +17,8 @@
#include "gfx/size.h"
#include "os/surface.h"
#include "text/font.h"
#include "text/fwd.h"
#include "text/shaper_features.h"
#include "ui/paint.h"
#include <memory>
@ -32,10 +34,6 @@ namespace os {
struct Sampling;
}
namespace text {
class DrawTextDelegate;
}
namespace ui {
class Display;
@ -110,7 +108,8 @@ namespace ui {
void drawText(const std::string& str,
gfx::Color fg, gfx::Color bg,
const gfx::Point& pt,
text::DrawTextDelegate* delegate = nullptr);
text::DrawTextDelegate* delegate = nullptr,
text::ShaperFeatures features = {});
void drawUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Point& pt, const int mnemonic);
void drawAlignedUIText(const std::string& str, gfx::Color fg, gfx::Color bg, const gfx::Rect& rc, const int align);

View File

@ -929,12 +929,8 @@ void Widget::getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags)
text::TextBlobRef Widget::textBlob() const
{
if (!m_blob && !m_text.empty()) {
m_blob = text::TextBlob::MakeWithShaper(
theme()->fontMgr(),
AddRef(font()),
m_text);
}
if (!m_blob && !m_text.empty())
m_blob = onMakeTextBlob();
return m_blob;
}
@ -1789,6 +1785,21 @@ double Widget::onGetTextDouble() const
return std::strtod(m_text.c_str(), nullptr);
}
text::TextBlobRef Widget::onMakeTextBlob() const
{
return text::TextBlob::MakeWithShaper(
theme()->fontMgr(),
AddRef(font()),
text(),
nullptr,
onGetTextShaperFeatures());
}
text::ShaperFeatures Widget::onGetTextShaperFeatures() const
{
return text::ShaperFeatures();
}
void Widget::offsetWidgets(int dx, int dy)
{
if (dx == 0 && dy == 0)

View File

@ -442,6 +442,8 @@ namespace ui {
virtual void onSetBgColor();
virtual int onGetTextInt() const;
virtual double onGetTextDouble() const;
virtual text::TextBlobRef onMakeTextBlob() const;
virtual text::ShaperFeatures onGetTextShaperFeatures() const;
private:
void removeChild(const WidgetsList::iterator& it);