Commits:
-
b1746bda
by Charlie Jiang
at 2022-07-10T15:06:02+08:00
[ftinspect] WIP: Add initial support to "Continuous View".
This commit adds a tab to the main GUI named "Continuous View",
corresponding to the `ftview` tool. Inside the tab is a glyph canvas and
another tab view. The tab view is used to switch modes and input settings..
The rendering canvas is *not* a `QGraphicsView`, but a simple `QWidget` with
a custom `paintEvent`. This is because we don't need features like panning
and zooming, so using `QGraphicsView` will only add complexity (e.g.
transforming coordination systems).
Code translating FreeType outline to the origin for rendering is extracted
as a helper function in a new "render utils" file.
The font size selector (size, unit, DPI) in the original `SingularTab` is
extracted as a new class `FontSizeSelector`.
The "graphics defaults", which are graphics objects like color palettes and
`QPen`s, are all extracted into a new singleton struct `GraphicsDefaults`,
so every component can easily obtain them.
Currently there're a lot of settings, status variables and parameters during
the rendering process. Will write some documentation clearifing them all.
WIP: Only "all glyphs" view is implemented.
TODO: Fancy, Stroked, Text String mode; LCD Anti-Aliasing; Bitmap font;
The rendering code is adopted from singular rendering, which is different
from the code in `ftcommon.c` from the legacy tools. In the future we may
need to re-implement this with code from the latter.
* src/ftinspect/rendering/glyphcontinuous.cpp,
src/ftinspect/rendering/glyphcontinuous.hpp: New file: the actual
"rendering canvas" in the "Continuous View". The code was a combination of
`ftcommon` and the singular view's render views. Mouse scrolling is
supported for navigation and resizing.
* src/ftinspect/panels/continuous.cpp, src/ftinspect/panels/continuous.hpp:
New files: the content of the "Continuous View" tab is a `ContinuousTab`.
It current only support one single mode and submode.
Settings about "All Glyphs" mode is placed in `ContinousAllGlyphsTab`.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp: Add
CharMap handling code, especially `CharMapInfo` struct. `CharMapInfo` can
provide the name and max index for the charmap. Vector of `CharMapInfo`
for current font is obtained via `Engine::currentFontCharMaps`.
CharMap mapping is done via `CharMapInfo::glyphIndexFromCharCode`, with
the added `FTC_CMapCache`.
Add more actions to support continuous view. `reloadFont` and
`loadGlyphWithoutUpdate` can load fonts and glyphs with minimal overhead.
`loadGlyphWithoutUpdate` uses `FTC_ImageCache_Lookup`, so the
`FTC_ImageTypeRec` object is cached.
* src/ftinspect/widgets/fontsizeselector.cpp,
src/ftinspect/widgets/fontsizeselector.hpp: New file, include the font
size setting part. It can directly apply settings to the engine via
`applyToEngine` function, and can handle wheel event from event object or
by steps count.
* src/ftinspect/widgets/glyphindexselector.cpp,
src/ftinspect/widgets/glyphindexselector.hpp: Combine `setMax` and
`setMin` functions, and reduce unnecessary signals by blocking them.
Rename `getCurrentIndex` to `currentIndex`.
Support custom number rendering (for Unicode charcode formatting). Adjust
layout and disabled wrapping on the spinbox.
* src/ftinspect/rendering/graphicsdefault.cpp,
src/ftinspect/rendering/graphicsdefault.hpp: New file, including graphics
initializing code.
* src/ftinspect/rendering/renderutils.cpp,
src/ftinspect/rendering/renderutils.hpp: New file, including outline
translating code.
* src/ftinspect/rendering/glyphbitmap.cpp: Move out outline tranlating code.
* src/ftinspect/panels/singular.cpp, src/ftinspect/panels/singular.hpp: Move
out the font size selector and graphics defaults. Minor change due to
change in `GlyphIndexSelector`.
* src/ftinspect/maingui.cpp, src/ftinspect/maingui.hpp: Add "Continuous
View" to tab view.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
21 changed files:
Changes:
src/ftinspect/CMakeLists.txt
... |
... |
@@ -27,15 +27,20 @@ add_executable(ftinspect |
27
|
27
|
"rendering/glyphoutline.cpp"
|
28
|
28
|
"rendering/glyphpointnumbers.cpp"
|
29
|
29
|
"rendering/glyphpoints.cpp"
|
|
30
|
+ "rendering/glyphcontinuous.cpp"
|
30
|
31
|
"rendering/grid.cpp"
|
|
32
|
+ "rendering/graphicsdefault.cpp"
|
|
33
|
+ "rendering/renderutils.cpp"
|
31
|
34
|
|
32
|
35
|
"widgets/customwidgets.cpp"
|
33
|
36
|
"widgets/glyphindexselector.cpp"
|
|
37
|
+ "widgets/fontsizeselector.cpp"
|
34
|
38
|
|
35
|
39
|
"models/ttsettingscomboboxmodel.cpp"
|
36
|
40
|
|
37
|
41
|
"panels/settingpanel.cpp"
|
38
|
42
|
"panels/singular.cpp"
|
|
43
|
+ "panels/continuous.cpp"
|
39
|
44
|
)
|
40
|
45
|
target_link_libraries(ftinspect
|
41
|
46
|
Qt5::Core Qt5::Widgets
|
src/ftinspect/engine/engine.cpp
... |
... |
@@ -148,6 +148,12 @@ Engine::Engine() |
148
|
148
|
// XXX error handling
|
149
|
149
|
}
|
150
|
150
|
|
|
151
|
+ error = FTC_CMapCache_New(cacheManager_, &cmapCache_);
|
|
152
|
+ if (error)
|
|
153
|
+ {
|
|
154
|
+ // XXX error handling
|
|
155
|
+ }
|
|
156
|
+
|
151
|
157
|
queryEngine();
|
152
|
158
|
}
|
153
|
159
|
|
... |
... |
@@ -278,6 +284,8 @@ Engine::loadFont(int fontIndex, |
278
|
284
|
}
|
279
|
285
|
}
|
280
|
286
|
|
|
287
|
+ imageType_.face_id = scaler_.face_id;
|
|
288
|
+
|
281
|
289
|
if (numGlyphs < 0)
|
282
|
290
|
{
|
283
|
291
|
ftSize_ = NULL;
|
... |
... |
@@ -286,16 +294,22 @@ Engine::loadFont(int fontIndex, |
286
|
294
|
}
|
287
|
295
|
else
|
288
|
296
|
{
|
289
|
|
- curFamilyName_ = QString(ftSize_->face->family_name);
|
290
|
|
- curStyleName_ = QString(ftSize_->face->style_name);
|
|
297
|
+ auto face = ftSize_->face;
|
|
298
|
+ curFamilyName_ = QString(face->family_name);
|
|
299
|
+ curStyleName_ = QString(face->style_name);
|
291
|
300
|
|
292
|
|
- const char* moduleName = FT_FACE_DRIVER_NAME( ftSize_->face );
|
|
301
|
+ const char* moduleName = FT_FACE_DRIVER_NAME( face );
|
293
|
302
|
|
294
|
303
|
// XXX cover all available modules
|
295
|
304
|
if (!strcmp(moduleName, "cff"))
|
296
|
305
|
fontType_ = FontType_CFF;
|
297
|
306
|
else if (!strcmp(moduleName, "truetype"))
|
298
|
307
|
fontType_ = FontType_TrueType;
|
|
308
|
+
|
|
309
|
+ curCharMaps_.clear();
|
|
310
|
+ curCharMaps_.reserve(face->num_charmaps);
|
|
311
|
+ for (int i = 0; i < face->num_charmaps; i++)
|
|
312
|
+ curCharMaps_.append(CharMapInfo(i, face->charmaps[i]));
|
299
|
313
|
}
|
300
|
314
|
|
301
|
315
|
curNumGlyphs_ = numGlyphs;
|
... |
... |
@@ -303,6 +317,17 @@ Engine::loadFont(int fontIndex, |
303
|
317
|
}
|
304
|
318
|
|
305
|
319
|
|
|
320
|
+void
|
|
321
|
+Engine::reloadFont()
|
|
322
|
+{
|
|
323
|
+ update();
|
|
324
|
+ if (!scaler_.face_id)
|
|
325
|
+ return;
|
|
326
|
+ imageType_.face_id = scaler_.face_id;
|
|
327
|
+ FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_);
|
|
328
|
+}
|
|
329
|
+
|
|
330
|
+
|
306
|
331
|
void
|
307
|
332
|
Engine::removeFont(int fontIndex, bool closeFile)
|
308
|
333
|
{
|
... |
... |
@@ -331,6 +356,22 @@ Engine::removeFont(int fontIndex, bool closeFile) |
331
|
356
|
}
|
332
|
357
|
|
333
|
358
|
|
|
359
|
+unsigned
|
|
360
|
+Engine::glyphIndexFromCharCode(int code, int charMapIndex)
|
|
361
|
+{
|
|
362
|
+ if (charMapIndex == -1)
|
|
363
|
+ return code;
|
|
364
|
+ return FTC_CMapCache_Lookup(cmapCache_, scaler_.face_id, charMapIndex, code);
|
|
365
|
+}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+FT_Size_Metrics const&
|
|
369
|
+Engine::currentFontMetrics()
|
|
370
|
+{
|
|
371
|
+ return ftSize_->metrics;
|
|
372
|
+}
|
|
373
|
+
|
|
374
|
+
|
334
|
375
|
QString
|
335
|
376
|
Engine::glyphName(int index)
|
336
|
377
|
{
|
... |
... |
@@ -390,6 +431,26 @@ Engine::loadOutline(int glyphIndex) |
390
|
431
|
}
|
391
|
432
|
|
392
|
433
|
|
|
434
|
+FT_Glyph
|
|
435
|
+Engine::loadGlyphWithoutUpdate(int glyphIndex)
|
|
436
|
+{
|
|
437
|
+ // TODO bitmap fonts? color layered fonts?
|
|
438
|
+ FT_Glyph glyph;
|
|
439
|
+ imageType_.flags |= FT_LOAD_NO_BITMAP;
|
|
440
|
+ if (FTC_ImageCache_Lookup(imageCache_,
|
|
441
|
+ &imageType_,
|
|
442
|
+ glyphIndex,
|
|
443
|
+ &glyph,
|
|
444
|
+ NULL))
|
|
445
|
+ {
|
|
446
|
+ // XXX error handling?
|
|
447
|
+ return NULL;
|
|
448
|
+ }
|
|
449
|
+
|
|
450
|
+ return glyph;
|
|
451
|
+}
|
|
452
|
+
|
|
453
|
+
|
393
|
454
|
int
|
394
|
455
|
Engine::numberOfOpenedFonts()
|
395
|
456
|
{
|
... |
... |
@@ -500,6 +561,10 @@ Engine::update() |
500
|
561
|
scaler_.x_res = dpi_;
|
501
|
562
|
scaler_.y_res = dpi_;
|
502
|
563
|
}
|
|
564
|
+
|
|
565
|
+ imageType_.width = static_cast<unsigned int>(pixelSize_);
|
|
566
|
+ imageType_.height = static_cast<unsigned int>(pixelSize_);
|
|
567
|
+ imageType_.flags = static_cast<int>(loadFlags_);
|
503
|
568
|
}
|
504
|
569
|
|
505
|
570
|
|
... |
... |
@@ -602,4 +667,136 @@ Engine::queryEngine() |
602
|
667
|
}
|
603
|
668
|
}
|
604
|
669
|
|
|
670
|
+
|
|
671
|
+QHash<FT_Encoding, QString> encodingNamesCache;
|
|
672
|
+QHash<FT_Encoding, QString>&
|
|
673
|
+encodingNames()
|
|
674
|
+{
|
|
675
|
+ if (encodingNamesCache.empty())
|
|
676
|
+ {
|
|
677
|
+ encodingNamesCache[static_cast<FT_Encoding>(FT_ENCODING_OTHER)]
|
|
678
|
+ = "Unknown Encoding";
|
|
679
|
+ encodingNamesCache[FT_ENCODING_NONE] = "No Encoding";
|
|
680
|
+ encodingNamesCache[FT_ENCODING_MS_SYMBOL] = "MS Symbol (symb)";
|
|
681
|
+ encodingNamesCache[FT_ENCODING_UNICODE] = "Unicode (unic)";
|
|
682
|
+ encodingNamesCache[FT_ENCODING_SJIS] = "Shift JIS (sjis)";
|
|
683
|
+ encodingNamesCache[FT_ENCODING_PRC] = "PRC/GB 18030 (gb)";
|
|
684
|
+ encodingNamesCache[FT_ENCODING_BIG5] = "Big5 (big5)";
|
|
685
|
+ encodingNamesCache[FT_ENCODING_WANSUNG] = "Wansung (wans)";
|
|
686
|
+ encodingNamesCache[FT_ENCODING_JOHAB] = "Johab (joha)";
|
|
687
|
+ encodingNamesCache[FT_ENCODING_ADOBE_STANDARD] = "Adobe Standard (ADOB)";
|
|
688
|
+ encodingNamesCache[FT_ENCODING_ADOBE_EXPERT] = "Adobe Expert (ADBE)";
|
|
689
|
+ encodingNamesCache[FT_ENCODING_ADOBE_CUSTOM] = "Adobe Custom (ADBC)";
|
|
690
|
+ encodingNamesCache[FT_ENCODING_ADOBE_LATIN_1] = "Latin 1 (lat1)";
|
|
691
|
+ encodingNamesCache[FT_ENCODING_OLD_LATIN_2] = "Latin 2 (lat2)";
|
|
692
|
+ encodingNamesCache[FT_ENCODING_APPLE_ROMAN] = "Apple Roman (armn)";
|
|
693
|
+ }
|
|
694
|
+
|
|
695
|
+ return encodingNamesCache;
|
|
696
|
+}
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+CharMapInfo::CharMapInfo(int index, FT_CharMap cmap)
|
|
700
|
+: index(index), ptr(cmap), encoding(cmap->encoding), maxIndex(-1)
|
|
701
|
+{
|
|
702
|
+ auto& names = encodingNames();
|
|
703
|
+ auto it = names.find(encoding);
|
|
704
|
+ if (it == names.end())
|
|
705
|
+ encodingName = &names[static_cast<FT_Encoding>(FT_ENCODING_OTHER)];
|
|
706
|
+ else
|
|
707
|
+ encodingName = &it.value();
|
|
708
|
+
|
|
709
|
+ if (encoding != FT_ENCODING_OTHER)
|
|
710
|
+ maxIndex = computeMaxIndex();
|
|
711
|
+}
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+QString
|
|
715
|
+CharMapInfo::stringifyIndex(int code, int index)
|
|
716
|
+{
|
|
717
|
+ return QString("CharCode: %1 (glyph idx %2)")
|
|
718
|
+ .arg(stringifyIndexShort(code))
|
|
719
|
+ .arg(index);
|
|
720
|
+}
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+QString
|
|
724
|
+CharMapInfo::stringifyIndexShort(int code)
|
|
725
|
+{
|
|
726
|
+ return (encoding == FT_ENCODING_UNICODE ? "U+" : "0x")
|
|
727
|
+ + QString::number(code, 16).rightJustified(4, '0').toUpper();
|
|
728
|
+}
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+int
|
|
732
|
+CharMapInfo::computeMaxIndex()
|
|
733
|
+{
|
|
734
|
+ int maxIndex = 0;
|
|
735
|
+ switch (encoding)
|
|
736
|
+ {
|
|
737
|
+ case FT_ENCODING_UNICODE:
|
|
738
|
+ maxIndex = maxIndexForFaceAndCharMap(ptr, 0x110000) + 1;
|
|
739
|
+ break;
|
|
740
|
+
|
|
741
|
+ case FT_ENCODING_ADOBE_LATIN_1:
|
|
742
|
+ case FT_ENCODING_ADOBE_STANDARD:
|
|
743
|
+ case FT_ENCODING_ADOBE_EXPERT:
|
|
744
|
+ case FT_ENCODING_ADOBE_CUSTOM:
|
|
745
|
+ case FT_ENCODING_APPLE_ROMAN:
|
|
746
|
+ maxIndex = 0x100;
|
|
747
|
+ break;
|
|
748
|
+
|
|
749
|
+ /* some fonts use range 0x00-0x100, others have 0xF000-0xF0FF */
|
|
750
|
+ case FT_ENCODING_MS_SYMBOL:
|
|
751
|
+ maxIndex = maxIndexForFaceAndCharMap(ptr, 0x10000) + 1;
|
|
752
|
+ break;
|
|
753
|
+
|
|
754
|
+ default:
|
|
755
|
+ // Some encodings can reach > 0x10000, e.g. GB 18030.
|
|
756
|
+ maxIndex = maxIndexForFaceAndCharMap(ptr, 0x110000) + 1;
|
|
757
|
+ }
|
|
758
|
+ return maxIndex;
|
|
759
|
+}
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+int
|
|
763
|
+CharMapInfo::maxIndexForFaceAndCharMap(FT_CharMap charMap,
|
|
764
|
+ unsigned max)
|
|
765
|
+{
|
|
766
|
+ // code adopted from `ftcommon.c`
|
|
767
|
+ FT_ULong min = 0;
|
|
768
|
+ FT_UInt glyphIndex;
|
|
769
|
+ FT_Face face = charMap->face;
|
|
770
|
+
|
|
771
|
+ if (FT_Set_Charmap(face, charMap))
|
|
772
|
+ return -1;
|
|
773
|
+
|
|
774
|
+ do
|
|
775
|
+ {
|
|
776
|
+ FT_ULong mid = (min + max) >> 1;
|
|
777
|
+ FT_ULong res = FT_Get_Next_Char(face, mid, &glyphIndex);
|
|
778
|
+
|
|
779
|
+ if (glyphIndex)
|
|
780
|
+ min = res;
|
|
781
|
+ else
|
|
782
|
+ {
|
|
783
|
+ max = mid;
|
|
784
|
+
|
|
785
|
+ // once moved, it helps to advance min through sparse regions
|
|
786
|
+ if (min)
|
|
787
|
+ {
|
|
788
|
+ res = FT_Get_Next_Char(face, min, &glyphIndex);
|
|
789
|
+
|
|
790
|
+ if (glyphIndex)
|
|
791
|
+ min = res;
|
|
792
|
+ else
|
|
793
|
+ max = min; // found it
|
|
794
|
+ }
|
|
795
|
+ }
|
|
796
|
+ } while (max > min);
|
|
797
|
+
|
|
798
|
+ return static_cast<int>(max);
|
|
799
|
+}
|
|
800
|
+
|
|
801
|
+
|
605
|
802
|
// end of engine.cpp |
src/ftinspect/engine/engine.hpp
... |
... |
@@ -36,6 +36,29 @@ struct FaceID |
36
|
36
|
bool operator<(const FaceID& other) const;
|
37
|
37
|
};
|
38
|
38
|
|
|
39
|
+class Engine;
|
|
40
|
+
|
|
41
|
+#define FT_ENCODING_OTHER 0xFFFE
|
|
42
|
+struct CharMapInfo
|
|
43
|
+{
|
|
44
|
+ int index;
|
|
45
|
+ FT_CharMap ptr;
|
|
46
|
+ FT_Encoding encoding;
|
|
47
|
+ QString* encodingName;
|
|
48
|
+
|
|
49
|
+ // Actually this shouldn't go here, but for convenience...
|
|
50
|
+ int maxIndex;
|
|
51
|
+
|
|
52
|
+ CharMapInfo(int index, FT_CharMap cmap);
|
|
53
|
+
|
|
54
|
+ QString stringifyIndex(int code, int index);
|
|
55
|
+ QString stringifyIndexShort(int code);
|
|
56
|
+
|
|
57
|
+private:
|
|
58
|
+ int computeMaxIndex();
|
|
59
|
+ static int maxIndexForFaceAndCharMap(FT_CharMap charMap, unsigned max);
|
|
60
|
+};
|
|
61
|
+
|
39
|
62
|
// FreeType specific data.
|
40
|
63
|
|
41
|
64
|
class Engine
|
... |
... |
@@ -70,6 +93,13 @@ public: |
70
|
93
|
int namedInstanceIndex); // return number of glyphs
|
71
|
94
|
FT_Outline* loadOutline(int glyphIndex);
|
72
|
95
|
|
|
96
|
+ // Sometimes the engine is already updated, and we want to be faster
|
|
97
|
+ FT_Glyph loadGlyphWithoutUpdate(int glyphIndex);
|
|
98
|
+
|
|
99
|
+ // reload current triplet, but with updated settings, useful for updating
|
|
100
|
+ // `ftSize_` only
|
|
101
|
+ void reloadFont();
|
|
102
|
+
|
73
|
103
|
void openFonts(QStringList fontFileNames);
|
74
|
104
|
void removeFont(int fontIndex, bool closeFile = true);
|
75
|
105
|
|
... |
... |
@@ -87,14 +117,16 @@ public: |
87
|
117
|
long numberOfFaces(int fontIndex);
|
88
|
118
|
int numberOfNamedInstances(int fontIndex,
|
89
|
119
|
long faceIndex);
|
|
120
|
+ // Note: the current font face must be properly set
|
|
121
|
+ unsigned glyphIndexFromCharCode(int code, int charMapIndex);
|
|
122
|
+ FT_Size_Metrics const& currentFontMetrics();
|
90
|
123
|
|
91
|
|
- // XXX We should prepend '_' to all private member variable so we can create
|
92
|
|
- // getter without naming conflict... e.g. var named _fontFileManager while
|
93
|
|
- // getter named fontFileManager
|
|
124
|
+ QVector<CharMapInfo>& currentFontCharMaps() { return curCharMaps_; }
|
94
|
125
|
FontFileManager& fontFileManager() { return fontFileManager_; }
|
95
|
126
|
EngineDefaultValues& engineDefaults() { return engineDefaults_; }
|
96
|
127
|
bool antiAliasingEnabled() { return antiAliasingEnabled_; }
|
97
|
128
|
|
|
129
|
+
|
98
|
130
|
//////// Setters (direct or indirect)
|
99
|
131
|
|
100
|
132
|
void setDPI(int d) { dpi_ = d; }
|
... |
... |
@@ -142,14 +174,17 @@ private: |
142
|
174
|
QString curFamilyName_;
|
143
|
175
|
QString curStyleName_;
|
144
|
176
|
int curNumGlyphs_ = -1;
|
|
177
|
+ QVector<CharMapInfo> curCharMaps_;
|
145
|
178
|
|
146
|
179
|
FT_Library library_;
|
147
|
180
|
FTC_Manager cacheManager_;
|
148
|
181
|
FTC_ImageCache imageCache_;
|
149
|
182
|
FTC_SBitCache sbitsCache_;
|
|
183
|
+ FTC_CMapCache cmapCache_;
|
150
|
184
|
|
151
|
185
|
FTC_ScalerRec scaler_;
|
152
|
186
|
FT_Size ftSize_;
|
|
187
|
+ FTC_ImageTypeRec imageType_;
|
153
|
188
|
|
154
|
189
|
EngineDefaultValues engineDefaults_;
|
155
|
190
|
|
src/ftinspect/maingui.cpp
... |
... |
@@ -15,6 +15,8 @@ |
15
|
15
|
|
16
|
16
|
#include <freetype/ftdriver.h>
|
17
|
17
|
|
|
18
|
+#include "panels/continuous.hpp"
|
|
19
|
+
|
18
|
20
|
|
19
|
21
|
MainGUI::MainGUI(Engine* engine)
|
20
|
22
|
: engine_(engine)
|
... |
... |
@@ -448,12 +450,15 @@ MainGUI::createLayout() |
448
|
450
|
fontNameLabel_ = new QLabel(this);
|
449
|
451
|
|
450
|
452
|
singularTab_ = new SingularTab(this, engine_);
|
|
453
|
+ continuousTab_ = new ContinuousTab(this, engine_);
|
451
|
454
|
|
452
|
455
|
tabWidget_ = new QTabWidget(this);
|
453
|
456
|
|
454
|
457
|
// Note those two list must be in sync
|
455
|
458
|
tabs_.append(singularTab_);
|
456
|
459
|
tabWidget_->addTab(singularTab_, tr("Singular Grid View"));
|
|
460
|
+ tabs_.append(continuousTab_);
|
|
461
|
+ tabWidget_->addTab(continuousTab_, tr("Continuous View"));
|
457
|
462
|
|
458
|
463
|
previousFontButton_ = new QPushButton(tr("Previous Font"), this);
|
459
|
464
|
nextFontButton_ = new QPushButton(tr("Next Font"), this);
|
... |
... |
@@ -504,6 +509,9 @@ MainGUI::createConnections() |
504
|
509
|
connect(settingPanel_, &SettingPanel::repaintNeeded,
|
505
|
510
|
this, &MainGUI::repaintCurrentTab);
|
506
|
511
|
|
|
512
|
+ connect(tabWidget_, &QTabWidget::currentChanged,
|
|
513
|
+ this, &MainGUI::reloadCurrentTabFont);
|
|
514
|
+
|
507
|
515
|
connect(previousFontButton_, &QPushButton::clicked,
|
508
|
516
|
this, &MainGUI::previousFont);
|
509
|
517
|
connect(nextFontButton_, &QPushButton::clicked,
|
src/ftinspect/maingui.hpp
... |
... |
@@ -11,6 +11,7 @@ |
11
|
11
|
#include "models/ttsettingscomboboxmodel.hpp"
|
12
|
12
|
#include "panels/settingpanel.hpp"
|
13
|
13
|
#include "panels/singular.hpp"
|
|
14
|
+#include "panels/continuous.hpp"
|
14
|
15
|
|
15
|
16
|
#include <QAction>
|
16
|
17
|
#include <QCheckBox>
|
... |
... |
@@ -136,6 +137,7 @@ private: |
136
|
137
|
QTabWidget* tabWidget_;
|
137
|
138
|
QVector<AbstractTab*> tabs_;
|
138
|
139
|
SingularTab* singularTab_;
|
|
140
|
+ ContinuousTab* continuousTab_;
|
139
|
141
|
|
140
|
142
|
void openFonts(QStringList const& fileNames);
|
141
|
143
|
|
src/ftinspect/meson.build
... |
... |
@@ -27,14 +27,19 @@ if qt5_dep.found() |
27
|
27
|
'rendering/glyphoutline.cpp',
|
28
|
28
|
'rendering/glyphpointnumbers.cpp',
|
29
|
29
|
'rendering/glyphpoints.cpp',
|
|
30
|
+ 'rendering/glyphcontinuous.cpp',
|
30
|
31
|
'rendering/grid.cpp',
|
|
32
|
+ 'rendering/graphicsdefault.cpp',
|
|
33
|
+ 'rendering/renderutils.cpp',
|
31
|
34
|
'widgets/customwidgets.cpp',
|
32
|
35
|
'widgets/glyphindexselector.cpp',
|
|
36
|
+ 'widgets/fontsizeselector.cpp',
|
33
|
37
|
|
34
|
38
|
'models/ttsettingscomboboxmodel.cpp',
|
35
|
39
|
|
36
|
40
|
'panels/settingpanel.cpp',
|
37
|
41
|
'panels/singular.cpp',
|
|
42
|
+ 'panels/continuous.cpp',
|
38
|
43
|
|
39
|
44
|
'ftinspect.cpp',
|
40
|
45
|
'maingui.cpp',
|
... |
... |
@@ -46,9 +51,12 @@ if qt5_dep.found() |
46
|
51
|
'engine/fontfilemanager.hpp',
|
47
|
52
|
'widgets/customwidgets.hpp',
|
48
|
53
|
'widgets/glyphindexselector.hpp',
|
|
54
|
+ 'widgets/fontsizeselector.hpp',
|
|
55
|
+ 'widgets/glyphcontinuous.hpp',
|
49
|
56
|
'models/ttsettingscomboboxmodel.hpp',
|
50
|
57
|
'panels/settingpanel.hpp',
|
51
|
58
|
'panels/singular.hpp',
|
|
59
|
+ 'panels/continuous.hpp',
|
52
|
60
|
'maingui.hpp',
|
53
|
61
|
],
|
54
|
62
|
dependencies: qt5_dep)
|
src/ftinspect/panels/continuous.cpp
|
1
|
+// continuous.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "continuous.hpp"
|
|
6
|
+
|
|
7
|
+#include <climits>
|
|
8
|
+#include <QVariant>
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+ContinuousTab::ContinuousTab(QWidget* parent,
|
|
12
|
+ Engine* engine)
|
|
13
|
+: QWidget(parent), engine_(engine)
|
|
14
|
+{
|
|
15
|
+ createLayout();
|
|
16
|
+ createConnections();
|
|
17
|
+}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+void
|
|
21
|
+ContinuousTab::repaintGlyph()
|
|
22
|
+{
|
|
23
|
+ sizeSelector_->applyToEngine(engine_);
|
|
24
|
+
|
|
25
|
+ updateFromCurrentSubTab();
|
|
26
|
+ canvas_->repaint();
|
|
27
|
+}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+void
|
|
31
|
+ContinuousTab::reloadFont()
|
|
32
|
+{
|
|
33
|
+ currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
|
|
34
|
+ updateCurrentSubTab();
|
|
35
|
+ repaintGlyph();
|
|
36
|
+}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+void
|
|
40
|
+ContinuousTab::syncSettings()
|
|
41
|
+{
|
|
42
|
+}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+void
|
|
46
|
+ContinuousTab::changeTab()
|
|
47
|
+{
|
|
48
|
+ updateCurrentSubTab();
|
|
49
|
+ repaintGlyph();
|
|
50
|
+}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+void
|
|
54
|
+ContinuousTab::wheelNavigate(int steps)
|
|
55
|
+{
|
|
56
|
+ if (tabWidget_->currentIndex() == AllGlyphs)
|
|
57
|
+ allGlyphsTab_->setGlyphBeginindex(allGlyphsTab_->glyphBeginindex()
|
|
58
|
+ + steps);
|
|
59
|
+}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+void
|
|
63
|
+ContinuousTab::wheelResize(int steps)
|
|
64
|
+{
|
|
65
|
+ sizeSelector_->handleWheelResizeBySteps(steps);
|
|
66
|
+}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+void
|
|
70
|
+ContinuousTab::createLayout()
|
|
71
|
+{
|
|
72
|
+ canvas_ = new GlyphContinuous(this, engine_);
|
|
73
|
+ sizeSelector_ = new FontSizeSelector(this);
|
|
74
|
+ allGlyphsTab_ = new ContinousAllGlyphsTab(this);
|
|
75
|
+
|
|
76
|
+ tabWidget_ = new QTabWidget(this);
|
|
77
|
+ tabWidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
78
|
+ // Must be in sync with `Tabs` enum.
|
|
79
|
+ tabWidget_->addTab(allGlyphsTab_, tr("All Glyphs"));
|
|
80
|
+
|
|
81
|
+ mainLayout_ = new QVBoxLayout;
|
|
82
|
+ mainLayout_->addWidget(canvas_);
|
|
83
|
+ mainLayout_->addWidget(sizeSelector_);
|
|
84
|
+ mainLayout_->addWidget(tabWidget_);
|
|
85
|
+
|
|
86
|
+ setLayout(mainLayout_);
|
|
87
|
+}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+void
|
|
91
|
+ContinuousTab::createConnections()
|
|
92
|
+{
|
|
93
|
+ connect(tabWidget_, &QTabWidget::currentChanged,
|
|
94
|
+ this, &ContinuousTab::changeTab);
|
|
95
|
+
|
|
96
|
+ connect(allGlyphsTab_, &ContinousAllGlyphsTab::changed,
|
|
97
|
+ this, &ContinuousTab::repaintGlyph);
|
|
98
|
+
|
|
99
|
+ connect(sizeSelector_, &FontSizeSelector::valueChanged,
|
|
100
|
+ this, &ContinuousTab::repaintGlyph);
|
|
101
|
+
|
|
102
|
+ connect(canvas_, &GlyphContinuous::wheelResize,
|
|
103
|
+ this, &ContinuousTab::wheelResize);
|
|
104
|
+ connect(canvas_, &GlyphContinuous::wheelNavigate,
|
|
105
|
+ this, &ContinuousTab::wheelNavigate);
|
|
106
|
+ connect(canvas_, &GlyphContinuous::displayingCountUpdated,
|
|
107
|
+ allGlyphsTab_, &ContinousAllGlyphsTab::setDisplayingCount);
|
|
108
|
+}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+void
|
|
112
|
+ContinuousTab::setDefaults()
|
|
113
|
+{
|
|
114
|
+}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+void
|
|
118
|
+ContinuousTab::updateCurrentSubTab()
|
|
119
|
+{
|
|
120
|
+ switch (tabWidget_->currentIndex())
|
|
121
|
+ {
|
|
122
|
+ case AllGlyphs:
|
|
123
|
+ allGlyphsTab_->setGlyphCount(qBound(0,
|
|
124
|
+ currentGlyphCount_,
|
|
125
|
+ INT_MAX));
|
|
126
|
+ allGlyphsTab_->setCharMaps(engine_->currentFontCharMaps());
|
|
127
|
+ break;
|
|
128
|
+ }
|
|
129
|
+}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+void
|
|
133
|
+ContinuousTab::updateFromCurrentSubTab()
|
|
134
|
+{
|
|
135
|
+ switch (tabWidget_->currentIndex())
|
|
136
|
+ {
|
|
137
|
+ case AllGlyphs:
|
|
138
|
+ // Begin index is selected from All Glyphs subtab,
|
|
139
|
+ // and Limit index is calculated by All Glyphs subtab
|
|
140
|
+ canvas_->setBeginIndex(allGlyphsTab_->glyphBeginindex());
|
|
141
|
+ canvas_->setLimitIndex(allGlyphsTab_->glyphLimitIndex());
|
|
142
|
+ canvas_->setMode(GlyphContinuous::AllGlyphs);
|
|
143
|
+ canvas_->setSubModeAllGlyphs(allGlyphsTab_->subMode());
|
|
144
|
+ canvas_->setCharMapIndex(allGlyphsTab_->charMapIndex());
|
|
145
|
+ break;
|
|
146
|
+ }
|
|
147
|
+}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+ContinousAllGlyphsTab::ContinousAllGlyphsTab(QWidget* parent)
|
|
151
|
+: QWidget(parent)
|
|
152
|
+{
|
|
153
|
+ createLayout();
|
|
154
|
+
|
|
155
|
+ QVector<CharMapInfo> tempCharMaps;
|
|
156
|
+ setCharMaps(tempCharMaps); // pass in an empty one
|
|
157
|
+
|
|
158
|
+ createConnections();
|
|
159
|
+}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+int
|
|
163
|
+ContinousAllGlyphsTab::glyphBeginindex()
|
|
164
|
+{
|
|
165
|
+ return indexSelector_->currentIndex();
|
|
166
|
+}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+int
|
|
170
|
+ContinousAllGlyphsTab::glyphLimitIndex()
|
|
171
|
+{
|
|
172
|
+ return glyphLimitIndex_;
|
|
173
|
+}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+GlyphContinuous::SubModeAllGlyphs
|
|
177
|
+ContinousAllGlyphsTab::subMode()
|
|
178
|
+{
|
|
179
|
+ return static_cast<GlyphContinuous::SubModeAllGlyphs>(
|
|
180
|
+ modeSelector_->currentIndex());
|
|
181
|
+}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+int
|
|
185
|
+ContinousAllGlyphsTab::charMapIndex()
|
|
186
|
+{
|
|
187
|
+ auto index = charMapSelector_->currentIndex() - 1;
|
|
188
|
+ if (index <= -1)
|
|
189
|
+ return -1;
|
|
190
|
+ if (index >= charMaps_.size())
|
|
191
|
+ return -1;
|
|
192
|
+ return index;
|
|
193
|
+}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+void
|
|
197
|
+ContinousAllGlyphsTab::setGlyphBeginindex(int index)
|
|
198
|
+{
|
|
199
|
+ indexSelector_->setCurrentIndex(index);
|
|
200
|
+ updateCharMapLimit();
|
|
201
|
+}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+void
|
|
205
|
+ContinousAllGlyphsTab::setDisplayingCount(int count)
|
|
206
|
+{
|
|
207
|
+ indexSelector_->setShowingCount(count);
|
|
208
|
+}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+#define EncodingRole (Qt::UserRole + 10)
|
|
212
|
+void
|
|
213
|
+ContinousAllGlyphsTab::setCharMaps(QVector<CharMapInfo>& charMaps)
|
|
214
|
+{
|
|
215
|
+ charMaps_ = charMaps;
|
|
216
|
+ int oldIndex = charMapSelector_->currentIndex();
|
|
217
|
+ unsigned oldEncoding = 0u;
|
|
218
|
+
|
|
219
|
+ // Using additional UserRole to store encoding id
|
|
220
|
+ auto oldEncodingV = charMapSelector_->itemData(oldIndex, EncodingRole);
|
|
221
|
+ if (oldEncodingV.isValid() && oldEncodingV.canConvert<unsigned>())
|
|
222
|
+ {
|
|
223
|
+ oldEncoding = oldEncodingV.value<unsigned>();
|
|
224
|
+ }
|
|
225
|
+
|
|
226
|
+ {
|
|
227
|
+ // suppress events during updating
|
|
228
|
+ QSignalBlocker selectorBlocker(charMapSelector_);
|
|
229
|
+
|
|
230
|
+ charMapSelector_->clear();
|
|
231
|
+ charMapSelector_->addItem(tr("Glyph Order"));
|
|
232
|
+ charMapSelector_->setItemData(0, 0u, EncodingRole);
|
|
233
|
+
|
|
234
|
+ int i = 0;
|
|
235
|
+ int newIndex = 0;
|
|
236
|
+ for (auto& map : charMaps)
|
|
237
|
+ {
|
|
238
|
+ charMapSelector_->addItem(tr("%1: %2")
|
|
239
|
+ .arg(i)
|
|
240
|
+ .arg(*map.encodingName));
|
|
241
|
+ auto encoding = static_cast<unsigned>(map.encoding);
|
|
242
|
+ charMapSelector_->setItemData(i, encoding, EncodingRole);
|
|
243
|
+
|
|
244
|
+ if (encoding == oldEncoding && i == oldIndex)
|
|
245
|
+ newIndex = i;
|
|
246
|
+
|
|
247
|
+ i++;
|
|
248
|
+ }
|
|
249
|
+
|
|
250
|
+ // this shouldn't emit any event either, because force repainting
|
|
251
|
+ // will happen later, so embrace it into blocker block
|
|
252
|
+ charMapSelector_->setCurrentIndex(newIndex);
|
|
253
|
+ }
|
|
254
|
+
|
|
255
|
+ updateCharMapLimit();
|
|
256
|
+}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+void
|
|
260
|
+ContinousAllGlyphsTab::updateCharMapLimit()
|
|
261
|
+{
|
|
262
|
+ if (charMapSelector_->currentIndex() <= 0)
|
|
263
|
+ glyphLimitIndex_ = currentGlyphCount_;
|
|
264
|
+ else
|
|
265
|
+ glyphLimitIndex_
|
|
266
|
+ = charMaps_[charMapSelector_->currentIndex() - 1].maxIndex + 1;
|
|
267
|
+ indexSelector_->setMinMax(0, glyphLimitIndex_ - 1);
|
|
268
|
+}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+void
|
|
272
|
+ContinousAllGlyphsTab::createLayout()
|
|
273
|
+{
|
|
274
|
+ indexSelector_ = new GlyphIndexSelector(this);
|
|
275
|
+ indexSelector_->setSingleMode(false);
|
|
276
|
+ indexSelector_->setNumberRenderer([this](int index)
|
|
277
|
+ { return formatIndex(index); });
|
|
278
|
+
|
|
279
|
+ modeSelector_ = new QComboBox(this);
|
|
280
|
+ charMapSelector_ = new QComboBox(this);
|
|
281
|
+
|
|
282
|
+ // Note: in sync with the enum!!
|
|
283
|
+ modeSelector_->insertItem(GlyphContinuous::AG_AllGlyphs, tr("All Glyphs"));
|
|
284
|
+ modeSelector_->insertItem(GlyphContinuous::AG_Fancy, tr("Fancy"));
|
|
285
|
+ modeSelector_->insertItem(GlyphContinuous::AG_Stroked, tr("Stroked"));
|
|
286
|
+ modeSelector_->insertItem(GlyphContinuous::AG_Waterfall, tr("Waterfall"));
|
|
287
|
+ modeSelector_->setCurrentIndex(GlyphContinuous::AG_AllGlyphs);
|
|
288
|
+
|
|
289
|
+ modeLabel_ = new QLabel(tr("Mode:"), this);
|
|
290
|
+ charMapLabel_ = new QLabel(tr("Char Map:"), this);
|
|
291
|
+
|
|
292
|
+ layout_ = new QGridLayout;
|
|
293
|
+ layout_->addWidget(indexSelector_, 0, 0, 1, 2);
|
|
294
|
+ layout_->addWidget(modeLabel_, 1, 0);
|
|
295
|
+ layout_->addWidget(charMapLabel_, 2, 0);
|
|
296
|
+ layout_->addWidget(modeSelector_, 1, 1);
|
|
297
|
+ layout_->addWidget(charMapSelector_, 2, 1);
|
|
298
|
+
|
|
299
|
+ layout_->setColumnStretch(1, 1);
|
|
300
|
+
|
|
301
|
+ setLayout(layout_);
|
|
302
|
+}
|
|
303
|
+
|
|
304
|
+void
|
|
305
|
+ContinousAllGlyphsTab::createConnections()
|
|
306
|
+{
|
|
307
|
+ connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
|
|
308
|
+ this, &ContinousAllGlyphsTab::changed);
|
|
309
|
+ connect(modeSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
310
|
+ this, &ContinousAllGlyphsTab::changed);
|
|
311
|
+ connect(charMapSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
312
|
+ this, &ContinousAllGlyphsTab::charMapChanged);
|
|
313
|
+}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+QString
|
|
317
|
+ContinousAllGlyphsTab::formatIndex(int index)
|
|
318
|
+{
|
|
319
|
+ if (charMapSelector_->currentIndex() <= 0) // glyph order
|
|
320
|
+ return QString::number(index);
|
|
321
|
+ return charMaps_[charMapSelector_->currentIndex() - 1]
|
|
322
|
+ .stringifyIndexShort(index);
|
|
323
|
+}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+void
|
|
327
|
+ContinousAllGlyphsTab::charMapChanged()
|
|
328
|
+{
|
|
329
|
+ int newIndex = charMapSelector_->currentIndex();
|
|
330
|
+ if (newIndex != lastCharMapIndex_)
|
|
331
|
+ {
|
|
332
|
+ if (newIndex <= 0 || charMaps_.size() <= newIndex - 1)
|
|
333
|
+ setGlyphBeginindex(0);
|
|
334
|
+ else if (charMaps_[newIndex - 1].maxIndex <= 20)
|
|
335
|
+ setGlyphBeginindex(charMaps_[newIndex - 1].maxIndex - 1);
|
|
336
|
+ else
|
|
337
|
+ setGlyphBeginindex(0x20);
|
|
338
|
+ }
|
|
339
|
+ updateCharMapLimit();
|
|
340
|
+
|
|
341
|
+ emit changed();
|
|
342
|
+
|
|
343
|
+ lastCharMapIndex_ = newIndex;
|
|
344
|
+}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+// end of continuous.cpp |
src/ftinspect/panels/continuous.hpp
|
1
|
+// continuous.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 "../rendering/graphicsdefault.hpp"
|
|
12
|
+#include "../rendering/glyphcontinuous.hpp"
|
|
13
|
+#include "../engine/engine.hpp"
|
|
14
|
+
|
|
15
|
+#include <QWidget>
|
|
16
|
+#include <QLabel>
|
|
17
|
+#include <QComboBox>
|
|
18
|
+#include <QVector>
|
|
19
|
+#include <QGridLayout>
|
|
20
|
+#include <QBoxLayout>
|
|
21
|
+
|
|
22
|
+class ContinousAllGlyphsTab;
|
|
23
|
+
|
|
24
|
+class ContinuousTab
|
|
25
|
+: public QWidget, public AbstractTab
|
|
26
|
+{
|
|
27
|
+ Q_OBJECT
|
|
28
|
+public:
|
|
29
|
+ ContinuousTab(QWidget* parent, Engine* engine);
|
|
30
|
+ ~ContinuousTab() override = default;
|
|
31
|
+
|
|
32
|
+ void repaintGlyph() override;
|
|
33
|
+ void reloadFont() override;
|
|
34
|
+ void syncSettings() override;
|
|
35
|
+ void setDefaults() override;
|
|
36
|
+
|
|
37
|
+ // Info about current font (glyph count, charmaps...) is flowed to subtab
|
|
38
|
+ // via `updateCurrentSubTab`.
|
|
39
|
+ // Settings and parameters (e.g. mode) are flowed from subtab to `this` via
|
|
40
|
+ // `updateFromCurrentSubTab`.
|
|
41
|
+ // SubTabs can notify `this` via signals, see `createConnections`
|
|
42
|
+ void updateCurrentSubTab();
|
|
43
|
+ void updateFromCurrentSubTab();
|
|
44
|
+
|
|
45
|
+private slots:
|
|
46
|
+ void changeTab();
|
|
47
|
+ void wheelNavigate(int steps);
|
|
48
|
+ void wheelResize(int steps);
|
|
49
|
+
|
|
50
|
+private:
|
|
51
|
+ Engine* engine_;
|
|
52
|
+
|
|
53
|
+ int currentGlyphCount_;
|
|
54
|
+ GlyphContinuous* canvas_;
|
|
55
|
+
|
|
56
|
+ FontSizeSelector* sizeSelector_;
|
|
57
|
+
|
|
58
|
+ QTabWidget* tabWidget_;
|
|
59
|
+ ContinousAllGlyphsTab* allGlyphsTab_;
|
|
60
|
+
|
|
61
|
+ enum Tabs
|
|
62
|
+ {
|
|
63
|
+ AllGlyphs = 0
|
|
64
|
+ };
|
|
65
|
+
|
|
66
|
+ QVBoxLayout* mainLayout_;
|
|
67
|
+
|
|
68
|
+ void createLayout();
|
|
69
|
+ void createConnections();
|
|
70
|
+};
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+class ContinousAllGlyphsTab
|
|
74
|
+: public QWidget
|
|
75
|
+{
|
|
76
|
+ Q_OBJECT
|
|
77
|
+public:
|
|
78
|
+ explicit ContinousAllGlyphsTab(QWidget* parent);
|
|
79
|
+ ~ContinousAllGlyphsTab() override = default;
|
|
80
|
+
|
|
81
|
+ int glyphBeginindex();
|
|
82
|
+ int glyphLimitIndex();
|
|
83
|
+ GlyphContinuous::SubModeAllGlyphs subMode();
|
|
84
|
+
|
|
85
|
+ // -1: Glyph order, otherwise the char map index in the original list
|
|
86
|
+ int charMapIndex();
|
|
87
|
+ void setGlyphBeginindex(int index);
|
|
88
|
+
|
|
89
|
+ // This doesn't trigger immediate repaint
|
|
90
|
+ void setGlyphCount(int count) { currentGlyphCount_ = count; }
|
|
91
|
+ void setDisplayingCount(int count);
|
|
92
|
+
|
|
93
|
+ void setCharMaps(QVector<CharMapInfo>& charMaps);
|
|
94
|
+ // This doesn't trigger either.
|
|
95
|
+ void updateCharMapLimit();
|
|
96
|
+
|
|
97
|
+signals:
|
|
98
|
+ void changed();
|
|
99
|
+
|
|
100
|
+private:
|
|
101
|
+ int lastCharMapIndex_ = 0;
|
|
102
|
+ int currentGlyphCount_;
|
|
103
|
+ int glyphLimitIndex_ = 0;
|
|
104
|
+
|
|
105
|
+ GlyphIndexSelector* indexSelector_;
|
|
106
|
+ QComboBox* modeSelector_;
|
|
107
|
+ QComboBox* charMapSelector_;
|
|
108
|
+
|
|
109
|
+ QLabel* modeLabel_;
|
|
110
|
+ QLabel* charMapLabel_;
|
|
111
|
+
|
|
112
|
+ QGridLayout* layout_;
|
|
113
|
+
|
|
114
|
+ QVector<CharMapInfo> charMaps_;
|
|
115
|
+
|
|
116
|
+ void createLayout();
|
|
117
|
+ void createConnections();
|
|
118
|
+
|
|
119
|
+ QString formatIndex(int index);
|
|
120
|
+ void charMapChanged();
|
|
121
|
+};
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+// end of continuous.hpp |
src/ftinspect/panels/singular.cpp
... |
... |
@@ -9,15 +9,14 @@ |
9
|
9
|
|
10
|
10
|
|
11
|
11
|
SingularTab::SingularTab(QWidget* parent, Engine* engine)
|
12
|
|
-: QWidget(parent), engine_(engine)
|
|
12
|
+: QWidget(parent), engine_(engine),
|
|
13
|
+ graphicsDefault_(GraphicsDefault::deafultInstance())
|
13
|
14
|
{
|
14
|
|
- setGraphicsDefaults();
|
15
|
15
|
createLayout();
|
16
|
16
|
createConnections();
|
17
|
17
|
|
18
|
18
|
currentGlyphIndex_ = 0;
|
19
|
19
|
checkShowPoints();
|
20
|
|
- checkUnits();
|
21
|
20
|
}
|
22
|
21
|
|
23
|
22
|
|
... |
... |
@@ -97,30 +96,35 @@ SingularTab::drawGlyph() |
97
|
96
|
if (!engine_->antiAliasingEnabled())
|
98
|
97
|
pixelMode = FT_PIXEL_MODE_MONO;
|
99
|
98
|
|
100
|
|
- currentGlyphBitmapItem_ = new GlyphBitmap(outline,
|
101
|
|
- engine_->ftLibrary(),
|
102
|
|
- pixelMode,
|
103
|
|
- monoColorTable_,
|
104
|
|
- grayColorTable_);
|
|
99
|
+ currentGlyphBitmapItem_
|
|
100
|
+ = new GlyphBitmap(outline,
|
|
101
|
+ engine_->ftLibrary(),
|
|
102
|
+ pixelMode,
|
|
103
|
+ graphicsDefault_->monoColorTable,
|
|
104
|
+ graphicsDefault_->grayColorTable);
|
105
|
105
|
glyphScene_->addItem(currentGlyphBitmapItem_);
|
106
|
106
|
}
|
107
|
107
|
|
108
|
108
|
if (showOutlinesCheckBox_->isChecked())
|
109
|
109
|
{
|
110
|
|
- currentGlyphOutlineItem_ = new GlyphOutline(outlinePen_, outline);
|
|
110
|
+ currentGlyphOutlineItem_ = new GlyphOutline(graphicsDefault_->outlinePen,
|
|
111
|
+ outline);
|
111
|
112
|
glyphScene_->addItem(currentGlyphOutlineItem_);
|
112
|
113
|
}
|
113
|
114
|
|
114
|
115
|
if (showPointsCheckBox_->isChecked())
|
115
|
116
|
{
|
116
|
|
- currentGlyphPointsItem_ = new GlyphPoints(onPen_, offPen_, outline);
|
|
117
|
+ currentGlyphPointsItem_ = new GlyphPoints(graphicsDefault_->onPen,
|
|
118
|
+ graphicsDefault_->offPen,
|
|
119
|
+ outline);
|
117
|
120
|
glyphScene_->addItem(currentGlyphPointsItem_);
|
118
|
121
|
|
119
|
122
|
if (showPointNumbersCheckBox_->isChecked())
|
120
|
123
|
{
|
121
|
|
- currentGlyphPointNumbersItem_ = new GlyphPointNumbers(onPen_,
|
122
|
|
- offPen_,
|
123
|
|
- outline);
|
|
124
|
+ currentGlyphPointNumbersItem_
|
|
125
|
+ = new GlyphPointNumbers(graphicsDefault_->onPen,
|
|
126
|
+ graphicsDefault_->offPen,
|
|
127
|
+ outline);
|
124
|
128
|
glyphScene_->addItem(currentGlyphPointNumbersItem_);
|
125
|
129
|
}
|
126
|
130
|
}
|
... |
... |
@@ -130,29 +134,6 @@ SingularTab::drawGlyph() |
130
|
134
|
}
|
131
|
135
|
|
132
|
136
|
|
133
|
|
-void
|
134
|
|
-SingularTab::checkUnits()
|
135
|
|
-{
|
136
|
|
- int index = unitsComboBox_->currentIndex();
|
137
|
|
-
|
138
|
|
- if (index == Units_px)
|
139
|
|
- {
|
140
|
|
- dpiLabel_->setEnabled(false);
|
141
|
|
- dpiSpinBox_->setEnabled(false);
|
142
|
|
- sizeDoubleSpinBox_->setSingleStep(1);
|
143
|
|
- sizeDoubleSpinBox_->setValue(qRound(sizeDoubleSpinBox_->value()));
|
144
|
|
- }
|
145
|
|
- else
|
146
|
|
- {
|
147
|
|
- dpiLabel_->setEnabled(true);
|
148
|
|
- dpiSpinBox_->setEnabled(true);
|
149
|
|
- sizeDoubleSpinBox_->setSingleStep(0.5);
|
150
|
|
- }
|
151
|
|
-
|
152
|
|
- drawGlyph();
|
153
|
|
-}
|
154
|
|
-
|
155
|
|
-
|
156
|
137
|
void
|
157
|
138
|
SingularTab::checkShowPoints()
|
158
|
139
|
{
|
... |
... |
@@ -219,11 +200,7 @@ SingularTab::wheelZoom(QWheelEvent* event) |
219
|
200
|
void
|
220
|
201
|
SingularTab::wheelResize(QWheelEvent* event)
|
221
|
202
|
{
|
222
|
|
- int numSteps = event->angleDelta().y() / 120;
|
223
|
|
- double sizeAfter = sizeDoubleSpinBox_->value() + numSteps * 0.5;
|
224
|
|
- sizeAfter = std::max(sizeDoubleSpinBox_->minimum(),
|
225
|
|
- std::min(sizeAfter, sizeDoubleSpinBox_->maximum()));
|
226
|
|
- sizeDoubleSpinBox_->setValue(sizeAfter);
|
|
203
|
+ sizeSelector_->handleWheelResizeFromGrid(event);
|
227
|
204
|
}
|
228
|
205
|
|
229
|
206
|
|
... |
... |
@@ -246,7 +223,8 @@ SingularTab::createLayout() |
246
|
223
|
glyphView_->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
247
|
224
|
glyphView_->setScene(glyphScene_);
|
248
|
225
|
|
249
|
|
- gridItem_ = new Grid(glyphView_, gridPen_, axisPen_);
|
|
226
|
+ gridItem_ = new Grid(glyphView_, graphicsDefault_->gridPen,
|
|
227
|
+ graphicsDefault_->axisPen);
|
250
|
228
|
glyphScene_->addItem(gridItem_);
|
251
|
229
|
|
252
|
230
|
// Don't use QGraphicsTextItem: We want this hint to be anchored at the
|
... |
... |
@@ -271,27 +249,10 @@ SingularTab::createLayout() |
271
|
249
|
glyphIndexLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
272
|
250
|
glyphNameLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
273
|
251
|
|
274
|
|
- sizeLabel_ = new QLabel(tr("Size "), this);
|
275
|
|
- sizeLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
276
|
|
- sizeDoubleSpinBox_ = new QDoubleSpinBox;
|
277
|
|
- sizeDoubleSpinBox_->setAlignment(Qt::AlignRight);
|
278
|
|
- sizeDoubleSpinBox_->setDecimals(1);
|
279
|
|
- sizeDoubleSpinBox_->setRange(1, 500);
|
280
|
|
- sizeLabel_->setBuddy(sizeDoubleSpinBox_);
|
281
|
|
-
|
282
|
252
|
indexSelector_ = new GlyphIndexSelector(this);
|
283
|
253
|
indexSelector_->setSingleMode(true);
|
284
|
254
|
|
285
|
|
- unitsComboBox_ = new QComboBox(this);
|
286
|
|
- unitsComboBox_->insertItem(Units_px, "px");
|
287
|
|
- unitsComboBox_->insertItem(Units_pt, "pt");
|
288
|
|
-
|
289
|
|
- dpiLabel_ = new QLabel(tr("DPI "), this);
|
290
|
|
- dpiLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
291
|
|
- dpiSpinBox_ = new QSpinBox(this);
|
292
|
|
- dpiSpinBox_->setAlignment(Qt::AlignRight);
|
293
|
|
- dpiSpinBox_->setRange(10, 600);
|
294
|
|
- dpiLabel_->setBuddy(dpiSpinBox_);
|
|
255
|
+ sizeSelector_ = new FontSizeSelector(this);
|
295
|
256
|
|
296
|
257
|
zoomLabel_ = new QLabel(tr("Zoom Factor"), this);
|
297
|
258
|
zoomLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
... |
... |
@@ -310,12 +271,7 @@ SingularTab::createLayout() |
310
|
271
|
|
311
|
272
|
sizeLayout_ = new QHBoxLayout;
|
312
|
273
|
sizeLayout_->addStretch(2);
|
313
|
|
- sizeLayout_->addWidget(sizeLabel_);
|
314
|
|
- sizeLayout_->addWidget(sizeDoubleSpinBox_);
|
315
|
|
- sizeLayout_->addWidget(unitsComboBox_);
|
316
|
|
- sizeLayout_->addStretch(1);
|
317
|
|
- sizeLayout_->addWidget(dpiLabel_);
|
318
|
|
- sizeLayout_->addWidget(dpiSpinBox_);
|
|
274
|
+ sizeLayout_->addWidget(sizeSelector_, 3);
|
319
|
275
|
sizeLayout_->addStretch(1);
|
320
|
276
|
sizeLayout_->addWidget(zoomLabel_);
|
321
|
277
|
sizeLayout_->addWidget(zoomSpinBox_);
|
... |
... |
@@ -355,14 +311,10 @@ SingularTab::createLayout() |
355
|
311
|
void
|
356
|
312
|
SingularTab::createConnections()
|
357
|
313
|
{
|
|
314
|
+ connect(sizeSelector_, &FontSizeSelector::valueChanged,
|
|
315
|
+ this, &SingularTab::repaintGlyph);
|
358
|
316
|
connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
|
359
|
317
|
this, &SingularTab::setGlyphIndex);
|
360
|
|
- connect(sizeDoubleSpinBox_, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
361
|
|
- this, &SingularTab::drawGlyph);
|
362
|
|
- connect(unitsComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
363
|
|
- this, &SingularTab::checkUnits);
|
364
|
|
- connect(dpiSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
|
365
|
|
- this, &SingularTab::drawGlyph);
|
366
|
318
|
|
367
|
319
|
connect(zoomSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
|
368
|
320
|
this, &SingularTab::zoom);
|
... |
... |
@@ -389,36 +341,6 @@ SingularTab::createConnections() |
389
|
341
|
}
|
390
|
342
|
|
391
|
343
|
|
392
|
|
-void
|
393
|
|
-SingularTab::setGraphicsDefaults()
|
394
|
|
-{
|
395
|
|
- // color tables (with suitable opacity values) for converting
|
396
|
|
- // FreeType's pixmaps to something Qt understands
|
397
|
|
- monoColorTable_.append(QColor(Qt::transparent).rgba());
|
398
|
|
- monoColorTable_.append(QColor(Qt::black).rgba());
|
399
|
|
-
|
400
|
|
- for (int i = 0xFF; i >= 0; i--)
|
401
|
|
- grayColorTable_.append(qRgba(i, i, i, 0xFF - i));
|
402
|
|
-
|
403
|
|
- // XXX make this user-configurable
|
404
|
|
-
|
405
|
|
- axisPen_.setColor(Qt::black);
|
406
|
|
- axisPen_.setWidth(0);
|
407
|
|
- blueZonePen_.setColor(QColor(64, 64, 255, 64)); // light blue
|
408
|
|
- blueZonePen_.setWidth(0);
|
409
|
|
- gridPen_.setColor(Qt::lightGray);
|
410
|
|
- gridPen_.setWidth(0);
|
411
|
|
- offPen_.setColor(Qt::darkGreen);
|
412
|
|
- offPen_.setWidth(3);
|
413
|
|
- onPen_.setColor(Qt::red);
|
414
|
|
- onPen_.setWidth(3);
|
415
|
|
- outlinePen_.setColor(Qt::red);
|
416
|
|
- outlinePen_.setWidth(0);
|
417
|
|
- segmentPen_.setColor(QColor(64, 255, 128, 64)); // light green
|
418
|
|
- segmentPen_.setWidth(0);
|
419
|
|
-}
|
420
|
|
-
|
421
|
|
-
|
422
|
344
|
void
|
423
|
345
|
SingularTab::repaintGlyph()
|
424
|
346
|
{
|
... |
... |
@@ -430,9 +352,7 @@ void |
430
|
352
|
SingularTab::reloadFont()
|
431
|
353
|
{
|
432
|
354
|
currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
|
433
|
|
- indexSelector_->setMin(0);
|
434
|
|
- indexSelector_->setMax(currentGlyphCount_);
|
435
|
|
- indexSelector_->setCurrentIndex(indexSelector_->getCurrentIndex(), true);
|
|
355
|
+ indexSelector_->setMinMax(0, currentGlyphCount_);
|
436
|
356
|
drawGlyph();
|
437
|
357
|
}
|
438
|
358
|
|
... |
... |
@@ -440,13 +360,7 @@ SingularTab::reloadFont() |
440
|
360
|
void
|
441
|
361
|
SingularTab::syncSettings()
|
442
|
362
|
{
|
443
|
|
- // Spinbox value cannot become negative
|
444
|
|
- engine_->setDPI(static_cast<unsigned int>(dpiSpinBox_->value()));
|
445
|
|
-
|
446
|
|
- if (unitsComboBox_->currentIndex() == Units_px)
|
447
|
|
- engine_->setSizeByPixel(sizeDoubleSpinBox_->value());
|
448
|
|
- else
|
449
|
|
- engine_->setSizeByPoint(sizeDoubleSpinBox_->value());
|
|
363
|
+ sizeSelector_->applyToEngine(engine_);
|
450
|
364
|
}
|
451
|
365
|
|
452
|
366
|
|
... |
... |
@@ -455,14 +369,11 @@ SingularTab::setDefaults() |
455
|
369
|
{
|
456
|
370
|
currentGlyphIndex_ = 0;
|
457
|
371
|
|
458
|
|
- sizeDoubleSpinBox_->setValue(20);
|
459
|
|
- dpiSpinBox_->setValue(96);
|
460
|
372
|
zoomSpinBox_->setValue(20);
|
461
|
373
|
showBitmapCheckBox_->setChecked(true);
|
462
|
374
|
showOutlinesCheckBox_->setChecked(true);
|
463
|
|
-
|
464
|
|
- checkUnits();
|
465
|
|
- indexSelector_->setCurrentIndex(indexSelector_->getCurrentIndex(), true);
|
|
375
|
+
|
|
376
|
+ indexSelector_->setCurrentIndex(indexSelector_->currentIndex(), true);
|
466
|
377
|
zoom();
|
467
|
378
|
}
|
468
|
379
|
|
src/ftinspect/panels/singular.hpp
... |
... |
@@ -7,11 +7,13 @@ |
7
|
7
|
#include "abstracttab.hpp"
|
8
|
8
|
#include "../widgets/customwidgets.hpp"
|
9
|
9
|
#include "../widgets/glyphindexselector.hpp"
|
|
10
|
+#include "../widgets/fontsizeselector.hpp"
|
10
|
11
|
#include "../rendering/glyphbitmap.hpp"
|
11
|
12
|
#include "../rendering/glyphoutline.hpp"
|
12
|
13
|
#include "../rendering/glyphpointnumbers.hpp"
|
13
|
14
|
#include "../rendering/glyphpoints.hpp"
|
14
|
15
|
#include "../rendering/grid.hpp"
|
|
16
|
+#include "../rendering/graphicsdefault.hpp"
|
15
|
17
|
#include "../engine/engine.hpp"
|
16
|
18
|
#include "../models/ttsettingscomboboxmodel.hpp"
|
17
|
19
|
|
... |
... |
@@ -45,8 +47,7 @@ public: |
45
|
47
|
private slots:
|
46
|
48
|
void setGlyphIndex(int);
|
47
|
49
|
void drawGlyph();
|
48
|
|
-
|
49
|
|
- void checkUnits();
|
|
50
|
+
|
50
|
51
|
void checkShowPoints();
|
51
|
52
|
|
52
|
53
|
void zoom();
|
... |
... |
@@ -71,13 +72,9 @@ private: |
71
|
72
|
QLabel* mouseUsageHint_;
|
72
|
73
|
|
73
|
74
|
GlyphIndexSelector* indexSelector_;
|
74
|
|
- QLabel* dpiLabel_;
|
75
|
|
- QLabel* sizeLabel_;
|
|
75
|
+ FontSizeSelector* sizeSelector_;
|
76
|
76
|
QLabel* zoomLabel_;
|
77
|
|
- QSpinBox* dpiSpinBox_;
|
78
|
77
|
ZoomSpinBox* zoomSpinBox_;
|
79
|
|
- QComboBox* unitsComboBox_;
|
80
|
|
- QDoubleSpinBox* sizeDoubleSpinBox_;
|
81
|
78
|
QPushButton* centerGridButton_;
|
82
|
79
|
|
83
|
80
|
QLabel* glyphIndexLabel_;
|
... |
... |
@@ -94,26 +91,10 @@ private: |
94
|
91
|
QGridLayout* glyphOverlayLayout_;
|
95
|
92
|
QHBoxLayout* glyphOverlayIndexLayout_;
|
96
|
93
|
|
97
|
|
- QPen axisPen_;
|
98
|
|
- QPen blueZonePen_;
|
99
|
|
- QPen gridPen_;
|
100
|
|
- QPen offPen_;
|
101
|
|
- QPen onPen_;
|
102
|
|
- QPen outlinePen_;
|
103
|
|
- QPen segmentPen_;
|
104
|
|
-
|
105
|
|
- QVector<QRgb> grayColorTable_;
|
106
|
|
- QVector<QRgb> monoColorTable_;
|
107
|
|
-
|
108
|
|
- enum Units
|
109
|
|
- {
|
110
|
|
- Units_px,
|
111
|
|
- Units_pt
|
112
|
|
- };
|
|
94
|
+ GraphicsDefault* graphicsDefault_;
|
113
|
95
|
|
114
|
96
|
void createLayout();
|
115
|
97
|
void createConnections();
|
116
|
|
- void setGraphicsDefaults();
|
117
|
98
|
|
118
|
99
|
void updateGrid();
|
119
|
100
|
};
|
src/ftinspect/rendering/glyphbitmap.cpp
... |
... |
@@ -5,6 +5,8 @@ |
5
|
5
|
|
6
|
6
|
#include "glyphbitmap.hpp"
|
7
|
7
|
|
|
8
|
+#include "renderutils.hpp"
|
|
9
|
+
|
8
|
10
|
#include <cmath>
|
9
|
11
|
#include <QPainter>
|
10
|
12
|
#include <QStyleOptionGraphicsItem>
|
... |
... |
@@ -21,23 +23,8 @@ GlyphBitmap::GlyphBitmap(FT_Outline* outline, |
21
|
23
|
grayColorTable_(grayColorTbl)
|
22
|
24
|
{
|
23
|
25
|
// 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
|
26
|
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
|
|
-
|
|
27
|
+ transformed_ = transformOutlineToOrigin(lib, outline, &cbox);
|
41
|
28
|
boundingRect_.setCoords(cbox.xMin / 64, -cbox.yMax / 64,
|
42
|
29
|
cbox.xMax / 64, -cbox.yMin / 64);
|
43
|
30
|
}
|
src/ftinspect/rendering/glyphcontinuous.cpp
|
1
|
+// glyphcontinuous.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "glyphcontinuous.hpp"
|
|
6
|
+
|
|
7
|
+#include <cmath>
|
|
8
|
+#include <QPainter>
|
|
9
|
+#include <QWheelEvent>
|
|
10
|
+
|
|
11
|
+#include "../engine/engine.hpp"
|
|
12
|
+#include "../rendering/renderutils.hpp"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
|
|
16
|
+: QWidget(parent), engine_(engine)
|
|
17
|
+{
|
|
18
|
+ setAcceptDrops(false);
|
|
19
|
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
20
|
+ graphicsDefault_ = GraphicsDefault::deafultInstance();
|
|
21
|
+}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+void
|
|
25
|
+GlyphContinuous::paintEvent(QPaintEvent* event)
|
|
26
|
+{
|
|
27
|
+ QPainter painter;
|
|
28
|
+ painter.begin(this);
|
|
29
|
+ painter.fillRect(rect(), Qt::white);
|
|
30
|
+
|
|
31
|
+ if (limitIndex_ > 0)
|
|
32
|
+ {
|
|
33
|
+ prePaint();
|
|
34
|
+
|
|
35
|
+ switch (mode_)
|
|
36
|
+ {
|
|
37
|
+ case AllGlyphs:
|
|
38
|
+ switch (modeAG_)
|
|
39
|
+ {
|
|
40
|
+ case AG_AllGlyphs:
|
|
41
|
+ paintAGAllGlyphs(&painter);
|
|
42
|
+ break;
|
|
43
|
+ // TODO more modes
|
|
44
|
+ case AG_Fancy:
|
|
45
|
+ break;
|
|
46
|
+ case AG_Stroked:
|
|
47
|
+ break;
|
|
48
|
+ case AG_Waterfall:
|
|
49
|
+ break;
|
|
50
|
+ }
|
|
51
|
+ break;
|
|
52
|
+ case TextString:
|
|
53
|
+ break;
|
|
54
|
+ }
|
|
55
|
+ emit displayingCountUpdated(displayingCount_);
|
|
56
|
+ }
|
|
57
|
+
|
|
58
|
+ painter.end();
|
|
59
|
+}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+void
|
|
63
|
+GlyphContinuous::wheelEvent(QWheelEvent* event)
|
|
64
|
+{
|
|
65
|
+ int numSteps = event->angleDelta().y() / 120;
|
|
66
|
+ if (event->modifiers() & Qt::ShiftModifier)
|
|
67
|
+ emit wheelResize(numSteps);
|
|
68
|
+ else if (event->modifiers() == 0)
|
|
69
|
+ emit wheelNavigate(-numSteps);
|
|
70
|
+}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+void
|
|
74
|
+GlyphContinuous::paintAGAllGlyphs(QPainter* painter)
|
|
75
|
+{
|
|
76
|
+ for (int i = beginIndex_; i < limitIndex_; i++)
|
|
77
|
+ {
|
|
78
|
+ unsigned index = i;
|
|
79
|
+ if (charMapIndex_ >= 0)
|
|
80
|
+ index = engine_->glyphIndexFromCharCode(i, charMapIndex_);
|
|
81
|
+
|
|
82
|
+ if (!paintChar(painter, index))
|
|
83
|
+ break;
|
|
84
|
+
|
|
85
|
+ displayingCount_++;
|
|
86
|
+ }
|
|
87
|
+}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+void
|
|
91
|
+GlyphContinuous::prePaint()
|
|
92
|
+{
|
|
93
|
+ displayingCount_ = 0;
|
|
94
|
+ engine_->reloadFont();
|
|
95
|
+ metrics_ = engine_->currentFontMetrics();
|
|
96
|
+ x_ = 0;
|
|
97
|
+ // See ftview.c:42
|
|
98
|
+ y_ = ((metrics_.ascender - metrics_.descender + 63) >> 6) + 4;
|
|
99
|
+ stepY_ = ((metrics_.height + 63) >> 6) + 4;
|
|
100
|
+}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+bool
|
|
104
|
+GlyphContinuous::paintChar(QPainter* painter,
|
|
105
|
+ int index)
|
|
106
|
+{
|
|
107
|
+ auto glyph = engine_->loadGlyphWithoutUpdate(index);
|
|
108
|
+ if (!glyph)
|
|
109
|
+ return false;
|
|
110
|
+
|
|
111
|
+ // ftview.c:557
|
|
112
|
+ int width = glyph->advance.x ? glyph->advance.x >> 16
|
|
113
|
+ : metrics_.y_ppem / 2;
|
|
114
|
+
|
|
115
|
+ if (!checkFitX(x_ + width))
|
|
116
|
+ {
|
|
117
|
+ x_ = 0;
|
|
118
|
+ y_ += stepY_;
|
|
119
|
+
|
|
120
|
+ if (!checkFitY(y_))
|
|
121
|
+ return false;
|
|
122
|
+ }
|
|
123
|
+
|
|
124
|
+ x_++; // extra space
|
|
125
|
+ if (glyph->advance.x == 0)
|
|
126
|
+ {
|
|
127
|
+ // Draw a red square to indicate
|
|
128
|
+ painter->fillRect(x_, y_ - width, width, width,
|
|
129
|
+ Qt::red);
|
|
130
|
+ x_ += width;
|
|
131
|
+ }
|
|
132
|
+
|
|
133
|
+ // The real drawing part
|
|
134
|
+ // XXX: this is different from what's being done in
|
|
135
|
+ // `ftcommon.c`:FTDemo_Draw_Slot: is this correct??
|
|
136
|
+
|
|
137
|
+ // First translate the outline
|
|
138
|
+
|
|
139
|
+ if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
140
|
+ return true; // XXX only outline is supported - need to impl others later
|
|
141
|
+
|
|
142
|
+ FT_BBox cbox;
|
|
143
|
+ // Don't forget to free this when returning
|
|
144
|
+ auto outline = transformOutlineToOrigin(
|
|
145
|
+ engine_->ftLibrary(),
|
|
146
|
+ &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
|
|
147
|
+ &cbox);
|
|
148
|
+
|
|
149
|
+ auto outlineWidth = (cbox.xMax - cbox.xMin) / 64;
|
|
150
|
+ auto outlineHeight = (cbox.yMax - cbox.yMin) / 64;
|
|
151
|
+
|
|
152
|
+ // Then convert to bitmap
|
|
153
|
+ FT_Bitmap bitmap;
|
|
154
|
+ QImage::Format format = QImage::Format_Indexed8;
|
|
155
|
+ auto aaEnabled = engine_->antiAliasingEnabled();
|
|
156
|
+
|
|
157
|
+ // TODO cover LCD and color
|
|
158
|
+ if (!aaEnabled)
|
|
159
|
+ format = QImage::Format_Mono;
|
|
160
|
+
|
|
161
|
+ // TODO optimization: reuse QImage?
|
|
162
|
+ QImage image(QSize(outlineWidth, outlineHeight), format);
|
|
163
|
+
|
|
164
|
+ if (!aaEnabled)
|
|
165
|
+ image.setColorTable(graphicsDefault_->monoColorTable);
|
|
166
|
+ else
|
|
167
|
+ image.setColorTable(graphicsDefault_->grayColorTable);
|
|
168
|
+
|
|
169
|
+ image.fill(0);
|
|
170
|
+
|
|
171
|
+ bitmap.rows = static_cast<unsigned int>(outlineHeight);
|
|
172
|
+ bitmap.width = static_cast<unsigned int>(outlineWidth);
|
|
173
|
+ bitmap.buffer = image.bits();
|
|
174
|
+ bitmap.pitch = image.bytesPerLine();
|
|
175
|
+ bitmap.pixel_mode = aaEnabled ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO;
|
|
176
|
+
|
|
177
|
+ FT_Error error = FT_Outline_Get_Bitmap(engine_->ftLibrary(),
|
|
178
|
+ &outline,
|
|
179
|
+ &bitmap);
|
|
180
|
+ if (error)
|
|
181
|
+ {
|
|
182
|
+ // XXX error handling
|
|
183
|
+ FT_Outline_Done(engine_->ftLibrary(), &outline);
|
|
184
|
+ return true;
|
|
185
|
+ }
|
|
186
|
+
|
|
187
|
+ painter->drawImage(
|
|
188
|
+ QPoint(x_ + cbox.xMin / 64, y_ + (-cbox.yMax / 64)),
|
|
189
|
+ image.convertToFormat(QImage::Format_ARGB32_Premultiplied));
|
|
190
|
+
|
|
191
|
+ x_ += width;
|
|
192
|
+
|
|
193
|
+ FT_Outline_Done(engine_->ftLibrary(), &outline);
|
|
194
|
+ return true;
|
|
195
|
+}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+bool
|
|
199
|
+GlyphContinuous::checkFitX(int x)
|
|
200
|
+{
|
|
201
|
+ return x < width() - 3;
|
|
202
|
+}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+bool
|
|
206
|
+GlyphContinuous::checkFitY(int y)
|
|
207
|
+{
|
|
208
|
+ return y < height() - 3;
|
|
209
|
+}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+// end of glyphcontinuous.cpp |
src/ftinspect/rendering/glyphcontinuous.hpp
|
1
|
+// glyphcontinuous.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include "graphicsdefault.hpp"
|
|
8
|
+#include <QWidget>
|
|
9
|
+#include <freetype/freetype.h>
|
|
10
|
+
|
|
11
|
+class Engine;
|
|
12
|
+class GlyphContinuous
|
|
13
|
+: public QWidget
|
|
14
|
+{
|
|
15
|
+ Q_OBJECT
|
|
16
|
+public:
|
|
17
|
+ GlyphContinuous(QWidget* parent, Engine* engine);
|
|
18
|
+ ~GlyphContinuous() override = default;
|
|
19
|
+
|
|
20
|
+ enum Mode : int
|
|
21
|
+ {
|
|
22
|
+ AllGlyphs,
|
|
23
|
+ TextString
|
|
24
|
+ };
|
|
25
|
+
|
|
26
|
+ enum SubModeAllGlyphs : int
|
|
27
|
+ {
|
|
28
|
+ AG_AllGlyphs,
|
|
29
|
+ AG_Fancy,
|
|
30
|
+ AG_Stroked,
|
|
31
|
+ AG_Waterfall
|
|
32
|
+ };
|
|
33
|
+
|
|
34
|
+ int displayingCount() { return displayingCount_; }
|
|
35
|
+
|
|
36
|
+ // all those setters don't trigger repaint.
|
|
37
|
+ void setBeginIndex(int index) { beginIndex_ = index; }
|
|
38
|
+ void setLimitIndex(int index) { limitIndex_ = index; }
|
|
39
|
+ void setCharMapIndex(int index) { charMapIndex_ = index; }
|
|
40
|
+ void setMode(Mode mode) { mode_ = mode; }
|
|
41
|
+ void setSubModeAllGlyphs(SubModeAllGlyphs modeAg) { modeAG_ = modeAg; }
|
|
42
|
+
|
|
43
|
+signals:
|
|
44
|
+ void wheelNavigate(int steps);
|
|
45
|
+ void wheelResize(int steps);
|
|
46
|
+ void displayingCountUpdated(int newCount);
|
|
47
|
+
|
|
48
|
+protected:
|
|
49
|
+ void paintEvent(QPaintEvent* event) override;
|
|
50
|
+ void wheelEvent(QWheelEvent* event) override;
|
|
51
|
+
|
|
52
|
+private:
|
|
53
|
+ Engine* engine_;
|
|
54
|
+ GraphicsDefault* graphicsDefault_;
|
|
55
|
+
|
|
56
|
+ int beginIndex_;
|
|
57
|
+ int limitIndex_;
|
|
58
|
+ int charMapIndex_;
|
|
59
|
+ Mode mode_ = AllGlyphs;
|
|
60
|
+ SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
|
|
61
|
+
|
|
62
|
+ int displayingCount_ = 0;
|
|
63
|
+ FT_Size_Metrics metrics_;
|
|
64
|
+ int x_ = 0, y_ = 0;
|
|
65
|
+ int stepY_ = 0;
|
|
66
|
+
|
|
67
|
+ void paintAGAllGlyphs(QPainter* painter);
|
|
68
|
+ void prePaint();
|
|
69
|
+ // return if there's enough space to paint the current char
|
|
70
|
+ bool paintChar(QPainter* painter, int index);
|
|
71
|
+
|
|
72
|
+ bool checkFitX(int x);
|
|
73
|
+ bool checkFitY(int y);
|
|
74
|
+};
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+// end of glyphcontinuous.hpp |
src/ftinspect/rendering/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
|
+ // color tables (with suitable opacity values) for converting
|
|
12
|
+ // FreeType's pixmaps to something Qt understands
|
|
13
|
+ monoColorTable.append(QColor(Qt::transparent).rgba());
|
|
14
|
+ monoColorTable.append(QColor(Qt::black).rgba());
|
|
15
|
+
|
|
16
|
+ for (int i = 0xFF; i >= 0; i--)
|
|
17
|
+ grayColorTable.append(qRgba(i, i, i, 0xFF - i));
|
|
18
|
+
|
|
19
|
+ // XXX make this user-configurable
|
|
20
|
+
|
|
21
|
+ axisPen.setColor(Qt::black);
|
|
22
|
+ axisPen.setWidth(0);
|
|
23
|
+ blueZonePen.setColor(QColor(64, 64, 255, 64)); // light blue
|
|
24
|
+ blueZonePen.setWidth(0);
|
|
25
|
+ gridPen.setColor(Qt::lightGray);
|
|
26
|
+ gridPen.setWidth(0);
|
|
27
|
+ offPen.setColor(Qt::darkGreen);
|
|
28
|
+ offPen.setWidth(3);
|
|
29
|
+ onPen.setColor(Qt::red);
|
|
30
|
+ onPen.setWidth(3);
|
|
31
|
+ outlinePen.setColor(Qt::red);
|
|
32
|
+ outlinePen.setWidth(0);
|
|
33
|
+ segmentPen.setColor(QColor(64, 255, 128, 64)); // light green
|
|
34
|
+ segmentPen.setWidth(0);
|
|
35
|
+}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+GraphicsDefault*
|
|
39
|
+GraphicsDefault::deafultInstance()
|
|
40
|
+{
|
|
41
|
+ if (!instance_)
|
|
42
|
+ instance_ = new GraphicsDefault;
|
|
43
|
+
|
|
44
|
+ return instance_;
|
|
45
|
+}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+// end of graphicsdefault.cpp |
src/ftinspect/rendering/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
|
+ QVector<QRgb> grayColorTable;
|
|
15
|
+ QVector<QRgb> monoColorTable;
|
|
16
|
+
|
|
17
|
+ QPen axisPen;
|
|
18
|
+ QPen blueZonePen;
|
|
19
|
+ QPen gridPen;
|
|
20
|
+ QPen offPen;
|
|
21
|
+ QPen onPen;
|
|
22
|
+ QPen outlinePen;
|
|
23
|
+ QPen segmentPen;
|
|
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/rendering/renderutils.cpp
|
1
|
+// renderutils.cpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#include "renderutils.hpp"
|
|
6
|
+
|
|
7
|
+FT_Outline
|
|
8
|
+transformOutlineToOrigin(FT_Library library,
|
|
9
|
+ FT_Outline* outline,
|
|
10
|
+ FT_BBox* outControlBox)
|
|
11
|
+{
|
|
12
|
+ FT_Outline transformed;
|
|
13
|
+ FT_Outline_New(library,
|
|
14
|
+ static_cast<unsigned int>(outline->n_points),
|
|
15
|
+ outline->n_contours, &transformed);
|
|
16
|
+ FT_Outline_Copy(outline, &transformed);
|
|
17
|
+
|
|
18
|
+ FT_BBox cbox;
|
|
19
|
+ FT_Outline_Get_CBox(outline, &cbox);
|
|
20
|
+
|
|
21
|
+ cbox.xMin &= ~63;
|
|
22
|
+ cbox.yMin &= ~63;
|
|
23
|
+ cbox.xMax = (cbox.xMax + 63) & ~63;
|
|
24
|
+ cbox.yMax = (cbox.yMax + 63) & ~63;
|
|
25
|
+ // we shift the outline to the origin for rendering later on
|
|
26
|
+ FT_Outline_Translate(&transformed, -cbox.xMin, -cbox.yMin);
|
|
27
|
+
|
|
28
|
+ if (outControlBox)
|
|
29
|
+ *outControlBox = cbox;
|
|
30
|
+ return transformed;
|
|
31
|
+}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+// end of renderutils.cpp |
src/ftinspect/rendering/renderutils.hpp
|
1
|
+// renderutils.hpp
|
|
2
|
+
|
|
3
|
+// Copyright (C) 2022 by Charlie Jiang.
|
|
4
|
+
|
|
5
|
+#pragma once
|
|
6
|
+
|
|
7
|
+#include <freetype/ftoutln.h>
|
|
8
|
+
|
|
9
|
+// The constructed `outline` must be freed by the caller
|
|
10
|
+FT_Outline transformOutlineToOrigin(FT_Library library,
|
|
11
|
+ FT_Outline* outline,
|
|
12
|
+ FT_BBox* outControlBox);
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+// end of renderutils.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
|
+FontSizeSelector::FontSizeSelector(QWidget* parent)
|
|
10
|
+: QWidget(parent)
|
|
11
|
+{
|
|
12
|
+ createLayout();
|
|
13
|
+ createConnections();
|
|
14
|
+ setDefaults();
|
|
15
|
+}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+double
|
|
19
|
+FontSizeSelector::selectedSize()
|
|
20
|
+{
|
|
21
|
+ return sizeDoubleSpinBox_->value();
|
|
22
|
+}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+FontSizeSelector::Units
|
|
26
|
+FontSizeSelector::selectedUnit()
|
|
27
|
+{
|
|
28
|
+ return static_cast<Units>(unitsComboBox_->currentIndex());
|
|
29
|
+}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+void
|
|
33
|
+FontSizeSelector::applyToEngine(Engine* engine)
|
|
34
|
+{
|
|
35
|
+ // Spinbox value cannot become negative
|
|
36
|
+ engine->setDPI(dpiSpinBox_->value());
|
|
37
|
+
|
|
38
|
+ if (unitsComboBox_->currentIndex() == Units_px)
|
|
39
|
+ engine->setSizeByPixel(sizeDoubleSpinBox_->value());
|
|
40
|
+ else
|
|
41
|
+ engine->setSizeByPoint(sizeDoubleSpinBox_->value());
|
|
42
|
+}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+void
|
|
46
|
+FontSizeSelector::handleWheelResizeBySteps(int steps)
|
|
47
|
+{
|
|
48
|
+ double sizeAfter = sizeDoubleSpinBox_->value() + steps * 0.5;
|
|
49
|
+ sizeAfter = std::max(sizeDoubleSpinBox_->minimum(),
|
|
50
|
+ std::min(sizeAfter, sizeDoubleSpinBox_->maximum()));
|
|
51
|
+ sizeDoubleSpinBox_->setValue(sizeAfter);
|
|
52
|
+}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+void
|
|
56
|
+FontSizeSelector::handleWheelResizeFromGrid(QWheelEvent* event)
|
|
57
|
+{
|
|
58
|
+ int numSteps = event->angleDelta().y() / 120;
|
|
59
|
+ handleWheelResizeBySteps(numSteps);
|
|
60
|
+}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+void
|
|
64
|
+FontSizeSelector::checkUnits()
|
|
65
|
+{
|
|
66
|
+ int index = unitsComboBox_->currentIndex();
|
|
67
|
+
|
|
68
|
+ if (index == Units_px)
|
|
69
|
+ {
|
|
70
|
+ dpiLabel_->setEnabled(false);
|
|
71
|
+ dpiSpinBox_->setEnabled(false);
|
|
72
|
+ sizeDoubleSpinBox_->setSingleStep(1);
|
|
73
|
+
|
|
74
|
+ QSignalBlocker blocker(sizeDoubleSpinBox_);
|
|
75
|
+ sizeDoubleSpinBox_->setValue(qRound(sizeDoubleSpinBox_->value()));
|
|
76
|
+ }
|
|
77
|
+ else
|
|
78
|
+ {
|
|
79
|
+ dpiLabel_->setEnabled(true);
|
|
80
|
+ dpiSpinBox_->setEnabled(true);
|
|
81
|
+ sizeDoubleSpinBox_->setSingleStep(0.5);
|
|
82
|
+ }
|
|
83
|
+
|
|
84
|
+ emit valueChanged();
|
|
85
|
+}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+void
|
|
89
|
+FontSizeSelector::createLayout()
|
|
90
|
+{
|
|
91
|
+ sizeLabel_ = new QLabel(tr("Size "), this);
|
|
92
|
+ sizeLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
93
|
+ sizeDoubleSpinBox_ = new QDoubleSpinBox;
|
|
94
|
+ sizeDoubleSpinBox_->setAlignment(Qt::AlignRight);
|
|
95
|
+ sizeDoubleSpinBox_->setDecimals(1);
|
|
96
|
+ sizeDoubleSpinBox_->setRange(1, 500);
|
|
97
|
+ sizeLabel_->setBuddy(sizeDoubleSpinBox_);
|
|
98
|
+
|
|
99
|
+ unitsComboBox_ = new QComboBox(this);
|
|
100
|
+ unitsComboBox_->insertItem(Units_px, "px");
|
|
101
|
+ unitsComboBox_->insertItem(Units_pt, "pt");
|
|
102
|
+
|
|
103
|
+ dpiLabel_ = new QLabel(tr("DPI "), this);
|
|
104
|
+ dpiLabel_->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
105
|
+ dpiSpinBox_ = new QSpinBox(this);
|
|
106
|
+ dpiSpinBox_->setAlignment(Qt::AlignRight);
|
|
107
|
+ dpiSpinBox_->setRange(10, 600);
|
|
108
|
+ dpiLabel_->setBuddy(dpiSpinBox_);
|
|
109
|
+
|
|
110
|
+ layout_ = new QHBoxLayout;
|
|
111
|
+
|
|
112
|
+ layout_->addStretch(1);
|
|
113
|
+ layout_->addWidget(sizeLabel_);
|
|
114
|
+ layout_->addWidget(sizeDoubleSpinBox_);
|
|
115
|
+ layout_->addWidget(unitsComboBox_);
|
|
116
|
+ layout_->addStretch(1);
|
|
117
|
+ layout_->addWidget(dpiLabel_);
|
|
118
|
+ layout_->addWidget(dpiSpinBox_);
|
|
119
|
+ layout_->addStretch(1);
|
|
120
|
+
|
|
121
|
+ setLayout(layout_);
|
|
122
|
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
123
|
+}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+void
|
|
127
|
+FontSizeSelector::createConnections()
|
|
128
|
+{
|
|
129
|
+ connect(sizeDoubleSpinBox_, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
130
|
+ this, &FontSizeSelector::valueChanged);
|
|
131
|
+ connect(unitsComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
132
|
+ this, &FontSizeSelector::checkUnits);
|
|
133
|
+ connect(dpiSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
|
|
134
|
+ this, &FontSizeSelector::valueChanged);
|
|
135
|
+}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+void
|
|
139
|
+FontSizeSelector::setDefaults()
|
|
140
|
+{
|
|
141
|
+ sizeDoubleSpinBox_->setValue(20);
|
|
142
|
+ dpiSpinBox_->setValue(96);
|
|
143
|
+ checkUnits();
|
|
144
|
+}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+// 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 <QComboBox>
|
|
8
|
+#include <QDoubleSpinBox>
|
|
9
|
+#include <QLabel>
|
|
10
|
+#include <QWidget>
|
|
11
|
+#include <QBoxLayout>
|
|
12
|
+#include <QWheelEvent>
|
|
13
|
+
|
|
14
|
+class Engine;
|
|
15
|
+class FontSizeSelector : public QWidget
|
|
16
|
+{
|
|
17
|
+ Q_OBJECT
|
|
18
|
+
|
|
19
|
+public:
|
|
20
|
+ FontSizeSelector(QWidget* parent);
|
|
21
|
+ ~FontSizeSelector() override = default;
|
|
22
|
+
|
|
23
|
+ enum Units : int
|
|
24
|
+ {
|
|
25
|
+ Units_px,
|
|
26
|
+ Units_pt
|
|
27
|
+ };
|
|
28
|
+
|
|
29
|
+ double selectedSize();
|
|
30
|
+ Units selectedUnit();
|
|
31
|
+
|
|
32
|
+ void applyToEngine(Engine* engine);
|
|
33
|
+ void handleWheelResizeBySteps(int steps);
|
|
34
|
+ void handleWheelResizeFromGrid(QWheelEvent* event);
|
|
35
|
+
|
|
36
|
+signals:
|
|
37
|
+ void valueChanged();
|
|
38
|
+
|
|
39
|
+private slots:
|
|
40
|
+ void checkUnits();
|
|
41
|
+
|
|
42
|
+private:
|
|
43
|
+ QLabel* sizeLabel_;
|
|
44
|
+ QLabel* dpiLabel_;
|
|
45
|
+
|
|
46
|
+ QDoubleSpinBox* sizeDoubleSpinBox_;
|
|
47
|
+ QComboBox* unitsComboBox_;
|
|
48
|
+ QSpinBox* dpiSpinBox_;
|
|
49
|
+
|
|
50
|
+ QHBoxLayout* layout_;
|
|
51
|
+
|
|
52
|
+ void createLayout();
|
|
53
|
+ void createConnections();
|
|
54
|
+ void setDefaults();
|
|
55
|
+};
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+// end of fontsizeselector.hpp |
src/ftinspect/widgets/glyphindexselector.cpp
... |
... |
@@ -6,33 +6,33 @@ |
6
|
6
|
|
7
|
7
|
#include "../uihelper.hpp"
|
8
|
8
|
|
|
9
|
+#include <climits>
|
|
10
|
+
|
9
|
11
|
GlyphIndexSelector::GlyphIndexSelector(QWidget* parent)
|
10
|
12
|
: QWidget(parent)
|
11
|
13
|
{
|
|
14
|
+ numberRenderer_ = &GlyphIndexSelector::renderNumberDefault;
|
|
15
|
+
|
12
|
16
|
createLayout();
|
13
|
17
|
createConnections();
|
|
18
|
+ showingCount_ = 0;
|
14
|
19
|
}
|
15
|
20
|
|
16
|
21
|
|
17
|
22
|
void
|
18
|
|
-GlyphIndexSelector::setMin(int min)
|
|
23
|
+GlyphIndexSelector::setMinMax(int min,
|
|
24
|
+ int max)
|
19
|
25
|
{
|
|
26
|
+ // Don't emit events during setting
|
|
27
|
+ auto eventState = blockSignals(true);
|
20
|
28
|
indexSpinBox_->setMinimum(min);
|
|
29
|
+ indexSpinBox_->setMaximum(qBound(0, max, INT_MAX));
|
21
|
30
|
indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
|
22
|
31
|
indexSpinBox_->value(),
|
23
|
32
|
indexSpinBox_->maximum()));
|
24
|
|
- // spinBoxChanged will be automatically called
|
25
|
|
-}
|
|
33
|
+ blockSignals(eventState);
|
26
|
34
|
|
27
|
|
-
|
28
|
|
-void
|
29
|
|
-GlyphIndexSelector::setMax(int max)
|
30
|
|
-{
|
31
|
|
- indexSpinBox_->setMaximum(max);
|
32
|
|
- indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
|
33
|
|
- indexSpinBox_->value(),
|
34
|
|
- indexSpinBox_->maximum()));
|
35
|
|
- // spinBoxChanged will be automatically called
|
|
35
|
+ updateLabel();
|
36
|
36
|
}
|
37
|
37
|
|
38
|
38
|
|
... |
... |
@@ -55,26 +55,41 @@ GlyphIndexSelector::setSingleMode(bool singleMode) |
55
|
55
|
void
|
56
|
56
|
GlyphIndexSelector::setCurrentIndex(int index, bool forceUpdate)
|
57
|
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);
|
58
|
62
|
indexSpinBox_->setValue(index);
|
59
|
|
- updateLabel();
|
|
63
|
+ blockSignals(state);
|
|
64
|
+
|
60
|
65
|
if (forceUpdate)
|
61
|
66
|
emit currentIndexChanged(indexSpinBox_->value());
|
62
|
67
|
}
|
63
|
68
|
|
64
|
69
|
|
65
|
70
|
int
|
66
|
|
-GlyphIndexSelector::getCurrentIndex()
|
|
71
|
+GlyphIndexSelector::currentIndex()
|
67
|
72
|
{
|
68
|
73
|
return indexSpinBox_->value();
|
69
|
74
|
}
|
70
|
75
|
|
71
|
76
|
|
|
77
|
+void
|
|
78
|
+GlyphIndexSelector::setNumberRenderer(std::function<QString(int)> renderer)
|
|
79
|
+{
|
|
80
|
+ numberRenderer_ = std::move(renderer);
|
|
81
|
+}
|
|
82
|
+
|
|
83
|
+
|
72
|
84
|
void
|
73
|
85
|
GlyphIndexSelector::adjustIndex(int delta)
|
74
|
86
|
{
|
75
|
|
- indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
|
76
|
|
- indexSpinBox_->value() + delta,
|
77
|
|
- indexSpinBox_->maximum()));
|
|
87
|
+ {
|
|
88
|
+ QSignalBlocker blocker(this);
|
|
89
|
+ indexSpinBox_->setValue(qBound(indexSpinBox_->minimum(),
|
|
90
|
+ indexSpinBox_->value() + delta,
|
|
91
|
+ indexSpinBox_->maximum()));
|
|
92
|
+ }
|
78
|
93
|
emitValueChanged();
|
79
|
94
|
}
|
80
|
95
|
|
... |
... |
@@ -92,13 +107,17 @@ GlyphIndexSelector::updateLabel() |
92
|
107
|
{
|
93
|
108
|
if (singleMode_)
|
94
|
109
|
indexLabel_->setText(QString("%1\nLimit: %2")
|
95
|
|
- .arg(indexSpinBox_->value())
|
96
|
|
- .arg(indexSpinBox_->maximum()));
|
|
110
|
+ .arg(numberRenderer_(indexSpinBox_->value()))
|
|
111
|
+ .arg(numberRenderer_(indexSpinBox_->maximum())));
|
97
|
112
|
else
|
98
|
|
- indexLabel_->setText(QString("%1~%2\nCount: %3\nLimit: %4")
|
99
|
|
- .arg(indexSpinBox_->value())
|
100
|
|
- .arg(indexSpinBox_->value() + showingCount_ - 1)
|
101
|
|
- .arg(showingCount_, indexSpinBox_->maximum()));
|
|
113
|
+ indexLabel_->setText(
|
|
114
|
+ QString("%1~%2\nCount: %3\nLimit: %4")
|
|
115
|
+ .arg(numberRenderer_(indexSpinBox_->value()))
|
|
116
|
+ .arg(numberRenderer_(
|
|
117
|
+ qBound(indexSpinBox_->value(),
|
|
118
|
+ indexSpinBox_->value() + showingCount_ - 1, INT_MAX)))
|
|
119
|
+ .arg(showingCount_)
|
|
120
|
+ .arg(numberRenderer_(indexSpinBox_->maximum())));
|
102
|
121
|
}
|
103
|
122
|
|
104
|
123
|
|
... |
... |
@@ -121,9 +140,10 @@ GlyphIndexSelector::createLayout() |
121
|
140
|
indexSpinBox_->setButtonSymbols(QAbstractSpinBox::NoButtons);
|
122
|
141
|
indexSpinBox_->setRange(0, 0);
|
123
|
142
|
indexSpinBox_->setFixedWidth(80);
|
124
|
|
- indexSpinBox_->setWrapping(true);
|
|
143
|
+ indexSpinBox_->setWrapping(false);
|
125
|
144
|
|
126
|
145
|
indexLabel_ = new QLabel("0\nCount: 0\nLimit: 0");
|
|
146
|
+ indexLabel_->setMinimumWidth(200);
|
127
|
147
|
|
128
|
148
|
setButtonNarrowest(toStartButton_);
|
129
|
149
|
setButtonNarrowest(toM1000Button_);
|
... |
... |
@@ -155,6 +175,7 @@ GlyphIndexSelector::createLayout() |
155
|
175
|
navigationLayout_->addWidget(indexLabel_);
|
156
|
176
|
navigationLayout_->addStretch(3);
|
157
|
177
|
|
|
178
|
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
158
|
179
|
setLayout(navigationLayout_);
|
159
|
180
|
}
|
160
|
181
|
|
... |
... |
@@ -201,4 +222,11 @@ GlyphIndexSelector::createConnections() |
201
|
222
|
}
|
202
|
223
|
|
203
|
224
|
|
|
225
|
+QString
|
|
226
|
+GlyphIndexSelector::renderNumberDefault(int i)
|
|
227
|
+{
|
|
228
|
+ return QString::number(i);
|
|
229
|
+}
|
|
230
|
+
|
|
231
|
+
|
204
|
232
|
// end of glyphindexselector.cpp |
src/ftinspect/widgets/glyphindexselector.hpp
... |
... |
@@ -4,6 +4,7 @@ |
4
|
4
|
|
5
|
5
|
#pragma once
|
6
|
6
|
|
|
7
|
+#include <functional>
|
7
|
8
|
#include <QWidget>
|
8
|
9
|
#include <QPushButton>
|
9
|
10
|
#include <QSpinBox>
|
... |
... |
@@ -19,13 +20,15 @@ public: |
19
|
20
|
GlyphIndexSelector(QWidget* parent);
|
20
|
21
|
~GlyphIndexSelector() override = default;
|
21
|
22
|
|
22
|
|
- void setMin(int min);
|
23
|
|
- void setMax(int max);
|
|
23
|
+ // Will never trigger repaint!
|
|
24
|
+ void setMinMax(int min, int max);
|
24
|
25
|
void setShowingCount(int showingCount);
|
25
|
26
|
void setSingleMode(bool singleMode);
|
26
|
27
|
|
27
|
28
|
void setCurrentIndex(int index, bool forceUpdate = false);
|
28
|
|
- int getCurrentIndex();
|
|
29
|
+ int currentIndex();
|
|
30
|
+
|
|
31
|
+ void setNumberRenderer(std::function<QString(int)> renderer);
|
29
|
32
|
|
30
|
33
|
signals:
|
31
|
34
|
void currentIndexChanged(int index);
|
... |
... |
@@ -38,6 +41,7 @@ private slots: |
38
|
41
|
private:
|
39
|
42
|
bool singleMode_ = true;
|
40
|
43
|
int showingCount_;
|
|
44
|
+ std::function<QString(int)> numberRenderer_;
|
41
|
45
|
|
42
|
46
|
// min, max and current status are held by `indexSpinBox_`
|
43
|
47
|
|
... |
... |
@@ -61,6 +65,8 @@ private: |
61
|
65
|
|
62
|
66
|
void createLayout();
|
63
|
67
|
void createConnections();
|
|
68
|
+
|
|
69
|
+ static QString renderNumberDefault(int i);
|
64
|
70
|
};
|
65
|
71
|
|
66
|
72
|
|
|