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 MI_OpenOnlineManual diff --git a/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml b/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml index 9f4037d..2d3a527 100644 --- a/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml +++ b/stuff/profiles/layouts/rooms/StudioGhibli/menubar_template.xml @@ -217,6 +217,7 @@ MI_OpenComboViewer MI_OpenXshView MI_OpenHistoryPanel + MI_OpenCustomPanels MI_ResetRoomLayout 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();