Commits:
-
a1b171f9
by Charlie Jiang
at 2022-08-28T13:29:42+08:00
[ftinspect] Add composite glyphs info tab.
Not tested for nested glyphs.
* src/ftinspect/models/fontinfomodels.cpp,
src/ftinspect/models/fontinfomodels.hpp:
Add `CompositeGlyphsInfoModel` as a lazily loaded tree model.
* src/ftinspect/panels/info.cpp, src/ftinspect/panels/info.hpp:
Implement the `CompositeGlyphsTab` and integrate into the `InfoTab`.
* src/ftinspect/engine/fontinfo.cpp, src/ftinspect/engine/fontinfo.hpp:
Add `CompositeGlyphInfo` to retrieve composite glyph info from the `glyf`
table.
* src/ftinspect/maingui.cpp: Connect event for switching to singular.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
Add `currentFontHasGlyphName`.
* src/ftinspect/engine/fontfilemanager.cpp,
src/ftinspect/engine/fontfilemanager.hpp:
Add getter to check if current font reload is caused by periodic reload of
symbolic files. Don't reload the composite glyphs in such case.
-
c93a74f4
by Charlie Jiang
at 2022-08-28T13:30:42+08:00
* src/ftinspect/panels/singular.cpp: Adjust initial zoom factor.
-
3575e6e5
by Charlie Jiang
at 2022-08-28T13:39:47+08:00
* src/ftinspect/maingui.cpp: Use min size hint for initial window size.
-
b49f5ac8
by Charlie Jiang
at 2022-08-28T13:41:16+08:00
* src/ftinspect/panels/settingpanel.cpp: Set gamma slider steps.
-
cc61ce5a
by Charlie Jiang
at 2022-08-28T13:43:38+08:00
* src/ftinspect/panels/continuous.cpp: Hide the details pane when...
switching to the singular tab.
-
0a8df748
by Charlie Jiang
at 2022-08-28T14:04:04+08:00
[ftinspect] Add preset settings to comparators view.
* src/ftinspect/panels/comparator.cpp: Set presets based on index.
* src/ftinspect/panels/settingpanel.cpp,
src/ftinspect/panels/settingpanel.hpp: Add `setDefaultsPreset` values.
-
212821d5
by Charlie Jiang
at 2022-08-28T14:06:26+08:00
* src/ftinspect/panels/continuous.cpp: Disable kerning for "All Glyphs".
-
612b5f7c
by Charlie Jiang
at 2022-08-28T14:08:21+08:00
* src/ftinspect/maingui.cpp: Stop setting panels' tab bars from being...
shifted right in the comparator view.
-
1c468180
by Charlie Jiang
at 2022-08-28T14:20:52+08:00
[ftinspect] Improve fore/background color setting.
Now they're shown in the comparator view as well, and an indicator for
current color is shown.
* src/ftinspect/panels/settingpanel.cpp,
src/ftinspect/panels/settingpanel.hpp: As described.
16 changed files:
Changes:
src/ftinspect/engine/engine.cpp
... |
... |
@@ -560,6 +560,15 @@ Engine::currentFontFixedSizes() |
560
|
560
|
}
|
561
|
561
|
|
562
|
562
|
|
|
563
|
+bool
|
|
564
|
+Engine::currentFontHasGlyphName()
|
|
565
|
+{
|
|
566
|
+ if (!ftFallbackFace_)
|
|
567
|
+ return false;
|
|
568
|
+ return FT_HAS_GLYPH_NAMES(ftFallbackFace_);
|
|
569
|
+}
|
|
570
|
+
|
|
571
|
+
|
563
|
572
|
QString
|
564
|
573
|
Engine::glyphName(int index)
|
565
|
574
|
{
|
src/ftinspect/engine/engine.hpp
... |
... |
@@ -122,6 +122,7 @@ public: |
122
|
122
|
const QString& currentFamilyName() { return curFamilyName_; }
|
123
|
123
|
const QString& currentStyleName() { return curStyleName_; }
|
124
|
124
|
int currentFontNumberOfGlyphs() { return curNumGlyphs_; }
|
|
125
|
+ bool currentFontHasGlyphName();
|
125
|
126
|
|
126
|
127
|
QString glyphName(int glyphIndex);
|
127
|
128
|
long numberOfFaces(int fontIndex);
|
src/ftinspect/engine/fontfilemanager.cpp
... |
... |
@@ -17,9 +17,9 @@ FontFileManager::FontFileManager() |
17
|
17
|
watchTimer_->setInterval(1000);
|
18
|
18
|
|
19
|
19
|
connect(fontWatcher_, &QFileSystemWatcher::fileChanged,
|
20
|
|
- this, &FontFileManager::onTimerOrWatcherFire);
|
|
20
|
+ this, &FontFileManager::onWatcherFire);
|
21
|
21
|
connect(watchTimer_, &QTimer::timeout,
|
22
|
|
- this, &FontFileManager::onTimerOrWatcherFire);
|
|
22
|
+ this, &FontFileManager::onTimerFire);
|
23
|
23
|
}
|
24
|
24
|
|
25
|
25
|
|
... |
... |
@@ -137,11 +137,20 @@ FontFileManager::loadFromCommandLine() |
137
|
137
|
|
138
|
138
|
|
139
|
139
|
void
|
140
|
|
-FontFileManager::onTimerOrWatcherFire()
|
|
140
|
+FontFileManager::onWatcherFire()
|
141
|
141
|
{
|
142
|
142
|
watchTimer_->stop();
|
143
|
143
|
emit currentFileChanged();
|
144
|
144
|
}
|
145
|
145
|
|
146
|
146
|
|
|
147
|
+void
|
|
148
|
+FontFileManager::onTimerFire()
|
|
149
|
+{
|
|
150
|
+ periodicUpdating_ = true;
|
|
151
|
+ onWatcherFire();
|
|
152
|
+ periodicUpdating_ = false;
|
|
153
|
+}
|
|
154
|
+
|
|
155
|
+
|
147
|
156
|
// end of fontfilemanager.hpp |
src/ftinspect/engine/fontfilemanager.hpp
... |
... |
@@ -31,16 +31,21 @@ public: |
31
|
31
|
void timerStart();
|
32
|
32
|
void loadFromCommandLine();
|
33
|
33
|
|
|
34
|
+ bool currentReloadDueToPeriodicUpdate() { return periodicUpdating_; }
|
|
35
|
+
|
34
|
36
|
signals:
|
35
|
37
|
void currentFileChanged();
|
36
|
38
|
|
37
|
39
|
private slots:
|
38
|
|
- void onTimerOrWatcherFire();
|
|
40
|
+ void onTimerFire();
|
|
41
|
+ void onWatcherFire();
|
39
|
42
|
|
40
|
43
|
private:
|
41
|
44
|
QList<QFileInfo> fontFileNameList_;
|
42
|
45
|
QFileSystemWatcher* fontWatcher_;
|
43
|
46
|
QTimer* watchTimer_;
|
|
47
|
+
|
|
48
|
+ bool periodicUpdating_ = false;
|
44
|
49
|
};
|
45
|
50
|
|
46
|
51
|
|
src/ftinspect/engine/fontinfo.cpp
... |
... |
@@ -571,4 +571,126 @@ SFNTTableInfo::getForAll(Engine* engine, |
571
|
571
|
}
|
572
|
572
|
|
573
|
573
|
|
|
574
|
+void
|
|
575
|
+CompositeGlyphInfo::get(Engine* engine,
|
|
576
|
+ std::vector<CompositeGlyphInfo>& list)
|
|
577
|
+{
|
|
578
|
+ list.clear();
|
|
579
|
+ engine->reloadFont();
|
|
580
|
+ auto face = engine->currentFallbackFtFace();
|
|
581
|
+ if (!face || !FT_IS_SFNT(face))
|
|
582
|
+ {
|
|
583
|
+ if (list.empty())
|
|
584
|
+ return;
|
|
585
|
+ }
|
|
586
|
+
|
|
587
|
+ // We're not using the FreeType's subglyph APIs, but directly reading from
|
|
588
|
+ // the `glyf` table since it's faster
|
|
589
|
+ auto head = static_cast<TT_Header*>(FT_Get_Sfnt_Table(face, FT_SFNT_HEAD));
|
|
590
|
+ auto maxp
|
|
591
|
+ = static_cast<TT_MaxProfile*>(FT_Get_Sfnt_Table(face, FT_SFNT_MAXP));
|
|
592
|
+ if (!head || !maxp)
|
|
593
|
+ return;
|
|
594
|
+
|
|
595
|
+ FT_ULong locaLength = head->Index_To_Loc_Format ? 4 * maxp->numGlyphs + 4
|
|
596
|
+ : 2 * maxp->numGlyphs + 2;
|
|
597
|
+ std::unique_ptr<unsigned char[]> locaBufferGuard(
|
|
598
|
+ new unsigned char[locaLength]);
|
|
599
|
+ auto offset = locaBufferGuard.get();
|
|
600
|
+ auto error = FT_Load_Sfnt_Table(face, TTAG_loca, 0, offset, &locaLength);
|
|
601
|
+ if (error)
|
|
602
|
+ return;
|
|
603
|
+
|
|
604
|
+ FT_ULong glyfLength = 0;
|
|
605
|
+ error = FT_Load_Sfnt_Table(face, TTAG_glyf, 0, NULL, &glyfLength);
|
|
606
|
+ if (error || !glyfLength)
|
|
607
|
+ return;
|
|
608
|
+
|
|
609
|
+ std::unique_ptr<unsigned char[]> glyfBufferGuard(
|
|
610
|
+ new unsigned char[glyfLength]);
|
|
611
|
+ auto buffer = glyfBufferGuard.get();
|
|
612
|
+ error = FT_Load_Sfnt_Table(face, TTAG_glyf, 0, buffer, &glyfLength);
|
|
613
|
+ if (error)
|
|
614
|
+ return;
|
|
615
|
+
|
|
616
|
+ for (size_t i = 0; i < maxp->numGlyphs; i++)
|
|
617
|
+ {
|
|
618
|
+ FT_UInt32 loc, end;
|
|
619
|
+ if (head->Index_To_Loc_Format)
|
|
620
|
+ {
|
|
621
|
+ loc = static_cast<FT_UInt32>(offset[4 * i ]) << 24 |
|
|
622
|
+ static_cast<FT_UInt32>(offset[4 * i + 1]) << 16 |
|
|
623
|
+ static_cast<FT_UInt32>(offset[4 * i + 2]) << 8 |
|
|
624
|
+ static_cast<FT_UInt32>(offset[4 * i + 3]) ;
|
|
625
|
+ end = static_cast<FT_UInt32>(offset[4 * i + 4]) << 24 |
|
|
626
|
+ static_cast<FT_UInt32>(offset[4 * i + 5]) << 16 |
|
|
627
|
+ static_cast<FT_UInt32>(offset[4 * i + 6]) << 8 |
|
|
628
|
+ static_cast<FT_UInt32>(offset[4 * i + 7]) ;
|
|
629
|
+ }
|
|
630
|
+ else
|
|
631
|
+ {
|
|
632
|
+ loc = static_cast<FT_UInt32>(offset[2 * i ]) << 9 |
|
|
633
|
+ static_cast<FT_UInt32>(offset[2 * i + 1]) << 1 ;
|
|
634
|
+ end = static_cast<FT_UInt32>(offset[2 * i + 2]) << 9 |
|
|
635
|
+ static_cast<FT_UInt32>(offset[2 * i + 3]) << 1 ;
|
|
636
|
+ }
|
|
637
|
+
|
|
638
|
+ if (end > glyfLength)
|
|
639
|
+ end = glyfLength;
|
|
640
|
+
|
|
641
|
+ if (loc + 16 > end)
|
|
642
|
+ continue;
|
|
643
|
+
|
|
644
|
+ auto len = static_cast<FT_Int16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
645
|
+ loc += 10; // skip header
|
|
646
|
+ if (len >= 0) // not a composite one
|
|
647
|
+ continue;
|
|
648
|
+
|
|
649
|
+ std::vector<SubGlyph> subglyphs;
|
|
650
|
+
|
|
651
|
+ while (true)
|
|
652
|
+ {
|
|
653
|
+ if (loc + 6 > end)
|
|
654
|
+ break;
|
|
655
|
+ auto flags = static_cast<FT_UInt16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
656
|
+ loc += 2;
|
|
657
|
+ auto index = static_cast<FT_UInt16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
658
|
+ loc += 2;
|
|
659
|
+ FT_Int16 arg1, arg2;
|
|
660
|
+
|
|
661
|
+ // https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description
|
|
662
|
+ if (flags & 0x0001)
|
|
663
|
+ {
|
|
664
|
+ arg1 = static_cast<FT_Int16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
665
|
+ loc += 2;
|
|
666
|
+ arg2 = static_cast<FT_Int16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
667
|
+ loc += 2;
|
|
668
|
+ }
|
|
669
|
+ else
|
|
670
|
+ {
|
|
671
|
+ arg1 = buffer[loc];
|
|
672
|
+ arg2 = buffer[loc + 1];
|
|
673
|
+ loc += 2;
|
|
674
|
+ }
|
|
675
|
+ if (flags & 0x0008)
|
|
676
|
+ loc += 2;
|
|
677
|
+ else if (flags & 0x0040)
|
|
678
|
+ loc += 4;
|
|
679
|
+ else if (flags & 0x0080)
|
|
680
|
+ loc += 8;
|
|
681
|
+
|
|
682
|
+ subglyphs.emplace_back(index, flags,
|
|
683
|
+ flags & 0x0002 ? SubGlyph::PT_Offset
|
|
684
|
+ : SubGlyph::PT_Align,
|
|
685
|
+ std::pair<short, short>(arg1, arg2));
|
|
686
|
+
|
|
687
|
+ if (!(flags & 0x0020))
|
|
688
|
+ break;
|
|
689
|
+ }
|
|
690
|
+
|
|
691
|
+ list.emplace_back(static_cast<int>(i), std::move(subglyphs));
|
|
692
|
+ }
|
|
693
|
+}
|
|
694
|
+
|
|
695
|
+
|
574
|
696
|
// end of fontinfo.cpp |
src/ftinspect/engine/fontinfo.hpp
... |
... |
@@ -244,6 +244,86 @@ struct FontFixedSize |
244
|
244
|
};
|
245
|
245
|
|
246
|
246
|
|
|
247
|
+struct CompositeGlyphInfo
|
|
248
|
+{
|
|
249
|
+ struct SubGlyph
|
|
250
|
+ {
|
|
251
|
+ enum PositionType
|
|
252
|
+ {
|
|
253
|
+ PT_Offset, // Child's points are added with a xy-offset
|
|
254
|
+ PT_Align // One point of the child is aligned with one point of the parent
|
|
255
|
+ };
|
|
256
|
+ unsigned short index;
|
|
257
|
+ unsigned short flag;
|
|
258
|
+ PositionType positionType;
|
|
259
|
+ // For PT_Offset: <deltaX, deltaY>
|
|
260
|
+ // For PT_Align: <childPoint, parentPoint>
|
|
261
|
+ std::pair<short, short> position;
|
|
262
|
+
|
|
263
|
+ SubGlyph(unsigned short index,
|
|
264
|
+ unsigned short flag,
|
|
265
|
+ PositionType positionType,
|
|
266
|
+ std::pair<short, short> position)
|
|
267
|
+ : index(index),
|
|
268
|
+ flag(flag),
|
|
269
|
+ positionType(positionType),
|
|
270
|
+ position(std::move(position))
|
|
271
|
+ { }
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+ friend bool
|
|
275
|
+ operator==(const SubGlyph& lhs,
|
|
276
|
+ const SubGlyph& rhs)
|
|
277
|
+ {
|
|
278
|
+ return lhs.index == rhs.index
|
|
279
|
+ && lhs.flag == rhs.flag
|
|
280
|
+ && lhs.positionType == rhs.positionType
|
|
281
|
+ && lhs.position == rhs.position;
|
|
282
|
+ }
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+ friend bool
|
|
286
|
+ operator!=(const SubGlyph& lhs,
|
|
287
|
+ const SubGlyph& rhs)
|
|
288
|
+ {
|
|
289
|
+ return !(lhs == rhs);
|
|
290
|
+ }
|
|
291
|
+ };
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+ int index;
|
|
295
|
+ std::vector<SubGlyph> subglyphs;
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+ CompositeGlyphInfo(short index,
|
|
299
|
+ std::vector<SubGlyph> subglyphs)
|
|
300
|
+ : index(index),
|
|
301
|
+ subglyphs(std::move(subglyphs))
|
|
302
|
+ { }
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+ friend bool
|
|
306
|
+ operator==(const CompositeGlyphInfo& lhs,
|
|
307
|
+ const CompositeGlyphInfo& rhs)
|
|
308
|
+ {
|
|
309
|
+ return lhs.index == rhs.index
|
|
310
|
+ && lhs.subglyphs == rhs.subglyphs;
|
|
311
|
+ }
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+ friend bool
|
|
315
|
+ operator!=(const CompositeGlyphInfo& lhs,
|
|
316
|
+ const CompositeGlyphInfo& rhs)
|
|
317
|
+ {
|
|
318
|
+ return !(lhs == rhs);
|
|
319
|
+ }
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+ // expensive
|
|
323
|
+ static void get(Engine* engine, std::vector<CompositeGlyphInfo>& list);
|
|
324
|
+};
|
|
325
|
+
|
|
326
|
+
|
247
|
327
|
QString* mapSFNTNameIDToName(unsigned short nameID);
|
248
|
328
|
QString* mapTTPlatformIDToName(unsigned short platformID);
|
249
|
329
|
QString* mapTTEncodingIDToName(unsigned short platformID,
|
src/ftinspect/maingui.cpp
... |
... |
@@ -171,8 +171,9 @@ MainGUI::switchTab() |
171
|
171
|
auto isComparator = current == comparatorTab_;
|
172
|
172
|
|
173
|
173
|
if (isComparator)
|
174
|
|
- tabWidget_->setStyleSheet(QString("QTabWidget::tab-bar {left: %1 px;}")
|
175
|
|
- .arg(leftWidget_->width()));
|
|
174
|
+ tabWidget_->setStyleSheet(
|
|
175
|
+ QString("QTabWidget#mainTab::tab-bar {left: %1 px;}")
|
|
176
|
+ .arg(leftWidget_->width()));
|
176
|
177
|
else
|
177
|
178
|
tabWidget_->setStyleSheet("");
|
178
|
179
|
|
... |
... |
@@ -271,6 +272,7 @@ MainGUI::createLayout() |
271
|
272
|
infoTab_ = new InfoTab(this, engine_);
|
272
|
273
|
|
273
|
274
|
tabWidget_ = new QTabWidget(this);
|
|
275
|
+ tabWidget_->setObjectName("mainTab"); // for stylesheet
|
274
|
276
|
|
275
|
277
|
// Note those two list must be in sync
|
276
|
278
|
tabs_.append(singularTab_);
|
... |
... |
@@ -320,7 +322,8 @@ MainGUI::createLayout() |
320
|
322
|
|
321
|
323
|
ftinspectLayout_->setSizeConstraint(QLayout::SetNoConstraint);
|
322
|
324
|
layout()->setSizeConstraint(QLayout::SetNoConstraint);
|
323
|
|
- resize(1400 * logicalDpiX() / 96, 700 * logicalDpiY() / 96);
|
|
325
|
+ resize(ftinspectWidget_->minimumSizeHint().width(),
|
|
326
|
+ 700 * logicalDpiY() / 96);
|
324
|
327
|
|
325
|
328
|
statusBar()->hide(); // remove the extra space
|
326
|
329
|
setCentralWidget(ftinspectWidget_);
|
... |
... |
@@ -344,6 +347,8 @@ MainGUI::createConnections() |
344
|
347
|
|
345
|
348
|
connect(continuousTab_, &ContinuousTab::switchToSingular,
|
346
|
349
|
this, &MainGUI::switchToSingular);
|
|
350
|
+ connect(infoTab_, &InfoTab::switchToSingular,
|
|
351
|
+ [&](int index) { switchToSingular(index, -1); });
|
347
|
352
|
}
|
348
|
353
|
|
349
|
354
|
|
src/ftinspect/models/fontinfomodels.cpp
... |
... |
@@ -3,6 +3,7 @@ |
3
|
3
|
// Copyright (C) 2022 by Charlie Jiang.
|
4
|
4
|
|
5
|
5
|
#include "fontinfomodels.hpp"
|
|
6
|
+#include "../engine/engine.hpp"
|
6
|
7
|
|
7
|
8
|
int
|
8
|
9
|
FixedSizeInfoModel::rowCount(const QModelIndex& parent) const
|
... |
... |
@@ -463,4 +464,219 @@ MMGXAxisInfoModel::headerData(int section, |
463
|
464
|
}
|
464
|
465
|
|
465
|
466
|
|
|
467
|
+int
|
|
468
|
+CompositeGlyphsInfoModel::rowCount(const QModelIndex& parent) const
|
|
469
|
+{
|
|
470
|
+ if (!parent.isValid())
|
|
471
|
+ return static_cast<int>(glyphs_.size());
|
|
472
|
+ auto id = parent.internalId();
|
|
473
|
+ if (id < 0 || id >= nodes_.size())
|
|
474
|
+ return 0;
|
|
475
|
+ auto gid = nodes_[id].glyphIndex;
|
|
476
|
+ auto iter = glyphMapper_.find(gid);
|
|
477
|
+ if (iter == glyphMapper_.end())
|
|
478
|
+ return 0;
|
|
479
|
+ if (iter->second > glyphs_.size())
|
|
480
|
+ return 0;
|
|
481
|
+ return static_cast<int>(glyphs_[iter->second]. subglyphs.size());
|
|
482
|
+}
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+int
|
|
486
|
+CompositeGlyphsInfoModel::columnCount(const QModelIndex& parent) const
|
|
487
|
+{
|
|
488
|
+ return CGIM_Max;
|
|
489
|
+}
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+QModelIndex
|
|
493
|
+CompositeGlyphsInfoModel::index(int row,
|
|
494
|
+ int column,
|
|
495
|
+ const QModelIndex& parent) const
|
|
496
|
+{
|
|
497
|
+ long long parentIdx = -1;
|
|
498
|
+ if (parent.isValid()) // Not top-level
|
|
499
|
+ parentIdx = static_cast<long long>(parent.internalId());
|
|
500
|
+ if (parentIdx < 0)
|
|
501
|
+ parentIdx = -1;
|
|
502
|
+ // find existing node by row and parent index, -1 for top-level
|
|
503
|
+ auto lookupPair = std::pair<int, long long>(row, parentIdx);
|
|
504
|
+
|
|
505
|
+ auto iter = nodeLookup_.find(lookupPair);
|
|
506
|
+ if (iter != nodeLookup_.end())
|
|
507
|
+ {
|
|
508
|
+ if (iter->second < 0 || static_cast<size_t>(iter->second) >= nodes_.size())
|
|
509
|
+ return {};
|
|
510
|
+ return createIndex(row, column, iter->second);
|
|
511
|
+ }
|
|
512
|
+
|
|
513
|
+ int id = -1;
|
|
514
|
+ CompositeGlyphInfo::SubGlyph const* sgInfo = nullptr;
|
|
515
|
+ if (!parent.isValid()) // top-level nodes
|
|
516
|
+ id = glyphs_[row].index;
|
|
517
|
+ else if (parent.internalId() < glyphs_.size())
|
|
518
|
+ {
|
|
519
|
+ auto& sg = glyphs_[parent.internalId()].subglyphs;
|
|
520
|
+ if (row < 0 || static_cast<size_t>(row) >= sg.size())
|
|
521
|
+ return {};
|
|
522
|
+ id = sg[row].index;
|
|
523
|
+ sgInfo = &sg[row];
|
|
524
|
+ }
|
|
525
|
+
|
|
526
|
+ if (id < 0)
|
|
527
|
+ return {};
|
|
528
|
+
|
|
529
|
+ InfoNode node = {
|
|
530
|
+ parentIdx,
|
|
531
|
+ row, id,
|
|
532
|
+ sgInfo
|
|
533
|
+ };
|
|
534
|
+ nodes_.push_back(node);
|
|
535
|
+ nodeLookup_.emplace(std::pair<int, long long>(row, parentIdx),
|
|
536
|
+ nodes_.size() - 1);
|
|
537
|
+
|
|
538
|
+ return createIndex(row, column, nodes_.size() - 1);
|
|
539
|
+}
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+QModelIndex
|
|
543
|
+CompositeGlyphsInfoModel::parent(const QModelIndex& child) const
|
|
544
|
+{
|
|
545
|
+ if (!child.isValid())
|
|
546
|
+ return {};
|
|
547
|
+
|
|
548
|
+ auto id = static_cast<long long>(child.internalId());
|
|
549
|
+ if (id < 0 || static_cast<size_t>(id) >= nodes_.size())
|
|
550
|
+ return {};
|
|
551
|
+
|
|
552
|
+ auto pid = nodes_[id].parentNodeIndex;
|
|
553
|
+ if (pid < 0 || static_cast<size_t>(pid) >= nodes_.size())
|
|
554
|
+ return {};
|
|
555
|
+
|
|
556
|
+ auto& p = nodes_[pid];
|
|
557
|
+ return createIndex(p.indexInParent, 0, pid);
|
|
558
|
+}
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+QVariant
|
|
562
|
+CompositeGlyphsInfoModel::data(const QModelIndex& index,
|
|
563
|
+ int role) const
|
|
564
|
+{
|
|
565
|
+ if (!index.isValid())
|
|
566
|
+ return {};
|
|
567
|
+
|
|
568
|
+ auto id = index.internalId();
|
|
569
|
+ if (id >= nodes_.size())
|
|
570
|
+ return {};
|
|
571
|
+ auto& n = nodes_[id];
|
|
572
|
+ auto glyphIdx = n.glyphIndex;
|
|
573
|
+
|
|
574
|
+ if (role == Qt::ToolTipRole && index.column() == CGIM_Position)
|
|
575
|
+ {
|
|
576
|
+ if (!n.subGlyphInfo)
|
|
577
|
+ return {};
|
|
578
|
+ auto pos = n.subGlyphInfo->position;
|
|
579
|
+ switch (n.subGlyphInfo->positionType)
|
|
580
|
+ {
|
|
581
|
+ case CompositeGlyphInfo::SubGlyph::PT_Offset:
|
|
582
|
+ return QString("Add a offset (%1, %2) to the subglyph's points")
|
|
583
|
+ .arg(pos.first)
|
|
584
|
+ .arg(pos.second);
|
|
585
|
+ case CompositeGlyphInfo::SubGlyph::PT_Align:
|
|
586
|
+ return QString("Align parent's point %1 to subglyph's point %2")
|
|
587
|
+ .arg(pos.first)
|
|
588
|
+ .arg(pos.second);
|
|
589
|
+ }
|
|
590
|
+ return {};
|
|
591
|
+ }
|
|
592
|
+
|
|
593
|
+ if (role != Qt::DisplayRole)
|
|
594
|
+ return {};
|
|
595
|
+
|
|
596
|
+ switch (static_cast<Columns>(index.column()))
|
|
597
|
+ {
|
|
598
|
+ case CGIM_Glyph:
|
|
599
|
+ if (engine_->currentFontHasGlyphName())
|
|
600
|
+ return QString("%1 <%2>").arg(glyphIdx).arg(engine_->glyphName(glyphIdx));
|
|
601
|
+ return QString::number(glyphIdx);
|
|
602
|
+ case CGIM_Flag:
|
|
603
|
+ if (!n.subGlyphInfo)
|
|
604
|
+ return {};
|
|
605
|
+ return QString::number(n.subGlyphInfo->flag, 16).rightJustified(4, '0');
|
|
606
|
+ case CGIM_Position:
|
|
607
|
+ {
|
|
608
|
+ if (!n.subGlyphInfo)
|
|
609
|
+ return {};
|
|
610
|
+ auto pos = n.subGlyphInfo->position;
|
|
611
|
+ switch (n.subGlyphInfo->positionType)
|
|
612
|
+ {
|
|
613
|
+ case CompositeGlyphInfo::SubGlyph::PT_Offset:
|
|
614
|
+ return QString("Offset (%1, %2)").arg(pos.first).arg(pos.second);
|
|
615
|
+ case CompositeGlyphInfo::SubGlyph::PT_Align:
|
|
616
|
+ return QString("Align %1 -> %2").arg(pos.first).arg(pos.second);
|
|
617
|
+ }
|
|
618
|
+ }
|
|
619
|
+ }
|
|
620
|
+
|
|
621
|
+ return {};
|
|
622
|
+}
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+QVariant
|
|
626
|
+CompositeGlyphsInfoModel::headerData(int section,
|
|
627
|
+ Qt::Orientation orientation,
|
|
628
|
+ int role) const
|
|
629
|
+{
|
|
630
|
+ if (role != Qt::DisplayRole)
|
|
631
|
+ return {};
|
|
632
|
+ if (orientation != Qt::Horizontal)
|
|
633
|
+ return {};
|
|
634
|
+
|
|
635
|
+ switch (static_cast<Columns>(section))
|
|
636
|
+ {
|
|
637
|
+ case CGIM_Glyph:
|
|
638
|
+ return tr("Glyph");
|
|
639
|
+ case CGIM_Flag:
|
|
640
|
+ return tr("Flags");
|
|
641
|
+ case CGIM_Position:
|
|
642
|
+ return tr("Position");
|
|
643
|
+ }
|
|
644
|
+ return {};
|
|
645
|
+}
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+int
|
|
649
|
+CompositeGlyphsInfoModel::glyphIndexFromIndex(const QModelIndex& idx)
|
|
650
|
+{
|
|
651
|
+ if (!idx.isValid())
|
|
652
|
+ return -1;
|
|
653
|
+
|
|
654
|
+ auto id = idx.internalId();
|
|
655
|
+ if (id >= nodes_.size())
|
|
656
|
+ return -1;
|
|
657
|
+ auto& n = nodes_[id];
|
|
658
|
+ return n.glyphIndex;
|
|
659
|
+}
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+void
|
|
663
|
+CompositeGlyphsInfoModel::beginModelUpdate()
|
|
664
|
+{
|
|
665
|
+ beginResetModel();
|
|
666
|
+ glyphs_.clear();
|
|
667
|
+ nodeLookup_.clear();
|
|
668
|
+ nodes_.clear();
|
|
669
|
+}
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+void
|
|
673
|
+CompositeGlyphsInfoModel::endModelUpdate()
|
|
674
|
+{
|
|
675
|
+ glyphMapper_.clear();
|
|
676
|
+ for (size_t i = 0; i < glyphs_.size(); i++)
|
|
677
|
+ glyphMapper_.emplace(glyphs_[i].index, i);
|
|
678
|
+ endResetModel();
|
|
679
|
+}
|
|
680
|
+
|
|
681
|
+
|
466
|
682
|
// end of fontinfomodels.cpp |
src/ftinspect/models/fontinfomodels.hpp
... |
... |
@@ -9,6 +9,7 @@ |
9
|
9
|
#include "../engine/mmgx.hpp"
|
10
|
10
|
|
11
|
11
|
#include <vector>
|
|
12
|
+#include <unordered_map>
|
12
|
13
|
#include <QAbstractTableModel>
|
13
|
14
|
|
14
|
15
|
class FixedSizeInfoModel
|
... |
... |
@@ -203,4 +204,82 @@ private: |
203
|
204
|
};
|
204
|
205
|
|
205
|
206
|
|
|
207
|
+struct LookupPairHash
|
|
208
|
+{
|
|
209
|
+public:
|
|
210
|
+ std::size_t
|
|
211
|
+ operator()(const std::pair<int, long long>& p) const
|
|
212
|
+ {
|
|
213
|
+ std::size_t seed = 0x291FEEA8;
|
|
214
|
+ seed ^= (seed << 6) + (seed >> 2) + 0x25F3E86D
|
|
215
|
+ + static_cast<std::size_t>(p.first);
|
|
216
|
+ seed ^= (seed << 6) + (seed >> 2) + 0x436E6B92
|
|
217
|
+ + static_cast<std::size_t>(p.second);
|
|
218
|
+ return seed;
|
|
219
|
+ }
|
|
220
|
+};
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+// A tree model, so much more complicated.
|
|
224
|
+class CompositeGlyphsInfoModel : public QAbstractItemModel
|
|
225
|
+{
|
|
226
|
+ Q_OBJECT
|
|
227
|
+public:
|
|
228
|
+ // A lazily created info node.
|
|
229
|
+ struct InfoNode
|
|
230
|
+ {
|
|
231
|
+ long long parentNodeIndex;
|
|
232
|
+ int indexInParent;
|
|
233
|
+ int glyphIndex;
|
|
234
|
+ CompositeGlyphInfo::SubGlyph const* subGlyphInfo;
|
|
235
|
+ };
|
|
236
|
+
|
|
237
|
+ explicit CompositeGlyphsInfoModel(QObject* parent, Engine* engine)
|
|
238
|
+ : QAbstractItemModel(parent), engine_(engine)
|
|
239
|
+ {
|
|
240
|
+ }
|
|
241
|
+
|
|
242
|
+ ~CompositeGlyphsInfoModel() override = default;
|
|
243
|
+
|
|
244
|
+ int rowCount(const QModelIndex& parent) const override;
|
|
245
|
+ int columnCount(const QModelIndex& parent) const override;
|
|
246
|
+ QModelIndex index(int row, int column,
|
|
247
|
+ const QModelIndex& parent) const override;
|
|
248
|
+ QModelIndex parent(const QModelIndex& child) const override;
|
|
249
|
+ QVariant data(const QModelIndex& index, int role) const override;
|
|
250
|
+ QVariant headerData(int section, Qt::Orientation orientation,
|
|
251
|
+ int role) const override;
|
|
252
|
+ int glyphIndexFromIndex(const QModelIndex& idx);
|
|
253
|
+
|
|
254
|
+ void beginModelUpdate();
|
|
255
|
+ void endModelUpdate();
|
|
256
|
+ std::vector<CompositeGlyphInfo>& storage() { return glyphs_; }
|
|
257
|
+
|
|
258
|
+ enum Columns : int
|
|
259
|
+ {
|
|
260
|
+ CGIM_Glyph = 0, // TODO: transformation, scale? consider more flags?
|
|
261
|
+ CGIM_Flag = 1,
|
|
262
|
+ CGIM_Position = 2,
|
|
263
|
+ CGIM_Max
|
|
264
|
+ };
|
|
265
|
+
|
|
266
|
+private:
|
|
267
|
+ Engine* engine_;
|
|
268
|
+ /*
|
|
269
|
+ * Take care of 3 types of index:
|
|
270
|
+ * 1. Glyph Index in Font File
|
|
271
|
+ * 2. Glyph Index in `glyphs_` - often called as "glyph info index"
|
|
272
|
+ * 3. Node Index
|
|
273
|
+ */
|
|
274
|
+ std::vector<CompositeGlyphInfo> glyphs_;
|
|
275
|
+ std::unordered_map<int, size_t> glyphMapper_;
|
|
276
|
+ // map <row, parentId> to node
|
|
277
|
+ // the internal id of `QModelIndex` is the node's index
|
|
278
|
+ mutable std::unordered_map<std::pair<int, long long>,
|
|
279
|
+ long long, LookupPairHash>
|
|
280
|
+ nodeLookup_;
|
|
281
|
+ mutable std::vector<InfoNode> nodes_;
|
|
282
|
+};
|
|
283
|
+
|
|
284
|
+
|
206
|
285
|
// end of fontinfomodels.hpp |
src/ftinspect/panels/comparator.cpp
... |
... |
@@ -98,6 +98,7 @@ ComperatorTab::createLayout() |
98
|
98
|
auto frame = new QFrame(this);
|
99
|
99
|
auto canvas = new GlyphContinuous(frame, engine_);
|
100
|
100
|
auto settingPanel = new SettingPanel(this, engine_, true);
|
|
101
|
+ settingPanel->setDefaultsPreset(i);
|
101
|
102
|
|
102
|
103
|
sizeSelector_->installEventFilterForWidget(canvas);
|
103
|
104
|
|
src/ftinspect/panels/continuous.cpp
... |
... |
@@ -161,6 +161,14 @@ ContinuousTab::checkModeSource() |
161
|
161
|
indexSelector_->setEnabled(src == GlyphContinuous::SRC_AllGlyphs);
|
162
|
162
|
sourceTextEdit_->setEnabled(isText);
|
163
|
163
|
sampleStringSelector_->setEnabled(isText);
|
|
164
|
+
|
|
165
|
+ {
|
|
166
|
+ QSignalBlocker blocker(kerningCheckBox_);
|
|
167
|
+ kerningCheckBox_->setEnabled(isText);
|
|
168
|
+ if (!isText)
|
|
169
|
+ kerningCheckBox_->setChecked(false);
|
|
170
|
+ }
|
|
171
|
+
|
164
|
172
|
canvas_->setSource(src);
|
165
|
173
|
|
166
|
174
|
{
|
... |
... |
@@ -548,7 +556,12 @@ ContinuousTab::createConnections() |
548
|
556
|
sizeSelector_->installEventFilterForWidget(this);
|
549
|
557
|
|
550
|
558
|
connect(glyphDetails_, &GlyphDetails::switchToSingular,
|
551
|
|
- [&] (int index) { switchToSingular(index, -1); });
|
|
559
|
+ [&] (int index)
|
|
560
|
+ {
|
|
561
|
+ switchToSingular(index, -1);
|
|
562
|
+ if (glyphDetailsWidget_->isFloating())
|
|
563
|
+ glyphDetailsWidget_->hide();
|
|
564
|
+ });
|
552
|
565
|
}
|
553
|
566
|
|
554
|
567
|
|
src/ftinspect/panels/info.cpp
... |
... |
@@ -22,6 +22,7 @@ InfoTab::InfoTab(QWidget* parent, |
22
|
22
|
: QWidget(parent), engine_(engine)
|
23
|
23
|
{
|
24
|
24
|
createLayout();
|
|
25
|
+ createConnections();
|
25
|
26
|
}
|
26
|
27
|
|
27
|
28
|
|
... |
... |
@@ -40,17 +41,19 @@ InfoTab::createLayout() |
40
|
41
|
sfntTab_ = new SFNTInfoTab(this, engine_);
|
41
|
42
|
postScriptTab_ = new PostScriptInfoTab(this, engine_);
|
42
|
43
|
mmgxTab_ = new MMGXInfoTab(this, engine_);
|
|
44
|
+ compositeGlyphsTab_ = new CompositeGlyphsTab(this, engine_);
|
43
|
45
|
|
44
|
46
|
tab_ = new QTabWidget(this);
|
45
|
47
|
tab_->addTab(generalTab_, tr("General"));
|
46
|
48
|
tab_->addTab(sfntTab_, tr("SFNT"));
|
47
|
49
|
tab_->addTab(postScriptTab_, tr("PostScript"));
|
48
|
50
|
tab_->addTab(mmgxTab_, tr("MM/GX"));
|
|
51
|
+ tab_->addTab(compositeGlyphsTab_, tr("Composite Glyphs"));
|
49
|
52
|
|
50
|
53
|
tabs_.append(generalTab_);
|
51
|
54
|
tabs_.append(sfntTab_);
|
52
|
55
|
tabs_.append(postScriptTab_);
|
53
|
|
- tabs_.append(mmgxTab_);
|
|
56
|
+ tabs_.append(compositeGlyphsTab_);
|
54
|
57
|
|
55
|
58
|
layout_ = new QHBoxLayout;
|
56
|
59
|
layout_->addWidget(tab_);
|
... |
... |
@@ -59,6 +62,14 @@ InfoTab::createLayout() |
59
|
62
|
}
|
60
|
63
|
|
61
|
64
|
|
|
65
|
+void
|
|
66
|
+InfoTab::createConnections()
|
|
67
|
+{
|
|
68
|
+ connect(compositeGlyphsTab_, &CompositeGlyphsTab::switchToSingular,
|
|
69
|
+ this, &InfoTab::switchToSingular);
|
|
70
|
+}
|
|
71
|
+
|
|
72
|
+
|
62
|
73
|
GeneralInfoTab::GeneralInfoTab(QWidget* parent,
|
63
|
74
|
Engine* engine)
|
64
|
75
|
: QWidget(parent), engine_(engine)
|
... |
... |
@@ -919,4 +930,89 @@ MMGXInfoTab::createLayout() |
919
|
930
|
}
|
920
|
931
|
|
921
|
932
|
|
|
933
|
+CompositeGlyphsTab::CompositeGlyphsTab(QWidget* parent,
|
|
934
|
+ Engine* engine)
|
|
935
|
+: QWidget(parent), engine_(engine)
|
|
936
|
+{
|
|
937
|
+ createLayout();
|
|
938
|
+ createConnections();
|
|
939
|
+}
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+void
|
|
943
|
+CompositeGlyphsTab::reloadFont()
|
|
944
|
+{
|
|
945
|
+ if (engine_->fontFileManager().currentReloadDueToPeriodicUpdate())
|
|
946
|
+ return;
|
|
947
|
+ forceReloadFont();
|
|
948
|
+}
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+void
|
|
952
|
+CompositeGlyphsTab::createLayout()
|
|
953
|
+{
|
|
954
|
+ compositeGlyphCountPromptLabel_ = new QLabel(tr("Composite Glyphs Count:"));
|
|
955
|
+ compositeGlyphCountLabel_ = new QLabel(this);
|
|
956
|
+ forceRefreshButton_ = new QPushButton(tr("Force Refresh"), this);
|
|
957
|
+ compositeTreeView_ = new QTreeView(this);
|
|
958
|
+
|
|
959
|
+ compositeModel_ = new CompositeGlyphsInfoModel(this, engine_);
|
|
960
|
+ compositeTreeView_->setModel(compositeModel_);
|
|
961
|
+
|
|
962
|
+ forceRefreshButton_->setToolTip(tr(
|
|
963
|
+ "Force refresh the tree view.\n"
|
|
964
|
+ "Note that periodic reloading of fonts loaded from symbolic links won't\n"
|
|
965
|
+ "trigger automatically refreshing, so you need to manually reload."));
|
|
966
|
+
|
|
967
|
+ // Layouting
|
|
968
|
+ countLayout_ = new QHBoxLayout;
|
|
969
|
+ countLayout_->addWidget(compositeGlyphCountPromptLabel_);
|
|
970
|
+ countLayout_->addWidget(compositeGlyphCountLabel_);
|
|
971
|
+ countLayout_->addWidget(forceRefreshButton_);
|
|
972
|
+ countLayout_->addStretch(1);
|
|
973
|
+
|
|
974
|
+ mainLayout_ = new QVBoxLayout;
|
|
975
|
+ mainLayout_->addLayout(countLayout_);
|
|
976
|
+ mainLayout_->addWidget(compositeTreeView_);
|
|
977
|
+
|
|
978
|
+ setLayout(mainLayout_);
|
|
979
|
+}
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+void
|
|
983
|
+CompositeGlyphsTab::createConnections()
|
|
984
|
+{
|
|
985
|
+ connect(forceRefreshButton_, &QPushButton::clicked,
|
|
986
|
+ this, &CompositeGlyphsTab::forceReloadFont);
|
|
987
|
+ connect(compositeTreeView_, &QTreeView::doubleClicked,
|
|
988
|
+ this, &CompositeGlyphsTab::treeRowDoubleClicked);
|
|
989
|
+}
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+void
|
|
993
|
+CompositeGlyphsTab::forceReloadFont()
|
|
994
|
+{
|
|
995
|
+ std::vector<CompositeGlyphInfo> list;
|
|
996
|
+ CompositeGlyphInfo::get(engine_, list);
|
|
997
|
+ if (list == compositeModel_->storage())
|
|
998
|
+ return;
|
|
999
|
+ compositeModel_->beginModelUpdate();
|
|
1000
|
+ compositeModel_->storage() = list;
|
|
1001
|
+ compositeModel_->endModelUpdate();
|
|
1002
|
+
|
|
1003
|
+ compositeGlyphCountLabel_->setText(
|
|
1004
|
+ QString::number(compositeModel_->storage().size()));
|
|
1005
|
+}
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+void
|
|
1009
|
+CompositeGlyphsTab::treeRowDoubleClicked(const QModelIndex& idx)
|
|
1010
|
+{
|
|
1011
|
+ auto gidx = compositeModel_->glyphIndexFromIndex(idx);
|
|
1012
|
+ if (gidx < 0)
|
|
1013
|
+ return;
|
|
1014
|
+ emit switchToSingular(gidx);
|
|
1015
|
+}
|
|
1016
|
+
|
|
1017
|
+
|
922
|
1018
|
// end of info.cpp |
src/ftinspect/panels/info.hpp
... |
... |
@@ -20,13 +20,14 @@ |
20
|
20
|
#include <QLabel>
|
21
|
21
|
#include <QGroupBox>
|
22
|
22
|
#include <QTableView>
|
23
|
|
-#include <QStackedLayout>
|
|
23
|
+#include <QTreeView>
|
24
|
24
|
|
25
|
25
|
class Engine;
|
26
|
26
|
class GeneralInfoTab;
|
27
|
27
|
class SFNTInfoTab;
|
28
|
28
|
class PostScriptInfoTab;
|
29
|
29
|
class MMGXInfoTab;
|
|
30
|
+class CompositeGlyphsTab;
|
30
|
31
|
|
31
|
32
|
class InfoTab
|
32
|
33
|
: public QWidget, public AbstractTab
|
... |
... |
@@ -39,19 +40,24 @@ public: |
39
|
40
|
void repaintGlyph() override {}
|
40
|
41
|
void reloadFont() override;
|
41
|
42
|
|
|
43
|
+signals:
|
|
44
|
+ void switchToSingular(int glyphIndex);
|
|
45
|
+
|
42
|
46
|
private:
|
43
|
47
|
Engine* engine_;
|
44
|
48
|
|
45
|
49
|
QVector<AbstractTab*> tabs_;
|
46
|
|
- GeneralInfoTab* generalTab_;
|
47
|
|
- SFNTInfoTab* sfntTab_;
|
48
|
|
- PostScriptInfoTab* postScriptTab_;
|
49
|
|
- MMGXInfoTab* mmgxTab_;
|
|
50
|
+ GeneralInfoTab* generalTab_;
|
|
51
|
+ SFNTInfoTab* sfntTab_;
|
|
52
|
+ PostScriptInfoTab* postScriptTab_;
|
|
53
|
+ MMGXInfoTab* mmgxTab_;
|
|
54
|
+ CompositeGlyphsTab* compositeGlyphsTab_;
|
50
|
55
|
|
51
|
56
|
QTabWidget* tab_;
|
52
|
57
|
QHBoxLayout* layout_;
|
53
|
58
|
|
54
|
59
|
void createLayout();
|
|
60
|
+ void createConnections();
|
55
|
61
|
};
|
56
|
62
|
|
57
|
63
|
|
... |
... |
@@ -285,4 +291,37 @@ private: |
285
|
291
|
};
|
286
|
292
|
|
287
|
293
|
|
|
294
|
+class CompositeGlyphsTab
|
|
295
|
+: public QWidget, public AbstractTab
|
|
296
|
+{
|
|
297
|
+ Q_OBJECT
|
|
298
|
+public:
|
|
299
|
+ CompositeGlyphsTab(QWidget* parent, Engine* engine);
|
|
300
|
+ ~CompositeGlyphsTab() override = default;
|
|
301
|
+
|
|
302
|
+ void repaintGlyph() override {}
|
|
303
|
+ void reloadFont() override;
|
|
304
|
+
|
|
305
|
+signals:
|
|
306
|
+ void switchToSingular(int glyphIndex);
|
|
307
|
+
|
|
308
|
+private:
|
|
309
|
+ Engine* engine_;
|
|
310
|
+
|
|
311
|
+ LabelPair(compositeGlyphCount)
|
|
312
|
+ QPushButton* forceRefreshButton_;
|
|
313
|
+ QTreeView* compositeTreeView_;
|
|
314
|
+ CompositeGlyphsInfoModel* compositeModel_;
|
|
315
|
+
|
|
316
|
+ QHBoxLayout* countLayout_;
|
|
317
|
+ QVBoxLayout* mainLayout_;
|
|
318
|
+
|
|
319
|
+ void createLayout();
|
|
320
|
+ void createConnections();
|
|
321
|
+
|
|
322
|
+ void forceReloadFont();
|
|
323
|
+ void treeRowDoubleClicked(const QModelIndex& idx);
|
|
324
|
+};
|
|
325
|
+
|
|
326
|
+
|
288
|
327
|
// end of info.hpp |
src/ftinspect/panels/settingpanel.cpp
... |
... |
@@ -49,6 +49,30 @@ SettingPanel::lsbRsbDeltaEnabled() |
49
|
49
|
}
|
50
|
50
|
|
51
|
51
|
|
|
52
|
+void
|
|
53
|
+SettingPanel::setDefaultsPreset(int preset)
|
|
54
|
+{
|
|
55
|
+ if (preset < 0)
|
|
56
|
+ preset = 0;
|
|
57
|
+ preset %= 3;
|
|
58
|
+ switch (preset)
|
|
59
|
+ {
|
|
60
|
+ case 0:
|
|
61
|
+ hintingCheckBox_->setChecked(true);
|
|
62
|
+ autoHintingCheckBox_->setChecked(false);
|
|
63
|
+ break;
|
|
64
|
+ case 1:
|
|
65
|
+ hintingCheckBox_->setChecked(true);
|
|
66
|
+ autoHintingCheckBox_->setChecked(true);
|
|
67
|
+ break;
|
|
68
|
+ case 2:
|
|
69
|
+ hintingCheckBox_->setChecked(false);
|
|
70
|
+ autoHintingCheckBox_->setChecked(false);
|
|
71
|
+ break;
|
|
72
|
+ }
|
|
73
|
+}
|
|
74
|
+
|
|
75
|
+
|
52
|
76
|
void
|
53
|
77
|
SettingPanel::checkAllSettings()
|
54
|
78
|
{
|
... |
... |
@@ -174,6 +198,7 @@ SettingPanel::openBackgroundPicker() |
174
|
198
|
if (result.isValid())
|
175
|
199
|
{
|
176
|
200
|
backgroundColor_ = result;
|
|
201
|
+ resetColorBlocks();
|
177
|
202
|
emit repaintNeeded();
|
178
|
203
|
}
|
179
|
204
|
}
|
... |
... |
@@ -189,6 +214,7 @@ SettingPanel::openForegroundPicker() |
189
|
214
|
if (result.isValid())
|
190
|
215
|
{
|
191
|
216
|
foregroundColor_ = result;
|
|
217
|
+ resetColorBlocks();
|
192
|
218
|
emit repaintNeeded();
|
193
|
219
|
}
|
194
|
220
|
}
|
... |
... |
@@ -204,6 +230,24 @@ SettingPanel::updateGamma() |
204
|
230
|
}
|
205
|
231
|
|
206
|
232
|
|
|
233
|
+void
|
|
234
|
+SettingPanel::resetColorBlocks()
|
|
235
|
+{
|
|
236
|
+ foregroundBlock_->setStyleSheet(
|
|
237
|
+ QString("QWidget {background-color: rgba(%1, %2, %3, %4);}")
|
|
238
|
+ .arg(foregroundColor_.red())
|
|
239
|
+ .arg(foregroundColor_.blue())
|
|
240
|
+ .arg(foregroundColor_.green())
|
|
241
|
+ .arg(foregroundColor_.alpha()));
|
|
242
|
+ backgroundBlock_->setStyleSheet(
|
|
243
|
+ QString("QWidget {background-color: rgba(%1, %2, %3, %4);}")
|
|
244
|
+ .arg(backgroundColor_.red())
|
|
245
|
+ .arg(backgroundColor_.blue())
|
|
246
|
+ .arg(backgroundColor_.green())
|
|
247
|
+ .arg(backgroundColor_.alpha()));
|
|
248
|
+}
|
|
249
|
+
|
|
250
|
+
|
207
|
251
|
void
|
208
|
252
|
SettingPanel::checkHintingMode()
|
209
|
253
|
{
|
... |
... |
@@ -427,13 +471,10 @@ SettingPanel::createConnections() |
427
|
471
|
this, &SettingPanel::repaintNeeded);
|
428
|
472
|
}
|
429
|
473
|
|
430
|
|
- if (!comparatorMode_)
|
431
|
|
- {
|
432
|
|
- connect(backgroundButton_, &QPushButton::clicked,
|
433
|
|
- this, &SettingPanel::openBackgroundPicker);
|
434
|
|
- connect(foregroundButton_, &QPushButton::clicked,
|
435
|
|
- this, &SettingPanel::openForegroundPicker);
|
436
|
|
- }
|
|
474
|
+ connect(backgroundButton_, &QPushButton::clicked,
|
|
475
|
+ this, &SettingPanel::openBackgroundPicker);
|
|
476
|
+ connect(foregroundButton_, &QPushButton::clicked,
|
|
477
|
+ this, &SettingPanel::openForegroundPicker);
|
437
|
478
|
|
438
|
479
|
connect(mmgxPanel_, &SettingPanelMMGX::mmgxCoordsChanged,
|
439
|
480
|
this, &SettingPanel::fontReloadNeeded);
|
... |
... |
@@ -518,16 +559,25 @@ SettingPanel::createLayout() |
518
|
559
|
gammaSlider_->setRange(0, 30); // in 1/10th
|
519
|
560
|
gammaSlider_->setTickPosition(QSlider::TicksBelow);
|
520
|
561
|
gammaSlider_->setTickInterval(5);
|
|
562
|
+ gammaSlider_->setPageStep(1);
|
|
563
|
+ gammaSlider_->setSingleStep(1);
|
521
|
564
|
gammaLabel_->setBuddy(gammaSlider_);
|
522
|
565
|
gammaValueLabel_ = new QLabel(this);
|
523
|
566
|
|
524
|
567
|
mmgxPanel_ = new SettingPanelMMGX(this, engine_);
|
525
|
568
|
|
526
|
|
- if (!comparatorMode_)
|
527
|
|
- {
|
528
|
|
- backgroundButton_ = new QPushButton(tr("Background"), this);
|
529
|
|
- foregroundButton_ = new QPushButton(tr("Foreground"), this);
|
530
|
|
- }
|
|
569
|
+ backgroundButton_ = new QPushButton(tr("Background"), this);
|
|
570
|
+ foregroundButton_ = new QPushButton(tr("Foreground"), this);
|
|
571
|
+
|
|
572
|
+ backgroundBlock_ = new QFrame(this);
|
|
573
|
+ backgroundBlock_->setFrameStyle(QFrame::Box);
|
|
574
|
+ backgroundBlock_->setLineWidth(1);
|
|
575
|
+ backgroundBlock_->setFixedWidth(18);
|
|
576
|
+
|
|
577
|
+ foregroundBlock_ = new QFrame(this);
|
|
578
|
+ foregroundBlock_->setFrameStyle(QFrame::Box);
|
|
579
|
+ foregroundBlock_->setLineWidth(1);
|
|
580
|
+ foregroundBlock_->setFixedWidth(18);
|
531
|
581
|
|
532
|
582
|
generalTab_ = new QWidget(this);
|
533
|
583
|
|
... |
... |
@@ -571,11 +621,8 @@ SettingPanel::createLayout() |
571
|
621
|
tr("Enable LSB/RSB delta positioning (only valid when hinting is "
|
572
|
622
|
"enabled)."));
|
573
|
623
|
}
|
574
|
|
- if (!comparatorMode_)
|
575
|
|
- {
|
576
|
|
- backgroundButton_->setToolTip(tr("Set canvas background color."));
|
577
|
|
- foregroundButton_->setToolTip(tr("Set text color."));
|
578
|
|
- }
|
|
624
|
+ backgroundButton_->setToolTip(tr("Set canvas background color."));
|
|
625
|
+ foregroundButton_->setToolTip(tr("Set text color."));
|
579
|
626
|
|
580
|
627
|
// Layouting
|
581
|
628
|
if (debugMode_)
|
... |
... |
@@ -593,6 +640,12 @@ SettingPanel::createLayout() |
593
|
640
|
gammaLayout_->addWidget(gammaSlider_);
|
594
|
641
|
gammaLayout_->addWidget(gammaValueLabel_);
|
595
|
642
|
|
|
643
|
+ colorPickerLayout_ = new QHBoxLayout;
|
|
644
|
+ colorPickerLayout_->addWidget(backgroundBlock_);
|
|
645
|
+ colorPickerLayout_->addWidget(backgroundButton_, 1);
|
|
646
|
+ colorPickerLayout_->addWidget(foregroundButton_, 1);
|
|
647
|
+ colorPickerLayout_->addWidget(foregroundBlock_);
|
|
648
|
+
|
596
|
649
|
if (comparatorMode_)
|
597
|
650
|
createLayoutComperator();
|
598
|
651
|
else
|
... |
... |
@@ -632,12 +685,7 @@ SettingPanel::createLayoutNormal() |
632
|
685
|
new QSpacerItem(0, 20, QSizePolicy::Minimum,
|
633
|
686
|
QSizePolicy::MinimumExpanding));
|
634
|
687
|
|
635
|
|
- colorPickerLayout_ = new QHBoxLayout;
|
636
|
|
- colorPickerLayout_->addWidget(backgroundButton_, 1);
|
637
|
|
- colorPickerLayout_->addWidget(foregroundButton_, 1);
|
638
|
|
- generalTabLayout_->addLayout(colorPickerLayout_,
|
639
|
|
- generalTabLayout_->rowCount(), 0, 1, 2);
|
640
|
|
-
|
|
688
|
+ gridLayout2ColAddLayout(generalTabLayout_, colorPickerLayout_);
|
641
|
689
|
gridLayout2ColAddLayout(generalTabLayout_, gammaLayout_);
|
642
|
690
|
gridLayout2ColAddWidget(generalTabLayout_, stemDarkeningCheckBox_);
|
643
|
691
|
gridLayout2ColAddWidget(generalTabLayout_, embeddedBitmapCheckBox_);
|
... |
... |
@@ -686,6 +734,7 @@ SettingPanel::createLayoutComperator() |
686
|
734
|
gridLayout2ColAddWidget(hintingRenderingTabLayout_, stemDarkeningCheckBox_);
|
687
|
735
|
|
688
|
736
|
// General
|
|
737
|
+ gridLayout2ColAddLayout(generalTabLayout_, colorPickerLayout_);
|
689
|
738
|
gridLayout2ColAddWidget(generalTabLayout_, embeddedBitmapCheckBox_);
|
690
|
739
|
gridLayout2ColAddWidget(generalTabLayout_, colorLayerCheckBox_);
|
691
|
740
|
gridLayout2ColAddWidget(generalTabLayout_,
|
... |
... |
@@ -755,6 +804,7 @@ SettingPanel::setDefaults() |
755
|
804
|
// These need to be set even in Comperator mode.
|
756
|
805
|
backgroundColor_ = Qt::white;
|
757
|
806
|
foregroundColor_ = Qt::black;
|
|
807
|
+ resetColorBlocks();
|
758
|
808
|
|
759
|
809
|
gammaSlider_->setValue(18); // 1.8
|
760
|
810
|
updateGamma();
|
src/ftinspect/panels/settingpanel.hpp
... |
... |
@@ -38,6 +38,7 @@ public: |
38
|
38
|
int antiAliasingModeIndex();
|
39
|
39
|
bool kerningEnabled();
|
40
|
40
|
bool lsbRsbDeltaEnabled();
|
|
41
|
+ void setDefaultsPreset(int preset);
|
41
|
42
|
|
42
|
43
|
signals:
|
43
|
44
|
void fontReloadNeeded();
|
... |
... |
@@ -111,6 +112,8 @@ private: |
111
|
112
|
|
112
|
113
|
QPushButton* backgroundButton_;
|
113
|
114
|
QPushButton* foregroundButton_;
|
|
115
|
+ QFrame* backgroundBlock_;
|
|
116
|
+ QFrame* foregroundBlock_;
|
114
|
117
|
|
115
|
118
|
QVBoxLayout* mainLayout_;
|
116
|
119
|
QGridLayout* generalTabLayout_;
|
... |
... |
@@ -135,6 +138,7 @@ private: |
135
|
138
|
void openBackgroundPicker();
|
136
|
139
|
void openForegroundPicker();
|
137
|
140
|
void updateGamma();
|
|
141
|
+ void resetColorBlocks();
|
138
|
142
|
};
|
139
|
143
|
|
140
|
144
|
|
src/ftinspect/panels/singular.cpp
... |
... |
@@ -276,7 +276,7 @@ SingularTab::resizeEvent(QResizeEvent* event) |
276
|
276
|
|
277
|
277
|
auto viewSize = glyphView_->size();
|
278
|
278
|
auto minViewSide = std::min(viewSize.height(), viewSize.width());
|
279
|
|
- zoomSpinBox_->setValue(static_cast<int>(minViewSide / size * 0.9));
|
|
279
|
+ zoomSpinBox_->setValue(static_cast<int>(minViewSide / size * 0.7));
|
280
|
280
|
}
|
281
|
281
|
|
282
|
282
|
|
|