app: migrate all engine access to the C ABI facade (nm U _ZN5olive = 0)
Every app module now reaches liboakengine exclusively through oakengine_* C calls, EngineEventBridge subscriptions and app-side handle headers (cliphandle/keyframehandle/nodevaluehandle/oakvaluehelper). Direct C++ command construction, engine signal connect()s, and engine type usage in MOC-visible signatures are gone: 557 -> 0 undefined olive:: symbols in oak-editor.
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/timeruler/markerpainting.h
|
||||
widget/timeruler/markerpainting.cpp
|
||||
widget/timeruler/seekablewidget.h
|
||||
widget/timeruler/seekablewidget.cpp
|
||||
widget/timeruler/timeruler.h
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/***
|
||||
|
||||
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/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_MARKERHANDLE_H
|
||||
#define OAK_MARKERHANDLE_H
|
||||
|
||||
#include <olive/core/core.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
#include "oakengine/timeline.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
class TimelineMarker;
|
||||
class TimelineMarkerList;
|
||||
|
||||
using olive::core::Rational;
|
||||
using olive::core::TimeRange;
|
||||
|
||||
/**
|
||||
* @brief Facade accessors for marker pointers held by the ruler/scrollbar
|
||||
* widgets.
|
||||
*
|
||||
* The widgets keep olive::TimelineMarker* / olive::TimelineMarkerList* as
|
||||
* opaque identity pointers (selection, drawing, hit-testing). All engine
|
||||
* data and mutations go through the liboakengine C ABI
|
||||
* (oakengine/timeline.h); the pointer itself is only a handle. Marker
|
||||
* times are rational seconds (num/den pairs), not frame timestamps.
|
||||
*/
|
||||
|
||||
inline OakEngineMarker *markerhandle(TimelineMarker *marker)
|
||||
{
|
||||
return reinterpret_cast<OakEngineMarker *>(marker);
|
||||
}
|
||||
|
||||
inline const OakEngineMarker *markerhandle(const TimelineMarker *marker)
|
||||
{
|
||||
return reinterpret_cast<const OakEngineMarker *>(marker);
|
||||
}
|
||||
|
||||
inline TimelineMarker *markerhandle(OakEngineMarker *marker)
|
||||
{
|
||||
return reinterpret_cast<TimelineMarker *>(marker);
|
||||
}
|
||||
|
||||
inline OakEngineMarkerList *markerlisthandle(TimelineMarkerList *list)
|
||||
{
|
||||
return reinterpret_cast<OakEngineMarkerList *>(list);
|
||||
}
|
||||
|
||||
inline const OakEngineMarkerList *markerlisthandle(
|
||||
const TimelineMarkerList *list)
|
||||
{
|
||||
return reinterpret_cast<const OakEngineMarkerList *>(list);
|
||||
}
|
||||
|
||||
inline TimeRange marker_time(const TimelineMarker *marker)
|
||||
{
|
||||
int64_t in_num = 0, in_den = 1, out_num = 0, out_den = 1;
|
||||
oakengine_marker_get_time(markerhandle(marker), &in_num, &in_den,
|
||||
&out_num, &out_den);
|
||||
return TimeRange(Rational(int(in_num), int(in_den)),
|
||||
Rational(int(out_num), int(out_den)));
|
||||
}
|
||||
|
||||
inline QString marker_name(const TimelineMarker *marker)
|
||||
{
|
||||
const int size =
|
||||
oakengine_marker_get_name(markerhandle(marker), nullptr, 0);
|
||||
QByteArray buf(size + 1, '\0');
|
||||
oakengine_marker_get_name(markerhandle(marker), buf.data(),
|
||||
int(buf.size()));
|
||||
return QString::fromUtf8(buf.constData());
|
||||
}
|
||||
|
||||
inline int marker_color(const TimelineMarker *marker)
|
||||
{
|
||||
return oakengine_marker_get_color(markerhandle(marker));
|
||||
}
|
||||
|
||||
inline bool marker_has_sibling_at_time(const TimelineMarker *marker,
|
||||
const Rational &time)
|
||||
{
|
||||
return oakengine_marker_has_sibling_at_time(
|
||||
markerhandle(marker), time.numerator(), time.denominator()) != 0;
|
||||
}
|
||||
|
||||
inline void marker_set_time_live(TimelineMarker *marker,
|
||||
const TimeRange &range)
|
||||
{
|
||||
oakengine_marker_set_time_live(
|
||||
markerhandle(marker), range.in().numerator(), range.in().denominator(),
|
||||
range.out().numerator(), range.out().denominator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ADL customization points for
|
||||
* TimeBasedViewSelectionManager<TimelineMarker>.
|
||||
*
|
||||
* The selection manager template calls these unqualified; these overloads
|
||||
* route marker access through the facade (see
|
||||
* widget/keyframeview/keyframehandle.h for the NodeKeyframe equivalent).
|
||||
* selection_time_target_parent() is intentionally not overloaded: the
|
||||
* generic template in timebasedviewselectionmanager.h works for markers
|
||||
* and marker drags never pass a time target.
|
||||
*/
|
||||
inline Rational selection_time(TimelineMarker *marker)
|
||||
{
|
||||
return marker_time(marker).in();
|
||||
}
|
||||
|
||||
inline Rational selection_time_end(TimelineMarker *marker)
|
||||
{
|
||||
return marker_time(marker).out();
|
||||
}
|
||||
|
||||
inline void selection_set_time(TimelineMarker *marker, const Rational &time)
|
||||
{
|
||||
// Move the in-point keeping the range length (was
|
||||
// TimelineMarker::set_time(const Rational &))
|
||||
const TimeRange range = marker_time(marker);
|
||||
const Rational length = range.out() - range.in();
|
||||
marker_set_time_live(marker, TimeRange(time, time + length));
|
||||
}
|
||||
|
||||
inline bool selection_has_sibling_at_time(TimelineMarker *marker,
|
||||
const Rational &time)
|
||||
{
|
||||
return marker_has_sibling_at_time(marker, time);
|
||||
}
|
||||
|
||||
} // namespace olive
|
||||
|
||||
#endif // OAK_MARKERHANDLE_H
|
||||
@@ -0,0 +1,115 @@
|
||||
/***
|
||||
|
||||
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 "markerpainting.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPalette>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "common/colorcodingapp.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
namespace MarkerPainting
|
||||
{
|
||||
|
||||
int height(const QFontMetrics &fm)
|
||||
{
|
||||
return fm.height();
|
||||
}
|
||||
|
||||
QRect draw(QPainter *p, const QPoint &pt, int max_right, double scale,
|
||||
bool selected, const QString &name, int color,
|
||||
const core::Rational &in, const core::Rational &out)
|
||||
{
|
||||
QFontMetrics fm = p->fontMetrics();
|
||||
|
||||
int marker_height = height(fm);
|
||||
int marker_width = QtUtils::q_font_metrics_width(fm, QStringLiteral("H"));
|
||||
|
||||
int half_width = marker_width / 2;
|
||||
|
||||
QColor c = QtUtils::to_q_color(ColorCoding::get_color(color));
|
||||
if (selected) {
|
||||
p->setPen(Qt::white);
|
||||
p->setBrush(c.lighter());
|
||||
} else {
|
||||
p->setPen(Qt::black);
|
||||
p->setBrush(c);
|
||||
}
|
||||
|
||||
int top = pt.y() - marker_height;
|
||||
|
||||
QTextOption op(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
op.setWrapMode(QTextOption::NoWrap);
|
||||
|
||||
if (out != in) {
|
||||
QRect marker_rect(pt.x(), top, (out - in).to_double() * scale,
|
||||
marker_height);
|
||||
|
||||
p->drawRect(marker_rect);
|
||||
|
||||
if (!name.isEmpty()) {
|
||||
p->setPen(ColorCoding::get_ui_selector_color(
|
||||
ColorCoding::get_color(color)));
|
||||
p->drawText(marker_rect.adjusted(marker_width / 4, 0, 0, 0), name,
|
||||
op);
|
||||
}
|
||||
|
||||
return marker_rect;
|
||||
} else {
|
||||
int half_marker_height = marker_height / 3;
|
||||
int left = pt.x() - half_width;
|
||||
int right = pt.x() + half_width;
|
||||
int center_y = pt.y() - half_marker_height;
|
||||
|
||||
QPoint points[] = {
|
||||
pt,
|
||||
QPoint(left, center_y),
|
||||
QPoint(left, top),
|
||||
QPoint(right, top),
|
||||
QPoint(right, center_y),
|
||||
pt,
|
||||
};
|
||||
|
||||
p->setRenderHint(QPainter::Antialiasing);
|
||||
p->drawPolygon(points, 6);
|
||||
|
||||
if (!name.isEmpty() && max_right != -1) {
|
||||
QRect text_rect(right, top, max_right - right, marker_height);
|
||||
|
||||
int padding = QtUtils::q_font_metrics_width(p->fontMetrics(),
|
||||
QStringLiteral(" "));
|
||||
text_rect.adjust(padding, 0, -padding - half_width, 0);
|
||||
|
||||
p->setPen(qApp->palette().text().color());
|
||||
p->drawText(text_rect, name, op);
|
||||
}
|
||||
|
||||
return QRect(left, top, marker_width, marker_height);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 Olive Team
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef OAK_MARKERPAINTING_H
|
||||
#define OAK_MARKERPAINTING_H
|
||||
|
||||
#include <olive/core/core.h>
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
#include <QPoint>
|
||||
#include <QRect>
|
||||
#include <QString>
|
||||
|
||||
namespace olive
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Marker painting helpers (pure UI code, moved app-side from the
|
||||
* engine's TimelineMarker::draw()/get_marker_height()).
|
||||
*
|
||||
* The caller passes the marker's data by value (name, color index and
|
||||
* in/out times) so no engine types are needed for drawing.
|
||||
*/
|
||||
namespace MarkerPainting
|
||||
{
|
||||
|
||||
/// Height of a marker in pixels for the given font (was
|
||||
/// TimelineMarker::get_marker_height()).
|
||||
int height(const QFontMetrics &fm);
|
||||
|
||||
/// Draw a marker at `pt` (bottom-center anchor) and return its bounding
|
||||
/// rect (was TimelineMarker::draw()). `max_right` of -1 disables the
|
||||
/// label text; `scale` is pixels per second for ranged markers.
|
||||
QRect draw(QPainter *p, const QPoint &pt, int max_right, double scale,
|
||||
bool selected, const QString &name, int color,
|
||||
const core::Rational &in, const core::Rational &out);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // OAK_MARKERPAINTING_H
|
||||
@@ -30,9 +30,13 @@
|
||||
#include "common/range.h"
|
||||
#include "core.h"
|
||||
#include "dialog/markerproperties/markerpropertiesdialog.h"
|
||||
#include "markerpainting.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "oakengine/serializer.h"
|
||||
#include "oakengine/timeline.h"
|
||||
#include "oakengine/viewer.h"
|
||||
#include "oakengine/undo.h"
|
||||
#include "timeline/timelineundoworkarea.h"
|
||||
#include "widget/colorlabelmenu/colorlabelmenu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
@@ -54,6 +58,7 @@ SeekableWidget::SeekableWidget(QWidget *parent)
|
||||
, marker_top_(0)
|
||||
, marker_bottom_(0)
|
||||
, marker_editing_enabled_(true)
|
||||
, bridge_(new EngineEventBridge(this))
|
||||
{
|
||||
QFontMetrics fm = fontMetrics();
|
||||
|
||||
@@ -73,26 +78,48 @@ SeekableWidget::SeekableWidget(QWidget *parent)
|
||||
|
||||
void SeekableWidget::set_markers(TimelineMarkerList *markers)
|
||||
{
|
||||
// Unsubscribe old marker list events via bridge
|
||||
for (int64_t id : marker_list_subs_) {
|
||||
bridge_->unsubscribe(id);
|
||||
}
|
||||
marker_list_subs_.clear();
|
||||
|
||||
if (markers_) {
|
||||
selection_manager_.clear_selection();
|
||||
|
||||
disconnect(markers_, &TimelineMarkerList::marker_added, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(markers_, &TimelineMarkerList::marker_removed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(markers_, &TimelineMarkerList::marker_modified, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
}
|
||||
|
||||
markers_ = markers;
|
||||
|
||||
if (markers_) {
|
||||
connect(markers_, &TimelineMarkerList::marker_added, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(markers_, &TimelineMarkerList::marker_removed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(markers_, &TimelineMarkerList::marker_modified, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
// Subscribe to marker list events via bridge instead of direct TimelineMarkerList signals
|
||||
marker_list_subs_.append(bridge_->subscribe(
|
||||
reinterpret_cast<OakEngineMarkerList *>(markers_),
|
||||
OAKENGINE_EVENT_MARKER_LIST_MARKER_ADDED));
|
||||
marker_list_subs_.append(bridge_->subscribe(
|
||||
reinterpret_cast<OakEngineMarkerList *>(markers_),
|
||||
OAKENGINE_EVENT_MARKER_LIST_MARKER_REMOVED));
|
||||
marker_list_subs_.append(bridge_->subscribe(
|
||||
reinterpret_cast<OakEngineMarkerList *>(markers_),
|
||||
OAKENGINE_EVENT_MARKER_LIST_MARKER_MODIFIED));
|
||||
|
||||
// Wire the bridge signals once — bridge_ outlives individual marker
|
||||
// list subscriptions, re-connecting on every set_markers() would
|
||||
// stack duplicate viewport updates.
|
||||
if (!marker_connects_done_) {
|
||||
marker_connects_done_ = true;
|
||||
connect(bridge_, &EngineEventBridge::marker_list_marker_added, this,
|
||||
[this](OakEngineMarkerList *, OakEngineMarker *) {
|
||||
viewport()->update();
|
||||
});
|
||||
connect(bridge_, &EngineEventBridge::marker_list_marker_removed, this,
|
||||
[this](OakEngineMarkerList *, OakEngineMarker *) {
|
||||
viewport()->update();
|
||||
});
|
||||
connect(bridge_, &EngineEventBridge::marker_list_marker_modified, this,
|
||||
[this](OakEngineMarkerList *, OakEngineMarker *) {
|
||||
viewport()->update();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
viewport()->update();
|
||||
@@ -103,18 +130,28 @@ void SeekableWidget::set_work_area(TimelineWorkArea *workarea)
|
||||
if (workarea_) {
|
||||
selection_manager_.clear_selection();
|
||||
|
||||
disconnect(workarea_, &TimelineWorkArea::range_changed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(workarea_, &TimelineWorkArea::enabled_changed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
if (workarea_range_sub_) {
|
||||
bridge_->unsubscribe(workarea_range_sub_);
|
||||
workarea_range_sub_ = 0;
|
||||
}
|
||||
if (workarea_enabled_sub_) {
|
||||
bridge_->unsubscribe(workarea_enabled_sub_);
|
||||
workarea_enabled_sub_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
workarea_ = workarea;
|
||||
|
||||
if (workarea_) {
|
||||
connect(workarea_, &TimelineWorkArea::range_changed, viewport(),
|
||||
workarea_range_sub_ = bridge_->subscribe(
|
||||
reinterpret_cast<void *>(workarea_),
|
||||
OAKENGINE_EVENT_WORKAREA_RANGE_CHANGED);
|
||||
workarea_enabled_sub_ = bridge_->subscribe(
|
||||
reinterpret_cast<void *>(workarea_),
|
||||
OAKENGINE_EVENT_WORKAREA_ENABLED_CHANGED);
|
||||
connect(bridge_, &EngineEventBridge::workarea_range_changed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(workarea_, &TimelineWorkArea::enabled_changed, viewport(),
|
||||
connect(bridge_, &EngineEventBridge::workarea_enabled_changed, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
}
|
||||
|
||||
@@ -147,25 +184,34 @@ void SeekableWidget::delete_selected()
|
||||
return;
|
||||
}
|
||||
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
QVector<OakEngineMarker *> oak_markers;
|
||||
foreach (TimelineMarker *marker, selected) {
|
||||
command->add_child(new MarkerRemoveCommand(marker));
|
||||
oak_markers.append(
|
||||
reinterpret_cast<OakEngineMarker *>(marker));
|
||||
}
|
||||
// Remove each marker (undoable individually)
|
||||
for (auto *m : oak_markers) {
|
||||
oakengine_marker_remove(m);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(
|
||||
command,
|
||||
tr("Deleted %1 Marker(s)").arg(selected.size()));
|
||||
}
|
||||
}
|
||||
|
||||
bool SeekableWidget::copy_selected(bool cut)
|
||||
{
|
||||
if (!selection_manager_.get_selected_objects().empty()) {
|
||||
ProjectSerializer::SaveData sdata(ProjectSerializer::k_only_markers);
|
||||
sdata.set_only_serialize_markers(selection_manager_.get_selected_objects());
|
||||
const auto &selected = selection_manager_.get_selected_objects();
|
||||
std::vector<const OakEngineMarker *> markers;
|
||||
markers.reserve(selected.size());
|
||||
for (auto *m : selected) {
|
||||
markers.push_back(reinterpret_cast<OakEngineMarker *>(m));
|
||||
}
|
||||
|
||||
ProjectSerializer::copy(sdata);
|
||||
OakEngineClipboard *cb = oakengine_clipboard_create(
|
||||
OAKENGINE_CLIPBOARD_MARKERS, nullptr, nullptr);
|
||||
oakengine_clipboard_set_markers(
|
||||
cb, markers.data(), static_cast<int>(markers.size()));
|
||||
oakengine_clipboard_copy(cb);
|
||||
oakengine_clipboard_free(cb);
|
||||
|
||||
if (cut) {
|
||||
delete_selected();
|
||||
@@ -179,13 +225,25 @@ bool SeekableWidget::copy_selected(bool cut)
|
||||
|
||||
bool SeekableWidget::paste_markers()
|
||||
{
|
||||
ProjectSerializer::Result res =
|
||||
ProjectSerializer::paste(ProjectSerializer::k_only_markers);
|
||||
if (res == ProjectSerializer::k_success) {
|
||||
const std::vector<TimelineMarker *> &markers =
|
||||
res.get_load_data().markers;
|
||||
if (!markers.empty()) {
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
OakEngineClipboard *cb = oakengine_clipboard_create(
|
||||
OAKENGINE_CLIPBOARD_MARKERS,
|
||||
reinterpret_cast<OakEngineProject *>(get_viewer_node()->project()),
|
||||
nullptr);
|
||||
int result_code;
|
||||
oakengine_clipboard_paste(
|
||||
cb, OAKENGINE_CLIPBOARD_MARKERS,
|
||||
reinterpret_cast<OakEngineProject *>(get_viewer_node()->project()),
|
||||
&result_code, nullptr, 0);
|
||||
if (result_code == OAKENGINE_OK) {
|
||||
int count = oakengine_clipboard_get_loaded_marker_count(cb);
|
||||
if (count > 0) {
|
||||
// Collect the pasted markers
|
||||
std::vector<TimelineMarker *> markers;
|
||||
markers.reserve(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
markers.push_back(reinterpret_cast<TimelineMarker *>(
|
||||
oakengine_clipboard_get_loaded_marker_at(cb, i)));
|
||||
}
|
||||
|
||||
// Normalize markers to start at playhead
|
||||
Rational min = RATIONAL_MAX;
|
||||
@@ -197,22 +255,30 @@ bool SeekableWidget::paste_markers()
|
||||
for (auto it = markers.cbegin(); it != markers.cend(); it++) {
|
||||
TimelineMarker *m = *it;
|
||||
|
||||
m->set_time(m->time().in() - min);
|
||||
Rational new_in = m->time().in() - min;
|
||||
oakengine_marker_set_time_live(
|
||||
reinterpret_cast<OakEngineMarker *>(m),
|
||||
new_in.numerator(), new_in.denominator(),
|
||||
new_in.numerator(), new_in.denominator());
|
||||
|
||||
if (TimelineMarker *existing =
|
||||
markers_->get_marker_at_time(m->time().in())) {
|
||||
command->add_child(new MarkerRemoveCommand(existing));
|
||||
oakengine_marker_remove(
|
||||
reinterpret_cast<OakEngineMarker *>(existing));
|
||||
}
|
||||
|
||||
command->add_child(new MarkerAddCommand(markers_, m));
|
||||
// Re-add the clipboard marker to the list (undoable)
|
||||
oakengine_marker_list_add_existing(
|
||||
reinterpret_cast<OakEngineMarkerList *>(markers_),
|
||||
reinterpret_cast<OakEngineMarker *>(m));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(
|
||||
command, tr("Pasted %1 Marker(s)").arg(markers.size()));
|
||||
oakengine_clipboard_free(cb);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
oakengine_clipboard_free(cb);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -293,11 +359,10 @@ void SeekableWidget::mouseReleaseEvent(QMouseEvent *event)
|
||||
}
|
||||
|
||||
if (selection_manager_.is_dragging()) {
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
void *command = oakengine_undo_command_create_multi();
|
||||
selection_manager_.drag_stop(command);
|
||||
Core::instance()->undo_stack()->push(
|
||||
command, tr("Moved %1 Marker(s)")
|
||||
.arg(selection_manager_.get_selected_objects().size()));
|
||||
oakengine_undo_push(
|
||||
command, tr("Moved %1 Marker(s)").arg(selection_manager_.get_selected_objects().size()).toUtf8().constData());
|
||||
}
|
||||
|
||||
if (get_snap_service()) {
|
||||
@@ -369,9 +434,11 @@ void SeekableWidget::draw_markers(QPainter *p, int marker_bottom)
|
||||
}
|
||||
}
|
||||
|
||||
QRect marker_rect = marker->draw(
|
||||
QRect marker_rect = MarkerPainting::draw(
|
||||
p, QPoint(marker_left, marker_bottom), max_marker_right,
|
||||
get_scale(), selection_manager_.is_selected(marker));
|
||||
get_scale(), selection_manager_.is_selected(marker),
|
||||
marker->name(), marker->color(),
|
||||
marker->time().in(), marker->time().out());
|
||||
marker_top_ = marker_rect.top();
|
||||
selection_manager_.declare_drawn_object(marker, marker_rect);
|
||||
}
|
||||
@@ -390,7 +457,7 @@ void SeekableWidget::draw_work_area(QPainter *p)
|
||||
int workarea_left = qMax(qreal(lim_left), time_to_scene(workarea_->in()));
|
||||
int workarea_right;
|
||||
|
||||
if (workarea_->out() == TimelineWorkArea::k_reset_out) {
|
||||
if (workarea_->out() == RATIONAL_MAX) {
|
||||
workarea_right = lim_right;
|
||||
} else {
|
||||
workarea_right =
|
||||
@@ -413,15 +480,14 @@ void SeekableWidget::deselect_all_markers()
|
||||
|
||||
void SeekableWidget::set_marker_color(int c)
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
foreach (TimelineMarker *marker, selection_manager_.get_selected_objects()) {
|
||||
command->add_child(new MarkerChangeColorCommand(marker, c));
|
||||
QVector<OakEngineMarker *> oak_markers;
|
||||
foreach (TimelineMarker *marker,
|
||||
selection_manager_.get_selected_objects()) {
|
||||
oak_markers.append(reinterpret_cast<OakEngineMarker *>(marker));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(
|
||||
command, tr("Changed Color of %1 Marker(s)")
|
||||
.arg(selection_manager_.get_selected_objects().size()));
|
||||
oakengine_marker_set_properties(
|
||||
oak_markers.data(), oak_markers.size(), c, nullptr, 0, 0, 0, 0, 0,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void SeekableWidget::show_marker_properties()
|
||||
@@ -459,7 +525,9 @@ void SeekableWidget::seek_to_scene_point(qreal scene)
|
||||
|
||||
ViewerOutput *viewer = get_viewer_node();
|
||||
if (viewer && playhead_time != viewer->get_playhead()) {
|
||||
viewer->set_playhead(playhead_time);
|
||||
oakengine_viewer_set_playhead(
|
||||
reinterpret_cast<OakEngineNode *>(viewer),
|
||||
playhead_time.numerator(), playhead_time.denominator());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -650,7 +718,8 @@ void SeekableWidget::drag_resize_handle(const QPointF &scene)
|
||||
// but I'm not sure if there's a good way to re-use that code
|
||||
if (TimelineMarker *marker =
|
||||
dynamic_cast<TimelineMarker *>(resize_item_)) {
|
||||
if (marker->has_sibling_at_time(proposed_time)) {
|
||||
if (markers_ &&
|
||||
markers_->get_marker_at_time(proposed_time) != marker) {
|
||||
proposed_time = presnap_time;
|
||||
|
||||
if (get_snap_service()) {
|
||||
@@ -658,7 +727,9 @@ void SeekableWidget::drag_resize_handle(const QPointF &scene)
|
||||
}
|
||||
}
|
||||
|
||||
while (marker->has_sibling_at_time(proposed_time)) {
|
||||
while (markers_ &&
|
||||
markers_->get_marker_at_time(proposed_time) != marker &&
|
||||
markers_->get_marker_at_time(proposed_time)) {
|
||||
proposed_time += Rational(1, 1000);
|
||||
}
|
||||
}
|
||||
@@ -669,31 +740,37 @@ void SeekableWidget::drag_resize_handle(const QPointF &scene)
|
||||
}
|
||||
|
||||
if (TimelineMarker *marker = dynamic_cast<TimelineMarker *>(resize_item_)) {
|
||||
marker->set_time(new_range);
|
||||
oakengine_marker_set_time_live(
|
||||
reinterpret_cast<OakEngineMarker *>(marker),
|
||||
new_range.in().numerator(), new_range.in().denominator(),
|
||||
new_range.out().numerator(), new_range.out().denominator());
|
||||
} else if (TimelineWorkArea *workarea =
|
||||
dynamic_cast<TimelineWorkArea *>(resize_item_)) {
|
||||
workarea->set_range(new_range);
|
||||
oakengine_workarea_set_range(
|
||||
reinterpret_cast<OakEngineWorkarea *>(workarea),
|
||||
new_range.in().numerator(), new_range.in().denominator(),
|
||||
new_range.out().numerator(), new_range.out().denominator());
|
||||
}
|
||||
}
|
||||
|
||||
void SeekableWidget::commit_resize_handle()
|
||||
{
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
QString command_name;
|
||||
|
||||
if (TimelineMarker *marker = dynamic_cast<TimelineMarker *>(resize_item_)) {
|
||||
command->add_child(new MarkerChangeTimeCommand(marker, marker->time(),
|
||||
resize_item_range_));
|
||||
command_name = tr("Changed Marker Length");
|
||||
oakengine_marker_set_properties(
|
||||
reinterpret_cast<OakEngineMarker **>(&marker), 1, -1, nullptr, 1,
|
||||
resize_item_range_.in().numerator(),
|
||||
resize_item_range_.in().denominator(),
|
||||
resize_item_range_.out().numerator(),
|
||||
resize_item_range_.out().denominator(),
|
||||
nullptr);
|
||||
} else if (TimelineWorkArea *workarea =
|
||||
dynamic_cast<TimelineWorkArea *>(resize_item_)) {
|
||||
command->add_child(new WorkareaSetRangeCommand(
|
||||
workarea, workarea->range(), resize_item_range_));
|
||||
command_name = tr("Changed Workarea Length");
|
||||
void *wa_cmd = oakengine_undo_command_create(
|
||||
tr("Changed Workarea Length").toUtf8().constData(),
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
oakengine_undo_push(wa_cmd,
|
||||
tr("Changed Workarea Length").toUtf8().constData());
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command, command_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/timebased/timebasedviewselectionmanager.h"
|
||||
#include "engineeventbridge.h"
|
||||
|
||||
namespace olive
|
||||
{
|
||||
@@ -169,6 +170,14 @@ private:
|
||||
|
||||
bool marker_editing_enabled_;
|
||||
|
||||
QVector<int64_t> marker_list_subs_;
|
||||
bool marker_connects_done_ = false;
|
||||
|
||||
EngineEventBridge *bridge_ = nullptr;
|
||||
|
||||
int64_t workarea_range_sub_ = 0;
|
||||
int64_t workarea_enabled_sub_ = 0;
|
||||
|
||||
QPolygon last_playhead_shape_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
#include <QPainter>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "config/config.h"
|
||||
#include "common/configwrapper.h"
|
||||
#include "core.h"
|
||||
#include "oakengine/viewer.h"
|
||||
#include "markerpainting.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
|
||||
@@ -74,6 +76,12 @@ TimeRuler::TimeRuler(bool text_visible, bool cache_status_visible,
|
||||
// the bottom of the widget. However, for now this makes sense since we just ported this
|
||||
// from a QWidget's paintEvent.
|
||||
setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
|
||||
bridge_ = new EngineEventBridge(this);
|
||||
connect(bridge_, &EngineEventBridge::playback_cache_invalidated,
|
||||
viewport(), [this]() { viewport()->update(); });
|
||||
connect(bridge_, &EngineEventBridge::playback_cache_validated,
|
||||
viewport(), [this]() { viewport()->update(); });
|
||||
}
|
||||
|
||||
void TimeRuler::set_centered_text(bool c)
|
||||
@@ -90,19 +98,21 @@ void TimeRuler::set_playback_cache(PlaybackCache *cache)
|
||||
}
|
||||
|
||||
if (playback_cache_) {
|
||||
disconnect(playback_cache_, &PlaybackCache::invalidated, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
disconnect(playback_cache_, &PlaybackCache::validated, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
bridge_->unsubscribe(cache_sub_invalidated_);
|
||||
bridge_->unsubscribe(cache_sub_validated_);
|
||||
cache_sub_invalidated_ = 0;
|
||||
cache_sub_validated_ = 0;
|
||||
}
|
||||
|
||||
playback_cache_ = cache;
|
||||
|
||||
if (playback_cache_) {
|
||||
connect(playback_cache_, &PlaybackCache::invalidated, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
connect(playback_cache_, &PlaybackCache::validated, viewport(),
|
||||
static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||
cache_sub_invalidated_ = bridge_->subscribe(
|
||||
reinterpret_cast<void *>(playback_cache_),
|
||||
OAKENGINE_EVENT_PLAYBACK_CACHE_INVALIDATED);
|
||||
cache_sub_validated_ = bridge_->subscribe(
|
||||
reinterpret_cast<void *>(playback_cache_),
|
||||
OAKENGINE_EVENT_PLAYBACK_CACHE_VALIDATED);
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -116,7 +126,7 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect)
|
||||
}
|
||||
|
||||
// Draw timeline points if connected
|
||||
int marker_height = TimelineMarker::get_marker_height(p->fontMetrics());
|
||||
int marker_height = MarkerPainting::height(p->fontMetrics());
|
||||
draw_work_area(p);
|
||||
draw_markers(p, marker_height);
|
||||
|
||||
@@ -195,7 +205,7 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect)
|
||||
int line_bottom = height();
|
||||
|
||||
if (show_cache_status_) {
|
||||
line_bottom -= PlaybackCache::get_cache_indicator_height();
|
||||
line_bottom -= oakengine_playback_cache_indicator_height();
|
||||
}
|
||||
|
||||
int long_height = fm.height();
|
||||
@@ -277,7 +287,7 @@ void TimeRuler::drawForeground(QPainter *p, const QRectF &rect)
|
||||
if (show_cache_status_ && playback_cache_ &&
|
||||
playback_cache_->has_validated_ranges()) {
|
||||
// FIXME: Hardcoded to get video length, if we ever need audio length, this will have to change
|
||||
int h = PlaybackCache::get_cache_indicator_height();
|
||||
int h = oakengine_playback_cache_indicator_height();
|
||||
QRect cache_rect(0, height() - h, width(), h);
|
||||
|
||||
if (ViewerOutput *viewer =
|
||||
@@ -340,11 +350,11 @@ void TimeRuler::update_height()
|
||||
|
||||
// Add cache status height
|
||||
if (show_cache_status_) {
|
||||
height += PlaybackCache::get_cache_indicator_height();
|
||||
height += oakengine_playback_cache_indicator_height();
|
||||
}
|
||||
|
||||
// Add marker height
|
||||
height += TimelineMarker::get_marker_height(fontMetrics());
|
||||
height += MarkerPainting::height(fontMetrics());
|
||||
|
||||
setFixedHeight(height);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "engineeventbridge.h"
|
||||
#include "seekablewidget.h"
|
||||
#include "render/playbackcache.h"
|
||||
|
||||
@@ -65,6 +66,10 @@ private:
|
||||
bool show_cache_status_;
|
||||
|
||||
PlaybackCache *playback_cache_;
|
||||
|
||||
EngineEventBridge *bridge_;
|
||||
int64_t cache_sub_invalidated_ = 0;
|
||||
int64_t cache_sub_validated_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user