[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[freetype2-demos] master 9f70ceb 20/41: [ftinspect] Support MM/GX.
From: |
Werner Lemberg |
Subject: |
[freetype2-demos] master 9f70ceb 20/41: [ftinspect] Support MM/GX. |
Date: |
Mon, 3 Oct 2022 11:27:02 -0400 (EDT) |
branch: master
commit 9f70ceb30e40f897575ee6008a33bafa617b0043
Author: Charlie Jiang <w@chariri.moe>
Commit: Werner Lemberg <wl@gnu.org>
[ftinspect] Support MM/GX.
* src/ftinspect/panels/settingpanelmmgx.cpp,
src/ftinspect/panels/settingpanelmmgx.hpp:
Add GUI widgets for MM/GX settings.
* src/ftinspect/engine/mmgx.cpp, src/ftinspect/engine/mmgx.hpp:
Add `MMGXAxisInfo` to retrieve axes info. However, the SFNT name table
isn't implemented, so the SFNT based axis names are unavailable.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
Add `applyMMGXDesignCoords`. Add getters `currentFontMMGXState` and
`currentFontMMGXAxes`. Info is retrieved when loading font.
* src/ftinspect/panels/settingpanel.cpp,
src/ftinspect/panels/settingpanel.hpp: Uncomment functional code.
Remove `checkHintingMode` and `checkStemDarkening`. Applying of delayed
settings is taken care by the `MainGUI`. So emitting `fontReloadNeeded`
simply will do the work.
* src/ftinspect/maingui.cpp: Don't reset the cache so aggressively.
* src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
---
src/ftinspect/CMakeLists.txt | 2 +
src/ftinspect/engine/engine.cpp | 15 ++
src/ftinspect/engine/engine.hpp | 10 +-
src/ftinspect/engine/mmgx.cpp | 83 +++++++
src/ftinspect/engine/mmgx.hpp | 57 +++++
src/ftinspect/maingui.cpp | 3 +-
src/ftinspect/meson.build | 3 +
src/ftinspect/panels/settingpanel.cpp | 34 +--
src/ftinspect/panels/settingpanel.hpp | 6 +-
src/ftinspect/panels/settingpanelmmgx.cpp | 359 ++++++++++++++++++++++++++++++
src/ftinspect/panels/settingpanelmmgx.hpp | 109 +++++++++
11 files changed, 647 insertions(+), 34 deletions(-)
diff --git a/src/ftinspect/CMakeLists.txt b/src/ftinspect/CMakeLists.txt
index a8eaea6..bbb9112 100644
--- a/src/ftinspect/CMakeLists.txt
+++ b/src/ftinspect/CMakeLists.txt
@@ -24,6 +24,7 @@ add_executable(ftinspect
"engine/fontfilemanager.cpp"
"engine/rendering.cpp"
"engine/paletteinfo.cpp"
+ "engine/mmgx.cpp"
"glyphcomponents/glyphbitmap.cpp"
"glyphcomponents/glyphoutline.cpp"
@@ -40,6 +41,7 @@ add_executable(ftinspect
"models/customcomboboxmodels.cpp"
"panels/settingpanel.cpp"
+ "panels/settingpanelmmgx.cpp"
"panels/singular.cpp"
)
target_link_libraries(ftinspect
diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index 9f96109..9cf30b4 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -11,6 +11,7 @@
#include <freetype/ftmodapi.h>
#include <freetype/ftdriver.h>
#include <freetype/ftlcdfil.h>
+#include <freetype/ftmm.h>
/////////////////////////////////////////////////////////////////////////////
@@ -344,6 +345,7 @@ Engine::loadFont(int fontIndex,
else
fontType_ = FontType_Other;
loadPaletteInfos();
+ curMMGXState_ = MMGXAxisInfo::get(this, curMMGXAxes_);
}
curNumGlyphs_ = numGlyphs;
@@ -658,6 +660,19 @@ Engine::setStemDarkening(bool darkening)
}
+void
+Engine::applyMMGXDesignCoords(FT_Fixed* coords,
+ size_t count)
+{
+ if (!ftSize_)
+ return;
+ if (count >= UINT_MAX)
+ count = UINT_MAX - 1;
+ FT_Set_Var_Design_Coordinates(ftSize_->face,
+ static_cast<unsigned>(count), coords);
+}
+
+
void
Engine::update()
{
diff --git a/src/ftinspect/engine/engine.hpp b/src/ftinspect/engine/engine.hpp
index 935a06b..7b264d7 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -8,6 +8,7 @@
#include "fontfilemanager.hpp"
#include "paletteinfo.hpp"
+#include "mmgx.hpp"
#include "rendering.hpp"
#include <memory>
@@ -119,6 +120,8 @@ public:
std::vector<PaletteInfo>& currentFontPalettes() { return curPaletteInfos_; }
FT_Color* currentPalette() { return palette_; }
FT_Palette_Data& currentFontPaletteData() { return paletteData_; }
+ MMGXState currentFontMMGXState() { return curMMGXState_; }
+ std::vector<MMGXAxisInfo>& currentFontMMGXAxes() { return curMMGXAxes_; }
QString glyphName(int glyphIndex);
long numberOfFaces(int fontIndex);
@@ -183,7 +186,8 @@ public:
void setTTInterpreterVersion(int version);
void setStemDarkening(bool darkening);
-
+ void applyMMGXDesignCoords(FT_Fixed* coords, size_t count);
+
//////// Misc
friend FT_Error faceRequester(FTC_FaceID,
@@ -200,11 +204,13 @@ private:
// font info
int curFontIndex_ = -1;
+ int fontType_;
QString curFamilyName_;
QString curStyleName_;
int curNumGlyphs_ = -1;
std::vector<PaletteInfo> curPaletteInfos_;
- int fontType_;
+ MMGXState curMMGXState_ = MMGXState::NoMMGX;
+ std::vector<MMGXAxisInfo> curMMGXAxes_;
// basic objects
FT_Library library_;
diff --git a/src/ftinspect/engine/mmgx.cpp b/src/ftinspect/engine/mmgx.cpp
new file mode 100644
index 0000000..b978272
--- /dev/null
+++ b/src/ftinspect/engine/mmgx.cpp
@@ -0,0 +1,83 @@
+// mmgx.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "mmgx.hpp"
+
+#include "engine.hpp"
+
+#include <freetype/ftmm.h>
+
+
+MMGXState
+MMGXAxisInfo::get(Engine* engine,
+ std::vector<MMGXAxisInfo>& infos)
+{
+ auto face = engine->currentFallbackFtFace();
+ if (!face)
+ {
+ infos.clear();
+ return MMGXState::NoMMGX;
+ }
+
+ if (!FT_HAS_MULTIPLE_MASTERS(face))
+ {
+ infos.clear();
+ return MMGXState::NoMMGX;
+ }
+
+ FT_Multi_Master dummy;
+ auto error = FT_Get_Multi_Master(face, &dummy);
+ auto state = error ? MMGXState::GX_OVF : MMGXState::MM;
+
+ FT_MM_Var* mm;
+ if (FT_Get_MM_Var(face, &mm))
+ {
+ infos.clear();
+ return state;
+ }
+
+ infos.resize(mm->num_axis);
+
+ //auto& sfnt = engine->currentFontSFNTNames();
+ for (unsigned int i = 0; i < mm->num_axis; ++i)
+ {
+ auto& axis = mm->axis[i];
+ auto& info = infos[i];
+ info.maximum = axis.maximum / 65536.0;
+ info.minimum = axis.minimum / 65536.0;
+ info.def = axis.def / 65536.0;
+ info.tag = axis.tag;
+ info.isMM = state == MMGXState::MM;
+
+ unsigned int flags = 0;
+ FT_Get_Var_Axis_Flags(mm, i, &flags);
+ info.hidden = (flags & FT_VAR_AXIS_FLAG_HIDDEN) != 0;
+
+ auto nameSet = false;
+ if (state == MMGXState::GX_OVF)
+ {
+ auto strid = mm->axis[i].strid;
+ //for (auto& obj : sfnt)
+ //{
+ // if (obj.nameID == strid && obj.strValid)
+ // {
+ // info.name = obj.str;
+ // nameSet = true;
+ // break;
+ // }
+ //}
+ }
+
+ // XXX security flaw
+ if (!nameSet)
+ info.name = QString(axis.name);
+ }
+
+ FT_Done_MM_Var(face->glyph->library, mm);
+
+ return state;
+}
+
+
+// end of mmgx.cpp
diff --git a/src/ftinspect/engine/mmgx.hpp b/src/ftinspect/engine/mmgx.hpp
new file mode 100644
index 0000000..6d5a3b0
--- /dev/null
+++ b/src/ftinspect/engine/mmgx.hpp
@@ -0,0 +1,57 @@
+// mmgx.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <vector>
+#include <QString>
+
+class Engine;
+
+enum class MMGXState
+{
+ NoMMGX,
+ MM, // Adobe MM
+ GX_OVF, // GX or OpenType variable fonts
+};
+
+struct MMGXAxisInfo
+{
+ QString name;
+ unsigned long tag;
+
+ double minimum;
+ double maximum;
+ double def;
+
+ bool hidden;
+ bool isMM;
+
+ static MMGXState get(Engine* engine, std::vector<MMGXAxisInfo>& infos);
+
+
+ friend bool
+ operator==(const MMGXAxisInfo& lhs,
+ const MMGXAxisInfo& rhs)
+ {
+ return lhs.name == rhs.name
+ && lhs.tag == rhs.tag
+ && lhs.minimum == rhs.minimum
+ && lhs.maximum == rhs.maximum
+ && lhs.def == rhs.def
+ && lhs.hidden == rhs.hidden
+ && lhs.isMM == rhs.isMM;
+ }
+
+
+ friend bool
+ operator!=(const MMGXAxisInfo& lhs,
+ const MMGXAxisInfo& rhs)
+ {
+ return !(lhs == rhs);
+ }
+};
+
+
+// end of mmgx.hpp
diff --git a/src/ftinspect/maingui.cpp b/src/ftinspect/maingui.cpp
index da3bada..9fc4c01 100644
--- a/src/ftinspect/maingui.cpp
+++ b/src/ftinspect/maingui.cpp
@@ -180,7 +180,7 @@ MainGUI::repaintCurrentTab()
void
MainGUI::reloadCurrentTabFont()
{
- engine_->resetCache();
+ settingPanel_->applyDelayedSettings(); // This will reset the cache.
applySettings();
auto index = tabWidget_->currentIndex();
if (index >= 0 && static_cast<size_t>(index) < tabs_.size())
@@ -192,7 +192,6 @@ void
MainGUI::applySettings()
{
settingPanel_->applySettings();
- settingPanel_->applyDelayedSettings();
}
diff --git a/src/ftinspect/meson.build b/src/ftinspect/meson.build
index 05c5854..71450ab 100644
--- a/src/ftinspect/meson.build
+++ b/src/ftinspect/meson.build
@@ -25,6 +25,7 @@ if qt5_dep.found()
'engine/fontfilemanager.cpp',
'engine/rendering.cpp',
'engine/paletteinfo.cpp',
+ 'engine/mmgx.cpp',
'glyphcomponents/glyphbitmap.cpp',
'glyphcomponents/glyphoutline.cpp',
@@ -41,6 +42,7 @@ if qt5_dep.found()
'models/customcomboboxmodels.cpp',
'panels/settingpanel.cpp',
+ 'panels/settingpanelmmgx.cpp',
'panels/singular.cpp',
'ftinspect.cpp',
@@ -58,6 +60,7 @@ if qt5_dep.found()
'maingui.hpp',
'models/customcomboboxmodels.hpp',
'panels/settingpanel.hpp',
+ 'panels/settingpanelmmgx.hpp',
'panels/singular.hpp',
],
dependencies: qt5_dep)
diff --git a/src/ftinspect/panels/settingpanel.cpp
b/src/ftinspect/panels/settingpanel.cpp
index 359410e..33345f7 100644
--- a/src/ftinspect/panels/settingpanel.cpp
+++ b/src/ftinspect/panels/settingpanel.cpp
@@ -99,16 +99,6 @@ SettingPanel::checkHinting()
}
-void
-SettingPanel::checkHintingMode()
-{
- //if (!comparatorMode_)
- applyDelayedSettings();
-
- emit fontReloadNeeded();
-}
-
-
void
SettingPanel::checkAutoHinting()
{
@@ -193,16 +183,6 @@ SettingPanel::checkPalette()
}
-void
-SettingPanel::checkStemDarkening()
-{
- //if (!comparatorMode_)
- applyDelayedSettings();
-
- emit fontReloadNeeded();
-}
-
-
void
SettingPanel::openBackgroundPicker()
{
@@ -339,7 +319,7 @@ SettingPanel::onFontChanged()
paletteComboBox_->setEnabled(colorLayerCheckBox_->isChecked()
&& paletteComboBox_->count() > 0);
populatePalettes();
- //mmgxPanel_->reloadFont();
+ mmgxPanel_->reloadFont();
blockSignals(blockState);
// Place this after `blockSignals` to let the signals emitted normally
@@ -389,7 +369,7 @@ SettingPanel::applySettings()
engine_->renderingEngine()->setForeground(foregroundColor_.rgba());
engine_->renderingEngine()->setBackground(backgroundColor_.rgba());
- //mmgxPanel_->applySettings();
+ mmgxPanel_->applySettings();
}
@@ -481,8 +461,7 @@ SettingPanel::createLayout()
gammaLabel_->setBuddy(gammaSlider_);
gammaValueLabel_ = new QLabel(this);
- // TODO: MM/GX
- mmgxPanel_ = new QWidget(this);
+ mmgxPanel_ = new SettingPanelMMGX(this, engine_);
backgroundButton_ = new QPushButton(tr("Background"), this);
foregroundButton_ = new QPushButton(tr("Foreground"), this);
@@ -622,7 +601,7 @@ SettingPanel::createConnections()
// use `qOverload` here to prevent ambiguity.
connect(hintingModeComboBox_,
QOverload<int>::of(&QComboBox::currentIndexChanged),
- this, &SettingPanel::checkHintingMode);
+ this, &SettingPanel::fontReloadNeeded);
connect(antiAliasingComboBox_,
QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &SettingPanel::checkAntiAliasing);
@@ -656,7 +635,7 @@ SettingPanel::createConnections()
connect(embeddedBitmapCheckBox_, &QCheckBox::clicked,
this, &SettingPanel::fontReloadNeeded);
connect(stemDarkeningCheckBox_, &QCheckBox::clicked,
- this, &SettingPanel::checkStemDarkening);
+ this, &SettingPanel::fontReloadNeeded);
connect(colorLayerCheckBox_, &QCheckBox::clicked,
this, &SettingPanel::checkPalette);
@@ -664,6 +643,9 @@ SettingPanel::createConnections()
this, &SettingPanel::openBackgroundPicker);
connect(foregroundButton_, &QPushButton::clicked,
this, &SettingPanel::openForegroundPicker);
+
+ connect(mmgxPanel_, &SettingPanelMMGX::mmgxCoordsChanged,
+ this, &SettingPanel::fontReloadNeeded);
}
diff --git a/src/ftinspect/panels/settingpanel.hpp
b/src/ftinspect/panels/settingpanel.hpp
index 73c309a..418958e 100644
--- a/src/ftinspect/panels/settingpanel.hpp
+++ b/src/ftinspect/panels/settingpanel.hpp
@@ -6,6 +6,7 @@
#include "../engine/engine.hpp"
#include "../models/customcomboboxmodels.hpp"
+#include "settingpanelmmgx.hpp"
#include <QWidget>
#include <QTabWidget>
@@ -54,8 +55,7 @@ private:
QWidget* generalTab_;
QWidget* hintingRenderingTab_;
- //SettingPanelMMGX* mmgxPanel_;
- QWidget* mmgxPanel_;
+ SettingPanelMMGX* mmgxPanel_;
QLabel* gammaLabel_;
QLabel* gammaValueLabel_;
@@ -113,11 +113,9 @@ private:
void checkAllSettings();
void checkHinting();
- void checkHintingMode();
void checkAutoHinting();
void checkAntiAliasing();
void checkPalette();
- void checkStemDarkening();
void openBackgroundPicker();
void openForegroundPicker();
diff --git a/src/ftinspect/panels/settingpanelmmgx.cpp
b/src/ftinspect/panels/settingpanelmmgx.cpp
new file mode 100644
index 0000000..f80c318
--- /dev/null
+++ b/src/ftinspect/panels/settingpanelmmgx.cpp
@@ -0,0 +1,359 @@
+// settingpanelmmgx.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "settingpanelmmgx.hpp"
+
+#include <QScrollBar>
+
+#include "../engine/engine.hpp"
+#include "../uihelper.hpp"
+
+SettingPanelMMGX::SettingPanelMMGX(QWidget* parent,
+ Engine* engine)
+: QWidget(parent),
+ engine_(engine)
+{
+ createLayout();
+ createConnections();
+}
+
+
+void
+SettingPanelMMGX::reloadFont()
+{
+ setEnabled(engine_->currentFontMMGXState() != MMGXState::NoMMGX);
+ if (currentAxes_ == engine_->currentFontMMGXAxes())
+ return;
+
+ currentAxes_ = engine_->currentFontMMGXAxes();
+
+ auto newSize = currentAxes_.size();
+ auto oldSize = itemWidgets_.size();
+ auto minSize = std::min(newSize, oldSize);
+
+ // This won't trigger unexpected updating since signals are blocked
+ for (size_t i = 0; i < minSize; ++i)
+ itemWidgets_[i]->updateInfo(currentAxes_[i]);
+
+ if (newSize < oldSize)
+ {
+ for (size_t i = newSize; i < oldSize; ++i)
+ {
+ auto w = itemWidgets_[i];
+ disconnect(w);
+ listLayout_->removeWidget(w);
+ delete w;
+ }
+ itemWidgets_.resize(newSize);
+ }
+ else if (newSize > oldSize)
+ {
+ itemWidgets_.resize(newSize);
+ for (size_t i = oldSize; i < newSize; ++i)
+ {
+ auto w = new MMGXSettingItem(this);
+ itemWidgets_[i] = w;
+ w->updateInfo(currentAxes_[i]);
+ listLayout_->addWidget(w);
+ connect(w, &MMGXSettingItem::valueChanged,
+ [this, i] { itemChanged(i); });
+ }
+ }
+ checkHidden();
+ retrieveValues();
+ applySettings();
+}
+
+
+void
+SettingPanelMMGX::applySettings()
+{
+ engine_->reloadFont();
+ engine_->applyMMGXDesignCoords(currentValues_.data(),
+ currentValues_.size());
+}
+
+
+void
+SettingPanelMMGX::checkHidden()
+{
+ if (itemWidgets_.size() < currentAxes_.size())
+ return; // This should never happen!
+ for (int i = 0; static_cast<size_t>(i) < currentAxes_.size(); ++i)
+ itemWidgets_[i]->setVisible(showHiddenCheckBox_->isChecked()
+ || !currentAxes_[i].hidden);
+}
+
+
+void
+SettingPanelMMGX::createLayout()
+{
+ showHiddenCheckBox_ = new QCheckBox(tr("Show Hidden"), this);
+ groupingCheckBox_ = new QCheckBox(tr("Grouping"), this);
+ resetDefaultButton_ = new QPushButton(tr("Reset Default"), this);
+ itemsListWidget_ = new QWidget(this);
+ scrollArea_ = new UnboundScrollArea(this);
+
+ scrollArea_->setWidget(itemsListWidget_);
+ scrollArea_->setWidgetResizable(true);
+ itemsListWidget_->setAutoFillBackground(false);
+
+ mainLayout_ = new QVBoxLayout;
+ listLayout_ = new QVBoxLayout;
+ listWrapperLayout_ = new QVBoxLayout;
+
+ listLayout_->setSpacing(0);
+ listLayout_->setContentsMargins(0, 0, 0, 0);
+ itemsListWidget_->setContentsMargins(0, 0, 0, 0);
+
+ itemsListWidget_->setLayout(listWrapperLayout_);
+
+ listWrapperLayout_->addLayout(listLayout_);
+ listWrapperLayout_->addStretch(1);
+
+ mainLayout_->addWidget(showHiddenCheckBox_);
+ mainLayout_->addWidget(groupingCheckBox_);
+ mainLayout_->addWidget(resetDefaultButton_);
+ mainLayout_->addWidget(scrollArea_, 1);
+
+ setLayout(mainLayout_);
+}
+
+
+void
+SettingPanelMMGX::createConnections()
+{
+ connect(showHiddenCheckBox_, &QCheckBox::clicked,
+ this, &SettingPanelMMGX::checkHidden);
+ connect(resetDefaultButton_, &QCheckBox::clicked,
+ this, &SettingPanelMMGX::resetDefaultClicked);
+ connect(groupingCheckBox_, &QCheckBox::clicked,
+ this, &SettingPanelMMGX::checkGrouping);
+}
+
+
+void
+SettingPanelMMGX::retrieveValues()
+{
+ currentValues_.resize(currentAxes_.size());
+ for (unsigned i = 0; i < currentAxes_.size(); ++i)
+ currentValues_[i] = itemWidgets_[i]->value();
+}
+
+
+void
+SettingPanelMMGX::itemChanged(size_t index)
+{
+ if (groupingCheckBox_->isChecked()
+ && index < currentAxes_.size() && index < itemWidgets_.size())
+ {
+ auto tag = currentAxes_[index].tag;
+ auto value = itemWidgets_[index]->value();
+ for (size_t i = 0; i < itemWidgets_.size(); ++i)
+ if (i != index && currentAxes_[i].tag == tag)
+ itemWidgets_[i]->setValue(value);
+ }
+
+ retrieveValues();
+ emit mmgxCoordsChanged();
+}
+
+
+void
+SettingPanelMMGX::resetDefaultClicked()
+{
+ for (auto w : itemWidgets_)
+ w->resetDefault();
+
+ retrieveValues();
+ emit mmgxCoordsChanged();
+}
+
+
+void
+SettingPanelMMGX::checkGrouping()
+{
+ if (!groupingCheckBox_->isChecked())
+ return;
+ auto maxIndex = std::max(itemWidgets_.size(), currentAxes_.size());
+ for (size_t i = maxIndex - 1; ; --i)
+ {
+ if (!currentAxes_[i].hidden)
+ {
+ auto tag = currentAxes_[i].tag;
+ auto value = itemWidgets_[i]->value();
+ for (size_t j = 0; j < maxIndex; ++j)
+ if (j != i && currentAxes_[j].tag == tag)
+ itemWidgets_[j]->setValue(value);
+ }
+
+ if (i == 0)
+ break;
+ }
+
+ retrieveValues();
+ emit mmgxCoordsChanged();
+}
+
+
+MMGXSettingItem::MMGXSettingItem(QWidget* parent)
+: QFrame(parent)
+{
+ createLayout();
+ createConnections();
+}
+
+
+void
+MMGXSettingItem::updateInfo(MMGXAxisInfo& info)
+{
+ axisInfo_ = info;
+ valueValidator_->setRange(info.minimum, info.maximum, 10);
+
+ if (info.hidden)
+ nameLabel_->setText("<i>" + info.name + "</i>");
+ else
+ nameLabel_->setText(info.name);
+
+ // To keep things simple, we will use 1/1024 of the span between the min and
+ // max as one step on the slider.
+
+ slider_->setMinimum(0);
+ slider_->setTickInterval(1);
+ slider_->setMaximum(1024);
+
+ resetDefault();
+}
+
+
+void
+MMGXSettingItem::setValue(FT_Fixed value)
+{
+ actualValue_ = value;
+ updateSlider();
+ updateLineEdit();
+}
+
+
+void
+MMGXSettingItem::resetDefault()
+{
+ setValue(static_cast<FT_Fixed>(axisInfo_.def * 65536.0));
+}
+
+
+void
+MMGXSettingItem::createLayout()
+{
+ nameLabel_ = new QLabel(this);
+
+ // 1/1024 = 0.0009765625
+ valueValidator_ = new QDoubleValidator(0, 0, 10, this);
+ valueValidator_->setNotation(QDoubleValidator::StandardNotation);
+
+ valueLineEdit_ = new QLineEdit("0", this);
+ valueLineEdit_->setValidator(valueValidator_);
+
+ slider_ = new QSlider(this);
+ slider_->setOrientation(Qt::Horizontal);
+
+ resetDefaultButton_ = new QPushButton(this);
+ resetDefaultButton_->setText(tr("Def"));
+ setButtonNarrowest(resetDefaultButton_);
+
+ nameLabel_->setToolTip(tr("Axis name or tag"));
+ slider_->setToolTip(
+ tr("Axis value (max precision: 1/1024 of the value range)"));
+ valueLineEdit_->setToolTip(tr("Axis value"));
+ resetDefaultButton_->setToolTip(tr("Reset axis to default"));
+
+ mainLayout_ = new QGridLayout();
+
+ mainLayout_->addWidget(nameLabel_, 0, 0, 1, 2);
+ mainLayout_->addWidget(slider_, 1, 0, 1, 2);
+ mainLayout_->addWidget(valueLineEdit_, 2, 0, 1, 1, Qt::AlignVCenter);
+ mainLayout_->addWidget(resetDefaultButton_, 2, 1, 1, 1, Qt::AlignVCenter);
+
+ mainLayout_->setSpacing(4);
+ mainLayout_->setContentsMargins(4, 4, 4, 4);
+ setContentsMargins(0, 0, 0, 0);
+ setLayout(mainLayout_);
+ setFrameShape(StyledPanel);
+}
+
+
+void
+MMGXSettingItem::createConnections()
+{
+ connect(slider_, &QSlider::valueChanged,
+ this, &MMGXSettingItem::sliderValueChanged);
+ connect(valueLineEdit_, &QLineEdit::editingFinished,
+ this, &MMGXSettingItem::lineEditChanged);
+ connect(resetDefaultButton_, &QToolButton::clicked,
+ this, &MMGXSettingItem::resetDefaultSingle);
+}
+
+
+void
+MMGXSettingItem::sliderValueChanged()
+{
+ auto value = slider_->value() / 1024.0
+ * (axisInfo_.maximum - axisInfo_.minimum)
+ + axisInfo_.minimum;
+ actualValue_ = static_cast<FT_Fixed>(value * 65536.0);
+
+ if (axisInfo_.isMM)
+ actualValue_ = FT_RoundFix(actualValue_);
+
+ updateLineEdit();
+ emit valueChanged();
+}
+
+
+void
+MMGXSettingItem::lineEditChanged()
+{
+ bool succ = false;
+ auto newValue = valueLineEdit_->text().toDouble(&succ);
+ if (!succ || newValue > axisInfo_.maximum || newValue < axisInfo_.minimum)
+ {
+ updateLineEdit();
+ return;
+ }
+
+ actualValue_ = static_cast<FT_Fixed>(newValue / 65536.0);
+
+ updateSlider();
+ emit valueChanged();
+}
+
+
+void
+MMGXSettingItem::updateLineEdit()
+{
+ QSignalBlocker blocker(valueLineEdit_);
+ valueLineEdit_->setText(QString::number(actualValue_ / 65536.0));
+}
+
+
+void
+MMGXSettingItem::updateSlider()
+{
+ QSignalBlocker blocker(slider_);
+ slider_->setValue(
+ static_cast<int>((actualValue_ / 65536.0 - axisInfo_.minimum)
+ / (axisInfo_.maximum - axisInfo_.minimum)
+ * 1024));
+}
+
+
+void
+MMGXSettingItem::resetDefaultSingle()
+{
+ resetDefault();
+ emit valueChanged();
+}
+
+
+// end of settingpanelmmgx.cpp
diff --git a/src/ftinspect/panels/settingpanelmmgx.hpp
b/src/ftinspect/panels/settingpanelmmgx.hpp
new file mode 100644
index 0000000..f61ca42
--- /dev/null
+++ b/src/ftinspect/panels/settingpanelmmgx.hpp
@@ -0,0 +1,109 @@
+// settingpanelmmgx.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include "../engine/mmgx.hpp"
+#include "../widgets/customwidgets.hpp"
+
+#include <QWidget>
+#include <QGridLayout>
+#include <QBoxLayout>
+#include <QCheckBox>
+#include <QScrollArea>
+#include <QLineEdit>
+#include <QSlider>
+#include <QFrame>
+#include <QPushButton>
+#include <QToolButton>
+#include <QLabel>
+#include <QDoubleValidator>
+
+#include <freetype/fttypes.h>
+
+class Engine;
+
+class MMGXSettingItem;
+class SettingPanelMMGX
+: public QWidget
+{
+ Q_OBJECT
+public:
+ SettingPanelMMGX(QWidget* parent, Engine* engine);
+ ~SettingPanelMMGX() override = default;
+
+ void reloadFont();
+ void applySettings();
+ void checkHidden();
+ std::vector<FT_Fixed>& mmgxCoords() { return currentValues_; }
+
+signals:
+ void mmgxCoordsChanged();
+
+private:
+ Engine* engine_;
+
+ QCheckBox* showHiddenCheckBox_;
+ QCheckBox* groupingCheckBox_;
+ QPushButton* resetDefaultButton_;
+ QWidget* itemsListWidget_;
+ UnboundScrollArea* scrollArea_;
+ std::vector<MMGXSettingItem*> itemWidgets_;
+
+ QVBoxLayout* mainLayout_;
+ QVBoxLayout* listLayout_;
+ QVBoxLayout* listWrapperLayout_;
+
+ std::vector<FT_Fixed> currentValues_;
+ std::vector<MMGXAxisInfo> currentAxes_;
+
+ void createLayout();
+ void createConnections();
+
+ void retrieveValues();
+ void itemChanged(size_t index);
+ void resetDefaultClicked();
+ void checkGrouping();
+};
+
+
+class MMGXSettingItem
+: public QFrame
+{
+ Q_OBJECT
+public:
+ MMGXSettingItem(QWidget* parent);
+ ~MMGXSettingItem() override = default;
+
+ void updateInfo(MMGXAxisInfo& info);
+ FT_Fixed value() { return actualValue_; }
+ void setValue(FT_Fixed value);
+ void resetDefault();
+
+signals:
+ void valueChanged();
+
+private:
+ QLabel* nameLabel_;
+ QSlider* slider_;
+ QPushButton* resetDefaultButton_;
+ QLineEdit* valueLineEdit_;
+ QDoubleValidator* valueValidator_;
+
+ QGridLayout* mainLayout_;
+
+ FT_Fixed actualValue_;
+ MMGXAxisInfo axisInfo_;
+
+ void createLayout();
+ void createConnections();
+ void sliderValueChanged();
+ void lineEditChanged();
+ void updateLineEdit();
+ void updateSlider();
+ void resetDefaultSingle();
+};
+
+
+// end of settingpanelmmgx.hpp
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freetype2-demos] master 9f70ceb 20/41: [ftinspect] Support MM/GX.,
Werner Lemberg <=