diff --git a/stuff/library/clapperboards/images/board1.png b/stuff/library/clapperboards/images/board1.png
new file mode 100644
index 0000000..2d03ef7
Binary files /dev/null and b/stuff/library/clapperboards/images/board1.png differ
diff --git a/stuff/library/clapperboards/preset_sample.clapperboard b/stuff/library/clapperboards/preset_sample.clapperboard
new file mode 100644
index 0000000..bcc3fd3
--- /dev/null
+++ b/stuff/library/clapperboards/preset_sample.clapperboard
@@ -0,0 +1,190 @@
+
+
+ 24
+
+
+ -
+
+ ScenePath_Full
+
+
+ "Scene file path"
+
+
+ 0.15 0.566708 0.7 0.0433166
+
+
+ 300
+
+
+ 0 0 0 255
+
+
+ Verdana 0 0
+
+
+ -
+
+ UserName
+
+
+ "User name"
+
+
+ 0.7 0.9 0.2 0.05
+
+
+ 300
+
+
+ 0 128 0 255
+
+
+ Verdana 0 1
+
+
+ -
+
+ CurrentDate
+
+
+ "Rendering date"
+
+
+ 0.1 0.9 0.2 0.05
+
+
+ 300
+
+
+ 0 0 0 255
+
+
+ Verdana 0 0
+
+
+ -
+
+ FreeText
+
+
+ "Type some description here"
+
+
+ 0.15 0.629176 0.55 0.248329
+
+
+ ""
+
+
+ 40
+
+
+ 0 0 64 255
+
+
+ Verdana 0 0
+
+
+ -
+
+ Duration_SecFrame
+
+
+ "Scene duration"
+
+
+ 0.351376 0.424454 0.3 0.125546
+
+
+ 300
+
+
+ 0 0 0 255
+
+
+ Verdana 0 0
+
+
+ -
+
+ SceneName
+
+
+ Scene
+
+
+ 0.35 0.25 0.3 0.2
+
+
+ 300
+
+
+ 0 0 0 255
+
+
+ Verdana 1 0
+
+
+ -
+
+ FreeText
+
+
+ "Episode #"
+
+
+ 0.15 0.3 0.2 0.2
+
+
+ "#"
+
+
+ 120
+
+
+ 0 0 0 255
+
+
+ Verdana 0 0
+
+
+ -
+
+ ProjectName
+
+
+ Title
+
+
+ 0.15 0.1 0.7 0.15
+
+
+ 300
+
+
+ 0 0 0 255
+
+
+ Verdana 1 0
+
+
+ -
+
+ Image
+
+
+ Background
+
+
+ 0 0 1 1
+
+
+ 1 "clapperboards\\images\\board1.png"
+
+
+ 0
+
+
+
+
diff --git a/toonz/sources/include/toonz/boardsettings.h b/toonz/sources/include/toonz/boardsettings.h
new file mode 100644
index 0000000..98dacbb
--- /dev/null
+++ b/toonz/sources/include/toonz/boardsettings.h
@@ -0,0 +1,141 @@
+#pragma once
+
+#ifndef BOARDSETTINGS_H
+#define BOARDSETTINGS_H
+
+#include "traster.h"
+#include "tfilepath.h"
+
+// TnzCore includes
+#include "tstream.h"
+
+#include
+#include
+#include
+#include
+
+#undef DVAPI
+#undef DVVAR
+#ifdef TOONZLIB_EXPORTS
+#define DVAPI DV_EXPORT_API
+#define DVVAR DV_EXPORT_VAR
+#else
+#define DVAPI DV_IMPORT_API
+#define DVVAR DV_IMPORT_VAR
+#endif
+
+class ToonzScene;
+
+class DVAPI BoardItem {
+public:
+ enum Type {
+ FreeText = 0,
+ ProjectName,
+ SceneName,
+ Duration_Frame,
+ Duration_SecFrame,
+ Duration_HHMMSSFF,
+ CurrentDate,
+ CurrentDateTime,
+ UserName,
+ ScenePath_Aliased,
+ ScenePath_Full,
+ MoviePath_Aliased,
+ MoviePath_Full,
+ Image,
+ TypeCount
+ };
+
+private:
+ QString m_name;
+ Type m_type;
+
+ // item region, represented by ratio to the whole
+ QRectF m_rect;
+
+ // Basically the font size will be automatically
+ // adjusted to fit the item region.
+ // The maximum font size can be used to make the font
+ // size smaller.
+ int m_maximumFontSize;
+
+ // font color
+ QColor m_color;
+
+ // font (familiy, bold, italic)
+ QFont m_font;
+
+ QString m_text;
+ TFilePath m_imgPath;
+ Qt::AspectRatioMode m_imgARMode = Qt::KeepAspectRatio;
+
+ QString getContentText(ToonzScene*);
+
+public:
+ BoardItem();
+
+ QRectF getRatioRect() { return m_rect; }
+ void setRatioRect(QRectF rect) { m_rect = rect; }
+
+ // returns the item rect in pixels
+ QRectF getItemRect(QSize imgSize);
+ void drawItem(QPainter& p, QSize imgSize, int shrink, ToonzScene* scene);
+
+ QString getName() { return m_name; }
+ void setName(QString name) { m_name = name; }
+
+ Type getType() { return m_type; }
+ void setType(Type type) { m_type = type; }
+
+ int getMaximumFontSize() { return m_maximumFontSize; }
+ void setMaximumFontSize(int size) { m_maximumFontSize = size; }
+
+ QColor getColor() { return m_color; }
+ void setColor(QColor color) { m_color = color; }
+
+ QFont& font() { return m_font; }
+
+ QString getFreeText() { return m_text; }
+ void setFreeText(QString text) { m_text = text; }
+
+ TFilePath getImgPath() { return m_imgPath; }
+ void setImgPath(TFilePath path) { m_imgPath = path; }
+
+ Qt::AspectRatioMode getImgARMode() { return m_imgARMode; }
+ void setImgARMode(Qt::AspectRatioMode mode) { m_imgARMode = mode; }
+
+ void saveData(TOStream& os);
+ void loadData(TIStream& is);
+};
+
+class DVAPI BoardSettings {
+ bool m_active = false;
+ int m_duration = 0;
+ QList m_items;
+
+public:
+ BoardSettings();
+
+ QImage getBoardImage(TDimension& dim, int shrink, ToonzScene* scene);
+
+ TRaster32P getBoardRaster(TDimension& dim, int shrink, ToonzScene* scene);
+
+ int getDuration() { return m_duration; }
+
+ bool isActive() { return m_active; }
+ void setActive(bool on) { m_active = on; }
+
+ int getItemCount() { return m_items.count(); }
+ BoardItem& getItem(int index) { return m_items[index]; }
+
+ void setDuration(int f) { m_duration = f; }
+
+ void addNewItem(int insertAt = 0);
+ void removeItem(int index);
+ void swapItems(int, int);
+
+ void saveData(TOStream& os, bool forPreset = false);
+ void loadData(TIStream& is);
+};
+
+#endif
\ No newline at end of file
diff --git a/toonz/sources/include/toonzqt/colorfield.h b/toonz/sources/include/toonzqt/colorfield.h
index 19b9c02..9aa5683 100644
--- a/toonz/sources/include/toonzqt/colorfield.h
+++ b/toonz/sources/include/toonzqt/colorfield.h
@@ -96,7 +96,7 @@ class DVAPI ChannelField final : public QWidget {
public:
ChannelField(QWidget *parent = 0, const QString &string = "", int value = 0,
int maxValue = 255, bool horizontal = false, int labelWidth = 13,
- int sliderWidth = 40);
+ int sliderWidth = -1);
~ChannelField() {}
@@ -145,7 +145,7 @@ public:
ColorField(QWidget *parent = 0, bool isAlphaActive = true,
TPixel32 color = TPixel32(0, 0, 0, 255), int squareSize = 40,
- bool useStyleEditor = true);
+ bool useStyleEditor = true, int sliderWidth = -1);
~ColorField() {}
diff --git a/toonz/sources/include/toutputproperties.h b/toonz/sources/include/toutputproperties.h
index 8c48652..ded931c 100644
--- a/toonz/sources/include/toutputproperties.h
+++ b/toonz/sources/include/toutputproperties.h
@@ -20,6 +20,7 @@
class TPropertyGroup;
class TWidget;
class TRenderSettings;
+class BoardSettings;
//=============================================================================
//! The TOutputProperties class provides a container for output properties and
@@ -91,6 +92,8 @@ private:
bool m_subcameraPreview;
+ BoardSettings *m_boardSettings;
+
public:
/*!
Constructs TOutputProperties with default value.
@@ -216,6 +219,8 @@ machine's CPU).
bool isSubcameraPreview() const { return m_subcameraPreview; }
void setSubcameraPreview(bool enabled) { m_subcameraPreview = enabled; }
+
+ BoardSettings *getBoardSettings() const { return m_boardSettings; }
};
//--------------------------------------------
diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt
index 004b5b1..ca889ff 100644
--- a/toonz/sources/toonz/CMakeLists.txt
+++ b/toonz/sources/toonz/CMakeLists.txt
@@ -158,6 +158,7 @@ set(MOC_HEADERS
reframepopup.h
autoinputcellnumberpopup.h
colormodelbehaviorpopup.h
+ boardsettingspopup.h
# Tracker file
dummyprocessor.h
metnum.h
@@ -331,6 +332,7 @@ set(SOURCES
reframepopup.cpp
autoinputcellnumberpopup.cpp
colormodelbehaviorpopup.cpp
+ boardsettingspopup.cpp
# Tracker file
dummyprocessor.cpp
metnum.cpp
diff --git a/toonz/sources/toonz/Resources/down.svg b/toonz/sources/toonz/Resources/down.svg
new file mode 100644
index 0000000..4e43af1
--- /dev/null
+++ b/toonz/sources/toonz/Resources/down.svg
@@ -0,0 +1,49 @@
+
+
\ No newline at end of file
diff --git a/toonz/sources/toonz/Resources/up.svg b/toonz/sources/toonz/Resources/up.svg
new file mode 100644
index 0000000..5621f63
--- /dev/null
+++ b/toonz/sources/toonz/Resources/up.svg
@@ -0,0 +1,49 @@
+
+
\ No newline at end of file
diff --git a/toonz/sources/toonz/boardsettingspopup.cpp b/toonz/sources/toonz/boardsettingspopup.cpp
new file mode 100644
index 0000000..78aecf9
--- /dev/null
+++ b/toonz/sources/toonz/boardsettingspopup.cpp
@@ -0,0 +1,1089 @@
+#include "boardsettingspopup.h"
+
+// Tnz6 includes
+#include "tapp.h"
+#include "filebrowser.h"
+
+// TnzQt includes
+#include "toonzqt/filefield.h"
+#include "toonzqt/colorfield.h"
+#include "toonzqt/intfield.h"
+
+// TnzLib includes
+#include "toutputproperties.h"
+#include "toonz/tscenehandle.h"
+#include "toonz/toonzscene.h"
+#include "toonz/sceneproperties.h"
+#include "toonz/tcamera.h"
+#include "toonz/boardsettings.h"
+#include "toonz/toonzfolders.h"
+
+// TnzBase includes
+#include "trasterfx.h"
+
+// TnzCore includes
+#include "tsystem.h"
+#include "tlevel_io.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+BoardItem* BoardSettingsPopup::currentBoardItem = nullptr;
+
+namespace {
+QMap stringByItemType;
+
+BoardItem* currentBoardItem() { return BoardSettingsPopup::currentBoardItem; }
+void setCurrentBoardItem(BoardItem* item) {
+ BoardSettingsPopup::currentBoardItem = item;
+}
+
+void editListWidgetItem(QListWidgetItem* listItem, BoardItem* item) {
+ QString itemText = item->getName() + "\n(" +
+ stringByItemType.value(item->getType(), "Unknown type") +
+ ")";
+ listItem->setText(itemText);
+
+ if (item->getType() == BoardItem::Image) {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ TFilePath decodedImgPath = scene->decodeFilePath(item->getImgPath());
+ QPixmap iconPm = QPixmap::fromImage(
+ QImage(decodedImgPath.getQString()).scaled(QSize(20, 20)));
+ listItem->setIcon(QIcon(iconPm));
+ } else {
+ QPixmap iconPm(20, 20);
+ iconPm.fill(item->getColor());
+ listItem->setIcon(QIcon(iconPm));
+ }
+}
+}
+
+//=============================================================================
+
+BoardView::BoardView(QWidget* parent) : QWidget(parent) {
+ setMouseTracking(true);
+}
+
+void BoardView::paintEvent(QPaintEvent* event) {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ QPainter p(this);
+
+ p.fillRect(rect(), Qt::black);
+
+ // Duration must be more than 0 to save the clapperboard.
+ // Scene file can maintain compatibility with older versions if you set the
+ // duration to 0.
+ if (boardSettings->getDuration() == 0) {
+ p.setPen(Qt::white);
+ QFont font = p.font();
+ font.setPixelSize(30);
+ p.setFont(font);
+ p.drawText(
+ rect().adjusted(5, 5, -5, -5), Qt::AlignCenter | Qt::TextWordWrap,
+ tr("Please set the duration more than 0 frame first, or the "
+ "clapperboard settings will not be saved in the scene at all!"));
+
+ p.setPen(QPen(Qt::red, 2));
+ p.drawLine(5, 5, 150, 5);
+ p.drawLine(5, 10, 150, 10);
+
+ return;
+ }
+
+ if (!m_valid) {
+ int shrinkX = outProp->getRenderSettings().m_shrinkX,
+ shrinkY = outProp->getRenderSettings().m_shrinkY;
+ TDimension frameSize = scene->getCurrentCamera()->getRes();
+ TDimension cameraRes(frameSize.lx / shrinkX, frameSize.ly / shrinkY);
+
+ m_boardImg = boardSettings->getBoardImage(cameraRes, shrinkX, scene);
+ m_valid = true;
+ }
+
+ p.drawImage(m_boardImgRect, m_boardImg);
+
+ p.translate(m_boardImgRect.topLeft());
+
+ p.setBrush(Qt::NoBrush);
+ for (int i = 0; i < boardSettings->getItemCount(); i++) {
+ BoardItem* item = &(boardSettings->getItem(i));
+ if (item == currentBoardItem()) {
+ p.setPen(QPen(QColor(255, 100, 0), 2));
+ } else {
+ p.setPen(QPen(QColor(64, 64, 255), 1, Qt::DashLine));
+ }
+ p.drawRect(item->getItemRect(m_boardImgRect.size().toSize()));
+ }
+}
+
+void BoardView::resizeEvent(QResizeEvent* event) {
+ QSize boardRes;
+ if (m_valid) {
+ boardRes = m_boardImg.size();
+ } else {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ int shrinkX = outProp->getRenderSettings().m_shrinkX,
+ shrinkY = outProp->getRenderSettings().m_shrinkY;
+ TDimension frameSize = scene->getCurrentCamera()->getRes();
+ boardRes = QSize(frameSize.lx / shrinkX, frameSize.ly / shrinkY);
+ }
+
+ float ratio = std::min((float)width() / (float)boardRes.width(),
+ (float)height() / (float)boardRes.height());
+ QSizeF imgSize = QSizeF(boardRes) * ratio;
+ QPointF imgTopLeft(((float)width() - imgSize.width()) * 0.5,
+ ((float)height() - imgSize.height()) * 0.5);
+
+ m_boardImgRect = QRectF(imgTopLeft, imgSize);
+}
+
+void BoardView::mouseMoveEvent(QMouseEvent* event) {
+ // mouse move - change drag item
+ if (!(event->buttons() & Qt::LeftButton)) {
+ if (!currentBoardItem()) {
+ m_dragItem = None;
+ setCursor(Qt::ArrowCursor);
+ return;
+ }
+
+ QPointF posOnImg = QPointF(event->pos()) - m_boardImgRect.topLeft();
+
+ QRectF itemRect =
+ currentBoardItem()->getItemRect(m_boardImgRect.size().toSize());
+
+ qreal distance = 10.0;
+
+ if ((itemRect.topLeft() - posOnImg).manhattanLength() < distance) {
+ m_dragItem = TopLeftCorner;
+ setCursor(Qt::SizeFDiagCursor);
+ } else if ((itemRect.topRight() - posOnImg).manhattanLength() < distance) {
+ m_dragItem = TopRightCorner;
+ setCursor(Qt::SizeBDiagCursor);
+ } else if ((itemRect.bottomRight() - posOnImg).manhattanLength() <
+ distance) {
+ m_dragItem = BottomRightCorner;
+ setCursor(Qt::SizeFDiagCursor);
+ } else if ((itemRect.bottomLeft() - posOnImg).manhattanLength() <
+ distance) {
+ m_dragItem = BottomLeftCorner;
+ setCursor(Qt::SizeBDiagCursor);
+ } else if (std::abs(itemRect.top() - posOnImg.y()) < distance &&
+ itemRect.left() < posOnImg.x() &&
+ itemRect.right() > posOnImg.x()) {
+ m_dragItem = TopEdge;
+ setCursor(Qt::SizeVerCursor);
+ } else if (std::abs(itemRect.right() - posOnImg.x()) < distance &&
+ itemRect.top() < posOnImg.y() &&
+ itemRect.bottom() > posOnImg.y()) {
+ m_dragItem = RightEdge;
+ setCursor(Qt::SizeHorCursor);
+ } else if (std::abs(itemRect.bottom() - posOnImg.y()) < distance &&
+ itemRect.left() < posOnImg.x() &&
+ itemRect.right() > posOnImg.x()) {
+ m_dragItem = BottomEdge;
+ setCursor(Qt::SizeVerCursor);
+ } else if (std::abs(itemRect.left() - posOnImg.x()) < distance &&
+ itemRect.top() < posOnImg.y() &&
+ itemRect.bottom() > posOnImg.y()) {
+ m_dragItem = LeftEdge;
+ setCursor(Qt::SizeHorCursor);
+ } else if (itemRect.contains(posOnImg)) {
+ m_dragItem = Translate;
+ setCursor(Qt::SizeAllCursor);
+ } else {
+ m_dragItem = None;
+ setCursor(Qt::ArrowCursor);
+ }
+
+ return;
+ }
+
+ // left mouse drag
+ if (m_dragItem == None) return;
+
+ if (m_dragStartItemRect.isNull()) return;
+
+ QPointF posOnImg = QPointF(event->pos()) - m_boardImgRect.topLeft();
+ QPointF ratioPos = QPointF(posOnImg.x() / m_boardImgRect.width(),
+ posOnImg.y() / m_boardImgRect.height());
+
+ // with alt : center resize
+ bool altPressed = event->modifiers() & Qt::AltModifier;
+ // with shift : align to discrete position
+ bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
+
+ auto adjVal = [&](double p) {
+ double step = 0.05;
+ if (!shiftPressed) return p;
+ return std::round(p / step) * step;
+ };
+ auto adjPos = [&](QPointF p) {
+ if (!shiftPressed) return p;
+ return QPointF(adjVal(p.x()), adjVal(p.y()));
+ };
+
+ QRectF newRect = m_dragStartItemRect;
+ QPointF d = ratioPos - m_dragStartPos;
+ switch (m_dragItem) {
+ case Translate:
+ newRect.translate(ratioPos - m_dragStartPos);
+ if (shiftPressed) {
+ newRect.setTopLeft(adjPos(newRect.topLeft()));
+ newRect.setBottomRight(adjPos(newRect.bottomRight()));
+ }
+ break;
+
+ case TopLeftCorner:
+ newRect.setTopLeft(adjPos(newRect.topLeft() + d));
+ if (altPressed) newRect.setBottomRight(adjPos(newRect.bottomRight() - d));
+ break;
+
+ case TopRightCorner:
+ newRect.setTopRight(adjPos(newRect.topRight() + d));
+ if (altPressed) newRect.setBottomLeft(adjPos(newRect.bottomLeft() - d));
+ break;
+
+ case BottomRightCorner:
+ newRect.setBottomRight(adjPos(newRect.bottomRight() + d));
+ if (altPressed) newRect.setTopLeft(adjPos(newRect.topLeft() - d));
+ break;
+
+ case BottomLeftCorner:
+ newRect.setBottomLeft(adjPos(newRect.bottomLeft() + d));
+ if (altPressed) newRect.setTopRight(adjPos(newRect.topRight() - d));
+ break;
+
+ case TopEdge:
+ newRect.setTop(adjVal(newRect.top() + d.y()));
+ if (altPressed) newRect.setBottom(adjVal(newRect.bottom() - d.y()));
+ break;
+
+ case RightEdge:
+ newRect.setRight(adjVal(newRect.right() + d.x()));
+ if (altPressed) newRect.setLeft(adjVal(newRect.left() - d.x()));
+ break;
+
+ case BottomEdge:
+ newRect.setBottom(adjVal(newRect.bottom() + d.y()));
+ if (altPressed) newRect.setTop(adjVal(newRect.top() - d.y()));
+ break;
+
+ case LeftEdge:
+ newRect.setLeft(adjVal(newRect.left() + d.x()));
+ if (altPressed) newRect.setRight(adjVal(newRect.right() - d.x()));
+ break;
+ default:
+ break;
+ }
+
+ currentBoardItem()->setRatioRect(newRect.normalized());
+ invalidate();
+ update();
+}
+
+void BoardView::mousePressEvent(QMouseEvent* event) {
+ // only the left button counts
+ if (event->button() != Qt::LeftButton) return;
+ // store the current item rect and mouse pos, relative to the image size
+ m_dragStartItemRect = currentBoardItem()->getRatioRect();
+ QPointF posOnImg = QPointF(event->pos()) - m_boardImgRect.topLeft();
+ m_dragStartPos = QPointF(posOnImg.x() / m_boardImgRect.width(),
+ posOnImg.y() / m_boardImgRect.height());
+}
+
+void BoardView::mouseReleaseEvent(QMouseEvent* event) {
+ m_dragStartItemRect = QRectF();
+ m_dragStartPos = QPointF();
+}
+
+//=============================================================================
+
+ItemInfoView::ItemInfoView(QWidget* parent) : QStackedWidget(parent) {
+ m_nameEdit = new QLineEdit(this);
+ m_maxFontSizeEdit = new DVGui::IntLineEdit(this, 1, 1);
+ m_typeCombo = new QComboBox(this);
+ m_textEdit = new QTextEdit(this);
+ m_imgPathField = new DVGui::FileField(this);
+ m_fontCombo = new QFontComboBox(this);
+ m_boldButton =
+ new QPushButton(QIcon(":Resources/bold_on.png"), tr("Bold"), this);
+ m_italicButton =
+ new QPushButton(QIcon(":Resources/italic_on.png"), tr("Italic"), this);
+ m_fontColorField =
+ new DVGui::ColorField(this, true, TPixel32(0, 0, 0, 255), 25, false, 54);
+ m_imgARModeCombo = new QComboBox(this);
+
+ m_fontPropBox = new QWidget(this);
+ m_imgPropBox = new QWidget(this);
+
+ for (int i = 0; i < BoardItem::TypeCount; i++) {
+ m_typeCombo->addItem(stringByItemType[(BoardItem::Type)i]);
+ }
+
+ m_textEdit->setAcceptRichText(false);
+ m_textEdit->setStyleSheet(
+ "background:white;\ncolor:black;\nborder:1 solid black;");
+
+ m_boldButton->setIconSize(QSize(30, 30));
+ m_boldButton->setFixedHeight(25);
+ m_boldButton->setCheckable(true);
+ m_italicButton->setIconSize(QSize(30, 30));
+ m_italicButton->setFixedHeight(25);
+ m_italicButton->setCheckable(true);
+
+ QStringList filters;
+ for (QByteArray& format : QImageReader::supportedImageFormats())
+ filters += format;
+ m_imgPathField->setFilters(filters);
+ m_imgPathField->setFileMode(QFileDialog::ExistingFile);
+
+ m_imgARModeCombo->addItem(tr("Ignore"), Qt::IgnoreAspectRatio);
+ m_imgARModeCombo->addItem(tr("Keep"), Qt::KeepAspectRatio);
+
+ //----- layout
+
+ QGridLayout* mainLay = new QGridLayout();
+ mainLay->setMargin(5);
+ mainLay->setHorizontalSpacing(3);
+ mainLay->setVerticalSpacing(10);
+ {
+ mainLay->addWidget(new QLabel(tr("Name:"), this), 0, 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ mainLay->addWidget(m_nameEdit, 0, 1);
+
+ mainLay->addWidget(new QLabel(tr("Type:"), this), 1, 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ mainLay->addWidget(m_typeCombo, 1, 1);
+
+ QVBoxLayout* extraInfoLay = new QVBoxLayout();
+ extraInfoLay->setMargin(0);
+ extraInfoLay->setSpacing(0);
+ {
+ extraInfoLay->addWidget(m_textEdit, 1);
+
+ QGridLayout* imgPropLay = new QGridLayout();
+ imgPropLay->setMargin(0);
+ imgPropLay->setHorizontalSpacing(3);
+ imgPropLay->setVerticalSpacing(10);
+ {
+ imgPropLay->addWidget(new QLabel(tr("Path:"), this), 0, 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ imgPropLay->addWidget(m_imgPathField, 0, 1, 1, 2);
+
+ imgPropLay->addWidget(new QLabel(tr("Aspect Ratio:"), this), 1, 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ imgPropLay->addWidget(m_imgARModeCombo, 1, 1);
+ }
+ imgPropLay->setColumnStretch(0, 0);
+ imgPropLay->setColumnStretch(1, 0);
+ imgPropLay->setColumnStretch(2, 1);
+ m_imgPropBox->setLayout(imgPropLay);
+ extraInfoLay->addWidget(m_imgPropBox, 0);
+
+ extraInfoLay->addSpacing(5);
+
+ QGridLayout* fontPropLay = new QGridLayout();
+ fontPropLay->setMargin(0);
+ fontPropLay->setHorizontalSpacing(3);
+ fontPropLay->setVerticalSpacing(10);
+ {
+ fontPropLay->addWidget(new QLabel(tr("Font:"), this), 0, 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ fontPropLay->addWidget(m_fontCombo, 0, 1, 1, 4);
+
+ fontPropLay->addWidget(new QLabel(tr("Max Size:"), this), 1, 0,
+ Qt::AlignRight | Qt::AlignVCenter);
+ fontPropLay->addWidget(m_maxFontSizeEdit, 1, 1);
+
+ fontPropLay->addWidget(m_boldButton, 1, 2);
+ fontPropLay->addWidget(m_italicButton, 1, 3);
+
+ fontPropLay->addWidget(m_fontColorField, 2, 0, 1, 5);
+ }
+ fontPropLay->setColumnStretch(0, 0);
+ fontPropLay->setColumnStretch(1, 0);
+ fontPropLay->setColumnStretch(2, 0);
+ fontPropLay->setColumnStretch(3, 0);
+ fontPropLay->setColumnStretch(4, 1);
+ m_fontPropBox->setLayout(fontPropLay);
+ extraInfoLay->addWidget(m_fontPropBox, 0);
+ }
+ mainLay->addLayout(extraInfoLay, 2, 0, 1, 2);
+ }
+ mainLay->setColumnStretch(0, 0);
+ mainLay->setColumnStretch(1, 1);
+ mainLay->setRowStretch(0, 0);
+ mainLay->setRowStretch(1, 0);
+ mainLay->setRowStretch(2, 1);
+
+ QWidget* mainWidget = new QWidget(this);
+ mainWidget->setLayout(mainLay);
+ addWidget(mainWidget);
+
+ addWidget(new QLabel(tr("No item selected."), this));
+
+ bool ret = true;
+ ret = ret && connect(m_nameEdit, SIGNAL(editingFinished()), this,
+ SLOT(onNameEdited()));
+ ret = ret && connect(m_maxFontSizeEdit, SIGNAL(editingFinished()), this,
+ SLOT(onMaxFontSizeEdited()));
+ ret = ret && connect(m_typeCombo, SIGNAL(activated(int)), this,
+ SLOT(onTypeComboActivated(int)));
+ ret = ret && connect(m_textEdit, SIGNAL(textChanged()), this,
+ SLOT(onFreeTextChanged()));
+ ret = ret && connect(m_imgPathField, SIGNAL(pathChanged()), this,
+ SLOT(onImgPathChanged()));
+ ret = ret && connect(m_fontCombo, SIGNAL(currentFontChanged(const QFont&)),
+ this, SLOT(onFontComboChanged(const QFont&)));
+ ret = ret && connect(m_boldButton, SIGNAL(clicked(bool)), this,
+ SLOT(onBoldButtonClicked(bool)));
+ ret = ret && connect(m_italicButton, SIGNAL(clicked(bool)), this,
+ SLOT(onItalicButtonClicked(bool)));
+ ret = ret &&
+ connect(m_fontColorField, SIGNAL(colorChanged(const TPixel32&, bool)),
+ this, SLOT(onFontColorChanged(const TPixel32&, bool)));
+ ret = ret && connect(m_imgARModeCombo, SIGNAL(activated(int)), this,
+ SLOT(onImgARModeComboActivated()));
+ assert(ret);
+}
+
+// called on switching the current item
+void ItemInfoView::setCurrentItem(int index) {
+ if (index == -1) {
+ if (!currentBoardItem()) return;
+ setCurrentBoardItem(nullptr);
+ setCurrentIndex(1); // set to "no item seleceted" page.
+ return;
+ }
+
+ setCurrentIndex(0); // set to normal page.
+
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ if (index >= boardSettings->getItemCount()) return;
+
+ BoardItem* newItem = &(boardSettings->getItem(index));
+ if (currentBoardItem() == newItem) return;
+
+ setCurrentBoardItem(newItem);
+
+ // sync
+ m_nameEdit->setText(newItem->getName());
+ m_maxFontSizeEdit->setValue(newItem->getMaximumFontSize());
+ m_typeCombo->setCurrentIndex((int)newItem->getType());
+ m_textEdit->setText(newItem->getFreeText());
+ m_imgPathField->setPath(newItem->getImgPath().getQString());
+ m_fontCombo->setCurrentFont(newItem->font());
+ m_boldButton->setChecked(newItem->font().bold());
+ m_italicButton->setChecked(newItem->font().italic());
+ QColor col = newItem->getColor();
+ m_fontColorField->setColor(
+ TPixel32(col.red(), col.green(), col.blue(), col.alpha()));
+ int ARModeIndex = m_imgARModeCombo->findData(newItem->getImgARMode());
+ if (ARModeIndex >= 0) m_imgARModeCombo->setCurrentIndex(ARModeIndex);
+
+ switch (newItem->getType()) {
+ case BoardItem::FreeText:
+ m_textEdit->show();
+ m_imgPropBox->hide();
+ m_fontPropBox->show();
+ break;
+ case BoardItem::Image:
+ m_textEdit->hide();
+ m_imgPropBox->show();
+ m_fontPropBox->hide();
+ break;
+ default:
+ m_textEdit->hide();
+ m_imgPropBox->hide();
+ m_fontPropBox->show();
+ break;
+ }
+}
+
+void ItemInfoView::onNameEdited() {
+ assert(currentBoardItem());
+
+ QString newName = m_nameEdit->text();
+
+ if (newName.isEmpty()) {
+ newName = tr("Item");
+ m_nameEdit->setText(newName);
+ }
+
+ if (currentBoardItem()->getName() == newName) return;
+
+ currentBoardItem()->setName(newName);
+
+ emit itemPropertyChanged(true);
+}
+
+void ItemInfoView::onMaxFontSizeEdited() {
+ assert(currentBoardItem());
+
+ int maxFontSize = m_maxFontSizeEdit->getValue();
+
+ if (currentBoardItem()->getMaximumFontSize() == maxFontSize) return;
+
+ currentBoardItem()->setMaximumFontSize(maxFontSize);
+
+ emit itemPropertyChanged(false);
+}
+
+void ItemInfoView::onTypeComboActivated(int) {
+ assert(currentBoardItem());
+
+ BoardItem::Type newType = (BoardItem::Type)m_typeCombo->currentIndex();
+
+ if (currentBoardItem()->getType() == newType) return;
+
+ currentBoardItem()->setType(newType);
+
+ switch (newType) {
+ case BoardItem::FreeText:
+ m_textEdit->show();
+ m_imgPropBox->hide();
+ m_fontPropBox->show();
+ break;
+ case BoardItem::Image:
+ m_textEdit->hide();
+ m_imgPropBox->show();
+ m_fontPropBox->hide();
+ break;
+ default:
+ m_textEdit->hide();
+ m_imgPropBox->hide();
+ m_fontPropBox->show();
+ break;
+ }
+
+ emit itemPropertyChanged(true);
+}
+
+void ItemInfoView::onFreeTextChanged() {
+ assert(currentBoardItem());
+
+ currentBoardItem()->setFreeText(m_textEdit->toPlainText());
+
+ emit itemPropertyChanged(false);
+}
+
+void ItemInfoView::onImgPathChanged() {
+ assert(currentBoardItem());
+ TFilePath fp(m_imgPathField->getPath());
+ if (fp.isLevelName()) {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ TLevelReaderP lr(scene->decodeFilePath(fp));
+ TLevelP level;
+ if (lr) level = lr->loadInfo();
+ if (level.getPointer() && level->getTable()->size() > 0) {
+ TFrameId firstFrame = level->begin()->first;
+ fp = fp.withFrame(firstFrame);
+ m_imgPathField->setPath(fp.getQString());
+ }
+ }
+ currentBoardItem()->setImgPath(fp);
+
+ emit itemPropertyChanged(true);
+}
+
+void ItemInfoView::onFontComboChanged(const QFont& newFont) {
+ assert(currentBoardItem());
+
+ currentBoardItem()->font().setFamily(newFont.family());
+
+ emit itemPropertyChanged(false);
+}
+
+void ItemInfoView::onBoldButtonClicked(bool on) {
+ assert(currentBoardItem());
+
+ currentBoardItem()->font().setBold(on);
+
+ emit itemPropertyChanged(false);
+}
+
+void ItemInfoView::onItalicButtonClicked(bool on) {
+ assert(currentBoardItem());
+
+ currentBoardItem()->font().setItalic(on);
+
+ emit itemPropertyChanged(false);
+}
+
+void ItemInfoView::onFontColorChanged(const TPixel32& color, bool isDragging) {
+ assert(currentBoardItem());
+
+ if (isDragging) return;
+
+ QColor newColor((int)color.r, (int)color.g, (int)color.b, (int)color.m);
+ if (currentBoardItem()->getColor() == newColor) return;
+ currentBoardItem()->setColor(newColor);
+
+ emit itemPropertyChanged(true);
+}
+
+void ItemInfoView::onImgARModeComboActivated() {
+ assert(currentBoardItem());
+
+ currentBoardItem()->setImgARMode(
+ (Qt::AspectRatioMode)m_imgARModeCombo->currentData().toInt());
+
+ emit itemPropertyChanged(false);
+}
+
+//=============================================================================
+
+ItemListView::ItemListView(QWidget* parent) : QWidget(parent) {
+ QPushButton* newItemBtn =
+ new QPushButton(QIcon(":Resources/plus.svg"), tr("Add"), this);
+ m_deleteItemBtn =
+ new QPushButton(QIcon(":Resources/delete_on.svg"), tr("Remove"), this);
+ m_moveUpBtn =
+ new QPushButton(QIcon(":Resources/up.svg"), tr("Move Up"), this);
+ m_moveDownBtn =
+ new QPushButton(QIcon(":Resources/down.svg"), tr("Move Down"), this);
+
+ m_list = new QListWidget(this);
+
+ QSize iconSize(20, 20);
+
+ newItemBtn->setIconSize(iconSize);
+ m_deleteItemBtn->setIconSize(iconSize);
+ m_moveUpBtn->setIconSize(iconSize);
+ m_moveDownBtn->setIconSize(iconSize);
+
+ m_list->setIconSize(QSize(20, 20));
+ m_list->setAlternatingRowColors(true);
+ m_list->setMaximumWidth(225);
+
+ QHBoxLayout* mainLay = new QHBoxLayout();
+ mainLay->setMargin(5);
+ mainLay->setSpacing(5);
+ {
+ QVBoxLayout* buttonsLay = new QVBoxLayout();
+ buttonsLay->setMargin(0);
+ buttonsLay->setSpacing(3);
+ {
+ buttonsLay->addWidget(newItemBtn, 0);
+ buttonsLay->addWidget(m_deleteItemBtn, 0);
+ buttonsLay->addWidget(m_moveUpBtn, 0);
+ buttonsLay->addWidget(m_moveDownBtn, 0);
+ buttonsLay->addStretch(1);
+ }
+ mainLay->addLayout(buttonsLay, 0);
+
+ mainLay->addWidget(m_list, 1);
+ }
+ setLayout(mainLay);
+
+ bool ret = true;
+ ret = ret && connect(m_list, SIGNAL(currentRowChanged(int)), this,
+ SLOT(onCurrentItemSwitched(int)));
+ ret = ret && connect(newItemBtn, SIGNAL(clicked(bool)), this,
+ SLOT(onNewItemButtonClicked()));
+ ret = ret && connect(m_deleteItemBtn, SIGNAL(clicked(bool)), this,
+ SLOT(onDeleteItemButtonClicked()));
+ ret = ret && connect(m_moveUpBtn, SIGNAL(clicked(bool)), this,
+ SLOT(onMoveUpButtonClicked()));
+ ret = ret && connect(m_moveDownBtn, SIGNAL(clicked(bool)), this,
+ SLOT(onMoveDownButtonClicked()));
+ assert(ret);
+}
+
+void ItemListView::initialize() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ // first, clear all the list items
+ m_list->clear();
+
+ if (boardSettings->getItemCount() == 0) return;
+
+ for (int i = 0; i < boardSettings->getItemCount(); i++) {
+ BoardItem* item = &(boardSettings->getItem(i));
+ QListWidgetItem* listItem = new QListWidgetItem(m_list);
+
+ editListWidgetItem(listItem, item);
+ }
+
+ // this will update information view
+ m_list->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
+}
+
+// called when the infoView is edited
+void ItemListView::updateCurrentItem() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ int currentItemIndex = m_list->currentRow();
+
+ if (currentItemIndex < 0 || currentItemIndex >= boardSettings->getItemCount())
+ return;
+
+ BoardItem* item = &(boardSettings->getItem(currentItemIndex));
+ editListWidgetItem(m_list->item(currentItemIndex), item);
+}
+
+void ItemListView::onCurrentItemSwitched(int currentRow) {
+ if (currentRow == -1) {
+ m_moveUpBtn->setEnabled(false);
+ m_moveDownBtn->setEnabled(false);
+ } else {
+ m_moveUpBtn->setEnabled(currentRow != 0);
+ m_moveDownBtn->setEnabled(currentRow != m_list->count() - 1);
+ }
+ emit currentItemSwitched(currentRow);
+}
+
+void ItemListView::onNewItemButtonClicked() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ boardSettings->addNewItem();
+
+ QListWidgetItem* listItem = new QListWidgetItem();
+ BoardItem* item = &(boardSettings->getItem(0));
+
+ editListWidgetItem(listItem, item);
+
+ m_list->insertItem(0, listItem);
+ m_list->setCurrentRow(0, QItemSelectionModel::ClearAndSelect);
+
+ m_deleteItemBtn->setEnabled(true);
+
+ emit itemAddedOrDeleted();
+}
+
+void ItemListView::onDeleteItemButtonClicked() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ int currentRow = m_list->currentRow();
+
+ assert(currentRow != -1);
+
+ boardSettings->removeItem(currentRow);
+
+ if (m_list->count() == 1) {
+ m_deleteItemBtn->setEnabled(false);
+ m_list->setCurrentRow(-1, QItemSelectionModel::ClearAndSelect);
+ } else if (currentRow == m_list->count() - 1)
+ m_list->setCurrentRow(m_list->count() - 2,
+ QItemSelectionModel::ClearAndSelect);
+
+ delete m_list->takeItem(currentRow);
+
+ emit itemAddedOrDeleted();
+}
+
+void ItemListView::onMoveUpButtonClicked() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ int currentRow = m_list->currentRow();
+ assert(currentRow > 0);
+
+ boardSettings->swapItems(currentRow - 1, currentRow);
+
+ m_list->insertItem(currentRow - 1, m_list->takeItem(currentRow));
+ m_list->setCurrentRow(currentRow - 1);
+
+ emit itemAddedOrDeleted();
+}
+
+void ItemListView::onMoveDownButtonClicked() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ int currentRow = m_list->currentRow();
+ assert(currentRow < m_list->count() - 1);
+
+ boardSettings->swapItems(currentRow, currentRow + 1);
+
+ QListWidgetItem* item = m_list->takeItem(currentRow);
+ m_list->insertItem(currentRow + 1, item);
+ m_list->setCurrentRow(currentRow + 1);
+
+ emit itemAddedOrDeleted();
+}
+
+//=============================================================================
+
+BoardSettingsPopup::BoardSettingsPopup(QWidget* parent)
+ : DVGui::Dialog(parent, true, false, "ClapperboardSettings") {
+ setWindowTitle(tr("Clapperboard Settings"));
+
+ initializeItemTypeString();
+
+ m_boardView = new BoardView(this);
+ m_itemInfoView = new ItemInfoView(this);
+ m_itemListView = new ItemListView(this);
+
+ m_durationEdit = new DVGui::IntLineEdit(this, 1, 0);
+
+ QPushButton* loadPresetBtn =
+ new QPushButton(QIcon(":Resources/load_on.svg"), tr("Load Preset"), this);
+ QPushButton* savePresetBtn = new QPushButton(QIcon(":Resources/save_on.svg"),
+ tr("Save as Preset"), this);
+
+ QPushButton* closeButton = new QPushButton(tr("Close"), this);
+
+ //--- layout
+
+ QHBoxLayout* mainLay = new QHBoxLayout();
+ mainLay->setMargin(0);
+ mainLay->setSpacing(10);
+ {
+ QVBoxLayout* leftLay = new QVBoxLayout();
+ leftLay->setMargin(0);
+ leftLay->setSpacing(0);
+ {
+ QHBoxLayout* leftTopLay = new QHBoxLayout();
+ leftTopLay->setMargin(5);
+ leftTopLay->setSpacing(3);
+ {
+ leftTopLay->addWidget(new QLabel(tr("Duration (frames):"), this), 0);
+ leftTopLay->addWidget(m_durationEdit, 0);
+
+ leftTopLay->addSpacing(10);
+
+ leftTopLay->addWidget(loadPresetBtn, 0);
+ leftTopLay->addWidget(savePresetBtn, 0);
+
+ leftTopLay->addStretch(1);
+ }
+ leftLay->addLayout(leftTopLay, 0);
+
+ leftLay->addWidget(m_boardView, 1);
+ }
+ mainLay->addLayout(leftLay, 1);
+
+ QVBoxLayout* rightLay = new QVBoxLayout();
+ rightLay->setMargin(0);
+ rightLay->setSpacing(15);
+ {
+ rightLay->addWidget(m_itemInfoView, 0);
+ rightLay->addWidget(m_itemListView, 1);
+ }
+ mainLay->addLayout(rightLay, 0);
+ }
+ m_topLayout->addLayout(mainLay, 1);
+
+ addButtonBarWidget(closeButton);
+
+ bool ret = true;
+ ret = ret && connect(m_itemListView, SIGNAL(currentItemSwitched(int)), this,
+ SLOT(onCurrentItemSwitched(int)));
+ ret = ret && connect(m_itemListView, SIGNAL(itemAddedOrDeleted()), this,
+ SLOT(onItemAddedOrDeleted()));
+ ret = ret && connect(m_itemInfoView, SIGNAL(itemPropertyChanged(bool)), this,
+ SLOT(onItemPropertyChanged(bool)));
+ ret = ret && connect(m_durationEdit, SIGNAL(editingFinished()), this,
+ SLOT(onDurationEdited()));
+ ret = ret && connect(closeButton, SIGNAL(pressed()), this, SLOT(close()));
+ ret = ret &&
+ connect(loadPresetBtn, SIGNAL(pressed()), this, SLOT(onLoadPreset()));
+ ret = ret &&
+ connect(savePresetBtn, SIGNAL(pressed()), this, SLOT(onSavePreset()));
+
+ assert(ret);
+}
+
+void BoardSettingsPopup::initialize() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ m_durationEdit->setValue(boardSettings->getDuration());
+
+ m_itemListView->initialize();
+
+ m_boardView->invalidate();
+ m_boardView->update();
+}
+
+void BoardSettingsPopup::initializeItemTypeString() {
+ if (!stringByItemType.isEmpty()) return;
+
+ stringByItemType[BoardItem::FreeText] = tr("Text"); // free text (m_text)
+ stringByItemType[BoardItem::ProjectName] = tr("Project name");
+ stringByItemType[BoardItem::SceneName] = tr("Scene name");
+ stringByItemType[BoardItem::Duration_Frame] = tr("Duration : Frame");
+ stringByItemType[BoardItem::Duration_SecFrame] = tr("Duration : Sec + Frame");
+ stringByItemType[BoardItem::Duration_HHMMSSFF] = tr("Duration : HH:MM:SS:FF");
+ stringByItemType[BoardItem::CurrentDate] = tr("Current date");
+ stringByItemType[BoardItem::CurrentDateTime] = tr("Current date and time");
+ stringByItemType[BoardItem::UserName] = tr("User name");
+ stringByItemType[BoardItem::ScenePath_Aliased] =
+ tr("Scene location : Aliased path");
+ stringByItemType[BoardItem::ScenePath_Full] =
+ tr("Scene location : Full path");
+ stringByItemType[BoardItem::MoviePath_Aliased] =
+ tr("Output location : Aliased path");
+ stringByItemType[BoardItem::MoviePath_Full] =
+ tr("Output location : Full path");
+ stringByItemType[BoardItem::Image] = tr("Image"); // m_imgPath
+}
+
+void BoardSettingsPopup::hideEvent(QHideEvent* event) {
+ setCurrentBoardItem(nullptr);
+}
+
+// called on changing the current row of ItemListView
+void BoardSettingsPopup::onCurrentItemSwitched(int index) {
+ // updat Info
+ m_itemInfoView->setCurrentItem(index);
+
+ m_boardView->update();
+}
+
+void BoardSettingsPopup::onItemAddedOrDeleted() {
+ m_boardView->invalidate();
+ m_boardView->update();
+}
+
+// called on modifying the infoView
+void BoardSettingsPopup::onItemPropertyChanged(bool updateListView) {
+ m_boardView->invalidate();
+ m_boardView->update();
+
+ if (updateListView) m_itemListView->updateCurrentItem();
+}
+
+void BoardSettingsPopup::onDurationEdited() {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ boardSettings->setDuration(m_durationEdit->getValue());
+
+ m_boardView->update();
+}
+
+void BoardSettingsPopup::onLoadPreset() {
+ LoadBoardPresetFilePopup popup;
+
+ TFilePath fp = popup.getPath();
+ if (fp.isEmpty()) return;
+
+ TIStream is(fp);
+ if (!is) throw TException(fp.getWideString() + L": Can't open file");
+ try {
+ std::string tagName = "";
+ if (!is.matchTag(tagName)) throw TException("Bad file format");
+ if (tagName == "clapperboardSettingsPreset") {
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp =
+ scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ boardSettings->loadData(is);
+ } else
+ throw TException("Bad file format");
+ } catch (const TSystemException& se) {
+ DVGui::warning(QString::fromStdWString(se.getMessage()));
+ } catch (...) {
+ DVGui::error(QObject::tr("Couldn't load %1").arg(fp.getQString()));
+ }
+
+ initialize();
+}
+
+void BoardSettingsPopup::onSavePreset() {
+ SaveBoardPresetFilePopup popup;
+
+ TFilePath fp = popup.getPath();
+ if (fp.isEmpty()) return;
+
+ try {
+ TFileStatus fs(fp);
+ if (fs.doesExist() && !fs.isWritable()) {
+ throw TSystemException(
+ fp, "The preset cannot be saved: it is a read only file.");
+ }
+
+ TOStream os(fp, false);
+ if (!os.checkStatus()) throw TException("Could not open file");
+
+ ToonzScene* scene = TApp::instance()->getCurrentScene()->getScene();
+ if (!scene) return;
+ TOutputProperties* outProp = scene->getProperties()->getOutputProperties();
+ BoardSettings* boardSettings = outProp->getBoardSettings();
+
+ os.openChild("clapperboardSettingsPreset");
+ boardSettings->saveData(os, true);
+ os.closeChild();
+
+ bool status = os.checkStatus();
+ if (!status) throw TException("Could not complete the save preset");
+
+ } catch (const TSystemException& se) {
+ DVGui::warning(QString::fromStdWString(se.getMessage()));
+ } catch (...) {
+ DVGui::error(QObject::tr("Couldn't save %1").arg(fp.getQString()));
+ }
+}
+
+//=============================================================================
+
+SaveBoardPresetFilePopup::SaveBoardPresetFilePopup()
+ : GenericSaveFilePopup(tr("Save Clapperboard Settings As Preset")) {
+ m_browser->enableGlobalSelection(false);
+ setFilterTypes(QStringList("clapperboard"));
+}
+
+void SaveBoardPresetFilePopup::showEvent(QShowEvent* e) {
+ FileBrowserPopup::showEvent(e);
+ setFolder(ToonzFolder::getLibraryFolder() + "clapperboards");
+}
+
+//=============================================================================
+
+LoadBoardPresetFilePopup::LoadBoardPresetFilePopup()
+ : GenericLoadFilePopup(tr("Load Clapperboard Settings Preset")) {
+ m_browser->enableGlobalSelection(false);
+ setFilterTypes(QStringList("clapperboard"));
+}
+
+void LoadBoardPresetFilePopup::showEvent(QShowEvent* e) {
+ FileBrowserPopup::showEvent(e);
+ setFolder(ToonzFolder::getLibraryFolder() + "clapperboards");
+}
\ No newline at end of file
diff --git a/toonz/sources/toonz/boardsettingspopup.h b/toonz/sources/toonz/boardsettingspopup.h
new file mode 100644
index 0000000..fb86d46
--- /dev/null
+++ b/toonz/sources/toonz/boardsettingspopup.h
@@ -0,0 +1,175 @@
+#pragma once
+
+#ifndef BOARDSETTINGSPOPUP_H
+#define BOARDSETTINGSPOPUP_H
+
+#include "toonzqt/dvdialog.h"
+#include "tpixel.h"
+#include "filebrowserpopup.h"
+#include
+#include
+
+class TOutputProperties;
+class QLineEdit;
+class QTextEdit;
+class QComboBox;
+class QFontComboBox;
+class QListWidget;
+class BoardItem;
+
+namespace DVGui {
+class FileField;
+class ColorField;
+class IntLineEdit;
+}
+
+//=============================================================================
+
+class BoardView : public QWidget {
+ Q_OBJECT
+
+ enum DragItem {
+ None = 0,
+ Translate,
+ TopLeftCorner,
+ TopRightCorner,
+ BottomRightCorner,
+ BottomLeftCorner,
+ TopEdge,
+ RightEdge,
+ BottomEdge,
+ LeftEdge
+ } m_dragItem = None;
+
+ QImage m_boardImg;
+ bool m_valid = false;
+
+ QRectF m_boardImgRect;
+
+ QRectF m_dragStartItemRect;
+ QPointF m_dragStartPos;
+
+public:
+ BoardView(QWidget* parent = nullptr);
+ void invalidate() { m_valid = false; }
+
+protected:
+ void paintEvent(QPaintEvent* event) override;
+ void resizeEvent(QResizeEvent* event) override;
+
+ void mouseMoveEvent(QMouseEvent* event) override;
+ void mousePressEvent(QMouseEvent* event) override;
+ void mouseReleaseEvent(QMouseEvent* event) override;
+};
+
+//=============================================================================
+
+class ItemInfoView : public QStackedWidget {
+ Q_OBJECT
+
+ QLineEdit* m_nameEdit;
+ DVGui::IntLineEdit* m_maxFontSizeEdit;
+ QComboBox* m_typeCombo;
+ QTextEdit* m_textEdit;
+ DVGui::FileField* m_imgPathField;
+ QFontComboBox* m_fontCombo;
+ QPushButton *m_boldButton, *m_italicButton;
+ DVGui::ColorField* m_fontColorField;
+ QComboBox* m_imgARModeCombo;
+
+ QWidget *m_fontPropBox, *m_imgPropBox;
+
+public:
+ ItemInfoView(QWidget* parent = nullptr);
+ void setCurrentItem(int index);
+
+protected slots:
+ void onNameEdited();
+ void onMaxFontSizeEdited();
+ void onTypeComboActivated(int);
+ void onFreeTextChanged();
+ void onImgPathChanged();
+ void onFontComboChanged(const QFont&);
+ void onBoldButtonClicked(bool);
+ void onItalicButtonClicked(bool);
+ void onFontColorChanged(const TPixel32&, bool);
+ void onImgARModeComboActivated();
+
+signals:
+ // if updateListView is true then update the list view as well
+ void itemPropertyChanged(bool updateListView);
+};
+
+//=============================================================================
+
+class ItemListView : public QWidget {
+ Q_OBJECT
+ QListWidget* m_list;
+ QPushButton *m_deleteItemBtn, *m_moveUpBtn, *m_moveDownBtn;
+
+public:
+ ItemListView(QWidget* parent = nullptr);
+ void initialize();
+ void updateCurrentItem();
+protected slots:
+ void onCurrentItemSwitched(int);
+ void onNewItemButtonClicked();
+ void onDeleteItemButtonClicked();
+ void onMoveUpButtonClicked();
+ void onMoveDownButtonClicked();
+signals:
+ void currentItemSwitched(int);
+ void itemAddedOrDeleted();
+};
+
+//=============================================================================
+
+class BoardSettingsPopup : public DVGui::Dialog {
+ Q_OBJECT
+
+ BoardView* m_boardView;
+ ItemInfoView* m_itemInfoView;
+ ItemListView* m_itemListView;
+
+ DVGui::IntLineEdit* m_durationEdit;
+
+ void initialize();
+ void initializeItemTypeString(); // call once on the first launch
+public:
+ static BoardItem* currentBoardItem;
+
+ BoardSettingsPopup(QWidget* parent = nullptr);
+
+protected:
+ void showEvent(QShowEvent*) override { initialize(); }
+ void hideEvent(QHideEvent*) override;
+protected slots:
+ void onCurrentItemSwitched(int);
+ void onItemAddedOrDeleted();
+ void onItemPropertyChanged(bool updateListView);
+ void onDurationEdited();
+ void onLoadPreset();
+ void onSavePreset();
+};
+
+//=============================================================================
+
+class SaveBoardPresetFilePopup final : public GenericSaveFilePopup {
+public:
+ SaveBoardPresetFilePopup();
+
+protected:
+ void showEvent(QShowEvent*) override;
+};
+
+//=============================================================================
+
+class LoadBoardPresetFilePopup final : public GenericLoadFilePopup {
+public:
+ LoadBoardPresetFilePopup();
+
+protected:
+ void showEvent(QShowEvent*) override;
+};
+
+#endif
diff --git a/toonz/sources/toonz/filebrowserpopup.h b/toonz/sources/toonz/filebrowserpopup.h
index 291ea79..a74d202 100644
--- a/toonz/sources/toonz/filebrowserpopup.h
+++ b/toonz/sources/toonz/filebrowserpopup.h
@@ -156,7 +156,7 @@ public:
//! The GenericLoadFilePopup is a simple, standard Toonz popup that
//! asks the user for \a single file to be loaded from disk.
-class GenericLoadFilePopup final : public FileBrowserPopup {
+class GenericLoadFilePopup : public FileBrowserPopup {
public:
GenericLoadFilePopup(const QString &title);
@@ -175,7 +175,7 @@ protected:
//! The GenericSaveFilePopup is a simple, standard Toonz popup that
//! asks the user for a \a single file path to save something to.
-class GenericSaveFilePopup final : public FileBrowserPopup {
+class GenericSaveFilePopup : public FileBrowserPopup {
public:
GenericSaveFilePopup(const QString &title);
diff --git a/toonz/sources/toonz/outputsettingspopup.cpp b/toonz/sources/toonz/outputsettingspopup.cpp
index f664f4c..a025f1a 100644
--- a/toonz/sources/toonz/outputsettingspopup.cpp
+++ b/toonz/sources/toonz/outputsettingspopup.cpp
@@ -8,6 +8,7 @@
#include "tapp.h"
#include "camerasettingspopup.h"
#include "pane.h"
+#include "boardsettingspopup.h"
// TnzQt includes
#include "toonzqt/menubarcommand.h"
@@ -25,6 +26,7 @@
#include "toonz/preferences.h"
#include "toutputproperties.h"
#include "toonz/tcamera.h"
+#include "toonz/boardsettings.h"
// TnzBase includes
#include "trasterfx.h"
@@ -166,6 +168,11 @@ OutputSettingsPopup::OutputSettingsPopup(bool isPreview)
otherSettingsLabel = new QLabel(tr("Other Settings"), this);
otherSettingsFrame = new QFrame(this);
m_renderButton = new QPushButton(tr("Render"), this);
+
+ // Board
+ m_addBoard = new DVGui::CheckBox(tr("Add Clapperboard"), this);
+ m_boardSettingsBtn = new QPushButton(tr("Edit Clapperboard..."), this);
+
// Gamma
m_gammaFld = new DVGui::DoubleLineEdit();
// Dominant Field
@@ -457,39 +464,43 @@ OutputSettingsPopup::OutputSettingsPopup(bool isPreview)
otherSettingsLay->setHorizontalSpacing(5);
otherSettingsLay->setVerticalSpacing(10);
{
+ // clapperboard
+ otherSettingsLay->addWidget(m_addBoard, 0, 0);
+ otherSettingsLay->addWidget(m_boardSettingsBtn, 0, 2, 1, 2);
+
// Gamma
- otherSettingsLay->addWidget(new QLabel(tr("Gamma:"), this), 0, 0,
+ otherSettingsLay->addWidget(new QLabel(tr("Gamma:"), this), 1, 0,
Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_gammaFld, 0, 1, 1, 3);
+ otherSettingsLay->addWidget(m_gammaFld, 1, 1, 1, 3);
// Dominant Field
otherSettingsLay->addWidget(new QLabel(tr("Dominant Field:"), this),
- 1, 0, Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_dominantFieldOm, 1, 1, 1, 4);
+ 2, 0, Qt::AlignRight | Qt::AlignVCenter);
+ otherSettingsLay->addWidget(m_dominantFieldOm, 2, 1, 1, 4);
// Scene Settings' FPS
otherSettingsLay->addWidget(
- new QLabel(tr("Frame Rate (linked to Scene Settings):"), this), 2,
+ new QLabel(tr("Frame Rate (linked to Scene Settings):"), this), 3,
0, Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_frameRateFld, 2, 1, 1, 3);
+ otherSettingsLay->addWidget(m_frameRateFld, 3, 1, 1, 3);
// Strech
otherSettingsLay->addWidget(new QLabel(tr("Stretch from FPS:"), this),
- 3, 0, Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_stretchFromFld, 3, 1);
- otherSettingsLay->addWidget(new QLabel(tr(" To:"), this), 3, 2,
+ 4, 0, Qt::AlignRight | Qt::AlignVCenter);
+ otherSettingsLay->addWidget(m_stretchFromFld, 4, 1);
+ otherSettingsLay->addWidget(new QLabel(tr(" To:"), this), 4, 2,
Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_stretchToFld, 3, 3);
+ otherSettingsLay->addWidget(m_stretchToFld, 4, 3);
// new in V6.1
// Multimedia rendering enum
otherSettingsLay->addWidget(
- new QLabel(tr("Multiple Rendering:"), this), 4, 0,
+ new QLabel(tr("Multiple Rendering:"), this), 5, 0,
Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_multimediaOm, 4, 1, 1, 4);
+ otherSettingsLay->addWidget(m_multimediaOm, 5, 1, 1, 4);
- otherSettingsLay->addWidget(m_doStereoscopy, 5, 0);
- otherSettingsLay->addWidget(new QLabel(tr("Camera Shift:")), 5, 1,
+ otherSettingsLay->addWidget(m_doStereoscopy, 6, 0);
+ otherSettingsLay->addWidget(new QLabel(tr("Camera Shift:")), 6, 1,
Qt::AlignRight | Qt::AlignVCenter);
- otherSettingsLay->addWidget(m_stereoShift, 5, 2);
+ otherSettingsLay->addWidget(m_stereoShift, 6, 2);
- otherSettingsLay->addLayout(bottomGridLay, 6, 0, 4, 5);
+ otherSettingsLay->addLayout(bottomGridLay, 7, 0, 4, 5);
}
otherSettingsLay->setColumnStretch(0, 0);
otherSettingsLay->setColumnStretch(1, 0);
@@ -548,6 +559,12 @@ OutputSettingsPopup::OutputSettingsPopup(bool isPreview)
SLOT(onChannelWidthChanged(int)));
if (!isPreview) {
+ // clapperboard
+ ret = ret && connect(m_addBoard, SIGNAL(stateChanged(int)), this,
+ SLOT(onAddBoardChecked(int)));
+ ret = ret && connect(m_boardSettingsBtn, SIGNAL(clicked()), this,
+ SLOT(onBoardSettingsBtnClicked()));
+
ret = ret && connect(m_gammaFld, SIGNAL(editingFinished()),
SLOT(onGammaFldEditFinished()));
ret = ret && connect(m_dominantFieldOm, SIGNAL(currentIndexChanged(int)),
@@ -862,6 +879,18 @@ void OutputSettingsPopup::updateField() {
m_stretchToFld->setValue(renderSettings.m_timeStretchTo);
m_frameRateFld->setValue(prop->getFrameRate());
+
+ // clapperboard
+ BoardSettings *boardSettings = prop->getBoardSettings();
+ m_addBoard->setChecked(boardSettings->isActive());
+ // clapperboard is only available with movie formats
+ if (isMovieType(m_fileFormat->currentText().toStdString())) {
+ m_addBoard->setEnabled(true);
+ m_boardSettingsBtn->setEnabled(m_addBoard->isChecked());
+ } else {
+ m_addBoard->setEnabled(false);
+ m_boardSettingsBtn->setEnabled(false);
+ }
}
//-----------------------------------------------------------------------------
@@ -960,6 +989,15 @@ void OutputSettingsPopup::onFormatChanged(const QString &str) {
m_threadsComboOm->setDisabled(false);
m_threadsComboOm->setCurrentIndex(2);
}
+
+ // clapperboard is only available with movie formats
+ if (isMovieType(str.toStdString())) {
+ m_addBoard->setEnabled(true);
+ m_boardSettingsBtn->setEnabled(m_addBoard->isChecked());
+ } else {
+ m_addBoard->setEnabled(false);
+ m_boardSettingsBtn->setEnabled(false);
+ }
}
//-----------------------------------------------------------------------------
@@ -1574,6 +1612,21 @@ void OutputSettingsPopup::onFrameRateEditingFinished() {
//-----------------------------------------------------------------------------
+void OutputSettingsPopup::onAddBoardChecked(int state) {
+ BoardSettings *boardSettings = getProperties()->getBoardSettings();
+ boardSettings->setActive(state == Qt::Checked);
+
+ m_boardSettingsBtn->setEnabled(state == Qt::Checked);
+}
+
+void OutputSettingsPopup::onBoardSettingsBtnClicked() {
+ std::cout << "board settings button clicked" << std::endl;
+ BoardSettingsPopup popup(this);
+ popup.exec();
+}
+
+//-----------------------------------------------------------------------------
+
OpenPopupCommandHandler openOutputSettingsPopup(
MI_OutputSettings);
OpenPopupCommandHandler openPreviewSettingsPopup(
diff --git a/toonz/sources/toonz/outputsettingspopup.h b/toonz/sources/toonz/outputsettingspopup.h
index 21668d3..6f047bf 100644
--- a/toonz/sources/toonz/outputsettingspopup.h
+++ b/toonz/sources/toonz/outputsettingspopup.h
@@ -53,6 +53,10 @@ class OutputSettingsPopup : public DVGui::Dialog {
CameraSettingsPopup *m_cameraSettings;
QComboBox *m_presetCombo;
+ // clapperboard
+ DVGui::CheckBox *m_addBoard;
+ QPushButton *m_boardSettingsBtn;
+
bool m_isPreviewSettings;
void updatePresetComboItems();
@@ -97,6 +101,9 @@ protected slots:
void onCameraSettingsChanged();
/*-- Scene Settings のFPSを編集できるようにする --*/
void onFrameRateEditingFinished();
+ // clapperboard
+ void onAddBoardChecked(int state);
+ void onBoardSettingsBtnClicked();
};
class PreviewSettingsPopup final : public OutputSettingsPopup {
diff --git a/toonz/sources/toonz/toonz.qrc b/toonz/sources/toonz/toonz.qrc
index bc223d9..7a2dd82 100644
--- a/toonz/sources/toonz/toonz.qrc
+++ b/toonz/sources/toonz/toonz.qrc
@@ -468,5 +468,7 @@
Resources/shift_and_trace.svg
Resources/shift_and_trace_edit.svg
Resources/shift_and_trace_reset.svg
+ Resources/up.svg
+ Resources/down.svg
diff --git a/toonz/sources/toonzlib/CMakeLists.txt b/toonz/sources/toonzlib/CMakeLists.txt
index bc03c7c..7648d6c 100644
--- a/toonz/sources/toonzlib/CMakeLists.txt
+++ b/toonz/sources/toonzlib/CMakeLists.txt
@@ -156,6 +156,7 @@ set(MOC_HEADERS
texturemanager.h
imagebuilders.h
../include/orientation.h
+ ../include/toonz/boardsettings.h
)
set(HEADERS ${MOC_HEADERS})
@@ -317,6 +318,7 @@ set(SOURCES
plasticdeformerfx.cpp
txshmeshcolumn.cpp
textureutils.cpp
+ boardsettings.cpp
)
if(BUILD_TARGET_WIN)
diff --git a/toonz/sources/toonzlib/boardsettings.cpp b/toonz/sources/toonzlib/boardsettings.cpp
new file mode 100644
index 0000000..16d4628
--- /dev/null
+++ b/toonz/sources/toonzlib/boardsettings.cpp
@@ -0,0 +1,364 @@
+#include "toonz/boardsettings.h"
+
+// TnzLib includes
+#include "toonz/toonzscene.h"
+#include "toonz/tproject.h"
+#include "toonz/sceneproperties.h"
+#include "toonz/toonzfolders.h"
+#include "toutputproperties.h"
+#include "tsystem.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+QMap strs = {
+ {BoardItem::FreeText, L"FreeText"},
+ {BoardItem::ProjectName, L"ProjectName"},
+ {BoardItem::SceneName, L"SceneName"},
+ {BoardItem::Duration_Frame, L"Duration_Frame"},
+ {BoardItem::Duration_SecFrame, L"Duration_SecFrame"},
+ {BoardItem::Duration_HHMMSSFF, L"Duration_HHMMSSFF"},
+ {BoardItem::CurrentDate, L"CurrentDate"},
+ {BoardItem::CurrentDateTime, L"CurrentDateTime"},
+ {BoardItem::UserName, L"UserName"},
+ {BoardItem::ScenePath_Aliased, L"ScenePath_Aliased"},
+ {BoardItem::ScenePath_Full, L"ScenePath_Full"},
+ {BoardItem::MoviePath_Aliased, L"MoviePath_Aliased"},
+ {BoardItem::MoviePath_Full, L"MoviePath_Full"},
+ {BoardItem::Image, L"Image"}};
+
+std::wstring type2String(BoardItem::Type type) { return strs.value(type, L""); }
+BoardItem::Type string2Type(std::wstring str) {
+ return strs.key(str, BoardItem::TypeCount);
+}
+};
+
+BoardItem::BoardItem() {
+ m_name = "Item";
+ m_type = ProjectName;
+ m_rect = QRectF(0.1, 0.1, 0.8, 0.8);
+ m_maximumFontSize = 300;
+ m_color = Qt::black;
+}
+
+QString BoardItem::getContentText(ToonzScene *scene) {
+ switch (m_type) {
+ case FreeText:
+ return m_text;
+ break;
+ case ProjectName:
+ return scene->getProject()->getName().getQString();
+ break;
+ case SceneName:
+ return QString::fromStdWString(scene->getSceneName());
+ break;
+ case Duration_Frame:
+ return QString::number(scene->getFrameCount());
+ break;
+ case Duration_SecFrame: {
+ TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
+ int fps = (int)oprop->getFrameRate();
+ int frame = scene->getFrameCount();
+ return QString("%1 + %2").arg(QString::number(frame / fps),
+ QString::number(frame % fps));
+ } break;
+ case Duration_HHMMSSFF: {
+ TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
+ int fps = (int)oprop->getFrameRate();
+ int frame = scene->getFrameCount();
+ int hh = frame / (fps * 60 * 60);
+ frame -= hh * fps * 60 * 60;
+ int mm = frame / (fps * 60);
+ frame -= mm * fps * 60;
+ int ss = frame / fps;
+ int ff = frame % fps;
+ return QString::number(hh).rightJustified(2, '0') + ":" +
+ QString::number(mm).rightJustified(2, '0') + ":" +
+ QString::number(ss).rightJustified(2, '0') + ":" +
+ QString::number(ff).rightJustified(2, '0');
+ } break;
+ case CurrentDate:
+ return QDate::currentDate().toString(Qt::DefaultLocaleLongDate);
+ break;
+ case CurrentDateTime:
+ return QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate);
+ break;
+ case UserName:
+ return TSystem::getUserName();
+ break;
+ case ScenePath_Aliased:
+ return scene->codeFilePath(scene->getScenePath()).getQString();
+ break;
+ case ScenePath_Full:
+ return scene->decodeFilePath(scene->getScenePath()).getQString();
+ break;
+ case MoviePath_Aliased: {
+ TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
+ return scene->codeFilePath(oprop->getPath()).getQString();
+ } break;
+ case MoviePath_Full: {
+ TOutputProperties *oprop = scene->getProperties()->getOutputProperties();
+ return scene->decodeFilePath(oprop->getPath()).getQString();
+ } break;
+ }
+ return QString();
+}
+
+QRectF BoardItem::getItemRect(QSize imgSize) {
+ QSizeF imgSizeF(imgSize);
+ return QRectF(
+ imgSizeF.width() * m_rect.left(), imgSizeF.height() * m_rect.top(),
+ imgSizeF.width() * m_rect.width(), imgSizeF.height() * m_rect.height());
+}
+
+void BoardItem::drawItem(QPainter &p, QSize imgSize, int shrink,
+ ToonzScene *scene) {
+ QRectF itemRect = getItemRect(imgSize);
+
+ if (m_type == Image) {
+ if (m_imgPath.isEmpty()) return;
+ TFilePath decodedPath = scene->decodeFilePath(m_imgPath);
+ QImage img(decodedPath.getQString());
+ if (m_imgARMode == Qt::KeepAspectRatio) {
+ float ratio = std::min((float)itemRect.width() / (float)img.width(),
+ (float)itemRect.height() / (float)img.height());
+ QSizeF imgSize((float)img.width() * ratio, (float)img.height() * ratio);
+ QPointF imgTopLeft =
+ itemRect.topLeft() +
+ QPointF((itemRect.width() - imgSize.width()) * 0.5f,
+ (itemRect.height() - imgSize.height()) * 0.5f);
+
+ p.drawImage(QRectF(imgTopLeft, imgSize), img);
+ } else if (m_imgARMode == Qt::IgnoreAspectRatio)
+ p.drawImage(itemRect, img);
+ return;
+ }
+
+ QString contentText = getContentText(scene);
+
+ QFont tmpFont(m_font);
+ tmpFont.setPixelSize(100);
+ QFontMetricsF tmpFm(tmpFont);
+ QRectF tmpBounding =
+ tmpFm.boundingRect(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
+
+ float ratio = std::min(itemRect.width() / tmpBounding.width(),
+ itemRect.height() / tmpBounding.height());
+
+ // compute the font size which will just fit the item region
+ int fontSize = (int)(100.0f * ratio);
+ tmpFont.setPixelSize(fontSize);
+ tmpFm = QFontMetricsF(tmpFont);
+ tmpBounding =
+ tmpFm.boundingRect(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
+ bool isInRect;
+ if (itemRect.width() >= tmpBounding.width() &&
+ itemRect.height() >= tmpBounding.height())
+ isInRect = true;
+ else
+ isInRect = false;
+ while (1) {
+ fontSize += (isInRect) ? 1 : -1;
+ if (fontSize <= 0) // cannot draw
+ return;
+ tmpFont.setPixelSize(fontSize);
+ tmpFm = QFontMetricsF(tmpFont);
+ tmpBounding =
+ tmpFm.boundingRect(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
+
+ bool newIsInRect = (itemRect.width() >= tmpBounding.width() &&
+ itemRect.height() >= tmpBounding.height());
+ if (isInRect != newIsInRect) {
+ if (isInRect) fontSize--;
+ break;
+ }
+ }
+
+ //----
+ fontSize = std::min(fontSize, m_maximumFontSize / shrink);
+
+ QFont font(m_font);
+ font.setPixelSize(fontSize);
+
+ p.setFont(font);
+ p.setPen(m_color);
+
+ if (m_type == FreeText)
+ p.drawText(itemRect, Qt::AlignLeft | Qt::AlignTop, contentText);
+ else
+ p.drawText(itemRect, Qt::AlignCenter, contentText);
+}
+
+void BoardItem::saveData(TOStream &os) {
+ os.child("type") << type2String(m_type);
+ os.child("name") << m_name;
+ os.child("rect") << m_rect.x() << m_rect.y() << m_rect.width()
+ << m_rect.height();
+
+ if (m_type == Image) {
+ // if the path is in library folder, then save the realtive path
+ TFilePath libFp = ToonzFolder::getLibraryFolder();
+ if (libFp.isAncestorOf(m_imgPath))
+ os.child("imgPath") << 1 << m_imgPath - libFp;
+ else
+ os.child("imgPath") << 0 << m_imgPath;
+ os.child("imgARMode") << (int)m_imgARMode;
+ } else {
+ if (m_type == FreeText) os.child("text") << m_text;
+
+ os.child("maximumFontSize") << m_maximumFontSize;
+ os.child("color") << m_color.red() << m_color.green() << m_color.blue()
+ << m_color.alpha();
+ os.child("font") << m_font.family() << (int)(m_font.bold() ? 1 : 0)
+ << (int)(m_font.italic() ? 1 : 0);
+ }
+}
+
+void BoardItem::loadData(TIStream &is) {
+ std::string tagName;
+ while (is.matchTag(tagName)) {
+ if (tagName == "type") {
+ std::wstring typeStr;
+ is >> typeStr;
+ m_type = string2Type(typeStr);
+ } else if (tagName == "name") {
+ std::wstring str;
+ is >> str;
+ m_name = QString::fromStdWString(str);
+ } else if (tagName == "rect") {
+ double x, y, width, height;
+ is >> x >> y >> width >> height;
+ m_rect = QRectF(x, y, width, height);
+ } else if (tagName == "imgPath") {
+ int isInLibrary;
+ TFilePath fp;
+ is >> isInLibrary >> fp;
+ if (isInLibrary == 1)
+ m_imgPath = ToonzFolder::getLibraryFolder() + fp;
+ else
+ m_imgPath = fp;
+ } else if (tagName == "imgARMode") {
+ int mode;
+ is >> mode;
+ m_imgARMode = (Qt::AspectRatioMode)mode;
+ } else if (tagName == "text") {
+ std::wstring str;
+ is >> str;
+ m_text = QString::fromStdWString(str);
+ } else if (tagName == "maximumFontSize") {
+ is >> m_maximumFontSize;
+ } else if (tagName == "color") {
+ int r, g, b, a;
+ is >> r >> g >> b >> a;
+ m_color = QColor(r, g, b, a);
+ } else if (tagName == "font") {
+ QString family;
+ int isBold, isItalic;
+ is >> family >> isBold >> isItalic;
+ m_font.setFamily(family);
+ m_font.setBold(isBold == 1);
+ m_font.setItalic(isItalic == 1);
+ } else
+ throw TException("unexpected tag: " + tagName);
+ is.closeChild();
+ }
+}
+
+//======================================================================================
+
+BoardSettings::BoardSettings() {
+ // add one item as an example
+ m_items.push_back(BoardItem());
+}
+
+QImage BoardSettings::getBoardImage(TDimension &dim, int shrink,
+ ToonzScene *scene) {
+ QImage img(dim.lx, dim.ly, QImage::Format_ARGB32);
+
+ QPainter painter(&img);
+
+ painter.fillRect(img.rect(), Qt::white);
+
+ // draw each item
+ for (int i = m_items.size() - 1; i >= 0; i--)
+ m_items[i].drawItem(painter, img.rect().size(), shrink, scene);
+
+ painter.end();
+
+ return img;
+}
+
+TRaster32P BoardSettings::getBoardRaster(TDimension &dim, int shrink,
+ ToonzScene *scene) {
+ QImage img = getBoardImage(dim, shrink, scene);
+
+ // convert QImage to TRaster
+ TRaster32P boardRas(dim);
+ int img_y = img.height() - 1;
+ for (int j = 0; j < dim.ly; j++, img_y--) {
+ TPixel32 *pix = boardRas->pixels(j);
+ QRgb *img_p = (QRgb *)img.scanLine(img_y);
+ for (int i = 0; i < dim.lx; i++, pix++, img_p++) {
+ (*pix).r = (typename TPixel32::Channel)(qRed(*img_p));
+ (*pix).g = (typename TPixel32::Channel)(qGreen(*img_p));
+ (*pix).b = (typename TPixel32::Channel)(qBlue(*img_p));
+ (*pix).m = (typename TPixel32::Channel)(qAlpha(*img_p));
+ }
+ }
+ return boardRas;
+}
+
+void BoardSettings::addNewItem(int insertAt) {
+ m_items.insert(insertAt, BoardItem());
+}
+
+void BoardSettings::removeItem(int index) {
+ if (index < 0 || index >= m_items.size()) return;
+ m_items.removeAt(index);
+}
+
+void BoardSettings::swapItems(int i, int j) { m_items.swap(i, j); }
+
+void BoardSettings::saveData(TOStream &os, bool forPreset) {
+ if (!forPreset) os.child("active") << (int)((m_active) ? 1 : 0);
+ os.child("duration") << m_duration;
+ if (!m_items.isEmpty()) {
+ os.openChild("boardItems");
+ for (int i = 0; i < getItemCount(); i++) {
+ os.openChild("item");
+ m_items[i].saveData(os);
+ os.closeChild();
+ }
+ os.closeChild();
+ }
+}
+
+void BoardSettings::loadData(TIStream &is) {
+ std::string tagName;
+ while (is.matchTag(tagName)) {
+ if (tagName == "active") {
+ int val;
+ is >> val;
+ setActive(val == 1);
+ } else if (tagName == "duration") {
+ is >> m_duration;
+ } else if (tagName == "boardItems") {
+ m_items.clear();
+ while (is.matchTag(tagName)) {
+ if (tagName == "item") {
+ BoardItem item;
+ item.loadData(is);
+ m_items.append(item);
+ } else
+ throw TException("unexpected tag: " + tagName);
+ is.closeChild();
+ }
+ } else
+ throw TException("unexpected tag: " + tagName);
+ is.closeChild();
+ }
+}
\ No newline at end of file
diff --git a/toonz/sources/toonzlib/movierenderer.cpp b/toonz/sources/toonzlib/movierenderer.cpp
index c11a4c2..1d8b306 100644
--- a/toonz/sources/toonzlib/movierenderer.cpp
+++ b/toonz/sources/toonzlib/movierenderer.cpp
@@ -20,6 +20,7 @@
#include "toonz/trasterimageutils.h"
#include "toonz/levelupdater.h"
#include "toutputproperties.h"
+#include "toonz/boardsettings.h"
// tcg includes
#include "tcg/tcg_macros.h"
@@ -160,6 +161,8 @@ public:
std::pair saveFrame(double frame,
const std::pair &rasters);
std::string getRenderCacheId();
+
+ void addBoard();
};
//---------------------------------------------------------
@@ -374,7 +377,15 @@ std::pair MovieRenderer::Imp::saveFrame(
m_renderSettings.m_timeStretchFrom;
int fr = (stretchFac != 1) ? tround(frame * stretchFac) : int(frame);
- TFrameId fid(fr + 1);
+
+ int boardDuration = 0;
+ if (m_movieType) {
+ BoardSettings *bs =
+ m_scene->getProperties()->getOutputProperties()->getBoardSettings();
+ boardDuration = (bs->isActive()) ? bs->getDuration() : 0;
+ }
+
+ TFrameId fid(fr + 1 + boardDuration);
if (m_levelUpdaterA.get()) {
assert(m_levelUpdaterB.get() || !rasters.second);
@@ -474,6 +485,8 @@ void MovieRenderer::Imp::doRenderRasterCompleted(const RenderData &renderData) {
if (m_levelUpdaterB.get())
m_levelUpdaterB->getLevelWriter()->saveSoundTrack(m_st.getPointer());
}
+
+ addBoard();
}
// Output frames must be *cloned*, since the supplied rasters will be
@@ -732,6 +745,40 @@ void MovieRenderer::Imp::onRenderFinished(bool isCanceled) {
// eventually be deleted.
}
+//---------------------------------------------------------
+
+void MovieRenderer::Imp::addBoard() {
+ BoardSettings *boardSettings =
+ m_scene->getProperties()->getOutputProperties()->getBoardSettings();
+ if (!boardSettings->isActive()) return;
+ int duration = boardSettings->getDuration();
+ if (duration == 0) return;
+ // Get the image size
+ int shrinkX = m_renderSettings.m_shrinkX,
+ shrinkY = m_renderSettings.m_shrinkY;
+ TDimensionD cameraRes(double(m_frameSize.lx) / shrinkX,
+ double(m_frameSize.ly) / shrinkY);
+ TDimension cameraResI(cameraRes.lx, cameraRes.ly);
+
+ TRaster32P boardRas =
+ boardSettings->getBoardRaster(cameraResI, shrinkX, m_scene);
+
+ if (m_levelUpdaterA.get()) {
+ // Flush images
+ try {
+ TRasterImageP img(boardRas);
+ for (int f = 0; f < duration; f++) {
+ m_levelUpdaterA->update(TFrameId(f + 1), img);
+ if (m_levelUpdaterB.get())
+ m_levelUpdaterB->update(TFrameId(f + 1), img);
+ }
+ } catch (...) {
+ // Nothing. The images could not be saved for whatever reason.
+ // Failure is reported.
+ }
+ }
+}
+
//======================================================================================
//======================
diff --git a/toonz/sources/toonzlib/outputproperties.cpp b/toonz/sources/toonzlib/outputproperties.cpp
index 0ea61b2..2b90dde 100644
--- a/toonz/sources/toonzlib/outputproperties.cpp
+++ b/toonz/sources/toonzlib/outputproperties.cpp
@@ -2,6 +2,9 @@
#include "toutputproperties.h"
+// TnzLib includes
+#include "toonz/boardsettings.h"
+
// TnzBase includes
#include "trasterfx.h"
@@ -37,7 +40,8 @@ TOutputProperties::TOutputProperties()
, m_multimediaRendering(0)
, m_maxTileSizeIndex(0)
, m_threadIndex(2)
- , m_subcameraPreview(false) {
+ , m_subcameraPreview(false)
+ , m_boardSettings(new BoardSettings()) {
m_renderSettings = new TRenderSettings();
}
@@ -56,7 +60,8 @@ TOutputProperties::TOutputProperties(const TOutputProperties &src)
, m_multimediaRendering(src.m_multimediaRendering)
, m_maxTileSizeIndex(src.m_maxTileSizeIndex)
, m_threadIndex(src.m_threadIndex)
- , m_subcameraPreview(src.m_subcameraPreview) {
+ , m_subcameraPreview(src.m_subcameraPreview)
+ , m_boardSettings(new BoardSettings(*src.m_boardSettings)) {
std::map::iterator ft,
fEnd = m_formatProperties.end();
for (ft = m_formatProperties.begin(); ft != fEnd; ++ft) {
@@ -100,6 +105,9 @@ TOutputProperties &TOutputProperties::operator=(const TOutputProperties &src) {
for (sft = src.m_formatProperties.begin(); sft != sfEnd; ++sft)
m_formatProperties[sft->first] = sft->second->clone();
+ delete m_boardSettings;
+ m_boardSettings = new BoardSettings(*src.m_boardSettings);
+
return *this;
}
diff --git a/toonz/sources/toonzlib/sceneproperties.cpp b/toonz/sources/toonzlib/sceneproperties.cpp
index acf0699..7bc3c59 100644
--- a/toonz/sources/toonzlib/sceneproperties.cpp
+++ b/toonz/sources/toonzlib/sceneproperties.cpp
@@ -11,6 +11,7 @@
#include "toonz/txshleveltypes.h"
#include "toonz/preferences.h"
#include "cleanuppalette.h"
+#include "toonz/boardsettings.h"
// TnzBase includes
#include "toutputproperties.h"
@@ -293,6 +294,12 @@ void TSceneProperties::saveData(TOStream &os) const {
}
os.closeChild();
+ if (out.getBoardSettings() && out.getBoardSettings()->getDuration()) {
+ os.openChild("clapperboardSettings");
+ out.getBoardSettings()->saveData(os);
+ os.closeChild();
+ }
+
os.closeChild(); //
}
os.closeChild();
@@ -654,6 +661,9 @@ void TSceneProperties::loadData(TIStream &is, bool isLoadingProject) {
} else
throw TException("unexpected tag: " + tagName);
} // end while
+ } else if (tagName == "clapperboardSettings") {
+ assert(out.getBoardSettings());
+ out.getBoardSettings()->loadData(is);
} else {
throw TException("unexpected property tag: " + tagName);
}
diff --git a/toonz/sources/toonzqt/colorfield.cpp b/toonz/sources/toonzqt/colorfield.cpp
index 2d91a76..407aee8 100644
--- a/toonz/sources/toonzqt/colorfield.cpp
+++ b/toonz/sources/toonzqt/colorfield.cpp
@@ -203,6 +203,7 @@ ChannelField::ChannelField(QWidget *parent, const QString &string, int value,
m_channelSlider->setRange(0, maxValue);
m_channelSlider->setValue(value);
+ if (sliderWidth > 0) m_channelSlider->setFixedWidth(sliderWidth);
//----layout
QGridLayout *mainLayout = new QGridLayout(this);
@@ -339,7 +340,7 @@ ColorField::ColorFieldEditorController *ColorField::m_editorController = 0;
Return ColorField current color.
*/
ColorField::ColorField(QWidget *parent, bool isAlphaActive, TPixel32 color,
- int squareSize, bool useStyleEditor)
+ int squareSize, bool useStyleEditor, int sliderWidth)
: QWidget(parent)
, m_color(color)
, m_notifyEditingChange(true)
@@ -355,16 +356,20 @@ ColorField::ColorField(QWidget *parent, bool isAlphaActive, TPixel32 color,
m_colorSample = new StyleSample(this, squareSize, squareSize);
m_colorSample->setColor(m_color);
- m_redChannel = new ChannelField(this, tr("R:"), m_color.r);
+ m_redChannel =
+ new ChannelField(this, tr("R:"), m_color.r, 255, false, 13, sliderWidth);
connect(m_redChannel, SIGNAL(valueChanged(int, bool)),
SLOT(onRedChannelChanged(int, bool)));
- m_greenChannel = new ChannelField(this, tr("G:"), m_color.g);
+ m_greenChannel =
+ new ChannelField(this, tr("G:"), m_color.g, 255, false, 13, sliderWidth);
connect(m_greenChannel, SIGNAL(valueChanged(int, bool)),
SLOT(onGreenChannelChanged(int, bool)));
- m_blueChannel = new ChannelField(this, tr("B:"), m_color.b);
+ m_blueChannel =
+ new ChannelField(this, tr("B:"), m_color.b, 255, false, 13, sliderWidth);
connect(m_blueChannel, SIGNAL(valueChanged(int, bool)),
SLOT(onBlueChannelChanged(int, bool)));
- m_alphaChannel = new ChannelField(this, tr("A:"), m_color.m);
+ m_alphaChannel =
+ new ChannelField(this, tr("A:"), m_color.m, 255, false, 13, sliderWidth);
connect(m_alphaChannel, SIGNAL(valueChanged(int, bool)),
SLOT(onAlphaChannelChanged(int, bool)));