[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[freetype2-demos] gsoc-2022-chariri-2 c913540 24/30: [ftinspect] WIP: Ad
From: |
Werner Lemberg |
Subject: |
[freetype2-demos] gsoc-2022-chariri-2 c913540 24/30: [ftinspect] WIP: Add initial support to "Continuous View". |
Date: |
Mon, 11 Jul 2022 07:17:41 -0400 (EDT) |
branch: gsoc-2022-chariri-2
commit c913540245e87620abe00e36742620e0cf948eb6
Author: Charlie Jiang <w@chariri.moe>
Commit: Charlie Jiang <w@chariri.moe>
[ftinspect] WIP: Add initial support to "Continuous View".
This commit adds a tab to the main GUI named "Continuous View",
corresponding to the `ftview` tool. Inside the tab is a glyph canvas and
another tab view. The tab view is used to switch modes and input settings.
The rendering canvas is *not* a `QGraphicsView`, but a simple `QWidget` with
a custom `paintEvent`. This is because we don't need features like panning
and zooming, so using `QGraphicsView` will only add complexity (e.g.
transforming coordination systems).
Code translating FreeType outline to the origin for rendering is extracted
as a helper function in a new "render utils" file.
The font size selector (size, unit, DPI) in the original `SingularTab` is
extracted as a new class `FontSizeSelector`.
The "graphics defaults", which are graphics objects like color palettes and
`QPen`s, are all extracted into a new singleton struct `GraphicsDefaults`,
so every component can easily obtain them.
Currently there're a lot of settings, status variables and parameters during
the rendering process. Will write some documentation clearifing them all.
WIP: Only "all glyphs" view is implemented.
TODO: Fancy, Stroked, Text String mode; LCD Anti-Aliasing; Bitmap font;
The rendering code is adopted from singular rendering, which is different
from the code in `ftcommon.c` from the legacy tools. In the future we may
need to re-implement this with code from the latter.
* src/ftinspect/rendering/glyphcontinuous.cpp,
src/ftinspect/rendering/glyphcontinuous.hpp: New file: the actual
"rendering canvas" in the "Continuous View". The code was a combination of
`ftcommon` and the singular view's render views. Mouse scrolling is
supported for navigation and resizing.
* src/ftinspect/panels/continuous.cpp, src/ftinspect/panels/continuous.hpp:
New files: the content of the "Continuous View" tab is a `ContinuousTab`.
It current only support one single mode and submode.
Settings about "All Glyphs" mode is placed in `ContinousAllGlyphsTab`.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp: Add
CharMap handling code, especially `CharMapInfo` struct. `CharMapInfo` can
provide the name and max index for the charmap. Vector of `CharMapInfo`
for current font is obtained via `Engine::currentFontCharMaps`.
CharMap mapping is done via `CharMapInfo::glyphIndexFromCharCode`, with
the added `FTC_CMapCache`.
Add more actions to support continuous view. `reloadFont` and
`loadGlyphWithoutUpdate` can load fonts and glyphs with minimal overhead.
`loadGlyphWithoutUpdate` uses `FTC_ImageCache_Lookup`, so the
`FTC_ImageTypeRec` object is cached.
* src/ftinspect/widgets/fontsizeselector.cpp,
src/ftinspect/widgets/fontsizeselector.hpp: New file, include the font
size setting part. It can directly apply settings to the engine via
`applyToEngine` function, and can handle wheel event from event object or
by steps count.
* src/ftinspect/widgets/glyphindexselector.cpp,
src/ftinspect/widgets/glyphindexselector.hpp: Combine `setMax` and
`setMin` functions, and reduce unnecessary signals by blocking them.
Rename `getCurrentIndex` to `currentIndex`.
Support custom number rendering (for Unicode charcode formatting). Adjust
layout and disabled wrapping on the spinbox.
* src/ftinspect/rendering/graphicsdefault.cpp,
src/ftinspect/rendering/graphicsdefault.hpp: New file, including graphics
initializing code.
* src/ftinspect/rendering/renderutils.cpp,
src/ftinspect/rendering/renderutils.hpp: New file, including outline
translating code.
* src/ftinspect/rendering/glyphbitmap.cpp: Move out outline tranlating code.
* src/ftinspect/panels/singular.cpp, src/ftinspect/panels/singular.hpp: Move
out the font size selector and graphics defaults. Minor change due to
change in `GlyphIndexSelector`.
* src/ftinspect/maingui.cpp, src/ftinspect/maingui.hpp: Add "Continuous
View" to tab view.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
---
src/ftinspect/CMakeLists.txt | 5 +
src/ftinspect/engine/engine.cpp | 203 +++++++++++++++-
src/ftinspect/engine/engine.hpp | 41 +++-
src/ftinspect/maingui.cpp | 8 +
src/ftinspect/maingui.hpp | 2 +
src/ftinspect/meson.build | 8 +
src/ftinspect/panels/continuous.cpp | 347 +++++++++++++++++++++++++++
src/ftinspect/panels/continuous.hpp | 124 ++++++++++
src/ftinspect/panels/singular.cpp | 145 +++--------
src/ftinspect/panels/singular.hpp | 29 +--
src/ftinspect/rendering/glyphbitmap.cpp | 19 +-
src/ftinspect/rendering/glyphcontinuous.cpp | 212 ++++++++++++++++
src/ftinspect/rendering/glyphcontinuous.hpp | 77 ++++++
src/ftinspect/rendering/graphicsdefault.cpp | 48 ++++
src/ftinspect/rendering/graphicsdefault.hpp | 34 +++
src/ftinspect/rendering/renderutils.cpp | 34 +++
src/ftinspect/rendering/renderutils.hpp | 15 ++
src/ftinspect/widgets/fontsizeselector.cpp | 147 ++++++++++++
src/ftinspect/widgets/fontsizeselector.hpp | 58 +++++
src/ftinspect/widgets/glyphindexselector.cpp | 76 ++++--
src/ftinspect/widgets/glyphindexselector.hpp | 12 +-
21 files changed, 1454 insertions(+), 190 deletions(-)
diff --git a/src/ftinspect/CMakeLists.txt b/src/ftinspect/CMakeLists.txt
index c0b1340..92d6b34 100644
--- a/src/ftinspect/CMakeLists.txt
+++ b/src/ftinspect/CMakeLists.txt
@@ -27,15 +27,20 @@ add_executable(ftinspect
"rendering/glyphoutline.cpp"
"rendering/glyphpointnumbers.cpp"
"rendering/glyphpoints.cpp"
+ "rendering/glyphcontinuous.cpp"
"rendering/grid.cpp"
+ "rendering/graphicsdefault.cpp"
+ "rendering/renderutils.cpp"
"widgets/customwidgets.cpp"
"widgets/glyphindexselector.cpp"
+ "widgets/fontsizeselector.cpp"
"models/ttsettingscomboboxmodel.cpp"
"panels/settingpanel.cpp"
"panels/singular.cpp"
+ "panels/continuous.cpp"
)
target_link_libraries(ftinspect
Qt5::Core Qt5::Widgets
diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index 552b0a8..e4e7e42 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -148,6 +148,12 @@ Engine::Engine()
// XXX error handling
}
+ error = FTC_CMapCache_New(cacheManager_, &cmapCache_);
+ if (error)
+ {
+ // XXX error handling
+ }
+
queryEngine();
}
@@ -278,6 +284,8 @@ Engine::loadFont(int fontIndex,
}
}
+ imageType_.face_id = scaler_.face_id;
+
if (numGlyphs < 0)
{
ftSize_ = NULL;
@@ -286,16 +294,22 @@ Engine::loadFont(int fontIndex,
}
else
{
- curFamilyName_ = QString(ftSize_->face->family_name);
- curStyleName_ = QString(ftSize_->face->style_name);
+ auto face = ftSize_->face;
+ curFamilyName_ = QString(face->family_name);
+ curStyleName_ = QString(face->style_name);
- const char* moduleName = FT_FACE_DRIVER_NAME( ftSize_->face );
+ const char* moduleName = FT_FACE_DRIVER_NAME( face );
// XXX cover all available modules
if (!strcmp(moduleName, "cff"))
fontType_ = FontType_CFF;
else if (!strcmp(moduleName, "truetype"))
fontType_ = FontType_TrueType;
+
+ curCharMaps_.clear();
+ curCharMaps_.reserve(face->num_charmaps);
+ for (int i = 0; i < face->num_charmaps; i++)
+ curCharMaps_.append(CharMapInfo(i, face->charmaps[i]));
}
curNumGlyphs_ = numGlyphs;
@@ -303,6 +317,17 @@ Engine::loadFont(int fontIndex,
}
+void
+Engine::reloadFont()
+{
+ update();
+ if (!scaler_.face_id)
+ return;
+ imageType_.face_id = scaler_.face_id;
+ FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_);
+}
+
+
void
Engine::removeFont(int fontIndex, bool closeFile)
{
@@ -331,6 +356,22 @@ Engine::removeFont(int fontIndex, bool closeFile)
}
+unsigned
+Engine::glyphIndexFromCharCode(int code, int charMapIndex)
+{
+ if (charMapIndex == -1)
+ return code;
+ return FTC_CMapCache_Lookup(cmapCache_, scaler_.face_id, charMapIndex, code);
+}
+
+
+FT_Size_Metrics const&
+Engine::currentFontMetrics()
+{
+ return ftSize_->metrics;
+}
+
+
QString
Engine::glyphName(int index)
{
@@ -390,6 +431,26 @@ Engine::loadOutline(int glyphIndex)
}
+FT_Glyph
+Engine::loadGlyphWithoutUpdate(int glyphIndex)
+{
+ // TODO bitmap fonts? color layered fonts?
+ FT_Glyph glyph;
+ imageType_.flags |= FT_LOAD_NO_BITMAP;
+ if (FTC_ImageCache_Lookup(imageCache_,
+ &imageType_,
+ glyphIndex,
+ &glyph,
+ NULL))
+ {
+ // XXX error handling?
+ return NULL;
+ }
+
+ return glyph;
+}
+
+
int
Engine::numberOfOpenedFonts()
{
@@ -500,6 +561,10 @@ Engine::update()
scaler_.x_res = dpi_;
scaler_.y_res = dpi_;
}
+
+ imageType_.width = static_cast<unsigned int>(pixelSize_);
+ imageType_.height = static_cast<unsigned int>(pixelSize_);
+ imageType_.flags = static_cast<int>(loadFlags_);
}
@@ -602,4 +667,136 @@ Engine::queryEngine()
}
}
+
+QHash<FT_Encoding, QString> encodingNamesCache;
+QHash<FT_Encoding, QString>&
+encodingNames()
+{
+ if (encodingNamesCache.empty())
+ {
+ encodingNamesCache[static_cast<FT_Encoding>(FT_ENCODING_OTHER)]
+ = "Unknown Encoding";
+ encodingNamesCache[FT_ENCODING_NONE] = "No Encoding";
+ encodingNamesCache[FT_ENCODING_MS_SYMBOL] = "MS Symbol (symb)";
+ encodingNamesCache[FT_ENCODING_UNICODE] = "Unicode (unic)";
+ encodingNamesCache[FT_ENCODING_SJIS] = "Shift JIS (sjis)";
+ encodingNamesCache[FT_ENCODING_PRC] = "PRC/GB 18030 (gb)";
+ encodingNamesCache[FT_ENCODING_BIG5] = "Big5 (big5)";
+ encodingNamesCache[FT_ENCODING_WANSUNG] = "Wansung (wans)";
+ encodingNamesCache[FT_ENCODING_JOHAB] = "Johab (joha)";
+ encodingNamesCache[FT_ENCODING_ADOBE_STANDARD] = "Adobe Standard (ADOB)";
+ encodingNamesCache[FT_ENCODING_ADOBE_EXPERT] = "Adobe Expert (ADBE)";
+ encodingNamesCache[FT_ENCODING_ADOBE_CUSTOM] = "Adobe Custom (ADBC)";
+ encodingNamesCache[FT_ENCODING_ADOBE_LATIN_1] = "Latin 1 (lat1)";
+ encodingNamesCache[FT_ENCODING_OLD_LATIN_2] = "Latin 2 (lat2)";
+ encodingNamesCache[FT_ENCODING_APPLE_ROMAN] = "Apple Roman (armn)";
+ }
+
+ return encodingNamesCache;
+}
+
+
+CharMapInfo::CharMapInfo(int index, FT_CharMap cmap)
+: index(index), ptr(cmap), encoding(cmap->encoding), maxIndex(-1)
+{
+ auto& names = encodingNames();
+ auto it = names.find(encoding);
+ if (it == names.end())
+ encodingName = &names[static_cast<FT_Encoding>(FT_ENCODING_OTHER)];
+ else
+ encodingName = &it.value();
+
+ if (encoding != FT_ENCODING_OTHER)
+ maxIndex = computeMaxIndex();
+}
+
+
+QString
+CharMapInfo::stringifyIndex(int code, int index)
+{
+ return QString("CharCode: %1 (glyph idx %2)")
+ .arg(stringifyIndexShort(code))
+ .arg(index);
+}
+
+
+QString
+CharMapInfo::stringifyIndexShort(int code)
+{
+ return (encoding == FT_ENCODING_UNICODE ? "U+" : "0x")
+ + QString::number(code, 16).rightJustified(4, '0').toUpper();
+}
+
+
+int
+CharMapInfo::computeMaxIndex()
+{
+ int maxIndex = 0;
+ switch (encoding)
+ {
+ case FT_ENCODING_UNICODE:
+ maxIndex = maxIndexForFaceAndCharMap(ptr, 0x110000) + 1;
+ break;
+
+ case FT_ENCODING_ADOBE_LATIN_1:
+ case FT_ENCODING_ADOBE_STANDARD:
+ case FT_ENCODING_ADOBE_EXPERT:
+ case FT_ENCODING_ADOBE_CUSTOM:
+ case FT_ENCODING_APPLE_ROMAN:
+ maxIndex = 0x100;
+ break;
+
+ /* some fonts use range 0x00-0x100, others have 0xF000-0xF0FF */
+ case FT_ENCODING_MS_SYMBOL:
+ maxIndex = maxIndexForFaceAndCharMap(ptr, 0x10000) + 1;
+ break;
+
+ default:
+ // Some encodings can reach > 0x10000, e.g. GB 18030.
+ maxIndex = maxIndexForFaceAndCharMap(ptr, 0x110000) + 1;
+ }
+ return maxIndex;
+}
+
+
+int
+CharMapInfo::maxIndexForFaceAndCharMap(FT_CharMap charMap,
+ unsigned max)
+{
+ // code adopted from `ftcommon.c`
+ FT_ULong min = 0;
+ FT_UInt glyphIndex;
+ FT_Face face = charMap->face;
+
+ if (FT_Set_Charmap(face, charMap))
+ return -1;
+
+ do
+ {
+ FT_ULong mid = (min + max) >> 1;
+ FT_ULong res = FT_Get_Next_Char(face, mid, &glyphIndex);
+
+ if (glyphIndex)
+ min = res;
+ else
+ {
+ max = mid;
+
+ // once moved, it helps to advance min through sparse regions
+ if (min)
+ {
+ res = FT_Get_Next_Char(face, min, &glyphIndex);
+
+ if (glyphIndex)
+ min = res;
+ else
+ max = min; // found it
+ }
+ }
+ } while (max > min);
+
+ return static_cast<int>(max);
+}
+
+
// end of engine.cpp
diff --git a/src/ftinspect/engine/engine.hpp b/src/ftinspect/engine/engine.hpp
index 7a45094..80ac9ed 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -36,6 +36,29 @@ struct FaceID
bool operator<(const FaceID& other) const;
};
+class Engine;
+
+#define FT_ENCODING_OTHER 0xFFFE
+struct CharMapInfo
+{
+ int index;
+ FT_CharMap ptr;
+ FT_Encoding encoding;
+ QString* encodingName;
+
+ // Actually this shouldn't go here, but for convenience...
+ int maxIndex;
+
+ CharMapInfo(int index, FT_CharMap cmap);
+
+ QString stringifyIndex(int code, int index);
+ QString stringifyIndexShort(int code);
+
+private:
+ int computeMaxIndex();
+ static int maxIndexForFaceAndCharMap(FT_CharMap charMap, unsigned max);
+};
+
// FreeType specific data.
class Engine
@@ -70,6 +93,13 @@ public:
int namedInstanceIndex); // return number of glyphs
FT_Outline* loadOutline(int glyphIndex);
+ // Sometimes the engine is already updated, and we want to be faster
+ FT_Glyph loadGlyphWithoutUpdate(int glyphIndex);
+
+ // reload current triplet, but with updated settings, useful for updating
+ // `ftSize_` only
+ void reloadFont();
+
void openFonts(QStringList fontFileNames);
void removeFont(int fontIndex, bool closeFile = true);
@@ -87,14 +117,16 @@ public:
long numberOfFaces(int fontIndex);
int numberOfNamedInstances(int fontIndex,
long faceIndex);
+ // Note: the current font face must be properly set
+ unsigned glyphIndexFromCharCode(int code, int charMapIndex);
+ FT_Size_Metrics const& currentFontMetrics();
- // XXX We should prepend '_' to all private member variable so we can create
- // getter without naming conflict... e.g. var named _fontFileManager while
- // getter named fontFileManager
+ QVector<CharMapInfo>& currentFontCharMaps() { return curCharMaps_; }
FontFileManager& fontFileManager() { return fontFileManager_; }
EngineDefaultValues& engineDefaults() { return engineDefaults_; }
bool antiAliasingEnabled() { return antiAliasingEnabled_; }
+
//////// Setters (direct or indirect)
void setDPI(int d) { dpi_ = d; }
@@ -142,14 +174,17 @@ private:
QString curFamilyName_;
QString curStyleName_;
int curNumGlyphs_ = -1;
+ QVector<CharMapInfo> curCharMaps_;
FT_Library library_;
FTC_Manager cacheManager_;
FTC_ImageCache imageCache_;
FTC_SBitCache sbitsCache_;
+ FTC_CMapCache cmapCache_;
FTC_ScalerRec scaler_;
FT_Size ftSize_;
+ FTC_ImageTypeRec imageType_;
EngineDefaultValues engineDefaults_;
diff --git a/src/ftinspect/maingui.cpp b/src/ftinspect/maingui.cpp
index e1fbf6f..2c78245 100644
--- a/src/ftinspect/maingui.cpp
+++ b/src/ftinspect/maingui.cpp
@@ -15,6 +15,8 @@
#include <freetype/ftdriver.h>
+#include "panels/continuous.hpp"
+
MainGUI::MainGUI(Engine* engine)
: engine_(engine)
@@ -448,12 +450,15 @@ MainGUI::createLayout()
fontNameLabel_ = new QLabel(this);
singularTab_ = new SingularTab(this, engine_);
+ continuousTab_ = new ContinuousTab(this, engine_);
tabWidget_ = new QTabWidget(this);
// Note those two list must be in sync
tabs_.append(singularTab_);
tabWidget_->addTab(singularTab_, tr("Singular Grid View"));
+ tabs_.append(continuousTab_);
+ tabWidget_->addTab(continuousTab_, tr("Continuous View"));
previousFontButton_ = new QPushButton(tr("Previous Font"), this);
nextFontButton_ = new QPushButton(tr("Next Font"), this);
@@ -504,6 +509,9 @@ MainGUI::createConnections()
connect(settingPanel_, &SettingPanel::repaintNeeded,
this, &MainGUI::repaintCurrentTab);
+ connect(tabWidget_, &QTabWidget::currentChanged,
+ this, &MainGUI::reloadCurrentTabFont);
+
connect(previousFontButton_, &QPushButton::clicked,
this, &MainGUI::previousFont);
connect(nextFontButton_, &QPushButton::clicked,
diff --git a/src/ftinspect/maingui.hpp b/src/ftinspect/maingui.hpp
index 5e9f9e7..ff08176 100644
--- a/src/ftinspect/maingui.hpp
+++ b/src/ftinspect/maingui.hpp
@@ -11,6 +11,7 @@
#include "models/ttsettingscomboboxmodel.hpp"
#include "panels/settingpanel.hpp"
#include "panels/singular.hpp"
+#include "panels/continuous.hpp"
#include <QAction>
#include <QCheckBox>
@@ -136,6 +137,7 @@ private:
QTabWidget* tabWidget_;
QVector<AbstractTab*> tabs_;
SingularTab* singularTab_;
+ ContinuousTab* continuousTab_;
void openFonts(QStringList const& fileNames);
diff --git a/src/ftinspect/meson.build b/src/ftinspect/meson.build
index de0e7e9..f41e731 100644
--- a/src/ftinspect/meson.build
+++ b/src/ftinspect/meson.build
@@ -27,14 +27,19 @@ if qt5_dep.found()
'rendering/glyphoutline.cpp',
'rendering/glyphpointnumbers.cpp',
'rendering/glyphpoints.cpp',
+ 'rendering/glyphcontinuous.cpp',
'rendering/grid.cpp',
+ 'rendering/graphicsdefault.cpp',
+ 'rendering/renderutils.cpp',
'widgets/customwidgets.cpp',
'widgets/glyphindexselector.cpp',
+ 'widgets/fontsizeselector.cpp',
'models/ttsettingscomboboxmodel.cpp',
'panels/settingpanel.cpp',
'panels/singular.cpp',
+ 'panels/continuous.cpp',
'ftinspect.cpp',
'maingui.cpp',
@@ -46,9 +51,12 @@ if qt5_dep.found()
'engine/fontfilemanager.hpp',
'widgets/customwidgets.hpp',
'widgets/glyphindexselector.hpp',
+ 'widgets/fontsizeselector.hpp',
+ 'widgets/glyphcontinuous.hpp',
'models/ttsettingscomboboxmodel.hpp',
'panels/settingpanel.hpp',
'panels/singular.hpp',
+ 'panels/continuous.hpp',
'maingui.hpp',
],
dependencies: qt5_dep)
diff --git a/src/ftinspect/panels/continuous.cpp
b/src/ftinspect/panels/continuous.cpp
new file mode 100644
index 0000000..d24ff87
--- /dev/null
+++ b/src/ftinspect/panels/continuous.cpp
@@ -0,0 +1,347 @@
+// continuous.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "continuous.hpp"
+
+#include <climits>
+#include <QVariant>
+
+
+ContinuousTab::ContinuousTab(QWidget* parent,
+ Engine* engine)
+: QWidget(parent), engine_(engine)
+{
+ createLayout();
+ createConnections();
+}
+
+
+void
+ContinuousTab::repaintGlyph()
+{
+ sizeSelector_->applyToEngine(engine_);
+
+ updateFromCurrentSubTab();
+ canvas_->repaint();
+}
+
+
+void
+ContinuousTab::reloadFont()
+{
+ currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
+ updateCurrentSubTab();
+ repaintGlyph();
+}
+
+
+void
+ContinuousTab::syncSettings()
+{
+}
+
+
+void
+ContinuousTab::changeTab()
+{
+ updateCurrentSubTab();
+ repaintGlyph();
+}
+
+
+void
+ContinuousTab::wheelNavigate(int steps)
+{
+ if (tabWidget_->currentIndex() == AllGlyphs)
+ allGlyphsTab_->setGlyphBeginindex(allGlyphsTab_->glyphBeginindex()
+ + steps);
+}
+
+
+void
+ContinuousTab::wheelResize(int steps)
+{
+ sizeSelector_->handleWheelResizeBySteps(steps);
+}
+
+
+void
+ContinuousTab::createLayout()
+{
+ canvas_ = new GlyphContinuous(this, engine_);
+ sizeSelector_ = new FontSizeSelector(this);
+ allGlyphsTab_ = new ContinousAllGlyphsTab(this);
+
+ tabWidget_ = new QTabWidget(this);
+ tabWidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ // Must be in sync with `Tabs` enum.
+ tabWidget_->addTab(allGlyphsTab_, tr("All Glyphs"));
+
+ mainLayout_ = new QVBoxLayout;
+ mainLayout_->addWidget(canvas_);
+ mainLayout_->addWidget(sizeSelector_);
+ mainLayout_->addWidget(tabWidget_);
+
+ setLayout(mainLayout_);
+}
+
+
+void
+ContinuousTab::createConnections()
+{
+ connect(tabWidget_, &QTabWidget::currentChanged,
+ this, &ContinuousTab::changeTab);
+
+ connect(allGlyphsTab_, &ContinousAllGlyphsTab::changed,
+ this, &ContinuousTab::repaintGlyph);
+
+ connect(sizeSelector_, &FontSizeSelector::valueChanged,
+ this, &ContinuousTab::repaintGlyph);
+
+ connect(canvas_, &GlyphContinuous::wheelResize,
+ this, &ContinuousTab::wheelResize);
+ connect(canvas_, &GlyphContinuous::wheelNavigate,
+ this, &ContinuousTab::wheelNavigate);
+ connect(canvas_, &GlyphContinuous::displayingCountUpdated,
+ allGlyphsTab_, &ContinousAllGlyphsTab::setDisplayingCount);
+}
+
+
+void
+ContinuousTab::setDefaults()
+{
+}
+
+
+void
+ContinuousTab::updateCurrentSubTab()
+{
+ switch (tabWidget_->currentIndex())
+ {
+ case AllGlyphs:
+ allGlyphsTab_->setGlyphCount(qBound(0,
+ currentGlyphCount_,
+ INT_MAX));
+ allGlyphsTab_->setCharMaps(engine_->currentFontCharMaps());
+ break;
+ }
+}
+
+
+void
+ContinuousTab::updateFromCurrentSubTab()
+{
+ switch (tabWidget_->currentIndex())
+ {
+ case AllGlyphs:
+ // Begin index is selected from All Glyphs subtab,
+ // and Limit index is calculated by All Glyphs subtab
+ canvas_->setBeginIndex(allGlyphsTab_->glyphBeginindex());
+ canvas_->setLimitIndex(allGlyphsTab_->glyphLimitIndex());
+ canvas_->setMode(GlyphContinuous::AllGlyphs);
+ canvas_->setSubModeAllGlyphs(allGlyphsTab_->subMode());
+ canvas_->setCharMapIndex(allGlyphsTab_->charMapIndex());
+ break;
+ }
+}
+
+
+ContinousAllGlyphsTab::ContinousAllGlyphsTab(QWidget* parent)
+: QWidget(parent)
+{
+ createLayout();
+
+ QVector<CharMapInfo> tempCharMaps;
+ setCharMaps(tempCharMaps); // pass in an empty one
+
+ createConnections();
+}
+
+
+int
+ContinousAllGlyphsTab::glyphBeginindex()
+{
+ return indexSelector_->currentIndex();
+}
+
+
+int
+ContinousAllGlyphsTab::glyphLimitIndex()
+{
+ return glyphLimitIndex_;
+}
+
+
+GlyphContinuous::SubModeAllGlyphs
+ContinousAllGlyphsTab::subMode()
+{
+ return static_cast<GlyphContinuous::SubModeAllGlyphs>(
+ modeSelector_->currentIndex());
+}
+
+
+int
+ContinousAllGlyphsTab::charMapIndex()
+{
+ auto index = charMapSelector_->currentIndex() - 1;
+ if (index <= -1)
+ return -1;
+ if (index >= charMaps_.size())
+ return -1;
+ return index;
+}
+
+
+void
+ContinousAllGlyphsTab::setGlyphBeginindex(int index)
+{
+ indexSelector_->setCurrentIndex(index);
+ updateCharMapLimit();
+}
+
+
+void
+ContinousAllGlyphsTab::setDisplayingCount(int count)
+{
+ indexSelector_->setShowingCount(count);
+}
+
+
+#define EncodingRole (Qt::UserRole + 10)
+void
+ContinousAllGlyphsTab::setCharMaps(QVector<CharMapInfo>& charMaps)
+{
+ charMaps_ = charMaps;
+ int oldIndex = charMapSelector_->currentIndex();
+ unsigned oldEncoding = 0u;
+
+ // Using additional UserRole to store encoding id
+ auto oldEncodingV = charMapSelector_->itemData(oldIndex, EncodingRole);
+ if (oldEncodingV.isValid() && oldEncodingV.canConvert<unsigned>())
+ {
+ oldEncoding = oldEncodingV.value<unsigned>();
+ }
+
+ {
+ // suppress events during updating
+ QSignalBlocker selectorBlocker(charMapSelector_);
+
+ charMapSelector_->clear();
+ charMapSelector_->addItem(tr("Glyph Order"));
+ charMapSelector_->setItemData(0, 0u, EncodingRole);
+
+ int i = 0;
+ int newIndex = 0;
+ for (auto& map : charMaps)
+ {
+ charMapSelector_->addItem(tr("%1: %2")
+ .arg(i)
+ .arg(*map.encodingName));
+ auto encoding = static_cast<unsigned>(map.encoding);
+ charMapSelector_->setItemData(i, encoding, EncodingRole);
+
+ if (encoding == oldEncoding && i == oldIndex)
+ newIndex = i;
+
+ i++;
+ }
+
+ // this shouldn't emit any event either, because force repainting
+ // will happen later, so embrace it into blocker block
+ charMapSelector_->setCurrentIndex(newIndex);
+ }
+
+ updateCharMapLimit();
+}
+
+
+void
+ContinousAllGlyphsTab::updateCharMapLimit()
+{
+ if (charMapSelector_->currentIndex() <= 0)
+ glyphLimitIndex_ = currentGlyphCount_;
+ else
+ glyphLimitIndex_
+ = charMaps_[charMapSelector_->currentIndex() - 1].maxIndex + 1;
+ indexSelector_->setMinMax(0, glyphLimitIndex_ - 1);
+}
+
+
+void
+ContinousAllGlyphsTab::createLayout()
+{
+ indexSelector_ = new GlyphIndexSelector(this);
+ indexSelector_->setSingleMode(false);
+ indexSelector_->setNumberRenderer([this](int index)
+ { return formatIndex(index); });
+
+ modeSelector_ = new QComboBox(this);
+ charMapSelector_ = new QComboBox(this);
+
+ // Note: in sync with the enum!!
+ modeSelector_->insertItem(GlyphContinuous::AG_AllGlyphs, tr("All Glyphs"));
+ modeSelector_->insertItem(GlyphContinuous::AG_Fancy, tr("Fancy"));
+ modeSelector_->insertItem(GlyphContinuous::AG_Stroked, tr("Stroked"));
+ modeSelector_->insertItem(GlyphContinuous::AG_Waterfall, tr("Waterfall"));
+ modeSelector_->setCurrentIndex(GlyphContinuous::AG_AllGlyphs);
+
+ modeLabel_ = new QLabel(tr("Mode:"), this);
+ charMapLabel_ = new QLabel(tr("Char Map:"), this);
+
+ layout_ = new QGridLayout;
+ layout_->addWidget(indexSelector_, 0, 0, 1, 2);
+ layout_->addWidget(modeLabel_, 1, 0);
+ layout_->addWidget(charMapLabel_, 2, 0);
+ layout_->addWidget(modeSelector_, 1, 1);
+ layout_->addWidget(charMapSelector_, 2, 1);
+
+ layout_->setColumnStretch(1, 1);
+
+ setLayout(layout_);
+}
+
+void
+ContinousAllGlyphsTab::createConnections()
+{
+ connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
+ this, &ContinousAllGlyphsTab::changed);
+ connect(modeSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &ContinousAllGlyphsTab::changed);
+ connect(charMapSelector_,
QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &ContinousAllGlyphsTab::charMapChanged);
+}
+
+
+QString
+ContinousAllGlyphsTab::formatIndex(int index)
+{
+ if (charMapSelector_->currentIndex() <= 0) // glyph order
+ return QString::number(index);
+ return charMaps_[charMapSelector_->currentIndex() - 1]
+ .stringifyIndexShort(index);
+}
+
+
+void
+ContinousAllGlyphsTab::charMapChanged()
+{
+ int newIndex = charMapSelector_->currentIndex();
+ if (newIndex != lastCharMapIndex_)
+ {
+ if (newIndex <= 0 || charMaps_.size() <= newIndex - 1)
+ setGlyphBeginindex(0);
+ else if (charMaps_[newIndex - 1].maxIndex <= 20)
+ setGlyphBeginindex(charMaps_[newIndex - 1].maxIndex - 1);
+ else
+ setGlyphBeginindex(0x20);
+ }
+ updateCharMapLimit();
+
+ emit changed();
+
+ lastCharMapIndex_ = newIndex;
+}
+
+
+// end of continuous.cpp
diff --git a/src/ftinspect/panels/continuous.hpp
b/src/ftinspect/panels/continuous.hpp
new file mode 100644
index 0000000..e13e76d
--- /dev/null
+++ b/src/ftinspect/panels/continuous.hpp
@@ -0,0 +1,124 @@
+// continuous.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include "abstracttab.hpp"
+#include "../widgets/customwidgets.hpp"
+#include "../widgets/glyphindexselector.hpp"
+#include "../widgets/fontsizeselector.hpp"
+#include "../rendering/graphicsdefault.hpp"
+#include "../rendering/glyphcontinuous.hpp"
+#include "../engine/engine.hpp"
+
+#include <QWidget>
+#include <QLabel>
+#include <QComboBox>
+#include <QVector>
+#include <QGridLayout>
+#include <QBoxLayout>
+
+class ContinousAllGlyphsTab;
+
+class ContinuousTab
+: public QWidget, public AbstractTab
+{
+ Q_OBJECT
+public:
+ ContinuousTab(QWidget* parent, Engine* engine);
+ ~ContinuousTab() override = default;
+
+ void repaintGlyph() override;
+ void reloadFont() override;
+ void syncSettings() override;
+ void setDefaults() override;
+
+ // Info about current font (glyph count, charmaps...) is flowed to subtab
+ // via `updateCurrentSubTab`.
+ // Settings and parameters (e.g. mode) are flowed from subtab to `this` via
+ // `updateFromCurrentSubTab`.
+ // SubTabs can notify `this` via signals, see `createConnections`
+ void updateCurrentSubTab();
+ void updateFromCurrentSubTab();
+
+private slots:
+ void changeTab();
+ void wheelNavigate(int steps);
+ void wheelResize(int steps);
+
+private:
+ Engine* engine_;
+
+ int currentGlyphCount_;
+ GlyphContinuous* canvas_;
+
+ FontSizeSelector* sizeSelector_;
+
+ QTabWidget* tabWidget_;
+ ContinousAllGlyphsTab* allGlyphsTab_;
+
+ enum Tabs
+ {
+ AllGlyphs = 0
+ };
+
+ QVBoxLayout* mainLayout_;
+
+ void createLayout();
+ void createConnections();
+};
+
+
+class ContinousAllGlyphsTab
+: public QWidget
+{
+ Q_OBJECT
+public:
+ explicit ContinousAllGlyphsTab(QWidget* parent);
+ ~ContinousAllGlyphsTab() override = default;
+
+ int glyphBeginindex();
+ int glyphLimitIndex();
+ GlyphContinuous::SubModeAllGlyphs subMode();
+
+ // -1: Glyph order, otherwise the char map index in the original list
+ int charMapIndex();
+ void setGlyphBeginindex(int index);
+
+ // This doesn't trigger immediate repaint
+ void setGlyphCount(int count) { currentGlyphCount_ = count; }
+ void setDisplayingCount(int count);
+
+ void setCharMaps(QVector<CharMapInfo>& charMaps);
+ // This doesn't trigger either.
+ void updateCharMapLimit();
+
+signals:
+ void changed();
+
+private:
+ int lastCharMapIndex_ = 0;
+ int currentGlyphCount_;
+ int glyphLimitIndex_ = 0;
+
+ GlyphIndexSelector* indexSelector_;
+ QComboBox* modeSelector_;
+ QComboBox* charMapSelector_;
+
+ QLabel* modeLabel_;
+ QLabel* charMapLabel_;
+
+ QGridLayout* layout_;
+
+ QVector<CharMapInfo> charMaps_;
+
+ void createLayout();
+ void createConnections();
+
+ QString formatIndex(int index);
+ void charMapChanged();
+};
+
+
+// end of continuous.hpp
diff --git a/src/ftinspect/panels/singular.cpp
b/src/ftinspect/panels/singular.cpp
index bf09105..bf7dcc4 100644
--- a/src/ftinspect/panels/singular.cpp
+++ b/src/ftinspect/panels/singular.cpp
@@ -9,15 +9,14 @@
SingularTab::SingularTab(QWidget* parent, Engine* engine)
-: QWidget(parent), engine_(engine)
+: QWidget(parent), engine_(engine),
+ graphicsDefault_(GraphicsDefault::deafultInstance())
{
- setGraphicsDefaults();
createLayout();
createConnections();
currentGlyphIndex_ = 0;
checkShowPoints();
- checkUnits();
}
@@ -97,30 +96,35 @@ SingularTab::drawGlyph()
if (!engine_->antiAliasingEnabled())
pixelMode = FT_PIXEL_MODE_MONO;
- currentGlyphBitmapItem_ = new GlyphBitmap(outline,
- engine_->ftLibrary(),
- pixelMode,
- monoColorTable_,
- grayColorTable_);
+ currentGlyphBitmapItem_
+ = new GlyphBitmap(outline,
+ engine_->ftLibrary(),
+ pixelMode,
+ graphicsDefault_->monoColorTable,
+ graphicsDefault_->grayColorTable);
glyphScene_->addItem(currentGlyphBitmapItem_);
}
if (showOutlinesCheckBox_->isChecked())
{
- currentGlyphOutlineItem_ = new GlyphOutline(outlinePen_, outline);
+ currentGlyphOutlineItem_ = new
GlyphOutline(graphicsDefault_->outlinePen,
+ outline);
glyphScene_->addItem(currentGlyphOutlineItem_);
}
if (showPointsCheckBox_->isChecked())
{
- currentGlyphPointsItem_ = new GlyphPoints(onPen_, offPen_, outline);
+ currentGlyphPointsItem_ = new GlyphPoints(graphicsDefault_->onPen,
+ graphicsDefault_->offPen,
+ outline);
glyphScene_->addItem(currentGlyphPointsItem_);
if (showPointNumbersCheckBox_->isChecked())
{
- currentGlyphPointNumbersItem_ = new GlyphPointNumbers(onPen_,
- offPen_,
- outline);
+ currentGlyphPointNumbersItem_
+ = new GlyphPointNumbers(graphicsDefault_->onPen,
+ graphicsDefault_->offPen,
+ outline);
glyphScene_->addItem(currentGlyphPointNumbersItem_);
}
}
@@ -130,29 +134,6 @@ SingularTab::drawGlyph()
}
-void
-SingularTab::checkUnits()
-{
- int index = unitsComboBox_->currentIndex();
-
- if (index == Units_px)
- {
- dpiLabel_->setEnabled(false);
- dpiSpinBox_->setEnabled(false);
- sizeDoubleSpinBox_->setSingleStep(1);
- sizeDoubleSpinBox_->setValue(qRound(sizeDoubleSpinBox_->value()));
- }
- else
- {
- dpiLabel_->setEnabled(true);
- dpiSpinBox_->setEnabled(true);
- sizeDoubleSpinBox_->setSingleStep(0.5);
- }
-
- drawGlyph();
-}
-
-
void
SingularTab::checkShowPoints()
{
@@ -219,11 +200,7 @@ SingularTab::wheelZoom(QWheelEvent* event)
void
SingularTab::wheelResize(QWheelEvent* event)
{
- int numSteps = event->angleDelta().y() / 120;
- double sizeAfter = sizeDoubleSpinBox_->value() + numSteps * 0.5;
- sizeAfter = std::max(sizeDoubleSpinBox_->minimum(),
- std::min(sizeAfter, sizeDoubleSpinBox_->maximum()));
- sizeDoubleSpinBox_->setValue(sizeAfter);
+ sizeSelector_->handleWheelResizeFromGrid(event);
}
@@ -246,7 +223,8 @@ SingularTab::createLayout()
glyphView_->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
glyphView_->setScene(glyphScene_);
- gridItem_ = new Grid(glyphView_, gridPen_, axisPen_);
+ gridItem_ = new Grid(glyphView_, graphicsDefault_->gridPen,
+ graphicsDefault_->axisPen);
glyphScene_->addItem(gridItem_);
// Don't use QGraphicsTextItem: We want this hint to be anchored at the
@@ -271,27 +249,10 @@ SingularTab::createLayout()
glyphIndexLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
glyphNameLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
- sizeLabel_ = new QLabel(tr("Size "), this);
- sizeLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
- sizeDoubleSpinBox_ = new QDoubleSpinBox;
- sizeDoubleSpinBox_->setAlignment(Qt::AlignRight);
- sizeDoubleSpinBox_->setDecimals(1);
- sizeDoubleSpinBox_->setRange(1, 500);
- sizeLabel_->setBuddy(sizeDoubleSpinBox_);
-
indexSelector_ = new GlyphIndexSelector(this);
indexSelector_->setSingleMode(true);
- unitsComboBox_ = new QComboBox(this);
- unitsComboBox_->insertItem(Units_px, "px");
- unitsComboBox_->insertItem(Units_pt, "pt");
-
- dpiLabel_ = new QLabel(tr("DPI "), this);
- dpiLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
- dpiSpinBox_ = new QSpinBox(this);
- dpiSpinBox_->setAlignment(Qt::AlignRight);
- dpiSpinBox_->setRange(10, 600);
- dpiLabel_->setBuddy(dpiSpinBox_);
+ sizeSelector_ = new FontSizeSelector(this);
zoomLabel_ = new QLabel(tr("Zoom Factor"), this);
zoomLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
@@ -310,12 +271,7 @@ SingularTab::createLayout()
sizeLayout_ = new QHBoxLayout;
sizeLayout_->addStretch(2);
- sizeLayout_->addWidget(sizeLabel_);
- sizeLayout_->addWidget(sizeDoubleSpinBox_);
- sizeLayout_->addWidget(unitsComboBox_);
- sizeLayout_->addStretch(1);
- sizeLayout_->addWidget(dpiLabel_);
- sizeLayout_->addWidget(dpiSpinBox_);
+ sizeLayout_->addWidget(sizeSelector_, 3);
sizeLayout_->addStretch(1);
sizeLayout_->addWidget(zoomLabel_);
sizeLayout_->addWidget(zoomSpinBox_);
@@ -355,14 +311,10 @@ SingularTab::createLayout()
void
SingularTab::createConnections()
{
+ connect(sizeSelector_, &FontSizeSelector::valueChanged,
+ this, &SingularTab::repaintGlyph);
connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
this, &SingularTab::setGlyphIndex);
- connect(sizeDoubleSpinBox_,
QOverload<double>::of(&QDoubleSpinBox::valueChanged),
- this, &SingularTab::drawGlyph);
- connect(unitsComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
- this, &SingularTab::checkUnits);
- connect(dpiSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
- this, &SingularTab::drawGlyph);
connect(zoomSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
this, &SingularTab::zoom);
@@ -389,36 +341,6 @@ SingularTab::createConnections()
}
-void
-SingularTab::setGraphicsDefaults()
-{
- // color tables (with suitable opacity values) for converting
- // FreeType's pixmaps to something Qt understands
- monoColorTable_.append(QColor(Qt::transparent).rgba());
- monoColorTable_.append(QColor(Qt::black).rgba());
-
- for (int i = 0xFF; i >= 0; i--)
- grayColorTable_.append(qRgba(i, i, i, 0xFF - i));
-
- // XXX make this user-configurable
-
- axisPen_.setColor(Qt::black);
- axisPen_.setWidth(0);
- blueZonePen_.setColor(QColor(64, 64, 255, 64)); // light blue
- blueZonePen_.setWidth(0);
- gridPen_.setColor(Qt::lightGray);
- gridPen_.setWidth(0);
- offPen_.setColor(Qt::darkGreen);
- offPen_.setWidth(3);
- onPen_.setColor(Qt::red);
- onPen_.setWidth(3);
- outlinePen_.setColor(Qt::red);
- outlinePen_.setWidth(0);
- segmentPen_.setColor(QColor(64, 255, 128, 64)); // light green
- segmentPen_.setWidth(0);
-}
-
-
void
SingularTab::repaintGlyph()
{
@@ -430,9 +352,7 @@ void
SingularTab::reloadFont()
{
currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
- indexSelector_->setMin(0);
- indexSelector_->setMax(currentGlyphCount_);
- indexSelector_->setCurrentIndex(indexSelector_->getCurrentIndex(), true);
+ indexSelector_->setMinMax(0, currentGlyphCount_);
drawGlyph();
}
@@ -440,13 +360,7 @@ SingularTab::reloadFont()
void
SingularTab::syncSettings()
{
- // Spinbox value cannot become negative
- engine_->setDPI(static_cast<unsigned int>(dpiSpinBox_->value()));
-
- if (unitsComboBox_->currentIndex() == Units_px)
- engine_->setSizeByPixel(sizeDoubleSpinBox_->value());
- else
- engine_->setSizeByPoint(sizeDoubleSpinBox_->value());
+ sizeSelector_->applyToEngine(engine_);
}
@@ -455,14 +369,11 @@ SingularTab::setDefaults()
{
currentGlyphIndex_ = 0;
- sizeDoubleSpinBox_->setValue(20);
- dpiSpinBox_->setValue(96);
zoomSpinBox_->setValue(20);
showBitmapCheckBox_->setChecked(true);
showOutlinesCheckBox_->setChecked(true);
-
- checkUnits();
- indexSelector_->setCurrentIndex(indexSelector_->getCurrentIndex(), true);
+
+ indexSelector_->setCurrentIndex(indexSelector_->currentIndex(), true);
zoom();
}
diff --git a/src/ftinspect/panels/singular.hpp
b/src/ftinspect/panels/singular.hpp
index 88df922..88481f8 100644
--- a/src/ftinspect/panels/singular.hpp
+++ b/src/ftinspect/panels/singular.hpp
@@ -7,11 +7,13 @@
#include "abstracttab.hpp"
#include "../widgets/customwidgets.hpp"
#include "../widgets/glyphindexselector.hpp"
+#include "../widgets/fontsizeselector.hpp"
#include "../rendering/glyphbitmap.hpp"
#include "../rendering/glyphoutline.hpp"
#include "../rendering/glyphpointnumbers.hpp"
#include "../rendering/glyphpoints.hpp"
#include "../rendering/grid.hpp"
+#include "../rendering/graphicsdefault.hpp"
#include "../engine/engine.hpp"
#include "../models/ttsettingscomboboxmodel.hpp"
@@ -45,8 +47,7 @@ public:
private slots:
void setGlyphIndex(int);
void drawGlyph();
-
- void checkUnits();
+
void checkShowPoints();
void zoom();
@@ -71,13 +72,9 @@ private:
QLabel* mouseUsageHint_;
GlyphIndexSelector* indexSelector_;
- QLabel* dpiLabel_;
- QLabel* sizeLabel_;
+ FontSizeSelector* sizeSelector_;
QLabel* zoomLabel_;
- QSpinBox* dpiSpinBox_;
ZoomSpinBox* zoomSpinBox_;
- QComboBox* unitsComboBox_;
- QDoubleSpinBox* sizeDoubleSpinBox_;
QPushButton* centerGridButton_;
QLabel* glyphIndexLabel_;
@@ -94,26 +91,10 @@ private:
QGridLayout* glyphOverlayLayout_;
QHBoxLayout* glyphOverlayIndexLayout_;
- QPen axisPen_;
- QPen blueZonePen_;
- QPen gridPen_;
- QPen offPen_;
- QPen onPen_;
- QPen outlinePen_;
- QPen segmentPen_;
-
- QVector<QRgb> grayColorTable_;
- QVector<QRgb> monoColorTable_;
-
- enum Units
- {
- Units_px,
- Units_pt
- };
+ GraphicsDefault* graphicsDefault_;
void createLayout();
void createConnections();
- void setGraphicsDefaults();
void updateGrid();
};
diff --git a/src/ftinspect/rendering/glyphbitmap.cpp
b/src/ftinspect/rendering/glyphbitmap.cpp
index dcef3ee..20bf92e 100644
--- a/src/ftinspect/rendering/glyphbitmap.cpp
+++ b/src/ftinspect/rendering/glyphbitmap.cpp
@@ -5,6 +5,8 @@
#include "glyphbitmap.hpp"
+#include "renderutils.hpp"
+
#include <cmath>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
@@ -21,23 +23,8 @@ GlyphBitmap::GlyphBitmap(FT_Outline* outline,
grayColorTable_(grayColorTbl)
{
// make a copy of the outline since we are going to manipulate it
- FT_Outline_New(library_,
- static_cast<unsigned int>(outline->n_points),
- outline->n_contours,
- &transformed_);
- FT_Outline_Copy(outline, &transformed_);
-
FT_BBox cbox;
- FT_Outline_Get_CBox(outline, &cbox);
-
- cbox.xMin &= ~63;
- cbox.yMin &= ~63;
- cbox.xMax = (cbox.xMax + 63) & ~63;
- cbox.yMax = (cbox.yMax + 63) & ~63;
-
- // we shift the outline to the origin for rendering later on
- FT_Outline_Translate(&transformed_, -cbox.xMin, -cbox.yMin);
-
+ transformed_ = transformOutlineToOrigin(lib, outline, &cbox);
boundingRect_.setCoords(cbox.xMin / 64, -cbox.yMax / 64,
cbox.xMax / 64, -cbox.yMin / 64);
}
diff --git a/src/ftinspect/rendering/glyphcontinuous.cpp
b/src/ftinspect/rendering/glyphcontinuous.cpp
new file mode 100644
index 0000000..524af4c
--- /dev/null
+++ b/src/ftinspect/rendering/glyphcontinuous.cpp
@@ -0,0 +1,212 @@
+// glyphcontinuous.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "glyphcontinuous.hpp"
+
+#include <cmath>
+#include <QPainter>
+#include <QWheelEvent>
+
+#include "../engine/engine.hpp"
+#include "../rendering/renderutils.hpp"
+
+
+GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
+: QWidget(parent), engine_(engine)
+{
+ setAcceptDrops(false);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ graphicsDefault_ = GraphicsDefault::deafultInstance();
+}
+
+
+void
+GlyphContinuous::paintEvent(QPaintEvent* event)
+{
+ QPainter painter;
+ painter.begin(this);
+ painter.fillRect(rect(), Qt::white);
+
+ if (limitIndex_ > 0)
+ {
+ prePaint();
+
+ switch (mode_)
+ {
+ case AllGlyphs:
+ switch (modeAG_)
+ {
+ case AG_AllGlyphs:
+ paintAGAllGlyphs(&painter);
+ break;
+ // TODO more modes
+ case AG_Fancy:
+ break;
+ case AG_Stroked:
+ break;
+ case AG_Waterfall:
+ break;
+ }
+ break;
+ case TextString:
+ break;
+ }
+ emit displayingCountUpdated(displayingCount_);
+ }
+
+ painter.end();
+}
+
+
+void
+GlyphContinuous::wheelEvent(QWheelEvent* event)
+{
+ int numSteps = event->angleDelta().y() / 120;
+ if (event->modifiers() & Qt::ShiftModifier)
+ emit wheelResize(numSteps);
+ else if (event->modifiers() == 0)
+ emit wheelNavigate(-numSteps);
+}
+
+
+void
+GlyphContinuous::paintAGAllGlyphs(QPainter* painter)
+{
+ for (int i = beginIndex_; i < limitIndex_; i++)
+ {
+ unsigned index = i;
+ if (charMapIndex_ >= 0)
+ index = engine_->glyphIndexFromCharCode(i, charMapIndex_);
+
+ if (!paintChar(painter, index))
+ break;
+
+ displayingCount_++;
+ }
+}
+
+
+void
+GlyphContinuous::prePaint()
+{
+ displayingCount_ = 0;
+ engine_->reloadFont();
+ metrics_ = engine_->currentFontMetrics();
+ x_ = 0;
+ // See ftview.c:42
+ y_ = ((metrics_.ascender - metrics_.descender + 63) >> 6) + 4;
+ stepY_ = ((metrics_.height + 63) >> 6) + 4;
+}
+
+
+bool
+GlyphContinuous::paintChar(QPainter* painter,
+ int index)
+{
+ auto glyph = engine_->loadGlyphWithoutUpdate(index);
+ if (!glyph)
+ return false;
+
+ // ftview.c:557
+ int width = glyph->advance.x ? glyph->advance.x >> 16
+ : metrics_.y_ppem / 2;
+
+ if (!checkFitX(x_ + width))
+ {
+ x_ = 0;
+ y_ += stepY_;
+
+ if (!checkFitY(y_))
+ return false;
+ }
+
+ x_++; // extra space
+ if (glyph->advance.x == 0)
+ {
+ // Draw a red square to indicate
+ painter->fillRect(x_, y_ - width, width, width,
+ Qt::red);
+ x_ += width;
+ }
+
+ // The real drawing part
+ // XXX: this is different from what's being done in
+ // `ftcommon.c`:FTDemo_Draw_Slot: is this correct??
+
+ // First translate the outline
+
+ if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+ return true; // XXX only outline is supported - need to impl others later
+
+ FT_BBox cbox;
+ // Don't forget to free this when returning
+ auto outline = transformOutlineToOrigin(
+ engine_->ftLibrary(),
+ &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
+ &cbox);
+
+ auto outlineWidth = (cbox.xMax - cbox.xMin) / 64;
+ auto outlineHeight = (cbox.yMax - cbox.yMin) / 64;
+
+ // Then convert to bitmap
+ FT_Bitmap bitmap;
+ QImage::Format format = QImage::Format_Indexed8;
+ auto aaEnabled = engine_->antiAliasingEnabled();
+
+ // TODO cover LCD and color
+ if (!aaEnabled)
+ format = QImage::Format_Mono;
+
+ // TODO optimization: reuse QImage?
+ QImage image(QSize(outlineWidth, outlineHeight), format);
+
+ if (!aaEnabled)
+ image.setColorTable(graphicsDefault_->monoColorTable);
+ else
+ image.setColorTable(graphicsDefault_->grayColorTable);
+
+ image.fill(0);
+
+ bitmap.rows = static_cast<unsigned int>(outlineHeight);
+ bitmap.width = static_cast<unsigned int>(outlineWidth);
+ bitmap.buffer = image.bits();
+ bitmap.pitch = image.bytesPerLine();
+ bitmap.pixel_mode = aaEnabled ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO;
+
+ FT_Error error = FT_Outline_Get_Bitmap(engine_->ftLibrary(),
+ &outline,
+ &bitmap);
+ if (error)
+ {
+ // XXX error handling
+ FT_Outline_Done(engine_->ftLibrary(), &outline);
+ return true;
+ }
+
+ painter->drawImage(
+ QPoint(x_ + cbox.xMin / 64, y_ + (-cbox.yMax / 64)),
+ image.convertToFormat(QImage::Format_ARGB32_Premultiplied));
+
+ x_ += width;
+
+ FT_Outline_Done(engine_->ftLibrary(), &outline);
+ return true;
+}
+
+
+bool
+GlyphContinuous::checkFitX(int x)
+{
+ return x < width() - 3;
+}
+
+
+bool
+GlyphContinuous::checkFitY(int y)
+{
+ return y < height() - 3;
+}
+
+
+// end of glyphcontinuous.cpp
diff --git a/src/ftinspect/rendering/glyphcontinuous.hpp
b/src/ftinspect/rendering/glyphcontinuous.hpp
new file mode 100644
index 0000000..9a87e7e
--- /dev/null
+++ b/src/ftinspect/rendering/glyphcontinuous.hpp
@@ -0,0 +1,77 @@
+// glyphcontinuous.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include "graphicsdefault.hpp"
+#include <QWidget>
+#include <freetype/freetype.h>
+
+class Engine;
+class GlyphContinuous
+: public QWidget
+{
+ Q_OBJECT
+public:
+ GlyphContinuous(QWidget* parent, Engine* engine);
+ ~GlyphContinuous() override = default;
+
+ enum Mode : int
+ {
+ AllGlyphs,
+ TextString
+ };
+
+ enum SubModeAllGlyphs : int
+ {
+ AG_AllGlyphs,
+ AG_Fancy,
+ AG_Stroked,
+ AG_Waterfall
+ };
+
+ int displayingCount() { return displayingCount_; }
+
+ // all those setters don't trigger repaint.
+ void setBeginIndex(int index) { beginIndex_ = index; }
+ void setLimitIndex(int index) { limitIndex_ = index; }
+ void setCharMapIndex(int index) { charMapIndex_ = index; }
+ void setMode(Mode mode) { mode_ = mode; }
+ void setSubModeAllGlyphs(SubModeAllGlyphs modeAg) { modeAG_ = modeAg; }
+
+signals:
+ void wheelNavigate(int steps);
+ void wheelResize(int steps);
+ void displayingCountUpdated(int newCount);
+
+protected:
+ void paintEvent(QPaintEvent* event) override;
+ void wheelEvent(QWheelEvent* event) override;
+
+private:
+ Engine* engine_;
+ GraphicsDefault* graphicsDefault_;
+
+ int beginIndex_;
+ int limitIndex_;
+ int charMapIndex_;
+ Mode mode_ = AllGlyphs;
+ SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
+
+ int displayingCount_ = 0;
+ FT_Size_Metrics metrics_;
+ int x_ = 0, y_ = 0;
+ int stepY_ = 0;
+
+ void paintAGAllGlyphs(QPainter* painter);
+ void prePaint();
+ // return if there's enough space to paint the current char
+ bool paintChar(QPainter* painter, int index);
+
+ bool checkFitX(int x);
+ bool checkFitY(int y);
+};
+
+
+// end of glyphcontinuous.hpp
diff --git a/src/ftinspect/rendering/graphicsdefault.cpp
b/src/ftinspect/rendering/graphicsdefault.cpp
new file mode 100644
index 0000000..4e80b22
--- /dev/null
+++ b/src/ftinspect/rendering/graphicsdefault.cpp
@@ -0,0 +1,48 @@
+// graphicsdefault.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "graphicsdefault.hpp"
+
+GraphicsDefault* GraphicsDefault::instance_ = NULL;
+
+GraphicsDefault::GraphicsDefault()
+{
+ // color tables (with suitable opacity values) for converting
+ // FreeType's pixmaps to something Qt understands
+ monoColorTable.append(QColor(Qt::transparent).rgba());
+ monoColorTable.append(QColor(Qt::black).rgba());
+
+ for (int i = 0xFF; i >= 0; i--)
+ grayColorTable.append(qRgba(i, i, i, 0xFF - i));
+
+ // XXX make this user-configurable
+
+ axisPen.setColor(Qt::black);
+ axisPen.setWidth(0);
+ blueZonePen.setColor(QColor(64, 64, 255, 64)); // light blue
+ blueZonePen.setWidth(0);
+ gridPen.setColor(Qt::lightGray);
+ gridPen.setWidth(0);
+ offPen.setColor(Qt::darkGreen);
+ offPen.setWidth(3);
+ onPen.setColor(Qt::red);
+ onPen.setWidth(3);
+ outlinePen.setColor(Qt::red);
+ outlinePen.setWidth(0);
+ segmentPen.setColor(QColor(64, 255, 128, 64)); // light green
+ segmentPen.setWidth(0);
+}
+
+
+GraphicsDefault*
+GraphicsDefault::deafultInstance()
+{
+ if (!instance_)
+ instance_ = new GraphicsDefault;
+
+ return instance_;
+}
+
+
+// end of graphicsdefault.cpp
diff --git a/src/ftinspect/rendering/graphicsdefault.hpp
b/src/ftinspect/rendering/graphicsdefault.hpp
new file mode 100644
index 0000000..4b8e588
--- /dev/null
+++ b/src/ftinspect/rendering/graphicsdefault.hpp
@@ -0,0 +1,34 @@
+// graphicsdefault.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <QVector>
+#include <QRgb>
+#include <QPen>
+
+// This is default graphics objects fed into render functions.
+struct GraphicsDefault
+{
+ QVector<QRgb> grayColorTable;
+ QVector<QRgb> monoColorTable;
+
+ QPen axisPen;
+ QPen blueZonePen;
+ QPen gridPen;
+ QPen offPen;
+ QPen onPen;
+ QPen outlinePen;
+ QPen segmentPen;
+
+ GraphicsDefault();
+
+ static GraphicsDefault* deafultInstance();
+
+private:
+ static GraphicsDefault* instance_;
+};
+
+
+// end of graphicsdefault.hpp
diff --git a/src/ftinspect/rendering/renderutils.cpp
b/src/ftinspect/rendering/renderutils.cpp
new file mode 100644
index 0000000..9866db8
--- /dev/null
+++ b/src/ftinspect/rendering/renderutils.cpp
@@ -0,0 +1,34 @@
+// renderutils.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "renderutils.hpp"
+
+FT_Outline
+transformOutlineToOrigin(FT_Library library,
+ FT_Outline* outline,
+ FT_BBox* outControlBox)
+{
+ FT_Outline transformed;
+ FT_Outline_New(library,
+ static_cast<unsigned int>(outline->n_points),
+ outline->n_contours, &transformed);
+ FT_Outline_Copy(outline, &transformed);
+
+ FT_BBox cbox;
+ FT_Outline_Get_CBox(outline, &cbox);
+
+ cbox.xMin &= ~63;
+ cbox.yMin &= ~63;
+ cbox.xMax = (cbox.xMax + 63) & ~63;
+ cbox.yMax = (cbox.yMax + 63) & ~63;
+ // we shift the outline to the origin for rendering later on
+ FT_Outline_Translate(&transformed, -cbox.xMin, -cbox.yMin);
+
+ if (outControlBox)
+ *outControlBox = cbox;
+ return transformed;
+}
+
+
+// end of renderutils.cpp
diff --git a/src/ftinspect/rendering/renderutils.hpp
b/src/ftinspect/rendering/renderutils.hpp
new file mode 100644
index 0000000..ba4caf4
--- /dev/null
+++ b/src/ftinspect/rendering/renderutils.hpp
@@ -0,0 +1,15 @@
+// renderutils.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <freetype/ftoutln.h>
+
+// The constructed `outline` must be freed by the caller
+FT_Outline transformOutlineToOrigin(FT_Library library,
+ FT_Outline* outline,
+ FT_BBox* outControlBox);
+
+
+// end of renderutils.hpp
diff --git a/src/ftinspect/widgets/fontsizeselector.cpp
b/src/ftinspect/widgets/fontsizeselector.cpp
new file mode 100644
index 0000000..ed04970
--- /dev/null
+++ b/src/ftinspect/widgets/fontsizeselector.cpp
@@ -0,0 +1,147 @@
+// fontsizeselector.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "fontsizeselector.hpp"
+
+#include "../engine/engine.hpp"
+
+FontSizeSelector::FontSizeSelector(QWidget* parent)
+: QWidget(parent)
+{
+ createLayout();
+ createConnections();
+ setDefaults();
+}
+
+
+double
+FontSizeSelector::selectedSize()
+{
+ return sizeDoubleSpinBox_->value();
+}
+
+
+FontSizeSelector::Units
+FontSizeSelector::selectedUnit()
+{
+ return static_cast<Units>(unitsComboBox_->currentIndex());
+}
+
+
+void
+FontSizeSelector::applyToEngine(Engine* engine)
+{
+ // Spinbox value cannot become negative
+ engine->setDPI(dpiSpinBox_->value());
+
+ if (unitsComboBox_->currentIndex() == Units_px)
+ engine->setSizeByPixel(sizeDoubleSpinBox_->value());
+ else
+ engine->setSizeByPoint(sizeDoubleSpinBox_->value());
+}
+
+
+void
+FontSizeSelector::handleWheelResizeBySteps(int steps)
+{
+ double sizeAfter = sizeDoubleSpinBox_->value() + steps * 0.5;
+ sizeAfter = std::max(sizeDoubleSpinBox_->minimum(),
+ std::min(sizeAfter, sizeDoubleSpinBox_->maximum()));
+ sizeDoubleSpinBox_->setValue(sizeAfter);
+}
+
+
+void
+FontSizeSelector::handleWheelResizeFromGrid(QWheelEvent* event)
+{
+ int numSteps = event->angleDelta().y() / 120;
+ handleWheelResizeBySteps(numSteps);
+}
+
+
+void
+FontSizeSelector::checkUnits()
+{
+ int index = unitsComboBox_->currentIndex();
+
+ if (index == Units_px)
+ {
+ dpiLabel_->setEnabled(false);
+ dpiSpinBox_->setEnabled(false);
+ sizeDoubleSpinBox_->setSingleStep(1);
+
+ QSignalBlocker blocker(sizeDoubleSpinBox_);
+ sizeDoubleSpinBox_->setValue(qRound(sizeDoubleSpinBox_->value()));
+ }
+ else
+ {
+ dpiLabel_->setEnabled(true);
+ dpiSpinBox_->setEnabled(true);
+ sizeDoubleSpinBox_->setSingleStep(0.5);
+ }
+
+ emit valueChanged();
+}
+
+
+void
+FontSizeSelector::createLayout()
+{
+ sizeLabel_ = new QLabel(tr("Size "), this);
+ sizeLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ sizeDoubleSpinBox_ = new QDoubleSpinBox;
+ sizeDoubleSpinBox_->setAlignment(Qt::AlignRight);
+ sizeDoubleSpinBox_->setDecimals(1);
+ sizeDoubleSpinBox_->setRange(1, 500);
+ sizeLabel_->setBuddy(sizeDoubleSpinBox_);
+
+ unitsComboBox_ = new QComboBox(this);
+ unitsComboBox_->insertItem(Units_px, "px");
+ unitsComboBox_->insertItem(Units_pt, "pt");
+
+ dpiLabel_ = new QLabel(tr("DPI "), this);
+ dpiLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ dpiSpinBox_ = new QSpinBox(this);
+ dpiSpinBox_->setAlignment(Qt::AlignRight);
+ dpiSpinBox_->setRange(10, 600);
+ dpiLabel_->setBuddy(dpiSpinBox_);
+
+ layout_ = new QHBoxLayout;
+
+ layout_->addStretch(1);
+ layout_->addWidget(sizeLabel_);
+ layout_->addWidget(sizeDoubleSpinBox_);
+ layout_->addWidget(unitsComboBox_);
+ layout_->addStretch(1);
+ layout_->addWidget(dpiLabel_);
+ layout_->addWidget(dpiSpinBox_);
+ layout_->addStretch(1);
+
+ setLayout(layout_);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+}
+
+
+void
+FontSizeSelector::createConnections()
+{
+ connect(sizeDoubleSpinBox_,
QOverload<double>::of(&QDoubleSpinBox::valueChanged),
+ this, &FontSizeSelector::valueChanged);
+ connect(unitsComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &FontSizeSelector::checkUnits);
+ connect(dpiSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
+ this, &FontSizeSelector::valueChanged);
+}
+
+
+void
+FontSizeSelector::setDefaults()
+{
+ sizeDoubleSpinBox_->setValue(20);
+ dpiSpinBox_->setValue(96);
+ checkUnits();
+}
+
+
+// end of fontsizeselector.cpp
diff --git a/src/ftinspect/widgets/fontsizeselector.hpp
b/src/ftinspect/widgets/fontsizeselector.hpp
new file mode 100644
index 0000000..581c860
--- /dev/null
+++ b/src/ftinspect/widgets/fontsizeselector.hpp
@@ -0,0 +1,58 @@
+// fontsizeselector.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <QComboBox>
+#include <QDoubleSpinBox>
+#include <QLabel>
+#include <QWidget>
+#include <QBoxLayout>
+#include <QWheelEvent>
+
+class Engine;
+class FontSizeSelector : public QWidget
+{
+ Q_OBJECT
+
+public:
+ FontSizeSelector(QWidget* parent);
+ ~FontSizeSelector() override = default;
+
+ enum Units : int
+ {
+ Units_px,
+ Units_pt
+ };
+
+ double selectedSize();
+ Units selectedUnit();
+
+ void applyToEngine(Engine* engine);
+ void handleWheelResizeBySteps(int steps);
+ void handleWheelResizeFromGrid(QWheelEvent* event);
+
+signals:
+ void valueChanged();
+
+private slots:
+ void checkUnits();
+
+private:
+ QLabel* sizeLabel_;
+ QLabel* dpiLabel_;
+
+ QDoubleSpinBox* sizeDoubleSpinBox_;
+ QComboBox* unitsComboBox_;
+ QSpinBox* dpiSpinBox_;
+
+ QHBoxLayout* layout_;
+
+ void createLayout();
+ void createConnections();
+ void setDefaults();
+};
+
+
+// end of fontsizeselector.hpp
diff --git a/src/ftinspect/widgets/glyphindexselector.cpp
b/src/ftinspect/widgets/glyphindexselector.cpp
index 13697f4..d926f53 100644
--- a/src/ftinspect/widgets/glyphindexselector.cpp
+++ b/src/ftinspect/widgets/glyphindexselector.cpp
@@ -6,33 +6,33 @@
#include "../uihelper.hpp"
+#include <climits>
+
GlyphIndexSelector::GlyphIndexSelector(QWidget* parent)
: QWidget(parent)
{
+ numberRenderer_ = &GlyphIndexSelector::renderNumberDefault;
+
createLayout();
createConnections();
+ showingCount_ = 0;
}
void
-GlyphIndexSelector::setMin(int min)
+GlyphIndexSelector::setMinMax(int min,
+ int max)
{
+ // Don't emit events during setting
+ auto eventState = blockSignals(true);
indexSpinBox_->setMinimum(min);
+ indexSpinBox_->setMaximum(qBound(0, max, INT_MAX));
indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
indexSpinBox_->value(),
indexSpinBox_->maximum()));
- // spinBoxChanged will be automatically called
-}
+ blockSignals(eventState);
-
-void
-GlyphIndexSelector::setMax(int max)
-{
- indexSpinBox_->setMaximum(max);
- indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
- indexSpinBox_->value(),
- indexSpinBox_->maximum()));
- // spinBoxChanged will be automatically called
+ updateLabel();
}
@@ -55,26 +55,41 @@ GlyphIndexSelector::setSingleMode(bool singleMode)
void
GlyphIndexSelector::setCurrentIndex(int index, bool forceUpdate)
{
+ // to avoid unnecessary update, if force update is enabled
+ // then the `setValue` shouldn't trigger update signal from `this`
+ // but we still need `updateLabel`, so block `this` only
+ auto state = blockSignals(forceUpdate);
indexSpinBox_->setValue(index);
- updateLabel();
+ blockSignals(state);
+
if (forceUpdate)
emit currentIndexChanged(indexSpinBox_->value());
}
int
-GlyphIndexSelector::getCurrentIndex()
+GlyphIndexSelector::currentIndex()
{
return indexSpinBox_->value();
}
+void
+GlyphIndexSelector::setNumberRenderer(std::function<QString(int)> renderer)
+{
+ numberRenderer_ = std::move(renderer);
+}
+
+
void
GlyphIndexSelector::adjustIndex(int delta)
{
- indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
- indexSpinBox_->value() + delta,
- indexSpinBox_->maximum()));
+ {
+ QSignalBlocker blocker(this);
+ indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
+ indexSpinBox_->value() + delta,
+ indexSpinBox_->maximum()));
+ }
emitValueChanged();
}
@@ -92,13 +107,17 @@ GlyphIndexSelector::updateLabel()
{
if (singleMode_)
indexLabel_->setText(QString("%1\nLimit: %2")
- .arg(indexSpinBox_->value())
- .arg(indexSpinBox_->maximum()));
+ .arg(numberRenderer_(indexSpinBox_->value()))
+ .arg(numberRenderer_(indexSpinBox_->maximum())));
else
- indexLabel_->setText(QString("%1~%2\nCount: %3\nLimit: %4")
- .arg(indexSpinBox_->value())
- .arg(indexSpinBox_->value() + showingCount_ - 1)
- .arg(showingCount_, indexSpinBox_->maximum()));
+ indexLabel_->setText(
+ QString("%1~%2\nCount: %3\nLimit: %4")
+ .arg(numberRenderer_(indexSpinBox_->value()))
+ .arg(numberRenderer_(
+ qBound(indexSpinBox_->value(),
+ indexSpinBox_->value() + showingCount_ - 1, INT_MAX)))
+ .arg(showingCount_)
+ .arg(numberRenderer_(indexSpinBox_->maximum())));
}
@@ -121,9 +140,10 @@ GlyphIndexSelector::createLayout()
indexSpinBox_->setButtonSymbols(QAbstractSpinBox::NoButtons);
indexSpinBox_->setRange(0, 0);
indexSpinBox_->setFixedWidth(80);
- indexSpinBox_->setWrapping(true);
+ indexSpinBox_->setWrapping(false);
indexLabel_ = new QLabel("0\nCount: 0\nLimit: 0");
+ indexLabel_->setMinimumWidth(200);
setButtonNarrowest(toStartButton_);
setButtonNarrowest(toM1000Button_);
@@ -155,6 +175,7 @@ GlyphIndexSelector::createLayout()
navigationLayout_->addWidget(indexLabel_);
navigationLayout_->addStretch(3);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
setLayout(navigationLayout_);
}
@@ -201,4 +222,11 @@ GlyphIndexSelector::createConnections()
}
+QString
+GlyphIndexSelector::renderNumberDefault(int i)
+{
+ return QString::number(i);
+}
+
+
// end of glyphindexselector.cpp
diff --git a/src/ftinspect/widgets/glyphindexselector.hpp
b/src/ftinspect/widgets/glyphindexselector.hpp
index ddd5ecf..17a531b 100644
--- a/src/ftinspect/widgets/glyphindexselector.hpp
+++ b/src/ftinspect/widgets/glyphindexselector.hpp
@@ -4,6 +4,7 @@
#pragma once
+#include <functional>
#include <QWidget>
#include <QPushButton>
#include <QSpinBox>
@@ -19,13 +20,15 @@ public:
GlyphIndexSelector(QWidget* parent);
~GlyphIndexSelector() override = default;
- void setMin(int min);
- void setMax(int max);
+ // Will never trigger repaint!
+ void setMinMax(int min, int max);
void setShowingCount(int showingCount);
void setSingleMode(bool singleMode);
void setCurrentIndex(int index, bool forceUpdate = false);
- int getCurrentIndex();
+ int currentIndex();
+
+ void setNumberRenderer(std::function<QString(int)> renderer);
signals:
void currentIndexChanged(int index);
@@ -38,6 +41,7 @@ private slots:
private:
bool singleMode_ = true;
int showingCount_;
+ std::function<QString(int)> numberRenderer_;
// min, max and current status are held by `indexSpinBox_`
@@ -61,6 +65,8 @@ private:
void createLayout();
void createConnections();
+
+ static QString renderNumberDefault(int i);
};
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freetype2-demos] gsoc-2022-chariri-2 c913540 24/30: [ftinspect] WIP: Add initial support to "Continuous View".,
Werner Lemberg <=