more documentation and exportthread restructure

This commit is contained in:
itsmattkc
2019-03-21 14:01:10 +11:00
parent f126c4ebc7
commit be22e5fbfa
29 changed files with 743 additions and 376 deletions
+3
View File
@@ -34,6 +34,7 @@ AboutDialog::AboutDialog(QWidget *parent) :
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setSpacing(20);
// Construct About text
QLabel* label =
new QLabel(QString("<html><head/><body>"
"<p><img src=\":/icons/olive-splash.png\"/></p>"
@@ -49,6 +50,8 @@ AboutDialog::AboutDialog(QWidget *parent) :
"protected by the GNU GPL."),
tr("Olive Team is obliged to inform users that Olive source code is "
"available for download from its website.")), this);
// Set text formatting
label->setAlignment(Qt::AlignCenter);
label->setWordWrap(true);
layout->addWidget(label);
+14 -1
View File
@@ -23,11 +23,24 @@
#include <QDialog>
/**
* @brief The AboutDialog class
*
* The About dialog (accessible through Help > About). Contains license and version information.
*/
class AboutDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief AboutDialog Constructor
*
* Creates About dialog.
*
* @param parent
*
* QWidget parent object. Usually this will be MainWindow.
*/
explicit AboutDialog(QWidget *parent = nullptr);
};
+100 -3
View File
@@ -30,63 +30,133 @@
ActionSearch::ActionSearch(QWidget *parent) :
QDialog(parent)
{
// ActionSearch requires a parent widget
Q_ASSERT(parent != nullptr);
// Set styling (object name is required for CSS specific to this object)
setObjectName("ASDiag");
setStyleSheet("#ASDiag{border: 2px solid #808080;}");
// Size proportionally to the parent (usually MainWindow).
resize(parent->width()/3, parent->height()/3);
// Show dialog as a "popup", which will make the dialog close if the user clicks out of it.
setWindowFlags(Qt::Popup);
QVBoxLayout* layout = new QVBoxLayout(this);
// Construct the main entry text field.
ActionSearchEntry* entry_field = new ActionSearchEntry(this);
// Set the main entry field font size to 1.2x its standard font size.
QFont entry_field_font = entry_field->font();
entry_field_font.setPointSize(qRound(entry_field_font.pointSize()*1.2));
entry_field->setFont(entry_field_font);
// Set placeholder text for the main entry field
entry_field->setPlaceholderText(tr("Search for action..."));
// Connect signals/slots
connect(entry_field, SIGNAL(textChanged(const QString&)), this, SLOT(search_update(const QString &)));
connect(entry_field, SIGNAL(returnPressed()), this, SLOT(perform_action()));
// moveSelectionUp() and moveSelectionDown() are emitted when the user pressed up or down on the text field.
// We override it here to select the upper or lower item in the list.
connect(entry_field, SIGNAL(moveSelectionUp()), this, SLOT(move_selection_up()));
connect(entry_field, SIGNAL(moveSelectionDown()), this, SLOT(move_selection_down()));
layout->addWidget(entry_field);
// Construct list of actions
list_widget = new ActionSearchList(this);
// Set list's font to 1.2x its standard font size
QFont list_widget_font = list_widget->font();
list_widget_font.setPointSize(qRound(list_widget_font.pointSize()*1.2));
list_widget->setFont(list_widget_font);
layout->addWidget(list_widget);
connect(list_widget, SIGNAL(dbl_click()), this, SLOT(perform_action()));
// Instantly focus on the entry field to allow for fully keyboard operation (if this popup was initiated by keyboard
// shortcut for example).
entry_field->setFocus();
}
void ActionSearch::search_update(const QString &s, const QString &p, QMenu *parent) {
// This function is recursive, using the `parent` parameter to loop through a menu's items. It functions in two
// modes - the parent being NULL, meaning it'll get MainWindow's menubar and loop over its menus, and the parent
// referring to a menu at which point it'll loop over its actions (and call itself recursively if it finds any
// submenus).
if (parent == nullptr) {
// If parent is NULL, we'll pull from the MainWindow's menubar and call this recursively on all of its submenus
// (and their submenus).
// We'll clear all the current items in the list since if we're here, we're just starting.
list_widget->clear();
QList<QAction*> menus = olive::MainWindow->menuBar()->actions();
QList<QAction*> menus = olive::MainWindow->menuBar()->actions();
// Loop through all menus from the menubar and run this function on each one.
for (int i=0;i<menus.size();i++) {
QMenu* menu = menus.at(i)->menu();
search_update(s, p, menu);
}
if (list_widget->count() > 0)
// Once we're here, all the recursion/item retrieval is complete. We auto-select the first item for better
// keyboard-exclusive functionality.
if (list_widget->count() > 0) {
list_widget->item(0)->setSelected(true);
}
} else {
// Parent was not NULL, so we loop over the actions in the menu we were given in `parent`.
// The list shows a '>' delimited hierarchy of the menus in which this action came from. We construct it here by
// adding the current menu's text to the existing hierarchy (passed in `p`).
QString menu_text;
if (!p.isEmpty()) menu_text += p + " > ";
menu_text += parent->title().replace("&", "");
menu_text += parent->title().replace("&", ""); // Strip out any &s used in menu action names
// Loop over the menu's actions
QList<QAction*> actions = parent->actions();
for (int i=0;i<actions.size();i++) {
QAction* a = actions.at(i);
// Ignore separator actions
if (!a->isSeparator()) {
if (a->menu() != nullptr) {
// If the action is a menu, run this function recursively on it
search_update(s, menu_text, a->menu());
} else {
// This is a valid non-separator non-menu action, so check it against the currently entered string.
// Strip out all &s from the action's name
QString comp = a->text().replace("&", "");
// See if the action's name contains any of the currently entered string
if (comp.contains(s, Qt::CaseInsensitive)) {
// If so, we add it to the list widget.
QListWidgetItem* item = new QListWidgetItem(QString("%1\n(%2)").arg(comp, menu_text), list_widget);
// Add a pointer to the original QAction in the item's data
item->setData(Qt::UserRole+1, reinterpret_cast<quintptr>(a));
list_widget->addItem(item);
}
}
}
}
@@ -94,16 +164,31 @@ void ActionSearch::search_update(const QString &s, const QString &p, QMenu *pare
}
void ActionSearch::perform_action() {
// Loop over all the items in the list and if we find one that's selected, we trigger it.
QList<QListWidgetItem*> selected_items = list_widget->selectedItems();
if (list_widget->count() > 0 && selected_items.size() > 0) {
QListWidgetItem* item = selected_items.at(0);
// Get QAction pointer from item's data
QAction* a = reinterpret_cast<QAction*>(item->data(Qt::UserRole+1).value<quintptr>());
a->trigger();
}
// Close this popup
accept();
}
void ActionSearch::move_selection_up() {
// Here we loop over all the items to find the currently selected one, and then select the one above it. We start
// iterating at 1 (instead of 0) to efficiently ignore the first item (since the selection can't go below the very
// bottom item).
int lim = list_widget->count();
for (int i=1;i<lim;i++) {
if (list_widget->item(i)->isSelected()) {
@@ -115,6 +200,11 @@ void ActionSearch::move_selection_up() {
}
void ActionSearch::move_selection_down() {
// Here we loop over all the items to find the currently selected one, and then select the one below it. We limit it
// one entry before count() to efficiently ignore the item at the end (since the selection can't go below the very
// bottom item).
int lim = list_widget->count()-1;
for (int i=0;i<lim;i++) {
if (list_widget->item(i)->isSelected()) {
@@ -128,6 +218,9 @@ void ActionSearch::move_selection_down() {
ActionSearchEntry::ActionSearchEntry(QWidget *parent) : QLineEdit(parent) {}
void ActionSearchEntry::keyPressEvent(QKeyEvent * event) {
// Listen for up/down, otherwise pass the key event to the base class.
switch (event->key()) {
case Qt::Key_Up:
emit moveSelectionUp();
@@ -138,10 +231,14 @@ void ActionSearchEntry::keyPressEvent(QKeyEvent * event) {
default:
QLineEdit::keyPressEvent(event);
}
}
ActionSearchList::ActionSearchList(QWidget *parent) : QListWidget(parent) {}
void ActionSearchList::mouseDoubleClickEvent(QMouseEvent *) {
// Indiscriminately emit a signal on any double click
emit dbl_click();
}
+128 -22
View File
@@ -26,39 +26,145 @@
#include <QListWidget>
#include <QMenu>
class ActionSearchList : public QListWidget {
Q_OBJECT
public:
ActionSearchList(QWidget* parent);
protected:
void mouseDoubleClickEvent(QMouseEvent *event);
signals:
void dbl_click();
};
class ActionSearchList;
/**
* @brief The ActionSearch class
*
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
* rather than browsing through the menu bar.
*/
class ActionSearch : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
ActionSearch(QWidget* parent = nullptr);
/**
* @brief ActionSearch Constructor
*
* Create ActionSearch popup.
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
ActionSearch(QWidget* parent);
private slots:
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr);
void perform_action();
void move_selection_up();
void move_selection_down();
/**
* @brief Update the list of actions according to a search query
*
* This function adds/removes actions in the action list according to a given search query entered by the user.
*
* To loop over the menubar and all of its menus and submenus, this function will call itself recursively. As such
* some of its parameters do not need to be set externally, as these will be set by the function itself as it calls
* itself.
*
* @param s
*
* The search text. This is the only parameter that should be set externally.
*
* @param p
*
* The current parent hierarchy. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively.
*
* @param parent
*
* The current menu to loop over. In most cases, this should be left as nullptr when called externally.
* search_update() will fill this automatically as it needs while calling itself recursively.
*/
void search_update(const QString& s, const QString &p = nullptr, QMenu *parent = nullptr);
/**
* @brief Perform the currently selected action
*
* Usually triggered by pressing Enter on the ActionSearchEntry field, this will trigger whatever action is currently
* highlighted and then close this popup. If no entries are highlighted (i.e. the list is empty), no action is
* triggered and the popup closes anyway.
*/
void perform_action();
/**
* @brief Move selection up
*
* A slot for pressing up on the ActionSearchEntry field. Moves the selection in the list up once. If the
* selection is already at the top of the list, this is a no-op.
*/
void move_selection_up();
/**
* @brief Move selection down
*
* A slot for pressing down on the ActionSearchEntry field. Moves the selection in the list down once. If the
* selection is already at the bottom of the list, this is a no-op.
*/
void move_selection_down();
private:
ActionSearchList* list_widget;
/**
* @brief Main widget that shows the list of commands
*/
ActionSearchList* list_widget;
};
class ActionSearchEntry : public QLineEdit {
Q_OBJECT
/**
* @brief The ActionSearchList class
*
* Simple wrapper around QListWidget that emits a signal when an item is double clicked that ActionSearch connects
* to a slot that triggers the currently selected action.
*/
class ActionSearchList : public QListWidget {
Q_OBJECT
public:
ActionSearchEntry(QWidget* parent);
/**
* @brief ActionSearchList Constructor
* @param parent
*
* Usually ActionSearch.
*/
ActionSearchList(QWidget* parent);
protected:
void keyPressEvent(QKeyEvent * event);
/**
* @brief Override of QListWidget's double click event that emits a signal.
*/
void mouseDoubleClickEvent(QMouseEvent *);
signals:
void moveSelectionUp();
void moveSelectionDown();
/**
* @brief Signal emitted when a QListWidget item is double clicked.
*/
void dbl_click();
};
/**
* @brief The ActionSearchEntry class
*
* Simple wrapper around QLineEdit that emits signals when the up or down arrow keys are pressed so that ActionSearch
* can connect them to moving the current selection up or down.
*/
class ActionSearchEntry : public QLineEdit {
Q_OBJECT
public:
/**
* @brief ActionSearchEntry
* @param parent
*
* Usually ActionSearch.
*/
ActionSearchEntry(QWidget* parent);
protected:
/**
* @brief Override of QLineEdit's key press event that listens for up/down key presses.
* @param event
*/
void keyPressEvent(QKeyEvent * event);
signals:
/**
* @brief Emitted when the user presses the up arrow key.
*/
void moveSelectionUp();
/**
* @brief Emitted when the user presses the down arrow key.
*/
void moveSelectionDown();
};
#endif // ACTIONSEARCH_H
+13 -6
View File
@@ -94,16 +94,23 @@ void ClipPropertiesDialog::accept()
for (int i=0;i<clips_.size();i++) {
Clip* clip = clips_.at(i);
if (!clip_name.isEmpty()) {
// If the user entered a Clip name and the name is different from the Clip's name, create a rename command
if (!clip_name.isEmpty() && clip_name != clip->name()) {
ca->append(new RenameClipCommand(clip, clip_name));
}
// If the user entered a clip duration (and the duration has changed), create a "clip move" command
if (!qIsNaN(clip_duration)) {
clip->move(ca,
clip->timeline_in(),
clip->timeline_in() + qRound(clip_duration),
clip->clip_in(),
clip->track());
long clip_duration_rounded = qRound(clip_duration);
if (clip->length() != clip_duration_rounded) {
clip->move(ca,
clip->timeline_in(),
clip->timeline_in() + clip_duration_rounded,
clip->clip_in(),
clip->track());
}
}
}
+28
View File
@@ -7,16 +7,44 @@
#include "timeline/clip.h"
#include "ui/labelslider.h"
/**
* @brief The ClipPropertiesDialog class
*
* A dialog for setting Clip properties, accessible by right clicking a Clip and clicking "Properties".
*/
class ClipPropertiesDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief ClipPropertiesDialog Constructor
* @param parent
*
* Parent widget.
*
* @param clips
*
* Array of Clip objects to set the properties of.
*/
ClipPropertiesDialog(QWidget* parent, QVector<Clip*> clips);
protected:
/**
* @brief Accept override. Saves the current properties to the array of Clips.
*/
virtual void accept() override;
private:
/**
* @brief Internal clip array (set in the constructor)
*/
QVector<Clip*> clips_;
/**
* @brief Widget for setting the Clip names
*/
QLineEdit* clip_name_field_;
/**
* @brief Widget for setting the Clip durations
*/
LabelSlider* duration_field_;
};
+35 -5
View File
@@ -24,22 +24,52 @@
#include <QDialog>
#include <QTextEdit>
/**
* @brief The DebugDialog class
*
* A dialog to display the current debug output.
*/
class DebugDialog : public QDialog {
Q_OBJECT
Q_OBJECT
public:
DebugDialog(QWidget* parent = 0);
/**
* @brief DebugDialog Constructor
* @param parent
*
* Parent widget. Usually MainWindow.
*/
DebugDialog(QWidget* parent = nullptr);
/**
* @brief Retranslate window title
*
* Sets title based on the current translation.
*/
void Retranslate();
public slots:
void update_log();
/**
* @brief Update the visual log with the debug text from get_debug_str()
*/
void update_log();
protected:
/**
* @brief Overrides change event to trigger Retranslate() on a LanguageChange event.
*/
virtual void changeEvent(QEvent* e) override;
/**
* @brief Overrides show event to trigger an update of the visual log (the visual log does not update while the
* debug dialog is hidden).
*/
virtual void showEvent(QShowEvent* event) override;
private:
QTextEdit* textEdit;
/**
* @brief Display widget for the debug dialog.
*/
QTextEdit* textEdit;
};
namespace olive {
extern DebugDialog* DebugDialog;
extern DebugDialog* DebugDialog;
}
#endif // DEBUGDIALOG_H
+27 -27
View File
@@ -25,38 +25,38 @@
#include <QDialogButtonBox>
DemoNotice::DemoNotice(QWidget *parent) :
QDialog(parent)
QDialog(parent)
{
setWindowTitle(tr("Welcome to Olive!"));
setWindowTitle(tr("Welcome to Olive!"));
QVBoxLayout* vlayout = new QVBoxLayout(this);
QVBoxLayout* vlayout = new QVBoxLayout(this);
QHBoxLayout* layout = new QHBoxLayout();
layout->setMargin(10);
layout->setSpacing(20);
QHBoxLayout* layout = new QHBoxLayout();
layout->setMargin(10);
layout->setSpacing(20);
QLabel* icon = new QLabel("<html><head/><body>"
"<p><img src=\":/icons/olive-splash.png\"/></p>"
"</body></html>", this);
layout->addWidget(icon);
QLabel* icon = new QLabel("<html><head/><body>"
"<p><img src=\":/icons/olive-splash.png\"/></p>"
"</body></html>", this);
layout->addWidget(icon);
QLabel* text = new QLabel("<html><head/><body><p>"
"<span style=\" font-size:14pt;\">"
+ tr("Welcome to Olive!")
+ "</span></p><p>"
+ tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.")
+ "</p><p>"
+ tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("<a href=\"https://olivevideoeditor.org/\"><span style=\" text-decoration: underline; color:#007af4;\">www.olivevideoeditor.org</span></a>")
+ "</p><p>"
+ tr("Thank you for trying Olive and we hope you enjoy it!")
+ "</p></body></html>", this);
text->setWordWrap(true);
layout->addWidget(text);
QLabel* text = new QLabel("<html><head/><body><p>"
"<span style=\" font-size:14pt;\">"
+ tr("Welcome to Olive!")
+ "</span></p><p>"
+ tr("Olive is a free open-source video editor released under the GNU GPL. If you have paid for this software, you have been scammed.")
+ "</p><p>"
+ tr("This software is currently in ALPHA which means it is unstable and very likely to crash, have bugs, and have missing features. We offer no warranty so use at your own risk. Please report any bugs or feature requests at %1").arg("<a href=\"https://olivevideoeditor.org/\"><span style=\" text-decoration: underline; color:#007af4;\">www.olivevideoeditor.org</span></a>")
+ "</p><p>"
+ tr("Thank you for trying Olive and we hope you enjoy it!")
+ "</p></body></html>", this);
text->setWordWrap(true);
layout->addWidget(text);
vlayout->addLayout(layout);
vlayout->addLayout(layout);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
buttons->setCenterButtons(true);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
vlayout->addWidget(buttons);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this);
buttons->setCenterButtons(true);
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
vlayout->addWidget(buttons);
}
+13 -2
View File
@@ -23,11 +23,22 @@
#include <QDialog>
/**
* @brief The DemoNotice class
*
* Simple dialog shown on startup to introduce Olive as alpha software (in release builds).
*/
class DemoNotice : public QDialog
{
Q_OBJECT
Q_OBJECT
public:
explicit DemoNotice(QWidget *parent = 0);
/**
* @brief DemoNotice Constructor
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
explicit DemoNotice(QWidget *parent = nullptr);
};
#endif // DEMONOTICE_H
+30 -19
View File
@@ -20,6 +20,10 @@
#include "exportdialog.h"
extern "C" {
#include <libavformat/avformat.h>
}
#include <QOpenGLWidget>
#include <QFileDialog>
#include <QThread>
@@ -39,10 +43,6 @@
#include "rendering/exportthread.h"
#include "ui/mainwindow.h"
extern "C" {
#include <libavformat/avformat.h>
}
enum ExportFormats {
FORMAT_3GPP,
FORMAT_AIFF,
@@ -118,9 +118,6 @@ ExportDialog::ExportDialog(QWidget *parent) :
vcodec_params.threads = 0;
}
ExportDialog::~ExportDialog()
{}
void ExportDialog::add_codec_to_combobox(QComboBox* box, enum AVCodecID codec) {
QString codec_name;
@@ -334,21 +331,42 @@ void ExportDialog::format_changed(int index) {
}
void ExportDialog::render_thread_finished() {
if (progressBar->value() < 100 && !cancelled) {
// Determine if the export succeeded
bool succeeded = (progressBar->value() == 100);
// If it failed and we didn't cancel it, it must have errored out. Show an error message.
if (!succeeded && !et->WasInterrupted()) {
QMessageBox::critical(
this,
tr("Export Failed"),
tr("Export failed - %1").arg(export_error),
tr("Export failed - %1").arg(et->GetError()),
QMessageBox::Ok
);
}
// Clear audio buffer
clear_audio_ibuffer();
// Re-enable/disable UI widgets based on the rendering state
prep_ui_for_render(false);
// Move OpenGL context back to the sequence viewer
panel_sequence_viewer->viewer_widget->makeCurrent();
panel_sequence_viewer->viewer_widget->initializeGL();
// Update the application UI
update_ui(false);
// Disconnect cancel button from export thread
disconnect(renderCancel, SIGNAL(clicked(bool)), et, SLOT(Interrupt()));
// Free the export thread
et->deleteLater();
if (progressBar->value() == 100) accept();
// If the export succeeded, close the dialog
if (succeeded) {
accept();
}
}
void ExportDialog::prep_ui_for_render(bool r) {
@@ -543,7 +561,8 @@ void ExportDialog::export_action() {
et = new ExportThread(params, vcodec_params, this);
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
connect(et, SIGNAL(progress_changed(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
connect(et, SIGNAL(ProgressChanged(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
connect(renderCancel, SIGNAL(clicked(bool)), et, SLOT(Interrupt()));
close_active_clips(olive::ActiveSequence.get());
@@ -553,8 +572,6 @@ void ExportDialog::export_action() {
prep_ui_for_render(true);
cancelled = false;
total_export_time_start = QDateTime::currentMSecsSinceEpoch();
et->start();
@@ -587,11 +604,6 @@ void ExportDialog::update_progress_bar(int value, qint64 remaining_ms) {
progressBar->setValue(value);
}
void ExportDialog::cancel_render() {
et->continueEncode = false;
cancelled = true;
}
void ExportDialog::vcodec_changed(int index) {
compressionTypeCombobox->clear();
@@ -755,7 +767,6 @@ void ExportDialog::setup_ui() {
renderCancel = new QPushButton(this);
renderCancel->setIcon(QIcon(":/icons/error.svg"));
renderCancel->setEnabled(false);
connect(renderCancel, SIGNAL(clicked(bool)), this, SLOT(cancel_render()));
progressLayout->addWidget(renderCancel);
verticalLayout->addLayout(progressLayout);
+16 -9
View File
@@ -30,34 +30,41 @@
#include <QGroupBox>
#include "timeline/sequence.h"
#include "rendering/exportthread.h"
/**
* @brief The ExportDialog class
*
* The dialog to initiate an export.
*/
class ExportDialog : public QDialog
{
Q_OBJECT
public:
explicit ExportDialog(QWidget *parent = nullptr);
~ExportDialog();
QString export_error;
/**
* @brief ExportDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
explicit ExportDialog(QWidget *parent);
private slots:
void format_changed(int index);
void export_action();
void update_progress_bar(int value, qint64 remaining_ms);
void cancel_render();
void render_thread_finished();
void vcodec_changed(int index);
void comp_type_changed(int index);
void open_advanced_video_dialog();
private:
QVector<QString> format_strings;
void setup_ui();
ExportThread* et;
void prep_ui_for_render(bool r);
bool cancelled;
QVector<QString> format_strings;
ExportThread* et;
void add_codec_to_combobox(QComboBox* box, enum AVCodecID codec);