diff --git a/.gitignore b/.gitignore index c2643dc4e..f9fe83a01 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .vscode/** .idea/** +.cache/** # Qt generated **/*.moc diff --git a/src/lib/app/RvCommon/CMakeLists.txt b/src/lib/app/RvCommon/CMakeLists.txt index dbe9b2f2d..1ac29f016 100644 --- a/src/lib/app/RvCommon/CMakeLists.txt +++ b/src/lib/app/RvCommon/CMakeLists.txt @@ -55,6 +55,7 @@ SET(_sources RvWebManager.cpp RvTopViewToolBar.cpp RvBottomViewToolBar.cpp + RvBottomViewToolBarBuild.cpp RvSourceEditor.cpp GLSLSyntaxHighlighter.cpp RvProfileManager.cpp diff --git a/src/lib/app/RvCommon/RvBottomViewToolBar.cpp b/src/lib/app/RvCommon/RvBottomViewToolBar.cpp index 69b58a8f6..7a847dfdf 100644 --- a/src/lib/app/RvCommon/RvBottomViewToolBar.cpp +++ b/src/lib/app/RvCommon/RvBottomViewToolBar.cpp @@ -9,13 +9,17 @@ #include #include #include +#include #include #include #include #include #include #include -#include +#include +#include +#include +#include #include #include #include @@ -69,24 +73,11 @@ namespace Rv setVisible(b); } - static QVariant enumCodeMap(int enumVal, QString codeVal) - { - QVariantMap m; - - m["enum"] = enumVal; - m["code"] = codeVal; - - return QVariant(m); - } - void RvBottomViewToolBar::build() { if (m_audioMenu) return; - Options& opts = Options::sharedOptions(); - - // Initialize QIcons m_playModeOnceIcon = QIcon(":/images/playmode_once_48x48.png"); m_playModeLoopIcon = QIcon(":/images/playmode_loop_48x48.png"); m_playModePingPongIcon = QIcon(":/images/playmode_pingpong_48x48.png"); @@ -99,254 +90,24 @@ namespace Rv setProperty("tbstyle", QVariant(QString("play_controls"))); - QAction* a; - QMenu* m; - QToolButton* b; - QFrame* qf; - - for (size_t i = 0; i < 6; i++) - { - a = addAction(""); - b = dynamic_cast(widgetForAction(a)); - b->setIcon(m_playModeLoopIcon); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setProperty("tbstyle", QVariant(QString("interior"))); - - switch (i) - { - case 0: - b->setProperty("tbstyle", QVariant(QString("left"))); - a->setIcon(QIcon(":/images/smanager.png")); - a->setToolTip("Toggle Session Manager"); - m_smAction = a; - break; - case 1: - a->setIcon(QIcon(":/images/paint_48x48.png")); - a->setToolTip("Toggle Annotation tools"); - m_paintAction = a; - break; - case 2: - a->setIcon(QIcon(":/images/about_48x48.png")); - a->setToolTip("Toggle Image Info"); - m_infoAction = a; - break; - case 3: - a->setIcon(QIcon(":/images/ntwrk_48x48.png")); - a->setToolTip("Toggle RV Networking Dialog"); - m_networkAction = a; - break; - case 4: - a->setIcon(QIcon(":/images/timeline_mag.png")); - a->setToolTip("Toggle Timeline Magnifier"); - m_timelineMagAction = a; - break; - case 5: - a->setIcon(QIcon(":/images/timeline.png")); - a->setToolTip("Toggle Timeline"); - b->setProperty("tbstyle", QVariant(QString("right"))); - m_timelineAction = a; - break; - } - } - - connect(m_smAction, SIGNAL(triggered(bool)), this, SLOT(smActionTriggered(bool))); - connect(m_paintAction, SIGNAL(triggered(bool)), this, SLOT(paintActionTriggered(bool))); - connect(m_infoAction, SIGNAL(triggered(bool)), this, SLOT(infoActionTriggered(bool))); - connect(m_timelineAction, SIGNAL(triggered(bool)), this, SLOT(timelineActionTriggered(bool))); - connect(m_timelineMagAction, SIGNAL(triggered(bool)), this, SLOT(timelineMagActionTriggered(bool))); - connect(m_networkAction, SIGNAL(triggered(bool)), this, SLOT(networkActionTriggered(bool))); - - a = addAction(""); - a->setIcon(QIcon(":/images/ghost.png")); - a->setToolTip("Ghost"); - a->setCheckable(true); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("left"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - m_ghostAction = a; - - a = addAction(""); - a->setIcon(QIcon(":/images/hold.png")); - a->setToolTip("Hold"); - a->setCheckable(true); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("right"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - m_holdAction = a; - - connect(m_ghostAction, SIGNAL(triggered(bool)), this, SLOT(ghostTriggered(bool))); - connect(m_holdAction, SIGNAL(triggered(bool)), this, SLOT(holdTriggered(bool))); - - // - // Add some expanding space before the play buttons - // - qf = new QFrame(this); - qf->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - qf->setMinimumWidth(0); - qf->setFrameStyle(QFrame::Plain | QFrame::NoFrame); - qf->setStyleSheet("background-color: transparent"); - addWidget(qf); - - // play buts - a = addAction(""); - a->setIcon(QIcon(":/images/control_bstep.png")); - a->setToolTip("Step back one frame"); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("left"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setObjectName("backStepButton"); - m_backStepAction = a; - - a = addAction(""); - a->setIcon(QIcon(":/images/control_fstep.png")); - a->setToolTip("Step forward one frame"); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("right"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setObjectName("forwardStepButton"); - m_forwardStepAction = a; - - a = addAction(""); - a->setIcon(QIcon(":/images/control_bplay.png")); - a->setToolTip("Play backwards"); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("left"))); - b->setProperty("tbsize", QVariant(QString("double"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - m_backwardPlayAction = a; - - a = addAction(""); - a->setIcon(QIcon(":/images/control_play.png")); - a->setToolTip("Play forwards"); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("right"))); - b->setProperty("tbsize", QVariant(QString("double"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - m_forwardPlayAction = a; - - a = addAction(""); - a->setIcon(QIcon(":/images/control_bmark.png")); - a->setToolTip("Skip to start of sequence"); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("left"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setObjectName("firstFrameButton"); - m_backMarkAction = a; - - a = addAction(""); - a->setIcon(QIcon(":/images/control_fmark.png")); - a->setToolTip("Skip to end of sequence"); - b = dynamic_cast(widgetForAction(a)); - b->setProperty("tbstyle", QVariant(QString("right"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setObjectName("lastFrameButton"); - m_forwardMarkAction = a; - - connect(m_backStepAction, SIGNAL(triggered()), this, SLOT(backStepTriggered())); - connect(m_forwardStepAction, SIGNAL(triggered()), this, SLOT(forwardStepTriggered())); - connect(m_backwardPlayAction, SIGNAL(triggered()), this, SLOT(backPlayTriggered())); - connect(m_forwardPlayAction, SIGNAL(triggered()), this, SLOT(forwardPlayTriggered())); - connect(m_backMarkAction, SIGNAL(triggered()), this, SLOT(backMarkTriggered())); - connect(m_forwardMarkAction, SIGNAL(triggered()), this, SLOT(forwardMarkTriggered())); - - // - // Add some expanding space after the play buttons - // - qf = new QFrame(this); - qf->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - qf->setMinimumWidth(0); - qf->setFrameStyle(QFrame::Plain | QFrame::NoFrame); - qf->setStyleSheet("background-color: transparent"); - addWidget(qf); - - // - // This expanding space will not go to more than 90 pixels. It's to - // "counter balance" the buttons on the other side, so the play control - // buttons stay in the center of the view (roughly). - // - qf = new QFrame(this); - qf->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - qf->setMinimumWidth(0); - qf->setMaximumWidth(90); - qf->setFrameStyle(QFrame::Plain | QFrame::NoFrame); - qf->setStyleSheet("background-color: transparent"); - addWidget(qf); - - m_playModeAction = addAction(""); - m_playModeAction->setToolTip(QString::fromUtf8(playModeDefaultTooltip.data(), playModeDefaultTooltip.size())); - b = dynamic_cast(widgetForAction(m_playModeAction)); - - switch (static_cast(opts.loopMode)) - { - case Session::PlayOnce: - m_playModeAction->setIcon(m_playModeOnceIcon); - break; + constexpr int buttonPadding = 5; - case Session::PlayPingPong: - m_playModeAction->setIcon(m_playModePingPongIcon); - break; + buildLeft(buttonPadding); + buildRight(buttonPadding); + buildCenter(); - default: - case Session::PlayLoop: - m_playModeAction->setIcon(m_playModeLoopIcon); - break; - } + barContainer = new QWidget(this); + barContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + grid = new QGridLayout(barContainer); + grid->setContentsMargins(0, 0, 0, 0); + grid->setSpacing(0); - b->setProperty("tbstyle", QVariant(QString("left_menu"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setPopupMode(QToolButton::InstantPopup); - - m_playModeMenu = new QMenu(b); - m_playModeMenu->addAction("Playback")->setDisabled(true); - m_playModeOnceAction = m_playModeMenu->addAction(" Once"); - m_playModeOnceAction->setData(enumCodeMap(Session::PlayOnce, "commands.setPlayMode(PlayOnce)")); - m_playModeLoopAction = m_playModeMenu->addAction(" Loop"); - m_playModeLoopAction->setData(enumCodeMap(Session::PlayLoop, "commands.setPlayMode(PlayLoop)")); - m_playModePingPongAction = m_playModeMenu->addAction(" PingPong"); - m_playModePingPongAction->setData(enumCodeMap(Session::PlayPingPong, "commands.setPlayMode(PlayPingPong)")); - // m_playModeMenu->addAction(" Loop with Black 0.5 Second"); - // m_playModeMenu->addAction(" Loop with Black 1.0 Second"); - - b->setMenu(m_playModeMenu); - - // Install event filter to show tooltips on disabled menu items - m_playModeMenu->installEventFilter(this); - - connect(m_playModeMenu, SIGNAL(triggered(QAction*)), this, SLOT(playModeMenuTriggered(QAction*))); - connect(m_playModeMenu, SIGNAL(aboutToShow()), this, SLOT(playModeMenuUpdate())); - - int volumeLevel = (int)(100.0f * IPCore::SoundTrackIPNode::defaultVolume); - m_audioAction = addAction(""); - m_audioAction->setToolTip("Audio control"); - b = dynamic_cast(widgetForAction(m_audioAction)); - setVolumeLevel(*b, volumeLevel); - b->setProperty("tbstyle", QVariant(QString("right_menu"))); - b->setToolButtonStyle(Qt::ToolButtonIconOnly); - b->setPopupMode(QToolButton::InstantPopup); - m = new QMenu(b); - m->setProperty("menuStyle", QVariant(QString("slim"))); - QAction* audio = m->addAction("Volume"); - audio->setDisabled(true); - QWidgetAction* wa = new QWidgetAction(b); - setVolumeLevel(*wa, volumeLevel); - m_audioSlider = new QSlider(b); - m_audioSlider->setProperty("sliderStyle", QVariant(QString("menu"))); - m_audioSlider->setTickInterval(10); - wa->setDefaultWidget(m_audioSlider); - m->addAction(wa); - m->addSeparator(); - m_muteAction = m->addAction("Mute"); - m_muteAction->setIcon(QIcon(":/images/mute_32x32.png")); - m_muteAction->setCheckable(true); - b->setMenu(m); - m_audioMenu = m; + grid->addWidget(m_leftBox, 0, 0, Qt::AlignLeft | Qt::AlignVCenter); + grid->addWidget(m_rightBox, 0, 0, Qt::AlignRight | Qt::AlignVCenter); + grid->addWidget(m_centerBox, 0, 0, Qt::AlignCenter); - connect(m_muteAction, SIGNAL(triggered(bool)), this, SLOT(audioMuteTriggered(bool))); - connect(m_audioMenu, SIGNAL(aboutToShow()), this, SLOT(audioMenuTriggered())); - connect(m_audioSlider, SIGNAL(valueChanged(int)), this, SLOT(audioSliderChanged(int))); - connect(m_audioSlider, SIGNAL(sliderReleased()), this, SLOT(audioSliderReleased())); + addWidget(barContainer); - // Map toolbar actions to their corresponding event categories m_actionCategoryMappings = {{ {m_smAction, IPCore::EventCategories::sessionmanagerCategory, m_smAction->toolTip()}, {m_paintAction, IPCore::EventCategories::annotateCategory, m_paintAction->toolTip()}, @@ -357,7 +118,6 @@ namespace Rv {m_backwardPlayAction, IPCore::EventCategories::backwardplayCategory, m_backwardPlayAction->toolTip()}, {m_forwardPlayAction, IPCore::EventCategories::playcontrolCategory, m_forwardPlayAction->toolTip()}, {m_playModeAction, IPCore::EventCategories::playcontrolCategory, m_playModeAction->toolTip()}, - {m_backMarkAction, IPCore::EventCategories::playcontrolCategory, m_backMarkAction->toolTip()}, {m_forwardMarkAction, IPCore::EventCategories::playcontrolCategory, m_forwardMarkAction->toolTip()}, }}; @@ -418,42 +178,33 @@ namespace Rv } else if (name == "play-start") { - QToolButton* bb = dynamic_cast(widgetForAction(m_backwardPlayAction)); - QToolButton* bf = dynamic_cast(widgetForAction(m_forwardPlayAction)); - if (m_session->inc() == -1) { - bb->setIcon(QIcon(":/images/control_pause.png")); + m_backwardPlayAction->setIcon(QIcon(":/images/control_pause.png")); } else { - bf->setIcon(QIcon(":/images/control_pause.png")); + m_forwardPlayAction->setIcon(QIcon(":/images/control_pause.png")); } } else if (name == "play-stop") { - QToolButton* bb = dynamic_cast(widgetForAction(m_backwardPlayAction)); - QToolButton* bf = dynamic_cast(widgetForAction(m_forwardPlayAction)); - - bb->setIcon(QIcon(":/images/control_bplay.png")); - bf->setIcon(QIcon(":/images/control_play.png")); + m_backwardPlayAction->setIcon(QIcon(":/images/control_bplay.png")); + m_forwardPlayAction->setIcon(QIcon(":/images/control_play.png")); } else if (name == "play-mode-changed") { - if (QToolButton* b = dynamic_cast(widgetForAction(m_playModeAction))) + switch (m_session->playMode()) { - switch (m_session->playMode()) - { - case 0: // Session::PlayLoop - b->setIcon(m_playModeLoopIcon); - break; - case 1: // Session::PlayOnce - b->setIcon(m_playModeOnceIcon); - break; - case 2: // Session::PlayPingPong - b->setIcon(m_playModePingPongIcon); - break; - } + case 0: // Session::PlayLoop + m_playModeAction->setIcon(m_playModeLoopIcon); + break; + case 1: // Session::PlayOnce + m_playModeAction->setIcon(m_playModeOnceIcon); + break; + case 2: // Session::PlayPingPong + m_playModeAction->setIcon(m_playModePingPongIcon); + break; } } else if (name == "update-ghost-button") @@ -604,20 +355,17 @@ namespace Rv void RvBottomViewToolBar::setVolumeIcon() { - if (QToolButton* b = dynamic_cast(widgetForAction(m_audioAction))) - { - IntPropertyEditor editor(m_session->graph(), m_session->currentFrame(), "#RVSoundTrack.audio.mute"); + IntPropertyEditor editor(m_session->graph(), m_session->currentFrame(), "#RVSoundTrack.audio.mute"); - if (editor.value()) - { - b->setIcon(m_volumeHighMutedIcon); - } - else - { - int volumeLevel = m_audioSlider->value(); // 0 - 99; + if (editor.value()) + { + m_audioAction->setIcon(m_volumeHighMutedIcon); + } + else + { + int volumeLevel = m_audioSlider->value(); // 0 - 99; - setVolumeLevel(*b, volumeLevel); - } + setVolumeLevel(*m_audioAction, volumeLevel); } } @@ -751,22 +499,19 @@ namespace Rv m_session->userGenericEvent("remote-eval", UTF8::qconvert(a->data().toMap()["code"].toString())); - if (QToolButton* b = dynamic_cast(widgetForAction(m_playModeAction))) + switch (m_session->playMode()) { - switch (m_session->playMode()) - { - case Session::PlayOnce: - b->setIcon(m_playModeOnceIcon); - break; + case Session::PlayOnce: + m_playModeAction->setIcon(m_playModeOnceIcon); + break; - case Session::PlayLoop: - b->setIcon(m_playModeLoopIcon); - break; + case Session::PlayLoop: + m_playModeAction->setIcon(m_playModeLoopIcon); + break; - case Session::PlayPingPong: - b->setIcon(m_playModePingPongIcon); - break; - } + case Session::PlayPingPong: + m_playModeAction->setIcon(m_playModePingPongIcon); + break; } } @@ -792,6 +537,106 @@ namespace Rv return QToolBar::eventFilter(obj, event); } + void RvBottomViewToolBar::resizeEvent(QResizeEvent* event) + { + QToolBar::resizeEvent(event); + + if (!m_overflowUpdatePending) + { + m_overflowUpdatePending = true; + + // Only re-render once toolbar finished rendering other work + QTimer::singleShot(0, this, + [this]() + { + m_overflowUpdatePending = false; + updateOverflow(); + }); + } + } + + void RvBottomViewToolBar::updateOverflow() + { + if (!m_leftBox || !m_centerBox || !m_rightBox) + return; + + const int centerWidth = m_centerBox->sizeHint().width(); + const int sideBudget = (width() - centerWidth) / 2; + + auto applyOverflow = [&](QWidget* box, bool reverse) + { + QLayout* layout = box->layout(); + if (!layout) + return; + + box->setUpdatesEnabled(false); + + const QMargins margins = layout->contentsMargins(); + const int spacing = layout->spacing(); + int remaining = sideBudget - margins.left(); + const int n = layout->count(); + + // Subtract space taken by widgets we don't own (e.g. plugin-injected + // widgets). + int visibleCount = 0; + for (int i = 0; i < n; ++i) + { + QWidget* w = layout->itemAt(i)->widget(); + if (!w) + continue; + if (w->property("toolbarOwned").toBool()) + continue; + if (!w->isVisible()) + continue; + + remaining -= w->sizeHint().width(); + if (visibleCount > 0) + remaining -= spacing; + ++visibleCount; + } + + // Walk toolbar-owned buttons from the outer edge inward, hiding + // those that don't fit in the remaining budget. Once any button + // fails to fit, every button after it, closer to the center is + // not rendered. + bool hideRest = false; + for (int idx = 0; idx < n; ++idx) + { + const int i = reverse ? (n - 1 - idx) : idx; + + QWidget* w = layout->itemAt(i)->widget(); + if (!w) + continue; + if (!w->property("toolbarOwned").toBool()) + continue; + + int cost = w->sizeHint().width(); + if (visibleCount > 0) + cost += spacing; + + const bool fits = !hideRest && cost <= remaining; + + if (!fits) + hideRest = true; + + if (w->isVisible() != fits) + w->setVisible(fits); + + if (fits) + { + remaining -= cost; + ++visibleCount; + } + } + + box->setUpdatesEnabled(true); + box->updateGeometry(); + }; + + applyOverflow(m_leftBox, false); + applyOverflow(m_rightBox, true); + } + void RvBottomViewToolBar::updateActionAvailability() { if (!m_session) diff --git a/src/lib/app/RvCommon/RvBottomViewToolBarBuild.cpp b/src/lib/app/RvCommon/RvBottomViewToolBarBuild.cpp new file mode 100644 index 000000000..ebacdbe0a --- /dev/null +++ b/src/lib/app/RvCommon/RvBottomViewToolBarBuild.cpp @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Rv +{ + using namespace IPCore; + + namespace + { + constexpr std::string_view playModeDefaultTooltip = "Select playback style"; + + /// + /// Make a button using an existing action and set it as the default action since + /// the action is not directly added to the toolbar + /// + QToolButton* makeBtn(QWidget* parent, QAction* action, const char* tbstyle, const char* tbsize = nullptr) + { + QToolButton* btn = new QToolButton(parent); + btn->setDefaultAction(action); + btn->setProperty("tbstyle", QVariant(QString(tbstyle))); + btn->setProperty("toolbarOwned", true); + if (tbsize) + btn->setProperty("tbsize", QVariant(QString(tbsize))); + btn->setToolButtonStyle(Qt::ToolButtonIconOnly); + return btn; + } + + QVariant enumCodeMap(int enumVal, QString codeVal) + { + QVariantMap m; + m["enum"] = enumVal; + m["code"] = codeVal; + return QVariant(m); + } + } // namespace + + /// + /// Add all buttons belonging to the left side of the toolbar and the actions in a single Widget + /// + void RvBottomViewToolBar::buildLeft(int padding) + { + m_leftBox = new QWidget(this); + m_leftBox->setObjectName("leftBox"); + QHBoxLayout* leftLayout = new QHBoxLayout(m_leftBox); + leftLayout->setContentsMargins(padding, 0, padding, 0); + leftLayout->setSpacing(0); + + struct LeftActionDef + { + const char* iconPath; + const char* tooltip; + const char* tbstyle; + QAction** target; + }; + + const LeftActionDef leftDefs[] = { + {":/images/smanager.png", "Toggle Session Manager", "left", &m_smAction}, + {":/images/paint_48x48.png", "Toggle Annotation tools", "interior", &m_paintAction}, + {":/images/about_48x48.png", "Toggle Image Info", "interior", &m_infoAction}, + {":/images/ntwrk_48x48.png", "Toggle RV Networking Dialog", "interior", &m_networkAction}, + {":/images/timeline_mag.png", "Toggle Timeline Magnifier", "interior", &m_timelineMagAction}, + {":/images/timeline.png", "Toggle Timeline", "right", &m_timelineAction}, + }; + for (const auto& def : leftDefs) + { + QAction* a = new QAction("", this); + a->setIcon(QIcon(def.iconPath)); + a->setToolTip(def.tooltip); + *def.target = a; + leftLayout->addWidget(makeBtn(m_leftBox, a, def.tbstyle)); + } + + connect(m_smAction, SIGNAL(triggered(bool)), this, SLOT(smActionTriggered(bool))); + connect(m_paintAction, SIGNAL(triggered(bool)), this, SLOT(paintActionTriggered(bool))); + connect(m_infoAction, SIGNAL(triggered(bool)), this, SLOT(infoActionTriggered(bool))); + connect(m_timelineAction, SIGNAL(triggered(bool)), this, SLOT(timelineActionTriggered(bool))); + connect(m_timelineMagAction, SIGNAL(triggered(bool)), this, SLOT(timelineMagActionTriggered(bool))); + connect(m_networkAction, SIGNAL(triggered(bool)), this, SLOT(networkActionTriggered(bool))); + + m_ghostAction = new QAction("", this); + m_ghostAction->setIcon(QIcon(":/images/ghost.png")); + m_ghostAction->setToolTip("Ghost"); + m_ghostAction->setCheckable(true); + leftLayout->addWidget(makeBtn(m_leftBox, m_ghostAction, "left")); + + m_holdAction = new QAction("", this); + m_holdAction->setIcon(QIcon(":/images/hold.png")); + m_holdAction->setToolTip("Hold"); + m_holdAction->setCheckable(true); + leftLayout->addWidget(makeBtn(m_leftBox, m_holdAction, "right")); + + connect(m_ghostAction, SIGNAL(triggered(bool)), this, SLOT(ghostTriggered(bool))); + connect(m_holdAction, SIGNAL(triggered(bool)), this, SLOT(holdTriggered(bool))); + } + + /// + /// Add all buttons belonging to the center (playback buttons) of the toolbar in a single widget + /// + void RvBottomViewToolBar::buildCenter() + { + m_centerBox = new QWidget(this); + m_centerBox->setObjectName("centerBox"); + QHBoxLayout* centerLayout = new QHBoxLayout(m_centerBox); + centerLayout->setContentsMargins(0, 0, 0, 0); + centerLayout->setSpacing(0); + + m_backStepAction = new QAction("", this); + m_backStepAction->setIcon(QIcon(":/images/control_bstep.png")); + m_backStepAction->setToolTip("Step back one frame"); + QToolButton* backStepBtn = makeBtn(m_centerBox, m_backStepAction, "left"); + backStepBtn->setObjectName("backStepButton"); + centerLayout->addWidget(backStepBtn); + + m_forwardStepAction = new QAction("", this); + m_forwardStepAction->setIcon(QIcon(":/images/control_fstep.png")); + m_forwardStepAction->setToolTip("Step forward one frame"); + QToolButton* fwdStepBtn = makeBtn(m_centerBox, m_forwardStepAction, "right"); + fwdStepBtn->setObjectName("forwardStepButton"); + centerLayout->addWidget(fwdStepBtn); + + m_backwardPlayAction = new QAction("", this); + m_backwardPlayAction->setIcon(QIcon(":/images/control_bplay.png")); + m_backwardPlayAction->setToolTip("Play backwards"); + centerLayout->addWidget(makeBtn(m_centerBox, m_backwardPlayAction, "left", "double")); + + m_forwardPlayAction = new QAction("", this); + m_forwardPlayAction->setIcon(QIcon(":/images/control_play.png")); + m_forwardPlayAction->setToolTip("Play forwards"); + centerLayout->addWidget(makeBtn(m_centerBox, m_forwardPlayAction, "right", "double")); + + m_backMarkAction = new QAction("", this); + m_backMarkAction->setIcon(QIcon(":/images/control_bmark.png")); + m_backMarkAction->setToolTip("Skip to start of sequence"); + QToolButton* backMarkBtn = makeBtn(m_centerBox, m_backMarkAction, "left"); + backMarkBtn->setObjectName("firstFrameButton"); + centerLayout->addWidget(backMarkBtn); + + m_forwardMarkAction = new QAction("", this); + m_forwardMarkAction->setIcon(QIcon(":/images/control_fmark.png")); + m_forwardMarkAction->setToolTip("Skip to end of sequence"); + QToolButton* fwdMarkBtn = makeBtn(m_centerBox, m_forwardMarkAction, "right"); + fwdMarkBtn->setObjectName("lastFrameButton"); + centerLayout->addWidget(fwdMarkBtn); + + connect(m_backStepAction, SIGNAL(triggered()), this, SLOT(backStepTriggered())); + connect(m_forwardStepAction, SIGNAL(triggered()), this, SLOT(forwardStepTriggered())); + connect(m_backwardPlayAction, SIGNAL(triggered()), this, SLOT(backPlayTriggered())); + connect(m_forwardPlayAction, SIGNAL(triggered()), this, SLOT(forwardPlayTriggered())); + connect(m_backMarkAction, SIGNAL(triggered()), this, SLOT(backMarkTriggered())); + connect(m_forwardMarkAction, SIGNAL(triggered()), this, SLOT(forwardMarkTriggered())); + } + + /// + /// Add all buttons belonging to the right of the toolbar in a single widget + /// + void RvBottomViewToolBar::buildRight(int padding) + { + Options& opts = Options::sharedOptions(); + + m_rightBox = new QWidget(this); + m_rightBox->setObjectName("rightBox"); + QHBoxLayout* rightLayout = new QHBoxLayout(m_rightBox); + rightLayout->setContentsMargins(0, 0, 0, 0); + rightLayout->setSpacing(0); + + m_playModeAction = new QAction("", this); + m_playModeAction->setToolTip(QString::fromUtf8(playModeDefaultTooltip.data(), playModeDefaultTooltip.size())); + switch (static_cast(opts.loopMode)) + { + case Session::PlayOnce: + m_playModeAction->setIcon(m_playModeOnceIcon); + break; + case Session::PlayPingPong: + m_playModeAction->setIcon(m_playModePingPongIcon); + break; + default: + case Session::PlayLoop: + m_playModeAction->setIcon(m_playModeLoopIcon); + break; + } + + QToolButton* playModeBtn = makeBtn(m_rightBox, m_playModeAction, "left_menu"); + playModeBtn->setObjectName("playModeButton"); + playModeBtn->setPopupMode(QToolButton::InstantPopup); + + m_playModeMenu = new QMenu(playModeBtn); + m_playModeMenu->addAction("Playback")->setDisabled(true); + m_playModeOnceAction = m_playModeMenu->addAction(" Once"); + m_playModeOnceAction->setData(enumCodeMap(Session::PlayOnce, "commands.setPlayMode(PlayOnce)")); + m_playModeLoopAction = m_playModeMenu->addAction(" Loop"); + m_playModeLoopAction->setData(enumCodeMap(Session::PlayLoop, "commands.setPlayMode(PlayLoop)")); + m_playModePingPongAction = m_playModeMenu->addAction(" PingPong"); + m_playModePingPongAction->setData(enumCodeMap(Session::PlayPingPong, "commands.setPlayMode(PlayPingPong)")); + playModeBtn->setMenu(m_playModeMenu); + rightLayout->addWidget(playModeBtn); + + m_playModeMenu->installEventFilter(this); + connect(m_playModeMenu, SIGNAL(triggered(QAction*)), this, SLOT(playModeMenuTriggered(QAction*))); + connect(m_playModeMenu, SIGNAL(aboutToShow()), this, SLOT(playModeMenuUpdate())); + + int volumeLevel = (int)(100.0f * IPCore::SoundTrackIPNode::defaultVolume); + m_audioAction = new QAction("", this); + m_audioAction->setToolTip("Audio control"); + setVolumeLevel(*m_audioAction, volumeLevel); + + QToolButton* audioBtn = makeBtn(m_rightBox, m_audioAction, "right_menu"); + audioBtn->setPopupMode(QToolButton::InstantPopup); + + m_audioMenu = new QMenu(audioBtn); + m_audioMenu->setProperty("menuStyle", QVariant(QString("slim"))); + QAction* audioLabel = m_audioMenu->addAction("Volume"); + audioLabel->setDisabled(true); + QWidgetAction* wa = new QWidgetAction(audioBtn); + setVolumeLevel(*wa, volumeLevel); + m_audioSlider = new QSlider(audioBtn); + m_audioSlider->setProperty("sliderStyle", QVariant(QString("menu"))); + m_audioSlider->setTickInterval(10); + wa->setDefaultWidget(m_audioSlider); + m_audioMenu->addAction(wa); + m_audioMenu->addSeparator(); + m_muteAction = m_audioMenu->addAction("Mute"); + m_muteAction->setIcon(QIcon(":/images/mute_32x32.png")); + m_muteAction->setCheckable(true); + audioBtn->setMenu(m_audioMenu); + rightLayout->addWidget(audioBtn); + + connect(m_muteAction, SIGNAL(triggered(bool)), this, SLOT(audioMuteTriggered(bool))); + connect(m_audioMenu, SIGNAL(aboutToShow()), this, SLOT(audioMenuTriggered())); + connect(m_audioSlider, SIGNAL(valueChanged(int)), this, SLOT(audioSliderChanged(int))); + connect(m_audioSlider, SIGNAL(sliderReleased()), this, SLOT(audioSliderReleased())); + } + +} // namespace Rv diff --git a/src/lib/app/RvCommon/RvCommon/RvBottomViewToolBar.h b/src/lib/app/RvCommon/RvCommon/RvBottomViewToolBar.h index 34a1d73c7..0c010c40a 100644 --- a/src/lib/app/RvCommon/RvCommon/RvBottomViewToolBar.h +++ b/src/lib/app/RvCommon/RvCommon/RvBottomViewToolBar.h @@ -11,6 +11,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -39,6 +43,9 @@ namespace Rv virtual Result receiveEvent(const TwkApp::Event&); void build(); + void buildLeft(int padding); + void buildCenter(); + void buildRight(int padding); void makeActive(bool); void makeActiveFromSettings(); @@ -70,6 +77,8 @@ namespace Rv protected: bool eventFilter(QObject* obj, QEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + void updateOverflow(); private: struct ActionCategoryMapping @@ -86,8 +95,13 @@ namespace Rv void updatePlayModeButtonState(); IPCore::Session* m_session; + QWidget* barContainer; + QGridLayout* grid; QString m_customCannotUseTooltip; QString m_customDisabledPrefix; + QWidget* m_leftBox; + QWidget* m_centerBox; + QWidget* m_rightBox; QAction* m_smAction; QAction* m_paintAction; QAction* m_infoAction; @@ -123,6 +137,7 @@ namespace Rv QIcon m_volumeHighMutedIcon; std::array m_actionCategoryMappings; + bool m_overflowUpdatePending = false; }; template void RvBottomViewToolBar::setVolumeLevel(T& inst, int level) diff --git a/src/plugins/rv-packages/multiple_source_media_rep/multiple_source_media_rep.py b/src/plugins/rv-packages/multiple_source_media_rep/multiple_source_media_rep.py index e5583c388..8487d192c 100755 --- a/src/plugins/rv-packages/multiple_source_media_rep/multiple_source_media_rep.py +++ b/src/plugins/rv-packages/multiple_source_media_rep/multiple_source_media_rep.py @@ -126,9 +126,6 @@ class MultipleSourceMediaRepMode(rvtypes.MinorMode): _media_resolution_lbl = None _media_extension_lbl = None - # Media representation widgets' actions - _media_representation_action = None - # This is used to postponed received events until progressive loading # gets completed. _in_progressive_loading = False @@ -281,13 +278,15 @@ def _build_media_representation_widgets(self): # after the function call. self._bottomToolBar = qtutils.sessionBottomToolBar() - playback_style_action = None - for action in self._bottomToolBar.actions(): - if action.toolTip() == QtCore.QObject().tr("Select playback style"): - playback_style_action = action + right_box = self._bottomToolBar.findChild(QtWidgets.QWidget, "rightBox") + play_mode_btn = right_box.findChild(QtWidgets.QToolButton, "playModeButton") + right_layout = right_box.layout() + + def insert_before_play_mode(widget): + right_layout.insertWidget(right_layout.indexOf(play_mode_btn), widget) # Dropdown menu for the media reps. - self._media_representation_btn = QtWidgets.QToolButton(self._bottomToolBar) + self._media_representation_btn = QtWidgets.QToolButton(right_box) self._media_representation_btn.setToolTip("Select media playback type") self._media_representation_btn.setProperty("tbstyle", "solo_menu") self._media_representation_btn.setStyleSheet("color: gray") @@ -298,20 +297,20 @@ def _build_media_representation_widgets(self): menu.aboutToShow.connect(self._on_media_rep_about_to_show) self._media_representation_btn.setMenu(menu) - self._media_representation_action = self._bottomToolBar.insertWidget( - playback_style_action, self._media_representation_btn - ) - self._media_representation_action.setVisible(False) + insert_before_play_mode(self._media_representation_btn) + self._media_representation_btn.setVisible(False) # Media resolution. - self._media_resolution_lbl = QtWidgets.QLabel("", self._bottomToolBar) - self._media_resolution_lbl.setStyleSheet("color: gray; background-color: transparent; margin-right: 5px") - self._bottomToolBar.insertWidget(playback_style_action, self._media_resolution_lbl) + self._media_resolution_lbl = QtWidgets.QLabel("", right_box) + self._media_resolution_lbl.setStyleSheet("color: gray; background-color: transparent; padding-right: 5px;") + self._media_resolution_lbl.setVisible(False) + insert_before_play_mode(self._media_resolution_lbl) # Media extension. - self._media_extension_lbl = QtWidgets.QLabel("", self._bottomToolBar) - self._media_extension_lbl.setStyleSheet("color: gray; background-color: transparent") - self._bottomToolBar.insertWidget(playback_style_action, self._media_extension_lbl) + self._media_extension_lbl = QtWidgets.QLabel("", right_box) + self._media_extension_lbl.setStyleSheet("color: gray; background-color: transparent; padding-right: 5px") + self._media_extension_lbl.setVisible(False) + insert_before_play_mode(self._media_extension_lbl) def _populate_media_rep_menu(self, menu, switch_nodes): """ @@ -388,8 +387,8 @@ def _show_media_representation(self, show): """ Shows or hides the media rep drop down menu """ - if self._media_representation_action.isVisible() != show: - self._media_representation_action.setVisible(show) + if self._media_representation_btn.isVisible() != show: + self._media_representation_btn.setVisible(show) self._bottomToolBar.repaint() def _update_media_representation(self): @@ -423,7 +422,9 @@ def _update_resolution_and_extension(self): common_source_media_infos = utils.get_common_source_media_infos(self._current_sources) self._media_resolution_lbl.setText(common_source_media_infos.resolution) + self._media_resolution_lbl.setVisible(bool(common_source_media_infos.resolution)) self._media_extension_lbl.setText(common_source_media_infos.extension) + self._media_extension_lbl.setVisible(bool(common_source_media_infos.extension)) def _update_media_info(self, event): """