From 534c7ce831affe68ffe4335a43291782a3d6b15a Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Aug 29 2023 18:51:53 +0000 Subject: improve assistant tool selection --- diff --git a/toonz/sources/include/tools/tool.h b/toonz/sources/include/tools/tool.h index fde613e..627a713 100644 --- a/toonz/sources/include/tools/tool.h +++ b/toonz/sources/include/tools/tool.h @@ -288,7 +288,7 @@ public: EmptyTarget = 0x80, //!< Will work on empty cells/columns - MetaImage = 0x100, //!< Will work on mets images + MetaImage = 0x100, //!< Will work on meta images CommonImages = VectorImage | ToonzImage | RasterImage, AllImages = CommonImages | MeshImage | MetaImage, @@ -572,8 +572,16 @@ protected: static std::set m_selectedFrames; -protected: - void bind(int targetType); +private: + void bind(const std::string &name, int targetType); + +public: + inline void bind(int targetType) + { bind(getName(), targetType); } + void bind( int targetType, + const std::string &alias1, + const std::string &alias2 = std::string(), + const std::string &alias3 = std::string() ); virtual void onSelectedFramesChanged() {} diff --git a/toonz/sources/include/tools/toolhandle.h b/toonz/sources/include/tools/toolhandle.h index 427c211..0fd1f71 100644 --- a/toonz/sources/include/tools/toolhandle.h +++ b/toonz/sources/include/tools/toolhandle.h @@ -44,11 +44,12 @@ public: ~ToolHandle(); TTool *getTool() const; - void setTool(TTool *tool); - void setTool(QString name); void setTargetType(int targetType); + const QString& getRequestedToolName() const + { return m_toolName; } + // used to change tool for a short while (e.g. while keeping pressed a // short-key) void storeTool(); diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp index 25299d9..89a60a0 100644 --- a/toonz/sources/tnztools/editassistantstool.cpp +++ b/toonz/sources/tnztools/editassistantstool.cpp @@ -170,6 +170,11 @@ protected: TAssistant *m_writeAssistant; Selection *selection; + + // don't try to follow the pointer from history, it may be invalidated + typedef std::pair HistoryItem; + typedef std::vector History; + History m_history; public: EditAssistantsTool(): @@ -188,7 +193,8 @@ public: m_writeAssistant() { selection = new Selection(*this); - bind(MetaImage | EmptyTarget); + // also assign assistants to the "brush" button in toolbar + bind(MetaImage | EmptyTarget, "T_Brush"); m_toolProperties.bind(m_assistantType); updateTranslation(); } @@ -203,7 +209,7 @@ public: int getCursorId() const override { return ToolCursor::StrokeSelectCursor; } void onImageChanged() override { - if (m_currentImage != getImage(false)) resetCurrentPoint(); + if (m_currentImage != getImage(false)) loadHistory(); getViewer()->GLInvalidateAll(); } @@ -211,7 +217,7 @@ public: std::wstring value = m_assistantType.getValue(); m_assistantType.deleteAllValues(); - m_assistantType.addValueWithUIName(L"", tr("--")); + m_assistantType.addValueWithUIName(L"", tr("")); const TMetaObject::Registry ®istry = TMetaObject::getRegistry(); for(TMetaObject::Registry::const_iterator i = registry.begin(); i != registry.end(); ++i) @@ -240,11 +246,11 @@ public: void onActivate() override { updateAssistantTypes(); - resetCurrentPoint(); + loadHistory(); } void onDeactivate() override - { resetCurrentPoint(); } + { resetCurrentPoint(true, false); } void updateTranslation() override { m_assistantType.setQStringName( tr("Assistant Type") ); @@ -270,6 +276,22 @@ public: { return isSelected() ? selection : 0; } protected: + void putHistory(const void *img, int assistantIndex) { + if (!img) return; + for(History::iterator i = m_history.begin(); i != m_history.end(); ) + if (i->first == img) i = m_history.erase(i); else ++i; + if (m_history.size() >= 10) m_history.pop_back(); + m_history.push_back(HistoryItem(img, assistantIndex)); + } + + void loadHistory() { + int index = -1; + if (Closer closer = read(ModeImage)) + for(History::iterator i = m_history.begin(); i != m_history.end(); ++i) + if (i->first == m_readImage) index = i->second; + if (index < 0) resetCurrentPoint(true, false); else chooseAssistant(index); + } + void close() { m_readAssistant = 0; m_readObject.reset(); @@ -377,7 +399,7 @@ protected: void updateOptionsBox() { getApplication()->getCurrentTool()->notifyToolOptionsBoxChanged(); } - void resetCurrentPoint(bool updateOptionsBox = true) { + void resetCurrentPoint(bool updateOptionsBox = true, bool updateHistory = true) { close(); m_currentImage.reset(); m_currentAssistant.reset(); @@ -389,15 +411,37 @@ protected: m_currentAssistantBackup.reset(); // deselect points - if (Closer closer = read(ModeImage)) + if (Closer closer = read(ModeImage)) { for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) if (*i) if (const TAssistant *assistant = (*i)->getHandler()) assistant->deselectAll(); + if (updateHistory) putHistory(m_readImage, -1); + } if (updateOptionsBox) this->updateOptionsBox(); } + bool chooseAssistant(int index) { + resetCurrentPoint(false); + if (index >= 0) + if (Closer closer = read(ModeImage)) { + m_currentImage.set(m_readImage); + if (index < (*m_reader)->size()) + if (const TMetaObjectPC &obj = (**m_reader)[index]) + if (const TAssistant *assistant = obj->getHandler()) { + assistant->deselectAll(); + m_currentAssistant.set(obj); + m_currentAssistantIndex = index; + m_currentPointName = assistant->getBasePoint().name; + assistant->selectAll(); + } + putHistory(m_readImage, m_currentAssistantIndex); + } + this->updateOptionsBox(); + return m_currentAssistantIndex >= 0; + } + bool findCurrentPoint(const TPointD &position, double pixelSize = 1, bool updateOptionsBox = true) { resetCurrentPoint(false); if (Closer closer = read(ModeImage)) { @@ -428,6 +472,7 @@ protected: } } } + putHistory(m_readImage, m_currentAssistantIndex); } if (updateOptionsBox) this->updateOptionsBox(); @@ -469,7 +514,7 @@ protected: public: void deselect() - { resetCurrentPoint(); } + { resetCurrentPoint(true, false); } bool isSelected() { return read(ModeAssistant); } diff --git a/toonz/sources/tnztools/tool.cpp b/toonz/sources/tnztools/tool.cpp index 38434c5..3cec709 100644 --- a/toonz/sources/tnztools/tool.cpp +++ b/toonz/sources/tnztools/tool.cpp @@ -54,13 +54,8 @@ namespace { -// Global variables - typedef std::pair ToolKey; -typedef std::multimap ToolTable; -ToolTable *toolTable = 0; - -std::set *toolNames = 0; +typedef std::multimap ToolTable; //=================================================================== @@ -76,10 +71,13 @@ struct DummyTool final : public TTool { int getCursorId() const override { return ToolCursor::ForbiddenCursor; } // Forbids everything + DummyTool(): TTool("T_Dummy") {} +} theDummyTool; - DummyTool() : TTool("T_Dummy") {} +// Local functions -} theDummyTool; +ToolTable& toolTable() + { static ToolTable t; return t; } //------------------------------------------------------------------- @@ -180,8 +178,6 @@ TTool::TTool(std::string name) //------------------------------------------------------------------- TTool *TTool::getTool(std::string toolName, ToolTargetType targetType) { - if (!toolTable) return 0; - // if to this name and target type was assigned more then one tool // then select tool which more compatible with default target type @@ -199,7 +195,7 @@ TTool *TTool::getTool(std::string toolName, ToolTargetType targetType) { TTool *tool = 0; std::pair range = - toolTable->equal_range(std::make_pair(toolName, targetType)); + toolTable().equal_range(std::make_pair(toolName, targetType)); for(ToolTable::iterator it = range.first; it != range.second; ++it) { int t = it->second->getTargetType(); bool d = (bool)(t & defTarget); @@ -215,12 +211,32 @@ TTool *TTool::getTool(std::string toolName, ToolTargetType targetType) { //----------------------------------------------------------------------------- -void TTool::bind(int targetType) { - m_targetType = targetType; +void TTool::bind( int targetType, + const std::string &alias1, + const std::string &alias2, + const std::string &alias3 ) +{ + bind(targetType); + if ( !alias1.empty() + && alias1 != m_name ) + bind(alias1, targetType); + if ( !alias2.empty() + && alias2 != m_name + && alias2 != alias1 ) + bind(alias2, targetType); + if ( !alias3.empty() + && alias3 != m_name + && alias3 != alias1 + && alias3 != alias2 ) + bind(alias3, targetType); +} - if (!toolTable) toolTable = new ToolTable(); +//----------------------------------------------------------------------------- - if (!toolNames) toolNames = new std::set(); +void TTool::bind(const std::string &name, int targetType) { + static std::set registeredNames; + + m_targetType = targetType; ToolTargetType targets[] = { EmptyTarget, @@ -231,14 +247,13 @@ void TTool::bind(int targetType) { MetaImage }; int targetsCount = sizeof(targets)/sizeof(*targets); - std::string name = getName(); - if (toolNames->count(name) == 0) { - toolNames->insert(name); + if (!registeredNames.count(name)) { + registeredNames.insert(name); // Initialize with the dummy tool for(int i = 0; i < targetsCount; ++i) - if (!toolTable->count(std::make_pair(name, targets[i]))) - toolTable->insert( + if (!toolTable().count(std::make_pair(name, targets[i]))) + toolTable().insert( std::make_pair(std::make_pair(name, targets[i]), &theDummyTool)); ToolSelector *toolSelector = new ToolSelector(name); @@ -249,7 +264,7 @@ void TTool::bind(int targetType) { for(int i = 0; i < targetsCount; ++i) if (targetType & targets[i]) - toolTable->insert( + toolTable().insert( std::make_pair(std::make_pair(name, targets[i]), this)); } @@ -590,8 +605,8 @@ TImage *TTool::touchImage() { //----------------------------------------------------------------------------- void TTool::updateToolsPropertiesTranslation() { - ToolTable::iterator tt, tEnd(toolTable->end()); - for (tt = toolTable->begin(); tt != tEnd; ++tt) + ToolTable::iterator tt, tEnd(toolTable().end()); + for (tt = toolTable().begin(); tt != tEnd; ++tt) tt->second->updateTranslation(); } @@ -894,6 +909,20 @@ QString TTool::updateEnabled() { // See the overridden function EditTool::updateEnabled() for the Animate Tool QString TTool::updateEnabled(int rowIndex, int columnIndex) { + static const struct LevelTypeDesc { + int levelType; + ToolTargetType targetType; + const char *name; + } types[] = { + { PLI_XSHLEVEL , VectorImage , QT_TR_NOOP("Toonz Vector Level") }, + { TZP_XSHLEVEL , ToonzImage , QT_TR_NOOP("Toonz Raster Level") }, + { OVL_XSHLEVEL , RasterImage , QT_TR_NOOP("Raster Level") }, + { MESH_XSHLEVEL , MeshImage , QT_TR_NOOP("Mesh Level") }, + { META_XSHLEVEL , MetaImage , QT_TR_NOOP("Assistants Level") }, + }; + static const int typesCnt = sizeof(types)/sizeof(*types); + + // Disable every tool during playback if (m_application->getCurrentFrame()->isPlaying()) return (enable(false), QString()); @@ -948,16 +977,15 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { // a version of the same tool that does { TTool *tool = this; - - if ((levelType == PLI_XSHLEVEL) && !(targetType & VectorImage)) - tool = TTool::getTool(m_name, VectorImage); - else if ((levelType == TZP_XSHLEVEL) && !(targetType & ToonzImage)) - tool = TTool::getTool(m_name, ToonzImage); - else if ((levelType == OVL_XSHLEVEL) && !(targetType & RasterImage)) - tool = TTool::getTool(m_name, RasterImage); - else if ((levelType == MESH_XSHLEVEL) && !(targetType & MeshImage)) - tool = TTool::getTool(m_name, MeshImage); - + for(int i = 0; i < typesCnt; ++i) { + if ( levelType == types[i].levelType + && !(targetType & types[i].targetType) ) + { + tool = getTool(m_name, types[i].targetType); + break; + } + } + if (tool && tool != this && tool->getTargetType() != TTool::NoTarget) return tool->updateEnabled(); } @@ -1052,30 +1080,50 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) { // Check against level types { - if ((levelType == PLI_XSHLEVEL) && !(targetType & VectorImage)) - return ( - enable(false), - QObject::tr("The current tool cannot be used on a Vector Level.")); - - if ((levelType == TZP_XSHLEVEL) && !(targetType & ToonzImage)) - return ( - enable(false), - QObject::tr("The current tool cannot be used on a Toonz Level.")); - - if ((levelType == OVL_XSHLEVEL) && !(targetType & RasterImage)) - return ( - enable(false), - QObject::tr("The current tool cannot be used on a Raster Level.")); - - if ((levelType == MESH_XSHLEVEL) && !(targetType & MeshImage)) - return ( - enable(false), - QObject::tr("The current tool cannot be used on a Mesh Level.")); - - if ((levelType == META_XSHLEVEL) && !(targetType & MetaImage)) - return ( - enable(false), - QObject::tr("The current tool cannot be used on a Assistants (Meta) Level.")); + for(int i = 0; i < typesCnt; ++i) { + if (levelType != types[i].levelType) continue; + if (targetType & types[i].targetType) break; + + // if we are here, then the level is not being supported by the tool + // prepeare a detailed message about it + + QString message = QObject::tr("The current tool cannot be used on: ") + + QObject::tr(types[i].name); + + QString allowedLevels; + + // if current tool is dummy, then this->getName() will not informative + // we need to know the requested tool name instead + if (TApplication *app = getApplication()) + if (ToolHandle *th = app->getCurrentTool()) { + std::string name = th->getRequestedToolName().toStdString(); + if (!name.empty()) { + for(int i = 0; i < typesCnt; ++i) { + TTool *tool = getTool(name, types[i].targetType); + if (tool && (tool->getTargetType() & types[i].targetType)) { + if (!allowedLevels.isEmpty()) + allowedLevels += QObject::tr(", "); + allowedLevels += QObject::tr(types[i].name); + } + } + } + } + + if (allowedLevels.isEmpty()) { + if ( Preferences::instance()->isAutoCreateEnabled() + && (targetType & EmptyTarget) ) + message += "\n\n" + QObject::tr("You can use this tool on empty cell in Xsheet/Timeline."); + } else { + message += "\n\n" + QObject::tr("You can use this tool with: ") + + "\n" + allowedLevels; + if ( Preferences::instance()->isAutoCreateEnabled() + && (targetType & EmptyTarget) ) + message += "\n\n" + QObject::tr("Or just choose an empty cell in Xsheet/Timeline."); + } + + enable(false); + return message; + } } // Check against impossibly traceable movements on the column diff --git a/toonz/sources/toonz/toolbar.cpp b/toonz/sources/toonz/toolbar.cpp index 39b51e8..ec9fc2c 100755 --- a/toonz/sources/toonz/toolbar.cpp +++ b/toonz/sources/toonz/toolbar.cpp @@ -287,8 +287,7 @@ void Toolbar::hideEvent(QHideEvent *e) { void Toolbar::onToolChanged() { ToolHandle *toolHandle = TApp::instance()->getCurrentTool(); - TTool *tool = toolHandle->getTool(); - std::string toolName = tool->getName(); + std::string toolName = toolHandle->getRequestedToolName().toStdString(); QAction *act = CommandManager::instance()->getAction(toolName.c_str()); if (!act || act->isChecked()) return; act->setChecked(true);