engine: internals supporting the facade migration

UndoStack push_pre_executed, FolderAddChild-based true folder moves,
NodeViewDeleteCommand delete-then-reconnect support, event emission
points for the facade event bus, and related internal adjustments.
This commit is contained in:
2026-07-26 22:43:09 +08:00
parent e9f173916f
commit f95590e924
38 changed files with 302 additions and 746 deletions
+4
View File
@@ -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:
+1 -1
View File
@@ -178,7 +178,7 @@ void EngineCore::declare_types_for_qt()
qRegisterMetaType<olive::AudioVisualWaveform>();
qRegisterMetaType<olive::VideoParams>();
qRegisterMetaType<olive::VideoParams::Interlacing>();
qRegisterMetaType<olive::MainWindowLayoutInfo>();
qRegisterMetaType<olive::SerializedLayoutInfo>();
qRegisterMetaType<olive::RenderTicketPtr>();
}
+33 -16
View File
@@ -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<void(const MainWindowLayoutInfo &layout)>;
std::function<void(const SerializedLayoutInfo &layout)>;
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<Sequence *> &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
+3
View File
@@ -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;
+6 -7
View File
@@ -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<OakEngineKeyframe *>(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<OakEngineKeyframe *>(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<OakEngineKeyframe *>(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<OakEngineKeyframe *>(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<OakEngineKeyframe *>(key));
}
void Node::set_value_at_time(const NodeInput &input, const Rational &time,
+9 -5
View File
@@ -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);
+1 -2
View File
@@ -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);
+5 -6
View File
@@ -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_) {
+1 -2
View File
@@ -23,7 +23,6 @@
#include <QThread>
#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);
@@ -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
@@ -16,19 +16,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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<quintptr>(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<quintptr>(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<quintptr>(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<quintptr, Node *> &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<Folder *>(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<Sequence *>(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<ViewerOutput *>(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;
}
}
@@ -16,8 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OAK_MAINWINDOWLAYOUTINFO_H
#define OAK_MAINWINDOWLAYOUTINFO_H
#ifndef OAK_SERIALIZEDLAYOUTINFO_H
#define OAK_SERIALIZEDLAYOUTINFO_H
#include <map>
@@ -35,68 +35,38 @@ namespace olive
*/
using PanelLayoutInfo = std::map<QString, QString>;
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<quintptr, Node *> &node_map);
void add_folder(Folder *f);
QByteArray state;
void add_sequence(Sequence *seq);
std::vector<Folder *> open_folders;
void add_viewer(ViewerOutput *viewer);
std::vector<Sequence *> open_sequences;
void set_panel_data(const QString &id, const PanelLayoutInfo &data);
std::vector<ViewerOutput *> open_viewers;
void move_panel_data(const QString &old, const QString &now);
void set_state(const QByteArray &layout);
const std::vector<Folder *> &open_folders() const
{
return open_folders_;
}
const std::vector<Sequence *> &open_sequences() const
{
return open_sequences_;
}
const std::vector<ViewerOutput *> &open_viewers() const
{
return open_viewers_;
}
const std::map<QString, PanelLayoutInfo> &panel_data() const
{
return panel_data_;
}
const QByteArray &state() const
{
return state_;
}
std::map<QString, PanelLayoutInfo> panel_data;
private:
QByteArray state_;
std::vector<Folder *> open_folders_;
std::vector<Sequence *> open_sequences_;
std::vector<ViewerOutput *> open_viewers_;
std::map<QString, PanelLayoutInfo> 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
+5 -5
View File
@@ -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<Node *> 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<Node *> only_serialize_nodes_;
@@ -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")) {
@@ -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();
+6
View File
@@ -0,0 +1,6 @@
{
global:
oakengine_*;
local:
*;
};
+6 -7
View File
@@ -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();
}
}
+2 -1
View File
@@ -22,6 +22,7 @@
#include <QString>
#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<PluginProgressReporter> progress_reporter_;
std::unique_ptr<PluginProgressReporter> progress_reporter_;
bool progress_cancelled_ = false;
bool progress_active_ = false;
bool open_gl_enabled_ = false;
@@ -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:
+42 -11
View File
@@ -18,7 +18,6 @@
#ifndef OAK_PLUGIN_PROGRESS_REPORTER_H
#define OAK_PLUGIN_PROGRESS_REPORTER_H
#include <QObject>
#include <QString>
#include <functional>
@@ -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<void(void *)> 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<void(void *)> cancel_callback_;
void *cancel_callback_userdata_ = nullptr;
bool cancelled_ = false;
};
/**
@@ -64,7 +95,7 @@ signals:
*/
using PluginProgressReporterFactory =
std::function<PluginProgressReporter *(const QString &message,
const QString &title)>;
const QString &title)>;
void set_plugin_progress_reporter_factory(
PluginProgressReporterFactory factory);
+4 -47
View File
@@ -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).
+5 -29
View File
@@ -22,34 +22,10 @@
#ifndef OAK_MANAGEDCOLOR_H
#define OAK_MANAGEDCOLOR_H
#include <olive/core/core.h>
#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
+6
View File
@@ -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();
+26
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#include <Cocoa/Cocoa.h>
void HideWorkerDockIcon()
{
[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
}
+5
View File
@@ -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_;
+7 -2
View File
@@ -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
+1 -1
View File
@@ -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
{
+3 -3
View File
@@ -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_;
+3 -3
View File
@@ -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_;
};
}
-2
View File
@@ -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
-67
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#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;
}
}
-51
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#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
-2
View File
@@ -14,8 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(icons)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
ui/colorcoding.cpp
-22
View File
@@ -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 <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
ui/icons/icons.h
ui/icons/icons.cpp
PARENT_SCOPE
)
-192
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#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;
}
}
-159
View File
@@ -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 <http://www.gnu.org/licenses/>.
***/
#ifndef OAK_ICONS_H
#define OAK_ICONS_H
#include <QIcon>
#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
+36
View File
@@ -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<MultiUndoCommand *>(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) {
+43
View File
@@ -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);