diff --git a/app/app.pro b/app/app.pro index e1cf3eb823..5695aab33b 100644 --- a/app/app.pro +++ b/app/app.pro @@ -93,6 +93,7 @@ HEADERS += \ src/generalpage.h \ src/shortcutspage.h \ src/strokeoptionswidget.h \ + src/timecodecontrolwidget.h \ src/timelinepage.h \ src/toolboxwidget.h \ src/toolspage.h \ @@ -151,6 +152,7 @@ SOURCES += \ src/generalpage.cpp \ src/shortcutspage.cpp \ src/strokeoptionswidget.cpp \ + src/timecodecontrolwidget.cpp \ src/timelinepage.cpp \ src/toolboxwidget.cpp \ src/toolspage.cpp \ diff --git a/app/data/app.qrc b/app/data/app.qrc index 5b9116dab2..2a144aa9db 100644 --- a/app/data/app.qrc +++ b/app/data/app.qrc @@ -100,6 +100,7 @@ icons/themes/playful/window/window-float-button-normal-darkm.svg icons/themes/playful/window/window-float-button-normal.svg icons/themes/playful/window/window-close-button-normal.svg + icons/themes/playful/controls/control-timecode.svg pencil2d_quick_guide.pdf diff --git a/app/data/icons/themes/playful/controls/control-timecode.svg b/app/data/icons/themes/playful/controls/control-timecode.svg new file mode 100644 index 0000000000..2db7b5d2ff --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-timecode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/src/basedockwidget.cpp b/app/src/basedockwidget.cpp index 3336b71706..057b0b6b16 100644 --- a/app/src/basedockwidget.cpp +++ b/app/src/basedockwidget.cpp @@ -63,9 +63,14 @@ void BaseDockWidget::lock(bool locked) // nullptr means removing the custom title bar and restoring the default one if (locked) { - setTitleBarWidget(mNoTitleBarWidget); + if (mTitleBarWidget->hasChildWidget()) { + mTitleBarWidget->lock(locked); + } else { + setTitleBarWidget(mNoTitleBarWidget); + } } else { setTitleBarWidget(mTitleBarWidget); + mTitleBarWidget->lock(locked); } mLocked = locked; @@ -77,6 +82,13 @@ void BaseDockWidget::setTitle(const QString& title) mTitleBarWidget->setTitle(title); } +void BaseDockWidget::setWidgetInTitleBarArea(QWidget* widget) +{ + if (mTitleBarWidget) { + mTitleBarWidget->setChildWidget(widget); + } +} + void BaseDockWidget::resizeEvent(QResizeEvent *event) { QDockWidget::resizeEvent(event); diff --git a/app/src/basedockwidget.h b/app/src/basedockwidget.h index fd4323c4a1..a509c3e78e 100644 --- a/app/src/basedockwidget.h +++ b/app/src/basedockwidget.h @@ -42,6 +42,10 @@ class BaseDockWidget : public QDockWidget Editor* editor() const { return mEditor; } void setEditor( Editor* e ) { mEditor = e; } + /// Sets a widget in addition to the normal titlebar buttons. + /// If the titlebar already has one widget, the previous one will be deleted. + void setWidgetInTitleBarArea(QWidget* widget); + protected: void resizeEvent(QResizeEvent* event) override; diff --git a/app/src/timecodecontrolwidget.cpp b/app/src/timecodecontrolwidget.cpp new file mode 100644 index 0000000000..1d49f8b9e8 --- /dev/null +++ b/app/src/timecodecontrolwidget.cpp @@ -0,0 +1,118 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#include "timecodecontrolwidget.h" + +#include "preferencemanager.h" +#include "pencildef.h" +#include "pencilsettings.h" + +#include +#include +#include +#include +#include +#include + +TimeCodeControlWidget::TimeCodeControlWidget(TimeCodeControls* controls, QWidget* parent) + : QWidget(parent, Qt::Popup), mControls(controls) +{ + auto vboxLayout = new QVBoxLayout(); + vboxLayout->setContentsMargins(4,4,4,4); + setLayout(vboxLayout); + + auto titlelabel = new QLabel(tr("Timecode controls")); + + vboxLayout->addWidget(titlelabel); + + auto showTimeCodeCheckBox = new QCheckBox(this); + showTimeCodeCheckBox->setText(tr("Show")); + showTimeCodeCheckBox->setChecked(controls->enabled); + + auto optionsGroupBox = new QGroupBox(this); + optionsGroupBox->setTitle(tr("Options")); + optionsGroupBox->setEnabled(controls->enabled); + + auto showFramesCheckBox = new QCheckBox(this); + showFramesCheckBox->setText(tr("Frames")); + showFramesCheckBox->setChecked(controls->showFrames); + + auto timecodeComboBox = new QComboBox(this); + + auto groupBoxVBoxLayout = new QVBoxLayout(); + groupBoxVBoxLayout->setContentsMargins(4,4,4,4); + optionsGroupBox->setLayout(groupBoxVBoxLayout); + + groupBoxVBoxLayout->addWidget(showFramesCheckBox); + groupBoxVBoxLayout->addWidget(timecodeComboBox); + + timecodeComboBox->addItem(tr("No Timecode"), timecodeKindToInt(TimecodeKind::NONE)); + timecodeComboBox->addItem(tr("SMPTE Timecode"), timecodeKindToInt(TimecodeKind::SMPTE)); + timecodeComboBox->addItem(tr("SFF Timecode"), timecodeKindToInt(TimecodeKind::SFF)); + timecodeComboBox->setCurrentIndex(timecodeKindToInt(controls->kind)); + + vboxLayout->addWidget(showTimeCodeCheckBox); + vboxLayout->addWidget(optionsGroupBox); + + connect(showTimeCodeCheckBox, &QCheckBox::toggled, this, [this, optionsGroupBox](bool toggled) { + showTimecode(toggled); + optionsGroupBox->setEnabled(toggled); + }); + + connect(showFramesCheckBox, &QCheckBox::toggled, this, [this](bool toggled) { + showFrames(toggled); + }); + + auto comboBoxValueChanged = static_cast(&QComboBox::currentIndexChanged); + connect(timecodeComboBox, comboBoxValueChanged, this, [this, controls](int index) { + auto timecodeKind = controls->timecodeKindFromInt(index); + + setTimecodeTimerKind(timecodeKind); + }); +} + +void TimeCodeControlWidget::showFrames(bool shown) +{ + QSettings settings(PENCIL2D, PENCIL2D); + settings.setValue(SETTING_TIMECODE_FRAMES_ON, shown); + + mControls->showFrames = shown; + emit timecodeUpdated(); +} + +int TimeCodeControlWidget::timecodeKindToInt(TimecodeKind kind) const +{ + return static_cast(kind); +} + +void TimeCodeControlWidget::showTimecode(bool shown) +{ + QSettings settings(PENCIL2D, PENCIL2D); + settings.setValue(SETTING_TIMECODE_ON, shown); + + mControls->enabled = shown; + emit timecodeUpdated(); +} + +void TimeCodeControlWidget::setTimecodeTimerKind(TimecodeKind kind) +{ + QSettings settings(PENCIL2D, PENCIL2D); + settings.setValue(SETTING_TIMECODE_KIND, timecodeKindToInt(kind)); + settings.remove(SETTING_TIMECODE_TEXT); + mControls->kind = kind; + + emit timecodeUpdated(); +} diff --git a/app/src/timecodecontrolwidget.h b/app/src/timecodecontrolwidget.h new file mode 100644 index 0000000000..670f845e67 --- /dev/null +++ b/app/src/timecodecontrolwidget.h @@ -0,0 +1,66 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#ifndef TIMECODECONTROLWIDGET_H +#define TIMECODECONTROLWIDGET_H + +#include +#include "pencildef.h" + +struct TimeCodeControls { + bool enabled = false; + bool showFrames = false; + + TimecodeKind kind = TimecodeKind::NONE; + + TimecodeKind timecodeKindFromInt(int value) const + { + switch (value) + { + case 0: + return TimecodeKind::NONE; + case 1: + return TimecodeKind::SMPTE; + case 2: + return TimecodeKind::SFF; + default: + return TimecodeKind::NONE; + } + } +}; + +class TimeCodeControlWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TimeCodeControlWidget(TimeCodeControls* controls, QWidget* parent = nullptr); + +signals: + void timecodeUpdated(); + +private: + + int timecodeKindToInt(TimecodeKind kind) const; + + void setTimecodeTimerKind(TimecodeKind kind); + void showFrames(bool shown); + void showTimecode(bool shown); + + TimeCodeControls* mControls = nullptr; +}; + +#endif // TIMECODECONTROLWIDGET_H diff --git a/app/src/timecontrols.cpp b/app/src/timecontrols.cpp index 2dc7748f15..a38bff1eef 100644 --- a/app/src/timecontrols.cpp +++ b/app/src/timecontrols.cpp @@ -21,6 +21,8 @@ GNU General Public License for more details. #include #include #include +#include +#include #include "editor.h" #include "playbackmanager.h" #include "layermanager.h" @@ -30,7 +32,9 @@ GNU General Public License for more details. #include "timeline.h" #include "pencildef.h" -TimeControls::TimeControls(TimeLine* parent) : QToolBar(parent) +#include + +TimeControls::TimeControls(TimeLine* parent) : QWidget(parent) { mTimeline = parent; } @@ -39,8 +43,12 @@ void TimeControls::initUI() { QSettings settings(PENCIL2D, PENCIL2D); + QHBoxLayout* hBoxLayout = new QHBoxLayout(); + hBoxLayout->setContentsMargins(0,0,0,0); + + setLayout(hBoxLayout); + mFpsBox = new QSpinBox(this); - mFpsBox->setFixedHeight(24); mFpsBox->setValue(settings.value("Fps").toInt()); mFpsBox->setMinimum(1); mFpsBox->setMaximum(90); @@ -49,43 +57,26 @@ void TimeControls::initUI() mFpsBox->setFocusPolicy(Qt::WheelFocus); mFps = mFpsBox->value(); - mTimecodeSelect = new QToolButton(this); - - QMenu* timeSelectMenu = new QMenu(tr("Display timecode", "Timeline menu for choose a timecode"), this); - mTimecodeSelect->setIcon(QIcon(":/icons/themes/playful/misc/more-options.svg")); - - timeSelectMenu->addAction(mNoTimecodeAction = new QAction(tr("No text"), this)); - timeSelectMenu->addAction(mOnlyFramesAction = new QAction(tr("Frames"), this)); - timeSelectMenu->addAction(mSmpteAction = new QAction(tr("SMPTE Timecode"), this)); - timeSelectMenu->addAction(mSffAction = new QAction(tr("SFF Timecode"), this)); - mTimecodeSelect->setMenu(timeSelectMenu); - mTimecodeSelect->setPopupMode(QToolButton::InstantPopup); - mTimecodeSelect->setStyleSheet("::menu-indicator{ image: none; }"); - mTimecodeLabelEnum = mEditor->preference()->getInt(SETTING::TIMECODE_TEXT); + + mTimecodeButton = new QToolButton(this); + mTimecodeButton->setIconSize(QSize(22,22)); + mTimecodeButton->setIcon(QIcon(":/icons/themes/playful/controls/control-timecode.svg")); + mTimecodeLabel = new QLabel(this); + QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + // The default mono font is smaller, so we restore the font here + font.setPointSize(mTimecodeLabel->font().pointSize()); + mTimecodeLabel->setFont(font); mTimecodeLabel->setContentsMargins(2, 0, 0, 0); - mTimecodeLabel->setText(""); + mTimecodeLabel->setAlignment(Qt::AlignTrailing | Qt::AlignVCenter); + mTimecodeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + mTimecodeLabel->setToolTip(tr("Shows the current time code")); - switch (mTimecodeLabelEnum) - { - case NOTEXT: - mTimecodeLabel->setToolTip(""); - break; - case FRAMES: - mTimecodeLabel->setToolTip(tr("Actual frame number")); - break; - case SMPTE: - mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF")); - break; - case SFF: - mTimecodeLabel->setToolTip(tr("Timecode format S:FF")); - break; - default: - mTimecodeLabel->setToolTip(""); - } + mTimecodeControls.enabled = mEditor->preference()->isOn(SETTING::TIMECODE_ON); + mTimecodeControls.kind = mTimecodeControls.timecodeKindFromInt(mEditor->preference()->getInt(SETTING::TIMECODE_KIND)); + mTimecodeControls.showFrames = mEditor->preference()->isOn(SETTING::TIMECODE_FRAMES_ON); mLoopStartSpinBox = new QSpinBox(this); - mLoopStartSpinBox->setFixedHeight(24); mLoopStartSpinBox->setValue(settings.value("loopStart").toInt()); mLoopStartSpinBox->setMinimum(1); mLoopStartSpinBox->setEnabled(false); @@ -93,7 +84,6 @@ void TimeControls::initUI() mLoopStartSpinBox->setFocusPolicy(Qt::WheelFocus); mLoopEndSpinBox = new QSpinBox(this); - mLoopEndSpinBox->setFixedHeight(24); mLoopEndSpinBox->setValue(settings.value("loopEnd").toInt()); mLoopEndSpinBox->setMinimum(2); mLoopEndSpinBox->setEnabled(false); @@ -101,20 +91,19 @@ void TimeControls::initUI() mLoopEndSpinBox->setFocusPolicy(Qt::WheelFocus); mPlaybackRangeCheckBox = new QCheckBox(tr("Range")); - mPlaybackRangeCheckBox->setFixedHeight(24); mPlaybackRangeCheckBox->setToolTip(tr("Playback range")); - mPlayButton = new QPushButton(this); + mPlayButton = new QToolButton(this); mPlayButton->setIconSize(QSize(22,22)); - mLoopButton = new QPushButton(this); + mLoopButton = new QToolButton(this); mLoopButton->setIconSize(QSize(22,22)); - mSoundButton = new QPushButton(this); + mSoundButton = new QToolButton(this); mSoundButton->setIconSize(QSize(22,22)); - mSoundScrubButton = new QPushButton(this); + mSoundScrubButton = new QToolButton(this); mSoundScrubButton->setIconSize(QSize(22,22)); - mJumpToEndButton = new QPushButton(this); + mJumpToEndButton = new QToolButton(this); mJumpToEndButton->setIconSize(QSize(22,22)); - mJumpToStartButton = new QPushButton(this); + mJumpToStartButton = new QToolButton(this); mJumpToStartButton->setIconSize(QSize(22,22)); mLoopIcon = QIcon(":icons/themes/playful/controls/control-loop.svg"); @@ -144,25 +133,32 @@ void TimeControls::initUI() mSoundScrubButton->setCheckable(true); mSoundScrubButton->setChecked(mEditor->preference()->isOn(SETTING::SOUND_SCRUB_ACTIVE)); - - addWidget(mJumpToStartButton); - addWidget(mPlayButton); - addWidget(mJumpToEndButton); - addWidget(mLoopButton); - addWidget(mFpsBox); - addWidget(mPlaybackRangeCheckBox); - addWidget(mLoopStartSpinBox); - addWidget(mLoopEndSpinBox); - addWidget(mSoundButton); - addWidget(mSoundScrubButton); - addWidget(mTimecodeSelect); - mTimecodeLabelAction = addWidget(mTimecodeLabel); + layout()->addItem(new QSpacerItem(1,1, QSizePolicy::Expanding)); + layout()->addWidget(mTimecodeLabel); + layout()->addWidget(mTimecodeButton); + layout()->addWidget(mJumpToStartButton); + layout()->addWidget(mPlayButton); + layout()->addWidget(mJumpToEndButton); + layout()->addWidget(mLoopButton); + + layout()->addWidget(mFpsBox); + layout()->addWidget(mPlaybackRangeCheckBox); + layout()->addWidget(mLoopStartSpinBox); + layout()->addWidget(mLoopEndSpinBox); + layout()->addWidget(mSoundButton); + layout()->addWidget(mSoundScrubButton); + layout()->addItem(new QSpacerItem(1,1, QSizePolicy::Expanding)); makeConnections(); updateUI(); } +void TimeControls::updateTimecode() +{ + updateTimecodeLabel(mEditor->currentFrame()); +} + void TimeControls::updateUI() { PlaybackManager* playback = mEditor->playback(); @@ -180,6 +176,8 @@ void TimeControls::updateUI() QSignalBlocker b4(mLoopButton); mLoopButton->setChecked(playback->isLooping()); + + updateTimecode(); } void TimeControls::setEditor(Editor* editor) @@ -188,6 +186,42 @@ void TimeControls::setEditor(Editor* editor) mEditor = editor; } +void TimeControls::updateTimecodeLabel(int frame) +{ + mTimecodeLabel->setVisible(mTimecodeControls.enabled); + + if (mTimecodeControls.enabled) { + const bool showFrames = mTimecodeControls.showFrames; + const QString frameSuffix = showFrames + ? QString(" | %1").arg(QString::number(frame).rightJustified(4, '0')) + : QString(); + + QString timecode; + switch (mTimecodeControls.kind) + { + case TimecodeKind::SMPTE: { + timecode = QString("%1:%2:%3") + .arg(frame / (60 * mFps) % 60, 2, 10, QLatin1Char('0')) + .arg(frame / mFps % 60, 2, 10, QLatin1Char('0')) + .arg(frame % mFps, 2, 10, QLatin1Char('0')); + mTimecodeLabel->setText(timecode + frameSuffix); + break; + } + case TimecodeKind::SFF: { + timecode = QString("%1:%2") + .arg(frame / mFps) + .arg(frame % mFps, 2, 10, QLatin1Char('0')); + mTimecodeLabel->setText(timecode + frameSuffix); + break; + } + default: + timecode = showFrames ? QString::number(frame).rightJustified(4, '0') : QString(); + mTimecodeLabel->setText(timecode); + break; + } + } +} + void TimeControls::setFps(int value) { QSignalBlocker blocker(mFpsBox); @@ -234,10 +268,20 @@ void TimeControls::makeConnections() connect(mFpsBox, spinBoxValueChanged, this, &TimeControls::setFps); connect(mEditor, &Editor::fpsChanged, this, &TimeControls::setFps); - connect(mNoTimecodeAction, &QAction::triggered, this, &TimeControls::noTimecodeText); - connect(mOnlyFramesAction, &QAction::triggered, this, &TimeControls::onlyFramesText); - connect(mSmpteAction, &QAction::triggered, this, &TimeControls::smpteText); - connect(mSffAction, &QAction::triggered, this, &TimeControls::sffText); + + connect(mTimecodeButton, &QToolButton::clicked, this, &TimeControls::showTimecodePanel); +} + +void TimeControls::showTimecodePanel() +{ + mTimeCodeWidget = new TimeCodeControlWidget(&mTimecodeControls, this); + mTimeCodeWidget->setAttribute(Qt::WA_DeleteOnClose); + mTimeCodeWidget->show(); + mTimeCodeWidget->move(QCursor::pos()); + + connect(mTimeCodeWidget, &TimeCodeControlWidget::timecodeUpdated, this, [this] { + updateTimecode(); + }); } void TimeControls::playButtonClicked() @@ -319,42 +363,6 @@ void TimeControls::updateSoundScrubIcon(bool soundScrubEnabled) mEditor->preference()->set(SETTING::SOUND_SCRUB_ACTIVE, soundScrubEnabled); } -void TimeControls::noTimecodeText() -{ - QSettings settings(PENCIL2D, PENCIL2D); - settings.setValue(SETTING_TIMECODE_TEXT, NOTEXT); - mTimecodeLabelEnum = NOTEXT; - mTimecodeLabel->setToolTip(tr("")); - updateTimecodeLabel(mEditor->currentFrame()); -} - -void TimeControls::onlyFramesText() -{ - QSettings settings(PENCIL2D, PENCIL2D); - settings.setValue(SETTING_TIMECODE_TEXT, FRAMES); - mTimecodeLabelEnum = FRAMES; - mTimecodeLabel->setToolTip(tr("Actual frame number")); - updateTimecodeLabel(mEditor->currentFrame()); -} - -void TimeControls::sffText() -{ - QSettings settings(PENCIL2D, PENCIL2D); - settings.setValue(SETTING_TIMECODE_TEXT, SFF); - mTimecodeLabelEnum = SFF; - mTimecodeLabel->setToolTip(tr("Timecode format S:FF")); - updateTimecodeLabel(mEditor->currentFrame()); -} - -void TimeControls::smpteText() -{ - QSettings settings(PENCIL2D, PENCIL2D); - settings.setValue(SETTING_TIMECODE_TEXT, SMPTE); - mTimecodeLabelEnum = SMPTE; - mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF")); - updateTimecodeLabel(mEditor->currentFrame()); -} - void TimeControls::onFpsEditingFinished() { mFpsBox->clearFocus(); @@ -362,33 +370,6 @@ void TimeControls::onFpsEditingFinished() mFps = mFpsBox->value(); } -void TimeControls::updateTimecodeLabel(int frame) -{ - mTimecodeLabelAction->setVisible(true); - - switch (mTimecodeLabelEnum) - { - case TimecodeTextLevel::SMPTE: - mTimecodeLabel->setText(QString("%1:%2:%3") - .arg(frame / (60 * mFps) % 60, 2, 10, QLatin1Char('0')) - .arg(frame / mFps % 60, 2, 10, QLatin1Char('0')) - .arg(frame % mFps, 2, 10, QLatin1Char('0'))); - break; - case TimecodeTextLevel::SFF: - mTimecodeLabel->setText(QString("%1:%2") - .arg(frame / mFps) - .arg(frame % mFps, 2, 10, QLatin1Char('0'))); - break; - case TimecodeTextLevel::FRAMES: - mTimecodeLabel->setText(QString::number(frame).rightJustified(4, '0')); - break; - case TimecodeTextLevel::NOTEXT: - default: - mTimecodeLabelAction->setVisible(false); - break; - } - -} void TimeControls::updateLength(int frameLength) { diff --git a/app/src/timecontrols.h b/app/src/timecontrols.h index d2901085c9..a612587e3d 100644 --- a/app/src/timecontrols.h +++ b/app/src/timecontrols.h @@ -25,11 +25,15 @@ GNU General Public License for more details. #include #include +#include "pencildef.h" + +#include "timecodecontrolwidget.h" + class Editor; class PreferenceManager; class TimeLine; -class TimeControls : public QToolBar +class TimeControls : public QWidget { Q_OBJECT @@ -60,8 +64,14 @@ public slots: void updateTimecodeLabel(int frame); private: + void showTimecodePanel(); + void updateTimecode(); + + void showFramesInTimecode(bool shown); + void showTimecode(bool shown); void makeConnections(); + void setupTimeCodeMenu(); void playButtonClicked(); void jumpToStartButtonClicked(); void jumpToEndButtonClicked(); @@ -71,29 +81,23 @@ public slots: void loopEndValueChanged(int); void updateSoundScrubIcon(bool soundScrubEnabled); - void noTimecodeText(); - void onlyFramesText(); - void sffText(); - void smpteText(); - private: - QPushButton* mPlayButton = nullptr; - QPushButton* mJumpToEndButton = nullptr; - QPushButton* mJumpToStartButton = nullptr; - QPushButton* mLoopButton = nullptr; - QPushButton* mSoundButton = nullptr; - QPushButton* mSoundScrubButton = nullptr; + QToolButton* mPlayButton = nullptr; + QToolButton* mJumpToEndButton = nullptr; + QToolButton* mJumpToStartButton = nullptr; + QToolButton* mLoopButton = nullptr; + QToolButton* mSoundButton = nullptr; + QToolButton* mSoundScrubButton = nullptr; QSpinBox* mFpsBox = nullptr; QCheckBox* mPlaybackRangeCheckBox = nullptr; QSpinBox* mLoopStartSpinBox = nullptr; QSpinBox* mLoopEndSpinBox = nullptr; - QToolButton* mTimecodeSelect = nullptr; + + QToolButton* mTimecodeButton = nullptr; QLabel* mTimecodeLabel = nullptr; - QAction* mNoTimecodeAction = nullptr; - QAction* mOnlyFramesAction = nullptr; - QAction* mSmpteAction = nullptr; - QAction* mSffAction = nullptr; - QAction* mTimecodeLabelAction = nullptr; + + TimeCodeControls mTimecodeControls; + TimeCodeControlWidget* mTimeCodeWidget = nullptr; QIcon mStartIcon; QIcon mStopIcon; @@ -106,7 +110,6 @@ public slots: TimeLine* mTimeline = nullptr; Editor* mEditor = nullptr; int mFps = 12; - int mTimecodeLabelEnum; }; #endif diff --git a/app/src/timeline.cpp b/app/src/timeline.cpp index 92a5544e9c..1acc9c325d 100644 --- a/app/src/timeline.cpp +++ b/app/src/timeline.cpp @@ -156,15 +156,17 @@ void TimeLine::initUI() // --------- Time controls --------- mTimeControls = new TimeControls(this); - mTimeControls->setIconSize(QSize(22,22)); + // Needs to be slightly larger than the titlebar to prevent the icons from becoming smaller. + mTimeControls->setFixedHeight(this->titleBarWidget()->height() + 1); mTimeControls->setEditor(editor()); mTimeControls->initUI(); updateLength(); + this->setWidgetInTitleBarArea(mTimeControls); + QHBoxLayout* rightToolBarLayout = new QHBoxLayout(); rightToolBarLayout->addWidget(timelineButtons); rightToolBarLayout->setAlignment(Qt::AlignLeft); - rightToolBarLayout->addWidget(mTimeControls); rightToolBarLayout->setContentsMargins(0, 0, 0, 0); rightToolBarLayout->setSpacing(0); rightToolBar->setLayout(rightToolBarLayout); diff --git a/app/src/titlebarwidget.cpp b/app/src/titlebarwidget.cpp index d7aabb95da..b2ad1feeb1 100644 --- a/app/src/titlebarwidget.cpp +++ b/app/src/titlebarwidget.cpp @@ -33,12 +33,15 @@ TitleBarWidget::TitleBarWidget(QWidget* parent) : QWidget(parent) { - QVBoxLayout* vLayout = new QVBoxLayout(); + QHBoxLayout* vLayout = new QHBoxLayout(); - vLayout->setContentsMargins(3,4,3,4); + vLayout->setContentsMargins(2,0,2,0); vLayout->setSpacing(0); - vLayout->addWidget(createCustomTitleBarWidget(this)); + QWidget* contentWidget = createCustomTitleBarWidget(this); + vLayout->addWidget(contentWidget); + + contentWidget->setMinimumHeight(28); setLayout(vLayout); } @@ -52,7 +55,7 @@ QWidget* TitleBarWidget::createCustomTitleBarWidget(QWidget* parent) bool isDarkmode = PlatformHandler::isDarkMode(); QWidget* containerWidget = new QWidget(parent); - QHBoxLayout* containerLayout = new QHBoxLayout(parent); + mContainerLayout = new QHBoxLayout(parent); mCloseButton = new QToolButton(parent); @@ -104,25 +107,34 @@ QWidget* TitleBarWidget::createCustomTitleBarWidget(QWidget* parent) mTitleLabel->setAlignment(Qt::AlignVCenter); #ifdef __APPLE__ - containerLayout->addWidget(mCloseButton); - containerLayout->addWidget(mDockButton); - containerLayout->addWidget(mTitleLabel); + mContainerLayout->addWidget(mCloseButton); + mContainerLayout->addWidget(mDockButton); + mContainerLayout->addWidget(mTitleLabel); #else - containerLayout->addWidget(mTitleLabel); - containerLayout->addWidget(mDockButton); - containerLayout->addWidget(mCloseButton); + mContainerLayout->addWidget(mTitleLabel); + mContainerLayout->addWidget(mDockButton); + mContainerLayout->addWidget(mCloseButton); #endif - containerLayout->setSpacing(3); - containerLayout->setContentsMargins(0,0,0,0); + mContainerLayout->setSpacing(3); + mContainerLayout->setContentsMargins(0,0,0,0); - containerWidget->setLayout(containerLayout); + containerWidget->setLayout(mContainerLayout); containerWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); containerWidget->setMinimumSize(QSize(1,1)); return containerWidget; } +void TitleBarWidget::setChildWidget(QWidget* widget) +{ + if (mHasChildWidget) { + mContainerLayout->removeWidget(widget); + } + mContainerLayout->addWidget(widget); + mHasChildWidget = true; +} + QString TitleBarWidget::flatButtonStylesheet() const { return "QToolButton { border: 0; }"; @@ -133,6 +145,16 @@ void TitleBarWidget::setTitle(const QString &title) mTitleLabel->setText(title); } +void TitleBarWidget::lock(bool locked) +{ + if (locked) { + hideButtons(true); + } else { + hideButtonsIfNeeded(this->width()); + } + mIsLocked = locked; +} + void TitleBarWidget::hideButtons(bool hide) { mCloseButton->setHidden(hide); @@ -150,7 +172,7 @@ void TitleBarWidget::hideButtonsIfNeeded(int width) { if (width <= mWidthOfFullLayout) { hideButtons(true); - } else { + } else if (!mIsLocked) { hideButtons(false); } } diff --git a/app/src/titlebarwidget.h b/app/src/titlebarwidget.h index 70f1482725..4130d67277 100644 --- a/app/src/titlebarwidget.h +++ b/app/src/titlebarwidget.h @@ -23,6 +23,8 @@ GNU General Public License for more details. class QLabel; class QToolButton; +class QToolBar; +class QHBoxLayout; class TitleBarWidget : public QWidget { @@ -37,14 +39,23 @@ class TitleBarWidget : public QWidget void setIsFloating(bool floating) { mIsFloating = floating; } + /// Add a child widget next to the bar buttons + void setChildWidget(QWidget* widget); + + /// Locking the title area hides the title bar buttons + void lock(bool locked); + + /// Returns true if a custom child widget has been added, otherwise false + bool hasChildWidget() const { return mHasChildWidget; } + signals: void closeButtonPressed(); void undockButtonPressed(); private: + void hideButtons(bool hide); QString flatButtonStylesheet() const; void showEvent(QShowEvent* event) override; - void hideButtons(bool hide); void hideButtonsIfNeeded(int width); QWidget* createCustomTitleBarWidget(QWidget* parent); @@ -53,7 +64,11 @@ class TitleBarWidget : public QWidget QToolButton* mCloseButton = nullptr; QToolButton* mDockButton = nullptr; + QHBoxLayout* mContainerLayout = nullptr; + + bool mIsLocked = false; bool mIsFloating = false; + bool mHasChildWidget = false; int mWidthOfFullLayout = 0; }; diff --git a/core_lib/src/managers/preferencemanager.cpp b/core_lib/src/managers/preferencemanager.cpp index 8cb247aa63..08bc9b2413 100644 --- a/core_lib/src/managers/preferencemanager.cpp +++ b/core_lib/src/managers/preferencemanager.cpp @@ -98,7 +98,9 @@ void PreferenceManager::loadPrefs() set(SETTING::FPS, settings.value(SETTING_FPS, 12).toInt()); set(SETTING::FIELD_W, settings.value(SETTING_FIELD_W, 800).toInt()); set(SETTING::FIELD_H, settings.value(SETTING_FIELD_H, 600).toInt()); - set(SETTING::TIMECODE_TEXT, settings.value(SETTING_TIMECODE_TEXT, 1).toInt()); + set(SETTING::TIMECODE_ON, settings.value(SETTING_TIMECODE_ON, true).toBool()); + set(SETTING::TIMECODE_FRAMES_ON, settings.value(SETTING_TIMECODE_FRAMES_ON, true).toBool()); + set(SETTING::TIMECODE_KIND, settings.value(SETTING_TIMECODE_KIND, static_cast(TimecodeKind::SMPTE)).toInt()); // Files set(SETTING::AUTO_SAVE, settings.value(SETTING_AUTO_SAVE, false).toBool()); @@ -298,8 +300,8 @@ void PreferenceManager::set(SETTING option, int value) case SETTING::GRID_SIZE_W: settings.setValue(SETTING_GRID_SIZE_W, value); break; - case SETTING::TIMECODE_TEXT: - settings.setValue(SETTING_TIMECODE_TEXT, value); + case SETTING::TIMECODE_KIND: + settings.setValue(SETTING_TIMECODE_KIND, value); break; case SETTING::GRID_SIZE_H: settings.setValue(SETTING_GRID_SIZE_H, value); @@ -471,6 +473,12 @@ void PreferenceManager::set(SETTING option, bool value) case SETTING::NEW_UNDO_REDO_SYSTEM_ON: settings.setValue(SETTING_NEW_UNDO_REDO_ON, value); break; + case SETTING::TIMECODE_ON: + settings.setValue(SETTING_TIMECODE_ON, value); + break; + case SETTING::TIMECODE_FRAMES_ON: + settings.setValue(SETTING_TIMECODE_FRAMES_ON, value); + break; default: Q_ASSERT(false); break; diff --git a/core_lib/src/util/pencildef.h b/core_lib/src/util/pencildef.h index 00efcb73fd..9e907625d1 100644 --- a/core_lib/src/util/pencildef.h +++ b/core_lib/src/util/pencildef.h @@ -72,10 +72,9 @@ enum StabilizationLevel STRONG }; -enum TimecodeTextLevel +enum class TimecodeKind { - NOTEXT, - FRAMES, // FF + NONE, SMPTE, // HH:MM:SS:FF SFF // S:FF }; @@ -288,7 +287,12 @@ const static int MaxFramesBound = 9999; #define SETTING_ACTION_SAFE_ON "ActionSafeOn" #define SETTING_ACTION_SAFE "ActionSafe" #define SETTING_OVERLAY_SAFE_HELPER_TEXT_ON "OverlaySafeHelperTextOn" -#define SETTING_TIMECODE_TEXT "TimecodeText" + +#define SETTING_TIMECODE_TEXT "TimecodeText" + +#define SETTING_TIMECODE_ON "TimecodeEnabled" +#define SETTING_TIMECODE_KIND "TimecodeKind" +#define SETTING_TIMECODE_FRAMES_ON "TimecodeFramesEnabled" #define SETTING_ONION_MAX_OPACITY "OnionMaxOpacity" #define SETTING_ONION_MIN_OPACITY "OnionMinOpacity" diff --git a/core_lib/src/util/preferencesdef.h b/core_lib/src/util/preferencesdef.h index 66683c2e5a..d60177b56d 100644 --- a/core_lib/src/util/preferencesdef.h +++ b/core_lib/src/util/preferencesdef.h @@ -73,7 +73,9 @@ enum class SETTING OVERLAY_SAFE_HELPER_TEXT_ON, ACTION_SAFE_ON, ACTION_SAFE, - TIMECODE_TEXT, + TIMECODE_ON, + TIMECODE_KIND, + TIMECODE_FRAMES_ON, TITLE_SAFE_ON, TITLE_SAFE, NEW_UNDO_REDO_SYSTEM_ON,