diff --git a/stuff/config/qss/Blue/Blue.qss b/stuff/config/qss/Blue/Blue.qss
index 1fa0b03..931b665 100644
--- a/stuff/config/qss/Blue/Blue.qss
+++ b/stuff/config/qss/Blue/Blue.qss
@@ -836,6 +836,7 @@ QTreeWidget {
----------------------------------------------------------------------------- */
.Button,
QPushButton,
+#CustomPanelButton,
.ComboBox,
#enableBlankFrameButton,
QComboBox {
@@ -850,6 +851,7 @@ QComboBox {
}
.Button:hover,
QPushButton:hover,
+#CustomPanelButton:hover,
#ViewerFpsSlider::sub-line:horizontal:hover,
#ViewerFpsSlider::add-line:horizontal:hover,
#enableBlankFrameButton:hover {
@@ -859,6 +861,7 @@ QPushButton:hover,
}
.Button:pressed,
QPushButton:pressed,
+#CustomPanelButton:pressed,
#ViewerFpsSlider::sub-line:horizontal:pressed,
#ViewerFpsSlider::add-line:horizontal:pressed,
#enableBlankFrameButton:pressed {
@@ -868,6 +871,7 @@ QPushButton:pressed,
}
.Button:checked,
QPushButton:checked,
+#CustomPanelButton:checked,
#enableBlankFrameButton:checked {
background-color: #262728;
border-color: #212223;
@@ -875,17 +879,20 @@ QPushButton:checked,
}
.Button:checked:hover,
QPushButton:checked:hover,
+#CustomPanelButton:checked:hover,
#enableBlankFrameButton:checked:hover {
background-color: #3a3b3d;
border-color: #2d2f30;
}
.Button:checked:hover:pressed,
QPushButton:checked:hover:pressed,
+#CustomPanelButton:checked:hover:pressed,
#enableBlankFrameButton:checked:hover:pressed {
background: #2d2f30;
}
.Button:disabled,
QPushButton:disabled,
+#CustomPanelButton:disabled,
.ComboBox:disabled,
#ViewerFpsSlider::sub-line:horizontal:disabled,
#ViewerFpsSlider::add-line:horizontal:disabled,
@@ -898,6 +905,9 @@ QComboBox:disabled {
#PushButton_NoPadding {
padding: 3;
}
+#CustomPanelButton {
+ padding: 0;
+}
/* -----------------------------------------------------------------------------
ComboBox
----------------------------------------------------------------------------- */
diff --git a/stuff/config/qss/Dark/Dark.qss b/stuff/config/qss/Dark/Dark.qss
index 6e8e752..330c260 100644
--- a/stuff/config/qss/Dark/Dark.qss
+++ b/stuff/config/qss/Dark/Dark.qss
@@ -836,6 +836,7 @@ QTreeWidget {
----------------------------------------------------------------------------- */
.Button,
QPushButton,
+#CustomPanelButton,
.ComboBox,
#enableBlankFrameButton,
QComboBox {
@@ -850,6 +851,7 @@ QComboBox {
}
.Button:hover,
QPushButton:hover,
+#CustomPanelButton:hover,
#ViewerFpsSlider::sub-line:horizontal:hover,
#ViewerFpsSlider::add-line:horizontal:hover,
#enableBlankFrameButton:hover {
@@ -859,6 +861,7 @@ QPushButton:hover,
}
.Button:pressed,
QPushButton:pressed,
+#CustomPanelButton:pressed,
#ViewerFpsSlider::sub-line:horizontal:pressed,
#ViewerFpsSlider::add-line:horizontal:pressed,
#enableBlankFrameButton:pressed {
@@ -868,6 +871,7 @@ QPushButton:pressed,
}
.Button:checked,
QPushButton:checked,
+#CustomPanelButton:checked,
#enableBlankFrameButton:checked {
background-color: #141414;
border-color: #0f0f0f;
@@ -875,17 +879,20 @@ QPushButton:checked,
}
.Button:checked:hover,
QPushButton:checked:hover,
+#CustomPanelButton:checked:hover,
#enableBlankFrameButton:checked:hover {
background-color: #282828;
border-color: #1c1c1c;
}
.Button:checked:hover:pressed,
QPushButton:checked:hover:pressed,
+#CustomPanelButton:checked:hover:pressed,
#enableBlankFrameButton:checked:hover:pressed {
background: #1c1c1c;
}
.Button:disabled,
QPushButton:disabled,
+#CustomPanelButton:disabled,
.ComboBox:disabled,
#ViewerFpsSlider::sub-line:horizontal:disabled,
#ViewerFpsSlider::add-line:horizontal:disabled,
@@ -898,6 +905,9 @@ QComboBox:disabled {
#PushButton_NoPadding {
padding: 3;
}
+#CustomPanelButton {
+ padding: 0;
+}
/* -----------------------------------------------------------------------------
ComboBox
----------------------------------------------------------------------------- */
diff --git a/stuff/config/qss/Default/Default.qss b/stuff/config/qss/Default/Default.qss
index 1155376..ff3a7ed 100644
--- a/stuff/config/qss/Default/Default.qss
+++ b/stuff/config/qss/Default/Default.qss
@@ -836,6 +836,7 @@ QTreeWidget {
----------------------------------------------------------------------------- */
.Button,
QPushButton,
+#CustomPanelButton,
.ComboBox,
#enableBlankFrameButton,
QComboBox {
@@ -850,6 +851,7 @@ QComboBox {
}
.Button:hover,
QPushButton:hover,
+#CustomPanelButton:hover,
#ViewerFpsSlider::sub-line:horizontal:hover,
#ViewerFpsSlider::add-line:horizontal:hover,
#enableBlankFrameButton:hover {
@@ -859,6 +861,7 @@ QPushButton:hover,
}
.Button:pressed,
QPushButton:pressed,
+#CustomPanelButton:pressed,
#ViewerFpsSlider::sub-line:horizontal:pressed,
#ViewerFpsSlider::add-line:horizontal:pressed,
#enableBlankFrameButton:pressed {
@@ -868,6 +871,7 @@ QPushButton:pressed,
}
.Button:checked,
QPushButton:checked,
+#CustomPanelButton:checked,
#enableBlankFrameButton:checked {
background-color: #2c2c2c;
border-color: #272727;
@@ -875,17 +879,20 @@ QPushButton:checked,
}
.Button:checked:hover,
QPushButton:checked:hover,
+#CustomPanelButton:checked:hover,
#enableBlankFrameButton:checked:hover {
background-color: #404040;
border-color: #343434;
}
.Button:checked:hover:pressed,
QPushButton:checked:hover:pressed,
+#CustomPanelButton:checked:hover:pressed,
#enableBlankFrameButton:checked:hover:pressed {
background: #343434;
}
.Button:disabled,
QPushButton:disabled,
+#CustomPanelButton:disabled,
.ComboBox:disabled,
#ViewerFpsSlider::sub-line:horizontal:disabled,
#ViewerFpsSlider::add-line:horizontal:disabled,
@@ -898,6 +905,9 @@ QComboBox:disabled {
#PushButton_NoPadding {
padding: 3;
}
+#CustomPanelButton {
+ padding: 0;
+}
/* -----------------------------------------------------------------------------
ComboBox
----------------------------------------------------------------------------- */
diff --git a/stuff/config/qss/Default/less/layouts/controls.less b/stuff/config/qss/Default/less/layouts/controls.less
index 3cbf2e5..037aaac 100644
--- a/stuff/config/qss/Default/less/layouts/controls.less
+++ b/stuff/config/qss/Default/less/layouts/controls.less
@@ -52,6 +52,11 @@ QPushButton {
padding: 3;
}
+#CustomPanelButton {
+ &:extend(.Button all);
+ padding: 0;
+}
+
/* -----------------------------------------------------------------------------
ComboBox
----------------------------------------------------------------------------- */
diff --git a/stuff/config/qss/Light/Light.qss b/stuff/config/qss/Light/Light.qss
index f0c5ff5..145ec26 100644
--- a/stuff/config/qss/Light/Light.qss
+++ b/stuff/config/qss/Light/Light.qss
@@ -836,6 +836,7 @@ QTreeWidget {
----------------------------------------------------------------------------- */
.Button,
QPushButton,
+#CustomPanelButton,
.ComboBox,
#enableBlankFrameButton,
QComboBox {
@@ -850,6 +851,7 @@ QComboBox {
}
.Button:hover,
QPushButton:hover,
+#CustomPanelButton:hover,
#ViewerFpsSlider::sub-line:horizontal:hover,
#ViewerFpsSlider::add-line:horizontal:hover,
#enableBlankFrameButton:hover {
@@ -859,6 +861,7 @@ QPushButton:hover,
}
.Button:pressed,
QPushButton:pressed,
+#CustomPanelButton:pressed,
#ViewerFpsSlider::sub-line:horizontal:pressed,
#ViewerFpsSlider::add-line:horizontal:pressed,
#enableBlankFrameButton:pressed {
@@ -868,6 +871,7 @@ QPushButton:pressed,
}
.Button:checked,
QPushButton:checked,
+#CustomPanelButton:checked,
#enableBlankFrameButton:checked {
background-color: #b5b5b5;
border-color: #8e8e8e;
@@ -875,17 +879,20 @@ QPushButton:checked,
}
.Button:checked:hover,
QPushButton:checked:hover,
+#CustomPanelButton:checked:hover,
#enableBlankFrameButton:checked:hover {
background-color: #c9c9c9;
border-color: #bcbcbc;
}
.Button:checked:hover:pressed,
QPushButton:checked:hover:pressed,
+#CustomPanelButton:checked:hover:pressed,
#enableBlankFrameButton:checked:hover:pressed {
background: #bcbcbc;
}
.Button:disabled,
QPushButton:disabled,
+#CustomPanelButton:disabled,
.ComboBox:disabled,
#ViewerFpsSlider::sub-line:horizontal:disabled,
#ViewerFpsSlider::add-line:horizontal:disabled,
@@ -898,6 +905,9 @@ QComboBox:disabled {
#PushButton_NoPadding {
padding: 3;
}
+#CustomPanelButton {
+ padding: 0;
+}
/* -----------------------------------------------------------------------------
ComboBox
----------------------------------------------------------------------------- */
diff --git a/stuff/config/qss/Neutral/Neutral.qss b/stuff/config/qss/Neutral/Neutral.qss
index 52d8cf8..55c7411 100644
--- a/stuff/config/qss/Neutral/Neutral.qss
+++ b/stuff/config/qss/Neutral/Neutral.qss
@@ -836,6 +836,7 @@ QTreeWidget {
----------------------------------------------------------------------------- */
.Button,
QPushButton,
+#CustomPanelButton,
.ComboBox,
#enableBlankFrameButton,
QComboBox {
@@ -850,6 +851,7 @@ QComboBox {
}
.Button:hover,
QPushButton:hover,
+#CustomPanelButton:hover,
#ViewerFpsSlider::sub-line:horizontal:hover,
#ViewerFpsSlider::add-line:horizontal:hover,
#enableBlankFrameButton:hover {
@@ -859,6 +861,7 @@ QPushButton:hover,
}
.Button:pressed,
QPushButton:pressed,
+#CustomPanelButton:pressed,
#ViewerFpsSlider::sub-line:horizontal:pressed,
#ViewerFpsSlider::add-line:horizontal:pressed,
#enableBlankFrameButton:pressed {
@@ -868,6 +871,7 @@ QPushButton:pressed,
}
.Button:checked,
QPushButton:checked,
+#CustomPanelButton:checked,
#enableBlankFrameButton:checked {
background-color: #5a5a5a;
border-color: #404040;
@@ -875,17 +879,20 @@ QPushButton:checked,
}
.Button:checked:hover,
QPushButton:checked:hover,
+#CustomPanelButton:checked:hover,
#enableBlankFrameButton:checked:hover {
background-color: #6e6e6e;
border-color: #616161;
}
.Button:checked:hover:pressed,
QPushButton:checked:hover:pressed,
+#CustomPanelButton:checked:hover:pressed,
#enableBlankFrameButton:checked:hover:pressed {
background: #616161;
}
.Button:disabled,
QPushButton:disabled,
+#CustomPanelButton:disabled,
.ComboBox:disabled,
#ViewerFpsSlider::sub-line:horizontal:disabled,
#ViewerFpsSlider::add-line:horizontal:disabled,
@@ -898,6 +905,9 @@ QComboBox:disabled {
#PushButton_NoPadding {
padding: 3;
}
+#CustomPanelButton {
+ padding: 0;
+}
/* -----------------------------------------------------------------------------
ComboBox
----------------------------------------------------------------------------- */
diff --git a/stuff/library/custom panel templates/drawing toolbox.ui b/stuff/library/custom panel templates/drawing toolbox.ui
new file mode 100644
index 0000000..488ec42
--- /dev/null
+++ b/stuff/library/custom panel templates/drawing toolbox.ui
@@ -0,0 +1,362 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 127
+ 516
+
+
+
+
+ 0
+ 0
+
+
+
+ Form
+
+
+
+ 15
+
+
+ QLayout::SetDefaultConstraint
+
+ -
+
+
+ 5
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn1
+
+
+
+ 40
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn2
+
+
+
+ 40
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn3
+
+
+
+ 40
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn4
+
+
+
+ 40
+ 40
+
+
+
+
+
+
+ -
+
+
+ 5
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn5
+
+
+
+ 40
+ 40
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn6
+
+
+
+ 40
+ 40
+
+
+
+
+
+
+ -
+
+
+ 5
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 105
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn7
+
+
+
+ 40
+ 40
+
+
+
+
+ -
+
+
+ 5
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 40
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn8
+
+
+
+ 30
+ 30
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 15
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 40
+ 90
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Btn9
+
+
+
+ 30
+ 30
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/stuff/library/custom panel templates/eight buttons.ui b/stuff/library/custom panel templates/eight buttons.ui
new file mode 100644
index 0000000..d005898
--- /dev/null
+++ b/stuff/library/custom panel templates/eight buttons.ui
@@ -0,0 +1,221 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 236
+ 124
+
+
+
+ Form
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn2
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn1
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn4
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn3
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn5
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn6
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn7
+
+
+
+ 20
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 50
+
+
+
+ Btn8
+
+
+
+ 20
+ 20
+
+
+
+
+
+
+
+
+
diff --git a/stuff/profiles/layouts/rooms/Default/menubar_template.xml b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
index 29f513d..c4654a2 100644
--- a/stuff/profiles/layouts/rooms/Default/menubar_template.xml
+++ b/stuff/profiles/layouts/rooms/Default/menubar_template.xml
@@ -322,9 +322,10 @@
MI_OpenStopMotionPanel
MI_StartupPopup
MI_OpenGuidedDrawingControls
+ MI_OpenCustomPanels
MI_MaximizePanel
- MI_FullScreenWindow
+ MI_FullScreenWindow
diff --git a/toonz/sources/CMakeLists.txt b/toonz/sources/CMakeLists.txt
index 7cf3d7c..585eae4 100644
--- a/toonz/sources/CMakeLists.txt
+++ b/toonz/sources/CMakeLists.txt
@@ -251,6 +251,7 @@ find_package(Qt5 REQUIRED
Multimedia
MultimediaWidgets
SerialPort
+ UiTools
)
set(QT_MINIMUM_VERSION 5.5.0)
diff --git a/toonz/sources/include/toonzqt/gutil.h b/toonz/sources/include/toonzqt/gutil.h
index 19cabb1..234f986 100644
--- a/toonz/sources/include/toonzqt/gutil.h
+++ b/toonz/sources/include/toonzqt/gutil.h
@@ -118,6 +118,8 @@ QPixmap DVAPI recolorPixmap(
: Qt::white);
QIcon DVAPI createQIcon(const char *iconSVGName, bool useFullOpacity = false,
bool isForMenuItem = false);
+void DVAPI addSpecifiedSizedImageToIcon(QIcon &icon, const char *iconSVGName,
+ QSize newSize);
QIcon DVAPI createQIconPNG(const char *iconPNGName);
QIcon DVAPI createQIconOnOffPNG(const char *iconPNGName, bool withOver = true);
QIcon DVAPI createTemporaryIconFromName(const char *commandName);
diff --git a/toonz/sources/include/toonzqt/menubarcommand.h b/toonz/sources/include/toonzqt/menubarcommand.h
index c70e892..a940214 100644
--- a/toonz/sources/include/toonzqt/menubarcommand.h
+++ b/toonz/sources/include/toonzqt/menubarcommand.h
@@ -110,13 +110,15 @@ class DVAPI CommandManager { // singleton
bool m_enabled;
QString m_onText,
m_offText; // for toggle commands. e.g. show/hide something
+ const char *m_iconSVGName;
Node(CommandId id)
: m_id(id)
, m_type(UndefinedCommandType)
, m_qaction(0)
, m_handler(0)
- , m_enabled(true) {}
+ , m_enabled(true)
+ , m_iconSVGName("") {}
~Node() {
if (m_handler) delete m_handler;
@@ -140,7 +142,7 @@ public:
void setHandler(CommandId id, CommandHandlerInterface *handler);
void define(CommandId id, CommandType type, std::string defaultShortcutString,
- QAction *action);
+ QAction *action, const char *iconSVGName = "");
QAction *createAction(const char *id, const char *name,
const char *defaultShortcut);
@@ -179,6 +181,8 @@ public:
const QString &offText);
std::string getIdFromAction(QAction *action);
+ const char *getIconSVGName(CommandId id);
+ void enlargeIcon(CommandId id, const QSize size);
// load user defined shortcuts
void loadShortcuts();
@@ -266,9 +270,11 @@ class DVAPI DVMenuAction final : public QMenu {
Q_OBJECT
int m_triggeredActionIndex;
+ bool m_isForRecentFiles;
public:
- DVMenuAction(const QString &text, QWidget *parent, QList actions);
+ DVMenuAction(const QString &text, QWidget *parent, QList actions,
+ bool isForRecentFiles = true);
void setActions(QList actions);
int getTriggeredActionIndex() { return m_triggeredActionIndex; }
diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt
index 06aaf1d..900e35a 100644
--- a/toonz/sources/toonz/CMakeLists.txt
+++ b/toonz/sources/toonz/CMakeLists.txt
@@ -124,6 +124,8 @@ set(MOC_HEADERS
expressionreferencemanager.h
tooloptionsshortcutinvoker.h
exportxsheetpdf.h
+ custompanelmanager.h
+ custompaneleditorpopup.h
)
if(PLATFORM EQUAL 64)
@@ -369,6 +371,8 @@ set(SOURCES
tooloptionsshortcutinvoker.cpp
tvpjson_io.cpp
exportxsheetpdf.cpp
+ custompanelmanager.cpp
+ custompaneleditorpopup.cpp
# Tracker file
dummyprocessor.cpp
metnum.cpp
@@ -488,7 +492,7 @@ if(BUILD_ENV_MSVC)
endif()
target_link_libraries(OpenToonz
Qt5::WinMain Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml
- Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia
+ Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::UiTools
${GL_LIB} ${GLUT_LIB}
${EXTRA_LIBS} strmiids
tnzcore tnzbase toonzlib colorfx tnzext image sound toonzqt tnztools tnzstdfx tfarm
@@ -512,7 +516,7 @@ elseif(BUILD_ENV_APPLE)
target_link_libraries(OpenToonz
Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml
- Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::MultimediaWidgets
+ Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::MultimediaWidgets Qt5::UiTools
${GL_LIB} ${GLUT_LIB}
${COCOA_LIB} ${EXTRA_LIBS} ${X64ONLY_LIBS} mousedragfilter
)
@@ -534,7 +538,7 @@ elseif(BUILD_ENV_UNIXLIKE)
target_link_libraries(
OpenToonz Qt5::Core Qt5::Gui Qt5::Network Qt5::OpenGL Qt5::Svg Qt5::Xml
- Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia
+ Qt5::Script Qt5::Widgets Qt5::PrintSupport Qt5::Multimedia Qt5::UiTools
${GL_LIB} ${GLUT_LIB} ${GLU_LIB}
${EXTRA_LIBS}
)
diff --git a/toonz/sources/toonz/commandbarpopup.cpp b/toonz/sources/toonz/commandbarpopup.cpp
index baea1c3..9e1d725 100644
--- a/toonz/sources/toonz/commandbarpopup.cpp
+++ b/toonz/sources/toonz/commandbarpopup.cpp
@@ -438,7 +438,6 @@ CommandBarPopup::CommandBarPopup(bool isXsheetToolbar)
noticeLabel->setFont(nf);
//--- layout
- QVBoxLayout* mainLay = new QVBoxLayout();
m_topLayout->setMargin(0);
m_topLayout->setSpacing(0);
{
diff --git a/toonz/sources/toonz/custompaneleditorpopup.cpp b/toonz/sources/toonz/custompaneleditorpopup.cpp
new file mode 100644
index 0000000..858f4e0
--- /dev/null
+++ b/toonz/sources/toonz/custompaneleditorpopup.cpp
@@ -0,0 +1,888 @@
+#include "custompaneleditorpopup.h"
+
+// Tnz includes
+#include "tapp.h"
+#include "menubarcommandids.h"
+#include "shortcutpopup.h"
+#include "custompanelmanager.h"
+
+// TnzQt includes
+#include "toonzqt/gutil.h"
+
+// ToonzLib
+#include "toonz/toonzfolders.h"
+
+// ToonzCore
+#include "tsystem.h"
+
+// Qt includes
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+const TFilePath CustomPanelTemplateFolderName("custom panel templates");
+const TFilePath customPaneTemplateFolderPath() {
+ return ToonzFolder::getLibraryFolder() + CustomPanelTemplateFolderName;
+}
+const TFilePath CustomPanelFolderName("custompanels");
+const TFilePath customPaneFolderPath() {
+ return ToonzFolder::getMyModuleDir() + CustomPanelFolderName;
+}
+
+QPoint relativePos(QWidget* child, QWidget* refParent) {
+ if (child->parentWidget() == refParent)
+ return child->pos();
+ else
+ return child->pos() + relativePos(child->parentWidget(), refParent);
+}
+
+} // namespace
+
+//=============================================================================
+// CustomPanelUIField
+//-----------------------------------------------------------------------------
+
+CustomPanelUIField::CustomPanelUIField(const int objId,
+ const QString objectName,
+ QWidget* parent, bool isFirst)
+ : QLabel(tr("Drag and set command"), parent), m_id(objId) {
+ QFont fnt = font();
+ fnt.setPointSize(12);
+ setFont(fnt);
+ setStyleSheet("background-color: rgb(255, 255, 128); color: black;");
+ setAcceptDrops(true);
+
+ // objName may be a commandId
+ if (setCommand(objectName)) return;
+
+ if (objectName.startsWith("HScroller") ||
+ objectName.startsWith("VScroller")) {
+ QStringList ids = objectName.split("__");
+ if (ids.size() == 3) {
+ setCommand((isFirst) ? ids[1] : ids[2]);
+ }
+ }
+}
+
+bool CustomPanelUIField::setCommand(QString commandId) {
+ if (m_commandId == commandId) return false;
+ if (commandId.isEmpty()) {
+ m_commandId = commandId;
+ setText(tr("Drag and set command"));
+ setStyleSheet("background-color: rgb(255, 255, 128); color: black;");
+ return true;
+ }
+
+ QAction* action =
+ CommandManager::instance()->getAction(commandId.toStdString().c_str());
+ if (!action) return false;
+
+ m_commandId = commandId;
+ QString tempText = action->text();
+ // removing accelerator key indicator
+ tempText = tempText.replace(QRegExp("&([^& ])"), "\\1");
+ // removing doubled &s
+ tempText = tempText.replace("&&", "&");
+ setText(tempText);
+ setStyleSheet("background-color: rgb(230, 230, 230); color: black;");
+ return true;
+}
+
+void CustomPanelUIField::enterEvent(QEvent* event) { emit highlight(m_id); }
+void CustomPanelUIField::leaveEvent(QEvent* event) { emit highlight(-1); }
+void CustomPanelUIField::dragEnterEvent(QDragEnterEvent* event) {
+ QString txt = event->mimeData()->text();
+ if (CommandManager::instance()->getAction(txt.toStdString().c_str())) {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ }
+ emit highlight(m_id);
+}
+
+void CustomPanelUIField::dragLeaveEvent(QDragLeaveEvent* event) {
+ emit highlight(-1);
+}
+
+void CustomPanelUIField::dropEvent(QDropEvent* event) {
+ QString oldCommandId = m_commandId;
+ QString commandId = event->mimeData()->text();
+ if (setCommand(commandId)) {
+ // if dragged from the command tree, command can be duplicated
+ if (event->dropAction() == Qt::CopyAction)
+ emit commandChanged(QString(), QString());
+ else
+ emit commandChanged(oldCommandId, m_commandId);
+ }
+}
+
+void CustomPanelUIField::mousePressEvent(QMouseEvent* event) {
+ if (m_commandId.isEmpty()) return;
+ QMimeData* mimeData = new QMimeData;
+ mimeData->setText(m_commandId);
+
+ QString dragPixmapTxt = text();
+ QFontMetrics fm(QApplication::font());
+ QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size());
+ QPainter painter(&pix);
+ painter.fillRect(pix.rect(), Qt::white);
+ painter.setPen(Qt::black);
+ painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt);
+
+ QDrag* drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->setPixmap(pix);
+
+ drag->exec(Qt::MoveAction);
+}
+
+//=============================================================================
+// UiPreviewWidget
+//-----------------------------------------------------------------------------
+
+UiPreviewWidget::UiPreviewWidget(QPixmap uiPixmap, QList& uiEntries,
+ QWidget* parent)
+ : QWidget(parent), m_highlightUiId(-1), m_uiPixmap(uiPixmap) {
+ for (auto entry : uiEntries) m_rectTable.append(entry.rect);
+ setFixedSize(m_uiPixmap.size());
+ setAcceptDrops(true);
+ setMouseTracking(true);
+}
+
+void UiPreviewWidget::onViewerResize(QSize size) {
+ if (m_uiPixmap.isNull()) return;
+ setFixedSize(std::max(size.width(), m_uiPixmap.width()),
+ std::max(size.height(), m_uiPixmap.height()));
+}
+
+void UiPreviewWidget::paintEvent(QPaintEvent*) {
+ QPainter p(this);
+ p.translate((width() - m_uiPixmap.width()) / 2,
+ (height() - m_uiPixmap.height()) / 2);
+ p.drawPixmap(0, 0, m_uiPixmap);
+
+ for (int id = 0; id < m_rectTable.count(); id++) {
+ QRect uiRect = m_rectTable.at(id);
+ if (id == m_highlightUiId)
+ p.setBrush(QColor(0, 255, 255, 64));
+ else
+ p.setBrush(Qt::NoBrush);
+ p.setPen(Qt::cyan);
+ p.drawRect(uiRect);
+ }
+}
+
+void UiPreviewWidget::highlightUi(const int objId) {
+ m_highlightUiId = objId;
+ update();
+}
+
+void UiPreviewWidget::mousePressEvent(QMouseEvent* event) {
+ if (m_highlightUiId >= 0) emit clicked(m_highlightUiId);
+}
+
+void UiPreviewWidget::onMove(const QPoint pos) {
+ QPoint offset((width() - m_uiPixmap.width()) / 2,
+ (height() - m_uiPixmap.height()) / 2);
+
+ for (int id = 0; id < m_rectTable.size(); id++) {
+ if (m_rectTable.at(id).contains(pos - offset)) {
+ highlightUi(id);
+ return;
+ }
+ }
+ highlightUi(-1);
+}
+
+void UiPreviewWidget::mouseMoveEvent(QMouseEvent* event) {
+ onMove(event->pos());
+}
+
+void UiPreviewWidget::dragEnterEvent(QDragEnterEvent* event) {
+ QString txt = event->mimeData()->text();
+ if (CommandManager::instance()->getAction(txt.toStdString().c_str())) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+}
+
+void UiPreviewWidget::dragMoveEvent(QDragMoveEvent* event) {
+ onMove(event->pos());
+
+ if (m_highlightUiId < 0) {
+ event->ignore();
+ return;
+ }
+
+ QString txt = event->mimeData()->text();
+ if (CommandManager::instance()->getAction(txt.toStdString().c_str())) {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+}
+
+void UiPreviewWidget::dropEvent(QDropEvent* event) {
+ QString commandId = event->mimeData()->text();
+ bool isDraggdFromTree = (event->dropAction() == Qt::CopyAction);
+
+ emit dropped(m_highlightUiId, commandId, isDraggdFromTree);
+}
+
+//-----------------------------------------------------------------------------
+
+UiPreviewArea::UiPreviewArea(QWidget* parent) : QScrollArea(parent) {}
+
+void UiPreviewArea::resizeEvent(QResizeEvent* event) {
+ if (widget()) {
+ UiPreviewWidget* previewWidget = dynamic_cast(widget());
+ if (previewWidget) {
+ previewWidget->onViewerResize(event->size());
+ }
+ }
+
+ QScrollArea::resizeEvent(event);
+}
+
+//=============================================================================
+// CommandBarCommandItem
+//-----------------------------------------------------------------------------
+
+class CustomPanelCommandItem final : public QTreeWidgetItem {
+ QAction* m_action;
+
+public:
+ CustomPanelCommandItem(QTreeWidgetItem* parent, QAction* action)
+ : QTreeWidgetItem(parent, UserType), m_action(action) {
+ setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled |
+ Qt::ItemNeverHasChildren);
+ QString tempText = m_action->text();
+ // removing accelerator key indicator
+ tempText = tempText.replace(QRegExp("&([^& ])"), "\\1");
+ // removing doubled &s
+ tempText = tempText.replace("&&", "&");
+ setText(0, tempText);
+ setToolTip(0, QObject::tr("[Drag] to move position"));
+ }
+ QAction* getAction() const { return m_action; }
+};
+
+//=============================================================================
+// CustomPanelCommandListTree
+//-----------------------------------------------------------------------------
+
+void CustomPanelCommandListTree::addFolder(const QString& title,
+ int commandType,
+ QTreeWidgetItem* parentFolder) {
+ QTreeWidgetItem* folder;
+ if (!parentFolder)
+ folder = new QTreeWidgetItem(this);
+ else
+ folder = new QTreeWidgetItem(parentFolder);
+ assert(folder);
+ folder->setText(0, title);
+ folder->setIcon(0, invisibleRootItem()->icon(0));
+
+ std::vector actions;
+ CommandManager::instance()->getActions((CommandType)commandType, actions);
+ for (int i = 0; i < (int)actions.size(); i++) {
+ CustomPanelCommandItem* item =
+ new CustomPanelCommandItem(folder, actions[i]);
+ item->setToolTip(
+ 0, QObject::tr(
+ "[Drag&Drop] to set command to control in the custom panel"));
+ }
+}
+
+CustomPanelCommandListTree::CustomPanelCommandListTree(QWidget* parent)
+ : QTreeWidget(parent) {
+ setObjectName("SolidLineFrame");
+ setAlternatingRowColors(true);
+ setDragEnabled(true);
+ setDragDropMode(QAbstractItemView::DragOnly);
+ setColumnCount(1);
+ setIconSize(QSize(21, 18));
+ header()->close();
+
+ QIcon menuFolderIcon(createQIcon("folder_project", true));
+ invisibleRootItem()->setIcon(0, menuFolderIcon);
+
+ QTreeWidgetItem* menuCommandFolder = new QTreeWidgetItem(this);
+ menuCommandFolder->setFlags(Qt::ItemIsEnabled);
+ menuCommandFolder->setText(0, ShortcutTree::tr("Menu Commands"));
+ menuCommandFolder->setExpanded(true);
+ menuCommandFolder->setIcon(0, invisibleRootItem()->icon(0));
+
+ addFolder(ShortcutTree::tr("File"), MenuFileCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("Edit"), MenuEditCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("Scan & Cleanup"), MenuScanCleanupCommandType,
+ menuCommandFolder);
+ addFolder(ShortcutTree::tr("Level"), MenuLevelCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("Xsheet"), MenuXsheetCommandType,
+ menuCommandFolder);
+ addFolder(ShortcutTree::tr("Cells"), MenuCellsCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("Play"), MenuPlayCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("Render"), MenuRenderCommandType,
+ menuCommandFolder);
+ addFolder(ShortcutTree::tr("View"), MenuViewCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("Windows"), MenuWindowsCommandType,
+ menuCommandFolder);
+ addFolder(ShortcutTree::tr("Help"), MenuHelpCommandType, menuCommandFolder);
+
+ addFolder(ShortcutTree::tr("Tools"), ToolCommandType);
+ addFolder(ShortcutTree::tr("Fill"), FillCommandType);
+ addFolder(ShortcutTree::tr("Right-click Menu Commands"),
+ RightClickMenuCommandType);
+ QTreeWidgetItem* rcmSubFolder =
+ invisibleRootItem()->child(invisibleRootItem()->childCount() - 1);
+ addFolder(ShortcutTree::tr("Cell Mark"), CellMarkCommandType, rcmSubFolder);
+ addFolder(ShortcutTree::tr("Tool Modifiers"), ToolModifierCommandType);
+ addFolder(ShortcutTree::tr("Visualization"), VisualizationButtonCommandType);
+ addFolder(ShortcutTree::tr("Misc"), MiscCommandType);
+ addFolder(ShortcutTree::tr("RGBA Channels"), RGBACommandType);
+
+ sortItems(0, Qt::AscendingOrder);
+}
+
+void CustomPanelCommandListTree::mousePressEvent(QMouseEvent* event) {
+ setCurrentItem(itemAt(event->pos()));
+ CustomPanelCommandItem* commandItem =
+ dynamic_cast(itemAt(event->pos()));
+
+ if (commandItem) {
+ std::string dragStr;
+ QString dragPixmapTxt;
+ dragStr =
+ CommandManager::instance()->getIdFromAction(commandItem->getAction());
+ dragPixmapTxt = commandItem->getAction()->text();
+ dragPixmapTxt.remove("&");
+
+ QMimeData* mimeData = new QMimeData;
+ mimeData->setText(QString::fromStdString(dragStr));
+
+ QFontMetrics fm(QApplication::font());
+ QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size());
+ QPainter painter(&pix);
+ painter.fillRect(pix.rect(), Qt::white);
+ painter.setPen(Qt::black);
+ painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt);
+
+ QDrag* drag = new QDrag(this);
+ drag->setMimeData(mimeData);
+ drag->setPixmap(pix);
+
+ drag->exec(Qt::CopyAction);
+ }
+
+ QTreeWidget::mousePressEvent(event);
+}
+
+//=============================================================================
+// CustomPanelEditorPopup
+//-----------------------------------------------------------------------------
+
+bool CustomPanelEditorPopup::loadTemplateList() {
+ // library / custom panel templates�̒����`�F�b�N
+ TFilePath customPanelTemplateFolder = customPaneTemplateFolderPath();
+ if (!TSystem::doesExistFileOrLevel(customPanelTemplateFolder)) {
+ DVGui::warning(tr("Template folder %1 not found.")
+ .arg(customPanelTemplateFolder.getQString()));
+ return false;
+ }
+ TFilePathSet fileList =
+ TSystem::readDirectory(customPanelTemplateFolder, false, true, false);
+ if (fileList.empty()) {
+ DVGui::warning(tr("Template files not found."));
+ return false;
+ }
+ m_templateCombo->clear();
+ QList fileNames;
+ for (auto file : fileList) {
+ // accept only .ui files
+ if (file.getType() != "ui") continue;
+ m_templateCombo->addItem(QString::fromStdString(file.getName()),
+ file.getQString());
+ }
+
+ int templateCount = m_templateCombo->count();
+
+ if (templateCount == 0) {
+ DVGui::warning(tr("Template files not found."));
+ return false;
+ }
+
+ // then, insert user custom panel
+ TFilePath customPanelsFolder = customPaneFolderPath();
+ if (TSystem::doesExistFileOrLevel(customPanelsFolder)) {
+ TFilePathSet fileList2 =
+ TSystem::readDirectory(customPanelsFolder, false, true, false);
+ for (auto file : fileList2) {
+ // accept only .ui files
+ if (file.getType() != "ui") continue;
+ m_templateCombo->addItem(
+ tr("%1 (Edit)").arg(QString::fromStdString(file.getName())),
+ file.getQString());
+ }
+ }
+
+ if (m_templateCombo->count() > templateCount)
+ m_templateCombo->insertSeparator(templateCount);
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+
+void CustomPanelEditorPopup::createFields() {
+ QList widgets = m_UiFieldsContainer->findChildren();
+ foreach (QWidget* child, widgets) {
+ delete child;
+ }
+
+ QGridLayout* gridLay;
+ if (m_UiFieldsContainer->layout())
+ gridLay = dynamic_cast(m_UiFieldsContainer->layout());
+ else {
+ gridLay = new QGridLayout();
+ gridLay->setMargin(15);
+ gridLay->setHorizontalSpacing(10);
+ gridLay->setVerticalSpacing(15);
+ gridLay->setColumnStretch(0, 0);
+ gridLay->setColumnStretch(1, 1);
+ gridLay->setColumnStretch(2, 1);
+ m_UiFieldsContainer->setLayout(gridLay);
+ }
+
+ // for each entry
+ int uiCounts[2] = {0, 0};
+ QString labelStr[2] = {tr("Button"), tr("Scroller")};
+ int id = 0;
+ int row = 0;
+ for (auto& entry : m_uiEntries) {
+ if (entry.type == Button || entry.type == Scroller_Back) {
+ entry.field = new CustomPanelUIField(id, entry.objectName, this);
+ QString label =
+ labelStr[(int)entry.type] + QString::number(uiCounts[entry.type] + 1);
+ uiCounts[entry.type]++;
+ gridLay->addWidget(new QLabel(label, this), row, 0, Qt::AlignRight);
+ gridLay->addWidget(entry.field, row, 1);
+ } else { // Scroller_Fore
+ entry.field = new CustomPanelUIField(id, entry.objectName, this, false);
+ gridLay->addWidget(entry.field, row, 2);
+ }
+ if (entry.type == Button || entry.type == Scroller_Fore) row++;
+
+ connect(entry.field, SIGNAL(highlight(int)), this, SLOT(onHighlight(int)));
+ connect(entry.field, SIGNAL(commandChanged(QString, QString)), this,
+ SLOT(onCommandChanged(QString, QString)));
+ id++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+// create entries from a widget just loaded from .ui file
+void CustomPanelEditorPopup::buildEntries(QWidget* customWidget) {
+ m_uiEntries.clear();
+
+ // this will define child widgets positions
+ customWidget->grab();
+
+ QList allWidgets = customWidget->findChildren();
+ std::sort(allWidgets.begin(), allWidgets.end(),
+ [](const QWidget* a, const QWidget* b) -> bool {
+ return (a->pos().y() == b->pos().y())
+ ? (a->pos().x() < b->pos().x())
+ : (a->pos().y() < b->pos().y());
+ });
+
+ for (auto widget : allWidgets) {
+ if (widget->objectName().isEmpty()) continue;
+ if (widget->layout() != nullptr) continue;
+ UiEntry entry;
+
+ entry.type = (dynamic_cast(widget))
+ ? (UiType)Button
+ : (UiType)Scroller_Back;
+
+ entry.objectName = widget->objectName();
+ if (entry.type == Button)
+ entry.rect = QRect(relativePos(widget, customWidget), widget->size());
+ else { // Sroller_Back
+ entry.orientation =
+ (widget->width() > widget->height()) ? Qt::Horizontal : Qt::Vertical;
+ if (entry.orientation == Qt::Horizontal)
+ entry.rect = QRect(relativePos(widget, customWidget),
+ QSize(widget->width() / 2, widget->height()));
+ else
+ entry.rect = QRect(relativePos(widget, customWidget),
+ QSize(widget->width(), widget->height() / 2));
+ }
+ m_uiEntries.append(entry);
+
+ // register Scroller_Fore
+ if (entry.type == Scroller_Back) {
+ entry.type = (UiType)Scroller_Fore;
+ if (entry.orientation == Qt::Horizontal)
+ entry.rect.translate(widget->width() / 2, 0);
+ else
+ entry.rect.translate(0, widget->height() / 2);
+
+ m_uiEntries.append(entry);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+// update widget using the current entries
+void CustomPanelEditorPopup::updateControls(QWidget* customWidget) {
+ QList allWidgets = customWidget->findChildren();
+ for (auto widget : allWidgets) {
+ QList entryIds = entryIdByObjName(widget->objectName());
+ if (entryIds.isEmpty()) continue;
+ UiEntry entry = m_uiEntries.at(entryIds.at(0));
+ if (entry.type == Button) {
+ QString commandId = entry.field->commandId();
+ QAction* action = CommandManager::instance()->getAction(
+ commandId.toStdString().c_str());
+ if (!action) continue;
+ QAbstractButton* button = dynamic_cast(widget);
+ QToolButton* tb = dynamic_cast(widget);
+ CommandManager::instance()->enlargeIcon(commandId.toStdString().c_str(),
+ button->iconSize());
+ if (tb)
+ tb->setDefaultAction(action);
+ else if (button)
+ button->setIcon(action->icon());
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+void CustomPanelEditorPopup::onTemplateSwitched() {
+ QUiLoader loader;
+ QString fp = m_templateCombo->currentData().toString();
+ QFile file(fp);
+
+ file.open(QFile::ReadOnly);
+ QWidget* customWidget = loader.load(&file, 0);
+ file.close();
+
+ // create entries from a widget just loaded from .ui file
+ buildEntries(customWidget);
+
+ // objectName of each widget will be overwritten in this function
+ CustomPanelManager::instance()->initializeControl(customWidget);
+
+ // create UI fields
+ createFields();
+
+ UiPreviewWidget* previewWidget =
+ new UiPreviewWidget(customWidget->grab(), m_uiEntries, this);
+
+ if (m_previewArea->widget()) {
+ UiPreviewWidget* oldPreview =
+ dynamic_cast(m_previewArea->widget());
+ if (oldPreview) {
+ disconnect(oldPreview, SIGNAL(clicked(int)), this,
+ SLOT(onPreviewClicked(int)));
+ disconnect(oldPreview, SIGNAL(dropped(int, QString, bool)), this,
+ SLOT(onPreviewDropped(int, QString, bool)));
+ }
+ delete m_previewArea->widget();
+ }
+
+ connect(previewWidget, SIGNAL(clicked(int)), this,
+ SLOT(onPreviewClicked(int)));
+ connect(previewWidget, SIGNAL(dropped(int, QString, bool)), this,
+ SLOT(onPreviewDropped(int, QString, bool)));
+
+ m_previewArea->setWidget(previewWidget);
+ previewWidget->onViewerResize(m_previewArea->size());
+
+ delete customWidget;
+
+ if (customPaneFolderPath().isAncestorOf(TFilePath(fp)))
+ m_panelNameEdit->setText(m_templateCombo->currentText().chopped(7));
+
+ updateGeometry();
+}
+//-----------------------------------------------------------------------------
+
+void CustomPanelEditorPopup::onHighlight(int id) {
+ if (!m_previewArea->widget()) return;
+ UiPreviewWidget* previewWidget =
+ dynamic_cast(m_previewArea->widget());
+ if (!previewWidget) return;
+ previewWidget->highlightUi(id);
+}
+
+//-----------------------------------------------------------------------------
+// set pixmap of updated ui to the preview
+void CustomPanelEditorPopup::onCommandChanged(QString oldCmdId,
+ QString newCmdId) {
+ // If the command is dragged from another field, then swap the commands.
+ if (!newCmdId.isEmpty()) {
+ CustomPanelUIField* senderField =
+ dynamic_cast(sender());
+ for (auto entry : m_uiEntries) {
+ if (!entry.field || entry.field == senderField) continue;
+ if (entry.field->commandId() == newCmdId) {
+ entry.field->setCommand(oldCmdId);
+ break;
+ }
+ }
+ }
+
+ QString fp = m_templateCombo->currentData().toString();
+ QFile tmplFile(fp);
+ tmplFile.open(QFile::ReadOnly);
+
+ QUiLoader loader;
+ QWidget* customWidget = loader.load(&tmplFile, 0);
+ tmplFile.close();
+
+ updateControls(customWidget);
+
+ UiPreviewWidget* previewWidget =
+ dynamic_cast(m_previewArea->widget());
+ previewWidget->setUiPixmap(customWidget->grab());
+ delete customWidget;
+}
+
+void CustomPanelEditorPopup::onPreviewClicked(int id) {
+ CustomPanelUIField* field = m_uiEntries.at(id).field;
+ if (!field) return;
+ QString commandId = field->commandId();
+ if (commandId.isEmpty()) return;
+ QMimeData* mimeData = new QMimeData;
+ mimeData->setText(commandId);
+
+ QString dragPixmapTxt = field->text();
+ QFontMetrics fm(QApplication::font());
+ QPixmap pix(fm.boundingRect(dragPixmapTxt).adjusted(-2, -2, 2, 2).size());
+ QPainter painter(&pix);
+ painter.fillRect(pix.rect(), Qt::white);
+ painter.setPen(Qt::black);
+ painter.drawText(pix.rect(), Qt::AlignCenter, dragPixmapTxt);
+
+ QDrag* drag = new QDrag(sender());
+ drag->setMimeData(mimeData);
+ drag->setPixmap(pix);
+
+ drag->exec(Qt::MoveAction);
+}
+
+void CustomPanelEditorPopup::onPreviewDropped(int id, QString cmdId,
+ bool fromTree) {
+ CustomPanelUIField* field = m_uiEntries.at(id).field;
+ if (!field) return;
+
+ QString oldCommandId = field->commandId();
+ if (field->setCommand(cmdId)) {
+ if (fromTree)
+ field->notifyCommandChanged(QString(), QString());
+ else
+ field->notifyCommandChanged(oldCommandId, cmdId);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+QList CustomPanelEditorPopup::entryIdByObjName(const QString objName) {
+ QList ret;
+ for (int i = 0; i < m_uiEntries.size(); i++) {
+ if (m_uiEntries[i].objectName == objName) ret.append(i);
+ }
+ return ret;
+}
+
+void CustomPanelEditorPopup::replaceObjectNames(QDomElement& element) {
+ QDomNode n = element.firstChild();
+ while (!n.isNull()) {
+ if (n.isElement()) {
+ QDomElement e = n.toElement();
+ //�������g���`�F�b�N
+ if (e.tagName() == "widget" && e.hasAttribute("name")) {
+ QString objName = e.attribute("name");
+ QList entryIds = entryIdByObjName(objName);
+ if (!entryIds.isEmpty()) {
+ UiEntry entry = m_uiEntries.at(entryIds[0]);
+
+ if (entry.type == Button)
+ e.setAttribute("name", entry.field->commandId());
+ else { // Scroller
+ UiEntry entryFore = m_uiEntries.at(entryIds[1]);
+ QStringList newNameList;
+ newNameList.append((entry.orientation == Qt::Horizontal)
+ ? "HScroller"
+ : "VScroller");
+ newNameList.append(entry.field->commandId());
+ newNameList.append(entryFore.field->commandId());
+ e.setAttribute("name", newNameList.join("__"));
+ }
+ }
+ }
+ // check recursively
+ replaceObjectNames(e);
+ }
+ n = n.nextSibling();
+ }
+}
+
+void CustomPanelEditorPopup::onRegister() {
+ QString panelName = m_panelNameEdit->text();
+ if (panelName.isEmpty()) {
+ DVGui::warning(tr("Please input the panel name."));
+ return;
+ }
+ // overwrite confirmation
+ TFilePath customPanelPath =
+ customPaneFolderPath() + TFilePath(panelName + ".ui");
+ if (TSystem::doesExistFileOrLevel(customPanelPath)) {
+ QString question =
+ tr("The custom panel %1 already exists. Do you want to overwrite?")
+ .arg(panelName);
+ int ret = DVGui::MsgBox(question, tr("Overwrite"), tr("Cancel"), 0);
+ if (ret == 0 || ret == 2) {
+ return;
+ }
+ }
+
+ // create folder if not exist
+ if (!TSystem::touchParentDir(customPanelPath)) {
+ DVGui::warning(tr("Failed to create folder."));
+ return;
+ }
+
+ // base template file
+ QDomDocument doc(panelName);
+ QFile tmplFile(m_templateCombo->currentData().toString());
+ if (!tmplFile.open(QIODevice::ReadOnly)) {
+ DVGui::warning(tr("Failed to open the template."));
+ return;
+ }
+ if (!doc.setContent(&tmplFile)) {
+ tmplFile.close();
+ return;
+ }
+ tmplFile.close();
+
+ QDomElement docElem = doc.documentElement();
+ replaceObjectNames(docElem);
+
+ QFile file(customPanelPath.getQString());
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ DVGui::warning(tr("Failed to open the file for writing."));
+ return;
+ }
+ QTextStream stream(&file);
+ stream.setCodec("UTF-8");
+ stream << doc.toString();
+ file.close();
+
+ CustomPanelManager::instance()->loadCustomPanelEntries();
+
+ close();
+}
+
+//-----------------------------------------------------------------------------
+
+CustomPanelEditorPopup::CustomPanelEditorPopup()
+ : Dialog(TApp::instance()->getMainWindow(), true, false,
+ "CustomPanelEditorPopup") {
+ setWindowTitle(tr("Custom Panel Editor"));
+
+ m_commandListTree = new CustomPanelCommandListTree(this);
+
+ QLabel* commandItemListLabel = new QLabel(tr("Command List"), this);
+ QFont f("Arial", 15, QFont::Bold);
+ commandItemListLabel->setFont(f);
+
+ m_previewArea = new UiPreviewArea(this);
+ m_UiFieldsContainer = new QWidget(this);
+ m_templateCombo = new QComboBox(this);
+ m_panelNameEdit = new QLineEdit("My Custom Panel", this);
+
+ QPushButton* registerButton = new QPushButton(tr("Register"), this);
+ QPushButton* cancelButton = new QPushButton(tr("Cancel"), this);
+
+ m_previewArea->setStyleSheet("background-color: black;");
+
+ beginHLayout();
+
+ QVBoxLayout* leftLay = new QVBoxLayout();
+ leftLay->setMargin(0);
+ leftLay->setSpacing(10);
+ {
+ QHBoxLayout* templateLay = new QHBoxLayout();
+ templateLay->setMargin(0);
+ templateLay->setSpacing(5);
+ {
+ templateLay->addWidget(new QLabel(tr("Template:"), this), 0);
+ templateLay->addWidget(m_templateCombo, 1, Qt::AlignLeft);
+ }
+ leftLay->addLayout(templateLay, 0);
+ leftLay->addWidget(m_UiFieldsContainer, 0);
+ leftLay->addWidget(m_previewArea, 1);
+ }
+ addLayout(leftLay);
+
+ QVBoxLayout* rightLay = new QVBoxLayout();
+ rightLay->setMargin(0);
+ rightLay->setSpacing(10);
+ {
+ rightLay->addWidget(commandItemListLabel, 0);
+ rightLay->addWidget(m_commandListTree, 1);
+ }
+ addLayout(rightLay);
+
+ endHLayout();
+
+ m_buttonLayout->addStretch(1);
+ QHBoxLayout* nameLay = new QHBoxLayout();
+ nameLay->setMargin(0);
+ nameLay->setSpacing(3);
+ {
+ nameLay->addWidget(new QLabel(tr("Panel name:"), this), 0);
+ nameLay->addWidget(m_panelNameEdit, 1);
+ }
+ m_buttonLayout->addLayout(nameLay, 0);
+ m_buttonLayout->addWidget(registerButton, 0);
+ m_buttonLayout->addSpacing(10);
+ m_buttonLayout->addWidget(cancelButton, 0);
+
+ bool ret = true;
+ ret = ret && connect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
+ ret = ret && connect(m_templateCombo, SIGNAL(currentIndexChanged(int)), this,
+ SLOT(onTemplateSwitched()));
+ ret = ret &&
+ connect(registerButton, SIGNAL(clicked()), this, SLOT(onRegister()));
+ assert(ret);
+
+ // load template
+ bool ok = loadTemplateList();
+ if (!ok) {
+ // show some warning?
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+OpenPopupCommandHandler openCustomPanelEditorPopup(
+ MI_CustomPanelEditor);
\ No newline at end of file
diff --git a/toonz/sources/toonz/custompaneleditorpopup.h b/toonz/sources/toonz/custompaneleditorpopup.h
new file mode 100644
index 0000000..b82ae63
--- /dev/null
+++ b/toonz/sources/toonz/custompaneleditorpopup.h
@@ -0,0 +1,160 @@
+#pragma once
+
+#ifndef CUSTOMPANELEDITORPOPUP_H
+#define CUSTOMPANELEDITORPOPUP_H
+
+#include "toonzqt/dvdialog.h"
+// ToonzQt
+#include "toonzqt/menubarcommand.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+class QComboBox;
+class CustomPanelUIField;
+
+enum UiType { Button = 0, Scroller_Back, Scroller_Fore, TypeCount };
+struct UiEntry {
+ QString objectName; // object name before editing
+ UiType type;
+ QRect rect;
+ CustomPanelUIField* field;
+ Qt::Orientation orientation = Qt::Horizontal;
+};
+
+//=============================================================================
+// CustomPanelUIField
+//-----------------------------------------------------------------------------
+class CustomPanelUIField : public QLabel {
+ Q_OBJECT
+ int m_id;
+ QString m_commandId;
+
+public:
+ CustomPanelUIField(const int id, const QString objectName,
+ QWidget* parent = nullptr, bool isFirst = true);
+ QString commandId() { return m_commandId; }
+ bool setCommand(QString commandId);
+
+protected:
+ void enterEvent(QEvent* event) override;
+ void leaveEvent(QEvent* event) override;
+ void dragEnterEvent(QDragEnterEvent* event) override;
+ void dragLeaveEvent(QDragLeaveEvent* event) override;
+ void dropEvent(QDropEvent* event) override;
+ void mousePressEvent(QMouseEvent* event) override;
+signals:
+ void highlight(int);
+ void commandChanged(QString, QString);
+
+public:
+ void notifyCommandChanged(QString oldCmdID, QString newCmdId) {
+ emit commandChanged(oldCmdID, newCmdId);
+ }
+};
+//=============================================================================
+// UiPreviewWidget
+//-----------------------------------------------------------------------------
+
+class UiPreviewWidget final : public QWidget {
+ Q_OBJECT
+ int m_highlightUiId;
+ QList m_rectTable;
+ QPixmap m_uiPixmap;
+
+ void onMove(const QPoint pos);
+
+public:
+ UiPreviewWidget(QPixmap uiPixmap, QList& uiEntries,
+ QWidget* parent = nullptr);
+ void onViewerResize(QSize size);
+ void highlightUi(const int objId);
+ void setUiPixmap(QPixmap uiPixmap) {
+ m_uiPixmap = uiPixmap;
+ update();
+ }
+
+protected:
+ void paintEvent(QPaintEvent*) override;
+
+ void mousePressEvent(QMouseEvent* event) override;
+ void mouseMoveEvent(QMouseEvent* event) override;
+ void dragEnterEvent(QDragEnterEvent* event) override;
+ void dragMoveEvent(QDragMoveEvent* event) override;
+ void dropEvent(QDropEvent* event) override;
+
+signals:
+ void clicked(int id);
+ void dropped(int id, QString cmdId, bool fromTree);
+};
+
+class UiPreviewArea final : public QScrollArea {
+ Q_OBJECT
+public:
+ UiPreviewArea(QWidget* parent = nullptr);
+
+protected:
+ void resizeEvent(QResizeEvent* event) override;
+};
+
+//=============================================================================
+// CustomPanelCommandListTree
+//-----------------------------------------------------------------------------
+
+class CustomPanelCommandListTree final : public QTreeWidget {
+ Q_OBJECT
+
+ void addFolder(const QString& title, int commandType,
+ QTreeWidgetItem* parentFolder = 0);
+
+public:
+ CustomPanelCommandListTree(QWidget* parent = 0);
+
+protected:
+ void mousePressEvent(QMouseEvent*) override;
+};
+
+//=============================================================================
+// CustomPanelEditorPopup
+//-----------------------------------------------------------------------------
+
+class CustomPanelEditorPopup : public DVGui::Dialog {
+ Q_OBJECT
+private:
+ CustomPanelCommandListTree* m_commandListTree;
+ QWidget* m_UiFieldsContainer;
+ UiPreviewArea* m_previewArea;
+
+ QComboBox* m_templateCombo;
+ QLineEdit* m_panelNameEdit;
+ QList m_uiEntries;
+
+ QList entryIdByObjName(const QString objName);
+
+ bool loadTemplateList();
+ void createFields();
+
+ void replaceObjectNames(QDomElement& element);
+
+ // create entries from a widget just loaded from .ui file
+ void buildEntries(QWidget* customWidget);
+ // update widget using the current entries
+ void updateControls(QWidget* customWidget);
+
+public:
+ CustomPanelEditorPopup();
+protected slots:
+ void onTemplateSwitched();
+ void onHighlight(int id);
+ void onCommandChanged(QString, QString);
+ void onPreviewClicked(int id);
+ void onPreviewDropped(int id, QString cmdId, bool fromTree);
+ void onRegister();
+};
+
+#endif
\ No newline at end of file
diff --git a/toonz/sources/toonz/custompanelmanager.cpp b/toonz/sources/toonz/custompanelmanager.cpp
new file mode 100644
index 0000000..f127e68
--- /dev/null
+++ b/toonz/sources/toonz/custompanelmanager.cpp
@@ -0,0 +1,235 @@
+#include "custompanelmanager.h"
+
+#include "menubarcommandids.h"
+#include "floatingpanelcommand.h"
+#include "pane.h"
+
+// ToonzLib
+#include "toonz/toonzfolders.h"
+// ToonzCore
+#include "tsystem.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+
+const TFilePath CustomPanelFolderName("custompanels");
+const TFilePath customPaneFolderPath() {
+ return ToonzFolder::getMyModuleDir() + CustomPanelFolderName;
+}
+} // namespace
+
+//-----------------------------------------------------------------------------
+
+MyScroller::MyScroller(Qt::Orientation orientation, CommandId command1,
+ CommandId command2, QWidget* parent)
+ : QWidget(parent), m_orientation(orientation) {
+ m_actions[0] = CommandManager::instance()->getAction(command1);
+ m_actions[1] = CommandManager::instance()->getAction(command2);
+}
+
+void MyScroller::paintEvent(QPaintEvent*) {
+ QPainter p(this);
+
+ p.setPen(Qt::white);
+ p.setBrush(Qt::NoBrush);
+
+ p.drawRect(rect().adjusted(0, 0, -1, -1));
+
+ if (m_orientation == Qt::Horizontal) {
+ for (int i = 1; i <= 7; i++) {
+ int xPos = width() * i / 8;
+ p.drawLine(xPos, 0, xPos, height());
+ }
+ } else { // vertical
+ for (int i = 1; i <= 7; i++) {
+ int yPos = height() * i / 8;
+ p.drawLine(0, yPos, width(), yPos);
+ }
+ }
+}
+
+void MyScroller::mousePressEvent(QMouseEvent* event) {
+ m_anchorPos =
+ (m_orientation == Qt::Horizontal) ? event->pos().x() : event->pos().y();
+}
+
+void MyScroller::mouseMoveEvent(QMouseEvent* event) {
+ int currentPos =
+ (m_orientation == Qt::Horizontal) ? event->pos().x() : event->pos().y();
+ static int threshold = 5;
+ if (m_anchorPos - currentPos >= threshold && m_actions[0]) {
+ m_actions[0]->trigger();
+ m_anchorPos = currentPos;
+ } else if (currentPos - m_anchorPos >= threshold && m_actions[1]) {
+ m_actions[1]->trigger();
+ m_anchorPos = currentPos;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+CustomPanelManager* CustomPanelManager::instance() {
+ static CustomPanelManager _instance;
+ return &_instance;
+}
+
+//-----------------------------------------------------------------------------
+// browse the custom panel settings and regisiter to the menu
+void CustomPanelManager::loadCustomPanelEntries() {
+ QAction* menuAct = CommandManager::instance()->getAction(MI_OpenCustomPanels);
+ if (!menuAct) return;
+ DVMenuAction* menu = dynamic_cast(menuAct->menu());
+ if (!menu) return;
+
+ if (!menu->isEmpty()) menu->clear();
+
+ TFilePath customPanelsFolder = customPaneFolderPath();
+ if (TSystem::doesExistFileOrLevel(customPanelsFolder)) {
+ TFilePathSet fileList =
+ TSystem::readDirectory(customPanelsFolder, false, true, false);
+ if (!fileList.empty()) {
+ QList fileNames;
+ for (auto file : fileList) {
+ // accept only .ui files
+ if (file.getType() != "ui") continue;
+ fileNames.append(QString::fromStdString(file.getName()));
+ }
+ if (!fileNames.isEmpty()) {
+ menu->setActions(fileNames);
+ menu->addSeparator();
+ }
+ }
+ }
+ menu->addAction(CommandManager::instance()->getAction(MI_CustomPanelEditor));
+}
+
+//-----------------------------------------------------------------------------
+
+TPanel* CustomPanelManager::createCustomPanel(const QString panelName,
+ QWidget* parent) {
+ TPanel* panel = new TPanel(parent);
+ QString panelType = "Custom_" + panelName;
+ panel->setPanelType(panelType.toStdString());
+ panel->setObjectName(panelType);
+
+ TFilePath customPanelsFp =
+ customPaneFolderPath() + TFilePath(panelName + ".ui");
+ QUiLoader loader;
+ QFile file(customPanelsFp.getQString());
+
+ file.open(QFile::ReadOnly);
+ QWidget* customWidget = loader.load(&file, panel);
+ file.close();
+
+ initializeControl(customWidget);
+
+ panel->setWindowTitle(panelName);
+ panel->setWidget(customWidget);
+
+ return panel;
+}
+
+//-----------------------------------------------------------------------------
+
+void CustomPanelManager::initializeControl(QWidget* customWidget) {
+ // connect buttons and commands
+ QList allButtons =
+ customWidget->findChildren();
+ for (auto button : allButtons) {
+ std::cout << button->objectName().toStdString() << std::endl;
+ QAction* action = CommandManager::instance()->getAction(
+ button->objectName().toStdString().c_str());
+ if (!action) continue;
+
+ CommandManager::instance()->enlargeIcon(
+ button->objectName().toStdString().c_str(), button->iconSize());
+
+ if (QToolButton* tb = dynamic_cast(button)) {
+ tb->setDefaultAction(action);
+ tb->setObjectName("CustomPanelButton");
+ if (tb->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
+ int padding = (tb->height() - button->iconSize().height() -
+ tb->font().pointSize() * 1.33) /
+ 3;
+ if (padding > 0)
+ tb->setStyleSheet(QString("padding-top: %1;").arg(padding));
+ }
+ continue;
+ }
+
+ if (action->isCheckable()) {
+ button->setCheckable(true);
+ button->setChecked(action->isChecked());
+ customWidget->connect(button, SIGNAL(clicked(bool)), action,
+ SLOT(setChecked(bool)));
+ customWidget->connect(action, SIGNAL(toggled(bool)), button,
+ SLOT(setChecked(bool)));
+ } else {
+ customWidget->connect(button, SIGNAL(clicked(bool)), action,
+ SLOT(trigger()));
+ }
+ if (!button->text().isEmpty()) button->setText(action->text());
+
+ button->setIcon(action->icon());
+ // button->addAction(action);
+ }
+
+ // other custom controls
+ QList allWidgets = customWidget->findChildren();
+ for (auto widget : allWidgets) {
+ // ignore buttons
+ if (dynamic_cast(widget)) continue;
+ // ignore if the widget already has a layout
+ if (widget->layout() != nullptr) continue;
+
+ QString name = widget->objectName();
+ QWidget* customControl = nullptr;
+ if (name.startsWith("HScroller")) {
+ QStringList ids = name.split("__");
+ if (ids.size() != 3 || ids[0] != "HScroller") continue;
+ customControl =
+ new MyScroller(Qt::Horizontal, ids[1].toStdString().c_str(),
+ ids[2].toStdString().c_str(), customWidget);
+ } else if (name.startsWith("VScroller")) {
+ QStringList ids = name.split("__");
+ if (ids.size() != 3 || ids[0] != "VScroller") continue;
+ customControl =
+ new MyScroller(Qt::Vertical, ids[1].toStdString().c_str(),
+ ids[2].toStdString().c_str(), customWidget);
+ }
+
+ if (customControl) {
+ QHBoxLayout* lay = new QHBoxLayout();
+ lay->setMargin(0);
+ lay->setSpacing(0);
+ lay->addWidget(customControl);
+ widget->setLayout(lay);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+class OpenCustomPanelCommandHandler final : public MenuItemHandler {
+public:
+ OpenCustomPanelCommandHandler() : MenuItemHandler(MI_OpenCustomPanels) {}
+ void execute() override {
+ QAction* act = CommandManager::instance()->getAction(MI_OpenCustomPanels);
+ DVMenuAction* menu = dynamic_cast(act->menu());
+ int index = menu->getTriggeredActionIndex();
+
+ // the last action is for opening custom panel editor
+ if (index == menu->actions().size() - 1) return;
+
+ QString panelId = menu->actions()[index]->text();
+
+ OpenFloatingPanel::getOrOpenFloatingPanel("Custom_" +
+ panelId.toStdString());
+ }
+} openCustomPanelCommandHandler;
diff --git a/toonz/sources/toonz/custompanelmanager.h b/toonz/sources/toonz/custompanelmanager.h
new file mode 100644
index 0000000..006afa2
--- /dev/null
+++ b/toonz/sources/toonz/custompanelmanager.h
@@ -0,0 +1,45 @@
+#pragma once
+#ifndef CUSTOM_PANEL_MANAGER_H
+#define CUSTOM_PANEL_MANAGER_H
+
+// ToonzQt
+#include "toonzqt/menubarcommand.h"
+
+#include
+#include
+
+class TPanel;
+
+//=============================================================================
+// original widgets for the custom panel
+class MyScroller : public QWidget {
+ Q_OBJECT
+ Qt::Orientation m_orientation;
+ QAction* m_actions[2];
+ int m_anchorPos;
+
+public:
+ MyScroller(Qt::Orientation orientation, CommandId command1,
+ CommandId command2, QWidget* parent = nullptr);
+
+protected:
+ void paintEvent(QPaintEvent*) override;
+ void mousePressEvent(QMouseEvent* event) override;
+ void mouseMoveEvent(QMouseEvent* event) override;
+};
+
+//=============================================================================
+
+class CustomPanelManager { // singleton
+ CustomPanelManager(){};
+
+public:
+ static CustomPanelManager* instance();
+ void loadCustomPanelEntries();
+
+ TPanel* createCustomPanel(const QString panelName, QWidget* parent);
+
+ void initializeControl(QWidget* customWidget);
+};
+
+#endif
diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp
index ce195bc..6381c69 100644
--- a/toonz/sources/toonz/mainwindow.cpp
+++ b/toonz/sources/toonz/mainwindow.cpp
@@ -14,6 +14,7 @@
#include "comboviewerpane.h"
#include "startuppopup.h"
#include "tooloptionsshortcutinvoker.h"
+#include "custompanelmanager.h"
// TnzTools includes
#include "tools/toolcommandids.h"
@@ -218,8 +219,8 @@ int get_version_code_from(std::string ver) {
// minor version: assume that the minor version is less than 255.
std::string::size_type const b = ver.find('.', a + 1);
std::string const minor = (b == std::string::npos)
- ? ver.substr(a + 1)
- : ver.substr(a + 1, b - a - 1);
+ ? ver.substr(a + 1)
+ : ver.substr(a + 1, b - a - 1);
version += std::stoi(minor) << 16;
if ((b == std::string::npos) || (b + 1 == ver.length())) {
return version;
@@ -564,9 +565,9 @@ void MainWindow::readSettings(const QString &argumentLayoutFileName) {
/*--
* タイトルバーに表示するレイアウト名を作る:_layoutがあればそこから省く。無ければ.txtのみ省く
* --*/
- int pos = (argumentLayoutFileName.indexOf("_layout") == -1)
- ? argumentLayoutFileName.indexOf(".txt")
- : argumentLayoutFileName.indexOf("_layout");
+ int pos = (argumentLayoutFileName.indexOf("_layout") == -1)
+ ? argumentLayoutFileName.indexOf(".txt")
+ : argumentLayoutFileName.indexOf("_layout");
m_layoutName = argumentLayoutFileName.left(pos);
}
}
@@ -659,6 +660,7 @@ void MainWindow::readSettings(const QString &argumentLayoutFileName) {
}
RecentFiles::instance()->loadRecentFiles();
+ CustomPanelManager::instance()->loadCustomPanelEntries();
}
//-----------------------------------------------------------------------------
@@ -1383,7 +1385,7 @@ QAction *MainWindow::createAction(const char *id, const char *name,
action->setMenuRole(QAction::NoRole);
#endif
CommandManager::instance()->define(id, type, defaultShortcut.toStdString(),
- action);
+ action, iconSVGName);
return action;
}
@@ -1515,8 +1517,9 @@ QAction *MainWindow::createFillAction(const char *id, const char *name,
//-----------------------------------------------------------------------------
QAction *MainWindow::createMenuAction(const char *id, const char *name,
- QList list) {
- QMenu *menu = new DVMenuAction(tr(name), this, list);
+ QList list,
+ bool isForRecentFiles) {
+ QMenu *menu = new DVMenuAction(tr(name), this, list, isForRecentFiles);
QAction *action = menu->menuAction();
CommandManager::instance()->define(id, MenuCommandType, "", action);
return action;
@@ -1608,8 +1611,8 @@ QAction *MainWindow::createToolAction(const char *id, const char *iconName,
// Adding tool actions to the main window solve this problem!
addAction(action);
- CommandManager::instance()->define(id, ToolCommandType,
- defaultShortcut.toStdString(), action);
+ CommandManager::instance()->define(
+ id, ToolCommandType, defaultShortcut.toStdString(), action, iconName);
return action;
}
@@ -2139,6 +2142,12 @@ void MainWindow::defineActions() {
createMenuWindowsAction(MI_OpenGuidedDrawingControls,
QT_TR_NOOP("Guided Drawing Controls"), "",
"guided_drawing");
+
+ createMenuAction(MI_OpenCustomPanels, QT_TR_NOOP("&Custom Panels"), files,
+ false);
+ createMenuWindowsAction(MI_CustomPanelEditor,
+ QT_TR_NOOP("&Custom Panel Editor..."), "", "");
+
menuAct =
createToggle(MI_DockingCheck, QT_TR_NOOP("&Lock Room Panes"), "",
DockingCheckToggleAction ? 1 : 0, MenuWindowsCommandType);
@@ -3015,10 +3024,9 @@ RecentFiles::~RecentFiles() {}
void RecentFiles::addFilePath(QString path, FileType fileType,
QString projectName) {
- QList files =
- (fileType == Scene)
- ? m_recentScenes
- : (fileType == Level) ? m_recentLevels : m_recentFlipbookImages;
+ QList files = (fileType == Scene) ? m_recentScenes
+ : (fileType == Level) ? m_recentLevels
+ : m_recentFlipbookImages;
int i;
for (i = 0; i < files.size(); i++)
if (files.at(i) == path) {
@@ -3071,10 +3079,9 @@ void RecentFiles::removeFilePath(int index, FileType fileType) {
//-----------------------------------------------------------------------------
QString RecentFiles::getFilePath(int index, FileType fileType) const {
- return (fileType == Scene)
- ? m_recentScenes[index]
- : (fileType == Level) ? m_recentLevels[index]
- : m_recentFlipbookImages[index];
+ return (fileType == Scene) ? m_recentScenes[index]
+ : (fileType == Level) ? m_recentLevels[index]
+ : m_recentFlipbookImages[index];
}
//-----------------------------------------------------------------------------
@@ -3184,10 +3191,9 @@ void RecentFiles::saveRecentFiles() {
//-----------------------------------------------------------------------------
QList RecentFiles::getFilesNameList(FileType fileType) {
- QList files =
- (fileType == Scene)
- ? m_recentScenes
- : (fileType == Level) ? m_recentLevels : m_recentFlipbookImages;
+ QList files = (fileType == Scene) ? m_recentScenes
+ : (fileType == Level) ? m_recentLevels
+ : m_recentFlipbookImages;
QList names;
int i;
for (i = 0; i < files.size(); i++) {
@@ -3202,9 +3208,9 @@ QList RecentFiles::getFilesNameList(FileType fileType) {
//-----------------------------------------------------------------------------
void RecentFiles::refreshRecentFilesMenu(FileType fileType) {
- CommandId id = (fileType == Scene) ? MI_OpenRecentScene
- : (fileType == Level) ? MI_OpenRecentLevel
- : MI_LoadRecentImage;
+ CommandId id = (fileType == Scene) ? MI_OpenRecentScene
+ : (fileType == Level) ? MI_OpenRecentLevel
+ : MI_LoadRecentImage;
QAction *act = CommandManager::instance()->getAction(id);
if (!act) return;
DVMenuAction *menu = dynamic_cast(act->menu());
@@ -3213,10 +3219,9 @@ void RecentFiles::refreshRecentFilesMenu(FileType fileType) {
if (names.isEmpty())
menu->setEnabled(false);
else {
- CommandId clearActionId =
- (fileType == Scene)
- ? MI_ClearRecentScene
- : (fileType == Level) ? MI_ClearRecentLevel : MI_ClearRecentImage;
+ CommandId clearActionId = (fileType == Scene) ? MI_ClearRecentScene
+ : (fileType == Level) ? MI_ClearRecentLevel
+ : MI_ClearRecentImage;
menu->setActions(names);
menu->addSeparator();
QAction *clearAction = CommandManager::instance()->getAction(clearActionId);
diff --git a/toonz/sources/toonz/mainwindow.h b/toonz/sources/toonz/mainwindow.h
index cd7839b..3c56a20 100644
--- a/toonz/sources/toonz/mainwindow.h
+++ b/toonz/sources/toonz/mainwindow.h
@@ -185,7 +185,7 @@ private:
const QString &defaultShortcut,
const char *iconSVGName = "");
QAction *createMenuAction(const char *id, const char *name,
- QList list);
+ QList list, bool isForRecentFiles = true);
QAction *createToggle(const char *id, const char *name,
const QString &defaultShortcut, bool startStatus,
CommandType type, const char *iconSVGName = "");
diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp
index b80022a..f829991 100644
--- a/toonz/sources/toonz/menubar.cpp
+++ b/toonz/sources/toonz/menubar.cpp
@@ -1428,6 +1428,7 @@ QMenuBar *StackedMenuBar::createFullMenuBar() {
#endif
addMenuItem(windowsMenu, MI_StartupPopup);
addMenuItem(windowsMenu, MI_OpenGuidedDrawingControls);
+ addMenuItem(windowsMenu, MI_OpenCustomPanels);
// windowsMenu->addSeparator();
// addMenuItem(windowsMenu, MI_OpenExport);
windowsMenu->addSeparator();
@@ -1574,7 +1575,7 @@ TopBar::TopBar(QWidget *parent) : QToolBar(parent) {
bool ret = true;
ret = ret && connect(m_roomTabBar, SIGNAL(currentChanged(int)),
- m_stackedMenuBar, SLOT(setCurrentIndex(int)));
+ m_stackedMenuBar, SLOT(setCurrentIndex(int)));
ret = ret && connect(m_roomTabBar, SIGNAL(indexSwapped(int, int)),
m_stackedMenuBar, SLOT(onIndexSwapped(int, int)));
diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h
index f76f447..4b2b05b 100644
--- a/toonz/sources/toonz/menubarcommandids.h
+++ b/toonz/sources/toonz/menubarcommandids.h
@@ -464,4 +464,7 @@
#define MI_ZoomInAndFitPanel "MI_ZoomInAndFitPanel"
#define MI_ZoomOutAndFitPanel "MI_ZoomOutAndFitPanel"
+#define MI_OpenCustomPanels "MI_OpenCustomPanels"
+#define MI_CustomPanelEditor "MI_CustomPanelEditor"
+
#endif
diff --git a/toonz/sources/toonz/menubarpopup.cpp b/toonz/sources/toonz/menubarpopup.cpp
index 1a9815e..4dc0466 100644
--- a/toonz/sources/toonz/menubarpopup.cpp
+++ b/toonz/sources/toonz/menubarpopup.cpp
@@ -381,6 +381,8 @@ CommandListTree::CommandListTree(QWidget* parent) : QTreeWidget(parent) {
addFolder(ShortcutTree::tr("Windows"), MenuWindowsCommandType,
menuCommandFolder);
addFolder(ShortcutTree::tr("Help"), MenuHelpCommandType, menuCommandFolder);
+ addFolder(ShortcutTree::tr("SubMenu Commands"), MenuCommandType,
+ menuCommandFolder);
addFolder(ShortcutTree::tr("Tools"), ToolCommandType);
diff --git a/toonz/sources/toonz/pane.cpp b/toonz/sources/toonz/pane.cpp
index 2e2c5c9..d6a6243 100644
--- a/toonz/sources/toonz/pane.cpp
+++ b/toonz/sources/toonz/pane.cpp
@@ -7,6 +7,7 @@
#include "mainwindow.h"
#include "tenv.h"
#include "saveloadqsettings.h"
+#include "custompanelmanager.h"
#include "toonzqt/gutil.h"
@@ -292,9 +293,10 @@ void TPanelTitleBarButton::paintEvent(QPaintEvent *event) {
QPixmap panePixmapOn = compositePixmap(panePixmap, 1, QSize(), 0, 0, bgColor);
QPainter painter(this);
- painter.drawPixmap(
- 0, 0,
- m_pressed ? panePixmapOn : m_rollover ? panePixmapOver : panePixmapOff);
+ painter.drawPixmap(0, 0,
+ m_pressed ? panePixmapOn
+ : m_rollover ? panePixmapOver
+ : panePixmapOff);
painter.end();
}
@@ -587,6 +589,12 @@ TPanel *TPanelFactory::createPanel(QWidget *parent, QString panelType) {
QMap::iterator it = tableInstance().find(panelType);
if (it == tableInstance().end()) {
+ if (panelType.startsWith("Custom_")) {
+ panelType = panelType.right(panelType.size() - 7);
+ return CustomPanelManager::instance()->createCustomPanel(panelType,
+ parent);
+ }
+
TPanel *panel = new TPanel(parent);
panel->setPanelType(panelType.toStdString());
return panel;
diff --git a/toonz/sources/toonzqt/gutil.cpp b/toonz/sources/toonzqt/gutil.cpp
index 16014b7..458df0c 100644
--- a/toonz/sources/toonzqt/gutil.cpp
+++ b/toonz/sources/toonzqt/gutil.cpp
@@ -448,6 +448,72 @@ QIcon createQIcon(const char *iconSVGName, bool useFullOpacity,
//-----------------------------------------------------------------------------
+void addSpecifiedSizedImageToIcon(QIcon &icon, const char *iconSVGName,
+ QSize newSize) {
+ static int devPixRatio = getHighestDevicePixelRatio();
+ newSize *= devPixRatio;
+ QIcon themeIcon = QIcon::fromTheme(iconSVGName);
+ // Pseudo state name strings
+ QString overStr = QString(iconSVGName) + "_over";
+ QString onStr = QString(iconSVGName) + "_on";
+ // Control lightness of the icons
+ const qreal activeOpacity = 1;
+ const qreal baseOpacity = 0.8;
+ const qreal disabledOpacity = 0.15;
+ //----------
+
+ // Base pixmap
+ QPixmap themeIconPixmap(recolorPixmap(themeIcon.pixmap(newSize)));
+ if (!themeIconPixmap.isNull()) { // suppress message
+ themeIconPixmap.setDevicePixelRatio(devPixRatio);
+ themeIconPixmap = themeIconPixmap.scaled(newSize, Qt::KeepAspectRatio,
+ Qt::SmoothTransformation);
+ }
+
+ // Over pixmap
+ QPixmap overPixmap(recolorPixmap(QIcon::fromTheme(overStr).pixmap(newSize)));
+ if (!overPixmap.isNull()) { // suppress message
+ overPixmap.setDevicePixelRatio(devPixRatio);
+ overPixmap = overPixmap.scaled(newSize, Qt::KeepAspectRatio,
+ Qt::SmoothTransformation);
+ }
+
+ // On pixmap
+ QPixmap onPixmap(recolorPixmap(QIcon::fromTheme(onStr).pixmap(newSize)));
+ if (!onPixmap.isNull()) { // suppress message
+ onPixmap.setDevicePixelRatio(devPixRatio);
+ onPixmap =
+ onPixmap.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ }
+
+ // Base icon
+ icon.addPixmap(compositePixmap(themeIconPixmap, baseOpacity), QIcon::Normal,
+ QIcon::Off);
+ icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity),
+ QIcon::Disabled, QIcon::Off);
+
+ // Over icon
+ icon.addPixmap(!overPixmap.isNull()
+ ? compositePixmap(overPixmap, activeOpacity)
+ : compositePixmap(themeIconPixmap, activeOpacity),
+ QIcon::Active);
+
+ // On icon
+ if (!onPixmap.isNull()) {
+ icon.addPixmap(compositePixmap(onPixmap, activeOpacity), QIcon::Normal,
+ QIcon::On);
+ icon.addPixmap(compositePixmap(onPixmap, disabledOpacity), QIcon::Disabled,
+ QIcon::On);
+ } else {
+ icon.addPixmap(compositePixmap(themeIconPixmap, activeOpacity),
+ QIcon::Normal, QIcon::On);
+ icon.addPixmap(compositePixmap(themeIconPixmap, disabledOpacity),
+ QIcon::Disabled, QIcon::On);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
QIcon createQIconPNG(const char *iconPNGName) {
QString normal = QString(":Resources/") + iconPNGName + ".png";
QString click = QString(":Resources/") + iconPNGName + "_click.png";
diff --git a/toonz/sources/toonzqt/menubarcommand.cpp b/toonz/sources/toonzqt/menubarcommand.cpp
index 74d817f..5779c9f 100644
--- a/toonz/sources/toonzqt/menubarcommand.cpp
+++ b/toonz/sources/toonzqt/menubarcommand.cpp
@@ -111,8 +111,8 @@ void CommandManager::setShortcut(CommandId id, QAction *action,
//---------------------------------------------------------
void CommandManager::define(CommandId id, CommandType type,
- std::string defaultShortcutString,
- QAction *qaction) {
+ std::string defaultShortcutString, QAction *qaction,
+ const char *iconSVGName) {
assert(type != UndefinedCommandType);
assert(qaction != 0);
assert(m_qactionTable.count(qaction) == 0);
@@ -129,6 +129,7 @@ void CommandManager::define(CommandId id, CommandType type,
node->m_type == MiscCommandType ||
node->m_type == ToolModifierCommandType ||
node->m_type == CellMarkCommandType);
+ node->m_iconSVGName = iconSVGName;
m_qactionTable[qaction] = node;
qaction->setShortcutContext(Qt::ApplicationShortcut);
@@ -386,6 +387,35 @@ std::string CommandManager::getIdFromAction(QAction *action) {
}
//---------------------------------------------------------
+
+const char *CommandManager::getIconSVGName(CommandId id) {
+ Node *node = getNode(id, false);
+ if (node) return node->m_iconSVGName;
+ return "";
+}
+
+void CommandManager::enlargeIcon(CommandId id, const QSize dstSize) {
+ QAction *action = getAction(id, false);
+ if (!action) return;
+
+ const char *iconSVGName = getIconSVGName(id);
+ if (iconSVGName == "") return;
+
+ QIcon icon = action->icon();
+
+ for (QList sizes = icon.availableSizes(); !sizes.isEmpty();
+ sizes.removeFirst()) {
+ if (sizes.first().width() > dstSize.width() &&
+ sizes.first().height() > dstSize.height())
+ return;
+ }
+
+ addSpecifiedSizedImageToIcon(icon, iconSVGName, dstSize);
+
+ action->setIcon(icon);
+}
+
+//---------------------------------------------------------
// load user defined shortcuts
void CommandManager::loadShortcuts() {
@@ -483,8 +513,10 @@ void DVAction::onTriggered() { CommandManager::instance()->execute(this); }
is helpful to use \b m_triggeredActionIndex to distingue action.
*/
DVMenuAction::DVMenuAction(const QString &text, QWidget *parent,
- QList actions)
- : QMenu(text, parent), m_triggeredActionIndex(-1) {
+ QList actions, bool isForRecentFiles)
+ : QMenu(text, parent)
+ , m_triggeredActionIndex(-1)
+ , m_isForRecentFiles(isForRecentFiles) {
int i;
for (i = 0; i < actions.size(); i++) addAction(actions.at(i));
connect(this, SIGNAL(triggered(QAction *)), this,
@@ -535,6 +567,10 @@ void DVMenuAction::onTriggered(QAction *action) {
QVariant data = action->data();
if (data.isValid()) m_triggeredActionIndex = data.toInt();
CommandManager::instance()->execute(action, menuAction());
+
+ // simply execute the menu item and return
+ if (!m_isForRecentFiles) return;
+
int oldIndex = m_triggeredActionIndex;
if (m_triggeredActionIndex != -1) m_triggeredActionIndex = -1;
QString str = data.toString();