diff --git a/stuff/config/current.txt b/stuff/config/current.txt
index 62c5c93..19e32cf 100644
--- a/stuff/config/current.txt
+++ b/stuff/config/current.txt
@@ -1255,6 +1255,19 @@
- "STD_iwa_BarrelDistortFx.vignetteMidpoint" "Vignetting Midpoint"
- "STD_iwa_BarrelDistortFx.scale" "Scale"
+ - "STD_iwa_TextFx" "Text Iwa"
+ - "STD_iwa_TextFx.targetType" "Source"
+ - "STD_iwa_TextFx.columnIndex" "Column Index"
+ - "STD_iwa_TextFx.hAlign" "Holizontal Align"
+ - "STD_iwa_TextFx.text" "Text"
+ - "STD_iwa_TextFx.center" "Center"
+ - "STD_iwa_TextFx.width" "Width"
+ - "STD_iwa_TextFx.height" "Height"
+ - "STD_iwa_TextFx.font" "Font"
+ - "STD_iwa_TextFx.textColor" "Text Color"
+ - "STD_iwa_TextFx.boxColor" "Box Color"
+ - "STD_iwa_TextFx.showBorder" "Show Border"
+
- STD_iwa_TiledParticlesFx "Tiled Particles Iwa"
diff --git a/stuff/profiles/layouts/fxs/STD_iwa_TextFx.xml b/stuff/profiles/layouts/fxs/STD_iwa_TextFx.xml
new file mode 100644
index 0000000..aab4c54
--- /dev/null
+++ b/stuff/profiles/layouts/fxs/STD_iwa_TextFx.xml
@@ -0,0 +1,17 @@
+
+
+
+ targetType
+ columnIndex
+ text
+ hAlign
+ center
+ width
+ height
+ font
+ textColor
+ boxColor
+ showBorder
+
+
+
diff --git a/stuff/profiles/layouts/fxs/fxs.lst b/stuff/profiles/layouts/fxs/fxs.lst
index 126bc35..622ed46 100644
--- a/stuff/profiles/layouts/fxs/fxs.lst
+++ b/stuff/profiles/layouts/fxs/fxs.lst
@@ -146,7 +146,8 @@
STD_inopnCloudsFx
STD_particlesFx
STD_iwa_TiledParticlesFx
- STD_iwa_TimeCodeFx
+ STD_iwa_PNPerspectiveFx
+ STD_iwa_TextFx
STD_colorEmbossFx
diff --git a/toonz/sources/common/tparam/tnotanimatableparam.cpp b/toonz/sources/common/tparam/tnotanimatableparam.cpp
index dc89a62..ea206f7 100644
--- a/toonz/sources/common/tparam/tnotanimatableparam.cpp
+++ b/toonz/sources/common/tparam/tnotanimatableparam.cpp
@@ -8,6 +8,7 @@ PERSIST_IDENTIFIER(TBoolParam, "boolParam")
PERSIST_IDENTIFIER(TFilePathParam, "filePathParam")
PERSIST_IDENTIFIER(TStringParam, "stringParam")
PERSIST_IDENTIFIER(TNADoubleParam, "naDoubleParam")
+PERSIST_IDENTIFIER(TFontParam, "fontParam")
// PERSIST_IDENTIFIER(TIntEnumParam, "intEnumParam")
TPersistDeclarationT TEnumParam::m_declaration("intEnumParam");
@@ -94,6 +95,17 @@ void TNADoubleParam::saveData(TOStream &os) {
os << getDefaultValue();
os << getValue();
}
+
+//=========================================================
+
+void TFontParam::loadData(TIStream &is) {
+ std::wstring str;
+ is >> str;
+ setValue(str, false);
+}
+
+void TFontParam::saveData(TOStream &os) { os << getValue(); }
+
//=========================================================
//=========================================================
diff --git a/toonz/sources/include/tnotanimatableparam.h b/toonz/sources/include/tnotanimatableparam.h
index b28497d..d61f07b 100644
--- a/toonz/sources/include/tnotanimatableparam.h
+++ b/toonz/sources/include/tnotanimatableparam.h
@@ -13,6 +13,7 @@
#include "tconvert.h"
#include
+#include
#undef DVAPI
#undef DVVAR
@@ -169,6 +170,7 @@ public:
using namespace std;
return to_string(getValue());
}
+
bool hasKeyframes() const override { return 0; };
void getKeyframes(std::set &) const override{};
int getNextKeyframe(double) const override { return -1; };
@@ -280,6 +282,8 @@ template class DVAPI TPersistDeclarationT;
class DVAPI TStringParam final : public TNotAnimatableParam {
PERSIST_DECLARATION(TStringParam);
+ bool m_multiLine = false;
+
public:
TStringParam(std::wstring v = L"") : TNotAnimatableParam(v) {}
TStringParam(const TStringParam &src)
@@ -287,6 +291,9 @@ public:
TParam *clone() const override { return new TStringParam(*this); }
void loadData(TIStream &is) override;
void saveData(TOStream &os) override;
+
+ void setMultiLineEnabled(bool enable) { m_multiLine = enable; }
+ bool isMultiLineEnabled() { return m_multiLine; }
};
DEFINE_PARAM_SMARTPOINTER(TStringParam, std::wstring)
@@ -393,6 +400,32 @@ private:
DEFINE_PARAM_SMARTPOINTER(TNADoubleParam, double)
+//=========================================================
+//
+// class TFontParam
+//
+//=========================================================
+
+#ifdef _WIN32
+template class DVAPI TNotAnimatableParam;
+class TFontParam;
+template class DVAPI TPersistDeclarationT;
+#endif
+
+class DVAPI TFontParam final : public TNotAnimatableParam {
+ PERSIST_DECLARATION(TFontParam);
+
+public:
+ TFontParam(std::wstring v = QFont().toString().toStdWString())
+ : TNotAnimatableParam(v) {}
+ TFontParam(const TFontParam &src) : TNotAnimatableParam(src) {}
+ TParam *clone() const override { return new TFontParam(*this); }
+ void loadData(TIStream &is) override;
+ void saveData(TOStream &os) override;
+};
+
+DEFINE_PARAM_SMARTPOINTER(TFontParam, std::wstring)
+
//-----------------------------------------------------------------------------
// TNotAnimatableParamChangeUndo
//-----------------------------------------------------------------------------
diff --git a/toonz/sources/include/toonzqt/paramfield.h b/toonz/sources/include/toonzqt/paramfield.h
index 858f823..dbf3880 100644
--- a/toonz/sources/include/toonzqt/paramfield.h
+++ b/toonz/sources/include/toonzqt/paramfield.h
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include "tgeometry.h"
#include "tparam.h"
@@ -40,6 +41,7 @@ class QString;
class QComboBox;
class QHBoxLayout;
class TFxHandle;
+class QFontComboBox;
namespace DVGui {
class LineEdit;
@@ -501,11 +503,28 @@ protected slots:
// StringParamField
//-----------------------------------------------------------------------------
+namespace component {
+class MyTextEdit : public QTextEdit {
+ Q_OBJECT
+public:
+ MyTextEdit(const QString &text, QWidget *parent = Q_NULLPTR)
+ : QTextEdit(text, parent) {}
+
+protected:
+ void keyPressEvent(QKeyEvent *event) override;
+ void focusOutEvent(QFocusEvent *e) override;
+
+signals:
+ void edited();
+};
+};
+
class DVAPI StringParamField final : public ParamField {
Q_OBJECT
TStringParamP m_currentParam, m_actualParam;
- DVGui::LineEdit *m_textFld;
+ DVGui::LineEdit *m_textFld = nullptr;
+ component::MyTextEdit *m_multiTextFld = nullptr;
public:
StringParamField(QWidget *parent, QString name, const TStringParamP ¶m);
@@ -514,8 +533,41 @@ public:
int frame) override;
void update(int frame) override;
- QSize getPreferedSize() override { return QSize(100, 20); }
+ QSize getPreferedSize() override {
+ if (m_textFld)
+ return QSize(100, 20);
+ else
+ return QSize(100, 80);
+ }
+protected slots:
+ void onChange();
+};
+
+//=============================================================================
+// FontParamField
+//-----------------------------------------------------------------------------
+
+class FontParamField final : public ParamField {
+ Q_OBJECT
+
+ TFontParamP m_currentParam, m_actualParam;
+
+ QFontComboBox *m_fontCombo;
+ QComboBox *m_styleCombo;
+ DVGui::IntField *m_sizeField;
+
+public:
+ FontParamField(QWidget *parent, QString name, const TFontParamP ¶m);
+
+ void setParam(const TParamP ¤t, const TParamP &actual,
+ int frame) override;
+ void update(int frame) override;
+
+ QSize getPreferedSize() override { return QSize(150, 20); }
+
protected slots:
+ void findStyles(const QFont &font);
+ void onSizeChange(bool);
void onChange();
};
diff --git a/toonz/sources/stdfx/CMakeLists.txt b/toonz/sources/stdfx/CMakeLists.txt
index b691a4e..f0a0017 100644
--- a/toonz/sources/stdfx/CMakeLists.txt
+++ b/toonz/sources/stdfx/CMakeLists.txt
@@ -72,9 +72,10 @@ set(HEADERS
iwa_fresnel.h
iwa_pnperspectivefx.h
iwa_soapbubblefx.h
- iwa_bokehfx.h
- iwa_timecodefx.h
- iwa_bokehreffx.h
+ iwa_bokehfx.h
+ iwa_timecodefx.h
+ iwa_bokehreffx.h
+ iwa_textfx.h
)
set(SOURCES
@@ -250,12 +251,13 @@ set(SOURCES
iwa_noise1234.cpp
iwa_pnperspectivefx.cpp
iwa_soapbubblefx.cpp
- ${SDKROOT}/kiss_fft130/kiss_fft.c
- ${SDKROOT}/kiss_fft130/tools/kiss_fftnd.c
- iwa_bokehfx.cpp
- iwa_timecodefx.cpp
- iwa_bokehreffx.cpp
- iwa_barreldistortfx.cpp
+ ${SDKROOT}/kiss_fft130/kiss_fft.c
+ ${SDKROOT}/kiss_fft130/tools/kiss_fftnd.c
+ iwa_bokehfx.cpp
+ iwa_timecodefx.cpp
+ iwa_bokehreffx.cpp
+ iwa_barreldistortfx.cpp
+ iwa_textfx.cpp
)
set(OBJCSOURCES
diff --git a/toonz/sources/stdfx/iwa_textfx.cpp b/toonz/sources/stdfx/iwa_textfx.cpp
new file mode 100644
index 0000000..41892f0
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_textfx.cpp
@@ -0,0 +1,250 @@
+#include "iwa_textfx.h"
+
+#include "tparamuiconcept.h"
+#include
+#include
+#include
+
+//------------------------------------------------------------------
+
+Iwa_TextFx::Iwa_TextFx()
+ : m_text(L"Lorem ipsum")
+ , m_hAlign(new TIntEnumParam(Qt::AlignLeft, "Left"))
+ , m_center(TPointD(0.0, 0.0))
+ , m_width(200.0)
+ , m_height(60.0)
+ , m_font(new TFontParam())
+ , m_textColor(TPixel32::Black)
+ , m_boxColor(TPixel32::Transparent)
+ , m_showBorder(false) {
+ m_targetType->setValue(INPUT_TEXT);
+
+ m_hAlign->addItem(Qt::AlignRight, "Right");
+ m_hAlign->addItem(Qt::AlignHCenter, "Center");
+ m_hAlign->addItem(Qt::AlignJustify, "Justify");
+
+ m_text->setMultiLineEnabled(true);
+
+ m_center->getX()->setMeasureName("fxLength");
+ m_center->getY()->setMeasureName("fxLength");
+ m_width->setMeasureName("fxLength");
+ m_height->setMeasureName("fxLength");
+
+ m_width->setValueRange(1.0, (std::numeric_limits::max)());
+ m_height->setValueRange(1.0, (std::numeric_limits::max)());
+
+ // set the initial font size to 30
+ QFont font;
+ font.fromString(QString::fromStdWString(m_font->getValue()));
+ font.setPixelSize(30);
+ m_font->setValue(font.toString().toStdWString(), true);
+
+ bindParam(this, "targetType", m_targetType);
+ bindParam(this, "columnIndex", m_columnIndex);
+ bindParam(this, "text", m_text);
+ bindParam(this, "hAlign", m_hAlign);
+ bindParam(this, "center", m_center);
+ bindParam(this, "width", m_width);
+ bindParam(this, "height", m_height);
+ bindParam(this, "font", m_font);
+ bindParam(this, "textColor", m_textColor);
+ bindParam(this, "boxColor", m_boxColor);
+ bindParam(this, "showBorder", m_showBorder);
+}
+
+//------------------------------------------------------------------
+
+bool Iwa_TextFx::doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &ri) {
+ bBox = TConsts::infiniteRectD;
+ return true;
+}
+
+//------------------------------------------------------------------
+
+void Iwa_TextFx::doCompute(TTile &tile, double frame,
+ const TRenderSettings &ri) {
+ QString text;
+ if (m_targetType->getValue() == INPUT_TEXT)
+ text = QString::fromStdWString(m_text->getValue());
+ else
+ text = m_noteLevelStr;
+ if (text.isEmpty()) return;
+
+ QFont font;
+ font.fromString(QString::fromStdWString(m_font->getValue()));
+
+ double fac = sqrt(fabs(ri.m_affine.det()));
+ int size = (int)(fac * fabs(font.pixelSize()));
+
+ TPoint center = convert(
+ fac * m_center->getValue(frame) -
+ (tile.m_pos + tile.getRaster()->getCenterD()) +
+ TPointD(ri.m_cameraBox.getLx() / 2.0, ri.m_cameraBox.getLy() / 2.0));
+
+ QRect textBoxRect(0, 0, fac * m_width->getValue(frame),
+ fac * m_height->getValue(frame));
+ textBoxRect.moveCenter(QPoint(center.x, center.y));
+
+ Qt::AlignmentFlag hAlignFlag = (Qt::AlignmentFlag)m_hAlign->getValue();
+
+ // For simplification, text will always be "wrapped".
+ // If user would like to make the line with no break, they can just expand the
+ // boundary or set the smaller font size.
+ int flag = hAlignFlag | Qt::AlignVCenter | Qt::TextWordWrap;
+
+ QFont tmpFont(font);
+ tmpFont.setPixelSize(100);
+ QFontMetricsF tmpFm(tmpFont);
+ QRectF bbox = tmpFm.boundingRect(textBoxRect, flag, text);
+
+ float ratio = std::min(textBoxRect.width() / bbox.width(),
+ textBoxRect.height() / bbox.height());
+
+ // compute the font size which will just fit the item region
+ int fontSize = (int)(100.0f * ratio);
+ tmpFont.setPixelSize(fontSize);
+ tmpFm = QFontMetricsF(tmpFont);
+ bbox = tmpFm.boundingRect(textBoxRect, flag, text);
+ bool isInRect;
+ if (textBoxRect.width() >= bbox.width() &&
+ textBoxRect.height() >= bbox.height())
+ isInRect = true;
+ else
+ isInRect = false;
+ while (1) {
+ fontSize += (isInRect) ? 1 : -1;
+ if (fontSize <= 0) // cannot draw
+ return;
+ if (isInRect && fontSize >= size) break;
+ tmpFont.setPixelSize(fontSize);
+ tmpFm = QFontMetricsF(tmpFont);
+ bbox = tmpFm.boundingRect(textBoxRect, flag, text);
+
+ bool newIsInRect = (textBoxRect.width() >= bbox.width() &&
+ textBoxRect.height() >= bbox.height());
+ if (isInRect != newIsInRect) {
+ if (isInRect) fontSize--;
+ break;
+ }
+ }
+
+ if (size < fontSize) {
+ fontSize = size;
+ }
+
+ font.setPixelSize(fontSize);
+ tmpFm = QFontMetricsF(font);
+ bbox = tmpFm.boundingRect(textBoxRect, flag, text);
+
+ double lineWidth = 0.1 * (double)fontSize;
+
+ // Usually the text bounding box has less horizontal margin than vertical.
+ // So here I added more margin to width.
+ QImage img(bbox.width() + (int)(lineWidth * 4),
+ bbox.height() + (int)(lineWidth * 2),
+ QImage::Format_ARGB32_Premultiplied);
+ img.fill(Qt::transparent);
+
+ bbox.moveCenter(img.rect().center());
+
+ QPainter painter(&img);
+ TPixel32 boxColor = m_boxColor->getValue(frame);
+ TPixel32 color = m_textColor->getValue(frame);
+
+ if (boxColor.m > 0)
+ painter.fillRect(img.rect(), QColor((int)boxColor.r, (int)boxColor.g,
+ (int)boxColor.b, (int)boxColor.m));
+
+ QPen pen(QColor((int)color.r, (int)color.g, (int)color.b, (int)color.m));
+ painter.setPen(pen);
+ painter.setFont(font);
+ painter.drawText(bbox, flag, text);
+
+ if (m_showBorder->getValue()) {
+ pen.setWidthF(lineWidth);
+ pen.setJoinStyle(Qt::MiterJoin);
+ painter.setPen(pen);
+ painter.drawRect(img.rect().adjusted(lineWidth / 2, lineWidth / 2,
+ -lineWidth / 2, -lineWidth / 2));
+ }
+
+ TPoint imgRootPos = center - TPoint(img.width() / 2, img.height() / 2);
+
+ tile.getRaster()->clear();
+ TRaster32P ras32 = (TRaster32P)tile.getRaster();
+ TRaster64P ras64 = (TRaster64P)tile.getRaster();
+ if (ras32)
+ putTextImage(ras32, imgRootPos, img);
+ else if (ras64)
+ putTextImage(ras64, imgRootPos, img);
+ else
+ throw TException("Iwa_TextFx: unsupported Pixel Type");
+}
+
+//------------------------------------------------------------------
+
+void Iwa_TextFx::getParamUIs(TParamUIConcept *&concepts, int &length) {
+ concepts = new TParamUIConcept[length = 2];
+
+ concepts[0].m_type = TParamUIConcept::POINT;
+ concepts[0].m_label = "Center";
+ concepts[0].m_params.push_back(m_center);
+
+ concepts[1].m_type = TParamUIConcept::RECT;
+ concepts[1].m_params.push_back(m_width);
+ concepts[1].m_params.push_back(m_height);
+ concepts[1].m_params.push_back(m_center);
+}
+
+//------------------------------------------------------------------
+
+std::string Iwa_TextFx::getAlias(double frame,
+ const TRenderSettings &info) const {
+ std::string alias = getFxType();
+ alias += "[";
+
+ std::string paramalias("");
+ for (int i = 0; i < getParams()->getParamCount(); ++i) {
+ TParam *param = getParams()->getParam(i);
+ paramalias += param->getName() + "=" + param->getValueAlias(frame, 3);
+ }
+
+ if (m_targetType->getValue() == INPUT_TEXT)
+ return alias + "," + std::to_string(getIdentifier()) + paramalias + "]";
+ else
+ return alias + std::to_string(frame) + "," +
+ std::to_string(getIdentifier()) + paramalias + "]";
+}
+
+//------------------------------------------------------------------
+
+template
+void Iwa_TextFx::putTextImage(const RASTER srcRas, TPoint &pos, QImage &img) {
+ for (int j = 0; j < img.height(); j++) {
+ int rasY = pos.y + j;
+ if (rasY < 0) continue;
+ if (srcRas->getLy() <= rasY) break;
+
+ PIXEL *pix = srcRas->pixels(rasY);
+ QRgb *img_p = (QRgb *)img.scanLine(img.height() - j - 1);
+ for (int i = 0; i < img.width(); i++, img_p++) {
+ int rasX = pos.x + i;
+ if (rasX < 0) continue;
+ if (srcRas->getLx() <= rasX) break;
+
+ pix[rasX].r = (typename PIXEL::Channel)(
+ qRed(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX);
+ pix[rasX].g = (typename PIXEL::Channel)(
+ qGreen(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX);
+ pix[rasX].b = (typename PIXEL::Channel)(
+ qBlue(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX);
+ pix[rasX].m = (typename PIXEL::Channel)(
+ qAlpha(*img_p) * (int)PIXEL::maxChannelValue / (int)UCHAR_MAX);
+ }
+ }
+}
+
+//==============================================================================
+
+FX_PLUGIN_IDENTIFIER(Iwa_TextFx, "iwa_TextFx");
\ No newline at end of file
diff --git a/toonz/sources/stdfx/iwa_textfx.h b/toonz/sources/stdfx/iwa_textfx.h
new file mode 100644
index 0000000..24ea20e
--- /dev/null
+++ b/toonz/sources/stdfx/iwa_textfx.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#ifndef IWA_TEXTFX_H
+#define IWA_TEXTFX_H
+
+#include "tparamset.h"
+#include "textawarebasefx.h"
+
+//******************************************************************
+// Iwa_Text Fx class
+//******************************************************************
+
+class Iwa_TextFx final : public TextAwareBaseFx {
+ FX_PLUGIN_DECLARATION(Iwa_TextFx)
+protected:
+ TStringParamP m_text;
+
+ TIntEnumParamP m_hAlign;
+
+ TPointParamP m_center;
+ TDoubleParamP m_width;
+ TDoubleParamP m_height;
+
+ TFontParamP m_font;
+ TPixelParamP m_textColor;
+ TPixelParamP m_boxColor;
+ TBoolParamP m_showBorder;
+
+ template
+ void putTextImage(const RASTER srcRas, TPoint &pos, QImage &img);
+
+public:
+ Iwa_TextFx();
+
+ bool canHandle(const TRenderSettings &info, double frame) override {
+ return true;
+ }
+
+ bool doGetBBox(double frame, TRectD &bBox,
+ const TRenderSettings &ri) override;
+ void doCompute(TTile &tile, double frame, const TRenderSettings &ri) override;
+ void getParamUIs(TParamUIConcept *&concepts, int &length) override;
+
+ std::string getAlias(double frame,
+ const TRenderSettings &info) const override;
+};
+#endif
\ No newline at end of file
diff --git a/toonz/sources/stdfx/textawarebasefx.h b/toonz/sources/stdfx/textawarebasefx.h
new file mode 100644
index 0000000..a25b0da
--- /dev/null
+++ b/toonz/sources/stdfx/textawarebasefx.h
@@ -0,0 +1,32 @@
+#pragma once
+#ifndef TEXTAWAREBASEFX_H
+#define TEXTAWAREBASEFX_H
+
+#include "tfxparam.h"
+#include "stdfx.h"
+
+class TextAwareBaseFx : public TStandardZeraryFx {
+protected:
+ QString m_noteLevelStr;
+
+ TIntEnumParamP m_targetType;
+ TIntParamP m_columnIndex;
+
+public:
+ enum SourceType { NEARBY_COLUMN, SPECIFIED_COLUMN, INPUT_TEXT };
+
+ TextAwareBaseFx()
+ : m_targetType(new TIntEnumParam(NEARBY_COLUMN, "Nearby Note Column"))
+ , m_columnIndex(0) {
+ m_targetType->addItem(SPECIFIED_COLUMN, "Specified Note Column");
+ m_targetType->addItem(INPUT_TEXT, "Input Text");
+ }
+
+ bool isZerary() const override { return true; }
+
+ void setNoteLevelStr(QString str) { m_noteLevelStr = str; }
+ SourceType getSourceType() { return (SourceType)(m_targetType->getValue()); }
+ int getNoteColumnIndex() { return m_columnIndex->getValue() - 1; }
+};
+
+#endif
diff --git a/toonz/sources/toonzlib/scenefx.cpp b/toonz/sources/toonzlib/scenefx.cpp
index e903d80..64672fb 100644
--- a/toonz/sources/toonzlib/scenefx.cpp
+++ b/toonz/sources/toonzlib/scenefx.cpp
@@ -28,8 +28,11 @@
#include "toonz/stage.h"
#include "toonz/preferences.h"
#include "ttzpimagefx.h"
+#include "toonz/txshsoundtextcolumn.h"
+#include "toonz/txshsoundtextlevel.h"
#include "../stdfx/motionawarebasefx.h"
+#include "../stdfx/textawarebasefx.h"
#include "toonz/scenefx.h"
@@ -497,6 +500,26 @@ static QList getColumnMotionPoints(TXsheet *xsh, double row, int col,
return points;
}
+namespace {
+
+QString getNoteText(TXsheet *xsh, double row, int col, int noteColumnIndex,
+ bool neighbor) {
+ int colIndex;
+ if (neighbor)
+ colIndex = col - 1;
+ else
+ colIndex = noteColumnIndex;
+
+ TXshColumn *column = xsh->getColumn(colIndex);
+ if (!column || !column->getSoundTextColumn()) return QString();
+
+ TXshCell cell = xsh->getCell(row, colIndex);
+ if (cell.isEmpty() || !cell.getSoundTextLevel()) return QString();
+
+ return cell.getSoundTextLevel()->getFrameText(cell.m_frameId.getNumber() - 1);
+}
+};
+
//***************************************************************************************************
// FxBuilder definition
//***************************************************************************************************
@@ -939,6 +962,18 @@ PlacedFx FxBuilder::makePF(TZeraryColumnFx *zcfx) {
}
}
+ if (pf.m_fx->getFxType() == "STD_iwa_TextFx") {
+ TextAwareBaseFx *textFx =
+ dynamic_cast(pf.m_fx.getPointer());
+ if (textFx && textFx->getSourceType() != TextAwareBaseFx::INPUT_TEXT) {
+ int noteColumnIndex = textFx->getNoteColumnIndex();
+ bool getNeighbor =
+ (textFx->getSourceType() == TextAwareBaseFx::NEARBY_COLUMN);
+ textFx->setNoteLevelStr(getNoteText(m_xsh, m_frame, pf.m_columnIndex,
+ noteColumnIndex, getNeighbor));
+ }
+ }
+
// Add the column placement NaAffineFx
if (getColumnPlacement(pf, m_xsh, m_frame, pf.m_columnIndex, m_isPreview))
return pf;
diff --git a/toonz/sources/toonzqt/paramfield.cpp b/toonz/sources/toonzqt/paramfield.cpp
index 5c899ec..3c1f114 100644
--- a/toonz/sources/toonzqt/paramfield.cpp
+++ b/toonz/sources/toonzqt/paramfield.cpp
@@ -20,6 +20,8 @@
#include
#include
#include
+#include
+#include
using namespace DVGui;
@@ -448,6 +450,38 @@ public:
};
//-----------------------------------------------------------------------------
+/*! FontParamFieldUndo
+*/
+class FontParamFieldUndo final : public FxSettingsUndo {
+ TFontParamP m_param;
+ std::wstring m_oldValue, m_newValue;
+
+public:
+ FontParamFieldUndo(const TFontParamP param, QString name, TFxHandle *fxHandle)
+ : FxSettingsUndo(name, fxHandle), m_param(param) {
+ m_oldValue = param->getValue();
+ m_newValue = m_oldValue;
+ }
+
+ void onAdd() override { m_newValue = m_param->getValue(); }
+
+ void undo() const override {
+ m_param->setValue(m_oldValue);
+ if (m_fxHandle) m_fxHandle->notifyFxChanged();
+ }
+
+ void redo() const override {
+ m_param->setValue(m_newValue);
+ if (m_fxHandle) m_fxHandle->notifyFxChanged();
+ }
+
+ QString getHistoryString() override {
+ QString str = QObject::tr("Modify Fx Param : %1").arg(m_name);
+ return str;
+ }
+};
+
+//-----------------------------------------------------------------------------
/*! ToneCurveParamField Undo
*/
class ToneCurveParamFieldUndo final : public AnimatableFxSettingsUndo {
@@ -1395,17 +1429,42 @@ void IntParamField::update(int frame) {
// StringParamField
//-----------------------------------------------------------------------------
+namespace component {
+
+void MyTextEdit::keyPressEvent(QKeyEvent *event) {
+ QTextEdit::keyPressEvent(event);
+ if (event->key() == Qt::Key_Return) emit edited();
+}
+
+void MyTextEdit::focusOutEvent(QFocusEvent *event) {
+ QTextEdit::focusOutEvent(event);
+ emit edited();
+}
+};
+
StringParamField::StringParamField(QWidget *parent, QString name,
const TStringParamP ¶m)
: ParamField(parent, name, param) {
QString str;
m_paramName = str.fromStdString(param->getName());
- m_textFld = new LineEdit(name, this);
- m_textFld->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
- bool ret =
- connect(m_textFld, SIGNAL(editingFinished()), this, SLOT(onChange()));
- m_layout->addWidget(m_textFld);
+ bool ret = true;
+ if (param->isMultiLineEnabled()) {
+ m_multiTextFld = new component::MyTextEdit(name, this);
+ m_multiTextFld->setFixedHeight(80);
+ m_multiTextFld->setAcceptRichText(false);
+ m_multiTextFld->setStyleSheet(
+ "background:white;\ncolor:black;\nborder:1 solid black;");
+ ret = ret &&
+ connect(m_multiTextFld, SIGNAL(edited()), this, SLOT(onChange()));
+ m_layout->addWidget(m_multiTextFld);
+ } else {
+ m_textFld = new LineEdit(name, this);
+ m_textFld->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ ret = ret &&
+ connect(m_textFld, SIGNAL(editingFinished()), this, SLOT(onChange()));
+ m_layout->addWidget(m_textFld);
+ }
setLayout(m_layout);
assert(ret);
}
@@ -1413,12 +1472,17 @@ StringParamField::StringParamField(QWidget *parent, QString name,
//-----------------------------------------------------------------------------
void StringParamField::onChange() {
- std::wstring value = m_textFld->text().toStdWString();
- TUndo *undo = 0;
+ std::wstring value;
+ if (m_multiTextFld)
+ value = m_multiTextFld->toPlainText().toStdWString();
+ else
+ value = m_textFld->text().toStdWString();
+ TUndo *undo = 0;
+
+ if (!m_actualParam || m_actualParam->getValue() == value) return;
- TStringParamP stringParam = m_actualParam;
- if (stringParam && stringParam->getValue() != value)
- undo = new StringParamFieldUndo(stringParam, m_interfaceName,
+ if (m_actualParam->getValue() != value)
+ undo = new StringParamFieldUndo(m_actualParam, m_interfaceName,
ParamField::m_fxHandleStat);
m_actualParam->setValue(value);
@@ -1447,13 +1511,147 @@ void StringParamField::update(int frame) {
if (!m_actualParam || !m_currentParam) return;
QString str;
QString strValue = str.fromStdWString(m_actualParam->getValue());
- if (m_textFld->text() == strValue) return;
- m_textFld->setText(strValue);
- // Faccio in modo che il cursore sia sulla prima cifra, cosi' se la stringa
- // da visualizzare e' piu' lunga del campo le cifre che vengono troncate sono
- // le ultime e non le prime (dovrebbero essere quelle dopo la virgola).
- m_textFld->setCursorPosition(0);
+ if (m_textFld) {
+ if (m_textFld->text() == strValue) return;
+ m_textFld->setText(strValue);
+
+ // Faccio in modo che il cursore sia sulla prima cifra, cosi' se la stringa
+ // da visualizzare e' piu' lunga del campo le cifre che vengono troncate
+ // sono
+ // le ultime e non le prime (dovrebbero essere quelle dopo la virgola).
+ m_textFld->setCursorPosition(0);
+ } else {
+ if (m_multiTextFld->toPlainText() == strValue) return;
+ m_multiTextFld->setPlainText(strValue);
+ }
+}
+
+//=============================================================================
+// FontParamField
+//-----------------------------------------------------------------------------
+
+FontParamField::FontParamField(QWidget *parent, QString name,
+ const TFontParamP ¶m)
+ : ParamField(parent, name, param) {
+ m_paramName = QString::fromStdString(param->getName());
+
+ m_fontCombo = new QFontComboBox(this);
+ m_styleCombo = new QComboBox(this);
+ m_sizeField = new IntField(this, false);
+
+ m_sizeField->setRange(1, 500);
+ m_sizeField->enableSlider(false);
+
+ m_layout->addWidget(m_fontCombo);
+ m_layout->addSpacing(5);
+ m_layout->addWidget(new QLabel(tr("Style:"), this), 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ m_layout->addWidget(m_styleCombo);
+ m_layout->addSpacing(5);
+ m_layout->addWidget(new QLabel(tr("Size:"), this), 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ m_layout->addWidget(m_sizeField);
+
+ m_layout->addStretch();
+
+ setLayout(m_layout);
+
+ bool ret = true;
+ ret = ret && connect(m_fontCombo, &QFontComboBox::currentFontChanged, this,
+ &FontParamField::findStyles);
+ ret = ret && connect(m_fontCombo, &QFontComboBox::currentFontChanged, this,
+ &FontParamField::onChange);
+ ret = ret && connect(m_styleCombo, SIGNAL(activated(const QString &)), this,
+ SLOT(onChange()));
+ ret = ret && connect(m_sizeField, SIGNAL(valueChanged(bool)), this,
+ SLOT(onSizeChange(bool)));
+ assert(ret);
+
+ findStyles(m_fontCombo->currentFont());
+}
+
+//-----------------------------------------------------------------------------
+
+void FontParamField::findStyles(const QFont &font) {
+ QFontDatabase fontDatabase;
+ QString currentItem = m_styleCombo->currentText();
+ m_styleCombo->clear();
+
+ QString style;
+ foreach (style, fontDatabase.styles(font.family()))
+ m_styleCombo->addItem(style);
+
+ int styleIndex = m_styleCombo->findText(currentItem);
+
+ if (styleIndex == -1)
+ m_styleCombo->setCurrentIndex(0);
+ else
+ m_styleCombo->setCurrentIndex(styleIndex);
+}
+
+//-----------------------------------------------------------------------------
+
+void FontParamField::onSizeChange(bool isDragging) {
+ if (isDragging) return;
+ onChange();
+}
+
+//-----------------------------------------------------------------------------
+
+void FontParamField::onChange() {
+ QString family = m_fontCombo->currentFont().family();
+ QString style = m_styleCombo->currentText();
+
+ int size = m_sizeField->getValue();
+ int min, max;
+ m_sizeField->getRange(min, max);
+ if (size < min) size = min;
+
+ QFontDatabase fontDatabase;
+ QFont font = fontDatabase.font(family, style, 10);
+ font.setPixelSize(size);
+
+ TUndo *undo = 0;
+
+ TFontParamP fontParam = m_actualParam;
+ QFont currentFont;
+ currentFont.fromString(QString::fromStdWString(fontParam->getValue()));
+ if (fontParam && currentFont != font)
+ undo = new FontParamFieldUndo(fontParam, m_interfaceName,
+ ParamField::m_fxHandleStat);
+
+ m_actualParam->setValue(font.toString().toStdWString());
+ emit currentParamChanged();
+ m_currentParam->setValue(font.toString().toStdWString());
+ emit actualParamChanged();
+
+ if (undo) TUndoManager::manager()->add(undo);
+}
+
+//-----------------------------------------------------------------------------
+
+void FontParamField::setParam(const TParamP ¤t, const TParamP &actual,
+ int frame) {
+ m_currentParam = current;
+ m_actualParam = actual;
+ assert(m_currentParam);
+ assert(m_actualParam);
+ update(frame);
+}
+
+//-----------------------------------------------------------------------------
+
+void FontParamField::update(int frame) {
+ if (!m_actualParam || !m_currentParam) return;
+ QFont font;
+ font.fromString(QString::fromStdWString(m_actualParam->getValue()));
+
+ if (m_fontCombo->currentText() != font.family())
+ m_fontCombo->setCurrentFont(font);
+
+ m_styleCombo->setCurrentText(font.styleName());
+ m_sizeField->setValue(font.pixelSize());
}
//=============================================================================
@@ -1660,6 +1858,8 @@ ParamField *ParamField::create(QWidget *parent, QString name,
return new StringParamField(parent, name, stringParam);
else if (TToneCurveParamP toneCurveParam = param)
return new ToneCurveParamField(parent, name, toneCurveParam);
+ else if (TFontParamP fontParam = param)
+ return new FontParamField(parent, name, fontParam);
else
return 0;
}