Commits:
-
b6b79a8a
by Charlie Jiang
at 2022-07-25T16:19:30+08:00
[ftinspect] Improve Font/Face/NI selector.
This commit replaces the 6 buttons switching Font/Face/Named Instance
triplet. Instead, it now squash the triplet selector into three comboboxes
with tiny Previous/Next buttons. All those widgets are lined horizontally
in the bottom the window. This reduces space waste.
Also make tiny changes to the layouts for proper alignment.
* src/ftinspect/widgets/tripletselector.cpp,
src/ftinspect/widgets/tripletselector.hpp: New file, as described.
`TripletSelector` contains all logic for populating the triplets and
calling the engine to load them.
* src/ftinspect/maingui.hpp, src/ftinspect/maingui.cpp: Move out triplet
selection and font loading code. Integrate in `TripletSelector` widget and
adjust the layout. Remove the status bar.
* src/ftinspect/engine/engine.hpp:
Simplify some `FaceID` related code.
Add proper handling of cases when `faceID == -1`.
Add `namedInstanceName` function (also can be used to fetch face name).
TODO: Body of function involving find a face/size object is highly
similar - chances for refactoring?
* src/ftinspect/panels/settingpanel.cpp: Layout change, remove margins.
* src/ftinspect/engine/fontfilemanager.cpp: Fix the bug that deletion isn't
detected in time caused by caching inside `QFileInfo`.
* src/ftinspect/ftinspect.cpp: Remove `MainGUI::setDefaults`.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
-
b9bf1a7c
by Charlie Jiang
at 2022-07-25T16:40:49+08:00
[ftinspect] Properly handling cases of glyph hAdvance == 0
Sometimes the glyph contains no control point thus can't be properly
translated to the pen position. Therefore the correct pen position must
be passed to the drawing callback.
* src/ftinspect/engine/stringrenderer.cpp,
src/ftinspect/engine/stringrenderer.hpp: Pass pen position to the
callback. Adjust the check of control box to allow x, y = 0, 0 cases.
* src/ftinspect/rendering/glyphcontinuous.cpp,
src/ftinspect/rendering/glyphcontinuous.hpp: Use the received pen position
to draw the red square.
-
41550938
by Charlie Jiang
at 2022-07-25T18:23:24+08:00
[ftinspect] Support `Shift-[+-0]` to adjust font size.
Make `Shift+[+-]` adjust Font Size Up/Down, and `Shift-0` will reset size
to the default size. However, numpad 0 is not support due to Qt's handling
of numpad 0.
* src/ftinspect/widgets/fontsizeselector.cpp,
src/ftinspect/widgets/fontsizeselector.hpp: Add event filter functions
to capture the releted keyboard events.
* src/ftinspect/panels/singular.cpp, src/ftinspect/panels/singular.hpp,
src/ftinspect/panels/continuous.cpp,
src/ftinspect/panels/continuous.hpp:
Add `eventFilter` func to handle the delegated key event, pass further
down to `FontSizeSelector`.
Append to the tooltip.
* src/ftinspect/maingui.cpp, src/ftinspect/maingui.hpp: Delegate key event
to the tabs.
-
c24bc38d
by Charlie Jiang
at 2022-07-25T18:49:16+08:00
[ftinspect] Add multiline support for Continuous View.
* src/ftinspect/engine/stringrenderer.cpp,
src/ftinspect/engine/stringrenderer.hpp:
Add multiline support for the string and repeated string mode.
The wrapping of offset is moved to the `render` function.
21 changed files:
Changes:
src/ftinspect/CMakeLists.txt
... |
... |
@@ -37,6 +37,7 @@ add_executable(ftinspect |
37
|
37
|
"widgets/customwidgets.cpp"
|
38
|
38
|
"widgets/glyphindexselector.cpp"
|
39
|
39
|
"widgets/fontsizeselector.cpp"
|
|
40
|
+ "widgets/tripletselector.cpp"
|
40
|
41
|
|
41
|
42
|
"models/customcomboboxmodels.cpp"
|
42
|
43
|
|
src/ftinspect/engine/engine.cpp
... |
... |
@@ -99,13 +99,14 @@ faceRequester(FTC_FaceID ftcFaceID, |
99
|
99
|
if (faceID.fontIndex < 0
|
100
|
100
|
|| faceID.fontIndex >= engine->numberOfOpenedFonts())
|
101
|
101
|
return FT_Err_Invalid_Argument;
|
102
|
|
-
|
|
102
|
+
|
103
|
103
|
QString font = engine->fontFileManager_[faceID.fontIndex].filePath();
|
104
|
104
|
long faceIndex = faceID.faceIndex;
|
105
|
105
|
|
106
|
106
|
if (faceID.namedInstanceIndex > 0)
|
107
|
107
|
faceIndex += faceID.namedInstanceIndex << 16;
|
108
|
108
|
|
|
109
|
+ *faceP = NULL;
|
109
|
110
|
return FT_New_Face(library,
|
110
|
111
|
qPrintable(font),
|
111
|
112
|
faceIndex,
|
... |
... |
@@ -175,11 +176,13 @@ Engine::numberOfFaces(int fontIndex) |
175
|
176
|
FT_Face face;
|
176
|
177
|
long numFaces = -1;
|
177
|
178
|
|
|
179
|
+ if (fontIndex < 0)
|
|
180
|
+ return -1;
|
|
181
|
+
|
|
182
|
+ auto id = FaceID(fontIndex, 0, 0);
|
|
183
|
+
|
178
|
184
|
// search triplet (fontIndex, 0, 0)
|
179
|
|
- FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
|
180
|
|
- (faceIDMap_.value(FaceID(fontIndex,
|
181
|
|
- 0,
|
182
|
|
- 0)));
|
|
185
|
+ FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
183
|
186
|
if (ftcFaceID)
|
184
|
187
|
{
|
185
|
188
|
// found
|
... |
... |
@@ -190,14 +193,13 @@ Engine::numberOfFaces(int fontIndex) |
190
|
193
|
{
|
191
|
194
|
// not found; try to load triplet (fontIndex, 0, 0)
|
192
|
195
|
ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
193
|
|
- faceIDMap_.insert(FaceID(fontIndex, 0, 0),
|
194
|
|
- faceCounter_++);
|
|
196
|
+ faceIDMap_.insert(id, faceCounter_++);
|
195
|
197
|
|
196
|
198
|
if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
|
197
|
199
|
numFaces = face->num_faces;
|
198
|
200
|
else
|
199
|
201
|
{
|
200
|
|
- faceIDMap_.remove(FaceID(fontIndex, 0, 0));
|
|
202
|
+ faceIDMap_.remove(id);
|
201
|
203
|
faceCounter_--;
|
202
|
204
|
}
|
203
|
205
|
}
|
... |
... |
@@ -214,12 +216,12 @@ Engine::numberOfNamedInstances(int fontIndex, |
214
|
216
|
// we return `n' named instances plus one;
|
215
|
217
|
// instance index 0 represents a face without a named instance selected
|
216
|
218
|
int numNamedInstances = -1;
|
|
219
|
+ if (fontIndex < 0)
|
|
220
|
+ return -1;
|
|
221
|
+ auto id = FaceID(fontIndex, faceIndex, 0);
|
217
|
222
|
|
218
|
223
|
// search triplet (fontIndex, faceIndex, 0)
|
219
|
|
- FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
|
220
|
|
- (faceIDMap_.value(FaceID(fontIndex,
|
221
|
|
- faceIndex,
|
222
|
|
- 0)));
|
|
224
|
+ FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
223
|
225
|
if (ftcFaceID)
|
224
|
226
|
{
|
225
|
227
|
// found
|
... |
... |
@@ -230,14 +232,13 @@ Engine::numberOfNamedInstances(int fontIndex, |
230
|
232
|
{
|
231
|
233
|
// not found; try to load triplet (fontIndex, faceIndex, 0)
|
232
|
234
|
ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
233
|
|
- faceIDMap_.insert(FaceID(fontIndex, faceIndex, 0),
|
234
|
|
- faceCounter_++);
|
|
235
|
+ faceIDMap_.insert(id, faceCounter_++);
|
235
|
236
|
|
236
|
237
|
if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
|
237
|
238
|
numNamedInstances = static_cast<int>((face->style_flags >> 16) + 1);
|
238
|
239
|
else
|
239
|
240
|
{
|
240
|
|
- faceIDMap_.remove(FaceID(fontIndex, faceIndex, 0));
|
|
241
|
+ faceIDMap_.remove(id);
|
241
|
242
|
faceCounter_--;
|
242
|
243
|
}
|
243
|
244
|
}
|
... |
... |
@@ -246,6 +247,48 @@ Engine::numberOfNamedInstances(int fontIndex, |
246
|
247
|
}
|
247
|
248
|
|
248
|
249
|
|
|
250
|
+QString
|
|
251
|
+Engine::namedInstanceName(int fontIndex, long faceIndex, int index)
|
|
252
|
+{
|
|
253
|
+ FT_Face face;
|
|
254
|
+ QString name;
|
|
255
|
+
|
|
256
|
+ if (fontIndex < 0)
|
|
257
|
+ return QString();
|
|
258
|
+
|
|
259
|
+ auto id = FaceID(fontIndex, faceIndex, index);
|
|
260
|
+
|
|
261
|
+ // search triplet (fontIndex, faceIndex, 0)
|
|
262
|
+ FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
|
263
|
+ if (ftcFaceID)
|
|
264
|
+ {
|
|
265
|
+ // found
|
|
266
|
+ if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
|
|
267
|
+ name = QString("%1 %2")
|
|
268
|
+ .arg(face->family_name)
|
|
269
|
+ .arg(face->style_name);
|
|
270
|
+ }
|
|
271
|
+ else
|
|
272
|
+ {
|
|
273
|
+ // not found; try to load triplet (fontIndex, faceIndex, 0)
|
|
274
|
+ ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
|
275
|
+ faceIDMap_.insert(id, faceCounter_++);
|
|
276
|
+
|
|
277
|
+ if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
|
|
278
|
+ name = QString("%1 %2")
|
|
279
|
+ .arg(face->family_name)
|
|
280
|
+ .arg(face->style_name);
|
|
281
|
+ else
|
|
282
|
+ {
|
|
283
|
+ faceIDMap_.remove(id);
|
|
284
|
+ faceCounter_--;
|
|
285
|
+ }
|
|
286
|
+ }
|
|
287
|
+
|
|
288
|
+ return name;
|
|
289
|
+}
|
|
290
|
+
|
|
291
|
+
|
249
|
292
|
int
|
250
|
293
|
Engine::currentFontFirstUnicodeCharMap()
|
251
|
294
|
{
|
... |
... |
@@ -267,34 +310,28 @@ Engine::loadFont(int fontIndex, |
267
|
310
|
|
268
|
311
|
update();
|
269
|
312
|
|
|
313
|
+ auto id = FaceID(fontIndex, faceIndex, namedInstanceIndex);
|
|
314
|
+
|
270
|
315
|
// search triplet (fontIndex, faceIndex, namedInstanceIndex)
|
271
|
|
- scaler_.face_id = reinterpret_cast<FTC_FaceID>
|
272
|
|
- (faceIDMap_.value(FaceID(fontIndex,
|
273
|
|
- faceIndex,
|
274
|
|
- namedInstanceIndex)));
|
|
316
|
+ scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
275
|
317
|
if (scaler_.face_id)
|
276
|
318
|
{
|
277
|
319
|
// found
|
278
|
320
|
if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
279
|
321
|
numGlyphs = ftSize_->face->num_glyphs;
|
280
|
322
|
}
|
281
|
|
- else
|
|
323
|
+ else if (fontIndex >= 0)
|
282
|
324
|
{
|
283
|
325
|
// not found; try to load triplet
|
284
|
326
|
// (fontIndex, faceIndex, namedInstanceIndex)
|
285
|
327
|
scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
286
|
|
- faceIDMap_.insert(FaceID(fontIndex,
|
287
|
|
- faceIndex,
|
288
|
|
- namedInstanceIndex),
|
289
|
|
- faceCounter_++);
|
|
328
|
+ faceIDMap_.insert(id, faceCounter_++);
|
290
|
329
|
|
291
|
330
|
if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
292
|
331
|
numGlyphs = ftSize_->face->num_glyphs;
|
293
|
332
|
else
|
294
|
333
|
{
|
295
|
|
- faceIDMap_.remove(FaceID(fontIndex,
|
296
|
|
- faceIndex,
|
297
|
|
- namedInstanceIndex));
|
|
334
|
+ faceIDMap_.remove(id);
|
298
|
335
|
faceCounter_--;
|
299
|
336
|
}
|
300
|
337
|
}
|
src/ftinspect/engine/engine.hpp
... |
... |
@@ -107,17 +107,22 @@ public: |
107
|
107
|
|
108
|
108
|
FT_Library ftLibrary() const { return library_; }
|
109
|
109
|
FTC_Manager cacheManager() { return cacheManager_; }
|
|
110
|
+
|
110
|
111
|
int dpi() { return dpi_; }
|
111
|
112
|
double pointSize() { return pointSize_; }
|
|
113
|
+
|
|
114
|
+ int numberOfOpenedFonts();
|
112
|
115
|
int currentFontType() const { return fontType_; }
|
113
|
116
|
const QString& currentFamilyName() { return curFamilyName_; }
|
114
|
117
|
const QString& currentStyleName() { return curStyleName_; }
|
115
|
118
|
int currentFontNumberOfGlyphs() { return curNumGlyphs_; }
|
116
|
|
- int numberOfOpenedFonts();
|
|
119
|
+
|
117
|
120
|
QString glyphName(int glyphIndex);
|
118
|
121
|
long numberOfFaces(int fontIndex);
|
119
|
122
|
int numberOfNamedInstances(int fontIndex,
|
120
|
123
|
long faceIndex);
|
|
124
|
+ QString namedInstanceName(int fontIndex, long faceIndex, int index);
|
|
125
|
+
|
121
|
126
|
int currentFontFirstUnicodeCharMap();
|
122
|
127
|
// Note: the current font face must be properly set
|
123
|
128
|
unsigned glyphIndexFromCharCode(int code, int charMapIndex);
|
src/ftinspect/engine/fontfilemanager.cpp
... |
... |
@@ -32,6 +32,7 @@ FontFileManager::append(QStringList newFileNames) |
32
|
32
|
for (auto& name : newFileNames)
|
33
|
33
|
{
|
34
|
34
|
auto info = QFileInfo(name);
|
|
35
|
+ info.setCaching(false);
|
35
|
36
|
|
36
|
37
|
// Filter non-file elements
|
37
|
38
|
if (!info.isFile())
|
src/ftinspect/engine/stringrenderer.cpp
... |
... |
@@ -252,7 +252,8 @@ StringRenderer::loadStringGlyphs() |
252
|
252
|
int
|
253
|
253
|
StringRenderer::prepareLine(int offset,
|
254
|
254
|
int lineWidth,
|
255
|
|
- FT_Vector& outActualLineWidth)
|
|
255
|
+ FT_Vector& outActualLineWidth,
|
|
256
|
+ bool handleMultiLine)
|
256
|
257
|
{
|
257
|
258
|
int totalCount = 0;
|
258
|
259
|
outActualLineWidth = {0, 0};
|
... |
... |
@@ -304,6 +305,13 @@ StringRenderer::prepareLine(int offset, |
304
|
305
|
for (unsigned n = offset; n < activeGlyphs_.size();)
|
305
|
306
|
{
|
306
|
307
|
auto& ctx = activeGlyphs_[n];
|
|
308
|
+
|
|
309
|
+ if (handleMultiLine && ctx.charCode == '\n')
|
|
310
|
+ {
|
|
311
|
+ totalCount += 1; // Break here.
|
|
312
|
+ break;
|
|
313
|
+ }
|
|
314
|
+
|
307
|
315
|
if (repeated_) // if repeated, we must stop when we touch the end of line
|
308
|
316
|
{
|
309
|
317
|
if (outActualLineWidth.x + ctx.hadvance.x > lineWidth)
|
... |
... |
@@ -344,7 +352,6 @@ StringRenderer::render(int width, |
344
|
352
|
|
345
|
353
|
// Separated into 3 modes:
|
346
|
354
|
// Waterfall, fill the whole canvas and only single string.
|
347
|
|
-
|
348
|
355
|
if (waterfall_)
|
349
|
356
|
{
|
350
|
357
|
// Waterfall
|
... |
... |
@@ -402,12 +409,18 @@ StringRenderer::render(int width, |
402
|
409
|
|
403
|
410
|
prepareRendering();
|
404
|
411
|
auto& metrics = engine_->currentFontMetrics();
|
405
|
|
- auto stepY = (metrics.height >> 6) + 1;
|
406
|
412
|
auto y = 4 + (metrics.ascender >> 6);
|
|
413
|
+ auto stepY = (metrics.height >> 6) + 1;
|
407
|
414
|
auto limitY = height + (metrics.descender >> 6);
|
408
|
415
|
|
|
416
|
+ // Only care about multiline when in string mode
|
409
|
417
|
for (; y < limitY; y += stepY)
|
410
|
|
- offset = renderLine(0, y, width, height, offset);
|
|
418
|
+ {
|
|
419
|
+ offset = renderLine(0, y, width, height, offset, usingString_);
|
|
420
|
+ // For repeating
|
|
421
|
+ if (usingString_ && repeated_ && !activeGlyphs_.empty())
|
|
422
|
+ offset %= static_cast<int>(activeGlyphs_.size());
|
|
423
|
+ }
|
411
|
424
|
return offset;
|
412
|
425
|
}
|
413
|
426
|
|
... |
... |
@@ -417,8 +430,15 @@ StringRenderer::render(int width, |
417
|
430
|
auto x = static_cast<int>(width * position_);
|
418
|
431
|
// Anchor at top-left in vertical mode, at the center in horizontal mode
|
419
|
432
|
auto y = vertical_ ? 0 : (height / 2);
|
|
433
|
+ auto stepY = (metrics.height >> 6) + 1;
|
420
|
434
|
y += 4 + (metrics.ascender >> 6);
|
421
|
|
- return renderLine(x, y, width, height, offset);
|
|
435
|
+
|
|
436
|
+ while (offset < static_cast<int>(activeGlyphs_.size()))
|
|
437
|
+ {
|
|
438
|
+ offset = renderLine(x, y, width, height, offset, true);
|
|
439
|
+ y += stepY;
|
|
440
|
+ }
|
|
441
|
+ return offset;
|
422
|
442
|
}
|
423
|
443
|
|
424
|
444
|
|
... |
... |
@@ -427,7 +447,8 @@ StringRenderer::renderLine(int x, |
427
|
447
|
int y,
|
428
|
448
|
int width,
|
429
|
449
|
int height,
|
430
|
|
- int offset)
|
|
450
|
+ int offset,
|
|
451
|
+ bool handleMultiLine)
|
431
|
452
|
{
|
432
|
453
|
if (x < 0 || y < 0 || x > width || y > height)
|
433
|
454
|
return 0;
|
... |
... |
@@ -444,7 +465,7 @@ StringRenderer::renderLine(int x, |
444
|
465
|
int lineLength = 64 * (vertical_ ? height : width);
|
445
|
466
|
|
446
|
467
|
// first prepare the line & determine the line length
|
447
|
|
- int totalCount = prepareLine(offset, lineLength, pen);
|
|
468
|
+ int totalCount = prepareLine(offset, lineLength, pen, handleMultiLine);
|
448
|
469
|
|
449
|
470
|
// round to control initial pen position and preserve hinting...
|
450
|
471
|
// pen.x, y is the actual length now, and we multiple it by pos
|
... |
... |
@@ -470,6 +491,8 @@ StringRenderer::renderLine(int x, |
470
|
491
|
for (int i = offset; i < totalCount + offset; i++)
|
471
|
492
|
{
|
472
|
493
|
auto& ctx = activeGlyphs_[i % activeGlyphs_.size()];
|
|
494
|
+ if (handleMultiLine && ctx.charCode == '\n')
|
|
495
|
+ continue; // skip \n
|
473
|
496
|
FT_Glyph image = NULL; // Remember to clean up
|
474
|
497
|
FT_BBox bbox;
|
475
|
498
|
|
... |
... |
@@ -523,27 +546,25 @@ StringRenderer::renderLine(int x, |
523
|
546
|
if (matrixEnabled_)
|
524
|
547
|
FT_Vector_Transform(&advance, &matrix_);
|
525
|
548
|
|
526
|
|
- pen.x += advance.x;
|
527
|
|
- pen.y += advance.y;
|
528
|
|
-
|
529
|
549
|
FT_Glyph_Get_CBox(image, FT_GLYPH_BBOX_PIXELS, &bbox);
|
530
|
550
|
|
531
|
551
|
// check bounding box; if it is completely outside the
|
532
|
552
|
// display surface, we don't need to render it
|
533
|
|
- if (bbox.xMax > 0
|
534
|
|
- && bbox.yMax > 0
|
535
|
|
- && bbox.xMin < width
|
536
|
|
- && bbox.yMin < height)
|
|
553
|
+ if (bbox.xMax >= 0
|
|
554
|
+ && bbox.yMax >= 0
|
|
555
|
+ && bbox.xMin <= width
|
|
556
|
+ && bbox.yMin <= height)
|
537
|
557
|
{
|
538
|
|
- renderCallback_(image);
|
|
558
|
+ FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
|
|
559
|
+ renderCallback_(image, penPos);
|
539
|
560
|
}
|
540
|
561
|
|
|
562
|
+ pen.x += advance.x;
|
|
563
|
+ pen.y += advance.y;
|
|
564
|
+
|
541
|
565
|
FT_Done_Glyph(image);
|
542
|
566
|
}
|
543
|
567
|
|
544
|
|
- // For repeating
|
545
|
|
- if (usingString_ && activeGlyphs_.size())
|
546
|
|
- return (offset + totalCount) % activeGlyphs_.size();
|
547
|
568
|
return offset + totalCount;
|
548
|
569
|
}
|
549
|
570
|
|
src/ftinspect/engine/stringrenderer.hpp
... |
... |
@@ -56,7 +56,12 @@ public: |
56
|
56
|
KM_Smart
|
57
|
57
|
};
|
58
|
58
|
|
59
|
|
- using RenderCallback = std::function<void(FT_Glyph)>;
|
|
59
|
+ /*
|
|
60
|
+ * Need to pass the pen position because sometimes the outline vector
|
|
61
|
+ * contains no points, and thus can't be translated to the desired pen
|
|
62
|
+ * position.
|
|
63
|
+ */
|
|
64
|
+ using RenderCallback = std::function<void(FT_Glyph, FT_Vector)>;
|
60
|
65
|
/*
|
61
|
66
|
* The glyph pointer may be replaced. In that case, ownership is transfered
|
62
|
67
|
* to the renderer, and the new glyph will be eventually freed by
|
... |
... |
@@ -112,7 +117,8 @@ public: |
112
|
117
|
int offset);
|
113
|
118
|
int renderLine(int x, int y,
|
114
|
119
|
int width, int height,
|
115
|
|
- int offset);
|
|
120
|
+ int offset,
|
|
121
|
+ bool handleMultiLine = false);
|
116
|
122
|
|
117
|
123
|
void reloadAll(); // text/font/charmap changes, will call
|
118
|
124
|
// `reloadGlyphs`
|
... |
... |
@@ -178,6 +184,7 @@ private: |
178
|
184
|
// Returns total line count
|
179
|
185
|
int prepareLine(int offset,
|
180
|
186
|
int lineWidth,
|
181
|
|
- FT_Vector& outActualLineWidth);
|
|
187
|
+ FT_Vector& outActualLineWidth,
|
|
188
|
+ bool handleMultiLine = false);
|
182
|
189
|
void clearActive(bool glyphOnly = false);
|
183
|
190
|
}; |
|
|
\ No newline at end of file |
src/ftinspect/ftinspect.cpp
... |
... |
@@ -24,8 +24,6 @@ main(int argc, |
24
|
24
|
Engine engine;
|
25
|
25
|
MainGUI gui(&engine);
|
26
|
26
|
|
27
|
|
- gui.setDefaults();
|
28
|
|
-
|
29
|
27
|
gui.show();
|
30
|
28
|
|
31
|
29
|
return app.exec();
|
src/ftinspect/maingui.cpp
... |
... |
@@ -25,7 +25,6 @@ MainGUI::MainGUI(Engine* engine) |
25
|
25
|
createConnections();
|
26
|
26
|
createActions();
|
27
|
27
|
createMenus();
|
28
|
|
- createStatusBar();
|
29
|
28
|
setupDragDrop();
|
30
|
29
|
|
31
|
30
|
readSettings();
|
... |
... |
@@ -81,6 +80,15 @@ MainGUI::dropEvent(QDropEvent* event) |
81
|
80
|
}
|
82
|
81
|
|
83
|
82
|
|
|
83
|
+void
|
|
84
|
+MainGUI::keyPressEvent(QKeyEvent* event)
|
|
85
|
+{
|
|
86
|
+ // Delegate key events to tabs
|
|
87
|
+ if (!tabWidget_->currentWidget()->eventFilter(this, event))
|
|
88
|
+ QMainWindow::keyPressEvent(event);
|
|
89
|
+}
|
|
90
|
+
|
|
91
|
+
|
84
|
92
|
void
|
85
|
93
|
MainGUI::about()
|
86
|
94
|
{
|
... |
... |
@@ -132,103 +140,13 @@ MainGUI::openFonts(QStringList const& fileNames) |
132
|
140
|
int oldSize = engine_->numberOfOpenedFonts();
|
133
|
141
|
engine_->openFonts(fileNames);
|
134
|
142
|
|
135
|
|
- // if we have new fonts, set the current index to the first new one
|
136
|
|
- if (oldSize < engine_->numberOfOpenedFonts())
|
137
|
|
- currentFontIndex_ = oldSize;
|
138
|
|
-
|
139
|
|
- showFont();
|
140
|
|
-}
|
141
|
|
-
|
142
|
|
-
|
143
|
|
-void
|
144
|
|
-MainGUI::closeFont()
|
145
|
|
-{
|
146
|
|
- if (currentFontIndex_ < engine_->numberOfOpenedFonts())
|
147
|
|
- {
|
148
|
|
- engine_->removeFont(currentFontIndex_);
|
149
|
|
- }
|
150
|
|
-
|
151
|
|
- // show next font after deletion, i.e., retain index if possible
|
152
|
|
- int num = engine_->numberOfOpenedFonts();
|
153
|
|
- if (num)
|
154
|
|
- {
|
155
|
|
- if (currentFontIndex_ >= num)
|
156
|
|
- currentFontIndex_ = num - 1;
|
157
|
|
- }
|
158
|
|
- else
|
159
|
|
- currentFontIndex_ = 0;
|
160
|
|
-
|
161
|
|
- showFont();
|
|
143
|
+ tripletSelector_->repopulateFonts();
|
162
|
144
|
}
|
163
|
145
|
|
164
|
146
|
|
165
|
147
|
void
|
166
|
|
-MainGUI::watchCurrentFont()
|
|
148
|
+MainGUI::onTripletChanged()
|
167
|
149
|
{
|
168
|
|
- showFont();
|
169
|
|
-}
|
170
|
|
-
|
171
|
|
-
|
172
|
|
-void
|
173
|
|
-MainGUI::showFont()
|
174
|
|
-{
|
175
|
|
- // we do lazy computation of FT_Face objects
|
176
|
|
-
|
177
|
|
- if (currentFontIndex_ < engine_->numberOfOpenedFonts())
|
178
|
|
- {
|
179
|
|
- QFileInfo& fileInfo = engine_->fontFileManager()[currentFontIndex_];
|
180
|
|
- QString fontName = fileInfo.fileName();
|
181
|
|
-
|
182
|
|
- engine_->fontFileManager().updateWatching(currentFontIndex_);
|
183
|
|
- if (fileInfo.isSymLink())
|
184
|
|
- {
|
185
|
|
- fontName.prepend("<i>");
|
186
|
|
- fontName.append("</i>");
|
187
|
|
- }
|
188
|
|
-
|
189
|
|
- if (!fileInfo.exists())
|
190
|
|
- {
|
191
|
|
- // On Unix-like systems, the symlink's target gets opened; this
|
192
|
|
- // implies that deletion of a symlink doesn't make `engine->loadFont'
|
193
|
|
- // fail since it operates on a file handle pointing to the target.
|
194
|
|
- // For this reason, we remove the font to enforce a reload.
|
195
|
|
- engine_->removeFont(currentFontIndex_, false);
|
196
|
|
- }
|
197
|
|
-
|
198
|
|
- fontFilenameLabel_->setText(fontName);
|
199
|
|
- }
|
200
|
|
- else
|
201
|
|
- fontFilenameLabel_->clear();
|
202
|
|
-
|
203
|
|
- syncSettings();
|
204
|
|
- currentNumberOfFaces_
|
205
|
|
- = engine_->numberOfFaces(currentFontIndex_);
|
206
|
|
- currentNumberOfNamedInstances_
|
207
|
|
- = engine_->numberOfNamedInstances(currentFontIndex_,
|
208
|
|
- currentFaceIndex_);
|
209
|
|
- currentNumberOfGlyphs_
|
210
|
|
- = engine_->loadFont(currentFontIndex_,
|
211
|
|
- currentFaceIndex_,
|
212
|
|
- currentNamedInstanceIndex_);
|
213
|
|
-
|
214
|
|
- if (currentNumberOfGlyphs_ < 0)
|
215
|
|
- {
|
216
|
|
- // there might be various reasons why the current
|
217
|
|
- // (file, face, instance) triplet is invalid or missing;
|
218
|
|
- // we thus start our timer to periodically test
|
219
|
|
- // whether the font starts working
|
220
|
|
- if (currentFontIndex_ > 0
|
221
|
|
- && currentFontIndex_ < engine_->numberOfOpenedFonts())
|
222
|
|
- engine_->fontFileManager().timerStart();
|
223
|
|
- }
|
224
|
|
-
|
225
|
|
- fontNameLabel_->setText(QString("%1 %2")
|
226
|
|
- .arg(engine_->currentFamilyName())
|
227
|
|
- .arg(engine_->currentStyleName()));
|
228
|
|
-
|
229
|
|
- checkCurrentFontIndex();
|
230
|
|
- checkCurrentFaceIndex();
|
231
|
|
- checkCurrentNamedInstanceIndex();
|
232
|
150
|
auto state = settingPanel_->blockSignals(true);
|
233
|
151
|
settingPanel_->checkHinting();
|
234
|
152
|
settingPanel_->blockSignals(state);
|
... |
... |
@@ -247,6 +165,7 @@ MainGUI::repaintCurrentTab() |
247
|
165
|
void
|
248
|
166
|
MainGUI::reloadCurrentTabFont()
|
249
|
167
|
{
|
|
168
|
+ syncSettings();
|
250
|
169
|
tabs_[tabWidget_->currentIndex()]->reloadFont();
|
251
|
170
|
}
|
252
|
171
|
|
... |
... |
@@ -258,180 +177,17 @@ MainGUI::syncSettings() |
258
|
177
|
}
|
259
|
178
|
|
260
|
179
|
|
261
|
|
-void
|
262
|
|
-MainGUI::clearStatusBar()
|
263
|
|
-{
|
264
|
|
- statusBar()->clearMessage();
|
265
|
|
- statusBar()->setStyleSheet("");
|
266
|
|
-}
|
267
|
|
-
|
268
|
|
-
|
269
|
|
-void
|
270
|
|
-MainGUI::checkCurrentFontIndex()
|
271
|
|
-{
|
272
|
|
- if (engine_->numberOfOpenedFonts() < 2)
|
273
|
|
- {
|
274
|
|
- previousFontButton_->setEnabled(false);
|
275
|
|
- nextFontButton_->setEnabled(false);
|
276
|
|
- }
|
277
|
|
- else if (currentFontIndex_ == 0)
|
278
|
|
- {
|
279
|
|
- previousFontButton_->setEnabled(false);
|
280
|
|
- nextFontButton_->setEnabled(true);
|
281
|
|
- }
|
282
|
|
- else if (currentFontIndex_ >= engine_->numberOfOpenedFonts() - 1)
|
283
|
|
- {
|
284
|
|
- previousFontButton_->setEnabled(true);
|
285
|
|
- nextFontButton_->setEnabled(false);
|
286
|
|
- }
|
287
|
|
- else
|
288
|
|
- {
|
289
|
|
- previousFontButton_->setEnabled(true);
|
290
|
|
- nextFontButton_->setEnabled(true);
|
291
|
|
- }
|
292
|
|
-}
|
293
|
|
-
|
294
|
|
-
|
295
|
|
-void
|
296
|
|
-MainGUI::checkCurrentFaceIndex()
|
297
|
|
-{
|
298
|
|
- if (currentNumberOfFaces_ < 2)
|
299
|
|
- {
|
300
|
|
- previousFaceButton_->setEnabled(false);
|
301
|
|
- nextFaceButton_->setEnabled(false);
|
302
|
|
- }
|
303
|
|
- else if (currentFaceIndex_ == 0)
|
304
|
|
- {
|
305
|
|
- previousFaceButton_->setEnabled(false);
|
306
|
|
- nextFaceButton_->setEnabled(true);
|
307
|
|
- }
|
308
|
|
- else if (currentFaceIndex_ >= currentNumberOfFaces_ - 1)
|
309
|
|
- {
|
310
|
|
- previousFaceButton_->setEnabled(true);
|
311
|
|
- nextFaceButton_->setEnabled(false);
|
312
|
|
- }
|
313
|
|
- else
|
314
|
|
- {
|
315
|
|
- previousFaceButton_->setEnabled(true);
|
316
|
|
- nextFaceButton_->setEnabled(true);
|
317
|
|
- }
|
318
|
|
-}
|
319
|
|
-
|
320
|
|
-
|
321
|
|
-void
|
322
|
|
-MainGUI::checkCurrentNamedInstanceIndex()
|
323
|
|
-{
|
324
|
|
- if (currentNumberOfNamedInstances_ < 2)
|
325
|
|
- {
|
326
|
|
- previousNamedInstanceButton_->setEnabled(false);
|
327
|
|
- nextNamedInstanceButton_->setEnabled(false);
|
328
|
|
- }
|
329
|
|
- else if (currentNamedInstanceIndex_ == 0)
|
330
|
|
- {
|
331
|
|
- previousNamedInstanceButton_->setEnabled(false);
|
332
|
|
- nextNamedInstanceButton_->setEnabled(true);
|
333
|
|
- }
|
334
|
|
- else if (currentNamedInstanceIndex_ >= currentNumberOfNamedInstances_ - 1)
|
335
|
|
- {
|
336
|
|
- previousNamedInstanceButton_->setEnabled(true);
|
337
|
|
- nextNamedInstanceButton_->setEnabled(false);
|
338
|
|
- }
|
339
|
|
- else
|
340
|
|
- {
|
341
|
|
- previousNamedInstanceButton_->setEnabled(true);
|
342
|
|
- nextNamedInstanceButton_->setEnabled(true);
|
343
|
|
- }
|
344
|
|
-}
|
345
|
|
-
|
346
|
|
-
|
347
|
|
-void
|
348
|
|
-MainGUI::previousFont()
|
349
|
|
-{
|
350
|
|
- if (currentFontIndex_ > 0)
|
351
|
|
- {
|
352
|
|
- currentFontIndex_--;
|
353
|
|
- currentFaceIndex_ = 0;
|
354
|
|
- currentNamedInstanceIndex_ = 0;
|
355
|
|
- showFont();
|
356
|
|
- }
|
357
|
|
-}
|
358
|
|
-
|
359
|
|
-
|
360
|
|
-void
|
361
|
|
-MainGUI::nextFont()
|
362
|
|
-{
|
363
|
|
- if (currentFontIndex_ < engine_->numberOfOpenedFonts() - 1)
|
364
|
|
- {
|
365
|
|
- currentFontIndex_++;
|
366
|
|
- currentFaceIndex_ = 0;
|
367
|
|
- currentNamedInstanceIndex_ = 0;
|
368
|
|
- showFont();
|
369
|
|
- }
|
370
|
|
-}
|
371
|
|
-
|
372
|
|
-
|
373
|
|
-void
|
374
|
|
-MainGUI::previousFace()
|
375
|
|
-{
|
376
|
|
- if (currentFaceIndex_ > 0)
|
377
|
|
- {
|
378
|
|
- currentFaceIndex_--;
|
379
|
|
- currentNamedInstanceIndex_ = 0;
|
380
|
|
- showFont();
|
381
|
|
- }
|
382
|
|
-}
|
383
|
|
-
|
384
|
|
-
|
385
|
|
-void
|
386
|
|
-MainGUI::nextFace()
|
387
|
|
-{
|
388
|
|
- if (currentFaceIndex_ < currentNumberOfFaces_ - 1)
|
389
|
|
- {
|
390
|
|
- currentFaceIndex_++;
|
391
|
|
- currentNamedInstanceIndex_ = 0;
|
392
|
|
- showFont();
|
393
|
|
- }
|
394
|
|
-}
|
395
|
|
-
|
396
|
|
-
|
397
|
|
-void
|
398
|
|
-MainGUI::previousNamedInstance()
|
399
|
|
-{
|
400
|
|
- if (currentNamedInstanceIndex_ > 0)
|
401
|
|
- {
|
402
|
|
- currentNamedInstanceIndex_--;
|
403
|
|
- showFont();
|
404
|
|
- }
|
405
|
|
-}
|
406
|
|
-
|
407
|
|
-
|
408
|
|
-void
|
409
|
|
-MainGUI::nextNamedInstance()
|
410
|
|
-{
|
411
|
|
- if (currentNamedInstanceIndex_ < currentNumberOfNamedInstances_ - 1)
|
412
|
|
- {
|
413
|
|
- currentNamedInstanceIndex_++;
|
414
|
|
- showFont();
|
415
|
|
- }
|
416
|
|
-}
|
417
|
|
-
|
418
|
|
-
|
419
|
180
|
// XXX distances are specified in pixels,
|
420
|
181
|
// making the layout dependent on the output device resolution
|
421
|
182
|
void
|
422
|
183
|
MainGUI::createLayout()
|
423
|
184
|
{
|
424
|
185
|
// left side
|
425
|
|
- fontFilenameLabel_ = new QLabel(this);
|
426
|
|
-
|
427
|
|
- infoLeftLayout_ = new QHBoxLayout;
|
428
|
|
- infoLeftLayout_->addWidget(fontFilenameLabel_);
|
429
|
|
-
|
430
|
186
|
settingPanel_ = new SettingPanel(this, engine_);
|
431
|
187
|
|
432
|
|
- leftLayout_ = new QVBoxLayout;
|
433
|
|
- leftLayout_->addLayout(infoLeftLayout_);
|
|
188
|
+ leftLayout_ = new QVBoxLayout; // The only point is to set a margin->remove?
|
434
|
189
|
leftLayout_->addWidget(settingPanel_);
|
|
190
|
+ leftLayout_->setContentsMargins(32, 32, 8, 16);
|
435
|
191
|
|
436
|
192
|
// we don't want to expand the left side horizontally;
|
437
|
193
|
// to change the policy we have to use a widget wrapper
|
... |
... |
@@ -446,8 +202,6 @@ MainGUI::createLayout() |
446
|
202
|
leftWidget_->setSizePolicy(leftWidgetPolicy);
|
447
|
203
|
|
448
|
204
|
// right side
|
449
|
|
- fontNameLabel_ = new QLabel(this);
|
450
|
|
-
|
451
|
205
|
singularTab_ = new SingularTab(this, engine_);
|
452
|
206
|
continuousTab_ = new ContinuousTab(this, engine_);
|
453
|
207
|
|
... |
... |
@@ -458,43 +212,33 @@ MainGUI::createLayout() |
458
|
212
|
tabWidget_->addTab(singularTab_, tr("Singular Grid View"));
|
459
|
213
|
tabs_.append(continuousTab_);
|
460
|
214
|
tabWidget_->addTab(continuousTab_, tr("Continuous View"));
|
461
|
|
-
|
462
|
|
- previousFontButton_ = new QPushButton(tr("Previous Font"), this);
|
463
|
|
- nextFontButton_ = new QPushButton(tr("Next Font"), this);
|
464
|
|
- previousFaceButton_ = new QPushButton(tr("Previous Face"), this);
|
465
|
|
- nextFaceButton_ = new QPushButton(tr("Next Face"), this);
|
466
|
|
- previousNamedInstanceButton_
|
467
|
|
- = new QPushButton(tr("Previous Named Instance"), this);
|
468
|
|
- nextNamedInstanceButton_ = new QPushButton(tr("Next Named Instance"), this);
|
469
|
|
-
|
470
|
|
- fontLayout = new QGridLayout;
|
471
|
|
- fontLayout->setColumnStretch(0, 2);
|
472
|
|
- fontLayout->addWidget(nextFontButton_, 0, 1);
|
473
|
|
- fontLayout->addWidget(previousFontButton_, 1, 1);
|
474
|
|
- fontLayout->setColumnStretch(2, 1);
|
475
|
|
- fontLayout->addWidget(nextFaceButton_, 0, 3);
|
476
|
|
- fontLayout->addWidget(previousFaceButton_, 1, 3);
|
477
|
|
- fontLayout->setColumnStretch(4, 1);
|
478
|
|
- fontLayout->addWidget(nextNamedInstanceButton_, 0, 5);
|
479
|
|
- fontLayout->addWidget(previousNamedInstanceButton_, 1, 5);
|
480
|
|
- fontLayout->setColumnStretch(6, 2);
|
|
215
|
+
|
|
216
|
+ tripletSelector_ = new TripletSelector(this, engine_);
|
481
|
217
|
|
482
|
218
|
rightLayout_ = new QVBoxLayout;
|
483
|
|
- rightLayout_->addWidget(fontNameLabel_);
|
484
|
|
- rightLayout_->addWidget(tabWidget_);
|
485
|
|
- rightLayout_->addLayout(fontLayout);
|
|
219
|
+ //rightLayout_->addWidget(fontNameLabel_);
|
|
220
|
+ rightLayout_->addWidget(tabWidget_); // same for `leftLayout_`: Remove?
|
|
221
|
+ rightLayout_->setContentsMargins(8, 32, 32, 16);
|
486
|
222
|
|
487
|
223
|
// for symmetry with the left side use a widget also
|
488
|
224
|
rightWidget_ = new QWidget(this);
|
489
|
225
|
rightWidget_->setLayout(rightLayout_);
|
490
|
226
|
|
491
|
227
|
// the whole thing
|
492
|
|
- ftinspectLayout_ = new QHBoxLayout;
|
493
|
|
- ftinspectLayout_->addWidget(leftWidget_);
|
494
|
|
- ftinspectLayout_->addWidget(rightWidget_);
|
|
228
|
+ mainPartLayout_ = new QHBoxLayout;
|
|
229
|
+ mainPartLayout_->addWidget(leftWidget_);
|
|
230
|
+ mainPartLayout_->addWidget(rightWidget_);
|
|
231
|
+
|
|
232
|
+ ftinspectLayout_ = new QVBoxLayout;
|
|
233
|
+ ftinspectLayout_->setSpacing(0);
|
|
234
|
+ ftinspectLayout_->addLayout(mainPartLayout_);
|
|
235
|
+ ftinspectLayout_->addWidget(tripletSelector_);
|
|
236
|
+ ftinspectLayout_->setContentsMargins(0, 0, 0, 0);
|
495
|
237
|
|
496
|
238
|
ftinspectWidget_ = new QWidget(this);
|
497
|
239
|
ftinspectWidget_->setLayout(ftinspectLayout_);
|
|
240
|
+
|
|
241
|
+ statusBar()->hide(); // remove the extra space
|
498
|
242
|
setCentralWidget(ftinspectWidget_);
|
499
|
243
|
setWindowTitle("ftinspect");
|
500
|
244
|
}
|
... |
... |
@@ -504,28 +248,15 @@ void |
504
|
248
|
MainGUI::createConnections()
|
505
|
249
|
{
|
506
|
250
|
connect(settingPanel_, &SettingPanel::fontReloadNeeded,
|
507
|
|
- this, &MainGUI::showFont);
|
|
251
|
+ this, &MainGUI::reloadCurrentTabFont);
|
508
|
252
|
connect(settingPanel_, &SettingPanel::repaintNeeded,
|
509
|
253
|
this, &MainGUI::repaintCurrentTab);
|
510
|
254
|
|
511
|
255
|
connect(tabWidget_, &QTabWidget::currentChanged,
|
512
|
256
|
this, &MainGUI::reloadCurrentTabFont);
|
513
|
257
|
|
514
|
|
- connect(previousFontButton_, &QPushButton::clicked,
|
515
|
|
- this, &MainGUI::previousFont);
|
516
|
|
- connect(nextFontButton_, &QPushButton::clicked,
|
517
|
|
- this, &MainGUI::nextFont);
|
518
|
|
- connect(previousFaceButton_, &QPushButton::clicked,
|
519
|
|
- this, &MainGUI::previousFace);
|
520
|
|
- connect(nextFaceButton_, &QPushButton::clicked,
|
521
|
|
- this, &MainGUI::nextFace);
|
522
|
|
- connect(previousNamedInstanceButton_, &QPushButton::clicked,
|
523
|
|
- this, &MainGUI::previousNamedInstance);
|
524
|
|
- connect(nextNamedInstanceButton_, &QPushButton::clicked,
|
525
|
|
- this, &MainGUI::nextNamedInstance);
|
526
|
|
-
|
527
|
|
- connect(&engine_->fontFileManager(), &FontFileManager::currentFileChanged,
|
528
|
|
- this, &MainGUI::watchCurrentFont);
|
|
258
|
+ connect(tripletSelector_, &TripletSelector::tripletChanged,
|
|
259
|
+ this, &MainGUI::onTripletChanged);
|
529
|
260
|
}
|
530
|
261
|
|
531
|
262
|
|
... |
... |
@@ -538,7 +269,8 @@ MainGUI::createActions() |
538
|
269
|
|
539
|
270
|
closeFontAct_ = new QAction(tr("&Close Font"), this);
|
540
|
271
|
closeFontAct_->setShortcuts(QKeySequence::Close);
|
541
|
|
- connect(closeFontAct_, &QAction::triggered, this, &MainGUI::closeFont);
|
|
272
|
+ connect(closeFontAct_, &QAction::triggered,
|
|
273
|
+ tripletSelector_, &TripletSelector::closeCurrentFont);
|
542
|
274
|
|
543
|
275
|
exitAct_ = new QAction(tr("E&xit"), this);
|
544
|
276
|
exitAct_->setShortcuts(QKeySequence::Quit);
|
... |
... |
@@ -566,13 +298,6 @@ MainGUI::createMenus() |
566
|
298
|
}
|
567
|
299
|
|
568
|
300
|
|
569
|
|
-void
|
570
|
|
-MainGUI::createStatusBar()
|
571
|
|
-{
|
572
|
|
- statusBar()->showMessage("");
|
573
|
|
-}
|
574
|
|
-
|
575
|
|
-
|
576
|
301
|
void
|
577
|
302
|
MainGUI::setupDragDrop()
|
578
|
303
|
{
|
... |
... |
@@ -580,20 +305,6 @@ MainGUI::setupDragDrop() |
580
|
305
|
}
|
581
|
306
|
|
582
|
307
|
|
583
|
|
-void
|
584
|
|
-MainGUI::setDefaults()
|
585
|
|
-{
|
586
|
|
- // the next four values always non-negative
|
587
|
|
- currentFontIndex_ = 0;
|
588
|
|
- currentFaceIndex_ = 0;
|
589
|
|
- currentNamedInstanceIndex_ = 0;
|
590
|
|
-
|
591
|
|
- checkCurrentFontIndex();
|
592
|
|
- checkCurrentFaceIndex();
|
593
|
|
- checkCurrentNamedInstanceIndex();
|
594
|
|
-}
|
595
|
|
-
|
596
|
|
-
|
597
|
308
|
void
|
598
|
309
|
MainGUI::readSettings()
|
599
|
310
|
{
|
src/ftinspect/maingui.hpp
... |
... |
@@ -6,9 +6,7 @@ |
6
|
6
|
#pragma once
|
7
|
7
|
|
8
|
8
|
#include "engine/engine.hpp"
|
9
|
|
-#include "widgets/customwidgets.hpp"
|
10
|
|
-#include "widgets/glyphindexselector.hpp"
|
11
|
|
-#include "models/customcomboboxmodels.hpp"
|
|
9
|
+#include "widgets/tripletselector.hpp"
|
12
|
10
|
#include "panels/settingpanel.hpp"
|
13
|
11
|
#include "panels/singular.hpp"
|
14
|
12
|
#include "panels/continuous.hpp"
|
... |
... |
@@ -53,8 +51,6 @@ public: |
53
|
51
|
MainGUI(Engine* engine);
|
54
|
52
|
~MainGUI() override;
|
55
|
53
|
|
56
|
|
- void setDefaults();
|
57
|
|
-
|
58
|
54
|
friend class Engine;
|
59
|
55
|
friend FT_Error faceRequester(FTC_FaceID,
|
60
|
56
|
FT_Library,
|
... |
... |
@@ -65,37 +61,19 @@ protected: |
65
|
61
|
void closeEvent(QCloseEvent*) override;
|
66
|
62
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
67
|
63
|
void dropEvent(QDropEvent* event) override;
|
|
64
|
+ void keyPressEvent(QKeyEvent* event) override;
|
68
|
65
|
|
69
|
66
|
private slots:
|
70
|
67
|
void about();
|
71
|
68
|
void aboutQt();
|
72
|
|
- void checkCurrentFaceIndex();
|
73
|
|
- void checkCurrentFontIndex();
|
74
|
|
- void checkCurrentNamedInstanceIndex();
|
75
|
|
- void closeFont();
|
76
|
|
- void showFont();
|
77
|
69
|
void repaintCurrentTab();
|
78
|
70
|
void reloadCurrentTabFont();
|
79
|
71
|
void loadFonts();
|
80
|
|
- void nextFace();
|
81
|
|
- void nextFont();
|
82
|
|
- void nextNamedInstance();
|
83
|
|
- void previousFace();
|
84
|
|
- void previousFont();
|
85
|
|
- void previousNamedInstance();
|
86
|
|
- void watchCurrentFont();
|
|
72
|
+ void onTripletChanged();
|
87
|
73
|
|
88
|
74
|
private:
|
89
|
75
|
Engine* engine_;
|
90
|
76
|
|
91
|
|
- int currentFontIndex_;
|
92
|
|
-
|
93
|
|
- long currentNumberOfFaces_;
|
94
|
|
- long currentFaceIndex_;
|
95
|
|
-
|
96
|
|
- int currentNumberOfNamedInstances_;
|
97
|
|
- int currentNamedInstanceIndex_;
|
98
|
|
-
|
99
|
77
|
int currentNumberOfGlyphs_;
|
100
|
78
|
|
101
|
79
|
// layout related stuff
|
... |
... |
@@ -105,25 +83,15 @@ private: |
105
|
83
|
QAction *exitAct_;
|
106
|
84
|
QAction *loadFontsAct_;
|
107
|
85
|
|
108
|
|
- QGridLayout *fontLayout;
|
109
|
|
-
|
110
|
|
- QHBoxLayout *ftinspectLayout_;
|
111
|
|
- QHBoxLayout *infoLeftLayout_;
|
112
|
|
-
|
113
|
|
- QLabel *fontFilenameLabel_;
|
114
|
|
- QLabel *fontNameLabel_;
|
|
86
|
+ QVBoxLayout *ftinspectLayout_;
|
|
87
|
+ QHBoxLayout *mainPartLayout_;
|
115
|
88
|
|
116
|
89
|
QLocale *locale_;
|
117
|
90
|
|
118
|
91
|
QMenu *menuFile_;
|
119
|
92
|
QMenu *menuHelp_;
|
120
|
|
-
|
121
|
|
- QPushButton *nextFaceButton_;
|
122
|
|
- QPushButton *nextFontButton_;
|
123
|
|
- QPushButton *nextNamedInstanceButton_;
|
124
|
|
- QPushButton *previousFaceButton_;
|
125
|
|
- QPushButton *previousFontButton_;
|
126
|
|
- QPushButton *previousNamedInstanceButton_;
|
|
93
|
+
|
|
94
|
+ TripletSelector* tripletSelector_;
|
127
|
95
|
|
128
|
96
|
QVBoxLayout *leftLayout_;
|
129
|
97
|
QVBoxLayout *rightLayout_;
|
... |
... |
@@ -142,13 +110,11 @@ private: |
142
|
110
|
void openFonts(QStringList const& fileNames);
|
143
|
111
|
|
144
|
112
|
void syncSettings();
|
145
|
|
- void clearStatusBar();
|
146
|
113
|
|
147
|
114
|
void createActions();
|
148
|
115
|
void createConnections();
|
149
|
116
|
void createLayout();
|
150
|
117
|
void createMenus();
|
151
|
|
- void createStatusBar();
|
152
|
118
|
void setupDragDrop();
|
153
|
119
|
|
154
|
120
|
void readSettings();
|
src/ftinspect/meson.build
... |
... |
@@ -36,6 +36,7 @@ if qt5_dep.found() |
36
|
36
|
'widgets/customwidgets.cpp',
|
37
|
37
|
'widgets/glyphindexselector.cpp',
|
38
|
38
|
'widgets/fontsizeselector.cpp',
|
|
39
|
+ 'widgets/tripletselector.cpp',
|
39
|
40
|
|
40
|
41
|
'models/customcomboboxmodels.cpp',
|
41
|
42
|
|
... |
... |
@@ -54,6 +55,7 @@ if qt5_dep.found() |
54
|
55
|
'widgets/customwidgets.hpp',
|
55
|
56
|
'widgets/glyphindexselector.hpp',
|
56
|
57
|
'widgets/fontsizeselector.hpp',
|
|
58
|
+ 'widgets/tripletselector.hpp',
|
57
|
59
|
'rendering/glyphcontinuous.hpp',
|
58
|
60
|
'models/customcomboboxmodels.hpp',
|
59
|
61
|
'panels/settingpanel.hpp',
|
src/ftinspect/panels/continuous.cpp
... |
... |
@@ -250,6 +250,20 @@ ContinuousTab::reloadGlyphsAndRepaint() |
250
|
250
|
}
|
251
|
251
|
|
252
|
252
|
|
|
253
|
+bool
|
|
254
|
+ContinuousTab::eventFilter(QObject* watched,
|
|
255
|
+ QEvent* event)
|
|
256
|
+{
|
|
257
|
+ if (event->type() == QEvent::KeyPress)
|
|
258
|
+ {
|
|
259
|
+ auto keyEvent = dynamic_cast<QKeyEvent*>(event);
|
|
260
|
+ if (sizeSelector_->handleKeyEvent(keyEvent))
|
|
261
|
+ return true;
|
|
262
|
+ }
|
|
263
|
+ return false;
|
|
264
|
+}
|
|
265
|
+
|
|
266
|
+
|
253
|
267
|
void
|
254
|
268
|
ContinuousTab::wheelNavigate(int steps)
|
255
|
269
|
{
|
... |
... |
@@ -431,6 +445,9 @@ ContinuousTab::createConnections() |
431
|
445
|
|
432
|
446
|
connect(positionSlider_, &QSlider::valueChanged,
|
433
|
447
|
this, &ContinuousTab::repaintGlyph);
|
|
448
|
+
|
|
449
|
+ sizeSelector_->installEventFilterForWidget(canvas_);
|
|
450
|
+ sizeSelector_->installEventFilterForWidget(this);
|
434
|
451
|
}
|
435
|
452
|
|
436
|
453
|
|
src/ftinspect/panels/continuous.hpp
... |
... |
@@ -49,6 +49,9 @@ public: |
49
|
49
|
void sourceTextChanged();
|
50
|
50
|
void reloadGlyphsAndRepaint();
|
51
|
51
|
|
|
52
|
+protected:
|
|
53
|
+ bool eventFilter(QObject* watched, QEvent* event) override;
|
|
54
|
+
|
52
|
55
|
private slots:
|
53
|
56
|
void wheelNavigate(int steps);
|
54
|
57
|
void wheelResize(int steps);
|
src/ftinspect/panels/settingpanel.cpp
... |
... |
@@ -358,6 +358,8 @@ SettingPanel::createLayout() |
358
|
358
|
mainLayout_ = new QVBoxLayout;
|
359
|
359
|
mainLayout_->addWidget(tab_);
|
360
|
360
|
setLayout(mainLayout_);
|
|
361
|
+ mainLayout_->setContentsMargins(0, 0, 0, 0);
|
|
362
|
+ setContentsMargins(0, 0, 0, 0);
|
361
|
363
|
}
|
362
|
364
|
|
363
|
365
|
|
src/ftinspect/panels/singular.cpp
... |
... |
@@ -212,11 +212,27 @@ SingularTab::showToolTip() |
212
|
212
|
tr("Scroll: Grid Up/Down\n"
|
213
|
213
|
"Alt + Scroll: Grid Left/Right\n"
|
214
|
214
|
"Ctrl + Scroll: Adjust Zoom (Relative to cursor)\n"
|
215
|
|
- "Shift + Scroll: Adjust Font Size"),
|
|
215
|
+ "Shift + Scroll: Adjust Font Size\n"
|
|
216
|
+ "Shift + Plus/Minus: Adjust Font Size\n"
|
|
217
|
+ "Shift + 0: Reset Font Size to Default"),
|
216
|
218
|
helpButton_);
|
217
|
219
|
}
|
218
|
220
|
|
219
|
221
|
|
|
222
|
+bool
|
|
223
|
+SingularTab::eventFilter(QObject* watched,
|
|
224
|
+ QEvent* event)
|
|
225
|
+{
|
|
226
|
+ if (event->type() == QEvent::KeyPress)
|
|
227
|
+ {
|
|
228
|
+ auto keyEvent = dynamic_cast<QKeyEvent*>(event);
|
|
229
|
+ if (sizeSelector_->handleKeyEvent(keyEvent))
|
|
230
|
+ return true;
|
|
231
|
+ }
|
|
232
|
+ return false;
|
|
233
|
+}
|
|
234
|
+
|
|
235
|
+
|
220
|
236
|
void
|
221
|
237
|
SingularTab::createLayout()
|
222
|
238
|
{
|
... |
... |
@@ -357,6 +373,9 @@ SingularTab::createConnections() |
357
|
373
|
this, &SingularTab::drawGlyph);
|
358
|
374
|
connect(showGridCheckBox_, &QCheckBox::clicked,
|
359
|
375
|
this, &SingularTab::setGridVisible);
|
|
376
|
+
|
|
377
|
+ sizeSelector_->installEventFilterForWidget(glyphView_);
|
|
378
|
+ sizeSelector_->installEventFilterForWidget(this);
|
360
|
379
|
}
|
361
|
380
|
|
362
|
381
|
|
src/ftinspect/panels/singular.hpp
... |
... |
@@ -55,6 +55,9 @@ private slots: |
55
|
55
|
void setGridVisible();
|
56
|
56
|
void showToolTip();
|
57
|
57
|
|
|
58
|
+protected:
|
|
59
|
+ bool eventFilter(QObject* watched, QEvent* event) override;
|
|
60
|
+
|
58
|
61
|
private:
|
59
|
62
|
int currentGlyphIndex_;
|
60
|
63
|
int currentGlyphCount_;
|
src/ftinspect/rendering/glyphcontinuous.cpp
... |
... |
@@ -97,9 +97,9 @@ GlyphContinuous::paintByRenderer(QPainter* painter) |
97
|
97
|
|
98
|
98
|
stringRenderer_.setRepeated(source_ == SRC_TextStringRepeated);
|
99
|
99
|
stringRenderer_.setCallback(
|
100
|
|
- [&](FT_Glyph glyph)
|
|
100
|
+ [&](FT_Glyph glyph, FT_Vector penPos)
|
101
|
101
|
{
|
102
|
|
- drawSingleGlyph(painter, glyph);
|
|
102
|
+ drawSingleGlyph(painter, glyph, penPos);
|
103
|
103
|
});
|
104
|
104
|
stringRenderer_.setPreprocessCallback(
|
105
|
105
|
[&](FT_Glyph* ptr)
|
... |
... |
@@ -261,17 +261,18 @@ GlyphContinuous::beginLine(QPainter* painter, |
261
|
261
|
|
262
|
262
|
|
263
|
263
|
void
|
264
|
|
-GlyphContinuous::drawSingleGlyph(QPainter* painter, FT_Glyph glyph)
|
|
264
|
+GlyphContinuous::drawSingleGlyph(QPainter* painter,
|
|
265
|
+ FT_Glyph glyph,
|
|
266
|
+ FT_Vector penPos)
|
265
|
267
|
{
|
266
|
268
|
// ftview.c:557
|
267
|
269
|
int width = glyph->advance.x ? glyph->advance.x >> 16
|
268
|
|
- : metrics_.y_ppem / 2;
|
269
|
|
-
|
|
270
|
+ : metrics_.y_ppem / 2;
|
|
271
|
+
|
270
|
272
|
if (glyph->advance.x == 0 && !stringRenderer_.isWaterfall())
|
271
|
273
|
{
|
272
|
274
|
// Draw a red square to indicate
|
273
|
|
- painter->fillRect(x_, y_ - width, width, width,
|
274
|
|
- Qt::red);
|
|
275
|
+ painter->fillRect(penPos.x, penPos.y - width, width, width, Qt::red);
|
275
|
276
|
}
|
276
|
277
|
|
277
|
278
|
QRect rect;
|
src/ftinspect/rendering/glyphcontinuous.hpp
... |
... |
@@ -101,7 +101,8 @@ private: |
101
|
101
|
FT_Vector pos,
|
102
|
102
|
double sizePoint);
|
103
|
103
|
void drawSingleGlyph(QPainter* painter,
|
104
|
|
- FT_Glyph glyph);
|
|
104
|
+ FT_Glyph glyph,
|
|
105
|
+ FT_Vector penPos);
|
105
|
106
|
};
|
106
|
107
|
|
107
|
108
|
|
src/ftinspect/widgets/fontsizeselector.cpp
... |
... |
@@ -45,7 +45,8 @@ FontSizeSelector::applyToEngine(Engine* engine) |
45
|
45
|
void
|
46
|
46
|
FontSizeSelector::handleWheelResizeBySteps(int steps)
|
47
|
47
|
{
|
48
|
|
- double sizeAfter = sizeDoubleSpinBox_->value() + steps * 0.5;
|
|
48
|
+ double sizeAfter = sizeDoubleSpinBox_->value()
|
|
49
|
+ + steps * sizeDoubleSpinBox_->singleStep();
|
49
|
50
|
sizeAfter = std::max(sizeDoubleSpinBox_->minimum(),
|
50
|
51
|
std::min(sizeAfter, sizeDoubleSpinBox_->maximum()));
|
51
|
52
|
sizeDoubleSpinBox_->setValue(sizeAfter);
|
... |
... |
@@ -60,6 +61,55 @@ FontSizeSelector::handleWheelResizeFromGrid(QWheelEvent* event) |
60
|
61
|
}
|
61
|
62
|
|
62
|
63
|
|
|
64
|
+bool
|
|
65
|
+FontSizeSelector::handleKeyEvent(QKeyEvent const* keyEvent)
|
|
66
|
+{
|
|
67
|
+ if (!keyEvent)
|
|
68
|
+ return false;
|
|
69
|
+ auto modifiers = keyEvent->modifiers();
|
|
70
|
+ auto key = keyEvent->key();
|
|
71
|
+ if ((modifiers == Qt::ShiftModifier
|
|
72
|
+ || modifiers == (Qt::ShiftModifier | Qt::KeypadModifier))
|
|
73
|
+ && (key == Qt::Key_Plus
|
|
74
|
+ || key == Qt::Key_Minus
|
|
75
|
+ || key == Qt::Key_Underscore
|
|
76
|
+ || key == Qt::Key_Equal
|
|
77
|
+ || key == Qt::Key_ParenRight))
|
|
78
|
+ {
|
|
79
|
+ if (key == Qt::Key_Plus || key == Qt::Key_Equal)
|
|
80
|
+ handleWheelResizeBySteps(1);
|
|
81
|
+ else if (key == Qt::Key_Minus
|
|
82
|
+ || key == Qt::Key_Underscore)
|
|
83
|
+ handleWheelResizeBySteps(-1);
|
|
84
|
+ else if (key == Qt::Key_ParenRight)
|
|
85
|
+ setDefaults(true);
|
|
86
|
+ return true;
|
|
87
|
+ }
|
|
88
|
+ return false;
|
|
89
|
+}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+void
|
|
93
|
+FontSizeSelector::installEventFilterForWidget(QWidget* widget)
|
|
94
|
+{
|
|
95
|
+ widget->installEventFilter(this);
|
|
96
|
+}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+bool
|
|
100
|
+FontSizeSelector::eventFilter(QObject* watched,
|
|
101
|
+ QEvent* event)
|
|
102
|
+{
|
|
103
|
+ if (event->type() == QEvent::KeyPress)
|
|
104
|
+ {
|
|
105
|
+ auto keyEvent = dynamic_cast<QKeyEvent*>(event);
|
|
106
|
+ if (handleKeyEvent(keyEvent))
|
|
107
|
+ return true;
|
|
108
|
+ }
|
|
109
|
+ return QWidget::eventFilter(watched, event);
|
|
110
|
+}
|
|
111
|
+
|
|
112
|
+
|
63
|
113
|
void
|
64
|
114
|
FontSizeSelector::checkUnits()
|
65
|
115
|
{
|
... |
... |
@@ -136,9 +186,11 @@ FontSizeSelector::createConnections() |
136
|
186
|
|
137
|
187
|
|
138
|
188
|
void
|
139
|
|
-FontSizeSelector::setDefaults()
|
|
189
|
+FontSizeSelector::setDefaults(bool sizeOnly)
|
140
|
190
|
{
|
141
|
191
|
sizeDoubleSpinBox_->setValue(20);
|
|
192
|
+ if (sizeOnly)
|
|
193
|
+ return;
|
142
|
194
|
dpiSpinBox_->setValue(96);
|
143
|
195
|
checkUnits();
|
144
|
196
|
}
|
src/ftinspect/widgets/fontsizeselector.hpp
... |
... |
@@ -32,6 +32,11 @@ public: |
32
|
32
|
void applyToEngine(Engine* engine);
|
33
|
33
|
void handleWheelResizeBySteps(int steps);
|
34
|
34
|
void handleWheelResizeFromGrid(QWheelEvent* event);
|
|
35
|
+ bool handleKeyEvent(QKeyEvent const* keyEvent);
|
|
36
|
+ void installEventFilterForWidget(QWidget* widget);
|
|
37
|
+
|
|
38
|
+protected:
|
|
39
|
+ bool eventFilter(QObject* watched, QEvent* event) override;
|
35
|
40
|
|
36
|
41
|
signals:
|
37
|
42
|
void valueChanged();
|
... |
... |
@@ -51,7 +56,7 @@ private: |
51
|
56
|
|
52
|
57
|
void createLayout();
|
53
|
58
|
void createConnections();
|
54
|
|
- void setDefaults();
|
|
59
|
+ void setDefaults(bool sizeOnly = false);
|
55
|
60
|
};
|
56
|
61
|
|
57
|
62
|
|
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
|
+ repopulateNamedInstances(fontSwitched);
|
|
89
|
+ return;
|
|
90
|
+ }
|
|
91
|
+
|
|
92
|
+ if (newSize != oldSize)
|
|
93
|
+ needToRecreate = true;
|
|
94
|
+
|
|
95
|
+ std::vector<QString> newFaces;
|
|
96
|
+ newFaces.reserve(newSize);
|
|
97
|
+ for (long i = 0; i < newSize; i++)
|
|
98
|
+ {
|
|
99
|
+ newFaces.emplace_back(engine_->namedInstanceName(fontIndex, i, 0));
|
|
100
|
+ if (!needToRecreate && newFaces[i] != faceComboBox_->itemData(i))
|
|
101
|
+ needToRecreate = true;
|
|
102
|
+ }
|
|
103
|
+
|
|
104
|
+ if (!needToRecreate)
|
|
105
|
+ {
|
|
106
|
+ // no need to refersh the combobox
|
|
107
|
+ repopulateNamedInstances(fontSwitched);
|
|
108
|
+ return;
|
|
109
|
+ }
|
|
110
|
+
|
|
111
|
+ {
|
|
112
|
+ QSignalBlocker blk2(faceComboBox_);
|
|
113
|
+ QSignalBlocker blk3(niComboBox_);
|
|
114
|
+ faceComboBox_->clear();
|
|
115
|
+
|
|
116
|
+ for (long i = 0; i < newSize; i++)
|
|
117
|
+ {
|
|
118
|
+ auto& name = newFaces[i];
|
|
119
|
+ auto displayName = QString("%1: %2").arg(i).arg(name);
|
|
120
|
+ faceComboBox_->addItem(displayName, name);
|
|
121
|
+ }
|
|
122
|
+
|
|
123
|
+ faceComboBox_->setCurrentIndex(0);
|
|
124
|
+ // Note no signal will be emitted from any combobox until this block ends
|
|
125
|
+ }
|
|
126
|
+
|
|
127
|
+ // This will check buttons & reload the triplet
|
|
128
|
+ repopulateNamedInstances(true);
|
|
129
|
+}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+void
|
|
133
|
+TripletSelector::repopulateNamedInstances(bool fontSwitched)
|
|
134
|
+{
|
|
135
|
+ // Avoid unnecessary recreating, to reduce interruption of user oper
|
|
136
|
+ // Similar to `repopulateFaces`
|
|
137
|
+ auto needToRecreate = fontSwitched;
|
|
138
|
+ auto oldSize = niComboBox_->count();
|
|
139
|
+
|
|
140
|
+ auto fontIndex = fontComboBox_->currentIndex();
|
|
141
|
+ auto faceIndex = faceComboBox_->currentIndex();
|
|
142
|
+ auto newSize = engine_->numberOfNamedInstances(fontIndex, faceIndex);
|
|
143
|
+
|
|
144
|
+ if (fontIndex < 0 || faceIndex < 0 || newSize < 0)
|
|
145
|
+ {
|
|
146
|
+ // Clear and go
|
|
147
|
+ niComboBox_->clear();
|
|
148
|
+ checkButtons();
|
|
149
|
+ loadTriplet();
|
|
150
|
+ return;
|
|
151
|
+ }
|
|
152
|
+
|
|
153
|
+ if (newSize != oldSize)
|
|
154
|
+ needToRecreate = true;
|
|
155
|
+
|
|
156
|
+ std::vector<QString> newFaces;
|
|
157
|
+ newFaces.reserve(newSize);
|
|
158
|
+ for (long i = 0; i < newSize; i++)
|
|
159
|
+ {
|
|
160
|
+ newFaces.emplace_back(engine_->namedInstanceName(fontIndex, faceIndex, i));
|
|
161
|
+ if (!needToRecreate && newFaces[i] != niComboBox_->itemData(i))
|
|
162
|
+ needToRecreate = true;
|
|
163
|
+ }
|
|
164
|
+
|
|
165
|
+ niComboBox_->setEnabled(newSize > 1);
|
|
166
|
+
|
|
167
|
+ if (!needToRecreate)
|
|
168
|
+ {
|
|
169
|
+ // no need to refersh the combobox
|
|
170
|
+ checkButtons();
|
|
171
|
+ loadTriplet();
|
|
172
|
+ return;
|
|
173
|
+ }
|
|
174
|
+
|
|
175
|
+ {
|
|
176
|
+ QSignalBlocker blk3(niComboBox_);
|
|
177
|
+ niComboBox_->clear();
|
|
178
|
+
|
|
179
|
+ for (long i = 0; i < newSize; i++)
|
|
180
|
+ {
|
|
181
|
+ auto& name = newFaces[i];
|
|
182
|
+ auto displayName = QString("%1: %2").arg(i).arg(name);
|
|
183
|
+ if (i == 0)
|
|
184
|
+ displayName = "* " + displayName;
|
|
185
|
+ niComboBox_->addItem(displayName, name);
|
|
186
|
+ }
|
|
187
|
+
|
|
188
|
+ niComboBox_->setCurrentIndex(0);
|
|
189
|
+ // Note no signal will be emitted from any combobox until this block ends
|
|
190
|
+ }
|
|
191
|
+
|
|
192
|
+ checkButtons();
|
|
193
|
+ loadTriplet();
|
|
194
|
+}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+void
|
|
198
|
+TripletSelector::closeCurrentFont()
|
|
199
|
+{
|
|
200
|
+ auto idx = fontComboBox_->currentIndex();
|
|
201
|
+ if (idx < 0)
|
|
202
|
+ return;
|
|
203
|
+ engine_->fontFileManager().remove(idx);
|
|
204
|
+
|
|
205
|
+ // show next font after deletion, i.e., retain index if possible
|
|
206
|
+ int num = engine_->numberOfOpenedFonts();
|
|
207
|
+ if (num)
|
|
208
|
+ {
|
|
209
|
+ if (idx >= num)
|
|
210
|
+ idx = num - 1;
|
|
211
|
+ }
|
|
212
|
+ else
|
|
213
|
+ idx = -1;
|
|
214
|
+
|
|
215
|
+ {
|
|
216
|
+ // Shut up when repopulating
|
|
217
|
+ QSignalBlocker blockerThis(this);
|
|
218
|
+ QSignalBlocker blockerComboBox(fontComboBox_);
|
|
219
|
+ repopulateFonts();
|
|
220
|
+ }
|
|
221
|
+
|
|
222
|
+ if (idx != -1)
|
|
223
|
+ faceComboBox_->setCurrentIndex(idx);
|
|
224
|
+ updateFont();
|
|
225
|
+}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+void
|
|
229
|
+TripletSelector::updateFont()
|
|
230
|
+{
|
|
231
|
+ auto idx = fontComboBox_->currentIndex();
|
|
232
|
+ auto num = engine_->numberOfOpenedFonts();
|
|
233
|
+ if (idx < 0)
|
|
234
|
+ {
|
|
235
|
+ faceComboBox_->clear();
|
|
236
|
+ niComboBox_->clear();
|
|
237
|
+
|
|
238
|
+ checkButtons();
|
|
239
|
+ loadTriplet();
|
|
240
|
+ return;
|
|
241
|
+ }
|
|
242
|
+
|
|
243
|
+ if (num <= 0 || idx >= num)
|
|
244
|
+ {
|
|
245
|
+ // out of sync: this shouldn't happen
|
|
246
|
+ repopulateFonts();
|
|
247
|
+ return;
|
|
248
|
+ }
|
|
249
|
+
|
|
250
|
+ // This will check buttons & reload the triplet
|
|
251
|
+ repopulateFaces();
|
|
252
|
+}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+void
|
|
256
|
+TripletSelector::updateFace()
|
|
257
|
+{
|
|
258
|
+ auto idx = faceComboBox_->currentIndex();
|
|
259
|
+ auto num = engine_->numberOfFaces(fontComboBox_->currentIndex());
|
|
260
|
+
|
|
261
|
+ if (idx >= num)
|
|
262
|
+ {
|
|
263
|
+ // out of sync
|
|
264
|
+ repopulateFaces();
|
|
265
|
+ return;
|
|
266
|
+ }
|
|
267
|
+
|
|
268
|
+ // This will check buttons & reload the triplet
|
|
269
|
+ repopulateNamedInstances();
|
|
270
|
+}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+void
|
|
274
|
+TripletSelector::updateNI()
|
|
275
|
+{
|
|
276
|
+ auto idx = niComboBox_->currentIndex();
|
|
277
|
+ auto num = engine_->numberOfNamedInstances(fontComboBox_->currentIndex(),
|
|
278
|
+ faceComboBox_->currentIndex());
|
|
279
|
+
|
|
280
|
+ if (idx >= num)
|
|
281
|
+ {
|
|
282
|
+ // out of sync
|
|
283
|
+ repopulateNamedInstances();
|
|
284
|
+ return;
|
|
285
|
+ }
|
|
286
|
+
|
|
287
|
+ checkButtons();
|
|
288
|
+ loadTriplet();
|
|
289
|
+}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+void
|
|
293
|
+TripletSelector::checkButtons()
|
|
294
|
+{
|
|
295
|
+ fontUpButton_->setEnabled(fontComboBox_->currentIndex() > 0);
|
|
296
|
+ fontDownButton_->setEnabled(fontComboBox_->currentIndex()
|
|
297
|
+ < fontComboBox_->count() - 1);
|
|
298
|
+ closeFontButton_->setEnabled(faceComboBox_->currentIndex() >= 0);
|
|
299
|
+
|
|
300
|
+ faceUpButton_->setEnabled(faceComboBox_->currentIndex() > 0);
|
|
301
|
+ faceDownButton_->setEnabled(faceComboBox_->currentIndex()
|
|
302
|
+ < faceComboBox_->count() - 1);
|
|
303
|
+
|
|
304
|
+ niUpButton_->setEnabled(niComboBox_->currentIndex() > 0);
|
|
305
|
+ niDownButton_->setEnabled(niComboBox_->currentIndex()
|
|
306
|
+ < niComboBox_->count() - 1);
|
|
307
|
+}
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+void
|
|
311
|
+TripletSelector::watchCurrentFont()
|
|
312
|
+{
|
|
313
|
+ repopulateFaces(false);
|
|
314
|
+}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+void
|
|
318
|
+TripletSelector::createLayout()
|
|
319
|
+{
|
|
320
|
+ fontComboBox_ = new QComboBox(this);
|
|
321
|
+ faceComboBox_ = new QComboBox(this);
|
|
322
|
+ niComboBox_ = new QComboBox(this);
|
|
323
|
+
|
|
324
|
+ fontComboBox_->setPlaceholderText(tr("No font open"));
|
|
325
|
+ faceComboBox_->setPlaceholderText(tr("No face available"));
|
|
326
|
+ niComboBox_->setPlaceholderText(tr("No named instance available"));
|
|
327
|
+
|
|
328
|
+ closeFontButton_ = new QToolButton(this);
|
|
329
|
+ fontUpButton_ = new QToolButton(this);
|
|
330
|
+ faceUpButton_ = new QToolButton(this);
|
|
331
|
+ niUpButton_ = new QToolButton(this);
|
|
332
|
+ fontDownButton_ = new QToolButton(this);
|
|
333
|
+ faceDownButton_ = new QToolButton(this);
|
|
334
|
+ niDownButton_ = new QToolButton(this);
|
|
335
|
+
|
|
336
|
+ closeFontButton_->setText(tr("Close"));
|
|
337
|
+ fontUpButton_ ->setText(tr("\xE2\x86\x91"));
|
|
338
|
+ faceUpButton_ ->setText(tr("\xE2\x86\x91"));
|
|
339
|
+ niUpButton_ ->setText(tr("\xE2\x86\x91"));
|
|
340
|
+ fontDownButton_ ->setText(tr("\xE2\x86\x93"));
|
|
341
|
+ faceDownButton_ ->setText(tr("\xE2\x86\x93"));
|
|
342
|
+ niDownButton_ ->setText(tr("\xE2\x86\x93"));
|
|
343
|
+
|
|
344
|
+ fontComboBox_ ->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
345
|
+ faceComboBox_ ->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
346
|
+ niComboBox_ ->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
347
|
+ closeFontButton_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
|
|
348
|
+ fontUpButton_ ->setFixedSize(30, 30);
|
|
349
|
+ faceUpButton_ ->setFixedSize(30, 30);
|
|
350
|
+ niUpButton_ ->setFixedSize(30, 30);
|
|
351
|
+ fontDownButton_ ->setFixedSize(30, 30);
|
|
352
|
+ faceDownButton_ ->setFixedSize(30, 30);
|
|
353
|
+ niDownButton_ ->setFixedSize(30, 30);
|
|
354
|
+
|
|
355
|
+ layout_ = new QHBoxLayout;
|
|
356
|
+ layout_->setSpacing(0);
|
|
357
|
+ layout_->setContentsMargins(0, 0, 0, 0);
|
|
358
|
+
|
|
359
|
+ layout_->addWidget(fontComboBox_);
|
|
360
|
+ layout_->addWidget(fontUpButton_);
|
|
361
|
+ layout_->addWidget(fontDownButton_);
|
|
362
|
+ layout_->addWidget(closeFontButton_);
|
|
363
|
+ layout_->addWidget(faceComboBox_);
|
|
364
|
+ layout_->addWidget(faceUpButton_);
|
|
365
|
+ layout_->addWidget(faceDownButton_);
|
|
366
|
+ layout_->addWidget(niComboBox_);
|
|
367
|
+ layout_->addWidget(niUpButton_);
|
|
368
|
+ layout_->addWidget(niDownButton_);
|
|
369
|
+
|
|
370
|
+ setFixedHeight(30);
|
|
371
|
+ setLayout(layout_);
|
|
372
|
+ layout_->setContentsMargins(0, 0, 0, 0);
|
|
373
|
+}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+void
|
|
377
|
+TripletSelector::createConnections()
|
|
378
|
+{
|
|
379
|
+ connect(fontComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
380
|
+ this, &TripletSelector::updateFont);
|
|
381
|
+ connect(faceComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
382
|
+ this, &TripletSelector::updateFace);
|
|
383
|
+ connect(niComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
384
|
+ this, &TripletSelector::updateNI);
|
|
385
|
+
|
|
386
|
+ connect(closeFontButton_, &QToolButton::clicked,
|
|
387
|
+ this, &TripletSelector::closeCurrentFont);
|
|
388
|
+ connect(fontUpButton_ , &QToolButton::clicked,
|
|
389
|
+ this,
|
|
390
|
+ std::bind(&TripletSelector::previousComboBoxItem, fontComboBox_));
|
|
391
|
+ connect(faceUpButton_ , &QToolButton::clicked,
|
|
392
|
+ this,
|
|
393
|
+ std::bind(&TripletSelector::previousComboBoxItem, faceComboBox_));
|
|
394
|
+ connect(niUpButton_ , &QToolButton::clicked,
|
|
395
|
+ this,
|
|
396
|
+ std::bind(&TripletSelector::previousComboBoxItem, niComboBox_));
|
|
397
|
+ connect(fontDownButton_ , &QToolButton::clicked,
|
|
398
|
+ this,
|
|
399
|
+ std::bind(&TripletSelector::nextComboBoxItem, fontComboBox_));
|
|
400
|
+ connect(faceDownButton_ , &QToolButton::clicked,
|
|
401
|
+ this,
|
|
402
|
+ std::bind(&TripletSelector::nextComboBoxItem, faceComboBox_));
|
|
403
|
+ connect(niDownButton_ , &QToolButton::clicked,
|
|
404
|
+ this,
|
|
405
|
+ std::bind(&TripletSelector::nextComboBoxItem, niComboBox_));
|
|
406
|
+
|
|
407
|
+ connect(&engine_->fontFileManager(), &FontFileManager::currentFileChanged,
|
|
408
|
+ this, &TripletSelector::watchCurrentFont);
|
|
409
|
+}
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+void
|
|
414
|
+TripletSelector::loadTriplet()
|
|
415
|
+{
|
|
416
|
+ // we do lazy computation of FT_Face objects
|
|
417
|
+
|
|
418
|
+ // TODO really?
|
|
419
|
+ auto fontIndex = fontComboBox_->currentIndex();
|
|
420
|
+ auto faceIndex = faceComboBox_->currentIndex();
|
|
421
|
+ auto instanceIndex = niComboBox_->currentIndex();
|
|
422
|
+
|
|
423
|
+ if (fontIndex >= 0 && fontIndex < engine_->numberOfOpenedFonts())
|
|
424
|
+ {
|
|
425
|
+ QFileInfo& fileInfo = engine_->fontFileManager()[fontIndex];
|
|
426
|
+ engine_->fontFileManager().updateWatching(fontIndex);
|
|
427
|
+
|
|
428
|
+ if (!fileInfo.exists())
|
|
429
|
+ {
|
|
430
|
+ // On Unix-like systems, the symlink's target gets opened; this
|
|
431
|
+ // implies that deletion of a symlink doesn't make `engine->loadFont'
|
|
432
|
+ // fail since it operates on a file handle pointing to the target.
|
|
433
|
+ // For this reason, we remove the font to enforce a reload.
|
|
434
|
+ engine_->removeFont(fontIndex, false);
|
|
435
|
+ }
|
|
436
|
+ }
|
|
437
|
+
|
|
438
|
+ auto number = engine_->loadFont(fontIndex, faceIndex, instanceIndex);
|
|
439
|
+
|
|
440
|
+ if (number < 0)
|
|
441
|
+ {
|
|
442
|
+ // there might be various reasons why the current
|
|
443
|
+ // (file, face, instance) triplet is invalid or missing;
|
|
444
|
+ // we thus start our timer to periodically test
|
|
445
|
+ // whether the font starts working
|
|
446
|
+ if (faceIndex >= 0 && faceIndex < engine_->numberOfOpenedFonts())
|
|
447
|
+ engine_->fontFileManager().timerStart();
|
|
448
|
+ }
|
|
449
|
+
|
|
450
|
+ emit tripletChanged();
|
|
451
|
+}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+void
|
|
455
|
+TripletSelector::nextComboBoxItem(QComboBox* c)
|
|
456
|
+{
|
|
457
|
+ if (c->currentIndex() < 0 || c->currentIndex() >= c->count() - 1)
|
|
458
|
+ return;
|
|
459
|
+ // No need to handle further steps, the event handler will take care of these
|
|
460
|
+ c->setCurrentIndex(c->currentIndex() + 1);
|
|
461
|
+}
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+void
|
|
465
|
+TripletSelector::previousComboBoxItem(QComboBox* c)
|
|
466
|
+{
|
|
467
|
+ if (c->currentIndex() <= 0)
|
|
468
|
+ return;
|
|
469
|
+ // No need to handle further steps, the event handler will take care of these
|
|
470
|
+ c->setCurrentIndex(c->currentIndex() - 1);
|
|
471
|
+}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+// 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 repopulateFaces(bool fontSwitched = true);
|
|
27
|
+ void repopulateNamedInstances(bool fontSwitched = true);
|
|
28
|
+ void closeCurrentFont();
|
|
29
|
+ void updateFont();
|
|
30
|
+ void updateFace();
|
|
31
|
+ void updateNI();
|
|
32
|
+ void loadTriplet();
|
|
33
|
+
|
|
34
|
+signals:
|
|
35
|
+ void tripletChanged();
|
|
36
|
+
|
|
37
|
+private:
|
|
38
|
+ Engine* engine_;
|
|
39
|
+
|
|
40
|
+ QComboBox* fontComboBox_;
|
|
41
|
+ QComboBox* faceComboBox_;
|
|
42
|
+ QComboBox* niComboBox_;
|
|
43
|
+
|
|
44
|
+ QToolButton* closeFontButton_;
|
|
45
|
+
|
|
46
|
+ QToolButton* fontUpButton_;
|
|
47
|
+ QToolButton* fontDownButton_;
|
|
48
|
+ QToolButton* faceUpButton_;
|
|
49
|
+ QToolButton* faceDownButton_;
|
|
50
|
+ QToolButton* niUpButton_;
|
|
51
|
+ QToolButton* niDownButton_;
|
|
52
|
+
|
|
53
|
+ QHBoxLayout* layout_;
|
|
54
|
+
|
|
55
|
+ void checkButtons();
|
|
56
|
+ void watchCurrentFont();
|
|
57
|
+
|
|
58
|
+ void createLayout();
|
|
59
|
+ void createConnections();
|
|
60
|
+
|
|
61
|
+ static void nextComboBoxItem(QComboBox* c);
|
|
62
|
+ static void previousComboBoxItem(QComboBox* c);
|
|
63
|
+};
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+// end of QPushButton.hpp |
|