diff --git a/effects/effectfield.cpp b/effects/effectfield.cpp index 3afec20c0..0321e8d76 100644 --- a/effects/effectfield.cpp +++ b/effects/effectfield.cpp @@ -95,9 +95,9 @@ QString EffectField::ConvertValueToString(const QVariant &v) return v.toString(); } -QWidget *EffectField::CreateWidget() +QWidget *EffectField::CreateWidget(QWidget *existing) { - return new QLabel(tr("(Invalid field)")); + return (existing == nullptr) ? new QLabel(tr("(Invalid field)")) : existing; } void EffectField::UpdateWidgetValue(QWidget *, double) {} diff --git a/effects/effectfield.h b/effects/effectfield.h index 767090920..af3e236e6 100644 --- a/effects/effectfield.h +++ b/effects/effectfield.h @@ -68,7 +68,7 @@ public: virtual QVariant ConvertStringToValue(const QString& s); virtual QString ConvertValueToString(const QVariant& v); - virtual QWidget* CreateWidget(); + virtual QWidget* CreateWidget(QWidget* existing = nullptr); virtual void UpdateWidgetValue(QWidget* widget, double); double GetValidKeyframeHandlePosition(int key, bool post); diff --git a/effects/effectrow.cpp b/effects/effectrow.cpp index 713222927..d61980fa2 100644 --- a/effects/effectrow.cpp +++ b/effects/effectrow.cpp @@ -71,6 +71,11 @@ bool EffectRow::IsSavable() return savable_; } +bool EffectRow::IsKeyframable() +{ + return keyframable_; +} + void EffectRow::SetKeyframingEnabled(bool enabled) { if (enabled) { diff --git a/effects/effectrow.h b/effects/effectrow.h index b3be99aed..71df69f15 100644 --- a/effects/effectrow.h +++ b/effects/effectrow.h @@ -56,6 +56,7 @@ public: void SetKeyframingInternal(bool); bool IsSavable(); + bool IsKeyframable(); public slots: void GoToPreviousKeyframe(); void ToggleKeyframe(); diff --git a/effects/fields/boolfield.cpp b/effects/fields/boolfield.cpp index f47f1d5e3..7fc7c2a4d 100644 --- a/effects/fields/boolfield.cpp +++ b/effects/fields/boolfield.cpp @@ -11,9 +11,20 @@ bool BoolField::GetBoolAt(double timecode) return GetValueAt(timecode).toBool(); } -QWidget *BoolField::CreateWidget() +QWidget *BoolField::CreateWidget(QWidget *existing) { - QCheckBox* cb = new QCheckBox(); + QCheckBox* cb; + + if (existing == nullptr) { + + cb = new QCheckBox(); + cb->setEnabled(IsEnabled()); + + } else { + + cb = static_cast(existing); + + } connect(cb, SIGNAL(toggled(bool)), this, SLOT(UpdateFromWidget(bool))); connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool))); diff --git a/effects/fields/boolfield.h b/effects/fields/boolfield.h index 0ac09a01e..5af612530 100644 --- a/effects/fields/boolfield.h +++ b/effects/fields/boolfield.h @@ -11,7 +11,7 @@ public: bool GetBoolAt(double timecode); - virtual QWidget* CreateWidget() override; + virtual QWidget* CreateWidget(QWidget *existing = nullptr) override; virtual void UpdateWidgetValue(QWidget* widget, double timecode) override; virtual QVariant ConvertStringToValue(const QString& s) override; diff --git a/effects/fields/buttonfield.cpp b/effects/fields/buttonfield.cpp index 57845808f..23cc860af 100644 --- a/effects/fields/buttonfield.cpp +++ b/effects/fields/buttonfield.cpp @@ -18,13 +18,23 @@ void ButtonField::SetChecked(bool c) emit CheckedChanged(c); } -QWidget *ButtonField::CreateWidget() +QWidget *ButtonField::CreateWidget(QWidget *existing) { - QPushButton* button = new QPushButton(); + QPushButton* button; - button->setCheckable(checkable_); - button->setEnabled(IsEnabled()); - button->setText(button_text_); + if (existing == nullptr) { + + button = new QPushButton(); + + button->setCheckable(checkable_); + button->setEnabled(IsEnabled()); + button->setText(button_text_); + + } else { + + button = static_cast(existing); + + } connect(this, SIGNAL(CheckedChanged(bool)), button, SLOT(setChecked(bool))); connect(this, SIGNAL(EnabledChanged(bool)), button, SLOT(setEnabled(bool))); diff --git a/effects/fields/buttonfield.h b/effects/fields/buttonfield.h index c3e3150f7..940e458e5 100644 --- a/effects/fields/buttonfield.h +++ b/effects/fields/buttonfield.h @@ -10,7 +10,7 @@ public: ButtonField(EffectRow* parent, const QString& string); void SetCheckable(bool c); - virtual QWidget* CreateWidget() override; + virtual QWidget* CreateWidget(QWidget *existing = nullptr) override; public slots: void SetChecked(bool c); diff --git a/effects/fields/colorfield.cpp b/effects/fields/colorfield.cpp index a48c4b841..a70135af4 100644 --- a/effects/fields/colorfield.cpp +++ b/effects/fields/colorfield.cpp @@ -13,9 +13,9 @@ QColor ColorField::GetColorAt(double timecode) return GetValueAt(timecode).value(); } -QWidget *ColorField::CreateWidget() +QWidget *ColorField::CreateWidget(QWidget *existing) { - ColorButton* cb = new ColorButton(); + ColorButton* cb = (existing != nullptr) ? static_cast(existing) : new ColorButton(); connect(cb, SIGNAL(color_changed(const QColor &)), this, SLOT(UpdateFromWidget(const QColor &))); connect(this, SIGNAL(EnabledChanged(bool)), cb, SLOT(setEnabled(bool))); diff --git a/effects/fields/colorfield.h b/effects/fields/colorfield.h index 9ff0b03c4..ab32e4b52 100644 --- a/effects/fields/colorfield.h +++ b/effects/fields/colorfield.h @@ -11,7 +11,7 @@ public: QColor GetColorAt(double timecode); - virtual QWidget* CreateWidget() override; + virtual QWidget* CreateWidget(QWidget *existing = nullptr) override; virtual void UpdateWidgetValue(QWidget* widget, double timecode) override; virtual QVariant ConvertStringToValue(const QString& s) override; diff --git a/effects/fields/combofield.cpp b/effects/fields/combofield.cpp index 828705f67..ad43bb4f5 100644 --- a/effects/fields/combofield.cpp +++ b/effects/fields/combofield.cpp @@ -18,14 +18,20 @@ void ComboField::AddItem(const QString &text, const QVariant &data) items_.append(item); } -QWidget *ComboField::CreateWidget() +QWidget *ComboField::CreateWidget(QWidget *existing) { - ComboBoxEx* cb = new ComboBoxEx(); + ComboBoxEx* cb; - cb->setScrollingEnabled(false); + if (existing == nullptr) { + cb = new ComboBoxEx(); - for (int i=0;iaddItem(items_.at(i).name); + cb->setScrollingEnabled(false); + + for (int i=0;iaddItem(items_.at(i).name); + } + } else { + cb = static_cast(existing); } connect(cb, SIGNAL(activated(int)), this, SLOT(UpdateFromWidget(int))); diff --git a/effects/fields/combofield.h b/effects/fields/combofield.h index d9c81e60a..c2b1cb295 100644 --- a/effects/fields/combofield.h +++ b/effects/fields/combofield.h @@ -16,7 +16,7 @@ public: void AddItem(const QString& text, const QVariant& data); - virtual QWidget *CreateWidget() override; + virtual QWidget *CreateWidget(QWidget *existing = nullptr) override; virtual void UpdateWidgetValue(QWidget* widget, double timecode) override; signals: diff --git a/effects/fields/doublefield.cpp b/effects/fields/doublefield.cpp index 900eb84b2..64d338587 100644 --- a/effects/fields/doublefield.cpp +++ b/effects/fields/doublefield.cpp @@ -60,22 +60,31 @@ QString DoubleField::ConvertValueToString(const QVariant &v) return QString::number(v.toDouble()); } -QWidget *DoubleField::CreateWidget() +QWidget *DoubleField::CreateWidget(QWidget *existing) { - LabelSlider* ls = new LabelSlider(); + LabelSlider* ls; - if (!qIsNaN(min_)) { - ls->SetMinimum(min_); - } - ls->SetDefault(default_); - if (!qIsNaN(max_)) { - ls->SetMaximum(max_); - } - ls->SetDisplayType(display_type_); - ls->SetFrameRate(frame_rate_); + if (existing == nullptr) { - //qDebug() << ""; - ls->setEnabled(IsEnabled()); + ls = new LabelSlider(); + + if (!qIsNaN(min_)) { + ls->SetMinimum(min_); + } + ls->SetDefault(default_); + if (!qIsNaN(max_)) { + ls->SetMaximum(max_); + } + ls->SetDisplayType(display_type_); + ls->SetFrameRate(frame_rate_); + + ls->setEnabled(IsEnabled()); + + } else { + + ls = static_cast(existing); + + } connect(ls, SIGNAL(valueChanged(double)), this, SLOT(UpdateFromWidget(double))); connect(ls, SIGNAL(clicked()), this, SIGNAL(Clicked())); diff --git a/effects/fields/doublefield.h b/effects/fields/doublefield.h index 935e895bf..4ba53da8a 100644 --- a/effects/fields/doublefield.h +++ b/effects/fields/doublefield.h @@ -32,10 +32,10 @@ public: void SetDisplayType(LabelSlider::DisplayType type); void SetFrameRate(const double& rate); - virtual QVariant ConvertStringToValue(const QString& s); - virtual QString ConvertValueToString(const QVariant& v); + virtual QVariant ConvertStringToValue(const QString& s) override; + virtual QString ConvertValueToString(const QVariant& v) override; - virtual QWidget* CreateWidget() override; + virtual QWidget* CreateWidget(QWidget *existing = nullptr) override; virtual void UpdateWidgetValue(QWidget* widget, double timecode) override; private: double min_; diff --git a/effects/fields/filefield.cpp b/effects/fields/filefield.cpp index 27698c94c..bf5125ae8 100644 --- a/effects/fields/filefield.cpp +++ b/effects/fields/filefield.cpp @@ -13,9 +13,9 @@ QString FileField::GetFileAt(double timecode) return GetValueAt(timecode).toString(); } -QWidget *FileField::CreateWidget() +QWidget *FileField::CreateWidget(QWidget *existing) { - EmbeddedFileChooser* efc = new EmbeddedFileChooser(); + EmbeddedFileChooser* efc = (existing != nullptr) ? static_cast(existing) : new EmbeddedFileChooser(); connect(efc, SIGNAL(changed(const QString&)), this, SLOT(UpdateFromWidget(const QString&))); connect(this, SIGNAL(EnabledChanged(bool)), efc, SLOT(setEnabled(bool))); diff --git a/effects/fields/filefield.h b/effects/fields/filefield.h index ef1e1e099..88c3f0237 100644 --- a/effects/fields/filefield.h +++ b/effects/fields/filefield.h @@ -11,7 +11,7 @@ public: QString GetFileAt(double timecode); - virtual QWidget* CreateWidget() override; + virtual QWidget* CreateWidget(QWidget *existing = nullptr) override; private slots: void UpdateFromWidget(const QString &s); }; diff --git a/effects/fields/fontfield.cpp b/effects/fields/fontfield.cpp index c95c87697..8ee06877a 100644 --- a/effects/fields/fontfield.cpp +++ b/effects/fields/fontfield.cpp @@ -20,13 +20,23 @@ QString FontField::GetFontAt(double timecode) return GetValueAt(timecode).toString(); } -QWidget *FontField::CreateWidget() +QWidget *FontField::CreateWidget(QWidget *existing) { ComboBoxEx* fcb = new ComboBoxEx(); - fcb->setScrollingEnabled(false); + if (existing == nullptr) { - fcb->addItems(font_list); + fcb = new ComboBoxEx(); + + fcb->setScrollingEnabled(false); + + fcb->addItems(font_list); + + } else { + + fcb = static_cast(existing); + + } connect(fcb, SIGNAL(currentTextChanged(const QString &)), this, SLOT(UpdateFromWidget(const QString &))); connect(this, SIGNAL(EnabledChanged(bool)), fcb, SLOT(setEnabled(bool))); diff --git a/effects/fields/fontfield.h b/effects/fields/fontfield.h index f8b095d2b..9df3e76ae 100644 --- a/effects/fields/fontfield.h +++ b/effects/fields/fontfield.h @@ -10,7 +10,7 @@ public: QString GetFontAt(double timecode); - virtual QWidget *CreateWidget() override; + virtual QWidget *CreateWidget(QWidget *existing = nullptr) override; virtual void UpdateWidgetValue(QWidget* widget, double timecode) override; private: diff --git a/effects/fields/labelfield.cpp b/effects/fields/labelfield.cpp index e50f337d8..b3eccfa6d 100644 --- a/effects/fields/labelfield.cpp +++ b/effects/fields/labelfield.cpp @@ -7,11 +7,22 @@ LabelField::LabelField(EffectRow *parent, const QString &string) : label_text_(string) {} -QWidget *LabelField::CreateWidget() +QWidget *LabelField::CreateWidget(QWidget *existing) { - QLabel* label = new QLabel(label_text_); + QLabel* label; + + if (existing == nullptr) { + + label = new QLabel(label_text_); + + label->setEnabled(IsEnabled()); + + } else { + + label = static_cast(existing); + + } - label->setEnabled(IsEnabled()); connect(this, SIGNAL(EnabledChanged(bool)), label, SLOT(setEnabled(bool))); return label; diff --git a/effects/fields/labelfield.h b/effects/fields/labelfield.h index 31a40462b..67b4728f4 100644 --- a/effects/fields/labelfield.h +++ b/effects/fields/labelfield.h @@ -9,7 +9,7 @@ class LabelField : public EffectField public: LabelField(EffectRow* parent, const QString& string); - virtual QWidget* CreateWidget() override; + virtual QWidget* CreateWidget(QWidget *existing = nullptr) override; private: QString label_text_; }; diff --git a/effects/fields/stringfield.cpp b/effects/fields/stringfield.cpp index 45d7b023c..55b59752d 100644 --- a/effects/fields/stringfield.cpp +++ b/effects/fields/stringfield.cpp @@ -18,17 +18,27 @@ QString StringField::GetStringAt(double timecode) return GetValueAt(timecode).toString(); } -QWidget *StringField::CreateWidget() +QWidget *StringField::CreateWidget(QWidget *existing) { - TextEditEx* text_edit = new TextEditEx(nullptr, rich_text_); + TextEditEx* text_edit; - text_edit->setEnabled(IsEnabled()); - text_edit->setUndoRedoEnabled(true); + if (existing == nullptr) { - // the "2" looks like a magic number, but it's just one pixel on the top and the bottom - text_edit->setFixedHeight(qCeil(text_edit->fontMetrics().lineSpacing()*olive::CurrentConfig.effect_textbox_lines - + text_edit->document()->documentMargin() - + text_edit->document()->documentMargin() + 2)); + text_edit = new TextEditEx(nullptr, rich_text_); + + text_edit->setEnabled(IsEnabled()); + text_edit->setUndoRedoEnabled(true); + + // the "2" is because the height needs one extra pixel of padding on the top and the bottom + text_edit->setFixedHeight(qCeil(text_edit->fontMetrics().lineSpacing()*olive::CurrentConfig.effect_textbox_lines + + text_edit->document()->documentMargin() + + text_edit->document()->documentMargin() + 2)); + + } else { + + text_edit = static_cast(existing); + + } connect(text_edit, SIGNAL(textModified(const QString&)), this, SLOT(UpdateFromWidget(const QString&))); connect(this, SIGNAL(EnabledChanged(bool)), text_edit, SLOT(setEnabled(bool))); diff --git a/effects/fields/stringfield.h b/effects/fields/stringfield.h index 6202afb27..01eb26e9d 100644 --- a/effects/fields/stringfield.h +++ b/effects/fields/stringfield.h @@ -11,7 +11,7 @@ public: QString GetStringAt(double timecode); - virtual QWidget *CreateWidget() override; + virtual QWidget *CreateWidget(QWidget *existing = nullptr) override; virtual void UpdateWidgetValue(QWidget* widget, double timecode) override; private slots: void UpdateFromWidget(const QString& b); diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp index 7ad83e43a..a221c8f98 100644 --- a/panels/effectcontrols.cpp +++ b/panels/effectcontrols.cpp @@ -601,8 +601,11 @@ void EffectControls::Load() { bool already_opened = false; for (int k=0;kGetEffect()->meta == c->effects.at(j)->meta) { + open_effects_.at(k)->AddAdditionalEffect(c->effects.at(j).get()); + already_opened = true; + break; } } diff --git a/ui/collapsiblewidget.cpp b/ui/collapsiblewidget.cpp index fb3e94bc6..e81c3dbc8 100644 --- a/ui/collapsiblewidget.cpp +++ b/ui/collapsiblewidget.cpp @@ -53,7 +53,7 @@ CollapsibleWidget::CollapsibleWidget(QWidget* parent) : QWidget(parent) { collapse_button = new QPushButton(title_bar); collapse_button->setIconSize(collapse_button->iconSize()*0.5); collapse_button->setFlat(true); - SetText(tr("")); + SetTitle(tr("")); title_bar_layout->addWidget(collapse_button); title_bar_layout->addWidget(enabled_check); title_bar_layout->addWidget(header); @@ -108,7 +108,12 @@ void CollapsibleWidget::SetContents(QWidget* c) { } } -void CollapsibleWidget::SetText(const QString &s) { +QString CollapsibleWidget::Title() +{ + return header->text(); +} + +void CollapsibleWidget::SetTitle(const QString &s) { header->setText(s); } diff --git a/ui/collapsiblewidget.h b/ui/collapsiblewidget.h index d13ebd300..8aa67ebb4 100644 --- a/ui/collapsiblewidget.h +++ b/ui/collapsiblewidget.h @@ -50,7 +50,8 @@ class CollapsibleWidget : public QWidget public: CollapsibleWidget(QWidget* parent = nullptr); void SetContents(QWidget* c); - void SetText(const QString &); + QString Title(); + void SetTitle(const QString &); bool IsFocused(); bool IsExpanded(); bool IsSelected(); diff --git a/ui/effectui.cpp b/ui/effectui.cpp index b79b5243f..e5e5ac734 100644 --- a/ui/effectui.cpp +++ b/ui/effectui.cpp @@ -6,14 +6,16 @@ #include "timeline/clip.h" #include "ui/menuhelper.h" #include "ui/keyframenavigator.h" +#include "ui/clickablelabel.h" #include "panels/panels.h" EffectUI::EffectUI(Effect* e) : - effect_(e) + effect_(e), + multiple_(false) { Q_ASSERT(e != nullptr); - SetText(e->name); + SetTitle(e->name); QWidget* ui = new QWidget(this); SetContents(ui); @@ -33,7 +35,8 @@ EffectUI::EffectUI(Effect* e) : for (int i=0;irow_count();i++) { EffectRow* row = e->row(i); - QLabel* row_label = new QLabel(row->name()); + ClickableLabel* row_label = new ClickableLabel(row->name()); + connect(row_label, SIGNAL(clicked()), row, SLOT(FocusRow())); labels_.append(row_label); @@ -61,21 +64,32 @@ EffectUI::EffectUI(Effect* e) : // Create keyframe controls maximum_column++; + keyframe_navigators_.resize(e->row_count()); + for (int i=0;irow_count();i++) { EffectRow* row = e->row(i); - KeyframeNavigator* nav = new KeyframeNavigator(); + KeyframeNavigator* nav; - nav->enable_keyframes(row->IsKeyframing()); + if (row->IsKeyframable()) { - connect(nav, SIGNAL(goto_previous_key()), row, SLOT(GoToPreviousKeyframe())); - connect(nav, SIGNAL(toggle_key()), row, SLOT(ToggleKeyframe())); - connect(nav, SIGNAL(goto_next_key()), row, SLOT(GoToNextKeyframe())); - connect(nav, SIGNAL(keyframe_enabled_changed(bool)), row, SLOT(SetKeyframingEnabled(bool))); - connect(nav, SIGNAL(clicked()), row, SLOT(FocusRow())); - connect(row, SIGNAL(KeyframingSetChanged(bool)), nav, SLOT(enable_keyframes(bool))); + nav = new KeyframeNavigator(); + + nav->enable_keyframes(row->IsKeyframing()); + + AttachKeyframeNavigationToRow(row, nav); + + layout_->addWidget(nav, i, maximum_column); + + } else { + + nav = nullptr; + + } + + + keyframe_navigators_[i] = nav; - layout_->addWidget(nav, i, maximum_column); } enabled_check->setChecked(e->IsEnabled()); @@ -85,7 +99,34 @@ EffectUI::EffectUI(Effect* e) : void EffectUI::AddAdditionalEffect(Effect *e) { + // Ensure this is the same kind of effect and will be fully compatible + Q_ASSERT(e->meta == effect_->meta); + + // Add multiple modifer to header label + QString new_title = QString(tr("%1 (multiple)")).arg(Title()); + SetTitle(new_title); + + // Add effect to list additional_effects_.append(e); + + // Attach this UI's widgets to the additional effect + for (int i=0;irow_count();i++) { + + EffectRow* row = effect_->row(i); + + // Attach existing keyframe navigator to this effect's row + AttachKeyframeNavigationToRow(e->row(i), keyframe_navigators_.at(i)); + + for (int j=0;jFieldCount();j++) { + + EffectField* field = row->Field(j); + + // Attach existing field widget to this effect's field + e->row(i)->Field(j)->CreateWidget(Widget(i, j)); + + } + + } } Effect *EffectUI::GetEffect() @@ -94,7 +135,13 @@ Effect *EffectUI::GetEffect() } int EffectUI::GetRowY(int row, QWidget* mapToWidget) { + + // Currently to get a Y value in the context of `mapToWidget`, we use `panel_effect_controls` as the base. Mapping + // to global doesn't work for some reason, so this is the best reference point we have. + QLabel* row_label = labels_.at(row); + + // Get center point of label (label->rect()->center()->y() - instead of y()+height/2 - produces an inaccurate result) return row_label->y() + row_label->height() / 2 + mapToWidget->mapFrom(panel_effect_controls, contents->mapTo(panel_effect_controls, contents->pos())).y() @@ -147,6 +194,20 @@ QWidget *EffectUI::Widget(int row, int field) return widgets_.at(row).at(field); } +void EffectUI::AttachKeyframeNavigationToRow(EffectRow *row, KeyframeNavigator *nav) +{ + if (nav == nullptr) { + return; + } + + connect(nav, SIGNAL(goto_previous_key()), row, SLOT(GoToPreviousKeyframe())); + connect(nav, SIGNAL(toggle_key()), row, SLOT(ToggleKeyframe())); + connect(nav, SIGNAL(goto_next_key()), row, SLOT(GoToNextKeyframe())); + connect(nav, SIGNAL(keyframe_enabled_changed(bool)), row, SLOT(SetKeyframingEnabled(bool))); + connect(nav, SIGNAL(clicked()), row, SLOT(FocusRow())); + connect(row, SIGNAL(KeyframingSetChanged(bool)), nav, SLOT(enable_keyframes(bool))); +} + void EffectUI::show_context_menu(const QPoint& pos) { if (effect_->meta->type == EFFECT_TYPE_EFFECT) { QMenu menu; diff --git a/ui/effectui.h b/ui/effectui.h index b63434b07..7e448a8d9 100644 --- a/ui/effectui.h +++ b/ui/effectui.h @@ -29,6 +29,11 @@ private: QGridLayout* layout_; QVector labels_; QVector< QVector > widgets_; + QVector keyframe_navigators_; + + bool multiple_; + + void AttachKeyframeNavigationToRow(EffectRow* row, KeyframeNavigator* nav); private slots: void show_context_menu(const QPoint&); };