Commits:
-
47238404
by Charlie Jiang
at 2022-09-04T00:23:30+08:00
[ftinspect] Rewrite `MainGUI`.
Note: This commit compiles, but the main view is removed, so you will get a
blank right panel. The singular view will be added back with major changes
in the next commit.
This commit mainly introduces below changes:
1. The original `MainGUI` contains almost all GUI elements. Most of them are
taken out to modular components. The current `MainGUI` serves only as
a skeleton provides coordinate between components, greatly shortened.
It also provides some auxiliary code such as calling to file chooser.
2. The left panel is moved to `SettingPanel` class. The current
`settingpanel.[ch]pp` are directly modified from the latest code, so they
contains some options not implemented in the `Engine`.
Structural code that is only added accompanying later change is removed,
such as the comparator mode. Such code will be added back with the
related feature. However, code for unimplemented options are simply
commented out.
3. The main part is transformed into a tabbed view. The original code is
removed. Refactored, it will be added back in the next commit.
4. The navigation buttons (Next/Prev Font/Face/NI) are changed into the new
triplet-selector, which is a major UI improvement.
* src/ftinspect/maingui.cpp, src/ftinspect/maingui.hpp: As described.
* src/ftinspect/panels/abstracttab.hpp: Add `AbstractTab` which is an
interface for all tabs listening for font reloading and repainting.
* src/ftinspect/panels/settingpanel.cpp,
src/ftinspect/panels/settingpanel.hpp:
As described, this is the left panel. This requires intensive reviewing
since many bugs had rooted here.
* src/ftinspect/widgets/tripletselector.cpp,
src/ftinspect/widgets/tripletselector.hpp:
As described, this is the triplet (Font/Subface/NI) selector.
This component is also responsible for repopulating triplet information
and keep up with the font file change (i.e. the
`FontFileManager::currentFileChanged` event is captured here).
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
Add `fontValid` and `namedInstanceName` since `TripletSelector` requires
them. However, they'll subject to change later.
* src/ftinspect/ftinspect.cpp: Remove call to `MainGUI::setDefaults`.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
-
14556570
by Charlie Jiang
at 2022-09-04T00:23:36+08:00
[ftinspect] Add the `SingularTab` and related widgets.
This introduces the new singular tab. However, because the new tab heavily
depend on the new engine structure, it's current not functional. No bitmap
or outline will be displayed. This would be fixed after the `Engine` was
refactored in the next commit.
The new singular tab has the size and glyph index selector moved out as
modular widgets to be reused.
The new scroll and shortcut behaviours are introduced in this commit, which
depend on scroll events introduced in the custom `QGraphicsViewx`.
The infinity panning of grid is implemented mainly via
`SingularTab::updateGrid` and `Grid::updateRect`.
This commit is introducing new features since it would be unfavorable to
"backport" old version of glyph components, and add those new features in
future commits - this is way too complex.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
Add `currentFontNumberOfGlyphs` and `dpi` functions.
* src/ftinspect/widgets/fontsizeselector.cpp,
src/ftinspect/widgets/fontsizeselector.hpp:
This is the new font size selector to replace the old size/DPI/zoom boxes.
This widget is capable of handling wheel and key events delegated from
other widgets.
The support for fixed sizes and bitmap-only font is not yet added.
* src/ftinspect/widgets/glyphindexselector.cpp,
src/ftinspect/widgets/glyphindexselector.hpp:
This is the new glyph index selector to replace the old navi buttons.
This selector is aware of index min/max, consists of a group of navi
buttons, a text box (actually a spin box without buttons) to directly
input glyph index.
* src/ftinspect/glyphcomponents/graphicsdefault.cpp,
src/ftinspect/glyphcomponents/graphicsdefault.hpp:
This struct contains all default graphical settings (mainly for singular
view, e.g. the grid line color).
* src/ftinspect/maingui.cpp, src/ftinspect/maingui.hpp:
Add the new tab into the main window.
* src/ftinspect/glyphcomponents/glyphbitmap.cpp,
src/ftinspect/glyphcomponents/glyphbitmap.hpp:
This will now delegate rendering to the engine instead of doing rendering
itself. However, since the rendering part of the `Engine` is not
implemented, code initializing `image_` is left commented.
Also add another constructor for initializing directly from a `QImage`.
* src/ftinspect/glyphcomponents/glyphoutline.cpp,
src/ftinspect/glyphcomponents/glyphoutline.hpp,
src/ftinspect/glyphcomponents/glyphpointnumbers.cpp,
src/ftinspect/glyphcomponents/glyphpointnumbers.hpp,
src/ftinspect/glyphcomponents/glyphpoints.cpp,
src/ftinspect/glyphcomponents/glyphpoints.hpp:
Constructors of those items now accept a `FT_Glyph` instead of
`FT_Outline`. The conversion is done inside the view, and the view won't
be displayed if the glyph isn't outline glyph.
This simplifies the code of `SingularTab`.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
30 changed files:
Changes:
src/ftinspect/CMakeLists.txt
... |
... |
@@ -28,10 +28,17 @@ add_executable(ftinspect |
28
|
28
|
"glyphcomponents/glyphpointnumbers.cpp"
|
29
|
29
|
"glyphcomponents/glyphpoints.cpp"
|
30
|
30
|
"glyphcomponents/grid.cpp"
|
|
31
|
+ "glyphcomponents/graphicsdefault.cpp"
|
31
|
32
|
|
32
|
33
|
"widgets/customwidgets.cpp"
|
|
34
|
+ "widgets/tripletselector.cpp"
|
|
35
|
+ "widgets/glyphindexselector.cpp"
|
|
36
|
+ "widgets/fontsizeselector.cpp"
|
33
|
37
|
|
34
|
38
|
"models/customcomboboxmodels.cpp"
|
|
39
|
+
|
|
40
|
+ "panels/settingpanel.cpp"
|
|
41
|
+ "panels/singular.cpp"
|
35
|
42
|
)
|
36
|
43
|
target_link_libraries(ftinspect
|
37
|
44
|
Qt5::Core Qt5::Widgets
|
src/ftinspect/engine/engine.cpp
... |
... |
@@ -237,6 +237,50 @@ Engine::numberOfNamedInstances(int fontIndex, |
237
|
237
|
}
|
238
|
238
|
|
239
|
239
|
|
|
240
|
+QString
|
|
241
|
+Engine::namedInstanceName(int fontIndex, long faceIndex, int index)
|
|
242
|
+{
|
|
243
|
+ if (fontIndex < 0)
|
|
244
|
+ return {};
|
|
245
|
+
|
|
246
|
+ FT_Face face;
|
|
247
|
+ QString name;
|
|
248
|
+
|
|
249
|
+ // search triplet (fontIndex, faceIndex, index)
|
|
250
|
+ FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
|
|
251
|
+ (faceIDMap_.value(FaceID(fontIndex,
|
|
252
|
+ faceIndex,
|
|
253
|
+ index)));
|
|
254
|
+ if (ftcFaceID)
|
|
255
|
+ {
|
|
256
|
+ // found
|
|
257
|
+ if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
|
|
258
|
+ name = QString("%1 %2")
|
|
259
|
+ .arg(face->family_name)
|
|
260
|
+ .arg(face->style_name);
|
|
261
|
+ }
|
|
262
|
+ else
|
|
263
|
+ {
|
|
264
|
+ // not found; try to load triplet (fontIndex, faceIndex, index)
|
|
265
|
+ ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
|
266
|
+ faceIDMap_.insert(FaceID(fontIndex, faceIndex, index),
|
|
267
|
+ faceCounter_++);
|
|
268
|
+
|
|
269
|
+ if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
|
|
270
|
+ name = QString("%1 %2")
|
|
271
|
+ .arg(face->family_name)
|
|
272
|
+ .arg(face->style_name);
|
|
273
|
+ else
|
|
274
|
+ {
|
|
275
|
+ faceIDMap_.remove(FaceID(fontIndex, faceIndex, 0));
|
|
276
|
+ faceCounter_--;
|
|
277
|
+ }
|
|
278
|
+ }
|
|
279
|
+
|
|
280
|
+ return name;
|
|
281
|
+}
|
|
282
|
+
|
|
283
|
+
|
240
|
284
|
int
|
241
|
285
|
Engine::loadFont(int fontIndex,
|
242
|
286
|
long faceIndex,
|
... |
... |
@@ -299,6 +343,7 @@ Engine::loadFont(int fontIndex, |
299
|
343
|
fontType_ = FontType_TrueType;
|
300
|
344
|
}
|
301
|
345
|
|
|
346
|
+ curNumGlyphs_ = numGlyphs;
|
302
|
347
|
return numGlyphs;
|
303
|
348
|
}
|
304
|
349
|
|
... |
... |
@@ -394,6 +439,14 @@ Engine::numberOfOpenedFonts() |
394
|
439
|
}
|
395
|
440
|
|
396
|
441
|
|
|
442
|
+bool
|
|
443
|
+Engine::fontValid()
|
|
444
|
+{
|
|
445
|
+ // TODO: use fallback font
|
|
446
|
+ return ftSize_ != NULL;
|
|
447
|
+}
|
|
448
|
+
|
|
449
|
+
|
397
|
450
|
void
|
398
|
451
|
Engine::openFonts(QStringList fontFileNames)
|
399
|
452
|
{
|
src/ftinspect/engine/engine.hpp
... |
... |
@@ -85,13 +85,20 @@ public: |
85
|
85
|
int numberOfOpenedFonts();
|
86
|
86
|
|
87
|
87
|
// (for current fonts)
|
|
88
|
+ bool fontValid();
|
88
|
89
|
int currentFontType() const { return fontType_; }
|
89
|
90
|
const QString& currentFamilyName() { return curFamilyName_; }
|
90
|
91
|
const QString& currentStyleName() { return curStyleName_; }
|
|
92
|
+ int currentFontNumberOfGlyphs() { return curNumGlyphs_; }
|
|
93
|
+
|
91
|
94
|
QString glyphName(int glyphIndex);
|
92
|
95
|
long numberOfFaces(int fontIndex);
|
93
|
96
|
int numberOfNamedInstances(int fontIndex,
|
94
|
97
|
long faceIndex);
|
|
98
|
+ QString namedInstanceName(int fontIndex, long faceIndex, int index);
|
|
99
|
+
|
|
100
|
+ // (settings)
|
|
101
|
+ int dpi() { return dpi_; }
|
95
|
102
|
|
96
|
103
|
//////// Setters (direct or indirect)
|
97
|
104
|
|
... |
... |
@@ -138,6 +145,7 @@ private: |
138
|
145
|
|
139
|
146
|
QString curFamilyName_;
|
140
|
147
|
QString curStyleName_;
|
|
148
|
+ int curNumGlyphs_ = -1;
|
141
|
149
|
|
142
|
150
|
FT_Library library_;
|
143
|
151
|
FTC_Manager cacheManager_;
|
src/ftinspect/ftinspect.cpp
... |
... |
@@ -23,7 +23,6 @@ main(int argc, |
23
|
23
|
|
24
|
24
|
Engine engine;
|
25
|
25
|
MainGUI gui(&engine);
|
26
|
|
- gui.setDefaults();
|
27
|
26
|
|
28
|
27
|
gui.show();
|
29
|
28
|
|
src/ftinspect/glyphcomponents/glyphbitmap.cpp
... |
... |
@@ -5,47 +5,44 @@ |
5
|
5
|
|
6
|
6
|
#include "glyphbitmap.hpp"
|
7
|
7
|
|
|
8
|
+#include "../engine/engine.hpp"
|
|
9
|
+
|
8
|
10
|
#include <cmath>
|
|
11
|
+#include <utility>
|
|
12
|
+#include <qevent.h>
|
9
|
13
|
#include <QPainter>
|
10
|
14
|
#include <QStyleOptionGraphicsItem>
|
|
15
|
+#include <freetype/ftbitmap.h>
|
11
|
16
|
|
12
|
17
|
|
13
|
|
-GlyphBitmap::GlyphBitmap(FT_Outline* outline,
|
14
|
|
- FT_Library lib,
|
15
|
|
- FT_Pixel_Mode pxlMode,
|
16
|
|
- const QVector<QRgb>& monoColorTbl,
|
17
|
|
- const QVector<QRgb>& grayColorTbl)
|
18
|
|
-: library_(lib),
|
19
|
|
- pixelMode_(pxlMode),
|
20
|
|
- monoColorTable_(monoColorTbl),
|
21
|
|
- grayColorTable_(grayColorTbl)
|
|
18
|
+GlyphBitmap::GlyphBitmap(QImage* image,
|
|
19
|
+ QRect rect)
|
|
20
|
+: image_(image),
|
|
21
|
+ boundingRect_(rect)
|
22
|
22
|
{
|
23
|
|
- // make a copy of the outline since we are going to manipulate it
|
24
|
|
- FT_Outline_New(library_,
|
25
|
|
- static_cast<unsigned int>(outline->n_points),
|
26
|
|
- outline->n_contours,
|
27
|
|
- &transformed_);
|
28
|
|
- FT_Outline_Copy(outline, &transformed_);
|
29
|
|
-
|
30
|
|
- FT_BBox cbox;
|
31
|
|
- FT_Outline_Get_CBox(outline, &cbox);
|
32
|
|
-
|
33
|
|
- cbox.xMin &= ~63;
|
34
|
|
- cbox.yMin &= ~63;
|
35
|
|
- cbox.xMax = (cbox.xMax + 63) & ~63;
|
36
|
|
- cbox.yMax = (cbox.yMax + 63) & ~63;
|
37
|
|
-
|
38
|
|
- // we shift the outline to the origin for rendering later on
|
39
|
|
- FT_Outline_Translate(&transformed_, -cbox.xMin, -cbox.yMin);
|
40
|
|
-
|
41
|
|
- boundingRect_.setCoords(cbox.xMin / 64, -cbox.yMax / 64,
|
42
|
|
- cbox.xMax / 64, -cbox.yMin / 64);
|
|
23
|
+
|
|
24
|
+}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+GlyphBitmap::GlyphBitmap(int glyphIndex,
|
|
28
|
+ FT_Glyph glyph,
|
|
29
|
+ Engine* engine)
|
|
30
|
+{
|
|
31
|
+ QRect bRect;
|
|
32
|
+ image_ = NULL; // TODO: refactr Engine
|
|
33
|
+ //image_ = engine->renderingEngine()->tryDirectRenderColorLayers(glyphIndex,
|
|
34
|
+ // &bRect, true);
|
|
35
|
+
|
|
36
|
+ //if (!image_)
|
|
37
|
+ // image_ = engine->renderingEngine()->convertGlyphToQImage(glyph, &bRect,
|
|
38
|
+ // true);
|
|
39
|
+ boundingRect_ = bRect; // QRect to QRectF
|
43
|
40
|
}
|
44
|
41
|
|
45
|
42
|
|
46
|
43
|
GlyphBitmap::~GlyphBitmap()
|
47
|
44
|
{
|
48
|
|
- FT_Outline_Done(library_, &transformed_);
|
|
45
|
+ delete image_;
|
49
|
46
|
}
|
50
|
47
|
|
51
|
48
|
QRectF
|
... |
... |
@@ -60,40 +57,9 @@ GlyphBitmap::paint(QPainter* painter, |
60
|
57
|
const QStyleOptionGraphicsItem* option,
|
61
|
58
|
QWidget*)
|
62
|
59
|
{
|
63
|
|
- FT_Bitmap bitmap;
|
64
|
|
-
|
65
|
|
- int height = static_cast<int>(ceil(boundingRect_.height()));
|
66
|
|
- int width = static_cast<int>(ceil(boundingRect_.width()));
|
67
|
|
- QImage::Format format = QImage::Format_Indexed8;
|
68
|
|
-
|
69
|
|
- // XXX cover LCD and color
|
70
|
|
- if (pixelMode_ == FT_PIXEL_MODE_MONO)
|
71
|
|
- format = QImage::Format_Mono;
|
72
|
|
-
|
73
|
|
- QImage image(QSize(width, height), format);
|
74
|
|
-
|
75
|
|
- if (pixelMode_ == FT_PIXEL_MODE_MONO)
|
76
|
|
- image.setColorTable(monoColorTable_);
|
77
|
|
- else
|
78
|
|
- image.setColorTable(grayColorTable_);
|
79
|
|
-
|
80
|
|
- image.fill(0);
|
81
|
|
-
|
82
|
|
- bitmap.rows = static_cast<unsigned int>(height);
|
83
|
|
- bitmap.width = static_cast<unsigned int>(width);
|
84
|
|
- bitmap.buffer = image.bits();
|
85
|
|
- bitmap.pitch = image.bytesPerLine();
|
86
|
|
- bitmap.pixel_mode = pixelMode_;
|
87
|
|
-
|
88
|
|
- FT_Error error = FT_Outline_Get_Bitmap(library_,
|
89
|
|
- &transformed_,
|
90
|
|
- &bitmap);
|
91
|
|
- if (error)
|
92
|
|
- {
|
93
|
|
- // XXX error handling
|
|
60
|
+ if (!image_)
|
94
|
61
|
return;
|
95
|
|
- }
|
96
|
|
-
|
|
62
|
+
|
97
|
63
|
// `drawImage' doesn't work as expected:
|
98
|
64
|
// the larger the zoom, the more the pixel rectangle positions
|
99
|
65
|
// deviate from the grid lines
|
... |
... |
@@ -102,16 +68,16 @@ GlyphBitmap::paint(QPainter* painter, |
102
|
68
|
image.convertToFormat(
|
103
|
69
|
QImage::Format_ARGB32_Premultiplied));
|
104
|
70
|
#else
|
105
|
|
- const qreal lod = option->levelOfDetailFromTransform(
|
106
|
|
- painter->worldTransform());
|
|
71
|
+ const qreal lod = QStyleOptionGraphicsItem::levelOfDetailFromTransform(
|
|
72
|
+ painter->worldTransform());
|
107
|
73
|
|
108
|
74
|
painter->setPen(Qt::NoPen);
|
109
|
75
|
|
110
|
|
- for (int x = 0; x < image.width(); x++)
|
111
|
|
- for (int y = 0; y < image.height(); y++)
|
|
76
|
+ for (int x = 0; x < image_->width(); x++)
|
|
77
|
+ for (int y = 0; y < image_->height(); y++)
|
112
|
78
|
{
|
113
|
79
|
// be careful not to lose the alpha channel
|
114
|
|
- QRgb p = image.pixel(x, y);
|
|
80
|
+ QRgb p = image_->pixel(x, y);
|
115
|
81
|
painter->fillRect(QRectF(x + boundingRect_.left() - 1 / lod / 2,
|
116
|
82
|
y + boundingRect_.top() - 1 / lod / 2,
|
117
|
83
|
1 + 1 / lod,
|
src/ftinspect/glyphcomponents/glyphbitmap.hpp
... |
... |
@@ -7,33 +7,34 @@ |
7
|
7
|
|
8
|
8
|
#include <QGraphicsItem>
|
9
|
9
|
#include <QPen>
|
|
10
|
+#include <QPaintEvent>
|
|
11
|
+#include <QWidget>
|
10
|
12
|
|
11
|
13
|
#include <ft2build.h>
|
12
|
14
|
#include <freetype/freetype.h>
|
|
15
|
+#include <freetype/ftglyph.h>
|
13
|
16
|
#include <freetype/ftoutln.h>
|
14
|
17
|
|
15
|
18
|
|
|
19
|
+class Engine;
|
|
20
|
+
|
16
|
21
|
class GlyphBitmap
|
17
|
22
|
: public QGraphicsItem
|
18
|
23
|
{
|
19
|
24
|
public:
|
20
|
|
- GlyphBitmap(FT_Outline* outline,
|
21
|
|
- FT_Library library,
|
22
|
|
- FT_Pixel_Mode pixelMode,
|
23
|
|
- const QVector<QRgb>& monoColorTable,
|
24
|
|
- const QVector<QRgb>& grayColorTable);
|
25
|
|
- ~GlyphBitmap();
|
26
|
|
- QRectF boundingRect() const;
|
|
25
|
+ GlyphBitmap(QImage* image,
|
|
26
|
+ QRect rect);
|
|
27
|
+ GlyphBitmap(int glyphIndex,
|
|
28
|
+ FT_Glyph glyph,
|
|
29
|
+ Engine* engine);
|
|
30
|
+ ~GlyphBitmap() override;
|
|
31
|
+ QRectF boundingRect() const override;
|
27
|
32
|
void paint(QPainter* painter,
|
28
|
33
|
const QStyleOptionGraphicsItem* option,
|
29
|
|
- QWidget* widget);
|
|
34
|
+ QWidget* widget) override;
|
30
|
35
|
|
31
|
36
|
private:
|
32
|
|
- FT_Outline transformed_;
|
33
|
|
- FT_Library library_;
|
34
|
|
- unsigned char pixelMode_;
|
35
|
|
- const QVector<QRgb>& monoColorTable_;
|
36
|
|
- const QVector<QRgb>& grayColorTable_;
|
|
37
|
+ QImage* image_ = NULL;
|
37
|
38
|
QRectF boundingRect_;
|
38
|
39
|
};
|
39
|
40
|
|
src/ftinspect/glyphcomponents/glyphoutline.cpp
... |
... |
@@ -87,11 +87,17 @@ static FT_Outline_Funcs outlineFuncs = |
87
|
87
|
} // extern "C"
|
88
|
88
|
|
89
|
89
|
|
90
|
|
-GlyphOutline::GlyphOutline(const QPen& outlineP,
|
91
|
|
- FT_Outline* outln)
|
92
|
|
-: outlinePen_(outlineP),
|
93
|
|
- outline_(outln)
|
|
90
|
+GlyphOutline::GlyphOutline(const QPen& pen,
|
|
91
|
+ FT_Glyph glyph)
|
|
92
|
+: outlinePen_(pen)
|
94
|
93
|
{
|
|
94
|
+ if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
95
|
+ {
|
|
96
|
+ outline_ = NULL;
|
|
97
|
+ return;
|
|
98
|
+ }
|
|
99
|
+ outline_ = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
|
|
100
|
+
|
95
|
101
|
FT_BBox cbox;
|
96
|
102
|
|
97
|
103
|
qreal halfPenWidth = outlinePen_.widthF();
|
... |
... |
@@ -117,6 +123,8 @@ GlyphOutline::paint(QPainter* painter, |
117
|
123
|
const QStyleOptionGraphicsItem*,
|
118
|
124
|
QWidget*)
|
119
|
125
|
{
|
|
126
|
+ if (!outline_)
|
|
127
|
+ return;
|
120
|
128
|
painter->setPen(outlinePen_);
|
121
|
129
|
|
122
|
130
|
QPainterPath path;
|
src/ftinspect/glyphcomponents/glyphoutline.hpp
... |
... |
@@ -10,6 +10,7 @@ |
10
|
10
|
|
11
|
11
|
#include <ft2build.h>
|
12
|
12
|
#include <freetype/freetype.h>
|
|
13
|
+#include <freetype/ftglyph.h>
|
13
|
14
|
#include <freetype/ftoutln.h>
|
14
|
15
|
|
15
|
16
|
|
... |
... |
@@ -18,11 +19,11 @@ class GlyphOutline |
18
|
19
|
{
|
19
|
20
|
public:
|
20
|
21
|
GlyphOutline(const QPen& pen,
|
21
|
|
- FT_Outline* outline);
|
22
|
|
- QRectF boundingRect() const;
|
|
22
|
+ FT_Glyph glyph);
|
|
23
|
+ QRectF boundingRect() const override;
|
23
|
24
|
void paint(QPainter* painter,
|
24
|
25
|
const QStyleOptionGraphicsItem* option,
|
25
|
|
- QWidget* widget);
|
|
26
|
+ QWidget* widget) override;
|
26
|
27
|
|
27
|
28
|
private:
|
28
|
29
|
QPen outlinePen_;
|
src/ftinspect/glyphcomponents/glyphpointnumbers.cpp
... |
... |
@@ -12,11 +12,17 @@ |
12
|
12
|
|
13
|
13
|
GlyphPointNumbers::GlyphPointNumbers(const QPen& onP,
|
14
|
14
|
const QPen& offP,
|
15
|
|
- FT_Outline* outln)
|
|
15
|
+ FT_Glyph glyph)
|
16
|
16
|
: onPen_(onP),
|
17
|
|
- offPen_(offP),
|
18
|
|
- outline_(outln)
|
|
17
|
+ offPen_(offP)
|
19
|
18
|
{
|
|
19
|
+ if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
20
|
+ {
|
|
21
|
+ outline_ = NULL;
|
|
22
|
+ return;
|
|
23
|
+ }
|
|
24
|
+ outline_ = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
|
|
25
|
+
|
20
|
26
|
FT_BBox cbox;
|
21
|
27
|
|
22
|
28
|
FT_Outline_Get_CBox(outline_, &cbox);
|
... |
... |
@@ -41,6 +47,8 @@ GlyphPointNumbers::paint(QPainter* painter, |
41
|
47
|
const QStyleOptionGraphicsItem* option,
|
42
|
48
|
QWidget*)
|
43
|
49
|
{
|
|
50
|
+ if (!outline_)
|
|
51
|
+ return;
|
44
|
52
|
const qreal lod = option->levelOfDetailFromTransform(
|
45
|
53
|
painter->worldTransform());
|
46
|
54
|
|
src/ftinspect/glyphcomponents/glyphpointnumbers.hpp
... |
... |
@@ -10,6 +10,7 @@ |
10
|
10
|
|
11
|
11
|
#include <ft2build.h>
|
12
|
12
|
#include <freetype/freetype.h>
|
|
13
|
+#include <freetype/ftglyph.h>
|
13
|
14
|
#include <freetype/ftoutln.h>
|
14
|
15
|
|
15
|
16
|
|
... |
... |
@@ -19,11 +20,11 @@ class GlyphPointNumbers |
19
|
20
|
public:
|
20
|
21
|
GlyphPointNumbers(const QPen& onPen,
|
21
|
22
|
const QPen& offPen,
|
22
|
|
- FT_Outline* outline);
|
23
|
|
- QRectF boundingRect() const;
|
|
23
|
+ FT_Glyph glyph);
|
|
24
|
+ QRectF boundingRect() const override;
|
24
|
25
|
void paint(QPainter* painter,
|
25
|
26
|
const QStyleOptionGraphicsItem* option,
|
26
|
|
- QWidget* widget);
|
|
27
|
+ QWidget* widget) override;
|
27
|
28
|
|
28
|
29
|
private:
|
29
|
30
|
QPen onPen_;
|
src/ftinspect/glyphcomponents/glyphpoints.cpp
... |
... |
@@ -11,11 +11,17 @@ |
11
|
11
|
|
12
|
12
|
GlyphPoints::GlyphPoints(const QPen& onP,
|
13
|
13
|
const QPen& offP,
|
14
|
|
- FT_Outline* outln)
|
|
14
|
+ FT_Glyph glyph)
|
15
|
15
|
: onPen_(onP),
|
16
|
|
- offPen_(offP),
|
17
|
|
- outline_(outln)
|
|
16
|
+ offPen_(offP)
|
18
|
17
|
{
|
|
18
|
+ if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
19
|
+ {
|
|
20
|
+ outline_ = NULL;
|
|
21
|
+ return;
|
|
22
|
+ }
|
|
23
|
+ outline_ = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
|
|
24
|
+
|
19
|
25
|
FT_BBox cbox;
|
20
|
26
|
|
21
|
27
|
qreal halfPenWidth = qMax(onPen_.widthF(), offPen_.widthF()) / 2;
|
... |
... |
@@ -41,6 +47,9 @@ GlyphPoints::paint(QPainter* painter, |
41
|
47
|
const QStyleOptionGraphicsItem* option,
|
42
|
48
|
QWidget*)
|
43
|
49
|
{
|
|
50
|
+ if (!outline_)
|
|
51
|
+ return;
|
|
52
|
+
|
44
|
53
|
const qreal lod = option->levelOfDetailFromTransform(
|
45
|
54
|
painter->worldTransform());
|
46
|
55
|
|
src/ftinspect/glyphcomponents/glyphpoints.hpp
... |
... |
@@ -10,6 +10,7 @@ |
10
|
10
|
|
11
|
11
|
#include <ft2build.h>
|
12
|
12
|
#include <freetype/freetype.h>
|
|
13
|
+#include <freetype/ftglyph.h>
|
13
|
14
|
#include <freetype/ftoutln.h>
|
14
|
15
|
|
15
|
16
|
|
... |
... |
@@ -19,11 +20,11 @@ class GlyphPoints |
19
|
20
|
public:
|
20
|
21
|
GlyphPoints(const QPen& onPen,
|
21
|
22
|
const QPen& offPen,
|
22
|
|
- FT_Outline* outline);
|
23
|
|
- QRectF boundingRect() const;
|
|
23
|
+ FT_Glyph glyph);
|
|
24
|
+ QRectF boundingRect() const override;
|
24
|
25
|
void paint(QPainter* painter,
|
25
|
26
|
const QStyleOptionGraphicsItem* option,
|
26
|
|
- QWidget* widget);
|
|
27
|
+ QWidget* widget) override;
|
27
|
28
|
|
28
|
29
|
private:
|
29
|
30
|
QPen onPen_;
|
src/ftinspect/glyphcomponents/graphicsdefault.cpp
|
1
|
+// graphicsdefault.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "graphicsdefault.hpp"
|
|
6
|
+
|
|
7
|
+GraphicsDefault* GraphicsDefault::instance_ = NULL;
|
|
8
|
+
|
|
9
|
+GraphicsDefault::GraphicsDefault()
|
|
10
|
+{
|
|
11
|
+ // XXX make this user-configurable
|
|
12
|
+
|
|
13
|
+ axisPen.setColor(Qt::black);
|
|
14
|
+ axisPen.setWidth(0);
|
|
15
|
+ blueZonePen.setColor(QColor(64, 64, 255, 64)); // light blue
|
|
16
|
+ blueZonePen.setWidth(0);
|
|
17
|
+ // Don't make this solid
|
|
18
|
+ gridPen.setColor(QColor(0, 0, 0, 255 - QColor(Qt::lightGray).red()));
|
|
19
|
+ gridPen.setWidth(0);
|
|
20
|
+ offPen.setColor(Qt::darkGreen);
|
|
21
|
+ offPen.setWidth(3);
|
|
22
|
+ onPen.setColor(Qt::red);
|
|
23
|
+ onPen.setWidth(3);
|
|
24
|
+ outlinePen.setColor(Qt::red);
|
|
25
|
+ outlinePen.setWidth(0);
|
|
26
|
+ segmentPen.setColor(QColor(64, 255, 128, 64)); // light green
|
|
27
|
+ segmentPen.setWidth(0);
|
|
28
|
+
|
|
29
|
+ advanceAuxPen.setColor(QColor(110, 52, 235)); // kind of blue
|
|
30
|
+ advanceAuxPen.setWidth(0);
|
|
31
|
+ ascDescAuxPen.setColor(QColor(255, 0, 0)); // red
|
|
32
|
+ ascDescAuxPen.setWidth(0);
|
|
33
|
+}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+GraphicsDefault*
|
|
37
|
+GraphicsDefault::deafultInstance()
|
|
38
|
+{
|
|
39
|
+ if (!instance_)
|
|
40
|
+ instance_ = new GraphicsDefault;
|
|
41
|
+
|
|
42
|
+ return instance_;
|
|
43
|
+}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+// end of graphicsdefault.cpp |
src/ftinspect/glyphcomponents/graphicsdefault.hpp
|
1
|
+// graphicsdefault.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include <QVector>
|
|
8
|
+#include <QRgb>
|
|
9
|
+#include <QPen>
|
|
10
|
+
|
|
11
|
+// This is default graphics objects fed into render functions.
|
|
12
|
+struct GraphicsDefault
|
|
13
|
+{
|
|
14
|
+ QPen axisPen;
|
|
15
|
+ QPen blueZonePen;
|
|
16
|
+ QPen gridPen;
|
|
17
|
+ QPen offPen;
|
|
18
|
+ QPen onPen;
|
|
19
|
+ QPen outlinePen;
|
|
20
|
+ QPen segmentPen;
|
|
21
|
+
|
|
22
|
+ QPen advanceAuxPen;
|
|
23
|
+ QPen ascDescAuxPen;
|
|
24
|
+
|
|
25
|
+ GraphicsDefault();
|
|
26
|
+
|
|
27
|
+ static GraphicsDefault* deafultInstance();
|
|
28
|
+
|
|
29
|
+private:
|
|
30
|
+ static GraphicsDefault* instance_;
|
|
31
|
+};
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+// end of graphicsdefault.hpp |
src/ftinspect/glyphcomponents/grid.cpp
... |
... |
@@ -5,88 +5,131 @@ |
5
|
5
|
|
6
|
6
|
#include "grid.hpp"
|
7
|
7
|
|
|
8
|
+#include "graphicsdefault.hpp"
|
|
9
|
+
|
8
|
10
|
#include <QPainter>
|
9
|
11
|
#include <QStyleOptionGraphicsItem>
|
|
12
|
+#include <QGraphicsWidget>
|
|
13
|
+#include <QGraphicsView>
|
10
|
14
|
|
11
|
15
|
|
12
|
|
-Grid::Grid(const QPen& gridP,
|
13
|
|
- const QPen& axisP)
|
14
|
|
-: gridPen_(gridP),
|
15
|
|
- axisPen_(axisP)
|
|
16
|
+Grid::Grid(QGraphicsView* parentView)
|
|
17
|
+: parentView_(parentView)
|
16
|
18
|
{
|
17
|
19
|
// empty
|
|
20
|
+ updateRect();
|
18
|
21
|
}
|
19
|
22
|
|
20
|
23
|
|
21
|
24
|
QRectF
|
22
|
25
|
Grid::boundingRect() const
|
23
|
26
|
{
|
24
|
|
- // XXX fix size
|
|
27
|
+ return rect_;
|
|
28
|
+}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+void
|
|
32
|
+Grid::updateRect()
|
|
33
|
+{
|
|
34
|
+ auto viewport = parentView_->mapToScene(parentView_->viewport()->geometry())
|
|
35
|
+ .boundingRect()
|
|
36
|
+ .toRect();
|
|
37
|
+ int minX = std::min(viewport.left() - 10, -100);
|
|
38
|
+ int minY = std::min(viewport.top() - 10, -100);
|
|
39
|
+ int maxX = std::max(viewport.right() + 10, 100);
|
|
40
|
+ int maxY = std::max(viewport.bottom() + 10, 100);
|
|
41
|
+
|
|
42
|
+ auto newSceneRect = QRectF(QPointF(minX - 20, minY - 20),
|
|
43
|
+ QPointF(maxX + 20, maxY + 20));
|
|
44
|
+ if (sceneRect_ != newSceneRect && scene())
|
|
45
|
+ {
|
|
46
|
+ scene()->setSceneRect(newSceneRect);
|
|
47
|
+ sceneRect_ = newSceneRect;
|
|
48
|
+ }
|
25
|
49
|
|
26
|
50
|
// no need to take care of pen width
|
27
|
|
- return QRectF(-100, -100,
|
28
|
|
- 200, 200);
|
|
51
|
+ rect_ = QRectF(QPointF(minX, minY),
|
|
52
|
+ QPointF(maxX, maxY));
|
29
|
53
|
}
|
30
|
54
|
|
31
|
55
|
|
32
|
|
-// XXX call this in a `myQDraphicsView::drawBackground' derived method
|
33
|
|
-// to always fill the complete viewport
|
34
|
|
-
|
35
|
56
|
void
|
36
|
57
|
Grid::paint(QPainter* painter,
|
37
|
58
|
const QStyleOptionGraphicsItem* option,
|
38
|
|
- QWidget*)
|
|
59
|
+ QWidget* widget)
|
39
|
60
|
{
|
|
61
|
+ auto gb = GraphicsDefault::deafultInstance();
|
|
62
|
+ auto br = boundingRect().toRect();
|
|
63
|
+ int minX = br.left();
|
|
64
|
+ int minY = br.top();
|
|
65
|
+ int maxX = br.right();
|
|
66
|
+ int maxY = br.bottom();
|
|
67
|
+
|
40
|
68
|
const qreal lod = option->levelOfDetailFromTransform(
|
41
|
69
|
painter->worldTransform());
|
42
|
|
-
|
43
|
|
- painter->setPen(gridPen_);
|
44
|
|
-
|
45
|
|
- // don't mark pixel center with a cross if magnification is too small
|
46
|
|
- if (lod > 20)
|
|
70
|
+ if (showGrid_)
|
47
|
71
|
{
|
48
|
|
- int halfLength = 1;
|
49
|
|
-
|
50
|
|
- // cf. QSpinBoxx
|
51
|
|
- if (lod > 640)
|
52
|
|
- halfLength = 6;
|
53
|
|
- else if (lod > 320)
|
54
|
|
- halfLength = 5;
|
55
|
|
- else if (lod > 160)
|
56
|
|
- halfLength = 4;
|
57
|
|
- else if (lod > 80)
|
58
|
|
- halfLength = 3;
|
59
|
|
- else if (lod > 40)
|
60
|
|
- halfLength = 2;
|
61
|
|
-
|
62
|
|
- for (qreal x = -100; x < 100; x++)
|
63
|
|
- for (qreal y = -100; y < 100; y++)
|
64
|
|
- {
|
65
|
|
- painter->drawLine(QLineF(x + 0.5, y + 0.5 - halfLength / lod,
|
66
|
|
- x + 0.5, y + 0.5 + halfLength / lod));
|
67
|
|
- painter->drawLine(QLineF(x + 0.5 - halfLength / lod, y + 0.5,
|
68
|
|
- x + 0.5 + halfLength / lod, y + 0.5));
|
69
|
|
- }
|
|
72
|
+ painter->setPen(gb->gridPen);
|
|
73
|
+
|
|
74
|
+ // don't mark pixel center with a cross if magnification is too small
|
|
75
|
+ if (lod > 20)
|
|
76
|
+ {
|
|
77
|
+ int halfLength = 1;
|
|
78
|
+
|
|
79
|
+ // cf. QSpinBoxx
|
|
80
|
+ if (lod > 640)
|
|
81
|
+ halfLength = 6;
|
|
82
|
+ else if (lod > 320)
|
|
83
|
+ halfLength = 5;
|
|
84
|
+ else if (lod > 160)
|
|
85
|
+ halfLength = 4;
|
|
86
|
+ else if (lod > 80)
|
|
87
|
+ halfLength = 3;
|
|
88
|
+ else if (lod > 40)
|
|
89
|
+ halfLength = 2;
|
|
90
|
+
|
|
91
|
+ for (qreal x = minX; x < maxX; x++)
|
|
92
|
+ for (qreal y = minY; y < maxY; y++)
|
|
93
|
+ {
|
|
94
|
+ painter->drawLine(QLineF(x + 0.5, y + 0.5 - halfLength / lod,
|
|
95
|
+ x + 0.5, y + 0.5 + halfLength / lod));
|
|
96
|
+ painter->drawLine(QLineF(x + 0.5 - halfLength / lod, y + 0.5,
|
|
97
|
+ x + 0.5 + halfLength / lod, y + 0.5));
|
|
98
|
+ }
|
|
99
|
+ }
|
|
100
|
+
|
|
101
|
+ // don't draw grid if magnification is too small
|
|
102
|
+ if (lod >= 5)
|
|
103
|
+ {
|
|
104
|
+ for (int x = minX; x <= maxX; x++)
|
|
105
|
+ painter->drawLine(x, minY,
|
|
106
|
+ x, maxY);
|
|
107
|
+ for (int y = minY; y <= maxY; y++)
|
|
108
|
+ painter->drawLine(minX, y,
|
|
109
|
+ maxX, y);
|
|
110
|
+ }
|
|
111
|
+
|
|
112
|
+ painter->setPen(gb->axisPen);
|
|
113
|
+
|
|
114
|
+ painter->drawLine(0, minY,
|
|
115
|
+ 0, maxY);
|
|
116
|
+ painter->drawLine(minX, 0,
|
|
117
|
+ maxX, 0);
|
70
|
118
|
}
|
71
|
119
|
|
72
|
|
- // don't draw grid if magnification is too small
|
73
|
|
- if (lod >= 5)
|
|
120
|
+ if (showAuxLines_)
|
74
|
121
|
{
|
75
|
|
- // XXX fix size
|
76
|
|
- for (int x = -100; x <= 100; x++)
|
77
|
|
- painter->drawLine(x, -100,
|
78
|
|
- x, 100);
|
79
|
|
- for (int y = -100; y <= 100; y++)
|
80
|
|
- painter->drawLine(-100, y,
|
81
|
|
- 100, y);
|
|
122
|
+ // TODO: impl
|
82
|
123
|
}
|
|
124
|
+}
|
83
|
125
|
|
84
|
|
- painter->setPen(axisPen_);
|
85
|
126
|
|
86
|
|
- painter->drawLine(0, -100,
|
87
|
|
- 0, 100);
|
88
|
|
- painter->drawLine(-100, 0,
|
89
|
|
- 100, 0);
|
|
127
|
+void
|
|
128
|
+Grid::setShowGrid(bool showGrid, bool showAuxLines)
|
|
129
|
+{
|
|
130
|
+ showGrid_ = showGrid;
|
|
131
|
+ showAuxLines_ = showAuxLines;
|
|
132
|
+ update();
|
90
|
133
|
}
|
91
|
134
|
|
92
|
135
|
|
src/ftinspect/glyphcomponents/grid.hpp
... |
... |
@@ -6,23 +6,30 @@ |
6
|
6
|
#pragma once
|
7
|
7
|
|
8
|
8
|
#include <QGraphicsItem>
|
|
9
|
+#include <QGraphicsView>
|
9
|
10
|
#include <QPen>
|
10
|
11
|
|
11
|
|
-
|
12
|
12
|
class Grid
|
13
|
13
|
: public QGraphicsItem
|
14
|
14
|
{
|
15
|
15
|
public:
|
16
|
|
- Grid(const QPen& gridPen,
|
17
|
|
- const QPen& axisPen);
|
18
|
|
- QRectF boundingRect() const;
|
|
16
|
+ Grid(QGraphicsView* parentView);
|
|
17
|
+ QRectF boundingRect() const override;
|
19
|
18
|
void paint(QPainter* painter,
|
20
|
19
|
const QStyleOptionGraphicsItem* option,
|
21
|
|
- QWidget* widget);
|
|
20
|
+ QWidget* widget) override;
|
|
21
|
+
|
|
22
|
+ void setShowGrid(bool showGrid, bool showAuxLines);
|
|
23
|
+
|
|
24
|
+ void updateRect(); // there's no signal/slots for QGraphicsItem.
|
22
|
25
|
|
23
|
26
|
private:
|
24
|
|
- QPen gridPen_;
|
25
|
|
- QPen axisPen_;
|
|
27
|
+ QGraphicsView* parentView_;
|
|
28
|
+ QRectF rect_;
|
|
29
|
+ QRectF sceneRect_;
|
|
30
|
+
|
|
31
|
+ bool showGrid_ = true;
|
|
32
|
+ bool showAuxLines_ = false;
|
26
|
33
|
};
|
27
|
34
|
|
28
|
35
|
|
src/ftinspect/maingui.cpp
|
1
|
+// maingui.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2016-2022 by Werner Lemberg.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+#include "maingui.hpp"
|
|
7
|
+
|
|
8
|
+#include <QApplication>
|
|
9
|
+#include <QFileDialog>
|
|
10
|
+#include <QMessageBox>
|
|
11
|
+#include <QSettings>
|
|
12
|
+#include <QScrollBar>
|
|
13
|
+#include <QStatusBar>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+MainGUI::MainGUI(Engine* engine)
|
|
17
|
+: engine_(engine)
|
|
18
|
+{
|
|
19
|
+ createLayout();
|
|
20
|
+ createConnections();
|
|
21
|
+ createActions();
|
|
22
|
+ createMenus();
|
|
23
|
+
|
|
24
|
+ readSettings();
|
|
25
|
+ setUnifiedTitleAndToolBarOnMac(true);
|
|
26
|
+
|
|
27
|
+ show();
|
|
28
|
+}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+MainGUI::~MainGUI()
|
|
32
|
+{
|
|
33
|
+ // empty
|
|
34
|
+}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+// overloading
|
|
38
|
+
|
|
39
|
+void
|
|
40
|
+MainGUI::closeEvent(QCloseEvent* event)
|
|
41
|
+{
|
|
42
|
+ writeSettings();
|
|
43
|
+ event->accept();
|
|
44
|
+}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+void
|
|
48
|
+MainGUI::keyPressEvent(QKeyEvent* event)
|
|
49
|
+{
|
|
50
|
+ // Delegate key events to tabs
|
|
51
|
+ if (!tabWidget_->currentWidget()->eventFilter(this, event))
|
|
52
|
+ QMainWindow::keyPressEvent(event);
|
|
53
|
+}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+void
|
|
57
|
+MainGUI::about()
|
|
58
|
+{
|
|
59
|
+ QMessageBox::about(
|
|
60
|
+ this,
|
|
61
|
+ tr("About ftinspect"),
|
|
62
|
+ tr("<p>This is <b>ftinspect</b> version %1<br>"
|
|
63
|
+ " Copyright %2 2016-2022<br>"
|
|
64
|
+ " by Werner Lemberg <tt><wl@gnu.org></tt></p>"
|
|
65
|
+ ""
|
|
66
|
+ "<p><b>ftinspect</b> shows how a font gets rendered"
|
|
67
|
+ " by FreeType, allowing control over virtually"
|
|
68
|
+ " all rendering parameters.</p>"
|
|
69
|
+ ""
|
|
70
|
+ "<p>License:"
|
|
71
|
+ " <a href=''>FreeType"
|
|
72
|
+ " License (FTL)</a> or"
|
|
73
|
+ " <a href=''>GNU"
|
|
74
|
+ " GPLv2</a></p>")
|
|
75
|
+ .arg(QApplication::applicationVersion())
|
|
76
|
+ .arg(QChar(0xA9)));
|
|
77
|
+}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+void
|
|
81
|
+MainGUI::aboutQt()
|
|
82
|
+{
|
|
83
|
+ QApplication::aboutQt();
|
|
84
|
+}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+void
|
|
88
|
+MainGUI::loadFonts()
|
|
89
|
+{
|
|
90
|
+ QStringList files = QFileDialog::getOpenFileNames(
|
|
91
|
+ this,
|
|
92
|
+ tr("Load one or more fonts"),
|
|
93
|
+ QDir::homePath(),
|
|
94
|
+ "",
|
|
95
|
+ NULL,
|
|
96
|
+ QFileDialog::ReadOnly);
|
|
97
|
+ openFonts(files);
|
|
98
|
+}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+void
|
|
102
|
+MainGUI::openFonts(QStringList const& fileNames)
|
|
103
|
+{
|
|
104
|
+ engine_->openFonts(fileNames);
|
|
105
|
+ tripletSelector_->repopulateFonts();
|
|
106
|
+}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+void
|
|
110
|
+MainGUI::onTripletChanged()
|
|
111
|
+{
|
|
112
|
+ auto state = settingPanel_->blockSignals(true);
|
|
113
|
+ settingPanel_->onFontChanged();
|
|
114
|
+ settingPanel_->blockSignals(state);
|
|
115
|
+ reloadCurrentTabFont();
|
|
116
|
+}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+void
|
|
120
|
+MainGUI::switchTab()
|
|
121
|
+{
|
|
122
|
+ reloadCurrentTabFont();
|
|
123
|
+ lastTab_ = tabWidget_->currentWidget();
|
|
124
|
+}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+void
|
|
128
|
+MainGUI::repaintCurrentTab()
|
|
129
|
+{
|
|
130
|
+ applySettings();
|
|
131
|
+ tabs_[tabWidget_->currentIndex()]->repaintGlyph();
|
|
132
|
+}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+void
|
|
136
|
+MainGUI::reloadCurrentTabFont()
|
|
137
|
+{
|
|
138
|
+ engine_->resetCache();
|
|
139
|
+ applySettings();
|
|
140
|
+ auto index = tabWidget_->currentIndex();
|
|
141
|
+ if (index >= 0 && static_cast<size_t>(index) < tabs_.size())
|
|
142
|
+ tabs_[index]->reloadFont();
|
|
143
|
+}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+void
|
|
147
|
+MainGUI::applySettings()
|
|
148
|
+{
|
|
149
|
+ settingPanel_->applySettings();
|
|
150
|
+ settingPanel_->applyDelayedSettings();
|
|
151
|
+}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+// XXX distances are specified in pixels,
|
|
155
|
+// making the layout dependent on the output device resolution
|
|
156
|
+void
|
|
157
|
+MainGUI::createLayout()
|
|
158
|
+{
|
|
159
|
+ // left side
|
|
160
|
+ settingPanel_ = new SettingPanel(this, engine_);
|
|
161
|
+
|
|
162
|
+ leftLayout_ = new QVBoxLayout; // The only point is to set a margin->remove?
|
|
163
|
+ leftLayout_->addWidget(settingPanel_);
|
|
164
|
+ leftLayout_->setContentsMargins(32, 32, 0, 16);
|
|
165
|
+
|
|
166
|
+ // we don't want to expand the left side horizontally;
|
|
167
|
+ // to change the policy we have to use a widget wrapper
|
|
168
|
+ leftWidget_ = new QWidget(this);
|
|
169
|
+ leftWidget_->setLayout(leftLayout_);
|
|
170
|
+ leftWidget_->setMaximumWidth(400);
|
|
171
|
+
|
|
172
|
+ // right side
|
|
173
|
+ singularTab_ = new SingularTab(this, engine_);
|
|
174
|
+
|
|
175
|
+ tabWidget_ = new QTabWidget(this);
|
|
176
|
+ tabWidget_->setObjectName("mainTab"); // for stylesheet
|
|
177
|
+
|
|
178
|
+ // Note those two list must be in sync
|
|
179
|
+ tabs_.push_back(singularTab_);
|
|
180
|
+ tabWidget_->addTab(singularTab_, tr("Singular Grid View"));
|
|
181
|
+ lastTab_ = singularTab_;
|
|
182
|
+
|
|
183
|
+ tabWidget_->setTabToolTip(0, tr("View single glyph in grid view.\n"
|
|
184
|
+ "For pixelwise inspection of the glyphs."));
|
|
185
|
+ tripletSelector_ = new TripletSelector(this, engine_);
|
|
186
|
+
|
|
187
|
+ rightLayout_ = new QVBoxLayout;
|
|
188
|
+ //rightLayout_->addWidget(fontNameLabel_);
|
|
189
|
+ rightLayout_->addWidget(tabWidget_); // same for `leftLayout_`: Remove?
|
|
190
|
+ rightLayout_->setContentsMargins(16, 32, 32, 16);
|
|
191
|
+
|
|
192
|
+ // for symmetry with the left side use a widget also
|
|
193
|
+ rightWidget_ = new QWidget(this);
|
|
194
|
+ rightWidget_->setLayout(rightLayout_);
|
|
195
|
+
|
|
196
|
+ // the whole thing
|
|
197
|
+ mainPartLayout_ = new QHBoxLayout;
|
|
198
|
+ mainPartLayout_->addWidget(leftWidget_);
|
|
199
|
+ mainPartLayout_->addWidget(rightWidget_);
|
|
200
|
+
|
|
201
|
+ ftinspectLayout_ = new QVBoxLayout;
|
|
202
|
+ ftinspectLayout_->setSpacing(0);
|
|
203
|
+ ftinspectLayout_->addLayout(mainPartLayout_);
|
|
204
|
+ ftinspectLayout_->addWidget(tripletSelector_);
|
|
205
|
+ ftinspectLayout_->setContentsMargins(0, 0, 0, 0);
|
|
206
|
+
|
|
207
|
+ ftinspectWidget_ = new QWidget(this);
|
|
208
|
+ ftinspectWidget_->setLayout(ftinspectLayout_);
|
|
209
|
+
|
|
210
|
+ ftinspectLayout_->setSizeConstraint(QLayout::SetNoConstraint);
|
|
211
|
+ layout()->setSizeConstraint(QLayout::SetNoConstraint);
|
|
212
|
+ resize(ftinspectWidget_->minimumSizeHint().width(),
|
|
213
|
+ 700 * logicalDpiY() / 96);
|
|
214
|
+
|
|
215
|
+ statusBar()->hide(); // remove the extra space
|
|
216
|
+ setCentralWidget(ftinspectWidget_);
|
|
217
|
+ setWindowTitle("ftinspect");
|
|
218
|
+}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+void
|
|
222
|
+MainGUI::createConnections()
|
|
223
|
+{
|
|
224
|
+ connect(settingPanel_, &SettingPanel::fontReloadNeeded,
|
|
225
|
+ this, &MainGUI::reloadCurrentTabFont);
|
|
226
|
+ connect(settingPanel_, &SettingPanel::repaintNeeded,
|
|
227
|
+ this, &MainGUI::repaintCurrentTab);
|
|
228
|
+
|
|
229
|
+ connect(tabWidget_, &QTabWidget::currentChanged,
|
|
230
|
+ this, &MainGUI::switchTab);
|
|
231
|
+
|
|
232
|
+ connect(tripletSelector_, &TripletSelector::tripletChanged,
|
|
233
|
+ this, &MainGUI::onTripletChanged);
|
|
234
|
+}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+void
|
|
238
|
+MainGUI::createActions()
|
|
239
|
+{
|
|
240
|
+ loadFontsAct_ = new QAction(tr("&Load Fonts"), this);
|
|
241
|
+ loadFontsAct_->setShortcuts(QKeySequence::Open);
|
|
242
|
+ connect(loadFontsAct_, &QAction::triggered, this, &MainGUI::loadFonts);
|
|
243
|
+
|
|
244
|
+ closeFontAct_ = new QAction(tr("&Close Font"), this);
|
|
245
|
+ closeFontAct_->setShortcuts(QKeySequence::Close);
|
|
246
|
+ connect(closeFontAct_, &QAction::triggered,
|
|
247
|
+ tripletSelector_, &TripletSelector::closeCurrentFont);
|
|
248
|
+
|
|
249
|
+ exitAct_ = new QAction(tr("E&xit"), this);
|
|
250
|
+ exitAct_->setShortcuts(QKeySequence::Quit);
|
|
251
|
+ connect(exitAct_, &QAction::triggered, this, &MainGUI::close);
|
|
252
|
+
|
|
253
|
+ aboutAct_ = new QAction(tr("&About"), this);
|
|
254
|
+ connect(aboutAct_, &QAction::triggered, this, &MainGUI::about);
|
|
255
|
+
|
|
256
|
+ aboutQtAct_ = new QAction(tr("About &Qt"), this);
|
|
257
|
+ connect(aboutQtAct_, &QAction::triggered, this, &MainGUI::aboutQt);
|
|
258
|
+}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+void
|
|
262
|
+MainGUI::createMenus()
|
|
263
|
+{
|
|
264
|
+ menuFile_ = menuBar()->addMenu(tr("&File"));
|
|
265
|
+ menuFile_->addAction(loadFontsAct_);
|
|
266
|
+ menuFile_->addAction(closeFontAct_);
|
|
267
|
+ menuFile_->addAction(exitAct_);
|
|
268
|
+
|
|
269
|
+ menuHelp_ = menuBar()->addMenu(tr("&Help"));
|
|
270
|
+ menuHelp_->addAction(aboutAct_);
|
|
271
|
+ menuHelp_->addAction(aboutQtAct_);
|
|
272
|
+}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+void
|
|
276
|
+MainGUI::readSettings()
|
|
277
|
+{
|
|
278
|
+ QSettings settings;
|
|
279
|
+// QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
|
|
280
|
+// QSize size = settings.value("size", QSize(400, 400)).toSize();
|
|
281
|
+// resize(size);
|
|
282
|
+// move(pos);
|
|
283
|
+}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+void
|
|
287
|
+MainGUI::writeSettings()
|
|
288
|
+{
|
|
289
|
+ QSettings settings;
|
|
290
|
+// settings.setValue("pos", pos());
|
|
291
|
+// settings.setValue("size", size());
|
|
292
|
+}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+// end of maingui.cpp |
src/ftinspect/maingui.hpp
|
1
|
+// maingui.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2016-2022 by Werner Lemberg.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+#pragma once
|
|
7
|
+
|
|
8
|
+#include "engine/engine.hpp"
|
|
9
|
+#include "widgets/tripletselector.hpp"
|
|
10
|
+#include "panels/settingpanel.hpp"
|
|
11
|
+#include "panels/abstracttab.hpp"
|
|
12
|
+#include "panels/singular.hpp"
|
|
13
|
+
|
|
14
|
+#include <vector>
|
|
15
|
+#include <QAction>
|
|
16
|
+#include <QCloseEvent>
|
|
17
|
+#include <QGridLayout>
|
|
18
|
+#include <QDockWidget>
|
|
19
|
+#include <QBoxLayout>
|
|
20
|
+#include <QLabel>
|
|
21
|
+#include <QMainWindow>
|
|
22
|
+#include <QMenu>
|
|
23
|
+#include <QMenuBar>
|
|
24
|
+#include <QTabWidget>
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+class MainGUI
|
|
28
|
+: public QMainWindow
|
|
29
|
+{
|
|
30
|
+ Q_OBJECT
|
|
31
|
+
|
|
32
|
+public:
|
|
33
|
+ MainGUI(Engine* engine);
|
|
34
|
+ ~MainGUI() override;
|
|
35
|
+
|
|
36
|
+ friend class Engine;
|
|
37
|
+ friend FT_Error faceRequester(FTC_FaceID,
|
|
38
|
+ FT_Library,
|
|
39
|
+ FT_Pointer,
|
|
40
|
+ FT_Face*);
|
|
41
|
+
|
|
42
|
+protected:
|
|
43
|
+ void closeEvent(QCloseEvent*) override;
|
|
44
|
+ void keyPressEvent(QKeyEvent* event) override;
|
|
45
|
+
|
|
46
|
+private slots:
|
|
47
|
+ void about();
|
|
48
|
+ void aboutQt();
|
|
49
|
+ void repaintCurrentTab();
|
|
50
|
+ void reloadCurrentTabFont();
|
|
51
|
+ void loadFonts();
|
|
52
|
+ void onTripletChanged();
|
|
53
|
+ void switchTab();
|
|
54
|
+
|
|
55
|
+private:
|
|
56
|
+ Engine* engine_;
|
|
57
|
+
|
|
58
|
+ int currentNumberOfGlyphs_;
|
|
59
|
+
|
|
60
|
+ // layout related stuff
|
|
61
|
+ QAction *aboutAct_;
|
|
62
|
+ QAction *aboutQtAct_;
|
|
63
|
+ QAction *closeFontAct_;
|
|
64
|
+ QAction *exitAct_;
|
|
65
|
+ QAction *loadFontsAct_;
|
|
66
|
+
|
|
67
|
+ QVBoxLayout *ftinspectLayout_;
|
|
68
|
+ QHBoxLayout *mainPartLayout_;
|
|
69
|
+
|
|
70
|
+ QLocale *locale_;
|
|
71
|
+
|
|
72
|
+ QMenu *menuFile_;
|
|
73
|
+ QMenu *menuHelp_;
|
|
74
|
+
|
|
75
|
+ TripletSelector* tripletSelector_;
|
|
76
|
+
|
|
77
|
+ QVBoxLayout *leftLayout_;
|
|
78
|
+ QVBoxLayout *rightLayout_;
|
|
79
|
+
|
|
80
|
+ QWidget *ftinspectWidget_;
|
|
81
|
+ QWidget *leftWidget_;
|
|
82
|
+ QWidget *rightWidget_;
|
|
83
|
+
|
|
84
|
+ SettingPanel* settingPanel_;
|
|
85
|
+
|
|
86
|
+ QTabWidget* tabWidget_;
|
|
87
|
+ std::vector<AbstractTab*> tabs_;
|
|
88
|
+ SingularTab* singularTab_;
|
|
89
|
+ QWidget* lastTab_ = NULL;
|
|
90
|
+
|
|
91
|
+ void openFonts(QStringList const& fileNames);
|
|
92
|
+
|
|
93
|
+ void applySettings();
|
|
94
|
+
|
|
95
|
+ void createActions();
|
|
96
|
+ void createConnections();
|
|
97
|
+ void createLayout();
|
|
98
|
+ void createMenus();
|
|
99
|
+
|
|
100
|
+ void readSettings();
|
|
101
|
+ void writeSettings();
|
|
102
|
+};
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+// end of maingui.hpp |
src/ftinspect/meson.build
... |
... |
@@ -29,11 +29,18 @@ if qt5_dep.found() |
29
|
29
|
'glyphcomponents/glyphpointnumbers.cpp',
|
30
|
30
|
'glyphcomponents/glyphpoints.cpp',
|
31
|
31
|
'glyphcomponents/grid.cpp',
|
|
32
|
+ 'glyphcomponents/graphicsdefault.cpp',
|
32
|
33
|
|
33
|
34
|
'widgets/customwidgets.cpp',
|
|
35
|
+ 'widgets/tripletselector.cpp',
|
|
36
|
+ 'widgets/glyphindexselector.cpp',
|
|
37
|
+ 'widgets/fontsizeselector.cpp',
|
34
|
38
|
|
35
|
39
|
'models/customcomboboxmodels.cpp',
|
36
|
40
|
|
|
41
|
+ 'panels/settingpanel.cpp',
|
|
42
|
+ 'panels/singular.cpp',
|
|
43
|
+
|
37
|
44
|
'ftinspect.cpp',
|
38
|
45
|
'maingui.cpp',
|
39
|
46
|
'uihelper.cpp',
|
... |
... |
@@ -43,8 +50,13 @@ if qt5_dep.found() |
43
|
50
|
moc_headers: [
|
44
|
51
|
'engine/fontfilemanager.hpp',
|
45
|
52
|
'widgets/customwidgets.hpp',
|
|
53
|
+ 'widgets/tripletselector.hpp',
|
|
54
|
+ 'widgets/glyphindexselector.hpp',
|
|
55
|
+ 'widgets/fontsizeselector.hpp',
|
46
|
56
|
'maingui.hpp',
|
47
|
57
|
'models/customcomboboxmodels.hpp',
|
|
58
|
+ 'panels/settingpanel.hpp',
|
|
59
|
+ 'panels/singular.hpp',
|
48
|
60
|
],
|
49
|
61
|
dependencies: qt5_dep)
|
50
|
62
|
|
src/ftinspect/panels/abstracttab.hpp
|
1
|
+// abstracttab.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+// This is an pure abstract interface for a ftinspect "tab".
|
|
8
|
+// The interface itself does not inherit from `QWidget`, but should be used as
|
|
9
|
+// the second base class.
|
|
10
|
+class AbstractTab
|
|
11
|
+{
|
|
12
|
+public:
|
|
13
|
+ virtual ~AbstractTab() = default; // must be `virtual` for `dynamic_cast`
|
|
14
|
+
|
|
15
|
+ virtual void repaintGlyph() = 0;
|
|
16
|
+ virtual void reloadFont() = 0;
|
|
17
|
+};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+// end of abstracttab.hpp |
src/ftinspect/panels/settingpanel.cpp
|
1
|
+// settingpanel.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "settingpanel.hpp"
|
|
6
|
+
|
|
7
|
+#include "../uihelper.hpp"
|
|
8
|
+
|
|
9
|
+#include <QColorDialog>
|
|
10
|
+
|
|
11
|
+// for `FT_DEBUG_AUTOFIT`
|
|
12
|
+#include <freetype/config/ftoption.h>
|
|
13
|
+
|
|
14
|
+SettingPanel::SettingPanel(QWidget* parent,
|
|
15
|
+ Engine* engine)
|
|
16
|
+: QWidget(parent),
|
|
17
|
+ engine_(engine)
|
|
18
|
+{
|
|
19
|
+#ifdef FT_DEBUG_AUTOFIT
|
|
20
|
+ debugMode_ = !comparatorMode_;
|
|
21
|
+#else
|
|
22
|
+ debugMode_ = false;
|
|
23
|
+#endif
|
|
24
|
+ createLayout();
|
|
25
|
+ setDefaults();
|
|
26
|
+ createConnections();
|
|
27
|
+ checkAllSettings();
|
|
28
|
+}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+int
|
|
32
|
+SettingPanel::antiAliasingModeIndex()
|
|
33
|
+{
|
|
34
|
+ return antiAliasingComboBox_->currentIndex();
|
|
35
|
+}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+bool
|
|
39
|
+SettingPanel::kerningEnabled()
|
|
40
|
+{
|
|
41
|
+ return kerningCheckBox_->isChecked();
|
|
42
|
+}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+bool
|
|
46
|
+SettingPanel::lsbRsbDeltaEnabled()
|
|
47
|
+{
|
|
48
|
+ return lsbRsbDeltaCheckBox_->isChecked();
|
|
49
|
+}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+void
|
|
53
|
+SettingPanel::populatePalettes()
|
|
54
|
+{
|
|
55
|
+ // TODO: Impl
|
|
56
|
+}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+void
|
|
60
|
+SettingPanel::checkAllSettings()
|
|
61
|
+{
|
|
62
|
+ onFontChanged();
|
|
63
|
+ checkAntiAliasing();
|
|
64
|
+}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+void
|
|
68
|
+SettingPanel::checkHinting()
|
|
69
|
+{
|
|
70
|
+ if (hintingCheckBox_->isChecked())
|
|
71
|
+ {
|
|
72
|
+ // TODO: tricky: disable auto-hinting
|
|
73
|
+ checkAutoHinting(); // this will emit repaint
|
|
74
|
+ }
|
|
75
|
+ else
|
|
76
|
+ {
|
|
77
|
+ hintingModeLabel_->setEnabled(false);
|
|
78
|
+ hintingModeComboBox_->setEnabled(false);
|
|
79
|
+
|
|
80
|
+ autoHintingCheckBox_->setEnabled(false);
|
|
81
|
+ if (debugMode_)
|
|
82
|
+ {
|
|
83
|
+ horizontalHintingCheckBox_->setEnabled(false);
|
|
84
|
+ verticalHintingCheckBox_->setEnabled(false);
|
|
85
|
+ blueZoneHintingCheckBox_->setEnabled(false);
|
|
86
|
+ segmentDrawingCheckBox_->setEnabled(false);
|
|
87
|
+ }
|
|
88
|
+
|
|
89
|
+ stemDarkeningCheckBox_->setEnabled(false);
|
|
90
|
+ antiAliasingComboBoxModel_->setLightAntiAliasingEnabled(false);
|
|
91
|
+ auto aaMode = antiAliasingComboBox_->currentIndex();
|
|
92
|
+ if (aaMode == AntiAliasingComboBoxModel::AntiAliasing_Light
|
|
93
|
+ || aaMode == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel)
|
|
94
|
+ antiAliasingComboBox_->setCurrentIndex(
|
|
95
|
+ AntiAliasingComboBoxModel::AntiAliasing_Normal);
|
|
96
|
+
|
|
97
|
+ emit repaintNeeded();
|
|
98
|
+ }
|
|
99
|
+}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+void
|
|
103
|
+SettingPanel::checkHintingMode()
|
|
104
|
+{
|
|
105
|
+ //if (!comparatorMode_)
|
|
106
|
+ // applyDelayedSettings();
|
|
107
|
+
|
|
108
|
+ emit fontReloadNeeded();
|
|
109
|
+}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+void
|
|
113
|
+SettingPanel::checkAutoHinting()
|
|
114
|
+{
|
|
115
|
+ if (autoHintingCheckBox_->isChecked())
|
|
116
|
+ {
|
|
117
|
+ hintingModeLabel_->setEnabled(false);
|
|
118
|
+ hintingModeComboBox_->setEnabled(false);
|
|
119
|
+
|
|
120
|
+ if (debugMode_)
|
|
121
|
+ {
|
|
122
|
+ horizontalHintingCheckBox_->setEnabled(true);
|
|
123
|
+ verticalHintingCheckBox_->setEnabled(true);
|
|
124
|
+ blueZoneHintingCheckBox_->setEnabled(true);
|
|
125
|
+ segmentDrawingCheckBox_->setEnabled(true);
|
|
126
|
+ }
|
|
127
|
+
|
|
128
|
+ antiAliasingComboBoxModel_->setLightAntiAliasingEnabled(true);
|
|
129
|
+ auto aaMode = antiAliasingComboBox_->currentIndex();
|
|
130
|
+ stemDarkeningCheckBox_->setEnabled(
|
|
131
|
+ aaMode == AntiAliasingComboBoxModel::AntiAliasing_Light
|
|
132
|
+ || aaMode == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel);
|
|
133
|
+ }
|
|
134
|
+ else
|
|
135
|
+ {
|
|
136
|
+ if (engine_->currentFontType() == Engine::FontType_CFF
|
|
137
|
+ || engine_->currentFontType() == Engine::FontType_TrueType)
|
|
138
|
+ {
|
|
139
|
+ hintingModeLabel_->setEnabled(true);
|
|
140
|
+ hintingModeComboBox_->setEnabled(true);
|
|
141
|
+ }
|
|
142
|
+
|
|
143
|
+ if (debugMode_)
|
|
144
|
+ {
|
|
145
|
+ horizontalHintingCheckBox_->setEnabled(false);
|
|
146
|
+ verticalHintingCheckBox_->setEnabled(false);
|
|
147
|
+ blueZoneHintingCheckBox_->setEnabled(false);
|
|
148
|
+ segmentDrawingCheckBox_->setEnabled(false);
|
|
149
|
+ }
|
|
150
|
+
|
|
151
|
+ antiAliasingComboBoxModel_->setLightAntiAliasingEnabled(false);
|
|
152
|
+ stemDarkeningCheckBox_->setEnabled(false);
|
|
153
|
+
|
|
154
|
+ auto aaMode = antiAliasingComboBox_->currentIndex();
|
|
155
|
+ if (aaMode == AntiAliasingComboBoxModel::AntiAliasing_Light
|
|
156
|
+ || aaMode == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel)
|
|
157
|
+ antiAliasingComboBox_->setCurrentIndex(
|
|
158
|
+ AntiAliasingComboBoxModel::AntiAliasing_Normal);
|
|
159
|
+ }
|
|
160
|
+ emit repaintNeeded();
|
|
161
|
+}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+void
|
|
165
|
+SettingPanel::checkAntiAliasing()
|
|
166
|
+{
|
|
167
|
+ int index = antiAliasingComboBox_->currentIndex();
|
|
168
|
+ auto isMono = index == AntiAliasingComboBoxModel::AntiAliasing_None;
|
|
169
|
+ auto isLight
|
|
170
|
+ = index == AntiAliasingComboBoxModel::AntiAliasing_Light
|
|
171
|
+ || index == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel;
|
|
172
|
+ auto disableLCD
|
|
173
|
+ = index == AntiAliasingComboBoxModel::AntiAliasing_None
|
|
174
|
+ || index == AntiAliasingComboBoxModel::AntiAliasing::AntiAliasing_Normal
|
|
175
|
+ || isLight;
|
|
176
|
+
|
|
177
|
+ lcdFilterLabel_->setEnabled(!disableLCD);
|
|
178
|
+ lcdFilterComboBox_->setEnabled(!disableLCD);
|
|
179
|
+ stemDarkeningCheckBox_->setEnabled(isLight);
|
|
180
|
+ gammaSlider_->setEnabled(!isMono);
|
|
181
|
+
|
|
182
|
+ emit repaintNeeded();
|
|
183
|
+}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+void
|
|
187
|
+SettingPanel::checkPalette()
|
|
188
|
+{
|
|
189
|
+ paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked());
|
|
190
|
+ emit repaintNeeded();
|
|
191
|
+}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+void
|
|
195
|
+SettingPanel::checkStemDarkening()
|
|
196
|
+{
|
|
197
|
+ //if (!comparatorMode_)
|
|
198
|
+ // applyDelayedSettings();
|
|
199
|
+
|
|
200
|
+ emit fontReloadNeeded();
|
|
201
|
+}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+void
|
|
205
|
+SettingPanel::openBackgroundPicker()
|
|
206
|
+{
|
|
207
|
+ auto result = QColorDialog::getColor(backgroundColor_,
|
|
208
|
+ this,
|
|
209
|
+ tr("Background Color"));
|
|
210
|
+ if (result.isValid())
|
|
211
|
+ {
|
|
212
|
+ backgroundColor_ = result;
|
|
213
|
+ resetColorBlocks();
|
|
214
|
+ emit repaintNeeded();
|
|
215
|
+ }
|
|
216
|
+}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+void
|
|
220
|
+SettingPanel::openForegroundPicker()
|
|
221
|
+{
|
|
222
|
+ auto result = QColorDialog::getColor(foregroundColor_,
|
|
223
|
+ this,
|
|
224
|
+ tr("Foreground Color"),
|
|
225
|
+ QColorDialog::ShowAlphaChannel);
|
|
226
|
+ if (result.isValid())
|
|
227
|
+ {
|
|
228
|
+ foregroundColor_ = result;
|
|
229
|
+ resetColorBlocks();
|
|
230
|
+ emit repaintNeeded();
|
|
231
|
+ }
|
|
232
|
+}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+void
|
|
236
|
+SettingPanel::updateGamma()
|
|
237
|
+{
|
|
238
|
+ gammaValueLabel_->setText(QString::number(gammaSlider_->value() / 10.0,
|
|
239
|
+ 'f',
|
|
240
|
+ 1));
|
|
241
|
+ emit repaintNeeded();
|
|
242
|
+}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+void
|
|
246
|
+SettingPanel::resetColorBlocks()
|
|
247
|
+{
|
|
248
|
+ foregroundBlock_->setStyleSheet(
|
|
249
|
+ QString("QWidget {background-color: rgba(%1, %2, %3, %4);}")
|
|
250
|
+ .arg(foregroundColor_.red())
|
|
251
|
+ .arg(foregroundColor_.green())
|
|
252
|
+ .arg(foregroundColor_.blue())
|
|
253
|
+ .arg(foregroundColor_.alpha()));
|
|
254
|
+ backgroundBlock_->setStyleSheet(
|
|
255
|
+ QString("QWidget {background-color: rgba(%1, %2, %3, %4);}")
|
|
256
|
+ .arg(backgroundColor_.red())
|
|
257
|
+ .arg(backgroundColor_.green())
|
|
258
|
+ .arg(backgroundColor_.blue())
|
|
259
|
+ .arg(backgroundColor_.alpha()));
|
|
260
|
+}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+void
|
|
264
|
+SettingPanel::onFontChanged()
|
|
265
|
+{
|
|
266
|
+ auto blockState = blockSignals(signalsBlocked());
|
|
267
|
+
|
|
268
|
+ if (engine_->currentFontType() == Engine::FontType_CFF)
|
|
269
|
+ {
|
|
270
|
+ hintingModeComboBoxModel_->setCurrentEngineType(
|
|
271
|
+ HintingModeComboBoxModel::HintingEngineType_CFF, false);
|
|
272
|
+ hintingModeComboBox_->setCurrentIndex(currentCFFHintingMode_);
|
|
273
|
+ }
|
|
274
|
+ else if (engine_->currentFontType() == Engine::FontType_TrueType)
|
|
275
|
+ {
|
|
276
|
+ // TODO: tricky
|
|
277
|
+ hintingModeComboBoxModel_->setCurrentEngineType(
|
|
278
|
+ HintingModeComboBoxModel::HintingEngineType_TrueType, false);
|
|
279
|
+ hintingModeComboBox_->setCurrentIndex(currentTTInterpreterVersion_);
|
|
280
|
+ }
|
|
281
|
+ else
|
|
282
|
+ {
|
|
283
|
+ hintingModeLabel_->setEnabled(false);
|
|
284
|
+ hintingModeComboBox_->setEnabled(false);
|
|
285
|
+ }
|
|
286
|
+
|
|
287
|
+ checkHinting();
|
|
288
|
+
|
|
289
|
+ //engine_->reloadFont();
|
|
290
|
+ //auto hasColor = engine_->currentFontHasColorLayers();
|
|
291
|
+ //colorLayerCheckBox_->setEnabled(hasColor);
|
|
292
|
+ //if (!hasColor)
|
|
293
|
+ // colorLayerCheckBox_->setChecked(false);
|
|
294
|
+ populatePalettes();
|
|
295
|
+ //mmgxPanel_->reloadFont();
|
|
296
|
+ blockSignals(blockState);
|
|
297
|
+
|
|
298
|
+ // Place this after `blockSignals` to let the signals emitted normally
|
|
299
|
+ //auto bmapOnly = engine_->currentFontBitmapOnly();
|
|
300
|
+ //embeddedBitmapCheckBox_->setEnabled(
|
|
301
|
+ // !bmapOnly && engine_->currentFontHasEmbeddedBitmap());
|
|
302
|
+ //if (bmapOnly)
|
|
303
|
+ // embeddedBitmapCheckBox_->setChecked(true);
|
|
304
|
+}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+void
|
|
308
|
+SettingPanel::applySettings()
|
|
309
|
+{
|
|
310
|
+ engine_->setLcdFilter(
|
|
311
|
+ static_cast<FT_LcdFilter>(lcdFilterComboboxModel_->indexToValue(
|
|
312
|
+ lcdFilterComboBox_->currentIndex())));
|
|
313
|
+
|
|
314
|
+ auto aaSettings = antiAliasingComboBoxModel_->indexToValue(
|
|
315
|
+ antiAliasingComboBox_->currentIndex());
|
|
316
|
+ engine_->setAntiAliasingTarget(aaSettings.loadFlag);
|
|
317
|
+ //engine_->setRenderMode(aaSettings.renderMode);
|
|
318
|
+
|
|
319
|
+ //engine_->setAntiAliasingEnabled(antiAliasingComboBox_->currentIndex()
|
|
320
|
+ // != AntiAliasingComboBoxModel::AntiAliasing_None);
|
|
321
|
+ engine_->setHinting(hintingCheckBox_->isChecked());
|
|
322
|
+ engine_->setAutoHinting(autoHintingCheckBox_->isChecked());
|
|
323
|
+
|
|
324
|
+ if (debugMode_)
|
|
325
|
+ {
|
|
326
|
+ engine_->setHorizontalHinting(horizontalHintingCheckBox_->isChecked());
|
|
327
|
+ engine_->setVerticalHinting(verticalHintingCheckBox_->isChecked());
|
|
328
|
+ engine_->setBlueZoneHinting(blueZoneHintingCheckBox_->isChecked());
|
|
329
|
+ engine_->setShowSegments(segmentDrawingCheckBox_->isChecked());
|
|
330
|
+ }
|
|
331
|
+
|
|
332
|
+ engine_->setGamma(gammaSlider_->value() / 10.0);
|
|
333
|
+
|
|
334
|
+ //engine_->setEmbeddedBitmap(embeddedBitmapCheckBox_->isChecked());
|
|
335
|
+ //engine_->setPaletteIndex(paletteComboBox_->currentIndex());
|
|
336
|
+
|
|
337
|
+ //engine_->setUseColorLayer(colorLayerCheckBox_->isChecked());
|
|
338
|
+ //engine_->setLCDUsesBGR(aaSettings.isBGR);
|
|
339
|
+ //engine_->setLCDSubPixelPositioning(
|
|
340
|
+ // antiAliasingComboBox_->currentIndex()
|
|
341
|
+ // == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel);
|
|
342
|
+
|
|
343
|
+ //engine_->renderingEngine()->setForeground(foregroundColor_.rgba());
|
|
344
|
+ //engine_->renderingEngine()->setBackground(backgroundColor_.rgba());
|
|
345
|
+ //mmgxPanel_->applySettings();
|
|
346
|
+}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+void
|
|
350
|
+SettingPanel::applyDelayedSettings()
|
|
351
|
+{
|
|
352
|
+ // This must not be combined into `applySettings`:
|
|
353
|
+ // those engine manipulations will reset the whole cache!!
|
|
354
|
+ // Therefore must only be called when the selection of the combo box actually
|
|
355
|
+ // changes a.k.a. QComboBox::activate.
|
|
356
|
+
|
|
357
|
+ int index = hintingModeComboBox_->currentIndex();
|
|
358
|
+
|
|
359
|
+ if (engine_->currentFontType() == Engine::FontType_CFF)
|
|
360
|
+ {
|
|
361
|
+ engine_->setCFFHintingMode(
|
|
362
|
+ hintingModeComboBoxModel_->indexToCFFMode(index));
|
|
363
|
+ if (index >= 0)
|
|
364
|
+ currentCFFHintingMode_ = index;
|
|
365
|
+ }
|
|
366
|
+ else if (engine_->currentFontType() == Engine::FontType_TrueType)
|
|
367
|
+ {
|
|
368
|
+ engine_->setTTInterpreterVersion(
|
|
369
|
+ hintingModeComboBoxModel_->indexToTTInterpreterVersion(index));
|
|
370
|
+ if (index >= 0)
|
|
371
|
+ currentTTInterpreterVersion_ = index;
|
|
372
|
+ }
|
|
373
|
+
|
|
374
|
+ //engine_->setStemDarkening(stemDarkeningCheckBox_->isChecked());
|
|
375
|
+}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+void
|
|
379
|
+SettingPanel::createLayout()
|
|
380
|
+{
|
|
381
|
+ hintingCheckBox_ = new QCheckBox(tr("Hinting"), this);
|
|
382
|
+
|
|
383
|
+ hintingModeLabel_ = new QLabel(tr("Hinting Mode"), this);
|
|
384
|
+ hintingModeLabel_->setAlignment(Qt::AlignRight);
|
|
385
|
+
|
|
386
|
+ hintingModeComboBoxModel_ = new HintingModeComboBoxModel(this);
|
|
387
|
+ hintingModeComboBox_ = new QComboBox(this);
|
|
388
|
+ hintingModeComboBox_->setModel(hintingModeComboBoxModel_);
|
|
389
|
+ hintingModeLabel_->setBuddy(hintingModeComboBox_);
|
|
390
|
+
|
|
391
|
+ autoHintingCheckBox_ = new QCheckBox(tr("Auto-Hinting"), this);
|
|
392
|
+ stemDarkeningCheckBox_ = new QCheckBox(tr("Stem Darkening"), this);
|
|
393
|
+
|
|
394
|
+ if (debugMode_)
|
|
395
|
+ {
|
|
396
|
+ horizontalHintingCheckBox_ = new QCheckBox(tr("Horizontal Hinting"), this);
|
|
397
|
+ verticalHintingCheckBox_ = new QCheckBox(tr("Vertical Hinting"), this);
|
|
398
|
+ blueZoneHintingCheckBox_ = new QCheckBox(tr("Blue-Zone Hinting"), this);
|
|
399
|
+ segmentDrawingCheckBox_ = new QCheckBox(tr("Segment Drawing"), this);
|
|
400
|
+ }
|
|
401
|
+
|
|
402
|
+ embeddedBitmapCheckBox_ = new QCheckBox(tr("Enable Embedded Bitmap"), this);
|
|
403
|
+ colorLayerCheckBox_ = new QCheckBox(tr("Enable Color Layer"), this);
|
|
404
|
+
|
|
405
|
+ antiAliasingLabel_ = new QLabel(tr("Anti-Aliasing"), this);
|
|
406
|
+ antiAliasingLabel_->setAlignment(Qt::AlignRight);
|
|
407
|
+
|
|
408
|
+ antiAliasingComboBoxModel_ = new AntiAliasingComboBoxModel(this);
|
|
409
|
+ antiAliasingComboBox_ = new QComboBox(this);
|
|
410
|
+ antiAliasingComboBox_->setModel(antiAliasingComboBoxModel_);
|
|
411
|
+ antiAliasingLabel_->setBuddy(antiAliasingComboBox_);
|
|
412
|
+
|
|
413
|
+ lcdFilterLabel_ = new QLabel(tr("LCD Filter"), this);
|
|
414
|
+ lcdFilterLabel_->setAlignment(Qt::AlignRight);
|
|
415
|
+
|
|
416
|
+ lcdFilterComboboxModel_ = new LCDFilterComboBoxModel(this);
|
|
417
|
+ lcdFilterComboBox_ = new QComboBox(this);
|
|
418
|
+ lcdFilterComboBox_->setModel(lcdFilterComboboxModel_);
|
|
419
|
+ lcdFilterLabel_->setBuddy(lcdFilterComboBox_);
|
|
420
|
+
|
|
421
|
+ paletteLabel_ = new QLabel(tr("Palette: "), this);
|
|
422
|
+
|
|
423
|
+ paletteComboBox_ = new QComboBox(this);
|
|
424
|
+ paletteLabel_->setBuddy(paletteComboBox_);
|
|
425
|
+
|
|
426
|
+ gammaLabel_ = new QLabel(tr("Gamma"), this);
|
|
427
|
+ gammaLabel_->setAlignment(Qt::AlignRight);
|
|
428
|
+ gammaSlider_ = new QSlider(Qt::Horizontal, this);
|
|
429
|
+ gammaSlider_->setRange(3, 30); // in 1/10th
|
|
430
|
+ gammaSlider_->setTickPosition(QSlider::TicksBelow);
|
|
431
|
+ gammaSlider_->setTickInterval(5);
|
|
432
|
+ gammaSlider_->setPageStep(1);
|
|
433
|
+ gammaSlider_->setSingleStep(1);
|
|
434
|
+ gammaLabel_->setBuddy(gammaSlider_);
|
|
435
|
+ gammaValueLabel_ = new QLabel(this);
|
|
436
|
+
|
|
437
|
+ // TODO: MM/GX
|
|
438
|
+ mmgxPanel_ = new QWidget(this);
|
|
439
|
+
|
|
440
|
+ backgroundButton_ = new QPushButton(tr("Background"), this);
|
|
441
|
+ foregroundButton_ = new QPushButton(tr("Foreground"), this);
|
|
442
|
+
|
|
443
|
+ backgroundBlock_ = new QFrame(this);
|
|
444
|
+ backgroundBlock_->setFrameStyle(QFrame::Box);
|
|
445
|
+ backgroundBlock_->setLineWidth(1);
|
|
446
|
+ backgroundBlock_->setFixedWidth(18);
|
|
447
|
+
|
|
448
|
+ foregroundBlock_ = new QFrame(this);
|
|
449
|
+ foregroundBlock_->setFrameStyle(QFrame::Box);
|
|
450
|
+ foregroundBlock_->setLineWidth(1);
|
|
451
|
+ foregroundBlock_->setFixedWidth(18);
|
|
452
|
+
|
|
453
|
+ generalTab_ = new QWidget(this);
|
|
454
|
+
|
|
455
|
+ generalTab_->setSizePolicy(QSizePolicy::MinimumExpanding,
|
|
456
|
+ QSizePolicy::MinimumExpanding);
|
|
457
|
+
|
|
458
|
+ tab_ = new QTabWidget(this);
|
|
459
|
+ tab_->setSizePolicy(QSizePolicy::MinimumExpanding,
|
|
460
|
+ QSizePolicy::MinimumExpanding);
|
|
461
|
+
|
|
462
|
+ // Tooltips
|
|
463
|
+ hintingCheckBox_->setToolTip(tr("Enable hinting a.k.a. grid-fitting."));
|
|
464
|
+ hintingModeComboBox_->setToolTip(
|
|
465
|
+ tr("Modes not available for current font type will be disabled. No "
|
|
466
|
+ "effect when auto-hinting is enabled"));
|
|
467
|
+ autoHintingCheckBox_->setToolTip(tr("Enable FreeType Auto-Hinter."));
|
|
468
|
+ if (debugMode_)
|
|
469
|
+ {
|
|
470
|
+ horizontalHintingCheckBox_->setToolTip(tr("(auto-hinter debug option)"));
|
|
471
|
+ verticalHintingCheckBox_ ->setToolTip(tr("(auto-hinter debug option)"));
|
|
472
|
+ blueZoneHintingCheckBox_ ->setToolTip(tr("(auto-hinter debug option)"));
|
|
473
|
+ segmentDrawingCheckBox_ ->setToolTip(tr("(auto-hinter debug option)"));
|
|
474
|
+ }
|
|
475
|
+ antiAliasingComboBox_->setToolTip(tr("Select anti-aliasing mode."));
|
|
476
|
+ lcdFilterComboBox_->setToolTip(
|
|
477
|
+ tr("Select LCD filter (only valid when LCD AA is enabled)."));
|
|
478
|
+ embeddedBitmapCheckBox_->setToolTip(tr(
|
|
479
|
+ "Enable embedded bitmap strikes (force enabled for bitmap-only fonts)."));
|
|
480
|
+ stemDarkeningCheckBox_->setToolTip(
|
|
481
|
+ tr("Enable stem darkening (only valid for auto-hinter with gamma "
|
|
482
|
+ "correction enabled and with Light AA modes)."));
|
|
483
|
+ gammaSlider_->setToolTip("Gamma correction value.");
|
|
484
|
+ colorLayerCheckBox_->setToolTip(tr("Enable color layer rendering."));
|
|
485
|
+ paletteComboBox_->setToolTip(tr("Select color layer palette (only valid when "
|
|
486
|
+ "any palette exists in the font)."));
|
|
487
|
+ backgroundButton_->setToolTip(tr("Set canvas background color."));
|
|
488
|
+ foregroundButton_->setToolTip(tr("Set text color."));
|
|
489
|
+
|
|
490
|
+ // Layouting
|
|
491
|
+ if (debugMode_)
|
|
492
|
+ {
|
|
493
|
+ debugLayout_ = new QVBoxLayout;
|
|
494
|
+ debugLayout_->setContentsMargins(20, 0, 0, 0);
|
|
495
|
+ debugLayout_->addWidget(horizontalHintingCheckBox_);
|
|
496
|
+ debugLayout_->addWidget(verticalHintingCheckBox_);
|
|
497
|
+ debugLayout_->addWidget(blueZoneHintingCheckBox_);
|
|
498
|
+ debugLayout_->addWidget(segmentDrawingCheckBox_);
|
|
499
|
+ }
|
|
500
|
+
|
|
501
|
+ gammaLayout_ = new QHBoxLayout;
|
|
502
|
+ gammaLayout_->addWidget(gammaLabel_);
|
|
503
|
+ gammaLayout_->addWidget(gammaSlider_);
|
|
504
|
+ gammaLayout_->addWidget(gammaValueLabel_);
|
|
505
|
+
|
|
506
|
+ colorPickerLayout_ = new QHBoxLayout;
|
|
507
|
+ colorPickerLayout_->addWidget(backgroundBlock_);
|
|
508
|
+ colorPickerLayout_->addWidget(backgroundButton_, 1);
|
|
509
|
+ colorPickerLayout_->addWidget(foregroundButton_, 1);
|
|
510
|
+ colorPickerLayout_->addWidget(foregroundBlock_);
|
|
511
|
+
|
|
512
|
+ createLayoutNormal();
|
|
513
|
+ // TODO: Comparator mode.
|
|
514
|
+
|
|
515
|
+ mainLayout_ = new QVBoxLayout;
|
|
516
|
+ mainLayout_->addWidget(tab_);
|
|
517
|
+ setLayout(mainLayout_);
|
|
518
|
+ mainLayout_->setContentsMargins(0, 0, 0, 0);
|
|
519
|
+ setContentsMargins(0, 0, 0, 0);
|
|
520
|
+}
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+void
|
|
524
|
+SettingPanel::createLayoutNormal()
|
|
525
|
+{
|
|
526
|
+ generalTabLayout_ = new QGridLayout;
|
|
527
|
+
|
|
528
|
+ gridLayout2ColAddWidget(generalTabLayout_, hintingCheckBox_);
|
|
529
|
+ gridLayout2ColAddWidget(generalTabLayout_,
|
|
530
|
+ hintingModeLabel_, hintingModeComboBox_);
|
|
531
|
+ gridLayout2ColAddWidget(generalTabLayout_, autoHintingCheckBox_);
|
|
532
|
+
|
|
533
|
+ if (debugMode_)
|
|
534
|
+ gridLayout2ColAddLayout(generalTabLayout_, debugLayout_);
|
|
535
|
+
|
|
536
|
+ gridLayout2ColAddItem(generalTabLayout_,
|
|
537
|
+ new QSpacerItem(0, 20, QSizePolicy::Minimum,
|
|
538
|
+ QSizePolicy::MinimumExpanding));
|
|
539
|
+
|
|
540
|
+ gridLayout2ColAddWidget(generalTabLayout_,
|
|
541
|
+ antiAliasingLabel_, antiAliasingComboBox_);
|
|
542
|
+ gridLayout2ColAddWidget(generalTabLayout_,
|
|
543
|
+ lcdFilterLabel_, lcdFilterComboBox_);
|
|
544
|
+
|
|
545
|
+ gridLayout2ColAddItem(generalTabLayout_,
|
|
546
|
+ new QSpacerItem(0, 20, QSizePolicy::Minimum,
|
|
547
|
+ QSizePolicy::MinimumExpanding));
|
|
548
|
+
|
|
549
|
+ gridLayout2ColAddLayout(generalTabLayout_, colorPickerLayout_);
|
|
550
|
+ gridLayout2ColAddLayout(generalTabLayout_, gammaLayout_);
|
|
551
|
+ gridLayout2ColAddWidget(generalTabLayout_, stemDarkeningCheckBox_);
|
|
552
|
+ gridLayout2ColAddWidget(generalTabLayout_, embeddedBitmapCheckBox_);
|
|
553
|
+ gridLayout2ColAddWidget(generalTabLayout_, colorLayerCheckBox_);
|
|
554
|
+ gridLayout2ColAddWidget(generalTabLayout_,
|
|
555
|
+ paletteLabel_, paletteComboBox_);
|
|
556
|
+
|
|
557
|
+ gridLayout2ColAddItem(generalTabLayout_,
|
|
558
|
+ new QSpacerItem(0, 20, QSizePolicy::Minimum,
|
|
559
|
+ QSizePolicy::MinimumExpanding));
|
|
560
|
+
|
|
561
|
+ generalTabLayout_->setColumnStretch(1, 1);
|
|
562
|
+ generalTab_->setLayout(generalTabLayout_);
|
|
563
|
+
|
|
564
|
+ tab_->addTab(generalTab_, tr("General"));
|
|
565
|
+ tab_->addTab(mmgxPanel_, tr("MM/GX"));
|
|
566
|
+
|
|
567
|
+ tab_->setTabToolTip(0, tr("General settings."));
|
|
568
|
+ tab_->setTabToolTip(1, tr("MM/GX axis parameters."));
|
|
569
|
+}
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+void
|
|
573
|
+SettingPanel::createConnections()
|
|
574
|
+{
|
|
575
|
+ // use `qOverload` here to prevent ambiguity.
|
|
576
|
+ connect(hintingModeComboBox_,
|
|
577
|
+ QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
578
|
+ this, &SettingPanel::checkHintingMode);
|
|
579
|
+ connect(antiAliasingComboBox_,
|
|
580
|
+ QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
581
|
+ this, &SettingPanel::checkAntiAliasing);
|
|
582
|
+ connect(lcdFilterComboBox_,
|
|
583
|
+ QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
584
|
+ this, &SettingPanel::repaintNeeded);
|
|
585
|
+ connect(paletteComboBox_,
|
|
586
|
+ QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
587
|
+ this, &SettingPanel::repaintNeeded);
|
|
588
|
+
|
|
589
|
+ connect(gammaSlider_, &QSlider::valueChanged,
|
|
590
|
+ this, &SettingPanel::updateGamma);
|
|
591
|
+
|
|
592
|
+ connect(hintingCheckBox_, &QCheckBox::clicked,
|
|
593
|
+ this, &SettingPanel::checkHinting);
|
|
594
|
+
|
|
595
|
+ if (debugMode_)
|
|
596
|
+ {
|
|
597
|
+ connect(horizontalHintingCheckBox_, &QCheckBox::clicked,
|
|
598
|
+ this, &SettingPanel::repaintNeeded);
|
|
599
|
+ connect(verticalHintingCheckBox_, &QCheckBox::clicked,
|
|
600
|
+ this, &SettingPanel::repaintNeeded);
|
|
601
|
+ connect(blueZoneHintingCheckBox_, &QCheckBox::clicked,
|
|
602
|
+ this, &SettingPanel::repaintNeeded);
|
|
603
|
+ connect(segmentDrawingCheckBox_, &QCheckBox::clicked,
|
|
604
|
+ this, &SettingPanel::repaintNeeded);
|
|
605
|
+ }
|
|
606
|
+
|
|
607
|
+ connect(autoHintingCheckBox_, &QCheckBox::clicked,
|
|
608
|
+ this, &SettingPanel::checkAutoHinting);
|
|
609
|
+ connect(embeddedBitmapCheckBox_, &QCheckBox::clicked,
|
|
610
|
+ this, &SettingPanel::fontReloadNeeded);
|
|
611
|
+ connect(stemDarkeningCheckBox_, &QCheckBox::clicked,
|
|
612
|
+ this, &SettingPanel::checkStemDarkening);
|
|
613
|
+ connect(colorLayerCheckBox_, &QCheckBox::clicked,
|
|
614
|
+ this, &SettingPanel::checkPalette);
|
|
615
|
+
|
|
616
|
+ connect(backgroundButton_, &QPushButton::clicked,
|
|
617
|
+ this, &SettingPanel::openBackgroundPicker);
|
|
618
|
+ connect(foregroundButton_, &QPushButton::clicked,
|
|
619
|
+ this, &SettingPanel::openForegroundPicker);
|
|
620
|
+}
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+void
|
|
624
|
+SettingPanel::setDefaults()
|
|
625
|
+{
|
|
626
|
+ Engine::EngineDefaultValues& defaults = engine_->engineDefaults();
|
|
627
|
+
|
|
628
|
+ hintingModeComboBoxModel_->setSupportedModes(
|
|
629
|
+ { defaults.ttInterpreterVersionDefault,
|
|
630
|
+ defaults.ttInterpreterVersionOther,
|
|
631
|
+ defaults.ttInterpreterVersionOther1 },
|
|
632
|
+ { defaults.cffHintingEngineDefault,
|
|
633
|
+ defaults.cffHintingEngineOther });
|
|
634
|
+
|
|
635
|
+ currentCFFHintingMode_
|
|
636
|
+ = hintingModeComboBoxModel_->cffModeToIndex(
|
|
637
|
+ defaults.cffHintingEngineDefault);
|
|
638
|
+ currentTTInterpreterVersion_
|
|
639
|
+ = hintingModeComboBoxModel_->ttInterpreterVersionToIndex(
|
|
640
|
+ defaults.ttInterpreterVersionDefault);
|
|
641
|
+
|
|
642
|
+ hintingCheckBox_->setChecked(true);
|
|
643
|
+
|
|
644
|
+ antiAliasingComboBox_->setCurrentIndex(
|
|
645
|
+ AntiAliasingComboBoxModel::AntiAliasing_Normal);
|
|
646
|
+ lcdFilterComboBox_->setCurrentIndex(
|
|
647
|
+ LCDFilterComboBoxModel::LCDFilter_Light);
|
|
648
|
+
|
|
649
|
+ if (debugMode_)
|
|
650
|
+ {
|
|
651
|
+ horizontalHintingCheckBox_->setChecked(true);
|
|
652
|
+ verticalHintingCheckBox_->setChecked(true);
|
|
653
|
+ blueZoneHintingCheckBox_->setChecked(true);
|
|
654
|
+ embeddedBitmapCheckBox_->setChecked(false);
|
|
655
|
+ }
|
|
656
|
+
|
|
657
|
+ colorLayerCheckBox_->setChecked(true);
|
|
658
|
+ paletteComboBox_->setEnabled(false);
|
|
659
|
+
|
|
660
|
+ // These need to be set even in Comperator mode.
|
|
661
|
+ backgroundColor_ = Qt::white;
|
|
662
|
+ foregroundColor_ = Qt::black;
|
|
663
|
+ resetColorBlocks();
|
|
664
|
+
|
|
665
|
+ gammaSlider_->setValue(18); // 1.8
|
|
666
|
+ updateGamma();
|
|
667
|
+}
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+// end of settingpanel.cpp |
src/ftinspect/panels/settingpanel.hpp
|
1
|
+// settingpanel.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include "../engine/engine.hpp"
|
|
8
|
+#include "../models/customcomboboxmodels.hpp"
|
|
9
|
+
|
|
10
|
+#include <QWidget>
|
|
11
|
+#include <QTabWidget>
|
|
12
|
+#include <QLabel>
|
|
13
|
+#include <QComboBox>
|
|
14
|
+#include <QCheckBox>
|
|
15
|
+#include <QGridLayout>
|
|
16
|
+#include <QBoxLayout>
|
|
17
|
+#include <QPushButton>
|
|
18
|
+
|
|
19
|
+class SettingPanel
|
|
20
|
+: public QWidget
|
|
21
|
+{
|
|
22
|
+ Q_OBJECT
|
|
23
|
+public:
|
|
24
|
+ SettingPanel(QWidget* parent, Engine* engine);
|
|
25
|
+ ~SettingPanel() override = default;
|
|
26
|
+
|
|
27
|
+ void onFontChanged();
|
|
28
|
+ void applySettings();
|
|
29
|
+ /*
|
|
30
|
+ * When in comparator mode, this is needed to sync the hinting modes when
|
|
31
|
+ * reloading the font.
|
|
32
|
+ */
|
|
33
|
+ void applyDelayedSettings();
|
|
34
|
+
|
|
35
|
+ //////// Getters/Setters
|
|
36
|
+
|
|
37
|
+ int antiAliasingModeIndex();
|
|
38
|
+ bool kerningEnabled();
|
|
39
|
+ bool lsbRsbDeltaEnabled();
|
|
40
|
+
|
|
41
|
+signals:
|
|
42
|
+ void fontReloadNeeded();
|
|
43
|
+ void repaintNeeded();
|
|
44
|
+
|
|
45
|
+private:
|
|
46
|
+ Engine* engine_;
|
|
47
|
+
|
|
48
|
+ int currentCFFHintingMode_;
|
|
49
|
+ int currentTTInterpreterVersion_;
|
|
50
|
+
|
|
51
|
+ bool debugMode_ = false;
|
|
52
|
+
|
|
53
|
+ QTabWidget* tab_;
|
|
54
|
+
|
|
55
|
+ QWidget* generalTab_;
|
|
56
|
+ QWidget* hintingRenderingTab_;
|
|
57
|
+ //SettingPanelMMGX* mmgxPanel_;
|
|
58
|
+ QWidget* mmgxPanel_;
|
|
59
|
+
|
|
60
|
+ QLabel* gammaLabel_;
|
|
61
|
+ QLabel* gammaValueLabel_;
|
|
62
|
+ QLabel* antiAliasingLabel_;
|
|
63
|
+ QLabel* hintingModeLabel_;
|
|
64
|
+ QLabel* lcdFilterLabel_;
|
|
65
|
+ QLabel* paletteLabel_;
|
|
66
|
+
|
|
67
|
+ QCheckBox* hintingCheckBox_;
|
|
68
|
+ QCheckBox* horizontalHintingCheckBox_;
|
|
69
|
+ QCheckBox* verticalHintingCheckBox_;
|
|
70
|
+ QCheckBox* blueZoneHintingCheckBox_;
|
|
71
|
+ QCheckBox* segmentDrawingCheckBox_;
|
|
72
|
+ QCheckBox* autoHintingCheckBox_;
|
|
73
|
+ QCheckBox* stemDarkeningCheckBox_;
|
|
74
|
+ QCheckBox* embeddedBitmapCheckBox_;
|
|
75
|
+ QCheckBox* colorLayerCheckBox_;
|
|
76
|
+ QCheckBox* kerningCheckBox_;
|
|
77
|
+ QCheckBox* lsbRsbDeltaCheckBox_;
|
|
78
|
+
|
|
79
|
+ AntiAliasingComboBoxModel* antiAliasingComboBoxModel_;
|
|
80
|
+ HintingModeComboBoxModel* hintingModeComboBoxModel_;
|
|
81
|
+ LCDFilterComboBoxModel* lcdFilterComboboxModel_;
|
|
82
|
+
|
|
83
|
+ QComboBox* hintingModeComboBox_;
|
|
84
|
+ QComboBox* antiAliasingComboBox_;
|
|
85
|
+ QComboBox* lcdFilterComboBox_;
|
|
86
|
+ QComboBox* paletteComboBox_;
|
|
87
|
+
|
|
88
|
+ QSlider* gammaSlider_;
|
|
89
|
+
|
|
90
|
+ QPushButton* backgroundButton_;
|
|
91
|
+ QPushButton* foregroundButton_;
|
|
92
|
+ QFrame* backgroundBlock_;
|
|
93
|
+ QFrame* foregroundBlock_;
|
|
94
|
+
|
|
95
|
+ QVBoxLayout* mainLayout_;
|
|
96
|
+ QGridLayout* generalTabLayout_;
|
|
97
|
+ QGridLayout* hintingRenderingTabLayout_;
|
|
98
|
+ QVBoxLayout* debugLayout_;
|
|
99
|
+ QHBoxLayout* gammaLayout_;
|
|
100
|
+ QHBoxLayout* colorPickerLayout_;
|
|
101
|
+
|
|
102
|
+ QColor backgroundColor_;
|
|
103
|
+ QColor foregroundColor_;
|
|
104
|
+
|
|
105
|
+ //////// Initializing funcs
|
|
106
|
+
|
|
107
|
+ void createConnections();
|
|
108
|
+ void createLayout();
|
|
109
|
+ void createLayoutNormal();
|
|
110
|
+ void setDefaults();
|
|
111
|
+
|
|
112
|
+ //////// Other funcs
|
|
113
|
+
|
|
114
|
+ void populatePalettes();
|
|
115
|
+
|
|
116
|
+ void checkAllSettings();
|
|
117
|
+ void checkHinting();
|
|
118
|
+ void checkHintingMode();
|
|
119
|
+ void checkAutoHinting();
|
|
120
|
+ void checkAntiAliasing();
|
|
121
|
+ void checkPalette();
|
|
122
|
+ void checkStemDarkening();
|
|
123
|
+
|
|
124
|
+ void openBackgroundPicker();
|
|
125
|
+ void openForegroundPicker();
|
|
126
|
+ void updateGamma();
|
|
127
|
+ void resetColorBlocks();
|
|
128
|
+};
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+// end of settingpanel.hpp |
src/ftinspect/panels/singular.cpp
|
1
|
+// singular.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "singular.hpp"
|
|
6
|
+
|
|
7
|
+#include <QSizePolicy>
|
|
8
|
+#include <QToolTip>
|
|
9
|
+#include <QWheelEvent>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+SingularTab::SingularTab(QWidget* parent, Engine* engine)
|
|
13
|
+: QWidget(parent), engine_(engine),
|
|
14
|
+ graphicsDefault_(GraphicsDefault::deafultInstance())
|
|
15
|
+{
|
|
16
|
+ createLayout();
|
|
17
|
+ createConnections();
|
|
18
|
+
|
|
19
|
+ currentGlyphIndex_ = 0;
|
|
20
|
+ setDefaults();
|
|
21
|
+ checkShowPoints();
|
|
22
|
+}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+SingularTab::~SingularTab()
|
|
26
|
+{
|
|
27
|
+ delete gridItem_;
|
|
28
|
+ gridItem_ = NULL;
|
|
29
|
+}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+void
|
|
33
|
+SingularTab::setGlyphIndex(int index)
|
|
34
|
+{
|
|
35
|
+ // only adjust current glyph index if we have a valid font
|
|
36
|
+ if (currentGlyphCount_ <= 0)
|
|
37
|
+ return;
|
|
38
|
+
|
|
39
|
+ currentGlyphIndex_ = qBound(0, index, currentGlyphCount_ - 1);
|
|
40
|
+
|
|
41
|
+ QString upperHex = QString::number(currentGlyphIndex_, 16).toUpper();
|
|
42
|
+ glyphIndexLabel_->setText(
|
|
43
|
+ QString("%1 (0x%2)").arg(currentGlyphIndex_).arg(upperHex));
|
|
44
|
+ glyphNameLabel_->setText(engine_->glyphName(currentGlyphIndex_));
|
|
45
|
+
|
|
46
|
+ drawGlyph();
|
|
47
|
+}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+void
|
|
51
|
+SingularTab::drawGlyph()
|
|
52
|
+{
|
|
53
|
+ // the call to `engine->loadOutline' updates FreeType's load flags
|
|
54
|
+
|
|
55
|
+ if (!engine_)
|
|
56
|
+ return;
|
|
57
|
+
|
|
58
|
+ if (currentGlyphBitmapItem_)
|
|
59
|
+ {
|
|
60
|
+ glyphScene_->removeItem(currentGlyphBitmapItem_);
|
|
61
|
+ delete currentGlyphBitmapItem_;
|
|
62
|
+
|
|
63
|
+ currentGlyphBitmapItem_ = NULL;
|
|
64
|
+ }
|
|
65
|
+
|
|
66
|
+ if (currentGlyphOutlineItem_)
|
|
67
|
+ {
|
|
68
|
+ glyphScene_->removeItem(currentGlyphOutlineItem_);
|
|
69
|
+ delete currentGlyphOutlineItem_;
|
|
70
|
+
|
|
71
|
+ currentGlyphOutlineItem_ = NULL;
|
|
72
|
+ }
|
|
73
|
+
|
|
74
|
+ if (currentGlyphPointsItem_)
|
|
75
|
+ {
|
|
76
|
+ glyphScene_->removeItem(currentGlyphPointsItem_);
|
|
77
|
+ delete currentGlyphPointsItem_;
|
|
78
|
+
|
|
79
|
+ currentGlyphPointsItem_ = NULL;
|
|
80
|
+ }
|
|
81
|
+
|
|
82
|
+ if (currentGlyphPointNumbersItem_)
|
|
83
|
+ {
|
|
84
|
+ glyphScene_->removeItem(currentGlyphPointNumbersItem_);
|
|
85
|
+ delete currentGlyphPointNumbersItem_;
|
|
86
|
+
|
|
87
|
+ currentGlyphPointNumbersItem_ = NULL;
|
|
88
|
+ }
|
|
89
|
+
|
|
90
|
+ // TODO refactor engine.
|
|
91
|
+ //glyphView_->setBackgroundBrush(
|
|
92
|
+ // QColor(engine_->renderingEngine()->background()));
|
|
93
|
+
|
|
94
|
+ applySettings();
|
|
95
|
+ FT_Glyph glyph = NULL; // TODO: refactor engine!
|
|
96
|
+ //FT_Glyph glyph = engine_->loadGlyph(currentGlyphIndex_);
|
|
97
|
+ if (glyph)
|
|
98
|
+ {
|
|
99
|
+ if (showBitmapCheckBox_->isChecked())
|
|
100
|
+ {
|
|
101
|
+ currentGlyphBitmapItem_
|
|
102
|
+ = new GlyphBitmap(currentGlyphIndex_,
|
|
103
|
+ glyph,
|
|
104
|
+ engine_);
|
|
105
|
+ currentGlyphBitmapItem_->setZValue(-1);
|
|
106
|
+ glyphScene_->addItem(currentGlyphBitmapItem_);
|
|
107
|
+ }
|
|
108
|
+
|
|
109
|
+ if (showOutlinesCheckBox_->isChecked())
|
|
110
|
+ {
|
|
111
|
+ currentGlyphOutlineItem_ = new GlyphOutline(graphicsDefault_->outlinePen,
|
|
112
|
+ glyph);
|
|
113
|
+ currentGlyphOutlineItem_->setZValue(1);
|
|
114
|
+ glyphScene_->addItem(currentGlyphOutlineItem_);
|
|
115
|
+ }
|
|
116
|
+
|
|
117
|
+ if (showPointsCheckBox_->isChecked())
|
|
118
|
+ {
|
|
119
|
+ currentGlyphPointsItem_ = new GlyphPoints(graphicsDefault_->onPen,
|
|
120
|
+ graphicsDefault_->offPen,
|
|
121
|
+ glyph);
|
|
122
|
+ currentGlyphPointsItem_->setZValue(1);
|
|
123
|
+ glyphScene_->addItem(currentGlyphPointsItem_);
|
|
124
|
+
|
|
125
|
+ if (showPointNumbersCheckBox_->isChecked())
|
|
126
|
+ {
|
|
127
|
+ currentGlyphPointNumbersItem_
|
|
128
|
+ = new GlyphPointNumbers(graphicsDefault_->onPen,
|
|
129
|
+ graphicsDefault_->offPen,
|
|
130
|
+ glyph);
|
|
131
|
+ currentGlyphPointNumbersItem_->setZValue(1);
|
|
132
|
+ glyphScene_->addItem(currentGlyphPointNumbersItem_);
|
|
133
|
+ }
|
|
134
|
+ }
|
|
135
|
+ }
|
|
136
|
+
|
|
137
|
+ glyphScene_->update();
|
|
138
|
+}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+void
|
|
142
|
+SingularTab::checkShowPoints()
|
|
143
|
+{
|
|
144
|
+ if (showPointsCheckBox_->isChecked())
|
|
145
|
+ showPointNumbersCheckBox_->setEnabled(true);
|
|
146
|
+ else
|
|
147
|
+ showPointNumbersCheckBox_->setEnabled(false);
|
|
148
|
+ drawGlyph();
|
|
149
|
+}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+void
|
|
153
|
+SingularTab::zoom()
|
|
154
|
+{
|
|
155
|
+ int scale = static_cast<int>(sizeSelector_->zoomFactor());
|
|
156
|
+
|
|
157
|
+ QTransform transform;
|
|
158
|
+ transform.scale(scale, scale);
|
|
159
|
+
|
|
160
|
+ // we want horizontal and vertical 1px lines displayed with full pixels;
|
|
161
|
+ // we thus have to shift the coordinate system accordingly, using a value
|
|
162
|
+ // that represents 0.5px (i.e., half the 1px line width) after the scaling
|
|
163
|
+ qreal shift = 0.5 / scale;
|
|
164
|
+ transform.translate(shift, shift);
|
|
165
|
+
|
|
166
|
+ glyphView_->setTransform(transform);
|
|
167
|
+ updateGrid();
|
|
168
|
+}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+void
|
|
172
|
+SingularTab::backToCenter()
|
|
173
|
+{
|
|
174
|
+ glyphView_->centerOn(0, 0);
|
|
175
|
+ if (currentGlyphBitmapItem_)
|
|
176
|
+ glyphView_->ensureVisible(currentGlyphBitmapItem_);
|
|
177
|
+ else if (currentGlyphPointsItem_)
|
|
178
|
+ glyphView_->ensureVisible(currentGlyphPointsItem_);
|
|
179
|
+
|
|
180
|
+ updateGrid();
|
|
181
|
+}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+void
|
|
185
|
+SingularTab::updateGrid()
|
|
186
|
+{
|
|
187
|
+ if (gridItem_)
|
|
188
|
+ gridItem_->updateRect();
|
|
189
|
+}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+void
|
|
193
|
+SingularTab::wheelZoom(QWheelEvent* event)
|
|
194
|
+{
|
|
195
|
+ int numSteps = event->angleDelta().y() / 120;
|
|
196
|
+ sizeSelector_->handleWheelZoomBySteps(numSteps);
|
|
197
|
+ // TODO: Zoom relative to viewport left-bottom?
|
|
198
|
+}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+void
|
|
202
|
+SingularTab::wheelResize(QWheelEvent* event)
|
|
203
|
+{
|
|
204
|
+ sizeSelector_->handleWheelResizeFromGrid(event);
|
|
205
|
+}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+void
|
|
209
|
+SingularTab::setGridVisible()
|
|
210
|
+{
|
|
211
|
+ gridItem_->setShowGrid(showGridCheckBox_->isChecked(),
|
|
212
|
+ showAuxLinesCheckBox_->isChecked());
|
|
213
|
+}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+void
|
|
217
|
+SingularTab::showToolTip()
|
|
218
|
+{
|
|
219
|
+ QToolTip::showText(mapToGlobal(helpButton_->pos()),
|
|
220
|
+ tr("Scroll: Grid Up/Down\n"
|
|
221
|
+ "Alt + Scroll: Grid Left/Right\n"
|
|
222
|
+ "Ctrl + Scroll: Adjust Zoom (Relative to cursor)\n"
|
|
223
|
+ "Shift + Scroll: Adjust Font Size\n"
|
|
224
|
+ "Shift + Plus/Minus: Adjust Font Size\n"
|
|
225
|
+ "Shift + 0: Reset Font Size to Default"),
|
|
226
|
+ helpButton_);
|
|
227
|
+}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+bool
|
|
231
|
+SingularTab::eventFilter(QObject* watched,
|
|
232
|
+ QEvent* event)
|
|
233
|
+{
|
|
234
|
+ if (event->type() == QEvent::KeyPress)
|
|
235
|
+ {
|
|
236
|
+ auto keyEvent = dynamic_cast<QKeyEvent*>(event);
|
|
237
|
+ if (sizeSelector_->handleKeyEvent(keyEvent))
|
|
238
|
+ return true;
|
|
239
|
+ }
|
|
240
|
+ return false;
|
|
241
|
+}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+void
|
|
245
|
+SingularTab::resizeEvent(QResizeEvent* event)
|
|
246
|
+{
|
|
247
|
+ QWidget::resizeEvent(event);
|
|
248
|
+
|
|
249
|
+ // Tricky part: when loading, this method will be called twice. Only at the
|
|
250
|
+ // second time the initial layouting is done, thus the result of `centerOn`
|
|
251
|
+ // can be valid.
|
|
252
|
+ // We want to only center the midpoint of the size when the program starts up
|
|
253
|
+ // so we use a counter to track the status.
|
|
254
|
+ if (initialPositionSetCount_ <= 0)
|
|
255
|
+ return;
|
|
256
|
+ initialPositionSetCount_--;
|
|
257
|
+
|
|
258
|
+ // The code below mainly:
|
|
259
|
+ // 1. Center the grid on the the center point of the ppem x ppem bbox.
|
|
260
|
+ // 2. Adjust the viewport zoom to fit the ppem x ppem bbox
|
|
261
|
+ updateGeometry();
|
|
262
|
+ auto size = sizeSelector_->selectedSize();
|
|
263
|
+ auto unit = sizeSelector_->selectedUnit();
|
|
264
|
+ if (unit == FontSizeSelector::Units_pt)
|
|
265
|
+ {
|
|
266
|
+ sizeSelector_->applyToEngine(engine_);
|
|
267
|
+ auto dpi = engine_->dpi();
|
|
268
|
+ size = size * dpi / 72.0;
|
|
269
|
+ }
|
|
270
|
+ glyphView_->centerOn(size / 2, -size / 2);
|
|
271
|
+
|
|
272
|
+ auto viewSize = glyphView_->size();
|
|
273
|
+ auto minViewSide = std::min(viewSize.height(), viewSize.width());
|
|
274
|
+ sizeSelector_->setZoomFactor(static_cast<int>(minViewSide / size * 0.7));
|
|
275
|
+}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+void
|
|
279
|
+SingularTab::createLayout()
|
|
280
|
+{
|
|
281
|
+ glyphScene_ = new QGraphicsScene(this);
|
|
282
|
+
|
|
283
|
+ currentGlyphBitmapItem_ = NULL;
|
|
284
|
+ currentGlyphOutlineItem_ = NULL;
|
|
285
|
+ currentGlyphPointsItem_ = NULL;
|
|
286
|
+ currentGlyphPointNumbersItem_ = NULL;
|
|
287
|
+
|
|
288
|
+ glyphView_ = new QGraphicsViewx(this);
|
|
289
|
+ glyphView_->setRenderHint(QPainter::Antialiasing, true);
|
|
290
|
+ glyphView_->setAcceptDrops(false);
|
|
291
|
+ glyphView_->setDragMode(QGraphicsView::ScrollHandDrag);
|
|
292
|
+ glyphView_->setOptimizationFlags(QGraphicsView::DontSavePainterState);
|
|
293
|
+ glyphView_->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
|
|
294
|
+ glyphView_->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
|
295
|
+ glyphView_->setScene(glyphScene_);
|
|
296
|
+ glyphView_->setBackgroundBrush(Qt::white);
|
|
297
|
+
|
|
298
|
+ gridItem_ = new Grid(glyphView_);
|
|
299
|
+ glyphScene_->addItem(gridItem_);
|
|
300
|
+
|
|
301
|
+ // Don't use QGraphicsTextItem: We want this hint to be anchored at the
|
|
302
|
+ // top-left corner.
|
|
303
|
+ auto overlayFont = font();
|
|
304
|
+ overlayFont.setPixelSize(24);
|
|
305
|
+
|
|
306
|
+ glyphIndexLabel_ = new QLabel(glyphView_);
|
|
307
|
+ glyphNameLabel_ = new QLabel(glyphView_);
|
|
308
|
+ glyphIndexLabel_->setFont(overlayFont);
|
|
309
|
+ glyphNameLabel_->setFont(overlayFont);
|
|
310
|
+ glyphIndexLabel_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
311
|
+ glyphNameLabel_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
312
|
+ glyphIndexLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
|
313
|
+ glyphNameLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
|
314
|
+
|
|
315
|
+ glyphIndexLabel_->setStyleSheet("QLabel { color : black; }");
|
|
316
|
+ glyphNameLabel_->setStyleSheet("QLabel { color : black; }");
|
|
317
|
+
|
|
318
|
+ indexSelector_ = new GlyphIndexSelector(this);
|
|
319
|
+ indexSelector_->setSingleMode(true);
|
|
320
|
+
|
|
321
|
+ sizeSelector_ = new FontSizeSelector(this, false, false);
|
|
322
|
+
|
|
323
|
+ centerGridButton_ = new QPushButton("Go Back to Grid Center", this);
|
|
324
|
+ helpButton_ = new QPushButton("?", this);
|
|
325
|
+ helpButton_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
|
326
|
+
|
|
327
|
+ showBitmapCheckBox_ = new QCheckBox(tr("Show Bitmap"), this);
|
|
328
|
+ showPointsCheckBox_ = new QCheckBox(tr("Show Points"), this);
|
|
329
|
+ showPointNumbersCheckBox_ = new QCheckBox(tr("Show Point Numbers"), this);
|
|
330
|
+ showOutlinesCheckBox_ = new QCheckBox(tr("Show Outlines"), this);
|
|
331
|
+ showGridCheckBox_ = new QCheckBox(tr("Show Grid"), this);
|
|
332
|
+ showAuxLinesCheckBox_ = new QCheckBox(tr("Show Aux. Lines"), this);
|
|
333
|
+
|
|
334
|
+ // Tooltips
|
|
335
|
+ centerGridButton_->setToolTip(tr(
|
|
336
|
+ "Move the viewport so the origin point is at the center of the view."));
|
|
337
|
+ showBitmapCheckBox_->setToolTip(tr("Show glyph bitmap."));
|
|
338
|
+ showPointsCheckBox_->setToolTip(
|
|
339
|
+ tr("Show control points (only valid when the glyph is an outline glyph)."));
|
|
340
|
+ showPointNumbersCheckBox_->setToolTip(
|
|
341
|
+ tr("Show point numbers (only available when points are shown)."));
|
|
342
|
+ showOutlinesCheckBox_->setToolTip(tr("Show (vector) outline (only valid when "
|
|
343
|
+ "the glyph is an outline glyph)."));
|
|
344
|
+ showGridCheckBox_->setToolTip(tr("Show grid lines (x axis: baseline)."));
|
|
345
|
+ showBitmapCheckBox_->setToolTip(
|
|
346
|
+ tr("Show auxiliary lines (blue: y-advance; red: ascender/descender)."));
|
|
347
|
+ helpButton_->setToolTip(tr("View scroll help"));
|
|
348
|
+
|
|
349
|
+ // Layouting
|
|
350
|
+ indexHelpLayout_ = new QHBoxLayout;
|
|
351
|
+ indexHelpLayout_->addWidget(indexSelector_, 1);
|
|
352
|
+ indexHelpLayout_->addWidget(helpButton_);
|
|
353
|
+
|
|
354
|
+ sizeLayout_ = new QHBoxLayout;
|
|
355
|
+ sizeLayout_->addStretch(2);
|
|
356
|
+ sizeLayout_->addWidget(sizeSelector_, 4);
|
|
357
|
+ sizeLayout_->addStretch(1);
|
|
358
|
+ sizeLayout_->addWidget(centerGridButton_);
|
|
359
|
+ sizeLayout_->addStretch(2);
|
|
360
|
+
|
|
361
|
+ checkBoxesLayout_ = new QHBoxLayout;
|
|
362
|
+ checkBoxesLayout_->setSpacing(10);
|
|
363
|
+ checkBoxesLayout_->addWidget(showBitmapCheckBox_);
|
|
364
|
+ checkBoxesLayout_->addWidget(showPointsCheckBox_);
|
|
365
|
+ checkBoxesLayout_->addWidget(showPointNumbersCheckBox_);
|
|
366
|
+ checkBoxesLayout_->addWidget(showOutlinesCheckBox_);
|
|
367
|
+ checkBoxesLayout_->addWidget(showGridCheckBox_);
|
|
368
|
+ checkBoxesLayout_->addWidget(showAuxLinesCheckBox_);
|
|
369
|
+
|
|
370
|
+ glyphOverlayIndexLayout_ = new QHBoxLayout;
|
|
371
|
+ glyphOverlayIndexLayout_->addWidget(glyphIndexLabel_);
|
|
372
|
+ glyphOverlayIndexLayout_->addWidget(glyphNameLabel_);
|
|
373
|
+ glyphOverlayLayout_ = new QGridLayout; // use a grid layout to align
|
|
374
|
+ glyphOverlayLayout_->addLayout(glyphOverlayIndexLayout_, 0, 1,
|
|
375
|
+ Qt::AlignTop | Qt::AlignRight);
|
|
376
|
+ glyphView_->setLayout(glyphOverlayLayout_);
|
|
377
|
+
|
|
378
|
+ mainLayout_ = new QVBoxLayout;
|
|
379
|
+ mainLayout_->addWidget(glyphView_);
|
|
380
|
+ mainLayout_->addLayout(indexHelpLayout_);
|
|
381
|
+ mainLayout_->addSpacing(10);
|
|
382
|
+ mainLayout_->addLayout(sizeLayout_);
|
|
383
|
+ mainLayout_->addLayout(checkBoxesLayout_);
|
|
384
|
+ mainLayout_->addSpacing(10);
|
|
385
|
+
|
|
386
|
+ setLayout(mainLayout_);
|
|
387
|
+}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+void
|
|
391
|
+SingularTab::createConnections()
|
|
392
|
+{
|
|
393
|
+ connect(sizeSelector_, &FontSizeSelector::valueChanged,
|
|
394
|
+ this, &SingularTab::repaintGlyph);
|
|
395
|
+ connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
|
|
396
|
+ this, &SingularTab::setGlyphIndex);
|
|
397
|
+
|
|
398
|
+ connect(glyphView_, &QGraphicsViewx::shiftWheelEvent,
|
|
399
|
+ this, &SingularTab::wheelResize);
|
|
400
|
+ connect(glyphView_, &QGraphicsViewx::ctrlWheelEvent,
|
|
401
|
+ this, &SingularTab::wheelZoom);
|
|
402
|
+ // Use `updateGrid` to support infinite panning.
|
|
403
|
+ connect(glyphView_->horizontalScrollBar(), &QScrollBar::valueChanged,
|
|
404
|
+ this, &SingularTab::updateGrid);
|
|
405
|
+ connect(glyphView_->verticalScrollBar(), &QScrollBar::valueChanged,
|
|
406
|
+ this, &SingularTab::updateGrid);
|
|
407
|
+
|
|
408
|
+ connect(centerGridButton_, &QPushButton::clicked,
|
|
409
|
+ this, &SingularTab::backToCenter);
|
|
410
|
+ connect(helpButton_, &QPushButton::clicked,
|
|
411
|
+ this, &SingularTab::showToolTip);
|
|
412
|
+
|
|
413
|
+ connect(showBitmapCheckBox_, &QCheckBox::clicked,
|
|
414
|
+ this, &SingularTab::drawGlyph);
|
|
415
|
+ connect(showPointsCheckBox_, &QCheckBox::clicked,
|
|
416
|
+ this, &SingularTab::checkShowPoints);
|
|
417
|
+ connect(showPointNumbersCheckBox_, &QCheckBox::clicked,
|
|
418
|
+ this, &SingularTab::drawGlyph);
|
|
419
|
+ connect(showOutlinesCheckBox_, &QCheckBox::clicked,
|
|
420
|
+ this, &SingularTab::drawGlyph);
|
|
421
|
+ connect(showGridCheckBox_, &QCheckBox::clicked,
|
|
422
|
+ this, &SingularTab::setGridVisible);
|
|
423
|
+ connect(showAuxLinesCheckBox_, &QCheckBox::clicked,
|
|
424
|
+ this, &SingularTab::setGridVisible);
|
|
425
|
+
|
|
426
|
+ sizeSelector_->installEventFilterForWidget(glyphView_);
|
|
427
|
+ sizeSelector_->installEventFilterForWidget(this);
|
|
428
|
+}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+void
|
|
432
|
+SingularTab::repaintGlyph()
|
|
433
|
+{
|
|
434
|
+ zoom();
|
|
435
|
+ drawGlyph();
|
|
436
|
+}
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+void
|
|
440
|
+SingularTab::reloadFont()
|
|
441
|
+{
|
|
442
|
+ currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
|
|
443
|
+ indexSelector_->setMinMax(0, currentGlyphCount_);
|
|
444
|
+ {
|
|
445
|
+ QSignalBlocker blocker(sizeSelector_);
|
|
446
|
+ sizeSelector_->reloadFromFont(engine_);
|
|
447
|
+ }
|
|
448
|
+ drawGlyph();
|
|
449
|
+}
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+void
|
|
453
|
+SingularTab::setCurrentGlyphAndSize(int glyphIndex,
|
|
454
|
+ double sizePoint)
|
|
455
|
+{
|
|
456
|
+ if (sizePoint >= 0)
|
|
457
|
+ sizeSelector_->setSizePoint(sizePoint);
|
|
458
|
+ indexSelector_->setCurrentIndex(glyphIndex); // this will auto trigger update
|
|
459
|
+}
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+int
|
|
463
|
+SingularTab::currentGlyph()
|
|
464
|
+{
|
|
465
|
+ return indexSelector_->currentIndex();
|
|
466
|
+}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+void
|
|
470
|
+SingularTab::applySettings()
|
|
471
|
+{
|
|
472
|
+ sizeSelector_->applyToEngine(engine_);
|
|
473
|
+}
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+void
|
|
477
|
+SingularTab::setDefaults()
|
|
478
|
+{
|
|
479
|
+ currentGlyphIndex_ = 0;
|
|
480
|
+
|
|
481
|
+ showBitmapCheckBox_->setChecked(true);
|
|
482
|
+ showOutlinesCheckBox_->setChecked(true);
|
|
483
|
+ showGridCheckBox_->setChecked(true);
|
|
484
|
+ showAuxLinesCheckBox_->setChecked(true);
|
|
485
|
+ gridItem_->setShowGrid(true, true);
|
|
486
|
+
|
|
487
|
+ indexSelector_->setCurrentIndex(indexSelector_->currentIndex(), true);
|
|
488
|
+ zoom();
|
|
489
|
+}
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+// end of singular.cpp |
src/ftinspect/panels/singular.hpp
|
1
|
+// singular.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include "abstracttab.hpp"
|
|
8
|
+#include "../widgets/customwidgets.hpp"
|
|
9
|
+#include "../widgets/glyphindexselector.hpp"
|
|
10
|
+#include "../widgets/fontsizeselector.hpp"
|
|
11
|
+#include "../glyphcomponents/glyphbitmap.hpp"
|
|
12
|
+#include "../glyphcomponents/glyphoutline.hpp"
|
|
13
|
+#include "../glyphcomponents/glyphpointnumbers.hpp"
|
|
14
|
+#include "../glyphcomponents/glyphpoints.hpp"
|
|
15
|
+#include "../glyphcomponents/grid.hpp"
|
|
16
|
+#include "../glyphcomponents/graphicsdefault.hpp"
|
|
17
|
+#include "../engine/engine.hpp"
|
|
18
|
+#include "../models/customcomboboxmodels.hpp"
|
|
19
|
+
|
|
20
|
+#include <QWidget>
|
|
21
|
+#include <QPushButton>
|
|
22
|
+#include <QSpinBox>
|
|
23
|
+#include <QGraphicsScene>
|
|
24
|
+#include <QGraphicsView>
|
|
25
|
+#include <QScrollBar>
|
|
26
|
+#include <QLabel>
|
|
27
|
+#include <QComboBox>
|
|
28
|
+#include <QPen>
|
|
29
|
+#include <QCheckBox>
|
|
30
|
+#include <QVector>
|
|
31
|
+#include <QGridLayout>
|
|
32
|
+#include <QBoxLayout>
|
|
33
|
+
|
|
34
|
+class SingularTab
|
|
35
|
+: public QWidget, public AbstractTab
|
|
36
|
+{
|
|
37
|
+ Q_OBJECT
|
|
38
|
+public:
|
|
39
|
+ SingularTab(QWidget* parent, Engine* engine);
|
|
40
|
+ ~SingularTab() override;
|
|
41
|
+
|
|
42
|
+ void repaintGlyph() override;
|
|
43
|
+ void reloadFont() override;
|
|
44
|
+ // when sizePoint <= 0, the size remains unchanged.
|
|
45
|
+ void setCurrentGlyphAndSize(int glyphIndex, double sizePoint);
|
|
46
|
+ int currentGlyph();
|
|
47
|
+
|
|
48
|
+private slots:
|
|
49
|
+ void setGlyphIndex(int);
|
|
50
|
+ void drawGlyph();
|
|
51
|
+
|
|
52
|
+ void checkShowPoints();
|
|
53
|
+
|
|
54
|
+ void zoom();
|
|
55
|
+ void backToCenter();
|
|
56
|
+ void wheelZoom(QWheelEvent* event);
|
|
57
|
+ void wheelResize(QWheelEvent* event);
|
|
58
|
+ void setGridVisible();
|
|
59
|
+ void showToolTip();
|
|
60
|
+
|
|
61
|
+protected:
|
|
62
|
+ bool eventFilter(QObject* watched, QEvent* event) override;
|
|
63
|
+ void resizeEvent(QResizeEvent* event) override;
|
|
64
|
+
|
|
65
|
+private:
|
|
66
|
+ int currentGlyphIndex_;
|
|
67
|
+ int currentGlyphCount_;
|
|
68
|
+
|
|
69
|
+ Engine* engine_;
|
|
70
|
+
|
|
71
|
+ QGraphicsScene* glyphScene_;
|
|
72
|
+ QGraphicsViewx* glyphView_;
|
|
73
|
+
|
|
74
|
+ GlyphOutline* currentGlyphOutlineItem_;
|
|
75
|
+ GlyphPoints* currentGlyphPointsItem_;
|
|
76
|
+ GlyphPointNumbers* currentGlyphPointNumbersItem_;
|
|
77
|
+ GlyphBitmap* currentGlyphBitmapItem_;
|
|
78
|
+ Grid* gridItem_ = NULL;
|
|
79
|
+
|
|
80
|
+ GlyphIndexSelector* indexSelector_;
|
|
81
|
+ FontSizeSelector* sizeSelector_;
|
|
82
|
+ QPushButton* centerGridButton_;
|
|
83
|
+ QPushButton* helpButton_;
|
|
84
|
+
|
|
85
|
+ QLabel* glyphIndexLabel_;
|
|
86
|
+ QLabel* glyphNameLabel_;
|
|
87
|
+
|
|
88
|
+ QCheckBox* showBitmapCheckBox_;
|
|
89
|
+ QCheckBox* showOutlinesCheckBox_;
|
|
90
|
+ QCheckBox* showPointNumbersCheckBox_;
|
|
91
|
+ QCheckBox* showPointsCheckBox_;
|
|
92
|
+ QCheckBox* showGridCheckBox_;
|
|
93
|
+ QCheckBox* showAuxLinesCheckBox_;
|
|
94
|
+
|
|
95
|
+ QVBoxLayout* mainLayout_;
|
|
96
|
+ QHBoxLayout* checkBoxesLayout_;
|
|
97
|
+ QHBoxLayout* indexHelpLayout_;
|
|
98
|
+ QHBoxLayout* sizeLayout_;
|
|
99
|
+ QGridLayout* glyphOverlayLayout_;
|
|
100
|
+ QHBoxLayout* glyphOverlayIndexLayout_;
|
|
101
|
+
|
|
102
|
+ GraphicsDefault* graphicsDefault_;
|
|
103
|
+
|
|
104
|
+ int initialPositionSetCount_ = 2; // see `resizeEvent`
|
|
105
|
+
|
|
106
|
+ void createLayout();
|
|
107
|
+ void createConnections();
|
|
108
|
+
|
|
109
|
+ void updateGrid();
|
|
110
|
+ void applySettings();
|
|
111
|
+ void setDefaults();
|
|
112
|
+};
|
|
113
|
+
|
|
114
|
+// end of singular.hpp |
src/ftinspect/widgets/fontsizeselector.cpp
|
1
|
+// fontsizeselector.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "fontsizeselector.hpp"
|
|
6
|
+
|
|
7
|
+#include "../engine/engine.hpp"
|
|
8
|
+
|
|
9
|
+#include <algorithm>
|
|
10
|
+
|
|
11
|
+FontSizeSelector::FontSizeSelector(QWidget* parent,
|
|
12
|
+ bool zoomNewLine,
|
|
13
|
+ bool continuousView)
|
|
14
|
+: QWidget(parent), continuousView_(continuousView)
|
|
15
|
+{
|
|
16
|
+ createLayout(zoomNewLine);
|
|
17
|
+ createConnections();
|
|
18
|
+ setDefaults();
|
|
19
|
+}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+double
|
|
23
|
+FontSizeSelector::selectedSize()
|
|
24
|
+{
|
|
25
|
+ return sizeDoubleSpinBox_->value();
|
|
26
|
+}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+FontSizeSelector::Units
|
|
30
|
+FontSizeSelector::selectedUnit()
|
|
31
|
+{
|
|
32
|
+ return static_cast<Units>(unitsComboBox_->currentIndex());
|
|
33
|
+}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+double
|
|
37
|
+FontSizeSelector::zoomFactor()
|
|
38
|
+{
|
|
39
|
+ if (continuousView_)
|
|
40
|
+ return zoomSpinBox_->value();
|
|
41
|
+ return static_cast<int>(zoomSpinBox_->value());
|
|
42
|
+}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+void
|
|
46
|
+FontSizeSelector::setSizePixel(int sizePixel)
|
|
47
|
+{
|
|
48
|
+ sizeDoubleSpinBox_->setValue(sizePixel);
|
|
49
|
+ unitsComboBox_->setCurrentIndex(Units_px);
|
|
50
|
+}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+void
|
|
54
|
+FontSizeSelector::setSizePoint(double sizePoint)
|
|
55
|
+{
|
|
56
|
+ sizeDoubleSpinBox_->setValue(sizePoint);
|
|
57
|
+ unitsComboBox_->setCurrentIndex(Units_pt);
|
|
58
|
+}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+void
|
|
62
|
+FontSizeSelector::setZoomFactor(double zoomFactor)
|
|
63
|
+{
|
|
64
|
+ if (continuousView_)
|
|
65
|
+ zoomSpinBox_->setValue(zoomFactor);
|
|
66
|
+ zoomSpinBox_->setValue(static_cast<int>(zoomFactor));
|
|
67
|
+}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+void
|
|
71
|
+FontSizeSelector::reloadFromFont(Engine* engine)
|
|
72
|
+{
|
|
73
|
+ // TODO: update available sizes.
|
|
74
|
+ checkFixedSizeAndEmit();
|
|
75
|
+}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+void
|
|
79
|
+FontSizeSelector::applyToEngine(Engine* engine)
|
|
80
|
+{
|
|
81
|
+ // Spinbox value cannot become negative
|
|
82
|
+ engine->setDPI(dpiSpinBox_->value());
|
|
83
|
+
|
|
84
|
+ if (unitsComboBox_->currentIndex() == Units_px)
|
|
85
|
+ engine->setSizeByPixel(sizeDoubleSpinBox_->value());
|
|
86
|
+ else
|
|
87
|
+ engine->setSizeByPoint(sizeDoubleSpinBox_->value());
|
|
88
|
+}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+void
|
|
92
|
+FontSizeSelector::handleWheelResizeBySteps(int steps)
|
|
93
|
+{
|
|
94
|
+ double sizeAfter = sizeDoubleSpinBox_->value()
|
|
95
|
+ + steps * sizeDoubleSpinBox_->singleStep();
|
|
96
|
+ sizeAfter = std::max(sizeDoubleSpinBox_->minimum(),
|
|
97
|
+ std::min(sizeAfter, sizeDoubleSpinBox_->maximum()));
|
|
98
|
+ sizeDoubleSpinBox_->setValue(sizeAfter);
|
|
99
|
+}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+void
|
|
103
|
+FontSizeSelector::handleWheelZoomBySteps(int steps)
|
|
104
|
+{
|
|
105
|
+ double zoomAfter = zoomSpinBox_->value()
|
|
106
|
+ + steps * zoomSpinBox_->singleStep();
|
|
107
|
+ zoomAfter = std::max(zoomSpinBox_->minimum(),
|
|
108
|
+ std::min(zoomAfter, zoomSpinBox_->maximum()));
|
|
109
|
+ zoomSpinBox_->setValue(zoomAfter);
|
|
110
|
+}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+void
|
|
114
|
+FontSizeSelector::handleWheelResizeFromGrid(QWheelEvent* event)
|
|
115
|
+{
|
|
116
|
+ int numSteps = event->angleDelta().y() / 120;
|
|
117
|
+ handleWheelResizeBySteps(numSteps);
|
|
118
|
+}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+bool
|
|
122
|
+FontSizeSelector::handleKeyEvent(QKeyEvent const* keyEvent)
|
|
123
|
+{
|
|
124
|
+ if (!keyEvent)
|
|
125
|
+ return false;
|
|
126
|
+ auto modifiers = keyEvent->modifiers();
|
|
127
|
+ auto key = keyEvent->key();
|
|
128
|
+ if ((modifiers == Qt::ShiftModifier
|
|
129
|
+ || modifiers == (Qt::ShiftModifier | Qt::KeypadModifier))
|
|
130
|
+ && (key == Qt::Key_Plus
|
|
131
|
+ || key == Qt::Key_Minus
|
|
132
|
+ || key == Qt::Key_Underscore
|
|
133
|
+ || key == Qt::Key_Equal
|
|
134
|
+ || key == Qt::Key_ParenRight))
|
|
135
|
+ {
|
|
136
|
+ if (key == Qt::Key_Plus || key == Qt::Key_Equal)
|
|
137
|
+ handleWheelResizeBySteps(1);
|
|
138
|
+ else if (key == Qt::Key_Minus
|
|
139
|
+ || key == Qt::Key_Underscore)
|
|
140
|
+ handleWheelResizeBySteps(-1);
|
|
141
|
+ else if (key == Qt::Key_ParenRight)
|
|
142
|
+ setDefaults(true);
|
|
143
|
+ return true;
|
|
144
|
+ }
|
|
145
|
+ return false;
|
|
146
|
+}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+void
|
|
150
|
+FontSizeSelector::installEventFilterForWidget(QWidget* widget)
|
|
151
|
+{
|
|
152
|
+ widget->installEventFilter(this);
|
|
153
|
+}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+bool
|
|
157
|
+FontSizeSelector::eventFilter(QObject* watched,
|
|
158
|
+ QEvent* event)
|
|
159
|
+{
|
|
160
|
+ if (event->type() == QEvent::KeyPress)
|
|
161
|
+ {
|
|
162
|
+ auto keyEvent = dynamic_cast<QKeyEvent*>(event);
|
|
163
|
+ if (handleKeyEvent(keyEvent))
|
|
164
|
+ return true;
|
|
165
|
+ }
|
|
166
|
+ return QWidget::eventFilter(watched, event);
|
|
167
|
+}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+void
|
|
171
|
+FontSizeSelector::checkUnits()
|
|
172
|
+{
|
|
173
|
+ int index = unitsComboBox_->currentIndex();
|
|
174
|
+
|
|
175
|
+ if (index == Units_px)
|
|
176
|
+ {
|
|
177
|
+ dpiLabel_->setEnabled(false);
|
|
178
|
+ dpiSpinBox_->setEnabled(false);
|
|
179
|
+ sizeDoubleSpinBox_->setSingleStep(1);
|
|
180
|
+
|
|
181
|
+ QSignalBlocker blocker(sizeDoubleSpinBox_);
|
|
182
|
+ sizeDoubleSpinBox_->setValue(qRound(sizeDoubleSpinBox_->value()));
|
|
183
|
+ }
|
|
184
|
+ else
|
|
185
|
+ {
|
|
186
|
+ dpiLabel_->setEnabled(true);
|
|
187
|
+ dpiSpinBox_->setEnabled(true);
|
|
188
|
+ sizeDoubleSpinBox_->setSingleStep(0.5);
|
|
189
|
+ }
|
|
190
|
+
|
|
191
|
+ emit valueChanged();
|
|
192
|
+}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+void
|
|
196
|
+FontSizeSelector::createLayout(bool zoomNewLine)
|
|
197
|
+{
|
|
198
|
+ sizeLabel_ = new QLabel(tr("Size "), this);
|
|
199
|
+ sizeLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
200
|
+ sizeDoubleSpinBox_ = new QDoubleSpinBox(this);
|
|
201
|
+ sizeDoubleSpinBox_->setAlignment(Qt::AlignRight);
|
|
202
|
+ sizeDoubleSpinBox_->setDecimals(1);
|
|
203
|
+ sizeDoubleSpinBox_->setRange(1, 500);
|
|
204
|
+ sizeLabel_->setBuddy(sizeDoubleSpinBox_);
|
|
205
|
+
|
|
206
|
+ unitsComboBox_ = new QComboBox(this);
|
|
207
|
+ unitsComboBox_->insertItem(Units_px, "px");
|
|
208
|
+ unitsComboBox_->insertItem(Units_pt, "pt");
|
|
209
|
+
|
|
210
|
+ dpiLabel_ = new QLabel(tr("DPI "), this);
|
|
211
|
+ dpiLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
212
|
+ dpiSpinBox_ = new QSpinBox(this);
|
|
213
|
+ dpiSpinBox_->setAlignment(Qt::AlignRight);
|
|
214
|
+ dpiSpinBox_->setRange(10, 600);
|
|
215
|
+ dpiLabel_->setBuddy(dpiSpinBox_);
|
|
216
|
+
|
|
217
|
+ zoomLabel_ = new QLabel(tr("Zoom Factor "), this);
|
|
218
|
+ zoomLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
219
|
+ zoomSpinBox_ = new ZoomSpinBox(this, continuousView_);
|
|
220
|
+ zoomSpinBox_->setAlignment(Qt::AlignRight);
|
|
221
|
+ zoomLabel_->setBuddy(zoomSpinBox_);
|
|
222
|
+
|
|
223
|
+ // Tooltips
|
|
224
|
+ sizeDoubleSpinBox_->setToolTip(
|
|
225
|
+ tr("Size value (will be limited to available sizes if\nthe current font "
|
|
226
|
+ "is not scalable)."));
|
|
227
|
+ unitsComboBox_->setToolTip(tr("Unit for the size value (force to pixel if\n"
|
|
228
|
+ "the current font is not scalable)."));
|
|
229
|
+ dpiSpinBox_->setToolTip(
|
|
230
|
+ tr("DPI for the point size value (only valid when the unit is point)."));
|
|
231
|
+ zoomSpinBox_->setToolTip(tr("Adjust zoom."));
|
|
232
|
+
|
|
233
|
+ // Layouting
|
|
234
|
+ mainLayout_ = new QVBoxLayout;
|
|
235
|
+ upLayout_ = new QHBoxLayout;
|
|
236
|
+ upLayout_->addStretch(1);
|
|
237
|
+ upLayout_->addWidget(sizeLabel_);
|
|
238
|
+ upLayout_->addWidget(sizeDoubleSpinBox_);
|
|
239
|
+ upLayout_->addWidget(unitsComboBox_);
|
|
240
|
+ upLayout_->addStretch(1);
|
|
241
|
+ upLayout_->addWidget(dpiLabel_);
|
|
242
|
+ upLayout_->addWidget(dpiSpinBox_);
|
|
243
|
+ upLayout_->addStretch(1);
|
|
244
|
+ if (!zoomNewLine)
|
|
245
|
+ {
|
|
246
|
+ upLayout_->addWidget(zoomLabel_);
|
|
247
|
+ upLayout_->addWidget(zoomSpinBox_);
|
|
248
|
+ upLayout_->addStretch(1);
|
|
249
|
+ mainLayout_->addLayout(upLayout_);
|
|
250
|
+ }
|
|
251
|
+ else
|
|
252
|
+ {
|
|
253
|
+ downLayout_ = new QHBoxLayout;
|
|
254
|
+ downLayout_->addWidget(zoomLabel_);
|
|
255
|
+ downLayout_->addWidget(zoomSpinBox_, 1);
|
|
256
|
+ mainLayout_->addLayout(upLayout_);
|
|
257
|
+ mainLayout_->addLayout(downLayout_);
|
|
258
|
+ }
|
|
259
|
+
|
|
260
|
+ setLayout(mainLayout_);
|
|
261
|
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
262
|
+}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+void
|
|
266
|
+FontSizeSelector::createConnections()
|
|
267
|
+{
|
|
268
|
+ connect(sizeDoubleSpinBox_, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
269
|
+ this, &FontSizeSelector::checkFixedSizeAndEmit);
|
|
270
|
+ connect(unitsComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
271
|
+ this, &FontSizeSelector::checkUnits);
|
|
272
|
+ connect(dpiSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
|
|
273
|
+ this, &FontSizeSelector::checkFixedSizeAndEmit);
|
|
274
|
+ connect(zoomSpinBox_, QOverload<double>::of(&ZoomSpinBox::valueChanged),
|
|
275
|
+ this, &FontSizeSelector::valueChanged);
|
|
276
|
+}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+void
|
|
280
|
+FontSizeSelector::setDefaults(bool sizeOnly)
|
|
281
|
+{
|
|
282
|
+ lastValue_ = 20;
|
|
283
|
+ sizeDoubleSpinBox_->setValue(lastValue_);
|
|
284
|
+ if (sizeOnly)
|
|
285
|
+ return;
|
|
286
|
+ dpiSpinBox_->setValue(96);
|
|
287
|
+ checkUnits();
|
|
288
|
+}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+void
|
|
292
|
+FontSizeSelector::checkFixedSizeAndEmit()
|
|
293
|
+{
|
|
294
|
+ // TODO: check fixed sizes, coerce to available sizes.
|
|
295
|
+ emit valueChanged();
|
|
296
|
+}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+// end of fontsizeselector.cpp |
src/ftinspect/widgets/fontsizeselector.hpp
|
1
|
+// fontsizeselector.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include "customwidgets.hpp"
|
|
8
|
+
|
|
9
|
+#include <QComboBox>
|
|
10
|
+#include <QDoubleSpinBox>
|
|
11
|
+#include <QLabel>
|
|
12
|
+#include <QWidget>
|
|
13
|
+#include <QBoxLayout>
|
|
14
|
+#include <QWheelEvent>
|
|
15
|
+
|
|
16
|
+class Engine;
|
|
17
|
+class FontSizeSelector : public QWidget
|
|
18
|
+{
|
|
19
|
+ Q_OBJECT
|
|
20
|
+
|
|
21
|
+public:
|
|
22
|
+ // For the continuous view mode, see `ZoomSpinBox`'s documentation.
|
|
23
|
+ FontSizeSelector(QWidget* parent, bool zoomNewLine, bool continuousView);
|
|
24
|
+ ~FontSizeSelector() override = default;
|
|
25
|
+
|
|
26
|
+ enum Units : int
|
|
27
|
+ {
|
|
28
|
+ Units_px,
|
|
29
|
+ Units_pt
|
|
30
|
+ };
|
|
31
|
+
|
|
32
|
+ //////// Getters
|
|
33
|
+ double selectedSize();
|
|
34
|
+ Units selectedUnit();
|
|
35
|
+ double zoomFactor();
|
|
36
|
+ //////// Setters
|
|
37
|
+ void setSizePixel(int sizePixel);
|
|
38
|
+ void setSizePoint(double sizePoint);
|
|
39
|
+ void setZoomFactor(double zoomFactor);
|
|
40
|
+
|
|
41
|
+ void reloadFromFont(Engine* engine);
|
|
42
|
+ void applyToEngine(Engine* engine);
|
|
43
|
+ void handleWheelResizeBySteps(int steps);
|
|
44
|
+ void handleWheelZoomBySteps(int steps);
|
|
45
|
+ void handleWheelResizeFromGrid(QWheelEvent* event);
|
|
46
|
+ bool handleKeyEvent(QKeyEvent const* keyEvent);
|
|
47
|
+ void installEventFilterForWidget(QWidget* widget);
|
|
48
|
+
|
|
49
|
+protected:
|
|
50
|
+ bool eventFilter(QObject* watched, QEvent* event) override;
|
|
51
|
+
|
|
52
|
+signals:
|
|
53
|
+ void valueChanged();
|
|
54
|
+
|
|
55
|
+private:
|
|
56
|
+ QLabel* sizeLabel_;
|
|
57
|
+ QLabel* dpiLabel_;
|
|
58
|
+ QLabel* zoomLabel_;
|
|
59
|
+
|
|
60
|
+ QDoubleSpinBox* sizeDoubleSpinBox_;
|
|
61
|
+ QComboBox* unitsComboBox_;
|
|
62
|
+ QSpinBox* dpiSpinBox_;
|
|
63
|
+ ZoomSpinBox* zoomSpinBox_;
|
|
64
|
+
|
|
65
|
+ // Sometimes we need to split 2 lines
|
|
66
|
+ QHBoxLayout* upLayout_;
|
|
67
|
+ QHBoxLayout* downLayout_;
|
|
68
|
+ QVBoxLayout* mainLayout_;
|
|
69
|
+
|
|
70
|
+ bool continuousView_;
|
|
71
|
+ double lastValue_;
|
|
72
|
+
|
|
73
|
+ void createLayout(bool zoomNewLine);
|
|
74
|
+ void createConnections();
|
|
75
|
+ void setDefaults(bool sizeOnly = false);
|
|
76
|
+
|
|
77
|
+ void checkUnits();
|
|
78
|
+ void checkFixedSizeAndEmit();
|
|
79
|
+};
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+// end of fontsizeselector.hpp |
src/ftinspect/widgets/glyphindexselector.cpp
|
1
|
+// glyphindexselector.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "glyphindexselector.hpp"
|
|
6
|
+
|
|
7
|
+#include "../uihelper.hpp"
|
|
8
|
+
|
|
9
|
+#include <climits>
|
|
10
|
+
|
|
11
|
+GlyphIndexSelector::GlyphIndexSelector(QWidget* parent)
|
|
12
|
+: QWidget(parent)
|
|
13
|
+{
|
|
14
|
+ numberRenderer_ = &GlyphIndexSelector::renderNumberDefault;
|
|
15
|
+
|
|
16
|
+ createLayout();
|
|
17
|
+ createConnections();
|
|
18
|
+ showingCount_ = 0;
|
|
19
|
+}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+void
|
|
23
|
+GlyphIndexSelector::setMinMax(int min,
|
|
24
|
+ int max)
|
|
25
|
+{
|
|
26
|
+ // Don't emit events during setting
|
|
27
|
+ auto eventState = blockSignals(true);
|
|
28
|
+ indexSpinBox_->setMinimum(min);
|
|
29
|
+ indexSpinBox_->setMaximum(qBound(0, max, INT_MAX));
|
|
30
|
+ indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
|
|
31
|
+ indexSpinBox_->value(),
|
|
32
|
+ indexSpinBox_->maximum()));
|
|
33
|
+ blockSignals(eventState);
|
|
34
|
+
|
|
35
|
+ updateLabel();
|
|
36
|
+}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+void
|
|
40
|
+GlyphIndexSelector::setShowingCount(int showingCount)
|
|
41
|
+{
|
|
42
|
+ showingCount_ = showingCount;
|
|
43
|
+ updateLabel();
|
|
44
|
+}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+void
|
|
48
|
+GlyphIndexSelector::setSingleMode(bool singleMode)
|
|
49
|
+{
|
|
50
|
+ singleMode_ = singleMode;
|
|
51
|
+ updateLabel();
|
|
52
|
+}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+void
|
|
56
|
+GlyphIndexSelector::setCurrentIndex(int index, bool forceUpdate)
|
|
57
|
+{
|
|
58
|
+ // to avoid unnecessary update, if force update is enabled
|
|
59
|
+ // then the `setValue` shouldn't trigger update signal from `this`
|
|
60
|
+ // but we still need `updateLabel`, so block `this` only
|
|
61
|
+ auto state = blockSignals(forceUpdate);
|
|
62
|
+ indexSpinBox_->setValue(index);
|
|
63
|
+ blockSignals(state);
|
|
64
|
+
|
|
65
|
+ if (forceUpdate)
|
|
66
|
+ emit currentIndexChanged(indexSpinBox_->value());
|
|
67
|
+}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+int
|
|
71
|
+GlyphIndexSelector::currentIndex()
|
|
72
|
+{
|
|
73
|
+ return indexSpinBox_->value();
|
|
74
|
+}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+void
|
|
78
|
+GlyphIndexSelector::setNumberRenderer(std::function<QString(int)> renderer)
|
|
79
|
+{
|
|
80
|
+ numberRenderer_ = std::move(renderer);
|
|
81
|
+}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+void
|
|
85
|
+GlyphIndexSelector::resizeEvent(QResizeEvent* event)
|
|
86
|
+{
|
|
87
|
+ QWidget::resizeEvent(event);
|
|
88
|
+ auto minimumWidth = minimumSizeHint().width();
|
|
89
|
+ if (toEndButton_->isVisible())
|
|
90
|
+ {
|
|
91
|
+ if (width() < minimumWidth)
|
|
92
|
+ navigationWidget_->setVisible(false);
|
|
93
|
+ }
|
|
94
|
+ else if (navigationWidget_->minimumSizeHint().width() + minimumWidth
|
|
95
|
+ <= width())
|
|
96
|
+ navigationWidget_->setVisible(true);
|
|
97
|
+}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+void
|
|
101
|
+GlyphIndexSelector::adjustIndex(int delta)
|
|
102
|
+{
|
|
103
|
+ {
|
|
104
|
+ QSignalBlocker blocker(this);
|
|
105
|
+ indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
|
|
106
|
+ indexSpinBox_->value() + delta,
|
|
107
|
+ indexSpinBox_->maximum()));
|
|
108
|
+ }
|
|
109
|
+ emitValueChanged();
|
|
110
|
+}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+void
|
|
114
|
+GlyphIndexSelector::emitValueChanged()
|
|
115
|
+{
|
|
116
|
+ emit currentIndexChanged(indexSpinBox_->value());
|
|
117
|
+ updateLabel();
|
|
118
|
+}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+void
|
|
122
|
+GlyphIndexSelector::updateLabel()
|
|
123
|
+{
|
|
124
|
+ if (singleMode_)
|
|
125
|
+ indexLabel_->setText(QString("%1\nLimit: %2")
|
|
126
|
+ .arg(numberRenderer_(indexSpinBox_->value()))
|
|
127
|
+ .arg(numberRenderer_(indexSpinBox_->maximum())));
|
|
128
|
+ else
|
|
129
|
+ indexLabel_->setText(
|
|
130
|
+ QString("%1~%2\nLimit: %4")
|
|
131
|
+ .arg(numberRenderer_(indexSpinBox_->value()))
|
|
132
|
+ .arg(numberRenderer_(
|
|
133
|
+ qBound(indexSpinBox_->value(),
|
|
134
|
+ indexSpinBox_->value() + showingCount_ - 1, INT_MAX)))
|
|
135
|
+ .arg(numberRenderer_(indexSpinBox_->maximum())));
|
|
136
|
+}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+void
|
|
140
|
+GlyphIndexSelector::createLayout()
|
|
141
|
+{
|
|
142
|
+ navigationWidget_ = new QWidget(this);
|
|
143
|
+ toStartButton_ = new QPushButton("|<", this);
|
|
144
|
+ toM1000Button_ = new QPushButton("-1000", this);
|
|
145
|
+ toM100Button_ = new QPushButton("-100", this);
|
|
146
|
+ toM10Button_ = new QPushButton("-10", this);
|
|
147
|
+ toM1Button_ = new QPushButton("-1", this);
|
|
148
|
+ toP1Button_ = new QPushButton("+1", this);
|
|
149
|
+ toP10Button_ = new QPushButton("+10", this);
|
|
150
|
+ toP100Button_ = new QPushButton("+100", this);
|
|
151
|
+ toP1000Button_ = new QPushButton("+1000", this);
|
|
152
|
+ toEndButton_ = new QPushButton(">|", this);
|
|
153
|
+
|
|
154
|
+ indexSpinBox_ = new QSpinBox(this);
|
|
155
|
+ indexSpinBox_->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue);
|
|
156
|
+ indexSpinBox_->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
|
157
|
+ indexSpinBox_->setRange(0, 0);
|
|
158
|
+ indexSpinBox_->setFixedWidth(80);
|
|
159
|
+ indexSpinBox_->setWrapping(false);
|
|
160
|
+ indexSpinBox_->setKeyboardTracking(false);
|
|
161
|
+
|
|
162
|
+ indexLabel_ = new QLabel("0\nLimit: 0");
|
|
163
|
+ indexLabel_->setMinimumWidth(200);
|
|
164
|
+
|
|
165
|
+ setButtonNarrowest(toStartButton_);
|
|
166
|
+ setButtonNarrowest(toM1000Button_);
|
|
167
|
+ setButtonNarrowest(toM100Button_);
|
|
168
|
+ setButtonNarrowest(toM10Button_);
|
|
169
|
+ setButtonNarrowest(toM1Button_);
|
|
170
|
+ setButtonNarrowest(toP1Button_);
|
|
171
|
+ setButtonNarrowest(toP10Button_);
|
|
172
|
+ setButtonNarrowest(toP100Button_);
|
|
173
|
+ setButtonNarrowest(toP1000Button_);
|
|
174
|
+ setButtonNarrowest(toEndButton_);
|
|
175
|
+
|
|
176
|
+ // Toltips
|
|
177
|
+ indexSpinBox_->setToolTip("Current glyph index.");
|
|
178
|
+ indexLabel_->setToolTip("Current glyph index/range and the max index.");
|
|
179
|
+
|
|
180
|
+ // Layouting
|
|
181
|
+ navigationLayout_ = new QHBoxLayout;
|
|
182
|
+ navigationLayout_->setSpacing(0);
|
|
183
|
+ navigationLayout_->addWidget(toStartButton_);
|
|
184
|
+ navigationLayout_->addWidget(toM1000Button_);
|
|
185
|
+ navigationLayout_->addWidget(toM100Button_);
|
|
186
|
+ navigationLayout_->addWidget(toM10Button_);
|
|
187
|
+ navigationLayout_->addWidget(toM1Button_);
|
|
188
|
+ navigationLayout_->addWidget(toP1Button_);
|
|
189
|
+ navigationLayout_->addWidget(toP10Button_);
|
|
190
|
+ navigationLayout_->addWidget(toP100Button_);
|
|
191
|
+ navigationLayout_->addWidget(toP1000Button_);
|
|
192
|
+ navigationLayout_->addWidget(toEndButton_);
|
|
193
|
+ navigationWidget_->setLayout(navigationLayout_);
|
|
194
|
+
|
|
195
|
+ layout_ = new QHBoxLayout;
|
|
196
|
+ layout_->setSpacing(0);
|
|
197
|
+ layout_->addStretch(3);
|
|
198
|
+ layout_->addWidget(navigationWidget_);
|
|
199
|
+ layout_->addStretch(1);
|
|
200
|
+ layout_->addWidget(indexSpinBox_);
|
|
201
|
+ layout_->addStretch(1);
|
|
202
|
+ layout_->addWidget(indexLabel_);
|
|
203
|
+ layout_->addStretch(3);
|
|
204
|
+
|
|
205
|
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
206
|
+ setLayout(layout_);
|
|
207
|
+}
|
|
208
|
+
|
|
209
|
+void
|
|
210
|
+GlyphIndexSelector::createConnections()
|
|
211
|
+{
|
|
212
|
+ connect(indexSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
|
|
213
|
+ this, &GlyphIndexSelector::emitValueChanged);
|
|
214
|
+
|
|
215
|
+ glyphNavigationMapper_ = new QSignalMapper(this);
|
|
216
|
+ connect(glyphNavigationMapper_, &QSignalMapper::mappedInt,
|
|
217
|
+ this, &GlyphIndexSelector::adjustIndex);
|
|
218
|
+
|
|
219
|
+ connect(toStartButton_, &QPushButton::clicked,
|
|
220
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
221
|
+ connect(toM1000Button_, &QPushButton::clicked,
|
|
222
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
223
|
+ connect(toM100Button_, &QPushButton::clicked,
|
|
224
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
225
|
+ connect(toM10Button_, &QPushButton::clicked,
|
|
226
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
227
|
+ connect(toM1Button_, &QPushButton::clicked,
|
|
228
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
229
|
+ connect(toP1Button_, &QPushButton::clicked,
|
|
230
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
231
|
+ connect(toP10Button_, &QPushButton::clicked,
|
|
232
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
233
|
+ connect(toP100Button_, &QPushButton::clicked,
|
|
234
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
235
|
+ connect(toP1000Button_, &QPushButton::clicked,
|
|
236
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
237
|
+ connect(toEndButton_, &QPushButton::clicked,
|
|
238
|
+ glyphNavigationMapper_, QOverload<>::of(&QSignalMapper::map));
|
|
239
|
+
|
|
240
|
+ glyphNavigationMapper_->setMapping(toStartButton_, -0x10000);
|
|
241
|
+ glyphNavigationMapper_->setMapping(toM1000Button_, -1000);
|
|
242
|
+ glyphNavigationMapper_->setMapping(toM100Button_, -100);
|
|
243
|
+ glyphNavigationMapper_->setMapping(toM10Button_, -10);
|
|
244
|
+ glyphNavigationMapper_->setMapping(toM1Button_, -1);
|
|
245
|
+ glyphNavigationMapper_->setMapping(toP1Button_, 1);
|
|
246
|
+ glyphNavigationMapper_->setMapping(toP10Button_, 10);
|
|
247
|
+ glyphNavigationMapper_->setMapping(toP100Button_, 100);
|
|
248
|
+ glyphNavigationMapper_->setMapping(toP1000Button_, 1000);
|
|
249
|
+ glyphNavigationMapper_->setMapping(toEndButton_, 0x10000);
|
|
250
|
+}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+QString
|
|
254
|
+GlyphIndexSelector::renderNumberDefault(int i)
|
|
255
|
+{
|
|
256
|
+ return QString::number(i);
|
|
257
|
+}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+// end of glyphindexselector.cpp |
src/ftinspect/widgets/glyphindexselector.hpp
|
1
|
+// glyphindexselector.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include <functional>
|
|
8
|
+#include <QWidget>
|
|
9
|
+#include <QPushButton>
|
|
10
|
+#include <QSpinBox>
|
|
11
|
+#include <QSignalMapper>
|
|
12
|
+#include <QHBoxLayout>
|
|
13
|
+#include <QLabel>
|
|
14
|
+
|
|
15
|
+class GlyphIndexSelector
|
|
16
|
+: public QWidget
|
|
17
|
+{
|
|
18
|
+ Q_OBJECT
|
|
19
|
+public:
|
|
20
|
+ GlyphIndexSelector(QWidget* parent);
|
|
21
|
+ ~GlyphIndexSelector() override = default;
|
|
22
|
+
|
|
23
|
+ // Will never trigger repaint!
|
|
24
|
+ void setMinMax(int min, int max);
|
|
25
|
+ void setShowingCount(int showingCount);
|
|
26
|
+
|
|
27
|
+ // Single mode will display single glyph index instead of a range.
|
|
28
|
+ void setSingleMode(bool singleMode);
|
|
29
|
+
|
|
30
|
+ void setCurrentIndex(int index, bool forceUpdate = false);
|
|
31
|
+ int currentIndex();
|
|
32
|
+
|
|
33
|
+ void setNumberRenderer(std::function<QString(int)> renderer);
|
|
34
|
+
|
|
35
|
+signals:
|
|
36
|
+ void currentIndexChanged(int index);
|
|
37
|
+
|
|
38
|
+protected:
|
|
39
|
+ void resizeEvent(QResizeEvent* event) override;
|
|
40
|
+
|
|
41
|
+private:
|
|
42
|
+ bool singleMode_ = true;
|
|
43
|
+ int showingCount_;
|
|
44
|
+ std::function<QString(int)> numberRenderer_;
|
|
45
|
+
|
|
46
|
+ // min, max and current status are held by `indexSpinBox_`
|
|
47
|
+ QWidget* navigationWidget_;
|
|
48
|
+ QPushButton* toEndButton_;
|
|
49
|
+ QPushButton* toM1000Button_;
|
|
50
|
+ QPushButton* toM100Button_;
|
|
51
|
+ QPushButton* toM10Button_;
|
|
52
|
+ QPushButton* toM1Button_;
|
|
53
|
+ QPushButton* toP1000Button_;
|
|
54
|
+ QPushButton* toP100Button_;
|
|
55
|
+ QPushButton* toP10Button_;
|
|
56
|
+ QPushButton* toP1Button_;
|
|
57
|
+ QPushButton* toStartButton_;
|
|
58
|
+
|
|
59
|
+ QLabel* indexLabel_;
|
|
60
|
+ QSpinBox* indexSpinBox_;
|
|
61
|
+
|
|
62
|
+ QHBoxLayout* navigationLayout_;
|
|
63
|
+ QHBoxLayout* layout_;
|
|
64
|
+
|
|
65
|
+ QSignalMapper* glyphNavigationMapper_;
|
|
66
|
+
|
|
67
|
+ void createLayout();
|
|
68
|
+ void createConnections();
|
|
69
|
+
|
|
70
|
+ void adjustIndex(int delta);
|
|
71
|
+ void emitValueChanged();
|
|
72
|
+ void updateLabel();
|
|
73
|
+
|
|
74
|
+ static QString renderNumberDefault(int i);
|
|
75
|
+};
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+// end of glyphindexselector.hpp |
src/ftinspect/widgets/tripletselector.cpp
|
1
|
+// tripletselector.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "tripletselector.hpp"
|
|
6
|
+
|
|
7
|
+#include "../engine/engine.hpp"
|
|
8
|
+
|
|
9
|
+#include <functional>
|
|
10
|
+
|
|
11
|
+TripletSelector::TripletSelector(QWidget* parent,
|
|
12
|
+ Engine* engine)
|
|
13
|
+: QWidget(parent),
|
|
14
|
+ engine_(engine)
|
|
15
|
+{
|
|
16
|
+ createLayout();
|
|
17
|
+ createConnections();
|
|
18
|
+ checkButtons();
|
|
19
|
+}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+TripletSelector::~TripletSelector()
|
|
23
|
+{
|
|
24
|
+}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+void
|
|
28
|
+TripletSelector::repopulateFonts()
|
|
29
|
+{
|
|
30
|
+ auto oldSize = fontComboBox_->count();
|
|
31
|
+ auto oldIndex = fontComboBox_->currentIndex();
|
|
32
|
+
|
|
33
|
+ {
|
|
34
|
+ QSignalBlocker blk(fontComboBox_);
|
|
35
|
+ QSignalBlocker blk2(faceComboBox_);
|
|
36
|
+ QSignalBlocker blk3(niComboBox_);
|
|
37
|
+ fontComboBox_->clear();
|
|
38
|
+
|
|
39
|
+ auto& ffm = engine_->fontFileManager();
|
|
40
|
+ auto newSize = ffm.size();
|
|
41
|
+ for (int i = 0; i < newSize; i++)
|
|
42
|
+ {
|
|
43
|
+ auto& info = ffm[i];
|
|
44
|
+ auto name = info.filePath();
|
|
45
|
+ auto displayName = info.fileName();
|
|
46
|
+ if (info.isSymbolicLink())
|
|
47
|
+ displayName += " [symlink]";
|
|
48
|
+
|
|
49
|
+ fontComboBox_->addItem(displayName, name);
|
|
50
|
+ }
|
|
51
|
+
|
|
52
|
+ if (newSize > oldSize)
|
|
53
|
+ {
|
|
54
|
+ // if we have new fonts, set the current index to the first new one
|
|
55
|
+ fontComboBox_->setCurrentIndex(oldSize);
|
|
56
|
+ }
|
|
57
|
+ else if (newSize < oldSize)
|
|
58
|
+ {
|
|
59
|
+ if (oldIndex >= newSize)
|
|
60
|
+ oldIndex = newSize - 1;
|
|
61
|
+ if (oldIndex < 0)
|
|
62
|
+ oldIndex = -1;
|
|
63
|
+ fontComboBox_->setCurrentIndex(oldIndex);
|
|
64
|
+ }
|
|
65
|
+
|
|
66
|
+ // Note no signal will be emitted from any combobox until this block ends
|
|
67
|
+ }
|
|
68
|
+
|
|
69
|
+ // This will check buttons & reload the triplet
|
|
70
|
+ repopulateFaces();
|
|
71
|
+}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+void
|
|
75
|
+TripletSelector::repopulateFaces(bool fontSwitched)
|
|
76
|
+{
|
|
77
|
+ // Avoid unnecessary recreating, to reduce interruption of user oper
|
|
78
|
+ auto needToRecreate = fontSwitched;
|
|
79
|
+ auto oldSize = faceComboBox_->count();
|
|
80
|
+
|
|
81
|
+ auto fontIndex = fontComboBox_->currentIndex();
|
|
82
|
+ auto newSize = engine_->numberOfFaces(fontIndex);
|
|
83
|
+
|
|
84
|
+ if (fontIndex < 0 || newSize < 0)
|
|
85
|
+ {
|
|
86
|
+ // Clear and go
|
|
87
|
+ faceComboBox_->clear();
|
|
88
|
+ // This will check buttons & reload the triplet
|
|
89
|
+ repopulateNamedInstances(fontSwitched);
|
|
90
|
+ return;
|
|
91
|
+ }
|
|
92
|
+
|
|
93
|
+ if (newSize != oldSize)
|
|
94
|
+ needToRecreate = true;
|
|
95
|
+
|
|
96
|
+ std::vector<QString> newFaces;
|
|
97
|
+ newFaces.reserve(newSize);
|
|
98
|
+ for (long i = 0; i < newSize; i++)
|
|
99
|
+ {
|
|
100
|
+ newFaces.emplace_back(engine_->namedInstanceName(fontIndex, i, 0));
|
|
101
|
+ if (!needToRecreate && newFaces[i] != faceComboBox_->itemData(i))
|
|
102
|
+ needToRecreate = true;
|
|
103
|
+ }
|
|
104
|
+
|
|
105
|
+ if (!needToRecreate)
|
|
106
|
+ {
|
|
107
|
+ // no need to refersh the combobox
|
|
108
|
+ // This will check buttons & reload the triplet
|
|
109
|
+ repopulateNamedInstances(fontSwitched);
|
|
110
|
+ return;
|
|
111
|
+ }
|
|
112
|
+
|
|
113
|
+ {
|
|
114
|
+ QSignalBlocker blk2(faceComboBox_);
|
|
115
|
+ QSignalBlocker blk3(niComboBox_);
|
|
116
|
+ faceComboBox_->clear();
|
|
117
|
+
|
|
118
|
+ for (long i = 0; i < newSize; i++)
|
|
119
|
+ {
|
|
120
|
+ auto& name = newFaces[i];
|
|
121
|
+ auto displayName = QString("%1: %2").arg(i).arg(name);
|
|
122
|
+ faceComboBox_->addItem(displayName, name);
|
|
123
|
+ }
|
|
124
|
+
|
|
125
|
+ faceComboBox_->setCurrentIndex(0);
|
|
126
|
+ // Note no signal will be emitted from any combobox until this block ends
|
|
127
|
+ }
|
|
128
|
+
|
|
129
|
+ // This will check buttons & reload the triplet
|
|
130
|
+ repopulateNamedInstances(true);
|
|
131
|
+}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+void
|
|
135
|
+TripletSelector::repopulateNamedInstances(bool fontSwitched)
|
|
136
|
+{
|
|
137
|
+ // Avoid unnecessary recreating, to reduce interruption of user oper
|
|
138
|
+ // Similar to `repopulateFaces`
|
|
139
|
+ auto needToRecreate = fontSwitched;
|
|
140
|
+ auto oldSize = niComboBox_->count();
|
|
141
|
+
|
|
142
|
+ auto fontIndex = fontComboBox_->currentIndex();
|
|
143
|
+ auto faceIndex = faceComboBox_->currentIndex();
|
|
144
|
+ auto newSize = engine_->numberOfNamedInstances(fontIndex, faceIndex);
|
|
145
|
+
|
|
146
|
+ if (fontIndex < 0 || faceIndex < 0 || newSize < 0)
|
|
147
|
+ {
|
|
148
|
+ // Clear and go, don't forget checking buttons and loading triplet
|
|
149
|
+ niComboBox_->clear();
|
|
150
|
+ checkButtons();
|
|
151
|
+ loadTriplet();
|
|
152
|
+ return;
|
|
153
|
+ }
|
|
154
|
+
|
|
155
|
+ if (newSize != oldSize)
|
|
156
|
+ needToRecreate = true;
|
|
157
|
+
|
|
158
|
+ std::vector<QString> newFaces;
|
|
159
|
+ newFaces.reserve(newSize);
|
|
160
|
+ for (long i = 0; i < newSize; i++)
|
|
161
|
+ {
|
|
162
|
+ newFaces.emplace_back(engine_->namedInstanceName(fontIndex, faceIndex, i));
|
|
163
|
+ if (!needToRecreate && newFaces[i] != niComboBox_->itemData(i))
|
|
164
|
+ needToRecreate = true;
|
|
165
|
+ }
|
|
166
|
+
|
|
167
|
+ niComboBox_->setEnabled(newSize > 1);
|
|
168
|
+
|
|
169
|
+ if (!needToRecreate)
|
|
170
|
+ {
|
|
171
|
+ // no need to refersh the combobox
|
|
172
|
+ checkButtons();
|
|
173
|
+ loadTriplet();
|
|
174
|
+ return;
|
|
175
|
+ }
|
|
176
|
+
|
|
177
|
+ {
|
|
178
|
+ QSignalBlocker blk3(niComboBox_);
|
|
179
|
+ niComboBox_->clear();
|
|
180
|
+
|
|
181
|
+ for (long i = 0; i < newSize; i++)
|
|
182
|
+ {
|
|
183
|
+ auto& name = newFaces[i];
|
|
184
|
+ auto displayName = QString("%1: %2").arg(i).arg(name);
|
|
185
|
+ if (i == 0)
|
|
186
|
+ displayName = "* " + displayName;
|
|
187
|
+ niComboBox_->addItem(displayName, name);
|
|
188
|
+ }
|
|
189
|
+
|
|
190
|
+ niComboBox_->setCurrentIndex(0);
|
|
191
|
+ // Note no signal will be emitted from any combobox until this block ends
|
|
192
|
+ }
|
|
193
|
+
|
|
194
|
+ checkButtons();
|
|
195
|
+ loadTriplet();
|
|
196
|
+}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+void
|
|
200
|
+TripletSelector::closeCurrentFont()
|
|
201
|
+{
|
|
202
|
+ auto idx = fontComboBox_->currentIndex();
|
|
203
|
+ if (idx < 0)
|
|
204
|
+ return;
|
|
205
|
+ engine_->fontFileManager().remove(idx);
|
|
206
|
+
|
|
207
|
+ // show next font after deletion, i.e., retain index if possible
|
|
208
|
+ int num = engine_->numberOfOpenedFonts();
|
|
209
|
+ if (num)
|
|
210
|
+ {
|
|
211
|
+ if (idx >= num)
|
|
212
|
+ idx = num - 1;
|
|
213
|
+ }
|
|
214
|
+ else
|
|
215
|
+ idx = -1;
|
|
216
|
+
|
|
217
|
+ {
|
|
218
|
+ // Shut up when repopulating
|
|
219
|
+ QSignalBlocker blockerThis(this);
|
|
220
|
+ QSignalBlocker blockerComboBox(fontComboBox_);
|
|
221
|
+ repopulateFonts();
|
|
222
|
+ }
|
|
223
|
+
|
|
224
|
+ if (idx != -1)
|
|
225
|
+ faceComboBox_->setCurrentIndex(idx);
|
|
226
|
+ updateFont();
|
|
227
|
+}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+void
|
|
231
|
+TripletSelector::updateFont()
|
|
232
|
+{
|
|
233
|
+ auto idx = fontComboBox_->currentIndex();
|
|
234
|
+ auto num = engine_->numberOfOpenedFonts();
|
|
235
|
+ if (idx < 0)
|
|
236
|
+ {
|
|
237
|
+ faceComboBox_->clear();
|
|
238
|
+ niComboBox_->clear();
|
|
239
|
+
|
|
240
|
+ checkButtons();
|
|
241
|
+ loadTriplet();
|
|
242
|
+ return;
|
|
243
|
+ }
|
|
244
|
+
|
|
245
|
+ if (num <= 0 || idx >= num)
|
|
246
|
+ {
|
|
247
|
+ // out of sync: this shouldn't happen
|
|
248
|
+ repopulateFonts();
|
|
249
|
+ return;
|
|
250
|
+ }
|
|
251
|
+
|
|
252
|
+ // This will check buttons & reload the triplet
|
|
253
|
+ repopulateFaces();
|
|
254
|
+}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+void
|
|
258
|
+TripletSelector::updateFace()
|
|
259
|
+{
|
|
260
|
+ auto idx = faceComboBox_->currentIndex();
|
|
261
|
+ auto num = engine_->numberOfFaces(fontComboBox_->currentIndex());
|
|
262
|
+
|
|
263
|
+ if (idx >= num)
|
|
264
|
+ {
|
|
265
|
+ // out of sync
|
|
266
|
+ repopulateFaces();
|
|
267
|
+ return;
|
|
268
|
+ }
|
|
269
|
+
|
|
270
|
+ // This will check buttons & reload the triplet
|
|
271
|
+ repopulateNamedInstances();
|
|
272
|
+}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+void
|
|
276
|
+TripletSelector::updateNI()
|
|
277
|
+{
|
|
278
|
+ auto idx = niComboBox_->currentIndex();
|
|
279
|
+ auto num = engine_->numberOfNamedInstances(fontComboBox_->currentIndex(),
|
|
280
|
+ faceComboBox_->currentIndex());
|
|
281
|
+
|
|
282
|
+ if (idx >= num)
|
|
283
|
+ {
|
|
284
|
+ // out of sync
|
|
285
|
+ repopulateNamedInstances();
|
|
286
|
+ return;
|
|
287
|
+ }
|
|
288
|
+
|
|
289
|
+ checkButtons();
|
|
290
|
+ loadTriplet();
|
|
291
|
+}
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+void
|
|
295
|
+TripletSelector::checkButtons()
|
|
296
|
+{
|
|
297
|
+ fontUpButton_->setEnabled(fontComboBox_->currentIndex() > 0);
|
|
298
|
+ fontDownButton_->setEnabled(fontComboBox_->currentIndex()
|
|
299
|
+ < fontComboBox_->count() - 1);
|
|
300
|
+ closeFontButton_->setEnabled(faceComboBox_->currentIndex() >= 0);
|
|
301
|
+
|
|
302
|
+ faceUpButton_->setEnabled(faceComboBox_->currentIndex() > 0);
|
|
303
|
+ faceDownButton_->setEnabled(faceComboBox_->currentIndex()
|
|
304
|
+ < faceComboBox_->count() - 1);
|
|
305
|
+
|
|
306
|
+ niUpButton_->setEnabled(niComboBox_->currentIndex() > 0);
|
|
307
|
+ niDownButton_->setEnabled(niComboBox_->currentIndex()
|
|
308
|
+ < niComboBox_->count() - 1);
|
|
309
|
+}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+void
|
|
313
|
+TripletSelector::watchCurrentFont()
|
|
314
|
+{
|
|
315
|
+ repopulateFaces(false);
|
|
316
|
+}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+void
|
|
320
|
+TripletSelector::createLayout()
|
|
321
|
+{
|
|
322
|
+ fontComboBox_ = new QComboBox(this);
|
|
323
|
+ faceComboBox_ = new QComboBox(this);
|
|
324
|
+ niComboBox_ = new QComboBox(this);
|
|
325
|
+
|
|
326
|
+ fontComboBox_->setPlaceholderText(tr("No font open"));
|
|
327
|
+ faceComboBox_->setPlaceholderText(tr("No face available"));
|
|
328
|
+ niComboBox_->setPlaceholderText(tr("No named instance available"));
|
|
329
|
+
|
|
330
|
+ closeFontButton_ = new QToolButton(this);
|
|
331
|
+ fontUpButton_ = new QToolButton(this);
|
|
332
|
+ faceUpButton_ = new QToolButton(this);
|
|
333
|
+ niUpButton_ = new QToolButton(this);
|
|
334
|
+ fontDownButton_ = new QToolButton(this);
|
|
335
|
+ faceDownButton_ = new QToolButton(this);
|
|
336
|
+ niDownButton_ = new QToolButton(this);
|
|
337
|
+
|
|
338
|
+ closeFontButton_->setText(tr("Close"));
|
|
339
|
+ fontUpButton_ ->setText(tr("\xE2\x86\x91"));
|
|
340
|
+ faceUpButton_ ->setText(tr("\xE2\x86\x91"));
|
|
341
|
+ niUpButton_ ->setText(tr("\xE2\x86\x91"));
|
|
342
|
+ fontDownButton_ ->setText(tr("\xE2\x86\x93"));
|
|
343
|
+ faceDownButton_ ->setText(tr("\xE2\x86\x93"));
|
|
344
|
+ niDownButton_ ->setText(tr("\xE2\x86\x93"));
|
|
345
|
+
|
|
346
|
+ fontComboBox_ ->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
347
|
+ faceComboBox_ ->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
348
|
+ niComboBox_ ->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
349
|
+ closeFontButton_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
|
|
350
|
+ fontUpButton_ ->setFixedSize(30, 30);
|
|
351
|
+ faceUpButton_ ->setFixedSize(30, 30);
|
|
352
|
+ niUpButton_ ->setFixedSize(30, 30);
|
|
353
|
+ fontDownButton_ ->setFixedSize(30, 30);
|
|
354
|
+ faceDownButton_ ->setFixedSize(30, 30);
|
|
355
|
+ niDownButton_ ->setFixedSize(30, 30);
|
|
356
|
+
|
|
357
|
+ // Tooltips
|
|
358
|
+ fontComboBox_->setToolTip(tr("Current font"));
|
|
359
|
+ faceComboBox_->setToolTip(tr("Current subfont (face)"));
|
|
360
|
+ niComboBox_->setToolTip(
|
|
361
|
+ tr("Current named instance (only available for variable fonts)"));
|
|
362
|
+ closeFontButton_->setToolTip(tr("Close current font"));
|
|
363
|
+ fontUpButton_ ->setToolTip(tr("Previous font"));
|
|
364
|
+ faceUpButton_ ->setToolTip(tr("Previous subfont (face)"));
|
|
365
|
+ niUpButton_ ->setToolTip(tr("Previous named instance"));
|
|
366
|
+ fontDownButton_ ->setToolTip(tr("Next font"));
|
|
367
|
+ faceDownButton_ ->setToolTip(tr("Next subfont (face)"));
|
|
368
|
+ niDownButton_ ->setToolTip(tr("Next named instance"));
|
|
369
|
+
|
|
370
|
+ // Layouting
|
|
371
|
+ layout_ = new QHBoxLayout;
|
|
372
|
+ layout_->setSpacing(0);
|
|
373
|
+ layout_->setContentsMargins(0, 0, 0, 0);
|
|
374
|
+
|
|
375
|
+ layout_->addWidget(fontComboBox_);
|
|
376
|
+ layout_->addWidget(fontUpButton_);
|
|
377
|
+ layout_->addWidget(fontDownButton_);
|
|
378
|
+ layout_->addWidget(closeFontButton_);
|
|
379
|
+ layout_->addWidget(faceComboBox_);
|
|
380
|
+ layout_->addWidget(faceUpButton_);
|
|
381
|
+ layout_->addWidget(faceDownButton_);
|
|
382
|
+ layout_->addWidget(niComboBox_);
|
|
383
|
+ layout_->addWidget(niUpButton_);
|
|
384
|
+ layout_->addWidget(niDownButton_);
|
|
385
|
+
|
|
386
|
+ setFixedHeight(30);
|
|
387
|
+ setLayout(layout_);
|
|
388
|
+ layout_->setContentsMargins(0, 0, 0, 0);
|
|
389
|
+}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+void
|
|
393
|
+TripletSelector::createConnections()
|
|
394
|
+{
|
|
395
|
+ connect(fontComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
396
|
+ this, &TripletSelector::updateFont);
|
|
397
|
+ connect(faceComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
398
|
+ this, &TripletSelector::updateFace);
|
|
399
|
+ connect(niComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
400
|
+ this, &TripletSelector::updateNI);
|
|
401
|
+
|
|
402
|
+ connect(closeFontButton_, &QToolButton::clicked,
|
|
403
|
+ this, &TripletSelector::closeCurrentFont);
|
|
404
|
+ connect(fontUpButton_ , &QToolButton::clicked,
|
|
405
|
+ this,
|
|
406
|
+ std::bind(&TripletSelector::previousComboBoxItem, fontComboBox_));
|
|
407
|
+ connect(faceUpButton_ , &QToolButton::clicked,
|
|
408
|
+ this,
|
|
409
|
+ std::bind(&TripletSelector::previousComboBoxItem, faceComboBox_));
|
|
410
|
+ connect(niUpButton_ , &QToolButton::clicked,
|
|
411
|
+ this,
|
|
412
|
+ std::bind(&TripletSelector::previousComboBoxItem, niComboBox_));
|
|
413
|
+ connect(fontDownButton_ , &QToolButton::clicked,
|
|
414
|
+ this,
|
|
415
|
+ std::bind(&TripletSelector::nextComboBoxItem, fontComboBox_));
|
|
416
|
+ connect(faceDownButton_ , &QToolButton::clicked,
|
|
417
|
+ this,
|
|
418
|
+ std::bind(&TripletSelector::nextComboBoxItem, faceComboBox_));
|
|
419
|
+ connect(niDownButton_ , &QToolButton::clicked,
|
|
420
|
+ this,
|
|
421
|
+ std::bind(&TripletSelector::nextComboBoxItem, niComboBox_));
|
|
422
|
+
|
|
423
|
+ connect(&engine_->fontFileManager(), &FontFileManager::currentFileChanged,
|
|
424
|
+ this, &TripletSelector::watchCurrentFont);
|
|
425
|
+}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+void
|
|
429
|
+TripletSelector::loadTriplet()
|
|
430
|
+{
|
|
431
|
+ // we do lazy computation of FT_Face objects
|
|
432
|
+
|
|
433
|
+ // TODO really?
|
|
434
|
+ auto fontIndex = fontComboBox_->currentIndex();
|
|
435
|
+ auto faceIndex = faceComboBox_->currentIndex();
|
|
436
|
+ auto instanceIndex = niComboBox_->currentIndex();
|
|
437
|
+
|
|
438
|
+ if (fontIndex >= 0 && fontIndex < engine_->numberOfOpenedFonts())
|
|
439
|
+ {
|
|
440
|
+ QFileInfo& fileInfo = engine_->fontFileManager()[fontIndex];
|
|
441
|
+ engine_->fontFileManager().updateWatching(fontIndex);
|
|
442
|
+
|
|
443
|
+ if (!fileInfo.exists())
|
|
444
|
+ {
|
|
445
|
+ // On Unix-like systems, the symlink's target gets opened; this
|
|
446
|
+ // implies that deletion of a symlink doesn't make `engine->loadFont'
|
|
447
|
+ // fail since it operates on a file handle pointing to the target.
|
|
448
|
+ // For this reason, we remove the font to enforce a reload.
|
|
449
|
+ engine_->removeFont(fontIndex, false);
|
|
450
|
+ }
|
|
451
|
+ }
|
|
452
|
+
|
|
453
|
+ engine_->loadFont(fontIndex, faceIndex, instanceIndex);
|
|
454
|
+
|
|
455
|
+ // TODO: This may messes up with bitmap-only fonts.
|
|
456
|
+ if (!engine_->fontValid())
|
|
457
|
+ {
|
|
458
|
+ // there might be various reasons why the current
|
|
459
|
+ // (file, face, instance) triplet is invalid or missing;
|
|
460
|
+ // we thus start our timer to periodically test
|
|
461
|
+ // whether the font starts working
|
|
462
|
+ if (faceIndex >= 0 && faceIndex < engine_->numberOfOpenedFonts())
|
|
463
|
+ engine_->fontFileManager().timerStart();
|
|
464
|
+ }
|
|
465
|
+
|
|
466
|
+ emit tripletChanged();
|
|
467
|
+}
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+void
|
|
471
|
+TripletSelector::nextComboBoxItem(QComboBox* c)
|
|
472
|
+{
|
|
473
|
+ if (c->currentIndex() < 0 || c->currentIndex() >= c->count() - 1)
|
|
474
|
+ return;
|
|
475
|
+ // No need to handle further steps, the event handler will take care of these
|
|
476
|
+ c->setCurrentIndex(c->currentIndex() + 1);
|
|
477
|
+}
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+void
|
|
481
|
+TripletSelector::previousComboBoxItem(QComboBox* c)
|
|
482
|
+{
|
|
483
|
+ if (c->currentIndex() <= 0)
|
|
484
|
+ return;
|
|
485
|
+ // No need to handle further steps, the event handler will take care of these
|
|
486
|
+ c->setCurrentIndex(c->currentIndex() - 1);
|
|
487
|
+}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+// end of tripletselector.cpp |
src/ftinspect/widgets/tripletselector.hpp
|
1
|
+// QPushButton.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include <vector>
|
|
8
|
+#include <QWidget>
|
|
9
|
+#include <QComboBox>
|
|
10
|
+#include <QPushButton>
|
|
11
|
+#include <QToolButton>
|
|
12
|
+#include <QBoxLayout>
|
|
13
|
+
|
|
14
|
+class Engine;
|
|
15
|
+class TripletSelector
|
|
16
|
+: public QWidget
|
|
17
|
+{
|
|
18
|
+ Q_OBJECT
|
|
19
|
+
|
|
20
|
+public:
|
|
21
|
+ TripletSelector(QWidget* parent,
|
|
22
|
+ Engine* engine);
|
|
23
|
+ ~TripletSelector() override;
|
|
24
|
+
|
|
25
|
+ void repopulateFonts();
|
|
26
|
+ void closeCurrentFont();
|
|
27
|
+
|
|
28
|
+signals:
|
|
29
|
+ void tripletChanged();
|
|
30
|
+
|
|
31
|
+private:
|
|
32
|
+ Engine* engine_;
|
|
33
|
+
|
|
34
|
+ QComboBox* fontComboBox_;
|
|
35
|
+ QComboBox* faceComboBox_;
|
|
36
|
+ QComboBox* niComboBox_;
|
|
37
|
+
|
|
38
|
+ QToolButton* closeFontButton_;
|
|
39
|
+
|
|
40
|
+ QToolButton* fontUpButton_;
|
|
41
|
+ QToolButton* fontDownButton_;
|
|
42
|
+ QToolButton* faceUpButton_;
|
|
43
|
+ QToolButton* faceDownButton_;
|
|
44
|
+ QToolButton* niUpButton_;
|
|
45
|
+ QToolButton* niDownButton_;
|
|
46
|
+
|
|
47
|
+ QHBoxLayout* layout_;
|
|
48
|
+
|
|
49
|
+ void checkButtons();
|
|
50
|
+ void watchCurrentFont();
|
|
51
|
+
|
|
52
|
+ void createLayout();
|
|
53
|
+ void createConnections();
|
|
54
|
+
|
|
55
|
+ void repopulateFaces(bool fontSwitched = true);
|
|
56
|
+ void repopulateNamedInstances(bool fontSwitched = true);
|
|
57
|
+ void updateFont();
|
|
58
|
+ void updateFace();
|
|
59
|
+ void updateNI();
|
|
60
|
+ void loadTriplet();
|
|
61
|
+
|
|
62
|
+ static void nextComboBoxItem(QComboBox* c);
|
|
63
|
+ static void previousComboBoxItem(QComboBox* c);
|
|
64
|
+};
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+// end of QPushButton.hpp |
|