diff --git a/stuff/config/qss/Dark/Dark.qss b/stuff/config/qss/Dark/Dark.qss index dbbc76dc..d76c1e37 100644 --- a/stuff/config/qss/Dark/Dark.qss +++ b/stuff/config/qss/Dark/Dark.qss @@ -2255,6 +2255,14 @@ Ruler { #RenameCellField { padding-right: 3; } +#XsheetBreadcrumbs { + padding: 0; + margin: 0; + border-bottom: 1 solid #111111; +} +#XsheetBreadcrumbs::separator:horizontal { + margin: 0 0 0 2; +} /* xsheetColumnHeader (Context Menus) ----------------------------------------------------------------------------- */ #xsheetColumnAreaMenu_Preview { diff --git a/stuff/config/qss/Darker/Darker.qss b/stuff/config/qss/Darker/Darker.qss index 95123741..250fc6ac 100644 --- a/stuff/config/qss/Darker/Darker.qss +++ b/stuff/config/qss/Darker/Darker.qss @@ -2255,6 +2255,14 @@ Ruler { #RenameCellField { padding-right: 3; } +#XsheetBreadcrumbs { + padding: 0; + margin: 0; + border-bottom: 1 solid #060606; +} +#XsheetBreadcrumbs::separator:horizontal { + margin: 0 0 0 2; +} /* xsheetColumnHeader (Context Menus) ----------------------------------------------------------------------------- */ #xsheetColumnAreaMenu_Preview { diff --git a/stuff/config/qss/Light/Light.qss b/stuff/config/qss/Light/Light.qss index 78e57c29..e4237725 100644 --- a/stuff/config/qss/Light/Light.qss +++ b/stuff/config/qss/Light/Light.qss @@ -2255,6 +2255,14 @@ Ruler { #RenameCellField { padding-right: 3; } +#XsheetBreadcrumbs { + padding: 0; + margin: 0; + border-bottom: 1 solid #a8a8a8; +} +#XsheetBreadcrumbs::separator:horizontal { + margin: 0 0 0 2; +} /* xsheetColumnHeader (Context Menus) ----------------------------------------------------------------------------- */ #xsheetColumnAreaMenu_Preview { diff --git a/stuff/config/qss/Medium/Medium.qss b/stuff/config/qss/Medium/Medium.qss index ca385eac..988fcb9a 100644 --- a/stuff/config/qss/Medium/Medium.qss +++ b/stuff/config/qss/Medium/Medium.qss @@ -2255,6 +2255,14 @@ Ruler { #RenameCellField { padding-right: 3; } +#XsheetBreadcrumbs { + padding: 0; + margin: 0; + border-bottom: 1 solid #2c2c2c; +} +#XsheetBreadcrumbs::separator:horizontal { + margin: 0 0 0 2; +} /* xsheetColumnHeader (Context Menus) ----------------------------------------------------------------------------- */ #xsheetColumnAreaMenu_Preview { diff --git a/stuff/config/qss/Medium/less/layouts/xsheet.less b/stuff/config/qss/Medium/less/layouts/xsheet.less index a6511cf1..2c50d3f5 100644 --- a/stuff/config/qss/Medium/less/layouts/xsheet.less +++ b/stuff/config/qss/Medium/less/layouts/xsheet.less @@ -40,6 +40,15 @@ padding-right: 3; } +#XsheetBreadcrumbs { + padding: 0; + margin: 0; + border-bottom: 1 solid @accent; + &::separator:horizontal { + margin: 0 0 0 2; + } +} + /* xsheetColumnHeader (Context Menus) ----------------------------------------------------------------------------- */ diff --git a/stuff/config/qss/Neutral/Neutral.qss b/stuff/config/qss/Neutral/Neutral.qss index 9923657b..622e986e 100644 --- a/stuff/config/qss/Neutral/Neutral.qss +++ b/stuff/config/qss/Neutral/Neutral.qss @@ -2255,6 +2255,14 @@ Ruler { #RenameCellField { padding-right: 3; } +#XsheetBreadcrumbs { + padding: 0; + margin: 0; + border-bottom: 1 solid #5a5a5a; +} +#XsheetBreadcrumbs::separator:horizontal { + margin: 0 0 0 2; +} /* xsheetColumnHeader (Context Menus) ----------------------------------------------------------------------------- */ #xsheetColumnAreaMenu_Preview { diff --git a/toonz/sources/include/toonz/childstack.h b/toonz/sources/include/toonz/childstack.h index 6ce9145a..e4ee816f 100644 --- a/toonz/sources/include/toonz/childstack.h +++ b/toonz/sources/include/toonz/childstack.h @@ -5,6 +5,8 @@ #include "tcommon.h" +#include "toonz/txshchildlevel.h" + #undef DVAPI #undef DVVAR #ifdef TOONZLIB_EXPORTS @@ -36,11 +38,29 @@ class TXshChildLevel; properties, getAncestor(), getAncestorCount(), getAncestorAffine(), getTopXsheet(). */ + //============================================================================= +//! The Node class is a container of element necessary to define a sub-xsheet. +/*! + The class contain a pointer to \b TXsheet \b m_xsheet, two integer to + identify column + \b m_col and row \b m_row, a \b TXshChildLevelP \b m_cl and a bool \b + m_justCreated. +*/ + +class AncestorNode { +public: + TXsheet *m_xsheet; + int m_row, m_col; + std::map m_rowTable; + TXshChildLevelP m_cl; + bool m_justCreated; + AncestorNode() + : m_xsheet(0), m_row(0), m_col(0), m_rowTable(), m_justCreated(false) {} +}; class DVAPI ChildStack { - class Node; - std::vector m_stack; + std::vector m_stack; TXsheet *m_xsheet; ToonzScene *m_scene; @@ -114,6 +134,8 @@ visible in \b row. */ bool getAncestorAffine(TAffine &aff, int row) const; + AncestorNode *getAncestorInfo(int ancestorDepth); + private: // not implemented ChildStack(const ChildStack &); diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index ef0f95b0..2e7bd4a9 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -407,6 +407,9 @@ public: bool isShowQuickToolbarEnabled() const { return getBoolValue(showQuickToolbar); } + bool isShowXsheetBreadcrumbsEnabled() const { + return getBoolValue(showXsheetBreadcrumbs); + } bool isExpandFunctionHeaderEnabled() const { return getBoolValue(expandFunctionHeader); } diff --git a/toonz/sources/include/toonz/preferencesitemids.h b/toonz/sources/include/toonz/preferencesitemids.h index 7f558b91..48118f97 100644 --- a/toonz/sources/include/toonz/preferencesitemids.h +++ b/toonz/sources/include/toonz/preferencesitemids.h @@ -136,6 +136,7 @@ enum PreferencesItemId { inputCellsWithoutDoubleClickingEnabled, shortcutCommandsWhileRenamingCellEnabled, showQuickToolbar, + showXsheetBreadcrumbs, expandFunctionHeader, showColumnNumbers, parentColorsInXsheetColumn, diff --git a/toonz/sources/toonz/CMakeLists.txt b/toonz/sources/toonz/CMakeLists.txt index ea19b402..8fd59d95 100644 --- a/toonz/sources/toonz/CMakeLists.txt +++ b/toonz/sources/toonz/CMakeLists.txt @@ -142,6 +142,7 @@ set(MOC_HEADERS cameracapturelevelcontrol.h navtageditorpopup.h viewereventlogpopup.h + xshbreadcrumbs.h ) set(HEADERS @@ -390,6 +391,7 @@ set(SOURCES cameracapturelevelcontrol.cpp navtageditorpopup.cpp viewereventlogpopup.cpp + xshbreadcrumbs.cpp ) if(WITH_TRANSLATION) diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index 1d1d9d7e..ab7267dd 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -2481,6 +2481,8 @@ void MainWindow::defineActions() { "view_file"); createRightClickMenuAction(MI_ToggleQuickToolbar, QT_TR_NOOP("Toggle Quick Toolbar"), ""); + createRightClickMenuAction(MI_ToggleXsheetBreadcrumbs, + QT_TR_NOOP("Toggle Sub-Scene Navigation Bar"), ""); createRightClickMenuAction(MI_ToggleXsheetCameraColumn, QT_TR_NOOP("Show/Hide Camera Column"), ""); createRightClickMenuAction(MI_SetKeyframes, QT_TR_NOOP("&Set Key"), "Z", diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 6723c5be..2a48b3e5 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -328,6 +328,7 @@ #define MI_UnlockAllColumns "MI_UnlockAllColumns" #define MI_ToggleColumnLocks "MI_ToggleColumnLocks" #define MI_ToggleQuickToolbar "MI_ToggleQuickToolbar" +#define MI_ToggleXsheetBreadcrumbs "MI_ToggleXsheetBreadcrumbs" #define MI_FoldColumns "MI_FoldColumns" #define MI_ToggleXsheetCameraColumn "MI_ToggleXsheetCameraColumn" #define MI_ToggleCurrentTimeIndicator "MI_ToggleCurrentTimeIndicator" diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index 318a366e..8cb138df 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -735,6 +735,13 @@ void PreferencesPopup::onShowQuickToolbarClicked() { //----------------------------------------------------------------------------- +void PreferencesPopup::onShowXsheetBreadcrumbsClicked() { + TApp::instance()->getCurrentScene()->notifyPreferenceChanged( + "XsheetBreadcrumbs"); +} + +//----------------------------------------------------------------------------- + void PreferencesPopup::onModifyExpressionOnMovingReferencesChanged() { TApp::instance()->getCurrentScene()->notifyPreferenceChanged( "modifyExpressionOnMovingReferences"); @@ -1375,8 +1382,8 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { {shortcutCommandsWhileRenamingCellEnabled, tr("Enable Tahoma2D Commands' Shortcut Keys While Renaming Cell")}, {showQuickToolbar, tr("Show Quick Toolbar")}, - {expandFunctionHeader, - tr("Expand Function Editor Header to Match Quick Toolbar Height*")}, + {showXsheetBreadcrumbs, tr("Show Sub-Scene Navigation Bar")}, + {expandFunctionHeader, tr("Expand Function Editor Header to Match Xsheet Header Height*")}, {showColumnNumbers, tr("Show Column Numbers")}, {parentColorsInXsheetColumn, tr("Show Column Parent's Color in the Xsheet")}, @@ -2126,8 +2133,12 @@ QWidget* PreferencesPopup::createXsheetPage() { insertUI(useArrowKeyToShiftCellSelection, lay); insertUI(inputCellsWithoutDoubleClickingEnabled, lay); insertUI(shortcutCommandsWhileRenamingCellEnabled, lay); - QGridLayout* xshToolbarLay = insertGroupBoxUI(showQuickToolbar, lay); - { insertUI(expandFunctionHeader, xshToolbarLay); } + QGridLayout* xshToolbarLay = insertGroupBox(tr("Scene Tools"), lay); + { + insertUI(showQuickToolbar, xshToolbarLay); + insertUI(showXsheetBreadcrumbs, xshToolbarLay); + insertUI(expandFunctionHeader, xshToolbarLay); + } insertUI(showColumnNumbers, lay); insertUI(parentColorsInXsheetColumn, lay); insertUI(highlightLineEverySecond, lay); @@ -2150,6 +2161,8 @@ QWidget* PreferencesPopup::createXsheetPage() { &PreferencesPopup::onShowKeyframesOnCellAreaChanged); m_onEditedFuncMap.insert(showQuickToolbar, &PreferencesPopup::onShowQuickToolbarClicked); + m_onEditedFuncMap.insert(showXsheetBreadcrumbs, + &PreferencesPopup::onShowXsheetBreadcrumbsClicked); return widget; } diff --git a/toonz/sources/toonz/preferencespopup.h b/toonz/sources/toonz/preferencespopup.h index 4b3c5c04..c4a8f453 100644 --- a/toonz/sources/toonz/preferencespopup.h +++ b/toonz/sources/toonz/preferencespopup.h @@ -165,6 +165,7 @@ private: // Xsheet void onShowKeyframesOnCellAreaChanged(); void onShowQuickToolbarClicked(); + void onShowXsheetBreadcrumbsClicked(); // Animation void onModifyExpressionOnMovingReferencesChanged(); // Preview diff --git a/toonz/sources/toonz/xshbreadcrumbs.cpp b/toonz/sources/toonz/xshbreadcrumbs.cpp new file mode 100644 index 00000000..09b29a50 --- /dev/null +++ b/toonz/sources/toonz/xshbreadcrumbs.cpp @@ -0,0 +1,345 @@ +#include "xshbreadcrumbs.h" + +// Tnz6 includes +#include "xsheetviewer.h" +#include "tapp.h" +#include "menubarcommandids.h" + +// TnzLib includes +#include "toonz/preferences.h" +#include "toonz/toonzscene.h" +#include "toonz/tscenehandle.h" +#include "toonz/childstack.h" + +#include "toonzqt/menubarcommand.h" +#include "toonzqt/tselectionhandle.h" +#include "toonzqt/dvscrollwidget.h" + +// Qt includes +#include +#include +#include + +//============================================================================= + +BreadcrumbClickableLabel::BreadcrumbClickableLabel(QString labelName, + QWidget *parent, + Qt::WindowFlags f) + : QLabel(labelName, parent) { + setStyleSheet("text-decoration: underline;"); +} + +BreadcrumbClickableLabel::~BreadcrumbClickableLabel() {} + +void BreadcrumbClickableLabel::mousePressEvent(QMouseEvent *event) { + emit clicked(); +} + +namespace XsheetGUI { +//============================================================================= + +Breadcrumb::Breadcrumb(CrumbType crumbType, QString crumbName, + CrumbWidgetType crumbWidgetType, QWidget *parent) + : QWidget(parent) + , m_crumbType(crumbType) + , m_col(-1) + , m_colList(0) + , m_distanceFromCurrent(0) { + if (crumbWidgetType == CrumbWidgetType::LABEL) + m_crumbWidget = new QLabel(crumbName, parent); + else if (crumbWidgetType == CrumbWidgetType::BUTTON) { + BreadcrumbClickableLabel *crumbButton = + new BreadcrumbClickableLabel(crumbName, parent); + m_crumbWidget = crumbButton; + connect(crumbButton, SIGNAL(clicked()), this, SLOT(onButtonClicked())); + } else if (crumbWidgetType == CrumbWidgetType::COMBOBOX) { + QComboBox *crumbCB = new QComboBox(parent); + m_crumbWidget = crumbCB; + connect(crumbCB, SIGNAL(currentIndexChanged(int)), this, + SLOT(onComboBoxIndexChanged(int))); + } +} + +void Breadcrumb::onButtonClicked() { + TApp *app = TApp::instance(); + ToonzScene *scene = app->getCurrentScene()->getScene(); + ChildStack *childStack = scene->getChildStack(); + + if (m_crumbType == CrumbType::CHILD) { + TXsheet *xsh = childStack->getXsheet(); + + int r = app->getCurrentFrame()->getFrameIndex(); + int r0, r1; + xsh->getCellRange(m_col, r0, r1); + if (r1 < r0) r1 = r0; + if (r < r0) + r = r0; + else if (r > r1) + r = r1; + else { + TXshCell cell = xsh->getCell(r, m_col); + while (cell.isEmpty()) cell = xsh->getCell(++r, m_col); + } + + app->getCurrentColumn()->setColumnIndex(m_col); + app->getCurrentFrame()->setFrameIndex(r); + app->getCurrentSelection()->getSelection()->selectNone(); + + QAction *openChildAction = + CommandManager::instance()->getAction(MI_OpenChild); + if (!openChildAction) return; + openChildAction->trigger(); + } else if (m_crumbType == CrumbType::ANCESTOR) { + QAction *closeChildAction = + CommandManager::instance()->getAction(MI_CloseChild); + if (!closeChildAction) return; + TUndoManager::manager()->beginBlock(); + for (int i = 0; i > m_distanceFromCurrent; i--) closeChildAction->trigger(); + TUndoManager::manager()->endBlock(); + } +} + +void Breadcrumb::onComboBoxIndexChanged(int index) { + if (m_crumbType != CrumbType::CHILD || !index) return; + + TApp *app = TApp::instance(); + ToonzScene *scene = app->getCurrentScene()->getScene(); + ChildStack *childStack = scene->getChildStack(); + TXsheet *xsh = childStack->getXsheet(); + + int col = m_colList[index - 1]; + int r = app->getCurrentFrame()->getFrameIndex(); + int r0, r1; + xsh->getCellRange(col, r0, r1); + if (r1 < r0) r1 = r0; + if (r < r0) + r = r0; + else if (r > r1) + r = r1; + else { + TXshCell cell = xsh->getCell(r, col); + while (cell.isEmpty()) cell = xsh->getCell(++r, col); + } + + app->getCurrentColumn()->setColumnIndex(col); + app->getCurrentFrame()->setFrameIndex(r); + app->getCurrentSelection()->getSelection()->selectNone(); + + QAction *openChildAction = + CommandManager::instance()->getAction(MI_OpenChild); + if (!openChildAction) return; + openChildAction->trigger(); +} + +//============================================================================= +// BreadcrumbArea +//----------------------------------------------------------------------------- + +BreadcrumbArea::BreadcrumbArea(XsheetViewer *parent, Qt::WindowFlags flags) + : m_viewer(parent) { + setObjectName("cornerWidget"); + setFixedHeight(29); + setObjectName("XsheetBreadcrumbs"); + + m_breadcrumbWidgets.clear(); + updateBreadcrumbs(); +} + +//----------------------------------------------------------------------------- + +void BreadcrumbArea::showBreadcrumbs(bool show) { + show ? this->show() : this->hide(); +} + +//----------------------------------------------------------------------------- + +void BreadcrumbArea::toggleBreadcrumbArea() { + bool breadcrumbsEnabled = + Preferences::instance()->isShowXsheetBreadcrumbsEnabled(); + Preferences::instance()->setValue(showXsheetBreadcrumbs, !breadcrumbsEnabled); + TApp::instance()->getCurrentScene()->notifyPreferenceChanged( + "XsheetBreadcrumbs"); +} + +//----------------------------------------------------------------------------- + +void BreadcrumbArea::showEvent(QShowEvent *e) { + TApp *app = TApp::instance(); + connect(app->getCurrentXsheet(), SIGNAL(xsheetSwitched()), this, + SLOT(updateBreadcrumbs())); + connect(app->getCurrentXsheet(), SIGNAL(xsheetChanged()), this, + SLOT(updateBreadcrumbs())); + + updateBreadcrumbs(); +} + +//----------------------------------------------------------------------------- + +void BreadcrumbArea::hideEvent(QHideEvent *e) { + TApp *app = TApp::instance(); + disconnect(app->getCurrentXsheet(), SIGNAL(xsheetSwitched()), this, + SLOT(updateBreadcrumbs())); + disconnect(app->getCurrentXsheet(), SIGNAL(xsheetChanged()), this, + SLOT(updateBreadcrumbs())); +} + +//----------------------------------------------------------------------------- + +void BreadcrumbArea::updateBreadcrumbs() { + if (isHidden()) return; + + // Remove the current layout + QLayout *currentLayout = layout(); + + if (currentLayout) { + QLayoutItem *item; + while ((item = currentLayout->takeAt(0)) != nullptr) { + currentLayout->removeWidget(item->widget()); + item->widget()->deleteLater(); + } + delete currentLayout; + } + + m_breadcrumbWidgets.clear(); + + // Rebuild the breadcrumb widget list + TApp *app = TApp::instance(); + + ToonzScene *scene = app->getCurrentScene()->getScene(); + ChildStack *childStack = scene->getChildStack(); + TXsheet *xsh = childStack->getXsheet(); + int ancestorCount = childStack->getAncestorCount(); + + // Look for any sub-xsheets in current xsheet + std::vector childCol; + for (int col = 0; col < xsh->getColumnCount(); col++) { + if (xsh->isColumnEmpty(col)) continue; + + int r0, r1; + xsh->getCellRange(col, r0, r1); + TXshCell cell = xsh->getCell(r0, col); + TXshLevel *xl = cell.m_level.getPointer(); + if (!xl) continue; + TXshChildLevel *cl = xl->getChildLevel(); + if (!cl) continue; + childCol.push_back(col); + } + + QString separator = tr(" > "); + QString separator2 = tr(" | "); + + Breadcrumb *crumb; + + QString childName; + if (childCol.size() == 1) { + TStageObjectId columnId = TStageObjectId::ColumnId(childCol[0]); + TStageObject *columnObject = xsh->getStageObject(columnId); + childName = QString::fromStdString(columnObject->getName()); + + crumb = new Breadcrumb(CrumbType::CHILD, childName, CrumbWidgetType::BUTTON, + this); + crumb->setColumnNumber(childCol[0]); + m_breadcrumbWidgets.push_back(crumb); + } else if (childCol.size() > 1) { + crumb = + new Breadcrumb(CrumbType::CHILD, 0, CrumbWidgetType::COMBOBOX, this); + crumb->setColumnNumberList(childCol); + QComboBox *childCB = dynamic_cast(crumb->getCrumbWidget()); + childCB->blockSignals(true); + childCB->addItem(tr("---")); + for (int i = 0; i < childCol.size(); i++) { + TStageObjectId columnId = TStageObjectId::ColumnId(childCol[i]); + TStageObject *columnObject = xsh->getStageObject(columnId); + childName = QString::fromStdString(columnObject->getName()); + childCB->addItem(childName); + } + childCB->blockSignals(false); + m_breadcrumbWidgets.push_back(crumb); + } + + if (m_breadcrumbWidgets.size()) + m_breadcrumbWidgets.push_back(new Breadcrumb( + CrumbType::SEPARATOR, separator2, CrumbWidgetType::LABEL, this)); + + QString ancestorName; + for (int i = ancestorCount; i > 0; i--) { + AncestorNode *ancestor = childStack->getAncestorInfo(i - 1); + if (!ancestor || !ancestor->m_cl || !ancestor->m_xsheet) break; + + TStageObjectId columnId = TStageObjectId::ColumnId(ancestor->m_col); + TStageObject *columnObject = ancestor->m_xsheet->getStageObject(columnId); + ancestorName = QString::fromStdString(columnObject->getName()); + + // Add label for current xsheet, button for everything else + if (i == ancestorCount) + crumb = new Breadcrumb(CrumbType::ANCESTOR, ancestorName, + CrumbWidgetType::LABEL, this); + else { + crumb = new Breadcrumb(CrumbType::ANCESTOR, ancestorName, + CrumbWidgetType::BUTTON, this); + crumb->setDistanceFromCurrent(i - ancestorCount); + } + m_breadcrumbWidgets.push_back(crumb); + + m_breadcrumbWidgets.push_back(new Breadcrumb( + CrumbType::SEPARATOR, separator, CrumbWidgetType::LABEL, this)); + } + + ancestorName = tr("Main"); + if (!ancestorCount) + crumb = new Breadcrumb(CrumbType::ANCESTOR, ancestorName, + CrumbWidgetType::LABEL, this); + else { + crumb = new Breadcrumb(CrumbType::ANCESTOR, ancestorName, + CrumbWidgetType::BUTTON, this); + crumb->setDistanceFromCurrent(-ancestorCount); + } + m_breadcrumbWidgets.push_back(crumb); + + // Now let's put everything in a layout + m_breadcrumbLayout = new QHBoxLayout(); + m_breadcrumbLayout->setMargin(0); + m_breadcrumbLayout->setSpacing(0); + { + if (!m_viewer->orientation()->isVerticalTimeline()) + m_breadcrumbLayout->addSpacing(220); + m_breadcrumbLayout->addWidget(new QLabel(tr("Scene Depth:"), this), 0, + Qt::AlignCenter); + m_breadcrumbLayout->addSpacing(5); + + std::vector::reverse_iterator rit; + for (rit = m_breadcrumbWidgets.rbegin(); rit != m_breadcrumbWidgets.rend(); + ++rit) { + m_breadcrumbLayout->addWidget((*rit)->getCrumbWidget(), 0, + Qt::AlignCenter); + } + } + m_breadcrumbLayout->addStretch(1); + + QHBoxLayout *hLayout = new QHBoxLayout; + hLayout->setMargin(0); + hLayout->setSpacing(0); + setLayout(hLayout); + + DvScrollWidget *scrollWidget = new DvScrollWidget; + hLayout->addWidget(scrollWidget); + + QWidget *crumbContainer = new QWidget; + scrollWidget->setWidget(crumbContainer); + + crumbContainer->setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed); + crumbContainer->setFixedHeight(24); + crumbContainer->setLayout(m_breadcrumbLayout); +} + +//============================================================ + +class ToggleXsheetBreadcrumbsCommand final : public MenuItemHandler { +public: + ToggleXsheetBreadcrumbsCommand() + : MenuItemHandler(MI_ToggleXsheetBreadcrumbs) {} + void execute() override { BreadcrumbArea::toggleBreadcrumbArea(); } +} ToggleXsheetBreadcrumbsCommand; + +} // namespace XsheetGUI diff --git a/toonz/sources/toonz/xshbreadcrumbs.h b/toonz/sources/toonz/xshbreadcrumbs.h new file mode 100644 index 00000000..f972eeda --- /dev/null +++ b/toonz/sources/toonz/xshbreadcrumbs.h @@ -0,0 +1,97 @@ +#pragma once + +#ifndef XSHBREADCRUMBS_H +#define XSHBREADCRUMBS_H + +#include +#include +#include + +#include "toonz/txsheet.h" + +//----------------------------------------------------------------------------- + +// forward declaration +class XsheetViewer; +class QAction; + +//----------------------------------------------------------------------------- + +class BreadcrumbClickableLabel : public QLabel { + Q_OBJECT + +public: + BreadcrumbClickableLabel(QString labelName, QWidget *parent = Q_NULLPTR, + Qt::WindowFlags f = Qt::WindowFlags()); + ~BreadcrumbClickableLabel(); + +signals: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *event); +}; + +//----------------------------------------------------------------------------- + +namespace XsheetGUI { + +enum CrumbType { SEPARATOR = 0, CURRENT, ANCESTOR, CHILD }; +enum CrumbWidgetType { LABEL = 0, BUTTON, COMBOBOX }; + +class Breadcrumb : public QWidget { + Q_OBJECT + + QWidget *m_crumbWidget; + CrumbType m_crumbType; + int m_col; + std::vector m_colList; + int m_distanceFromCurrent; + +public: + Breadcrumb(CrumbType crumbType, QString crumbName, + CrumbWidgetType crumbWidgetType, QWidget *parent); + ~Breadcrumb() {} + + void setColumnNumber(int col) { m_col = col; } + void setColumnNumberList(std::vector colList) { m_colList = colList; } + void setDistanceFromCurrent(int distance) { + m_distanceFromCurrent = distance; + } + + QWidget *getCrumbWidget() { return m_crumbWidget; } + +public slots: + void onButtonClicked(); + void onComboBoxIndexChanged(int); +}; + +//============================================================================= +// BreadcrumbArea +//----------------------------------------------------------------------------- + +class BreadcrumbArea final : public QFrame { + Q_OBJECT + + XsheetViewer *m_viewer; + std::vector m_breadcrumbWidgets; + + QHBoxLayout *m_breadcrumbLayout; + +public: + BreadcrumbArea(XsheetViewer *parent = 0, + Qt::WindowFlags flags = Qt::WindowFlags()); + static void toggleBreadcrumbArea(); + void showBreadcrumbs(bool show); + +protected: + void showEvent(QShowEvent *e) override; + void hideEvent(QHideEvent *e) override; + +public slots: + void updateBreadcrumbs(); +}; + +} // namespace XsheetGUI + +#endif // XSHBREADCRUMBS_H diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index b8b4ade7..b73e2e3f 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -3229,6 +3229,7 @@ void ColumnArea::contextMenuEvent(QContextMenuEvent *event) { } menu.addSeparator(); menu.addAction(cmdManager->getAction(MI_ToggleQuickToolbar)); + menu.addAction(cmdManager->getAction(MI_ToggleXsheetBreadcrumbs)); QAction *flipOrientation = new QAction(tr("Toggle Orientation"), this); diff --git a/toonz/sources/toonz/xsheetviewer.cpp b/toonz/sources/toonz/xsheetviewer.cpp index e732c253..bbad104b 100644 --- a/toonz/sources/toonz/xsheetviewer.cpp +++ b/toonz/sources/toonz/xsheetviewer.cpp @@ -57,12 +57,13 @@ TEnv::IntVar FrameDisplayStyleInXsheetRowArea( namespace XsheetGUI { //----------------------------------------------------------------------------- -const int ColumnWidth = 74; -const int RowHeight = 20; -const int SCROLLBAR_WIDTH = 16; -const int TOOLBAR_HEIGHT = 29; -const int ZOOM_FACTOR_MAX = 100; -const int ZOOM_FACTOR_MIN = 20; +const int ColumnWidth = 74; +const int RowHeight = 20; +const int SCROLLBAR_WIDTH = 16; +const int TOOLBAR_HEIGHT = 29; +const int BREADCRUMB_HEIGHT = 29; +const int ZOOM_FACTOR_MAX = 100; +const int ZOOM_FACTOR_MIN = 20; } // namespace XsheetGUI //============================================================================= @@ -255,6 +256,12 @@ XsheetViewer::XsheetViewer(QWidget *parent, Qt::WindowFlags flags) m_toolbar = new XsheetGUI::QuickToolbar(this, Qt::WindowFlags(), true); m_toolbarScrollArea->setWidget(m_toolbar); + m_breadcrumbArea = new XsheetGUI::BreadcrumbArea(this, Qt::WindowFlags()); + m_breadcrumbScrollArea = new XsheetScrollArea(this); + m_breadcrumbScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_breadcrumbScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_breadcrumbScrollArea->setWidget(m_breadcrumbArea); + m_noteArea = new XsheetGUI::NoteArea(this); m_noteScrollArea = new XsheetScrollArea(this); m_noteScrollArea->setObjectName("xsheetArea"); @@ -416,6 +423,31 @@ void XsheetViewer::positionSections() { } } else { m_toolbar->showToolbar(false); + m_toolbarScrollArea->setGeometry(0, 0, 0, 0); + } + + if (Preferences::instance()->isShowXsheetBreadcrumbsEnabled()) { + m_breadcrumbArea->showBreadcrumbs(true); + int w = visibleRegion().boundingRect().width(); + if (o->isVerticalTimeline()) + m_breadcrumbScrollArea->setGeometry(0, headerFrame.from(), w, + XsheetGUI::BREADCRUMB_HEIGHT); + else + m_breadcrumbScrollArea->setGeometry(0, headerLayer.from(), w, + XsheetGUI::BREADCRUMB_HEIGHT); + m_breadcrumbArea->setFixedWidth(w); + if (o->isVerticalTimeline()) { + headerFrame = headerFrame.adjusted(XsheetGUI::BREADCRUMB_HEIGHT, + XsheetGUI::BREADCRUMB_HEIGHT); + bodyFrame = bodyFrame.adjusted(XsheetGUI::BREADCRUMB_HEIGHT, 0); + } else { + headerLayer = headerLayer.adjusted(XsheetGUI::BREADCRUMB_HEIGHT, + XsheetGUI::BREADCRUMB_HEIGHT); + bodyLayer = bodyLayer.adjusted(XsheetGUI::BREADCRUMB_HEIGHT, 0); + } + } else { + m_breadcrumbArea->showBreadcrumbs(false); + m_breadcrumbScrollArea->setGeometry(0, 0, 0, 0); } m_noteScrollArea->setGeometry(o->frameLayerRect(headerFrame, headerLayer)); @@ -1464,7 +1496,7 @@ void XsheetViewer::onXsheetChanged() { //----------------------------------------------------------------------------- void XsheetViewer::onPreferenceChanged(const QString &prefName) { - if (prefName == "QuickToolbar") { + if (prefName == "QuickToolbar" || prefName == "XsheetBreadcrumbs") { positionSections(); refreshContentSize(0, 0); } else if (prefName == "XsheetCamera") { diff --git a/toonz/sources/toonz/xsheetviewer.h b/toonz/sources/toonz/xsheetviewer.h index 77526a57..538d98d3 100644 --- a/toonz/sources/toonz/xsheetviewer.h +++ b/toonz/sources/toonz/xsheetviewer.h @@ -16,6 +16,7 @@ #include "saveloadqsettings.h" #include "toonzqt/spreadsheetviewer.h" #include "orientation.h" +#include "xshbreadcrumbs.h" #include using boost::optional; @@ -581,12 +582,15 @@ class XsheetViewer final : public QFrame, public SaveLoadQSettings { XsheetScrollArea *m_rowScrollArea; XsheetScrollArea *m_noteScrollArea; XsheetScrollArea *m_toolbarScrollArea; + XsheetScrollArea *m_breadcrumbScrollArea; XsheetGUI::ColumnArea *m_columnArea; XsheetGUI::RowArea *m_rowArea; XsheetGUI::CellArea *m_cellArea; XsheetGUI::NoteArea *m_noteArea; XsheetGUI::QuickToolbar *m_toolbar; + XsheetGUI::BreadcrumbArea *m_breadcrumbArea; + LayerFooterPanel *m_layerFooterPanel; Spreadsheet::FrameScroller m_frameScroller; diff --git a/toonz/sources/toonzlib/childstack.cpp b/toonz/sources/toonzlib/childstack.cpp index 587e7916..a3b260d8 100644 --- a/toonz/sources/toonzlib/childstack.cpp +++ b/toonz/sources/toonzlib/childstack.cpp @@ -3,31 +3,10 @@ #include "toonz/childstack.h" #include "toonz/toonzscene.h" #include "toonz/txsheet.h" -#include "toonz/txshchildlevel.h" #include "toonz/txshcell.h" #include "toonz/txshleveltypes.h" #include "toonz/scenefx.h" -//============================================================================= -//! The Node class is a container of element necessary to define a sub-xsheet. -/*! - The class contain a pointer to \b TXsheet \b m_xsheet, two integer to - identify column - \b m_col and row \b m_row, a \b TXshChildLevelP \b m_cl and a bool \b - m_justCreated. -*/ - -class ChildStack::Node { -public: - TXsheet *m_xsheet; - int m_row, m_col; - std::map m_rowTable; - TXshChildLevelP m_cl; - bool m_justCreated; - Node() - : m_xsheet(0), m_row(0), m_col(0), m_rowTable(), m_justCreated(false) {} -}; - //============================================================================= // ChildStack @@ -64,7 +43,7 @@ bool ChildStack::openChild(int row, int col) { childLevel = cell.m_level->getChildLevel(); if (!childLevel) return false; TXsheet *childXsheet = childLevel->getXsheet(); - Node *node = new Node(); + AncestorNode *node = new AncestorNode(); node->m_row = row; node->m_col = col; node->m_xsheet = m_xsheet; @@ -95,7 +74,7 @@ bool ChildStack::closeChild(int &row, int &col) { ->updateFrameCount(); // non dovrebbe essere necessario, ma non si sa mai int childFrameCount = childXsh->getFrameCount(); - Node *node = m_stack.back(); + AncestorNode *node = m_stack.back(); m_stack.pop_back(); TXsheet *parentXsh = node->m_xsheet; @@ -168,7 +147,7 @@ bool ChildStack::getAncestorAffine(TAffine &aff, int row) const { if (it == m_stack[i]->m_rowTable.end()) break; row = it->second; - Node *node = m_stack[i]; + AncestorNode *node = m_stack[i]; TAffine aff2; if (!getColumnPlacement(aff2, node->m_xsheet, row, node->m_col, false)) return false; @@ -177,3 +156,11 @@ bool ChildStack::getAncestorAffine(TAffine &aff, int row) const { } return true; } + +//----------------------------------------------------------------------------- + +AncestorNode *ChildStack::getAncestorInfo(int ancestorDepth) { + if (ancestorDepth < 0 || ancestorDepth >= m_stack.size()) return nullptr; + + return m_stack[ancestorDepth]; +} diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 97058596..3e823d16 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -588,6 +588,8 @@ void Preferences::definePreferenceItems() { define(shortcutCommandsWhileRenamingCellEnabled, "shortcutCommandsWhileRenamingCellEnabled", QMetaType::Bool, false); define(showQuickToolbar, "showQuickToolbar", QMetaType::Bool, false); + define(showXsheetBreadcrumbs, "showXsheetBreadcrumbs", QMetaType::Bool, + false); define(expandFunctionHeader, "expandFunctionHeader", QMetaType::Bool, false); define(showColumnNumbers, "showColumnNumbers", QMetaType::Bool, false); define(parentColorsInXsheetColumn, "parentColorsInXsheetColumn", diff --git a/toonz/sources/toonzqt/functionviewer.cpp b/toonz/sources/toonzqt/functionviewer.cpp index e2005fef..6b595755 100644 --- a/toonz/sources/toonzqt/functionviewer.cpp +++ b/toonz/sources/toonzqt/functionviewer.cpp @@ -221,12 +221,17 @@ FunctionViewer::FunctionViewer(QWidget *parent, Qt::WindowFlags flags) bool toolBarVisible = Preferences::instance()->isShowQuickToolbarEnabled() && Preferences::instance()->isExpandFunctionHeaderEnabled(); + bool breadcrumbsVisible = + Preferences::instance()->isShowXsheetBreadcrumbsEnabled() && + Preferences::instance()->isExpandFunctionHeaderEnabled(); if (QSpacerItem *spacer = m_leftLayout->itemAt(0)->spacerItem()) { - spacer->changeSize(1, m_spacing + ((toolBarVisible) ? 10 : 0), + spacer->changeSize(1, m_spacing + ((toolBarVisible) ? 10 : 0) + + ((breadcrumbsVisible) ? 10 : 0), QSizePolicy::Fixed, QSizePolicy::Fixed); spacer->invalidate(); } else - m_leftLayout->setSpacing(m_spacing + ((toolBarVisible) ? 30 : 0)); + m_leftLayout->setSpacing(m_spacing + ((toolBarVisible) ? 30 : 0) + + ((breadcrumbsVisible) ? 10 : 0)); if (m_toggleStart == Preferences::FunctionEditorToggle::ShowGraphEditorInPopup) { m_functionGraph->hide(); @@ -511,14 +516,19 @@ void FunctionViewer::toggleMode() { bool toolBarVisible = Preferences::instance()->isShowQuickToolbarEnabled() && Preferences::instance()->isExpandFunctionHeaderEnabled(); + bool breadcrumbsVisible = + Preferences::instance()->isShowXsheetBreadcrumbsEnabled() && + Preferences::instance()->isExpandFunctionHeaderEnabled(); if (QSpacerItem *spacer = m_leftLayout->itemAt(0)->spacerItem()) { - spacer->changeSize(1, m_spacing + ((toolBarVisible) ? 10 : 0), + spacer->changeSize(1, m_spacing + ((toolBarVisible) ? 10 : 0) + + ((breadcrumbsVisible) ? 10 : 0), QSizePolicy::Fixed, QSizePolicy::Fixed); spacer->invalidate(); m_numericalColumns->updateHeaderHeight(); m_leftLayout->setSpacing(0); } else - m_leftLayout->setSpacing(m_spacing + ((toolBarVisible) ? 30 : 0)); + m_leftLayout->setSpacing(m_spacing + ((toolBarVisible) ? 30 : 0) + + ((breadcrumbsVisible) ? 10 : 0)); updateGeometry(); m_toggleStatus = 0; } else { @@ -666,7 +676,9 @@ void FunctionViewer::doSwitchCurrentObject(TStageObject *obj) { //----------------------------------------------------------------------------- void FunctionViewer::onPreferenceChanged(const QString &prefName) { - if (prefName != "XSheetToolbar" && !prefName.isEmpty()) return; + if (prefName != "QuickToolbar" && prefName != "XsheetBreadcrumbs" && + !prefName.isEmpty()) + return; if (!Preferences::instance()->isExpandFunctionHeaderEnabled()) return; if (m_toggleStart == Preferences::FunctionEditorToggle::ShowFunctionSpreadsheetInPopup) @@ -689,16 +701,21 @@ void FunctionViewer::onPreferenceChanged(const QString &prefName) { bool toolBarVisible = Preferences::instance()->isShowQuickToolbarEnabled() && Preferences::instance()->isExpandFunctionHeaderEnabled(); + bool breadcrumbsVisible = + Preferences::instance()->isShowXsheetBreadcrumbsEnabled() && + Preferences::instance()->isExpandFunctionHeaderEnabled(); m_functionGraph->hide(); m_numericalColumns->show(); if (QSpacerItem *spacer = m_leftLayout->itemAt(0)->spacerItem()) { - spacer->changeSize(1, m_spacing + ((toolBarVisible) ? 10 : 0), + spacer->changeSize(1, m_spacing + ((toolBarVisible) ? 10 : 0) + + ((breadcrumbsVisible) ? 10 : 0), QSizePolicy::Fixed, QSizePolicy::Fixed); spacer->invalidate(); m_numericalColumns->updateHeaderHeight(); m_leftLayout->setSpacing(0); } else - m_leftLayout->setSpacing(m_spacing + ((toolBarVisible) ? 30 : 0)); + m_leftLayout->setSpacing(m_spacing + ((toolBarVisible) ? 30 : 0) + + ((breadcrumbsVisible) ? 30 : 0)); updateGeometry(); }