freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[freetype2-demos] gsoc-2022-chariri-3 b6b79a8 27/36: [ftinspect] Improve


From: Werner Lemberg
Subject: [freetype2-demos] gsoc-2022-chariri-3 b6b79a8 27/36: [ftinspect] Improve Font/Face/NI selector.
Date: Wed, 27 Jul 2022 06:32:46 -0400 (EDT)

branch: gsoc-2022-chariri-3
commit b6b79a8ae125cf06e28ed6aa0320805acb455374
Author: Charlie Jiang <w@chariri.moe>
Commit: Charlie Jiang <w@chariri.moe>

    [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.
---
 src/ftinspect/CMakeLists.txt              |   1 +
 src/ftinspect/engine/engine.cpp           |  91 ++++--
 src/ftinspect/engine/engine.hpp           |   7 +-
 src/ftinspect/engine/fontfilemanager.cpp  |   1 +
 src/ftinspect/ftinspect.cpp               |   2 -
 src/ftinspect/maingui.cpp                 | 350 ++--------------------
 src/ftinspect/maingui.hpp                 |  47 +--
 src/ftinspect/meson.build                 |   2 +
 src/ftinspect/panels/settingpanel.cpp     |   2 +
 src/ftinspect/widgets/tripletselector.cpp | 474 ++++++++++++++++++++++++++++++
 src/ftinspect/widgets/tripletselector.hpp |  66 +++++
 11 files changed, 648 insertions(+), 395 deletions(-)

diff --git a/src/ftinspect/CMakeLists.txt b/src/ftinspect/CMakeLists.txt
index 4fe2a52..e544f7e 100644
--- a/src/ftinspect/CMakeLists.txt
+++ b/src/ftinspect/CMakeLists.txt
@@ -37,6 +37,7 @@ add_executable(ftinspect
   "widgets/customwidgets.cpp"
   "widgets/glyphindexselector.cpp"
   "widgets/fontsizeselector.cpp"
+  "widgets/tripletselector.cpp"
 
   "models/customcomboboxmodels.cpp"
 
diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index da0573b..4ee99d0 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -99,13 +99,14 @@ faceRequester(FTC_FaceID ftcFaceID,
   if (faceID.fontIndex < 0
       || faceID.fontIndex >= engine->numberOfOpenedFonts())
     return FT_Err_Invalid_Argument;
-
+  
   QString font = engine->fontFileManager_[faceID.fontIndex].filePath();
   long faceIndex = faceID.faceIndex;
 
   if (faceID.namedInstanceIndex > 0)
     faceIndex += faceID.namedInstanceIndex << 16;
 
+  *faceP = NULL;
   return FT_New_Face(library,
                      qPrintable(font),
                      faceIndex,
@@ -175,11 +176,13 @@ Engine::numberOfFaces(int fontIndex)
   FT_Face face;
   long numFaces = -1;
 
+  if (fontIndex < 0)
+    return -1;
+
+  auto id = FaceID(fontIndex, 0, 0);
+
   // search triplet (fontIndex, 0, 0)
-  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
-                           (faceIDMap_.value(FaceID(fontIndex,
-                                                   0,
-                                                   0)));
+  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
   if (ftcFaceID)
   {
     // found
@@ -190,14 +193,13 @@ Engine::numberOfFaces(int fontIndex)
   {
     // not found; try to load triplet (fontIndex, 0, 0)
     ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex, 0, 0),
-                     faceCounter_++);
+    faceIDMap_.insert(id, faceCounter_++);
 
     if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
       numFaces = face->num_faces;
     else
     {
-      faceIDMap_.remove(FaceID(fontIndex, 0, 0));
+      faceIDMap_.remove(id);
       faceCounter_--;
     }
   }
@@ -214,12 +216,12 @@ Engine::numberOfNamedInstances(int fontIndex,
   // we return `n' named instances plus one;
   // instance index 0 represents a face without a named instance selected
   int numNamedInstances = -1;
+  if (fontIndex < 0)
+    return -1;
+  auto id = FaceID(fontIndex, faceIndex, 0);
 
   // search triplet (fontIndex, faceIndex, 0)
-  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
-                           (faceIDMap_.value(FaceID(fontIndex,
-                                                   faceIndex,
-                                                   0)));
+  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
   if (ftcFaceID)
   {
     // found
@@ -230,14 +232,13 @@ Engine::numberOfNamedInstances(int fontIndex,
   {
     // not found; try to load triplet (fontIndex, faceIndex, 0)
     ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex, faceIndex, 0),
-                     faceCounter_++);
+    faceIDMap_.insert(id, faceCounter_++);
 
     if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
       numNamedInstances = static_cast<int>((face->style_flags >> 16) + 1);
     else
     {
-      faceIDMap_.remove(FaceID(fontIndex, faceIndex, 0));
+      faceIDMap_.remove(id);
       faceCounter_--;
     }
   }
@@ -246,6 +247,48 @@ Engine::numberOfNamedInstances(int fontIndex,
 }
 
 
+QString
+Engine::namedInstanceName(int fontIndex, long faceIndex, int index)
+{
+  FT_Face face;
+  QString name;
+
+  if (fontIndex < 0)
+    return QString();
+
+  auto id = FaceID(fontIndex, faceIndex, index);
+
+  // search triplet (fontIndex, faceIndex, 0)
+  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
+  if (ftcFaceID)
+  {
+    // found
+    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
+      name = QString("%1 %2")
+               .arg(face->family_name)
+               .arg(face->style_name);
+  }
+  else
+  {
+    // not found; try to load triplet (fontIndex, faceIndex, 0)
+    ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
+    faceIDMap_.insert(id, faceCounter_++);
+
+    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
+      name = QString("%1 %2")
+               .arg(face->family_name)
+               .arg(face->style_name);
+    else
+    {
+      faceIDMap_.remove(id);
+      faceCounter_--;
+    }
+  }
+
+  return name;
+}
+
+
 int
 Engine::currentFontFirstUnicodeCharMap()
 {
@@ -267,34 +310,28 @@ Engine::loadFont(int fontIndex,
 
   update();
 
+  auto id = FaceID(fontIndex, faceIndex, namedInstanceIndex);
+
   // search triplet (fontIndex, faceIndex, namedInstanceIndex)
-  scaler_.face_id = reinterpret_cast<FTC_FaceID>
-                     (faceIDMap_.value(FaceID(fontIndex,
-                                             faceIndex,
-                                             namedInstanceIndex)));
+  scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
   if (scaler_.face_id)
   {
     // found
     if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
       numGlyphs = ftSize_->face->num_glyphs;
   }
-  else
+  else if (fontIndex >= 0)
   {
     // not found; try to load triplet
     // (fontIndex, faceIndex, namedInstanceIndex)
     scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex,
-                            faceIndex,
-                            namedInstanceIndex),
-                     faceCounter_++);
+    faceIDMap_.insert(id, faceCounter_++);
 
     if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
       numGlyphs = ftSize_->face->num_glyphs;
     else
     {
-      faceIDMap_.remove(FaceID(fontIndex,
-                              faceIndex,
-                              namedInstanceIndex));
+      faceIDMap_.remove(id);
       faceCounter_--;
     }
   }
diff --git a/src/ftinspect/engine/engine.hpp b/src/ftinspect/engine/engine.hpp
index 2d89647..f4bff2d 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -107,17 +107,22 @@ public:
 
   FT_Library ftLibrary() const { return library_; }
   FTC_Manager cacheManager() { return cacheManager_; }
+
   int dpi() { return dpi_; }
   double pointSize() { return pointSize_; }
+
+  int numberOfOpenedFonts();
   int currentFontType() const { return fontType_; }
   const QString& currentFamilyName() { return curFamilyName_; }
   const QString& currentStyleName() { return curStyleName_; }
   int currentFontNumberOfGlyphs() { return curNumGlyphs_; }
-  int numberOfOpenedFonts();
+
   QString glyphName(int glyphIndex);
   long numberOfFaces(int fontIndex);
   int numberOfNamedInstances(int fontIndex,
                              long faceIndex);
+  QString namedInstanceName(int fontIndex, long faceIndex, int index);
+
   int currentFontFirstUnicodeCharMap();
   // Note: the current font face must be properly set
   unsigned glyphIndexFromCharCode(int code, int charMapIndex);
diff --git a/src/ftinspect/engine/fontfilemanager.cpp 
b/src/ftinspect/engine/fontfilemanager.cpp
index 9931430..c7f4861 100644
--- a/src/ftinspect/engine/fontfilemanager.cpp
+++ b/src/ftinspect/engine/fontfilemanager.cpp
@@ -32,6 +32,7 @@ FontFileManager::append(QStringList newFileNames)
   for (auto& name : newFileNames)
   {
     auto info = QFileInfo(name);
+    info.setCaching(false);
 
     // Filter non-file elements
     if (!info.isFile())
diff --git a/src/ftinspect/ftinspect.cpp b/src/ftinspect/ftinspect.cpp
index 128dff4..4060f9c 100644
--- a/src/ftinspect/ftinspect.cpp
+++ b/src/ftinspect/ftinspect.cpp
@@ -24,8 +24,6 @@ main(int argc,
   Engine engine;
   MainGUI gui(&engine);
 
-  gui.setDefaults();
-
   gui.show();
 
   return app.exec();
diff --git a/src/ftinspect/maingui.cpp b/src/ftinspect/maingui.cpp
index ab825ad..1fc93a7 100644
--- a/src/ftinspect/maingui.cpp
+++ b/src/ftinspect/maingui.cpp
@@ -25,7 +25,6 @@ MainGUI::MainGUI(Engine* engine)
   createConnections();
   createActions();
   createMenus();
-  createStatusBar();
   setupDragDrop();
 
   readSettings();
@@ -132,103 +131,13 @@ MainGUI::openFonts(QStringList const& fileNames)
   int oldSize = engine_->numberOfOpenedFonts();
   engine_->openFonts(fileNames);
 
-  // if we have new fonts, set the current index to the first new one
-  if (oldSize < engine_->numberOfOpenedFonts())
-    currentFontIndex_ = oldSize;
-
-  showFont();
-}
-
-
-void
-MainGUI::closeFont()
-{
-  if (currentFontIndex_ < engine_->numberOfOpenedFonts())
-  {
-    engine_->removeFont(currentFontIndex_);
-  }
-
-  // show next font after deletion, i.e., retain index if possible
-  int num = engine_->numberOfOpenedFonts();
-  if (num)
-  {
-    if (currentFontIndex_ >= num)
-      currentFontIndex_ = num - 1;
-  }
-  else
-    currentFontIndex_ = 0;
-
-  showFont();
-}
-
-
-void
-MainGUI::watchCurrentFont()
-{
-  showFont();
+  tripletSelector_->repopulateFonts();
 }
 
 
 void
-MainGUI::showFont()
+MainGUI::onTripletChanged()
 {
-  // we do lazy computation of FT_Face objects
-
-  if (currentFontIndex_ < engine_->numberOfOpenedFonts())
-  {
-    QFileInfo& fileInfo = engine_->fontFileManager()[currentFontIndex_];
-    QString fontName = fileInfo.fileName();
-
-    engine_->fontFileManager().updateWatching(currentFontIndex_);
-    if (fileInfo.isSymLink())
-    {
-      fontName.prepend("<i>");
-      fontName.append("</i>");
-    }
-
-    if (!fileInfo.exists())
-    {
-      // On Unix-like systems, the symlink's target gets opened; this
-      // implies that deletion of a symlink doesn't make `engine->loadFont'
-      // fail since it operates on a file handle pointing to the target.
-      // For this reason, we remove the font to enforce a reload.
-      engine_->removeFont(currentFontIndex_, false);
-    }
-
-    fontFilenameLabel_->setText(fontName);
-  }
-  else
-    fontFilenameLabel_->clear();
-
-  syncSettings();
-  currentNumberOfFaces_
-    = engine_->numberOfFaces(currentFontIndex_);
-  currentNumberOfNamedInstances_
-    = engine_->numberOfNamedInstances(currentFontIndex_,
-                                     currentFaceIndex_);
-  currentNumberOfGlyphs_
-    = engine_->loadFont(currentFontIndex_,
-                       currentFaceIndex_,
-                       currentNamedInstanceIndex_);
-
-  if (currentNumberOfGlyphs_ < 0)
-  {
-    // there might be various reasons why the current
-    // (file, face, instance) triplet is invalid or missing;
-    // we thus start our timer to periodically test
-    // whether the font starts working
-    if (currentFontIndex_ > 0
-        && currentFontIndex_ < engine_->numberOfOpenedFonts())
-      engine_->fontFileManager().timerStart();
-  }
-
-  fontNameLabel_->setText(QString("%1 %2")
-                         .arg(engine_->currentFamilyName())
-                         .arg(engine_->currentStyleName()));
-
-  checkCurrentFontIndex();
-  checkCurrentFaceIndex();
-  checkCurrentNamedInstanceIndex();
   auto state = settingPanel_->blockSignals(true);
   settingPanel_->checkHinting();
   settingPanel_->blockSignals(state);
@@ -247,6 +156,7 @@ MainGUI::repaintCurrentTab()
 void
 MainGUI::reloadCurrentTabFont()
 {
+  syncSettings();
   tabs_[tabWidget_->currentIndex()]->reloadFont();
 }
 
@@ -258,180 +168,17 @@ MainGUI::syncSettings()
 }
 
 
-void
-MainGUI::clearStatusBar()
-{
-  statusBar()->clearMessage();
-  statusBar()->setStyleSheet("");
-}
-
-
-void
-MainGUI::checkCurrentFontIndex()
-{
-  if (engine_->numberOfOpenedFonts() < 2)
-  {
-    previousFontButton_->setEnabled(false);
-    nextFontButton_->setEnabled(false);
-  }
-  else if (currentFontIndex_ == 0)
-  {
-    previousFontButton_->setEnabled(false);
-    nextFontButton_->setEnabled(true);
-  }
-  else if (currentFontIndex_ >= engine_->numberOfOpenedFonts() - 1)
-  {
-    previousFontButton_->setEnabled(true);
-    nextFontButton_->setEnabled(false);
-  }
-  else
-  {
-    previousFontButton_->setEnabled(true);
-    nextFontButton_->setEnabled(true);
-  }
-}
-
-
-void
-MainGUI::checkCurrentFaceIndex()
-{
-  if (currentNumberOfFaces_ < 2)
-  {
-    previousFaceButton_->setEnabled(false);
-    nextFaceButton_->setEnabled(false);
-  }
-  else if (currentFaceIndex_ == 0)
-  {
-    previousFaceButton_->setEnabled(false);
-    nextFaceButton_->setEnabled(true);
-  }
-  else if (currentFaceIndex_ >= currentNumberOfFaces_ - 1)
-  {
-    previousFaceButton_->setEnabled(true);
-    nextFaceButton_->setEnabled(false);
-  }
-  else
-  {
-    previousFaceButton_->setEnabled(true);
-    nextFaceButton_->setEnabled(true);
-  }
-}
-
-
-void
-MainGUI::checkCurrentNamedInstanceIndex()
-{
-  if (currentNumberOfNamedInstances_ < 2)
-  {
-    previousNamedInstanceButton_->setEnabled(false);
-    nextNamedInstanceButton_->setEnabled(false);
-  }
-  else if (currentNamedInstanceIndex_ == 0)
-  {
-    previousNamedInstanceButton_->setEnabled(false);
-    nextNamedInstanceButton_->setEnabled(true);
-  }
-  else if (currentNamedInstanceIndex_ >= currentNumberOfNamedInstances_ - 1)
-  {
-    previousNamedInstanceButton_->setEnabled(true);
-    nextNamedInstanceButton_->setEnabled(false);
-  }
-  else
-  {
-    previousNamedInstanceButton_->setEnabled(true);
-    nextNamedInstanceButton_->setEnabled(true);
-  }
-}
-
-
-void
-MainGUI::previousFont()
-{
-  if (currentFontIndex_ > 0)
-  {
-    currentFontIndex_--;
-    currentFaceIndex_ = 0;
-    currentNamedInstanceIndex_ = 0;
-    showFont();
-  }
-}
-
-
-void
-MainGUI::nextFont()
-{
-  if (currentFontIndex_ < engine_->numberOfOpenedFonts() - 1)
-  {
-    currentFontIndex_++;
-    currentFaceIndex_ = 0;
-    currentNamedInstanceIndex_ = 0;
-    showFont();
-  }
-}
-
-
-void
-MainGUI::previousFace()
-{
-  if (currentFaceIndex_ > 0)
-  {
-    currentFaceIndex_--;
-    currentNamedInstanceIndex_ = 0;
-    showFont();
-  }
-}
-
-
-void
-MainGUI::nextFace()
-{
-  if (currentFaceIndex_ < currentNumberOfFaces_ - 1)
-  {
-    currentFaceIndex_++;
-    currentNamedInstanceIndex_ = 0;
-    showFont();
-  }
-}
-
-
-void
-MainGUI::previousNamedInstance()
-{
-  if (currentNamedInstanceIndex_ > 0)
-  {
-    currentNamedInstanceIndex_--;
-    showFont();
-  }
-}
-
-
-void
-MainGUI::nextNamedInstance()
-{
-  if (currentNamedInstanceIndex_ < currentNumberOfNamedInstances_ - 1)
-  {
-    currentNamedInstanceIndex_++;
-    showFont();
-  }
-}
-
-
 // XXX distances are specified in pixels,
 //     making the layout dependent on the output device resolution
 void
 MainGUI::createLayout()
 {
   // left side
-  fontFilenameLabel_ = new QLabel(this);
-
-  infoLeftLayout_ = new QHBoxLayout;
-  infoLeftLayout_->addWidget(fontFilenameLabel_);
-
   settingPanel_ = new SettingPanel(this, engine_);
 
-  leftLayout_ = new QVBoxLayout;
-  leftLayout_->addLayout(infoLeftLayout_);
+  leftLayout_ = new QVBoxLayout; // The only point is to set a margin->remove?
   leftLayout_->addWidget(settingPanel_);
+  leftLayout_->setContentsMargins(32, 32, 8, 16);
 
   // we don't want to expand the left side horizontally;
   // to change the policy we have to use a widget wrapper
@@ -446,8 +193,6 @@ MainGUI::createLayout()
   leftWidget_->setSizePolicy(leftWidgetPolicy);
 
   // right side
-  fontNameLabel_ = new QLabel(this);
-
   singularTab_ = new SingularTab(this, engine_);
   continuousTab_ = new ContinuousTab(this, engine_);
 
@@ -458,43 +203,33 @@ MainGUI::createLayout()
   tabWidget_->addTab(singularTab_, tr("Singular Grid View"));
   tabs_.append(continuousTab_);
   tabWidget_->addTab(continuousTab_, tr("Continuous View"));
-
-  previousFontButton_ = new QPushButton(tr("Previous Font"), this);
-  nextFontButton_ = new QPushButton(tr("Next Font"), this);
-  previousFaceButton_ = new QPushButton(tr("Previous Face"), this);
-  nextFaceButton_ = new QPushButton(tr("Next Face"), this);
-  previousNamedInstanceButton_
-    = new QPushButton(tr("Previous Named Instance"), this);
-  nextNamedInstanceButton_ = new QPushButton(tr("Next Named Instance"), this);
-
-  fontLayout = new QGridLayout;
-  fontLayout->setColumnStretch(0, 2);
-  fontLayout->addWidget(nextFontButton_, 0, 1);
-  fontLayout->addWidget(previousFontButton_, 1, 1);
-  fontLayout->setColumnStretch(2, 1);
-  fontLayout->addWidget(nextFaceButton_, 0, 3);
-  fontLayout->addWidget(previousFaceButton_, 1, 3);
-  fontLayout->setColumnStretch(4, 1);
-  fontLayout->addWidget(nextNamedInstanceButton_, 0, 5);
-  fontLayout->addWidget(previousNamedInstanceButton_, 1, 5);
-  fontLayout->setColumnStretch(6, 2);
+  
+  tripletSelector_ = new TripletSelector(this, engine_);
 
   rightLayout_ = new QVBoxLayout;
-  rightLayout_->addWidget(fontNameLabel_);
-  rightLayout_->addWidget(tabWidget_);
-  rightLayout_->addLayout(fontLayout);
+  //rightLayout_->addWidget(fontNameLabel_);
+  rightLayout_->addWidget(tabWidget_); // same for `leftLayout_`: Remove?
+  rightLayout_->setContentsMargins(8, 32, 32, 16);
 
   // for symmetry with the left side use a widget also
   rightWidget_ = new QWidget(this);
   rightWidget_->setLayout(rightLayout_);
 
   // the whole thing
-  ftinspectLayout_ = new QHBoxLayout;
-  ftinspectLayout_->addWidget(leftWidget_);
-  ftinspectLayout_->addWidget(rightWidget_);
+  mainPartLayout_ = new QHBoxLayout;
+  mainPartLayout_->addWidget(leftWidget_);
+  mainPartLayout_->addWidget(rightWidget_);
+
+  ftinspectLayout_ = new QVBoxLayout;
+  ftinspectLayout_->setSpacing(0);
+  ftinspectLayout_->addLayout(mainPartLayout_);
+  ftinspectLayout_->addWidget(tripletSelector_);
+  ftinspectLayout_->setContentsMargins(0, 0, 0, 0);
 
   ftinspectWidget_ = new QWidget(this);
   ftinspectWidget_->setLayout(ftinspectLayout_);
+
+  statusBar()->hide(); // remove the extra space
   setCentralWidget(ftinspectWidget_);
   setWindowTitle("ftinspect");
 }
@@ -504,28 +239,15 @@ void
 MainGUI::createConnections()
 {
   connect(settingPanel_, &SettingPanel::fontReloadNeeded,
-          this, &MainGUI::showFont);
+          this, &MainGUI::reloadCurrentTabFont);
   connect(settingPanel_, &SettingPanel::repaintNeeded,
           this, &MainGUI::repaintCurrentTab);
 
   connect(tabWidget_, &QTabWidget::currentChanged,
           this, &MainGUI::reloadCurrentTabFont);
 
-  connect(previousFontButton_, &QPushButton::clicked,
-          this, &MainGUI::previousFont);
-  connect(nextFontButton_, &QPushButton::clicked,
-          this, &MainGUI::nextFont);
-  connect(previousFaceButton_, &QPushButton::clicked,
-          this, &MainGUI::previousFace);
-  connect(nextFaceButton_, &QPushButton::clicked,
-          this, &MainGUI::nextFace);
-  connect(previousNamedInstanceButton_, &QPushButton::clicked,
-          this, &MainGUI::previousNamedInstance);
-  connect(nextNamedInstanceButton_, &QPushButton::clicked,
-          this, &MainGUI::nextNamedInstance);
-
-  connect(&engine_->fontFileManager(), &FontFileManager::currentFileChanged,
-          this, &MainGUI::watchCurrentFont);
+  connect(tripletSelector_, &TripletSelector::tripletChanged,
+          this, &MainGUI::onTripletChanged);
 }
 
 
@@ -538,7 +260,8 @@ MainGUI::createActions()
 
   closeFontAct_ = new QAction(tr("&Close Font"), this);
   closeFontAct_->setShortcuts(QKeySequence::Close);
-  connect(closeFontAct_, &QAction::triggered, this, &MainGUI::closeFont);
+  connect(closeFontAct_, &QAction::triggered,
+          tripletSelector_, &TripletSelector::closeCurrentFont);
 
   exitAct_ = new QAction(tr("E&xit"), this);
   exitAct_->setShortcuts(QKeySequence::Quit);
@@ -566,13 +289,6 @@ MainGUI::createMenus()
 }
 
 
-void
-MainGUI::createStatusBar()
-{
-  statusBar()->showMessage("");
-}
-
-
 void
 MainGUI::setupDragDrop()
 {
@@ -580,20 +296,6 @@ MainGUI::setupDragDrop()
 }
 
 
-void
-MainGUI::setDefaults()
-{
-  // the next four values always non-negative
-  currentFontIndex_ = 0;
-  currentFaceIndex_ = 0;
-  currentNamedInstanceIndex_ = 0;
-  
-  checkCurrentFontIndex();
-  checkCurrentFaceIndex();
-  checkCurrentNamedInstanceIndex();
-}
-
-
 void
 MainGUI::readSettings()
 {
diff --git a/src/ftinspect/maingui.hpp b/src/ftinspect/maingui.hpp
index 8475f36..2d95332 100644
--- a/src/ftinspect/maingui.hpp
+++ b/src/ftinspect/maingui.hpp
@@ -6,9 +6,7 @@
 #pragma once
 
 #include "engine/engine.hpp"
-#include "widgets/customwidgets.hpp"
-#include "widgets/glyphindexselector.hpp"
-#include "models/customcomboboxmodels.hpp"
+#include "widgets/tripletselector.hpp"
 #include "panels/settingpanel.hpp"
 #include "panels/singular.hpp"
 #include "panels/continuous.hpp"
@@ -53,8 +51,6 @@ public:
   MainGUI(Engine* engine);
   ~MainGUI() override;
 
-  void setDefaults();
-
   friend class Engine;
   friend FT_Error faceRequester(FTC_FaceID,
                                 FT_Library,
@@ -69,33 +65,14 @@ protected:
 private slots:
   void about();
   void aboutQt();
-  void checkCurrentFaceIndex();
-  void checkCurrentFontIndex();
-  void checkCurrentNamedInstanceIndex();
-  void closeFont();
-  void showFont();
   void repaintCurrentTab();
   void reloadCurrentTabFont();
   void loadFonts();
-  void nextFace();
-  void nextFont();
-  void nextNamedInstance();
-  void previousFace();
-  void previousFont();
-  void previousNamedInstance();
-  void watchCurrentFont();
+  void onTripletChanged();
 
 private:
   Engine* engine_;
   
-  int currentFontIndex_;
-
-  long currentNumberOfFaces_;
-  long currentFaceIndex_;
-
-  int currentNumberOfNamedInstances_;
-  int currentNamedInstanceIndex_;
-
   int currentNumberOfGlyphs_;
 
   // layout related stuff
@@ -105,25 +82,15 @@ private:
   QAction *exitAct_;
   QAction *loadFontsAct_;
 
-  QGridLayout *fontLayout;
-
-  QHBoxLayout *ftinspectLayout_;
-  QHBoxLayout *infoLeftLayout_;
-
-  QLabel *fontFilenameLabel_;
-  QLabel *fontNameLabel_;
+  QVBoxLayout *ftinspectLayout_;
+  QHBoxLayout *mainPartLayout_;
 
   QLocale *locale_;
 
   QMenu *menuFile_;
   QMenu *menuHelp_;
-
-  QPushButton *nextFaceButton_;
-  QPushButton *nextFontButton_;
-  QPushButton *nextNamedInstanceButton_;
-  QPushButton *previousFaceButton_;
-  QPushButton *previousFontButton_;
-  QPushButton *previousNamedInstanceButton_;
+  
+  TripletSelector* tripletSelector_;
   
   QVBoxLayout *leftLayout_;
   QVBoxLayout *rightLayout_;
@@ -142,13 +109,11 @@ private:
   void openFonts(QStringList const& fileNames);
 
   void syncSettings();
-  void clearStatusBar();
 
   void createActions();
   void createConnections();
   void createLayout();
   void createMenus();
-  void createStatusBar();
   void setupDragDrop();
 
   void readSettings();
diff --git a/src/ftinspect/meson.build b/src/ftinspect/meson.build
index 50a7a63..ed9a328 100644
--- a/src/ftinspect/meson.build
+++ b/src/ftinspect/meson.build
@@ -36,6 +36,7 @@ if qt5_dep.found()
     'widgets/customwidgets.cpp',
     'widgets/glyphindexselector.cpp',
     'widgets/fontsizeselector.cpp',
+    'widgets/tripletselector.cpp',
 
     'models/customcomboboxmodels.cpp',
 
@@ -54,6 +55,7 @@ if qt5_dep.found()
       'widgets/customwidgets.hpp',
       'widgets/glyphindexselector.hpp',
       'widgets/fontsizeselector.hpp',
+      'widgets/tripletselector.hpp',
       'rendering/glyphcontinuous.hpp',
       'models/customcomboboxmodels.hpp',
       'panels/settingpanel.hpp',
diff --git a/src/ftinspect/panels/settingpanel.cpp 
b/src/ftinspect/panels/settingpanel.cpp
index 7825e10..6eeec70 100644
--- a/src/ftinspect/panels/settingpanel.cpp
+++ b/src/ftinspect/panels/settingpanel.cpp
@@ -358,6 +358,8 @@ SettingPanel::createLayout()
   mainLayout_ = new QVBoxLayout;
   mainLayout_->addWidget(tab_);
   setLayout(mainLayout_);
+  mainLayout_->setContentsMargins(0, 0, 0, 0);
+  setContentsMargins(0, 0, 0, 0);
 }
 
 
diff --git a/src/ftinspect/widgets/tripletselector.cpp 
b/src/ftinspect/widgets/tripletselector.cpp
new file mode 100644
index 0000000..6985027
--- /dev/null
+++ b/src/ftinspect/widgets/tripletselector.cpp
@@ -0,0 +1,474 @@
+// tripletselector.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "tripletselector.hpp"
+
+#include "../engine/engine.hpp"
+
+#include <functional>
+
+TripletSelector::TripletSelector(QWidget* parent,
+                                 Engine* engine)
+: QWidget(parent),
+  engine_(engine)
+{
+  createLayout();
+  createConnections();
+  checkButtons();
+}
+
+
+TripletSelector::~TripletSelector()
+{
+}
+
+
+void
+TripletSelector::repopulateFonts()
+{
+  auto oldSize = fontComboBox_->count();
+  auto oldIndex = fontComboBox_->currentIndex();
+
+  {
+    QSignalBlocker blk(fontComboBox_);
+    QSignalBlocker blk2(faceComboBox_);
+    QSignalBlocker blk3(niComboBox_);
+    fontComboBox_->clear();
+    
+    auto& ffm = engine_->fontFileManager();
+    auto newSize = ffm.size();
+    for (int i = 0; i < newSize; i++)
+    {
+      auto& info = ffm[i];
+      auto name = info.filePath();
+      auto displayName = info.fileName();
+      if (info.isSymbolicLink())
+        displayName += " [symlink]";
+
+      fontComboBox_->addItem(displayName, name);
+    }
+
+    if (newSize > oldSize)
+    {
+      // if we have new fonts, set the current index to the first new one
+      fontComboBox_->setCurrentIndex(oldSize);
+    }
+    else if (newSize < oldSize)
+    {
+      if (oldIndex >= newSize)
+        oldIndex = newSize - 1;
+      if (oldIndex < 0)
+        oldIndex = -1;
+      fontComboBox_->setCurrentIndex(oldIndex);
+    }
+
+    // Note no signal will be emitted from any combobox until this block ends
+  }
+
+  // This will check buttons & reload the triplet
+  repopulateFaces();
+}
+
+
+void
+TripletSelector::repopulateFaces(bool fontSwitched)
+{
+  // Avoid unnecessary recreating, to reduce interruption of user oper
+  auto needToRecreate = fontSwitched;
+  auto oldSize = faceComboBox_->count();
+
+  auto fontIndex = fontComboBox_->currentIndex();
+  auto newSize = engine_->numberOfFaces(fontIndex);
+
+  if (fontIndex < 0 || newSize < 0)
+  {
+    // Clear and go
+    faceComboBox_->clear();
+    repopulateNamedInstances(fontSwitched);
+    return;
+  }
+
+  if (newSize != oldSize)
+    needToRecreate = true;
+
+  std::vector<QString> newFaces;
+  newFaces.reserve(newSize);
+  for (long i = 0; i < newSize; i++)
+  {
+    newFaces.emplace_back(engine_->namedInstanceName(fontIndex, i, 0));
+    if (!needToRecreate && newFaces[i] != faceComboBox_->itemData(i))
+      needToRecreate = true;
+  }
+
+  if (!needToRecreate)
+  {
+    // no need to refersh the combobox
+    repopulateNamedInstances(fontSwitched);
+    return;
+  }
+
+  {
+    QSignalBlocker blk2(faceComboBox_);
+    QSignalBlocker blk3(niComboBox_);
+    faceComboBox_->clear();
+
+    for (long i = 0; i < newSize; i++)
+    {
+      auto& name = newFaces[i];
+      auto displayName = QString("%1: %2").arg(i).arg(name);
+      faceComboBox_->addItem(displayName, name);
+    }
+
+    faceComboBox_->setCurrentIndex(0);
+    // Note no signal will be emitted from any combobox until this block ends
+  }
+
+  // This will check buttons & reload the triplet
+  repopulateNamedInstances(true);
+}
+
+
+void
+TripletSelector::repopulateNamedInstances(bool fontSwitched)
+{
+  // Avoid unnecessary recreating, to reduce interruption of user oper
+  // Similar to `repopulateFaces`
+  auto needToRecreate = fontSwitched;
+  auto oldSize = niComboBox_->count();
+
+  auto fontIndex = fontComboBox_->currentIndex();
+  auto faceIndex = faceComboBox_->currentIndex();
+  auto newSize = engine_->numberOfNamedInstances(fontIndex, faceIndex);
+
+  if (fontIndex < 0 || faceIndex < 0 || newSize < 0)
+  {
+    // Clear and go
+    niComboBox_->clear();
+    checkButtons();
+    loadTriplet();
+    return;
+  }
+
+  if (newSize != oldSize)
+    needToRecreate = true;
+
+  std::vector<QString> newFaces;
+  newFaces.reserve(newSize);
+  for (long i = 0; i < newSize; i++)
+  {
+    newFaces.emplace_back(engine_->namedInstanceName(fontIndex, faceIndex, i));
+    if (!needToRecreate && newFaces[i] != niComboBox_->itemData(i))
+      needToRecreate = true;
+  }
+
+  niComboBox_->setEnabled(newSize > 1);
+
+  if (!needToRecreate)
+  {
+    // no need to refersh the combobox
+    checkButtons();
+    loadTriplet();
+    return;
+  }
+
+  {
+    QSignalBlocker blk3(niComboBox_);
+    niComboBox_->clear();
+
+    for (long i = 0; i < newSize; i++)
+    {
+      auto& name = newFaces[i];
+      auto displayName = QString("%1: %2").arg(i).arg(name);
+      if (i == 0)
+        displayName = "* " + displayName;
+      niComboBox_->addItem(displayName, name);
+    }
+
+    niComboBox_->setCurrentIndex(0);
+    // Note no signal will be emitted from any combobox until this block ends
+  }
+
+  checkButtons();
+  loadTriplet();
+}
+
+
+void
+TripletSelector::closeCurrentFont()
+{
+  auto idx = fontComboBox_->currentIndex();
+  if (idx < 0)
+    return;
+  engine_->fontFileManager().remove(idx);
+
+  // show next font after deletion, i.e., retain index if possible
+  int num = engine_->numberOfOpenedFonts();
+  if (num)
+  {
+    if (idx >= num)
+      idx = num - 1;
+  }
+  else
+    idx = -1;
+
+  {
+    // Shut up when repopulating
+    QSignalBlocker blockerThis(this);
+    QSignalBlocker blockerComboBox(fontComboBox_);
+    repopulateFonts();
+  }
+
+  if (idx != -1)
+    faceComboBox_->setCurrentIndex(idx);
+  updateFont();
+}
+
+
+void
+TripletSelector::updateFont()
+{
+  auto idx = fontComboBox_->currentIndex();
+  auto num = engine_->numberOfOpenedFonts();
+  if (idx < 0)
+  {
+    faceComboBox_->clear();
+    niComboBox_->clear();
+
+    checkButtons();
+    loadTriplet();
+    return;
+  }
+
+  if (num <= 0 || idx >= num)
+  {
+    // out of sync: this shouldn't happen
+    repopulateFonts();
+    return;
+  }
+
+  // This will check buttons & reload the triplet
+  repopulateFaces();
+}
+
+
+void
+TripletSelector::updateFace()
+{
+  auto idx = faceComboBox_->currentIndex();
+  auto num = engine_->numberOfFaces(fontComboBox_->currentIndex());
+  
+  if (idx >= num)
+  {
+    // out of sync
+    repopulateFaces();
+    return;
+  }
+
+  // This will check buttons & reload the triplet
+  repopulateNamedInstances();
+}
+
+
+void
+TripletSelector::updateNI()
+{
+  auto idx = niComboBox_->currentIndex();
+  auto num = engine_->numberOfNamedInstances(fontComboBox_->currentIndex(),
+                                             faceComboBox_->currentIndex());
+  
+  if (idx >= num)
+  {
+    // out of sync
+    repopulateNamedInstances();
+    return;
+  }
+
+  checkButtons();
+  loadTriplet();
+}
+
+
+void
+TripletSelector::checkButtons()
+{
+  fontUpButton_->setEnabled(fontComboBox_->currentIndex() > 0);
+  fontDownButton_->setEnabled(fontComboBox_->currentIndex()
+                              < fontComboBox_->count() - 1);
+  closeFontButton_->setEnabled(faceComboBox_->currentIndex() >= 0);
+
+  faceUpButton_->setEnabled(faceComboBox_->currentIndex() > 0);
+  faceDownButton_->setEnabled(faceComboBox_->currentIndex()
+                              < faceComboBox_->count() - 1);
+
+  niUpButton_->setEnabled(niComboBox_->currentIndex() > 0);
+  niDownButton_->setEnabled(niComboBox_->currentIndex()
+                            < niComboBox_->count() - 1);
+}
+
+
+void
+TripletSelector::watchCurrentFont()
+{
+  repopulateFaces(false);
+}
+
+
+void
+TripletSelector::createLayout()
+{
+  fontComboBox_ = new QComboBox(this);
+  faceComboBox_ = new QComboBox(this);
+  niComboBox_    = new QComboBox(this);
+
+  fontComboBox_->setPlaceholderText(tr("No font open"));
+  faceComboBox_->setPlaceholderText(tr("No face available"));
+  niComboBox_->setPlaceholderText(tr("No named instance available"));
+  
+  closeFontButton_ = new QToolButton(this);
+  fontUpButton_    = new QToolButton(this);
+  faceUpButton_    = new QToolButton(this);
+  niUpButton_      = new QToolButton(this);
+  fontDownButton_  = new QToolButton(this);
+  faceDownButton_  = new QToolButton(this);
+  niDownButton_    = new QToolButton(this);
+
+  closeFontButton_->setText(tr("Close"));
+  fontUpButton_   ->setText(tr("\xE2\x86\x91"));
+  faceUpButton_   ->setText(tr("\xE2\x86\x91"));
+  niUpButton_     ->setText(tr("\xE2\x86\x91"));
+  fontDownButton_ ->setText(tr("\xE2\x86\x93"));
+  faceDownButton_ ->setText(tr("\xE2\x86\x93"));
+  niDownButton_   ->setText(tr("\xE2\x86\x93"));
+  
+  fontComboBox_   ->setSizePolicy(QSizePolicy::Minimum, 
QSizePolicy::Expanding);
+  faceComboBox_   ->setSizePolicy(QSizePolicy::Minimum, 
QSizePolicy::Expanding);
+  niComboBox_     ->setSizePolicy(QSizePolicy::Minimum, 
QSizePolicy::Expanding);
+  closeFontButton_->setSizePolicy(QSizePolicy::Maximum, 
QSizePolicy::Expanding);
+  fontUpButton_   ->setFixedSize(30, 30);
+  faceUpButton_   ->setFixedSize(30, 30);
+  niUpButton_     ->setFixedSize(30, 30);
+  fontDownButton_ ->setFixedSize(30, 30);
+  faceDownButton_ ->setFixedSize(30, 30);
+  niDownButton_   ->setFixedSize(30, 30);
+
+  layout_ = new QHBoxLayout;
+  layout_->setSpacing(0);
+  layout_->setContentsMargins(0, 0, 0, 0);
+
+  layout_->addWidget(fontComboBox_);
+  layout_->addWidget(fontUpButton_);
+  layout_->addWidget(fontDownButton_);
+  layout_->addWidget(closeFontButton_);
+  layout_->addWidget(faceComboBox_);
+  layout_->addWidget(faceUpButton_);
+  layout_->addWidget(faceDownButton_);
+  layout_->addWidget(niComboBox_);
+  layout_->addWidget(niUpButton_);
+  layout_->addWidget(niDownButton_);
+
+  setFixedHeight(30);
+  setLayout(layout_);
+  layout_->setContentsMargins(0, 0, 0, 0);
+}
+
+
+void
+TripletSelector::createConnections()
+{
+  connect(fontComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
+          this, &TripletSelector::updateFont);
+  connect(faceComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
+          this, &TripletSelector::updateFace);
+  connect(niComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
+          this, &TripletSelector::updateNI);
+
+  connect(closeFontButton_, &QToolButton::clicked, 
+          this, &TripletSelector::closeCurrentFont);
+  connect(fontUpButton_   , &QToolButton::clicked, 
+          this, 
+          std::bind(&TripletSelector::previousComboBoxItem, fontComboBox_));
+  connect(faceUpButton_   , &QToolButton::clicked, 
+          this, 
+          std::bind(&TripletSelector::previousComboBoxItem, faceComboBox_));
+  connect(niUpButton_     , &QToolButton::clicked, 
+          this, 
+          std::bind(&TripletSelector::previousComboBoxItem, niComboBox_));
+  connect(fontDownButton_ , &QToolButton::clicked, 
+          this, 
+          std::bind(&TripletSelector::nextComboBoxItem, fontComboBox_));
+  connect(faceDownButton_ , &QToolButton::clicked, 
+          this,
+          std::bind(&TripletSelector::nextComboBoxItem, faceComboBox_));
+  connect(niDownButton_   , &QToolButton::clicked, 
+          this, 
+          std::bind(&TripletSelector::nextComboBoxItem, niComboBox_));
+
+  connect(&engine_->fontFileManager(), &FontFileManager::currentFileChanged,
+          this, &TripletSelector::watchCurrentFont);
+}
+
+
+
+void
+TripletSelector::loadTriplet()
+{
+  // we do lazy computation of FT_Face objects
+
+  // TODO really?
+  auto fontIndex = fontComboBox_->currentIndex();
+  auto faceIndex = faceComboBox_->currentIndex();
+  auto instanceIndex = niComboBox_->currentIndex();
+
+  if (fontIndex >= 0 && fontIndex < engine_->numberOfOpenedFonts())
+  {
+    QFileInfo& fileInfo = engine_->fontFileManager()[fontIndex];
+    engine_->fontFileManager().updateWatching(fontIndex);
+
+    if (!fileInfo.exists())
+    {
+      // On Unix-like systems, the symlink's target gets opened; this
+      // implies that deletion of a symlink doesn't make `engine->loadFont'
+      // fail since it operates on a file handle pointing to the target.
+      // For this reason, we remove the font to enforce a reload.
+      engine_->removeFont(fontIndex, false);
+    }
+  }
+
+  auto number = engine_->loadFont(fontIndex, faceIndex, instanceIndex);
+
+  if (number < 0)
+  {
+    // there might be various reasons why the current
+    // (file, face, instance) triplet is invalid or missing;
+    // we thus start our timer to periodically test
+    // whether the font starts working
+    if (faceIndex >= 0 && faceIndex < engine_->numberOfOpenedFonts())
+      engine_->fontFileManager().timerStart();
+  }
+
+  emit tripletChanged();
+}
+
+
+void
+TripletSelector::nextComboBoxItem(QComboBox* c)
+{
+  if (c->currentIndex() < 0 || c->currentIndex() >= c->count() - 1)
+    return;
+  // No need to handle further steps, the event handler will take care of these
+  c->setCurrentIndex(c->currentIndex() + 1);
+}
+
+
+void
+TripletSelector::previousComboBoxItem(QComboBox* c)
+{
+  if (c->currentIndex() <= 0)
+    return;
+  // No need to handle further steps, the event handler will take care of these
+  c->setCurrentIndex(c->currentIndex() - 1);
+}
+
+
+// end of tripletselector.cpp
diff --git a/src/ftinspect/widgets/tripletselector.hpp 
b/src/ftinspect/widgets/tripletselector.hpp
new file mode 100644
index 0000000..1c0c603
--- /dev/null
+++ b/src/ftinspect/widgets/tripletselector.hpp
@@ -0,0 +1,66 @@
+// QPushButton.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <vector>
+#include <QWidget>
+#include <QComboBox>
+#include <QPushButton>
+#include <QToolButton>
+#include <QBoxLayout>
+
+class Engine;
+class TripletSelector
+: public QWidget
+{
+  Q_OBJECT
+
+public:
+  TripletSelector(QWidget* parent,
+                  Engine* engine);
+  ~TripletSelector() override;
+
+  void repopulateFonts();
+  void repopulateFaces(bool fontSwitched = true);
+  void repopulateNamedInstances(bool fontSwitched = true);
+  void closeCurrentFont();
+  void updateFont();
+  void updateFace();
+  void updateNI();
+  void loadTriplet();
+
+signals:
+  void tripletChanged();
+
+private:
+  Engine* engine_;
+
+  QComboBox* fontComboBox_;
+  QComboBox* faceComboBox_;
+  QComboBox* niComboBox_;
+
+  QToolButton* closeFontButton_;
+
+  QToolButton* fontUpButton_;
+  QToolButton* fontDownButton_;
+  QToolButton* faceUpButton_;
+  QToolButton* faceDownButton_;
+  QToolButton* niUpButton_;
+  QToolButton* niDownButton_;
+
+  QHBoxLayout* layout_;
+
+  void checkButtons();
+  void watchCurrentFont();
+
+  void createLayout();
+  void createConnections();
+
+  static void nextComboBoxItem(QComboBox* c);
+  static void previousComboBoxItem(QComboBox* c);
+};
+
+
+// end of QPushButton.hpp



reply via email to

[Prev in Thread] Current Thread [Next in Thread]