refactor: invert the last engine-to-UI dependencies
- NodeFactory's menu creation moves to UI-side widget/menu/factorymenu (the factory only exposes its node library read-only now) - DiskManager's cache-settings dialog is created through a registered std::function handler (registered by Core at startup) - OlivePluginInstance creates progress UIs through a PluginProgressReporter interface (Null fallback headless) and queries the active viewer through a provider callback, both registered by Core - factory.h, diskmanager and pluginSupport no longer reference any widget//dialog//panel//window headers or classes
This commit is contained in:
@@ -44,11 +44,13 @@
|
||||
#include "config/config.h"
|
||||
#include "dialog/about/about.h"
|
||||
#include "dialog/autorecovery/autorecoverydialog.h"
|
||||
#include "dialog/diskcache/diskcachedialog.h"
|
||||
#include "dialog/export/export.h"
|
||||
#include "dialog/footagerelink/footagerelinkdialog.h"
|
||||
#ifdef USE_OTIO
|
||||
#include "dialog/otioproperties/otiopropertiesdialog.h"
|
||||
#endif
|
||||
#include "dialog/progress/pluginprogressdialogreporter.h"
|
||||
#include "dialog/projectproperties/projectproperties.h"
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "dialog/task/task.h"
|
||||
@@ -56,7 +58,11 @@
|
||||
#include "node/nodeundo.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "panel/project/project.h"
|
||||
#include "panel/timebased/timebased.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
#include "panel/viewer/viewer.h"
|
||||
#include "pluginSupport/oliveplugininstance.h"
|
||||
#include "pluginSupport/pluginprogressreporter.h"
|
||||
#include "render/diskmanager.h"
|
||||
#ifdef USE_OTIO
|
||||
#include "task/project/loadotio/loadotio.h"
|
||||
@@ -103,6 +109,45 @@ Core::Core(const CoreParams ¶ms)
|
||||
return DialogImportOTIOShow(sequences);
|
||||
});
|
||||
#endif
|
||||
|
||||
// Disk cache settings dialog (engine -> UI)
|
||||
DiskManager::set_show_disk_cache_settings_handler(
|
||||
[](DiskCacheFolder *folder, QWidget *parent) {
|
||||
DiskCacheDialog d(folder, parent);
|
||||
d.exec();
|
||||
});
|
||||
|
||||
// OFX plugin progress dialog (engine -> UI)
|
||||
plugin::set_plugin_progress_reporter_factory(
|
||||
[](const QString &message,
|
||||
const QString &title) -> plugin::PluginProgressReporter * {
|
||||
return new PluginProgressDialogReporter(message, title);
|
||||
});
|
||||
|
||||
// OFX timeline suite: resolve the active viewer through the panels
|
||||
plugin::set_active_viewer_provider([]() -> ViewerOutput * {
|
||||
PanelManager *manager = PanelManager::instance();
|
||||
if (!manager) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto *time_panel =
|
||||
manager->most_recently_focused<TimeBasedPanel>()) {
|
||||
if (time_panel->get_connected_viewer()) {
|
||||
return time_panel->get_connected_viewer();
|
||||
}
|
||||
}
|
||||
|
||||
QList<TimelinePanel *> timelines =
|
||||
manager->get_panels_of_type<TimelinePanel>();
|
||||
for (TimelinePanel *panel : timelines) {
|
||||
if (panel && panel->get_connected_viewer()) {
|
||||
return panel->get_connected_viewer();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void Core::start()
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
dialog/progress/pluginprogressdialogreporter.h
|
||||
dialog/progress/pluginprogressdialogreporter.cpp
|
||||
dialog/progress/progress.h
|
||||
dialog/progress/progress.cpp
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "pluginprogressdialogreporter.h"
|
||||
|
||||
#include "dialog/progress/progress.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
PluginProgressDialogReporter::PluginProgressDialogReporter(
|
||||
const QString &message, const QString &title)
|
||||
: dialog_(new ProgressDialog(message, title, nullptr))
|
||||
{
|
||||
dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
||||
connect(dialog_, &ProgressDialog::cancelled, this,
|
||||
&PluginProgressReporter::cancelled);
|
||||
}
|
||||
|
||||
PluginProgressDialogReporter::~PluginProgressDialogReporter()
|
||||
{
|
||||
delete dialog_;
|
||||
}
|
||||
|
||||
void PluginProgressDialogReporter::set_progress(double value)
|
||||
{
|
||||
if (dialog_) {
|
||||
dialog_->set_progress(value);
|
||||
}
|
||||
}
|
||||
|
||||
void PluginProgressDialogReporter::show()
|
||||
{
|
||||
if (dialog_) {
|
||||
dialog_->show();
|
||||
}
|
||||
}
|
||||
|
||||
void PluginProgressDialogReporter::close()
|
||||
{
|
||||
if (dialog_) {
|
||||
dialog_->close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_PLUGINPROGRESSDIALOGREPORTER_H
|
||||
#define OAK_PLUGINPROGRESSDIALOGREPORTER_H
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
#include "pluginSupport/pluginprogressreporter.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class ProgressDialog;
|
||||
|
||||
/**
|
||||
* @brief PluginProgressReporter implementation wrapping a ProgressDialog
|
||||
*
|
||||
* UI-side reporter created through the plugin progress reporter factory.
|
||||
* Forwards progress calls to the dialog and the dialog's cancelled() signal
|
||||
* back to the engine. The dialog is deleted on close; the reporter itself
|
||||
* is destroyed by the engine with deleteLater().
|
||||
*/
|
||||
class PluginProgressDialogReporter : public plugin::PluginProgressReporter {
|
||||
Q_OBJECT
|
||||
public:
|
||||
PluginProgressDialogReporter(const QString &message, const QString &title);
|
||||
|
||||
virtual ~PluginProgressDialogReporter() override;
|
||||
|
||||
virtual void set_progress(double value) override;
|
||||
|
||||
virtual void show() override;
|
||||
|
||||
virtual void close() override;
|
||||
|
||||
private:
|
||||
QPointer<ProgressDialog> dialog_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_PLUGINPROGRESSDIALOGREPORTER_H
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "factory.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHash>
|
||||
|
||||
#include "audio/pan/pan.h"
|
||||
@@ -105,113 +104,6 @@ void NodeFactory::destroy()
|
||||
library.clear();
|
||||
}
|
||||
|
||||
Menu *NodeFactory::create_menu(QWidget *parent, bool create_none_item,
|
||||
Node::CategoryID restrict_to,
|
||||
uint64_t restrict_flags)
|
||||
{
|
||||
Menu *menu = new Menu(parent);
|
||||
menu->setToolTipsVisible(true);
|
||||
|
||||
for (int i = 0; i < library.size(); i++) {
|
||||
Node *n = library.at(i);
|
||||
|
||||
if (restrict_to != Node::k_category_unknown &&
|
||||
!n->category().contains(restrict_to)) {
|
||||
// Skip this node
|
||||
continue;
|
||||
}
|
||||
|
||||
if (restrict_flags && !(n->get_flags() & restrict_flags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n->get_flags() & Node::k_dont_show_in_create_menu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure nodes are up-to-date with the current translation
|
||||
n->retranslate();
|
||||
|
||||
QString category_name = Node::get_category_name(
|
||||
n->category().isEmpty() ? Node::k_category_unknown :
|
||||
n->category().first());
|
||||
|
||||
// Find or create top-level category menu
|
||||
Menu *top_menu = nullptr;
|
||||
QList<QAction *> menu_actions = menu->actions();
|
||||
foreach (QAction *action, menu_actions) {
|
||||
if (action->menu() && action->menu()->title() == category_name) {
|
||||
top_menu = static_cast<Menu *>(action->menu());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!top_menu) {
|
||||
top_menu = new Menu(category_name, menu);
|
||||
menu->insert_alphabetically(top_menu);
|
||||
}
|
||||
|
||||
// Determine final destination (support secondary grouping)
|
||||
Menu *destination = top_menu;
|
||||
QString sub = n->sub_category();
|
||||
if (!sub.isEmpty() && n->category().contains(Node::k_category_open_fx)) {
|
||||
QList<QAction *> sub_actions = top_menu->actions();
|
||||
foreach (QAction *action, sub_actions) {
|
||||
if (action->menu() && action->menu()->title() == sub) {
|
||||
destination = static_cast<Menu *>(action->menu());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (destination == top_menu) {
|
||||
destination = new Menu(sub, top_menu);
|
||||
top_menu->insert_alphabetically(destination);
|
||||
}
|
||||
}
|
||||
|
||||
// Add entry to menu
|
||||
QAction *a = destination->insert_alphabetically(n->name());
|
||||
a->setData(i);
|
||||
a->setToolTip(n->description());
|
||||
}
|
||||
|
||||
if (create_none_item) {
|
||||
QAction *none_item = new QAction(
|
||||
QCoreApplication::translate("NodeFactory", "None"), menu);
|
||||
|
||||
none_item->setData(-1);
|
||||
|
||||
if (menu->actions().isEmpty()) {
|
||||
menu->addAction(none_item);
|
||||
} else {
|
||||
QAction *separator = menu->insertSeparator(menu->actions().first());
|
||||
menu->insertAction(separator, none_item);
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
Node *NodeFactory::CreateFromMenuAction(QAction *action)
|
||||
{
|
||||
int index = action->data().toInt();
|
||||
|
||||
if (index == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return library.at(index)->copy();
|
||||
}
|
||||
|
||||
QString NodeFactory::GetIDFromMenuAction(QAction *action)
|
||||
{
|
||||
int index = action->data().toInt();
|
||||
|
||||
if (index == -1) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return library.at(action->data().toInt())->id();
|
||||
}
|
||||
|
||||
QString NodeFactory::get_name_from_id(const QString &id)
|
||||
{
|
||||
if (!id.isEmpty()) {
|
||||
|
||||
+12
-10
@@ -25,7 +25,6 @@
|
||||
#include <QList>
|
||||
|
||||
#include "node.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -97,15 +96,6 @@ public:
|
||||
|
||||
static void destroy();
|
||||
|
||||
static Menu *
|
||||
create_menu(QWidget *parent, bool create_none_item = false,
|
||||
Node::CategoryID restrict_to = Node::k_category_unknown,
|
||||
uint64_t restrict_flags = 0);
|
||||
|
||||
static Node *CreateFromMenuAction(QAction *action);
|
||||
|
||||
static QString GetIDFromMenuAction(QAction *action);
|
||||
|
||||
static QString get_name_from_id(const QString &id);
|
||||
|
||||
static Node *create_from_id(const QString &id);
|
||||
@@ -113,6 +103,18 @@ public:
|
||||
|
||||
static Node *create_from_factory_index(const InternalID &id);
|
||||
|
||||
/**
|
||||
* @brief Access the internal node library
|
||||
*
|
||||
* Exposed for the UI layer (e.g. widget/menu/factorymenu), which builds
|
||||
* node creation menus from the library. The library itself stays
|
||||
* UI-independent.
|
||||
*/
|
||||
static const QList<Node *> &get_library()
|
||||
{
|
||||
return library;
|
||||
}
|
||||
|
||||
private:
|
||||
static QList<Node *> library;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@ target_sources(libolive-editor PRIVATE
|
||||
olivehost.cpp
|
||||
oliveplugininstance.h
|
||||
oliveplugininstance.cpp
|
||||
pluginprogressreporter.h
|
||||
pluginprogressreporter.cpp
|
||||
oliveclip.cpp
|
||||
oliveclip.h
|
||||
paraminstance.cpp
|
||||
|
||||
@@ -23,11 +23,8 @@
|
||||
#include "ofxMessage.h"
|
||||
#include "common/current.h"
|
||||
#include "coreengine.h"
|
||||
#include "dialog/progress/progress.h"
|
||||
#include "pluginprogressreporter.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "panel/timebased/timebased.h"
|
||||
#include "panel/timeline/timeline.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <QApplication>
|
||||
@@ -125,31 +122,19 @@ private:
|
||||
bool skip_first_redo_ = true;
|
||||
};
|
||||
|
||||
ActiveViewerProvider active_viewer_provider_;
|
||||
|
||||
ViewerOutput *get_active_viewer_output()
|
||||
{
|
||||
PanelManager *manager = PanelManager::instance();
|
||||
if (!manager) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto *time_panel = manager->most_recently_focused<TimeBasedPanel>()) {
|
||||
if (time_panel->get_connected_viewer()) {
|
||||
return time_panel->get_connected_viewer();
|
||||
}
|
||||
}
|
||||
|
||||
QList<TimelinePanel *> timelines =
|
||||
manager->get_panels_of_type<TimelinePanel>();
|
||||
for (TimelinePanel *panel : timelines) {
|
||||
if (panel && panel->get_connected_viewer()) {
|
||||
return panel->get_connected_viewer();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return active_viewer_provider_ ? active_viewer_provider_() : nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void set_active_viewer_provider(ActiveViewerProvider provider)
|
||||
{
|
||||
active_viewer_provider_ = std::move(provider);
|
||||
}
|
||||
|
||||
const std::string &OlivePluginInstance::getDefaultOutputFielding() const
|
||||
{
|
||||
return field_order_for_params(params_);
|
||||
@@ -452,21 +437,20 @@ void OlivePluginInstance::progressStart(const std::string &message,
|
||||
return;
|
||||
}
|
||||
|
||||
if (progress_dialog_) {
|
||||
progress_dialog_->close();
|
||||
progress_dialog_->deleteLater();
|
||||
if (progress_reporter_) {
|
||||
progress_reporter_->close();
|
||||
progress_reporter_->deleteLater();
|
||||
}
|
||||
|
||||
QString dialog_message = message.empty() ? QStringLiteral("Processing...") :
|
||||
QString::fromStdString(message);
|
||||
|
||||
progress_dialog_ = new ::olive::ProgressDialog(
|
||||
dialog_message, QStringLiteral("OpenFX"), nullptr);
|
||||
progress_dialog_->setAttribute(Qt::WA_DeleteOnClose);
|
||||
QObject::connect(progress_dialog_, &::olive::ProgressDialog::cancelled,
|
||||
progress_dialog_,
|
||||
progress_reporter_ = create_plugin_progress_reporter(
|
||||
dialog_message, QStringLiteral("OpenFX"));
|
||||
QObject::connect(progress_reporter_, &PluginProgressReporter::cancelled,
|
||||
progress_reporter_,
|
||||
[this]() { progress_cancelled_ = true; });
|
||||
progress_dialog_->show();
|
||||
progress_reporter_->show();
|
||||
}
|
||||
|
||||
void OlivePluginInstance::progressEnd()
|
||||
@@ -474,9 +458,9 @@ void OlivePluginInstance::progressEnd()
|
||||
progress_active_ = false;
|
||||
progress_cancelled_ = false;
|
||||
|
||||
if (progress_dialog_) {
|
||||
progress_dialog_->close();
|
||||
progress_dialog_->deleteLater();
|
||||
if (progress_reporter_) {
|
||||
progress_reporter_->close();
|
||||
progress_reporter_->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,9 +470,9 @@ bool OlivePluginInstance::progressUpdate(double t)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (progress_dialog_) {
|
||||
if (progress_reporter_) {
|
||||
double clamped = qBound(0.0, t, 1.0);
|
||||
progress_dialog_->set_progress(clamped);
|
||||
progress_reporter_->set_progress(clamped);
|
||||
}
|
||||
|
||||
return !progress_cancelled_;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
#include <QCoreApplication>
|
||||
#include <QPointer>
|
||||
#include <QThread>
|
||||
@@ -43,15 +44,28 @@ inline bool is_gui_thread()
|
||||
}
|
||||
return true;
|
||||
}
|
||||
class ProgressDialog;
|
||||
class ViewerOutput;
|
||||
namespace plugin
|
||||
{
|
||||
class PluginNode;
|
||||
class PluginProgressReporter;
|
||||
enum class ErrorType { error, warning, message };
|
||||
struct PersistentErrors {
|
||||
ErrorType type;
|
||||
QString message;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provider returning the currently active viewer
|
||||
*
|
||||
* Registered by the UI layer, which resolves the viewer through the panel
|
||||
* manager. Without a provider, the OFX timeline suite falls back to its
|
||||
* safe defaults (current time 0, empty bounds, seeking does nothing).
|
||||
*/
|
||||
using ActiveViewerProvider = std::function<ViewerOutput *()>;
|
||||
|
||||
void set_active_viewer_provider(ActiveViewerProvider provider);
|
||||
|
||||
class OlivePluginInstance : public OFX::Host::ImageEffect::Instance {
|
||||
public:
|
||||
OlivePluginInstance(OFX::Host::ImageEffect::ImageEffectPlugin *plugin,
|
||||
@@ -214,7 +228,7 @@ private:
|
||||
QString edit_label_;
|
||||
QString edit_first_label_;
|
||||
int edit_param_count_ = 0;
|
||||
QPointer<olive::ProgressDialog> progress_dialog_;
|
||||
QPointer<PluginProgressReporter> progress_reporter_;
|
||||
bool progress_cancelled_ = false;
|
||||
bool progress_active_ = false;
|
||||
bool open_gl_enabled_ = false;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "pluginprogressreporter.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief No-op reporter used when no UI factory is registered
|
||||
*
|
||||
* Never emits cancelled(), so processing always continues.
|
||||
*/
|
||||
class NullPluginProgressReporter : public PluginProgressReporter {
|
||||
public:
|
||||
void set_progress(double value) override
|
||||
{
|
||||
(void)value;
|
||||
}
|
||||
|
||||
void show() override
|
||||
{
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
PluginProgressReporterFactory reporter_factory_;
|
||||
|
||||
}
|
||||
|
||||
void set_plugin_progress_reporter_factory(
|
||||
PluginProgressReporterFactory factory)
|
||||
{
|
||||
reporter_factory_ = std::move(factory);
|
||||
}
|
||||
|
||||
PluginProgressReporter *
|
||||
create_plugin_progress_reporter(const QString &message, const QString &title)
|
||||
{
|
||||
if (reporter_factory_) {
|
||||
return reporter_factory_(message, title);
|
||||
}
|
||||
|
||||
return new NullPluginProgressReporter();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Oak Video Editor - Non-Linear Video Editor
|
||||
* Copyright (C) 2025 Olive CE Team
|
||||
*
|
||||
* 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, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef OAK_PLUGIN_PROGRESS_REPORTER_H
|
||||
#define OAK_PLUGIN_PROGRESS_REPORTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
namespace plugin
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief UI-independent interface for reporting plugin progress
|
||||
*
|
||||
* The engine cannot show UI itself, so OFX progress reporting goes through
|
||||
* this interface. The UI layer registers a factory (see
|
||||
* set_plugin_progress_reporter_factory()) that creates a reporter wrapping a
|
||||
* ProgressDialog; without a factory, a no-op reporter is used instead.
|
||||
*/
|
||||
class PluginProgressReporter : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PluginProgressReporter(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~PluginProgressReporter() override = default;
|
||||
|
||||
virtual void set_progress(double value) = 0;
|
||||
|
||||
virtual void show() = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
signals:
|
||||
void cancelled();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Factory creating a PluginProgressReporter for a progress session
|
||||
*
|
||||
* Registered by the UI layer at startup. The caller takes ownership of the
|
||||
* returned reporter.
|
||||
*/
|
||||
using PluginProgressReporterFactory =
|
||||
std::function<PluginProgressReporter *(const QString &message,
|
||||
const QString &title)>;
|
||||
|
||||
void set_plugin_progress_reporter_factory(
|
||||
PluginProgressReporterFactory factory);
|
||||
|
||||
/**
|
||||
* @brief Create a progress reporter through the registered factory
|
||||
*
|
||||
* Without a factory, returns a no-op reporter so engine code can run
|
||||
* headless. The caller takes ownership of the returned reporter.
|
||||
*/
|
||||
PluginProgressReporter *
|
||||
create_plugin_progress_reporter(const QString &message, const QString &title);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OAK_PLUGIN_PROGRESS_REPORTER_H
|
||||
@@ -31,13 +31,15 @@
|
||||
#include "common/filefunctions.h"
|
||||
#include "config/config.h"
|
||||
#include "coreengine.h"
|
||||
#include "dialog/diskcache/diskcachedialog.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
DiskManager *DiskManager::instance_ = nullptr;
|
||||
|
||||
DiskManager::ShowDiskCacheSettingsHandler
|
||||
DiskManager::show_disk_cache_settings_handler_;
|
||||
|
||||
DiskManager::DiskManager()
|
||||
{
|
||||
// Add default cache location
|
||||
@@ -181,11 +183,21 @@ QString DiskManager::get_default_disk_cache_path()
|
||||
.filePath("mediacache");
|
||||
}
|
||||
|
||||
void DiskManager::set_show_disk_cache_settings_handler(
|
||||
ShowDiskCacheSettingsHandler handler)
|
||||
{
|
||||
show_disk_cache_settings_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
void DiskManager::show_disk_cache_settings_dialog(DiskCacheFolder *folder,
|
||||
QWidget *parent)
|
||||
{
|
||||
DiskCacheDialog d(folder, parent);
|
||||
d.exec();
|
||||
if (show_disk_cache_settings_handler_) {
|
||||
show_disk_cache_settings_handler_(folder, parent);
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << "No disk cache settings dialog handler registered, skipping";
|
||||
}
|
||||
|
||||
void DiskManager::show_disk_cache_settings_dialog(const QString &path,
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "common/define.h"
|
||||
#include "node/project.h"
|
||||
|
||||
@@ -143,6 +145,19 @@ public:
|
||||
|
||||
static QString get_default_disk_cache_path();
|
||||
|
||||
/**
|
||||
* @brief Handler showing the disk cache settings dialog for a folder
|
||||
*
|
||||
* Registered by the UI layer (e.g. a DiskCacheDialog-based
|
||||
* implementation), since the engine cannot show dialogs itself. Without
|
||||
* a handler, the request is logged and skipped.
|
||||
*/
|
||||
using ShowDiskCacheSettingsHandler =
|
||||
std::function<void(DiskCacheFolder *folder, QWidget *parent)>;
|
||||
|
||||
static void set_show_disk_cache_settings_handler(
|
||||
ShowDiskCacheSettingsHandler handler);
|
||||
|
||||
void show_disk_cache_settings_dialog(DiskCacheFolder *folder, QWidget *parent);
|
||||
void show_disk_cache_settings_dialog(const QString &path, QWidget *parent);
|
||||
|
||||
@@ -165,6 +180,8 @@ private:
|
||||
|
||||
static DiskManager *instance_;
|
||||
|
||||
static ShowDiskCacheSettingsHandler show_disk_cache_settings_handler_;
|
||||
|
||||
QVector<DiskCacheFolder *> open_folders_;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/menu/factorymenu.cpp
|
||||
widget/menu/factorymenu.h
|
||||
widget/menu/menu.cpp
|
||||
widget/menu/menu.h
|
||||
widget/menu/menushared.cpp
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "factorymenu.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
Menu *create_node_menu(QWidget *parent, bool create_none_item,
|
||||
Node::CategoryID restrict_to, uint64_t restrict_flags)
|
||||
{
|
||||
const QList<Node *> &library = NodeFactory::get_library();
|
||||
|
||||
Menu *menu = new Menu(parent);
|
||||
menu->setToolTipsVisible(true);
|
||||
|
||||
for (int i = 0; i < library.size(); i++) {
|
||||
Node *n = library.at(i);
|
||||
|
||||
if (restrict_to != Node::k_category_unknown &&
|
||||
!n->category().contains(restrict_to)) {
|
||||
// Skip this node
|
||||
continue;
|
||||
}
|
||||
|
||||
if (restrict_flags && !(n->get_flags() & restrict_flags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n->get_flags() & Node::k_dont_show_in_create_menu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure nodes are up-to-date with the current translation
|
||||
n->retranslate();
|
||||
|
||||
QString category_name = Node::get_category_name(
|
||||
n->category().isEmpty() ? Node::k_category_unknown :
|
||||
n->category().first());
|
||||
|
||||
// Find or create top-level category menu
|
||||
Menu *top_menu = nullptr;
|
||||
QList<QAction *> menu_actions = menu->actions();
|
||||
foreach (QAction *action, menu_actions) {
|
||||
if (action->menu() && action->menu()->title() == category_name) {
|
||||
top_menu = static_cast<Menu *>(action->menu());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!top_menu) {
|
||||
top_menu = new Menu(category_name, menu);
|
||||
menu->insert_alphabetically(top_menu);
|
||||
}
|
||||
|
||||
// Determine final destination (support secondary grouping)
|
||||
Menu *destination = top_menu;
|
||||
QString sub = n->sub_category();
|
||||
if (!sub.isEmpty() && n->category().contains(Node::k_category_open_fx)) {
|
||||
QList<QAction *> sub_actions = top_menu->actions();
|
||||
foreach (QAction *action, sub_actions) {
|
||||
if (action->menu() && action->menu()->title() == sub) {
|
||||
destination = static_cast<Menu *>(action->menu());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (destination == top_menu) {
|
||||
destination = new Menu(sub, top_menu);
|
||||
top_menu->insert_alphabetically(destination);
|
||||
}
|
||||
}
|
||||
|
||||
// Add entry to menu
|
||||
QAction *a = destination->insert_alphabetically(n->name());
|
||||
a->setData(i);
|
||||
a->setToolTip(n->description());
|
||||
}
|
||||
|
||||
if (create_none_item) {
|
||||
QAction *none_item = new QAction(
|
||||
QCoreApplication::translate("NodeFactory", "None"), menu);
|
||||
|
||||
none_item->setData(-1);
|
||||
|
||||
if (menu->actions().isEmpty()) {
|
||||
menu->addAction(none_item);
|
||||
} else {
|
||||
QAction *separator = menu->insertSeparator(menu->actions().first());
|
||||
menu->insertAction(separator, none_item);
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
Node *create_node_from_menu_action(QAction *action)
|
||||
{
|
||||
int index = action->data().toInt();
|
||||
|
||||
if (index == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NodeFactory::get_library().at(index)->copy();
|
||||
}
|
||||
|
||||
QString get_node_id_from_menu_action(QAction *action)
|
||||
{
|
||||
int index = action->data().toInt();
|
||||
|
||||
if (index == -1) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return NodeFactory::get_library().at(action->data().toInt())->id();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_FACTORYMENU_H
|
||||
#define OAK_FACTORYMENU_H
|
||||
|
||||
#include <QAction>
|
||||
|
||||
#include "node/factory.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Create a menu of all available nodes, grouped by category
|
||||
*
|
||||
* UI-side counterpart of the node library: NodeFactory stays UI-independent
|
||||
* while this builds an olive::Menu from NodeFactory::get_library(). Each
|
||||
* leaf action carries the node's library index in its data, so it can be
|
||||
* resolved back with create_node_from_menu_action() or
|
||||
* get_node_id_from_menu_action().
|
||||
*/
|
||||
Menu *create_node_menu(QWidget *parent, bool create_none_item = false,
|
||||
Node::CategoryID restrict_to = Node::k_category_unknown,
|
||||
uint64_t restrict_flags = 0);
|
||||
|
||||
/**
|
||||
* @brief Create a node from an action of a menu built by create_node_menu()
|
||||
*
|
||||
* Returns nullptr for the "None" item.
|
||||
*/
|
||||
Node *create_node_from_menu_action(QAction *action);
|
||||
|
||||
/**
|
||||
* @brief Get the node ID from an action of a menu built by create_node_menu()
|
||||
*
|
||||
* Returns an empty string for the "None" item.
|
||||
*/
|
||||
QString get_node_id_from_menu_action(QAction *action);
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_FACTORYMENU_H
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "node/factory.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/menu/factorymenu.h"
|
||||
#include "widget/menu/menu.h"
|
||||
|
||||
namespace olive
|
||||
@@ -39,12 +40,12 @@ NodeComboBox::NodeComboBox(QWidget *parent)
|
||||
|
||||
void NodeComboBox::showPopup()
|
||||
{
|
||||
Menu *m = NodeFactory::create_menu(this, true);
|
||||
Menu *m = create_node_menu(this, true);
|
||||
|
||||
QAction *selected = m->exec(parentWidget()->mapToGlobal(pos()));
|
||||
|
||||
if (selected) {
|
||||
QString new_id = NodeFactory::GetIDFromMenuAction(selected);
|
||||
QString new_id = get_node_id_from_menu_action(selected);
|
||||
|
||||
set_node_internal(new_id, true);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "widget/menu/factorymenu.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -147,7 +147,7 @@ void NodeParamViewContext::add_effect_button_clicked()
|
||||
}
|
||||
|
||||
Menu *m =
|
||||
NodeFactory::create_menu(this, false, Node::k_category_unknown, flag);
|
||||
create_node_menu(this, false, Node::k_category_unknown, flag);
|
||||
connect(m, &Menu::triggered, this,
|
||||
&NodeParamViewContext::add_effect_menu_item_triggered);
|
||||
m->exec(QCursor::pos());
|
||||
@@ -156,7 +156,7 @@ void NodeParamViewContext::add_effect_button_clicked()
|
||||
|
||||
void NodeParamViewContext::add_effect_menu_item_triggered(QAction *a)
|
||||
{
|
||||
Node *n = NodeFactory::CreateFromMenuAction(a);
|
||||
Node *n = create_node_from_menu_action(a);
|
||||
|
||||
if (n) {
|
||||
NodeInput new_node_input = n->get_effect_input();
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
|
||||
#include "node/audio/volume/volume.h"
|
||||
#include "node/distort/transform/transformdistortnode.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/group/group.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "node/traverser.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/menu/factorymenu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/timebased/timebasedview.h"
|
||||
|
||||
@@ -936,7 +936,7 @@ void NodeView::show_context_menu(const QPoint &pos)
|
||||
|
||||
void NodeView::create_node_slot(QAction *action)
|
||||
{
|
||||
Node *new_node = NodeFactory::CreateFromMenuAction(action);
|
||||
Node *new_node = create_node_from_menu_action(action);
|
||||
|
||||
if (new_node) {
|
||||
NodeViewItem *new_item = new NodeViewItem(new_node, nullptr);
|
||||
@@ -1381,7 +1381,7 @@ bool NodeView::get_assumed_position_for_selected_node(Node *node,
|
||||
|
||||
Menu *NodeView::create_add_menu(Menu *parent)
|
||||
{
|
||||
Menu *add_menu = NodeFactory::create_menu(parent);
|
||||
Menu *add_menu = create_node_menu(parent);
|
||||
add_menu->setTitle(tr("Add"));
|
||||
connect(add_menu, &Menu::triggered, this, &NodeView::create_node_slot);
|
||||
return add_menu;
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#include <QResizeEvent>
|
||||
#include <QVariant>
|
||||
|
||||
#include "node/factory.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/menu/factorymenu.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
|
||||
@@ -206,7 +206,7 @@ void Toolbar::add_button_clicked()
|
||||
|
||||
void Toolbar::transition_button_clicked()
|
||||
{
|
||||
Menu *m = NodeFactory::create_menu(this, false, Node::k_category_transition);
|
||||
Menu *m = create_node_menu(this, false, Node::k_category_transition);
|
||||
|
||||
connect(m, &QMenu::triggered, this, &Toolbar::transition_menu_item_triggered);
|
||||
|
||||
@@ -217,7 +217,7 @@ void Toolbar::transition_button_clicked()
|
||||
|
||||
void Toolbar::transition_menu_item_triggered(QAction *a)
|
||||
{
|
||||
emit selected_transition_changed(NodeFactory::GetIDFromMenuAction(a));
|
||||
emit selected_transition_changed(get_node_id_from_menu_action(a));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user