diff --git a/engine/config/config.h b/engine/config/config.h index 34817f9cd..e5bcddb2d 100644 --- a/engine/config/config.h +++ b/engine/config/config.h @@ -31,8 +31,12 @@ namespace olive { +#ifndef OAK_CONFIG #define OAK_CONFIG(x) Config::current()[QStringLiteral(x)] +#endif +#ifndef OAK_CONFIG_STR #define OAK_CONFIG_STR(x) Config::current()[x] +#endif class Config { public: diff --git a/engine/coreengine.cpp b/engine/coreengine.cpp index ec8b0719a..716ed1165 100644 --- a/engine/coreengine.cpp +++ b/engine/coreengine.cpp @@ -178,7 +178,7 @@ void EngineCore::declare_types_for_qt() qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); - qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); } diff --git a/engine/coreengine.h b/engine/coreengine.h index 89fcc7c50..3f89eba11 100644 --- a/engine/coreengine.h +++ b/engine/coreengine.h @@ -33,7 +33,7 @@ #include "node/project/footage/footage.h" #include "node/project.h" #include "node/project/sequence/sequence.h" -#include "node/project/serializer/mainwindowlayoutinfo.h" +#include "node/project/serializer/serializedlayoutinfo.h" #include "task/task.h" #include "tool/tool.h" #include "undo/undostack.h" @@ -304,7 +304,7 @@ public: * @brief Handler applying a loaded main window layout after a project load */ using LoadLayoutHandler = - std::function; + std::function; void set_load_layout_handler(LoadLayoutHandler handler); /** @@ -320,6 +320,17 @@ public: */ void remove_recently_opened_project(int index); + /** + * @brief Currently open project (may be nullptr) + * + * Read accessor for the C ABI facade (oakengine_app_open_project()); + * the UI layer used to read the protected member directly. + */ + Project *open_project() const + { + return open_project_; + } + #ifdef USE_OTIO /** * @brief Handler showing the OTIO import options dialog @@ -410,6 +421,26 @@ public slots: bool show_otio_import_dialog(const QList &sequences); #endif + void add_recovery_project_from_task(Task *task); + +public: + /** + * @brief Adds a project to the "open projects" list + * + * (Public for the C ABI facade; was protected while olive::Core derived + * from this class.) + */ + void add_open_project(olive::Project *p, bool add_to_recents = false); + + bool add_open_project_from_task(Task *task, bool add_to_recents); + + void set_active_project(Project *p); + + /** + * @brief Returns the filename of the autorecovery index + */ + static QString get_auto_recovery_index_filename(); + signals: /** * @brief Signal emitted when the tool is changed from somewhere @@ -471,15 +502,6 @@ signals: void active_project_changed(Project *p); protected: - /** - * @brief Adds a project to the "open projects" list - */ - void add_open_project(olive::Project *p, bool add_to_recents = false); - - bool add_open_project_from_task(Task *task, bool add_to_recents); - - void set_active_project(Project *p); - /** * @brief Currently open project * @@ -487,11 +509,6 @@ protected: */ Project *open_project_; - static QString get_auto_recovery_index_filename(); - -protected slots: - void add_recovery_project_from_task(Task *task); - private: /** * @brief Returns the filename where the recently opened/saved projects should be stored diff --git a/engine/node/generator/text/textv3.h b/engine/node/generator/text/textv3.h index b5b8051c6..6e279f66b 100644 --- a/engine/node/generator/text/textv3.h +++ b/engine/node/generator/text/textv3.h @@ -69,6 +69,9 @@ public: static QString format_string(const QString &input, const QStringList &args); + /** @brief Access the text gizmo. */ + TextGizmo *text_gizmo() { return text_gizmo_; } + protected: virtual void InputValueChangedEvent(const QString &input, int element) override; diff --git a/engine/node/node.cpp b/engine/node/node.cpp index 91a89e472..ff6227621 100644 --- a/engine/node/node.cpp +++ b/engine/node/node.cpp @@ -34,7 +34,6 @@ #include "project.h" #include "serializeddata.h" #include "ui/colorcoding.h" -#include "ui/icons/icons.h" namespace olive { @@ -109,7 +108,7 @@ QVariant Node::data(const DataType &d) const { if (d == icon) { // Just a meaningless default icon to be used where necessary - return icon::New; + return QStringLiteral("new"); } return QVariant(); @@ -2481,7 +2480,7 @@ void Node::childEvent(QChildEvent *event) connect(key, &NodeKeyframe::bezier_control_out_changed, this, &Node::invalidate_from_keyframe_bezier_out_change); - emit keyframe_added(key); + emit keyframe_added(reinterpret_cast(key)); parameter_value_changed(i, get_range_affected_by_keyframe(key)); } else if (event->type() == QEvent::ChildRemoved) { TimeRange time_affected = get_range_affected_by_keyframe(key); @@ -2497,7 +2496,7 @@ void Node::childEvent(QChildEvent *event) disconnect(key, &NodeKeyframe::bezier_control_out_changed, this, &Node::invalidate_from_keyframe_bezier_out_change); - emit keyframe_removed(key); + emit keyframe_removed(reinterpret_cast(key)); get_immediate(key->input(), key->element())->remove_keyframe(key); parameter_value_changed(i, time_affected); @@ -2570,7 +2569,7 @@ void Node::invalidate_from_keyframe_time_change() parameter_value_changed(key->key_track_ref().input(), r); } - emit keyframe_time_changed(key); + emit keyframe_time_changed(reinterpret_cast(key)); } void Node::invalidate_from_keyframe_value_change() @@ -2579,7 +2578,7 @@ void Node::invalidate_from_keyframe_value_change() parameter_value_changed(key->key_track_ref().input(), get_range_affected_by_keyframe(key)); - emit keyframe_value_changed(key); + emit keyframe_value_changed(reinterpret_cast(key)); } void Node::invalidate_from_keyframe_type_changed() @@ -2597,7 +2596,7 @@ void Node::invalidate_from_keyframe_type_changed() get_range_around_index(key->input(), track.indexOf(key), key->track(), key->element())); - emit keyframe_type_changed(key); + emit keyframe_type_changed(reinterpret_cast(key)); } void Node::set_value_at_time(const NodeInput &input, const Rational &time, diff --git a/engine/node/node.h b/engine/node/node.h index 1bb8a682c..812d8e308 100644 --- a/engine/node/node.h +++ b/engine/node/node.h @@ -47,6 +47,10 @@ #include "render/shadercode.h" #include "splitvalue.h" +/* Forward declaration for C ABI keyframe handle used in signals that cross + * the app/engine boundary. */ +struct OakEngineKeyframe; + namespace olive { @@ -1319,17 +1323,17 @@ signals: void input_array_size_changed(const QString &input, int old_size, int new_size); - void keyframe_added(NodeKeyframe *key); + void keyframe_added(OakEngineKeyframe *key); - void keyframe_removed(NodeKeyframe *key); + void keyframe_removed(OakEngineKeyframe *key); - void keyframe_time_changed(NodeKeyframe *key); + void keyframe_time_changed(OakEngineKeyframe *key); void message_count_changed(); - void keyframe_type_changed(NodeKeyframe *key); + void keyframe_type_changed(OakEngineKeyframe *key); - void keyframe_value_changed(NodeKeyframe *key); + void keyframe_value_changed(OakEngineKeyframe *key); void keyframe_enable_changed(const NodeInput &input, bool enabled); diff --git a/engine/node/project/folder/folder.cpp b/engine/node/project/folder/folder.cpp index 4756d3c36..d913c3552 100644 --- a/engine/node/project/folder/folder.cpp +++ b/engine/node/project/folder/folder.cpp @@ -25,7 +25,6 @@ #include "node/nodeundo.h" #include "node/project/footage/footage.h" #include "node/project/sequence/sequence.h" -#include "ui/icons/icons.h" namespace olive { @@ -45,7 +44,7 @@ Folder::Folder() QVariant Folder::data(const DataType &d) const { if (d == icon) { - return icon::folder; + return QStringLiteral("folder"); } return super::data(d); diff --git a/engine/node/project/footage/footage.cpp b/engine/node/project/footage/footage.cpp index d2bc4e125..66f64a226 100644 --- a/engine/node/project/footage/footage.cpp +++ b/engine/node/project/footage/footage.cpp @@ -37,7 +37,6 @@ #include "node/color/colormanager/colormanager.h" #include "node/project.h" #include "render/job/footagejob.h" -#include "ui/icons/icons.h" namespace olive { @@ -613,18 +612,18 @@ QVariant Footage::data(const DataType &d) const if (s.is_valid() && s.video_type() != VideoParams::k_video_type_still) { - return icon::video; + return QStringLiteral("video"); } else if (has_enabled_audio_streams()) { - return icon::audio; + return QStringLiteral("audio"); } else if (s.is_valid() && s.video_type() == VideoParams::k_video_type_still) { - return icon::image; + return QStringLiteral("image"); } else if (has_enabled_subtitle_streams()) { - return icon::subtitles; + return QStringLiteral("subtitles"); } } - return icon::error; + return QStringLiteral("error"); } case tooltip: { if (valid_) { diff --git a/engine/node/project/sequence/sequence.cpp b/engine/node/project/sequence/sequence.cpp index 1decc43f2..8bd5f0cbb 100644 --- a/engine/node/project/sequence/sequence.cpp +++ b/engine/node/project/sequence/sequence.cpp @@ -23,7 +23,6 @@ #include -#include "ui/icons/icons.h" #include "timeline/timelineundogeneral.h" namespace olive @@ -81,7 +80,7 @@ void Sequence::add_default_nodes(MultiUndoCommand *command) QVariant Sequence::data(const DataType &d) const { if (d == icon) { - return icon::sequence; + return QStringLiteral("sequence"); } return super::data(d); diff --git a/engine/node/project/serializer/CMakeLists.txt b/engine/node/project/serializer/CMakeLists.txt index 2841661d7..f6a48659b 100644 --- a/engine/node/project/serializer/CMakeLists.txt +++ b/engine/node/project/serializer/CMakeLists.txt @@ -33,8 +33,8 @@ set(OLIVE_SOURCES node/project/serializer/serializer230220.h - node/project/serializer/mainwindowlayoutinfo.cpp - node/project/serializer/mainwindowlayoutinfo.h + node/project/serializer/serializedlayoutinfo.cpp + node/project/serializer/serializedlayoutinfo.h node/project/serializer/typeserializer.cpp node/project/serializer/typeserializer.h diff --git a/engine/node/project/serializer/mainwindowlayoutinfo.cpp b/engine/node/project/serializer/serializedlayoutinfo.cpp similarity index 77% rename from engine/node/project/serializer/mainwindowlayoutinfo.cpp rename to engine/node/project/serializer/serializedlayoutinfo.cpp index 274f53612..729010455 100644 --- a/engine/node/project/serializer/mainwindowlayoutinfo.cpp +++ b/engine/node/project/serializer/serializedlayoutinfo.cpp @@ -16,19 +16,19 @@ * along with this program. If not, see . */ -#include "mainwindowlayoutinfo.h" +#include "serializedlayoutinfo.h" namespace olive { -void MainWindowLayoutInfo::to_xml(QXmlStreamWriter *writer) const +void SerializedLayoutInfo::to_xml(QXmlStreamWriter *writer) const { writer->writeAttribute(QStringLiteral("version"), QString::number(k_version)); writer->writeStartElement(QStringLiteral("folders")); - foreach (Folder *folder, open_folders_) { + foreach (Folder *folder, open_folders) { writer->writeTextElement( QStringLiteral("folder"), QString::number(reinterpret_cast(folder))); @@ -38,7 +38,7 @@ void MainWindowLayoutInfo::to_xml(QXmlStreamWriter *writer) const writer->writeStartElement(QStringLiteral("timeline")); - foreach (Sequence *sequence, open_sequences_) { + foreach (Sequence *sequence, open_sequences) { writer->writeTextElement( QStringLiteral("sequence"), QString::number(reinterpret_cast(sequence))); @@ -48,7 +48,7 @@ void MainWindowLayoutInfo::to_xml(QXmlStreamWriter *writer) const writer->writeStartElement(QStringLiteral("viewers")); - foreach (ViewerOutput *viewer, open_viewers_) { + foreach (ViewerOutput *viewer, open_viewers) { writer->writeTextElement( QStringLiteral("viewer"), QString::number(reinterpret_cast(viewer))); @@ -58,7 +58,7 @@ void MainWindowLayoutInfo::to_xml(QXmlStreamWriter *writer) const writer->writeStartElement(QStringLiteral("data")); - for (auto it = panel_data_.cbegin(); it != panel_data_.cend(); it++) { + for (auto it = panel_data.cbegin(); it != panel_data.cend(); it++) { writer->writeStartElement(QStringLiteral("panel")); writer->writeAttribute(QStringLiteral("id"), it->first); @@ -80,14 +80,14 @@ void MainWindowLayoutInfo::to_xml(QXmlStreamWriter *writer) const writer->writeEndElement(); // data writer->writeTextElement(QStringLiteral("state"), - QString(state_.toBase64())); + QString(state.toBase64())); } -MainWindowLayoutInfo -MainWindowLayoutInfo::from_xml(QXmlStreamReader *reader, +SerializedLayoutInfo +SerializedLayoutInfo::from_xml(QXmlStreamReader *reader, const QHash &node_ptrs) { - MainWindowLayoutInfo info; + SerializedLayoutInfo info; unsigned int file_version = 0; @@ -110,7 +110,7 @@ MainWindowLayoutInfo::from_xml(QXmlStreamReader *reader, Folder *open_item = static_cast(node_ptrs.value(item_id)); - info.open_folders_.push_back(open_item); + info.open_folders.push_back(open_item); } else { reader->skipCurrentElement(); } @@ -123,7 +123,7 @@ MainWindowLayoutInfo::from_xml(QXmlStreamReader *reader, Sequence *open_seq = static_cast(node_ptrs.value(item_id)); - info.open_sequences_.push_back(open_seq); + info.open_sequences.push_back(open_seq); } else { reader->skipCurrentElement(); } @@ -136,14 +136,14 @@ MainWindowLayoutInfo::from_xml(QXmlStreamReader *reader, ViewerOutput *open_viewer = static_cast(node_ptrs.value(item_id)); - info.open_viewers_.push_back(open_viewer); + info.open_viewers.push_back(open_viewer); } else { reader->skipCurrentElement(); } } } else if (reader->name() == QStringLiteral("state")) { - info.state_ = + info.state = QByteArray::fromBase64(reader->readElementText().toLatin1()); } else if (reader->name() == QStringLiteral("data")) { @@ -179,7 +179,7 @@ MainWindowLayoutInfo::from_xml(QXmlStreamReader *reader, } } - info.panel_data_[id] = i; + info.panel_data[id] = i; } } else { @@ -195,38 +195,4 @@ MainWindowLayoutInfo::from_xml(QXmlStreamReader *reader, return info; } -void MainWindowLayoutInfo::add_folder(olive::Folder *f) -{ - open_folders_.push_back(f); -} - -void MainWindowLayoutInfo::add_sequence(Sequence *seq) -{ - open_sequences_.push_back(seq); -} - -void MainWindowLayoutInfo::add_viewer(ViewerOutput *viewer) -{ - open_viewers_.push_back(viewer); -} - -void MainWindowLayoutInfo::set_panel_data(const QString &id, - const PanelLayoutInfo &data) -{ - panel_data_[id] = data; -} - -void MainWindowLayoutInfo::move_panel_data(const QString &old, - const QString &now) -{ - PanelLayoutInfo tmp = panel_data_.at(old); - panel_data_.erase(old); - panel_data_[now] = tmp; -} - -void MainWindowLayoutInfo::set_state(const QByteArray &layout) -{ - state_ = layout; -} - } diff --git a/engine/node/project/serializer/mainwindowlayoutinfo.h b/engine/node/project/serializer/serializedlayoutinfo.h similarity index 53% rename from engine/node/project/serializer/mainwindowlayoutinfo.h rename to engine/node/project/serializer/serializedlayoutinfo.h index 16cde1dd3..d6a004395 100644 --- a/engine/node/project/serializer/mainwindowlayoutinfo.h +++ b/engine/node/project/serializer/serializedlayoutinfo.h @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#ifndef OAK_MAINWINDOWLAYOUTINFO_H -#define OAK_MAINWINDOWLAYOUTINFO_H +#ifndef OAK_SERIALIZEDLAYOUTINFO_H +#define OAK_SERIALIZEDLAYOUTINFO_H #include @@ -35,68 +35,38 @@ namespace olive */ using PanelLayoutInfo = std::map; -class MainWindowLayoutInfo { +/** + * @brief Plain data container for a serialized main window layout + * + * Pure data structure with no behavior beyond XML (de)serialization, so + * consumers (app, tests) can use it without pulling in any engine-side + * C++ symbols. + */ +class SerializedLayoutInfo { public: - MainWindowLayoutInfo() = default; + SerializedLayoutInfo() = default; void to_xml(QXmlStreamWriter *writer) const; - static MainWindowLayoutInfo + static SerializedLayoutInfo from_xml(QXmlStreamReader *reader, const QHash &node_map); - void add_folder(Folder *f); + QByteArray state; - void add_sequence(Sequence *seq); + std::vector open_folders; - void add_viewer(ViewerOutput *viewer); + std::vector open_sequences; - void set_panel_data(const QString &id, const PanelLayoutInfo &data); + std::vector open_viewers; - void move_panel_data(const QString &old, const QString &now); - - void set_state(const QByteArray &layout); - - const std::vector &open_folders() const - { - return open_folders_; - } - - const std::vector &open_sequences() const - { - return open_sequences_; - } - - const std::vector &open_viewers() const - { - return open_viewers_; - } - - const std::map &panel_data() const - { - return panel_data_; - } - - const QByteArray &state() const - { - return state_; - } + std::map panel_data; private: - QByteArray state_; - - std::vector open_folders_; - - std::vector open_sequences_; - - std::vector open_viewers_; - - std::map panel_data_; - static const unsigned int k_version = 1; }; } -Q_DECLARE_METATYPE(olive::MainWindowLayoutInfo) +Q_DECLARE_METATYPE(olive::SerializedLayoutInfo) -#endif // OAK_MAINWINDOWLAYOUTINFO_H +#endif // OAK_SERIALIZEDLAYOUTINFO_H diff --git a/engine/node/project/serializer/serializer.h b/engine/node/project/serializer/serializer.h index d731fc208..3e9fb1053 100644 --- a/engine/node/project/serializer/serializer.h +++ b/engine/node/project/serializer/serializer.h @@ -26,7 +26,7 @@ #include "common/define.h" #include "node/project.h" -#include "node/project/serializer/mainwindowlayoutinfo.h" +#include "node/project/serializer/serializedlayoutinfo.h" #include "typeserializer.h" namespace olive @@ -80,7 +80,7 @@ public: SerializedKeyframes keyframes; - MainWindowLayoutInfo layout; + SerializedLayoutInfo layout; QVector nodes; @@ -173,11 +173,11 @@ public: return type_; } - const MainWindowLayoutInfo &get_layout() const + const SerializedLayoutInfo &get_layout() const { return layout_; } - void set_layout(const MainWindowLayoutInfo &layout) + void set_layout(const SerializedLayoutInfo &layout) { layout_ = layout; } @@ -226,7 +226,7 @@ public: QString filename_; - MainWindowLayoutInfo layout_; + SerializedLayoutInfo layout_; QVector only_serialize_nodes_; diff --git a/engine/node/project/serializer/serializer220403.cpp b/engine/node/project/serializer/serializer220403.cpp index 2c1860538..bd1ec6410 100644 --- a/engine/node/project/serializer/serializer220403.cpp +++ b/engine/node/project/serializer/serializer220403.cpp @@ -55,7 +55,7 @@ ProjectSerializer220403::load(Project *project, QXmlStreamReader *reader, // can continue loading and queue it with the main window so it can handle the data // appropriately in its own thread. - load_data.layout = MainWindowLayoutInfo::from_xml( + load_data.layout = SerializedLayoutInfo::from_xml( reader, xml_node_data.node_ptrs); } else if (reader->name() == QStringLiteral("uuid")) { diff --git a/engine/node/project/serializer/serializer230220.cpp b/engine/node/project/serializer/serializer230220.cpp index 2037d87ff..00a12a1c3 100644 --- a/engine/node/project/serializer/serializer230220.cpp +++ b/engine/node/project/serializer/serializer230220.cpp @@ -49,7 +49,7 @@ ProjectSerializer230220::load(Project *project, QXmlStreamReader *reader, project_data = project->load(reader); load_data.node_ptrs = project_data.node_ptrs; } else if (reader->name() == QStringLiteral("layout")) { - load_data.layout = MainWindowLayoutInfo::from_xml( + load_data.layout = SerializedLayoutInfo::from_xml( reader, project_data.node_ptrs); } else { reader->skipCurrentElement(); diff --git a/engine/oakengine.ver b/engine/oakengine.ver new file mode 100644 index 000000000..087a149c7 --- /dev/null +++ b/engine/oakengine.ver @@ -0,0 +1,6 @@ +{ + global: + oakengine_*; + local: + *; +}; diff --git a/engine/pluginSupport/oliveplugininstance.cpp b/engine/pluginSupport/oliveplugininstance.cpp index 4443d9d16..4e5482da6 100644 --- a/engine/pluginSupport/oliveplugininstance.cpp +++ b/engine/pluginSupport/oliveplugininstance.cpp @@ -439,17 +439,16 @@ void OlivePluginInstance::progressStart(const std::string &message, if (progress_reporter_) { progress_reporter_->close(); - progress_reporter_->deleteLater(); + progress_reporter_.reset(); } QString dialog_message = message.empty() ? QStringLiteral("Processing...") : QString::fromStdString(message); - progress_reporter_ = create_plugin_progress_reporter( - dialog_message, QStringLiteral("OpenFX")); - QObject::connect(progress_reporter_, &PluginProgressReporter::cancelled, - progress_reporter_, - [this]() { progress_cancelled_ = true; }); + progress_reporter_.reset(create_plugin_progress_reporter( + dialog_message, QStringLiteral("OpenFX"))); + progress_reporter_->set_cancel_callback( + [this](void *) { progress_cancelled_ = true; }, nullptr); progress_reporter_->show(); } @@ -460,7 +459,7 @@ void OlivePluginInstance::progressEnd() if (progress_reporter_) { progress_reporter_->close(); - progress_reporter_->deleteLater(); + progress_reporter_.reset(); } } diff --git a/engine/pluginSupport/oliveplugininstance.h b/engine/pluginSupport/oliveplugininstance.h index 03e02c4f9..405395ab6 100644 --- a/engine/pluginSupport/oliveplugininstance.h +++ b/engine/pluginSupport/oliveplugininstance.h @@ -22,6 +22,7 @@ #include #include "ofxhImageEffect.h" #include "node/plugins/plugin.h" +#include "pluginSupport/pluginprogressreporter.h" #include "render/videoparams.h" #include "undo/undocommand.h" @@ -228,7 +229,7 @@ private: QString edit_label_; QString edit_first_label_; int edit_param_count_ = 0; - QPointer progress_reporter_; + std::unique_ptr progress_reporter_; bool progress_cancelled_ = false; bool progress_active_ = false; bool open_gl_enabled_ = false; diff --git a/engine/pluginSupport/pluginprogressreporter.cpp b/engine/pluginSupport/pluginprogressreporter.cpp index 8104bb806..0b1923f61 100644 --- a/engine/pluginSupport/pluginprogressreporter.cpp +++ b/engine/pluginSupport/pluginprogressreporter.cpp @@ -27,7 +27,7 @@ namespace /** * @brief No-op reporter used when no UI factory is registered * - * Never emits cancelled(), so processing always continues. + * Never reports cancellation, so processing always continues. */ class NullPluginProgressReporter : public PluginProgressReporter { public: diff --git a/engine/pluginSupport/pluginprogressreporter.h b/engine/pluginSupport/pluginprogressreporter.h index cd467612e..2e133b55f 100644 --- a/engine/pluginSupport/pluginprogressreporter.h +++ b/engine/pluginSupport/pluginprogressreporter.h @@ -18,7 +18,6 @@ #ifndef OAK_PLUGIN_PROGRESS_REPORTER_H #define OAK_PLUGIN_PROGRESS_REPORTER_H -#include #include #include @@ -35,16 +34,16 @@ namespace plugin * 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. + * + * Cancellation is delivered through a C-style callback rather than a Qt + * signal, so the class does not need Q_OBJECT and can be used across the + * liboakengine C ABI boundary without MOC-generated symbols. */ -class PluginProgressReporter : public QObject { - Q_OBJECT +class PluginProgressReporter { public: - explicit PluginProgressReporter(QObject *parent = nullptr) - : QObject(parent) - { - } + PluginProgressReporter() = default; - virtual ~PluginProgressReporter() override = default; + virtual ~PluginProgressReporter() = default; virtual void set_progress(double value) = 0; @@ -52,8 +51,40 @@ public: virtual void close() = 0; -signals: - void cancelled(); + /** + * @brief Register a callback to be invoked when the user cancels. + * + * The engine (or any C ABI consumer) registers this to receive the + * cancellation event. Only one callback is supported; subsequent calls + * replace the previous registration. Pass nullptr to clear. + */ + void set_cancel_callback(std::function cb, void *userdata) + { + cancel_callback_ = std::move(cb); + cancel_callback_userdata_ = userdata; + } + + /** + * @brief Mark this reporter as cancelled and notify the registered + * callback. + */ + void set_cancelled() + { + cancelled_ = true; + if (cancel_callback_) { + cancel_callback_(cancel_callback_userdata_); + } + } + + bool cancelled() const + { + return cancelled_; + } + +private: + std::function cancel_callback_; + void *cancel_callback_userdata_ = nullptr; + bool cancelled_ = false; }; /** @@ -64,7 +95,7 @@ signals: */ using PluginProgressReporterFactory = std::function; + const QString &title)>; void set_plugin_progress_reporter_factory( PluginProgressReporterFactory factory); diff --git a/engine/render/managedcolor.cpp b/engine/render/managedcolor.cpp index cb1ebffdd..89112d53c 100644 --- a/engine/render/managedcolor.cpp +++ b/engine/render/managedcolor.cpp @@ -19,50 +19,7 @@ ***/ -#include "managedcolor.h" - -namespace olive -{ - -ManagedColor::ManagedColor() -{ -} - -ManagedColor::ManagedColor(const double &r, const double &g, const double &b, - const double &a) - : Color(r, g, b, a) -{ -} - -ManagedColor::ManagedColor(const char *data, const PixelFormat &format, - int channel_layout) - : Color(data, format, channel_layout) -{ -} - -ManagedColor::ManagedColor(const Color &c) - : Color(c) -{ -} - -const QString &ManagedColor::color_input() const -{ - return color_input_; -} - -void ManagedColor::set_color_input(const QString &color_input) -{ - color_input_ = color_input; -} - -const ColorTransform &ManagedColor::color_output() const -{ - return color_transform_; -} - -void ManagedColor::set_color_output(const ColorTransform &color_output) -{ - color_transform_ = color_output; -} - -} +// ManagedColor has moved to application code +// (app/widget/manageddisplay/colorprocessorhandle.h) as part of the C ABI +// migration. This translation unit is intentionally left empty (the file is +// kept so the existing build rules keep working). diff --git a/engine/render/managedcolor.h b/engine/render/managedcolor.h index 2eb378f14..0f4bc9dfe 100644 --- a/engine/render/managedcolor.h +++ b/engine/render/managedcolor.h @@ -22,34 +22,10 @@ #ifndef OAK_MANAGEDCOLOR_H #define OAK_MANAGEDCOLOR_H -#include - -#include "colortransform.h" - -namespace olive -{ - -class ManagedColor : public Color { -public: - ManagedColor(); - ManagedColor(const double &r, const double &g, const double &b, - const double &a = 1.0); - ManagedColor(const char *data, const PixelFormat &format, - int channel_layout); - ManagedColor(const Color &c); - - const QString &color_input() const; - void set_color_input(const QString &color_input); - - const ColorTransform &color_output() const; - void set_color_output(const ColorTransform &color_output); - -private: - QString color_input_; - - ColorTransform color_transform_; -}; - -} +// ManagedColor has moved to application code +// (app/widget/manageddisplay/colorprocessorhandle.h) as part of the C ABI +// migration: it is a pure UI value type that the engine never uses. This +// header is intentionally left empty (the file is kept so the existing +// build rules keep working) and must not be included by new code. #endif // OAK_MANAGEDCOLOR_H diff --git a/engine/render/renderworkerpool.cpp b/engine/render/renderworkerpool.cpp index 5158f243c..3bffd2c2d 100644 --- a/engine/render/renderworkerpool.cpp +++ b/engine/render/renderworkerpool.cpp @@ -446,6 +446,12 @@ bool RenderWorkerPool::submit_frame( ticket->moveToThread(this); + // Mark the ticket running the moment it is accepted for rendering. + // Otherwise there is a window between dispatch and worker pickup where the + // ticket still appears idle, and clear_single_frame_renders() -- which only + // spares running tickets -- would cancel a frame the viewer just requested. + ticket->start(); + QMutexLocker locker(&mutex_); queue_.push_back(job); wait_.wakeOne(); diff --git a/engine/src/worker_dockicon_mac.mm b/engine/src/worker_dockicon_mac.mm new file mode 100644 index 000000000..31b24547c --- /dev/null +++ b/engine/src/worker_dockicon_mac.mm @@ -0,0 +1,26 @@ +/*** + + Oak - Non-Linear Video Editor + Copyright (C) 2026 Oak 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 . + +***/ + +#include + +void HideWorkerDockIcon() +{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; +} diff --git a/engine/task/project/import/import.cpp b/engine/task/project/import/import.cpp index d2d7bdf53..fd4e3b6e9 100644 --- a/engine/task/project/import/import.cpp +++ b/engine/task/project/import/import.cpp @@ -46,6 +46,11 @@ ProjectImportTask::ProjectImportTask(Folder *folder, set_title(tr("Importing %n file(s)", nullptr, file_count_)); } +ProjectImportTask::~ProjectImportTask() +{ + delete command_; +} + const int &ProjectImportTask::get_file_count() const { return file_count_; diff --git a/engine/task/project/import/import.h b/engine/task/project/import/import.h index 0f49bc5af..648ae6da9 100644 --- a/engine/task/project/import/import.h +++ b/engine/task/project/import/import.h @@ -37,12 +37,17 @@ class ProjectImportTask : public Task { Q_OBJECT public: ProjectImportTask(Folder *folder, const QStringList &filenames); + ~ProjectImportTask() override; const int &get_file_count() const; - MultiUndoCommand *get_command() const + /** Take ownership of the import command. After this call the task no + * longer owns (and will not delete) the returned command. */ + MultiUndoCommand *take_command() { - return command_; + MultiUndoCommand *c = command_; + command_ = nullptr; + return c; } const QStringList &get_invalid_files() const diff --git a/engine/task/project/load/load.h b/engine/task/project/load/load.h index 316558cf1..28ec34feb 100644 --- a/engine/task/project/load/load.h +++ b/engine/task/project/load/load.h @@ -23,7 +23,7 @@ #define OAK_PROJECTLOADMANAGER_H #include "loadbasetask.h" -#include "node/project/serializer/mainwindowlayoutinfo.h" +#include "node/project/serializer/serializedlayoutinfo.h" namespace olive { diff --git a/engine/task/project/load/loadbasetask.h b/engine/task/project/load/loadbasetask.h index 638d1e861..411093546 100644 --- a/engine/task/project/load/loadbasetask.h +++ b/engine/task/project/load/loadbasetask.h @@ -23,7 +23,7 @@ #define OAK_PROJECTLOADBASETASK_H #include "node/project.h" -#include "node/project/serializer/mainwindowlayoutinfo.h" +#include "node/project/serializer/serializedlayoutinfo.h" #include "task/task.h" namespace olive @@ -44,7 +44,7 @@ public: return filename_; } - const MainWindowLayoutInfo &get_loaded_layout() const + const SerializedLayoutInfo &get_loaded_layout() const { return layout_; } @@ -52,7 +52,7 @@ public: protected: Project *project_; - MainWindowLayoutInfo layout_; + SerializedLayoutInfo layout_; private: QString filename_; diff --git a/engine/task/project/save/save.h b/engine/task/project/save/save.h index 904e113eb..0fee942cc 100644 --- a/engine/task/project/save/save.h +++ b/engine/task/project/save/save.h @@ -23,7 +23,7 @@ #define OAK_PROJECTSAVEMANAGER_H #include "node/project.h" -#include "node/project/serializer/mainwindowlayoutinfo.h" +#include "node/project/serializer/serializedlayoutinfo.h" #include "task/task.h" namespace olive @@ -44,7 +44,7 @@ public: override_filename_ = filename; } - void set_layout(const MainWindowLayoutInfo &layout) + void set_layout(const SerializedLayoutInfo &layout) { layout_ = layout; } @@ -59,7 +59,7 @@ private: bool use_compression_; - MainWindowLayoutInfo layout_; + SerializedLayoutInfo layout_; }; } diff --git a/engine/timeline/CMakeLists.txt b/engine/timeline/CMakeLists.txt index 1a3e0aacc..706fe23be 100644 --- a/engine/timeline/CMakeLists.txt +++ b/engine/timeline/CMakeLists.txt @@ -17,8 +17,6 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} timeline/timelinecommon.h - timeline/timelinecoordinate.h - timeline/timelinecoordinate.cpp timeline/timelinemarker.h timeline/timelinemarker.cpp timeline/timelineundocommon.h diff --git a/engine/timeline/timelinecoordinate.cpp b/engine/timeline/timelinecoordinate.cpp deleted file mode 100644 index 90cc2c11d..000000000 --- a/engine/timeline/timelinecoordinate.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/*** - - 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 . - -***/ - -#include "timelinecoordinate.h" - -namespace olive -{ - -TimelineCoordinate::TimelineCoordinate() - : track_(Track::k_none, 0) -{ -} - -TimelineCoordinate::TimelineCoordinate(const Rational &frame, - const Track::Reference &track) - : frame_(frame) - , track_(track) -{ -} - -TimelineCoordinate::TimelineCoordinate(const Rational &frame, - const Track::Type &track_type, - const int &track_index) - : frame_(frame) - , track_(track_type, track_index) -{ -} - -const Rational &TimelineCoordinate::get_frame() const -{ - return frame_; -} - -const Track::Reference &TimelineCoordinate::get_track() const -{ - return track_; -} - -void TimelineCoordinate::set_frame(const Rational &frame) -{ - frame_ = frame; -} - -void TimelineCoordinate::set_track(const Track::Reference &track) -{ - track_ = track; -} - -} diff --git a/engine/timeline/timelinecoordinate.h b/engine/timeline/timelinecoordinate.h deleted file mode 100644 index 08d1a5d01..000000000 --- a/engine/timeline/timelinecoordinate.h +++ /dev/null @@ -1,51 +0,0 @@ -/*** - - 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 . - -***/ - -#ifndef OAK_TIMELINECOORDINATE_H -#define OAK_TIMELINECOORDINATE_H - -#include "node/output/track/track.h" - -namespace olive -{ - -class TimelineCoordinate { -public: - TimelineCoordinate(); - TimelineCoordinate(const Rational &frame, const Track::Reference &track); - TimelineCoordinate(const Rational &frame, const Track::Type &track_type, - const int &track_index); - - const Rational &get_frame() const; - const Track::Reference &get_track() const; - - void set_frame(const Rational &frame); - void set_track(const Track::Reference &track); - -private: - Rational frame_; - - Track::Reference track_; -}; - -} - -#endif // OAK_TIMELINECOORDINATE_H diff --git a/engine/ui/CMakeLists.txt b/engine/ui/CMakeLists.txt index e10c1a821..72d36e3dd 100644 --- a/engine/ui/CMakeLists.txt +++ b/engine/ui/CMakeLists.txt @@ -14,8 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -add_subdirectory(icons) - set(OLIVE_SOURCES ${OLIVE_SOURCES} ui/colorcoding.cpp diff --git a/engine/ui/icons/CMakeLists.txt b/engine/ui/icons/CMakeLists.txt deleted file mode 100644 index b1cae78dc..000000000 --- a/engine/ui/icons/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# Olive - Non-Linear Video Editor -# Copyright (C) 2022 Olive 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 . - -set(OLIVE_SOURCES - ${OLIVE_SOURCES} - ui/icons/icons.h - ui/icons/icons.cpp - PARENT_SCOPE -) diff --git a/engine/ui/icons/icons.cpp b/engine/ui/icons/icons.cpp deleted file mode 100644 index cadcf6e11..000000000 --- a/engine/ui/icons/icons.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/*** - - 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 . - -***/ - -#include "icons.h" - -namespace olive -{ - -/// Works in conjunction with `genicons.sh` to generate and utilize icons of specific sizes -const int icon_size_count = 4; -const int icon_sizes[] = { 16, 32, 64, 128 }; - -/// Internal icon library for use throughout Olive without having to regenerate constantly -QIcon icon::go_to_start; -QIcon icon::prev_frame; -QIcon icon::play; -QIcon icon::pause; -QIcon icon::next_frame; -QIcon icon::go_to_end; -QIcon icon::New; -QIcon icon::open; -QIcon icon::save; -QIcon icon::undo; -QIcon icon::redo; -QIcon icon::tree_view; -QIcon icon::list_view; -QIcon icon::icon_view; -QIcon icon::tool_pointer; -QIcon icon::tool_edit; -QIcon icon::tool_ripple; -QIcon icon::tool_rolling; -QIcon icon::tool_razor; -QIcon icon::tool_slip; -QIcon icon::tool_slide; -QIcon icon::tool_hand; -QIcon icon::tool_transition; -QIcon icon::tool_track_select; -QIcon icon::folder; -QIcon icon::sequence; -QIcon icon::video; -QIcon icon::audio; -QIcon icon::image; -QIcon icon::mini_map; -QIcon icon::tri_up; -QIcon icon::tri_left; -QIcon icon::tri_down; -QIcon icon::tri_right; -QIcon icon::text_bold; -QIcon icon::text_italic; -QIcon icon::text_underline; -QIcon icon::text_strikethrough; -QIcon icon::text_small_caps; -QIcon icon::text_align_left; -QIcon icon::text_align_right; -QIcon icon::text_align_center; -QIcon icon::text_align_justify; -QIcon icon::text_align_top; -QIcon icon::text_align_bottom; -QIcon icon::text_align_middle; -QIcon icon::snapping; -QIcon icon::zoom_in; -QIcon icon::zoom_out; -QIcon icon::record; -QIcon icon::add; -QIcon icon::error; -QIcon icon::dir_up; -QIcon icon::clock; -QIcon icon::diamond; -QIcon icon::plus; -QIcon icon::minus; -QIcon icon::add_effect; -QIcon icon::eye_opened; -QIcon icon::eye_closed; -QIcon icon::lock_opened; -QIcon icon::lock_closed; -QIcon icon::pencil; -QIcon icon::subtitles; -QIcon icon::color_picker; - -void icon::load_all(const QString &theme) -{ - go_to_start = create(theme, "prev"); - prev_frame = create(theme, "rew"); - play = create(theme, "play"); - pause = create(theme, "pause"); - next_frame = create(theme, "ff"); - go_to_end = create(theme, "next"); - - New = create(theme, "new"); - open = create(theme, "open"); - save = create(theme, "save"); - undo = create(theme, "undo"); - redo = create(theme, "redo"); - tree_view = create(theme, "treeview"); - list_view = create(theme, "listview"); - icon_view = create(theme, "iconview"); - - tool_pointer = create(theme, "arrow"); - tool_edit = create(theme, "beam"); - tool_ripple = create(theme, "ripple"); - tool_rolling = create(theme, "rolling"); - tool_razor = create(theme, "razor"); - tool_slip = create(theme, "slip"); - tool_slide = create(theme, "slide"); - tool_hand = create(theme, "hand"); - tool_transition = create(theme, "transition-tool"); - tool_track_select = create(theme, "track-tool"); - - folder = create(theme, "folder"); - sequence = create(theme, "sequence"); - video = create(theme, "videosource"); - audio = create(theme, "audiosource"); - image = create(theme, "imagesource"); - - mini_map = create(theme, "map"); - - tri_up = create(theme, "tri-up"); - tri_left = create(theme, "tri-left"); - tri_down = create(theme, "tri-down"); - tri_right = create(theme, "tri-right"); - - text_bold = create(theme, "text-bold"); - text_italic = create(theme, "text-italic"); - text_underline = create(theme, "text-underline"); - text_strikethrough = create(theme, "text-strikethrough"); - text_small_caps = create(theme, "text-small-caps"); - text_align_left = create(theme, "align-left"); - text_align_right = create(theme, "align-right"); - text_align_center = create(theme, "align-center"); - text_align_justify = create(theme, "align-justify-all"); - text_align_top = create(theme, "align-v-top"); - text_align_bottom = create(theme, "align-v-bottom"); - text_align_middle = create(theme, "align-v-middle"); - - snapping = create(theme, "magnet"); - zoom_in = create(theme, "zoomin"); - zoom_out = create(theme, "zoomout"); - record = create(theme, "record"); - add = create(theme, "add-button"); - error = create(theme, "error"); - dir_up = create(theme, "dirup"); - clock = create(theme, "clock"); - diamond = create(theme, "diamond"); - plus = create(theme, "plus"); - minus = create(theme, "minus"); - add_effect = create(theme, "add-effect"); - color_picker = create(theme, "color-picker"); - - eye_opened = create(theme, "eye-opened"); - eye_closed = create(theme, "eye-closed"); - lock_opened = create(theme, "lock-opened"); - lock_closed = create(theme, "lock-closed"); - - pencil = create(theme, "text-edit"); - subtitles = create(theme, "subtitles"); -} - -QIcon icon::create(const QString &theme, const QString &name) -{ - QIcon icon; - - for (int i = 0; i < icon_size_count; i++) { - icon.addFile(QStringLiteral("%1/png/%2.%3.png") - .arg(theme, name, QString::number(icon_sizes[i])), - QSize(icon_sizes[i], icon_sizes[i]), QIcon::Normal); - icon.addFile(QStringLiteral("%1/png/%2.%3.disabled.png") - .arg(theme, name, QString::number(icon_sizes[i])), - QSize(icon_sizes[i], icon_sizes[i]), QIcon::Disabled); - } - - return icon; -} - -} diff --git a/engine/ui/icons/icons.h b/engine/ui/icons/icons.h deleted file mode 100644 index bc8581823..000000000 --- a/engine/ui/icons/icons.h +++ /dev/null @@ -1,159 +0,0 @@ -/*** - - 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 . - -***/ - -#ifndef OAK_ICONS_H -#define OAK_ICONS_H - -#include - -#include "common/define.h" - -namespace olive -{ - -namespace icon -{ - -// Playback Icons -extern QIcon go_to_start; -extern QIcon prev_frame; -extern QIcon play; -extern QIcon pause; -extern QIcon next_frame; -extern QIcon go_to_end; - -// Project Management Toolbar Icons -extern QIcon New; -extern QIcon open; -extern QIcon save; -extern QIcon undo; -extern QIcon redo; -extern QIcon tree_view; -extern QIcon list_view; -extern QIcon icon_view; - -// Tool Icons -extern QIcon tool_pointer; -extern QIcon tool_edit; -extern QIcon tool_ripple; -extern QIcon tool_rolling; -extern QIcon tool_razor; -extern QIcon tool_slip; -extern QIcon tool_slide; -extern QIcon tool_hand; -extern QIcon tool_transition; -extern QIcon tool_track_select; - -// Project Icons -extern QIcon folder; -extern QIcon sequence; -extern QIcon video; -extern QIcon audio; -extern QIcon image; - -// Node Icons -extern QIcon mini_map; - -// Triangle Arrows -extern QIcon tri_up; -extern QIcon tri_left; -extern QIcon tri_down; -extern QIcon tri_right; - -// Text -extern QIcon text_bold; -extern QIcon text_italic; -extern QIcon text_underline; -extern QIcon text_strikethrough; -extern QIcon text_small_caps; -extern QIcon text_align_left; -extern QIcon text_align_right; -extern QIcon text_align_center; -extern QIcon text_align_justify; -extern QIcon text_align_top; -extern QIcon text_align_bottom; -extern QIcon text_align_middle; - -// Miscellaneous Icons -extern QIcon snapping; -extern QIcon zoom_in; -extern QIcon zoom_out; -extern QIcon record; -extern QIcon add; -extern QIcon error; -extern QIcon dir_up; -extern QIcon clock; -extern QIcon diamond; -extern QIcon plus; -extern QIcon minus; -extern QIcon add_effect; -extern QIcon eye_opened; -extern QIcon eye_closed; -extern QIcon lock_opened; -extern QIcon lock_closed; -extern QIcon pencil; -extern QIcon subtitles; -extern QIcon color_picker; - -/** - * @brief Create an icon object loaded from file - * - * Using `name`, this function will load icon files to create an icon object that can be used throughout the - * application. - * - * Olive's icons are stored in a very specific format. They are all sourced from SVGs, but stored as PNGs of various - * sizes. See `app/ui/icons/genicons.sh`, as this script not only generates the multiple sizes but also the QRC file - * used to compile the icons into the executable. - * - * This function is heavily tied into `genicons.sh` and will load all the different sized images (using the same - * filename formatting and QRC resource directory) that `genicons.sh` generates into one QIcon file. If you change - * either this function or `genicons.sh`, you will very likely have to change the other too. - * - * There is not much reason to call this outside of LoadAll() (which stores icons globally in memory so they don't - * have to be reloaded each time a new object needs an icon). - * - * @param theme - * - * Name of the theme (used in the URL as the folder to load PNGs from) - * - * @param name - * - * Name of the icon (will correspond to the original SVG's filename with no path or extension) - * - * @return - * - * A QIcon object containing the various icon sizes loaded from resource - */ -QIcon create(const QString &theme, const QString &name); - -/** - * @brief Methodically load all Olive icons into global variables that can be accessed throughout the application - * - * It's recommended to load any UI icons here so they're ready at startup and don't need to be re-loaded upon each - * use. - */ -void load_all(const QString &theme); - -} - -} - -#endif // OAK_ICONS_H diff --git a/engine/undo/undostack.cpp b/engine/undo/undostack.cpp index 80744287b..147821aef 100644 --- a/engine/undo/undostack.cpp +++ b/engine/undo/undostack.cpp @@ -105,6 +105,42 @@ void UndoStack::push(UndoCommand *command, const QString &name) update_actions(); } +void UndoStack::push_pre_executed(UndoCommand *command, const QString &name) +{ + MultiUndoCommand *mcu = dynamic_cast(command); + if (mcu && mcu->child_count() == 0) { + delete command; + return; + } + + // Clear any redoable commands + this->beginRemoveRows(QModelIndex(), commands_.size(), + commands_.size() + undone_commands_.size()); + if (can_redo()) { + for (auto it = undone_commands_.cbegin(); it != undone_commands_.cend(); + it++) { + delete (*it).command; + } + undone_commands_.clear(); + } + this->endRemoveRows(); + + // Push without redoing: the caller already executed the children. + this->beginInsertRows(QModelIndex(), commands_.size(), commands_.size()); + commands_.push_back({ command, name }); + this->endInsertRows(); + + // Delete oldest + if (commands_.size() > k_max_undo_commands) { + this->beginRemoveRows(QModelIndex(), 0, 0); + delete commands_.front().command; + commands_.pop_front(); + this->endRemoveRows(); + } + + update_actions(); +} + void UndoStack::jump(size_t index) { while (commands_.size() > index) { diff --git a/engine/undo/undostack.h b/engine/undo/undostack.h index 8684989d9..749cfa848 100644 --- a/engine/undo/undostack.h +++ b/engine/undo/undostack.h @@ -40,6 +40,15 @@ public: void push(UndoCommand *command, const QString &name); + /** + * @brief Push a command that has already been executed (redo skipped). + * + * Used by the facade undo-group: child commands are added to the group + * and executed eagerly, then the whole group is pushed with this method + * so it is not redone again. Empty commands are discarded. + */ + void push_pre_executed(UndoCommand *command, const QString &name); + void jump(size_t index); void clear(); @@ -78,6 +87,40 @@ public: virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + // Facade accessors (oakengine/undo.h C ABI): row-based history queries. + // Rows 0..done_count()-1 are done commands (commands_ in order), rows + // done_count()..command_count()-1 are undone commands (undone_commands_ + // in order, most recently undone first). + int command_count() const + { + return int(commands_.size() + undone_commands_.size()); + } + + int done_count() const + { + return int(commands_.size()); + } + + bool command_is_done(int row) const + { + return row >= 0 && row < done_count(); + } + + QString command_name(int row) const + { + if (row < 0 || row >= command_count()) { + return QString(); + } + if (row < done_count()) { + auto it = commands_.begin(); + std::advance(it, row); + return it->name; + } + auto it = undone_commands_.begin(); + std::advance(it, row - done_count()); + return it->name; + } + signals: void index_changed(int i);