update
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/domain/Project.h"
|
||||
|
||||
#include <QColor>
|
||||
#include <QPoint>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
// TimelineEditorModel:把时间轴的“可视数据 + 选择状态 + 命中测试结果”从 Widget 拆出来,
|
||||
// 便于支持多轨、统一交互逻辑,并降低 TimelineWidget 的复杂度。
|
||||
//
|
||||
// 阶段1最小实现:仅负责存储轨道视图数据与选择状态;Widget 仍自行处理鼠标事件,
|
||||
// 但数据不再用固定 4 条 QVector<int> 传递。
|
||||
|
||||
class TimelineEditorModel final {
|
||||
public:
|
||||
enum class TrackKind { Location, UserScale, Image, Visibility };
|
||||
|
||||
struct KeyRef {
|
||||
TrackKind kind {TrackKind::Location};
|
||||
int frame {-1};
|
||||
};
|
||||
|
||||
struct TrackView {
|
||||
TrackKind kind {TrackKind::Location};
|
||||
QString label;
|
||||
QColor color;
|
||||
QVector<int> keyframes; // sorted unique frames
|
||||
};
|
||||
|
||||
void clear() {
|
||||
m_tracks.clear();
|
||||
clearSelection();
|
||||
clearInterval();
|
||||
}
|
||||
|
||||
void setTracks(QVector<TrackView> tracks) { m_tracks = std::move(tracks); }
|
||||
const QVector<TrackView>& tracks() const { return m_tracks; }
|
||||
|
||||
void clearSelection() { m_hasSelectedKey = false; m_selected = {}; }
|
||||
void setSelectedKey(const KeyRef& ref) { m_hasSelectedKey = (ref.frame >= 0); m_selected = ref; }
|
||||
bool hasSelectedKey() const { return m_hasSelectedKey; }
|
||||
const KeyRef& selectedKey() const { return m_selected; }
|
||||
|
||||
void clearInterval() { m_selStart = -1; m_selEnd = -1; }
|
||||
void setInterval(int a, int b) {
|
||||
if (a < 0 || b < 0) { clearInterval(); return; }
|
||||
m_selStart = std::min(a, b);
|
||||
m_selEnd = std::max(a, b);
|
||||
}
|
||||
int intervalStart() const { return m_selStart; }
|
||||
int intervalEnd() const { return m_selEnd; }
|
||||
|
||||
private:
|
||||
QVector<TrackView> m_tracks;
|
||||
bool m_hasSelectedKey {false};
|
||||
KeyRef m_selected;
|
||||
int m_selStart {-1};
|
||||
int m_selEnd {-1};
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "timeline/TimelineWidget.h"
|
||||
|
||||
#include "timeline/TimelineEditorModel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
@@ -38,7 +40,11 @@ TimelineWidget::TimelineWidget(QWidget* parent)
|
||||
// 单行紧凑:标尺 + 轨道(帧号画在播放头处,随坐标轴滚动)
|
||||
setMinimumHeight(kRulerHeight + 18 + 6);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setToolTip(QStringLiteral("片段时间轴(固定 0-600):左键拖动播放头;滚轮:逐帧"));
|
||||
setToolTip({});
|
||||
}
|
||||
|
||||
void TimelineWidget::setModel(TimelineEditorModel* model) {
|
||||
m_model = model;
|
||||
}
|
||||
|
||||
void TimelineWidget::resizeEvent(QResizeEvent* e) {
|
||||
@@ -47,8 +53,10 @@ void TimelineWidget::resizeEvent(QResizeEvent* e) {
|
||||
}
|
||||
|
||||
void TimelineWidget::setFrameRange(int start, int end) {
|
||||
(void)start;
|
||||
(void)end;
|
||||
m_start = std::max(0, start);
|
||||
m_end = std::max(m_start + 1, end);
|
||||
m_currentFrame = std::clamp(m_currentFrame, m_start, m_end - 1);
|
||||
setSelectionRange(m_selStart, m_selEnd);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -57,7 +65,7 @@ void TimelineWidget::setCurrentFrame(int frame) {
|
||||
}
|
||||
|
||||
void TimelineWidget::setCurrentFrameProgrammatic(int frame) {
|
||||
const int f = std::clamp(frame, kStart, kEnd - 1);
|
||||
const int f = std::clamp(frame, m_start, m_end - 1);
|
||||
if (m_currentFrame == f) {
|
||||
return;
|
||||
}
|
||||
@@ -74,8 +82,8 @@ void TimelineWidget::setSelectionRange(int start, int end) {
|
||||
}
|
||||
const int lo = std::min(start, end);
|
||||
const int hi = std::max(start, end);
|
||||
m_selStart = std::clamp(lo, kStart, kEnd - 1);
|
||||
m_selEnd = std::clamp(hi, m_selStart, kEnd - 1);
|
||||
m_selStart = std::clamp(lo, m_start, m_end - 1);
|
||||
m_selEnd = std::clamp(hi, m_selStart, m_end - 1);
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -111,6 +119,29 @@ void TimelineWidget::setKeyframeTracks(const QVector<int>& locFrames,
|
||||
m_selKeyFrame = -1;
|
||||
emit keyframeSelectionChanged(m_selKeyKind, m_selKeyFrame);
|
||||
}
|
||||
if (m_model) {
|
||||
QVector<TimelineEditorModel::TrackView> tracks;
|
||||
tracks.reserve(4);
|
||||
tracks.push_back({TimelineEditorModel::TrackKind::Image, QStringLiteral("图像"), QColor(70, 130, 240), m_imgFrames});
|
||||
tracks.push_back({TimelineEditorModel::TrackKind::Location, QStringLiteral("位置"), QColor(240, 110, 40), m_locFrames});
|
||||
tracks.push_back({TimelineEditorModel::TrackKind::UserScale, QStringLiteral("缩放"), QColor(80, 190, 90), m_scaleFrames});
|
||||
tracks.push_back({TimelineEditorModel::TrackKind::Visibility, QStringLiteral("显隐"), QColor(160, 100, 230), m_visFrames});
|
||||
m_model->setTracks(std::move(tracks));
|
||||
if (m_selKeyKind == KeyKind::None) {
|
||||
m_model->clearSelection();
|
||||
} else {
|
||||
TimelineEditorModel::TrackKind kind = TimelineEditorModel::TrackKind::Location;
|
||||
switch (m_selKeyKind) {
|
||||
case KeyKind::Location: kind = TimelineEditorModel::TrackKind::Location; break;
|
||||
case KeyKind::UserScale: kind = TimelineEditorModel::TrackKind::UserScale; break;
|
||||
case KeyKind::Image: kind = TimelineEditorModel::TrackKind::Image; break;
|
||||
case KeyKind::Visibility: kind = TimelineEditorModel::TrackKind::Visibility; break;
|
||||
default: break;
|
||||
}
|
||||
m_model->setSelectedKey({kind, m_selKeyFrame});
|
||||
}
|
||||
m_model->setInterval(m_selStart, m_selEnd);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -131,6 +162,21 @@ void TimelineWidget::setToolKeyframeTracks(const QVector<int>& locFrames,
|
||||
m_selKeyFrame = -1;
|
||||
emit keyframeSelectionChanged(m_selKeyKind, m_selKeyFrame);
|
||||
}
|
||||
if (m_model) {
|
||||
QVector<TimelineEditorModel::TrackView> tracks;
|
||||
tracks.reserve(2);
|
||||
tracks.push_back({TimelineEditorModel::TrackKind::Location, QStringLiteral("位置"), QColor(240, 110, 40), m_locFrames});
|
||||
tracks.push_back({TimelineEditorModel::TrackKind::Visibility, QStringLiteral("显隐"), QColor(160, 100, 230), m_visFrames});
|
||||
m_model->setTracks(std::move(tracks));
|
||||
if (m_selKeyKind == KeyKind::None) {
|
||||
m_model->clearSelection();
|
||||
} else {
|
||||
TimelineEditorModel::TrackKind kind = TimelineEditorModel::TrackKind::Location;
|
||||
if (m_selKeyKind == KeyKind::Visibility) kind = TimelineEditorModel::TrackKind::Visibility;
|
||||
m_model->setSelectedKey({kind, m_selKeyFrame});
|
||||
}
|
||||
m_model->setInterval(m_selStart, m_selEnd);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -151,8 +197,8 @@ QRect TimelineWidget::keyAreaRect() const {
|
||||
}
|
||||
|
||||
double TimelineWidget::frameToXf(double frame) const {
|
||||
const double pxf = double(contentWidth()) / double(std::max(1, kEnd - kStart));
|
||||
return double(contentLeft()) + (frame - double(kStart)) * pxf;
|
||||
const double pxf = double(contentWidth()) / double(std::max(1, m_end - m_start));
|
||||
return double(contentLeft()) + (frame - double(m_start)) * pxf;
|
||||
}
|
||||
|
||||
int TimelineWidget::frameToX(int frame) const {
|
||||
@@ -160,8 +206,8 @@ int TimelineWidget::frameToX(int frame) const {
|
||||
}
|
||||
|
||||
double TimelineWidget::xToFramef(int x) const {
|
||||
const double pxf = double(contentWidth()) / double(std::max(1, kEnd - kStart));
|
||||
return double(kStart) + double(x - contentLeft()) / std::max(pxf, 1e-9);
|
||||
const double pxf = double(contentWidth()) / double(std::max(1, m_end - m_start));
|
||||
return double(m_start) + double(x - contentLeft()) / std::max(pxf, 1e-9);
|
||||
}
|
||||
|
||||
int TimelineWidget::xToFrame(int x) const {
|
||||
@@ -169,7 +215,7 @@ int TimelineWidget::xToFrame(int x) const {
|
||||
}
|
||||
|
||||
void TimelineWidget::setFrameInternal(int frame, bool commit) {
|
||||
const int f = std::clamp(frame, kStart, kEnd - 1);
|
||||
const int f = std::clamp(frame, m_start, m_end - 1);
|
||||
// 松手时若帧未变:只发 committed,禁止再发 scrubbed,否则主窗口会双次求值/刷新导致帧号与红线闪烁
|
||||
if (m_currentFrame == f) {
|
||||
if (commit) {
|
||||
@@ -194,9 +240,9 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
const QRect cr = contentRect();
|
||||
const QRect kr = keyAreaRect();
|
||||
const QRect rr = rulerRect();
|
||||
const double fLeft = double(kStart);
|
||||
const int visMin = kStart;
|
||||
const int visMax = kEnd;
|
||||
const double fLeft = double(m_start);
|
||||
const int visMin = m_start;
|
||||
const int visMax = m_end;
|
||||
|
||||
auto frameVisible = [&](int fr) { return fr >= visMin && fr <= visMax; };
|
||||
|
||||
@@ -292,7 +338,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
p.setBrush(palette().alternateBase());
|
||||
p.drawRoundedRect(rr, 3, 3);
|
||||
|
||||
const double pxf = double(contentWidth()) / double(std::max(1, kEnd - kStart));
|
||||
const double pxf = double(contentWidth()) / double(std::max(1, m_end - m_start));
|
||||
const int major = pickMajorStep(pxf);
|
||||
const int minor = pickMinorStep(major);
|
||||
QPen minorPen(QColor(60, 60, 60, 100));
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
#include <QWidget>
|
||||
|
||||
class QResizeEvent;
|
||||
class TimelineEditorModel;
|
||||
|
||||
class TimelineWidget final : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TimelineWidget(QWidget* parent = nullptr);
|
||||
|
||||
// 兼容旧接口:NLA/片段系统下时间轴始终固定为 0..600(local frame)。
|
||||
void setFrameRange(int start, int end);
|
||||
void setCurrentFrame(int frame);
|
||||
/// 由主窗口同步工程帧时调用:不发射 frameScrubbed,避免与拖动/刷新打架造成数字闪烁
|
||||
@@ -20,6 +20,9 @@ public:
|
||||
int selectionStart() const { return m_selStart; }
|
||||
int selectionEnd() const { return m_selEnd; }
|
||||
|
||||
void setModel(TimelineEditorModel* model);
|
||||
TimelineEditorModel* model() const { return m_model; }
|
||||
|
||||
// 轨道数据直接由上层提供(通常来自当前条带引用的 clip)。
|
||||
void setKeyframeTracks(const QVector<int>& locFrames,
|
||||
const QVector<int>& scaleFrames,
|
||||
@@ -63,9 +66,9 @@ private:
|
||||
|
||||
void setFrameInternal(int frame, bool commit);
|
||||
|
||||
static constexpr int kStart = 0;
|
||||
static constexpr int kEnd = 600; // exclusive for mapping, inclusive for UI labels
|
||||
int m_currentFrame = 0; // local frame: 0..599
|
||||
int m_start = 0;
|
||||
int m_end = 600; // exclusive for mapping
|
||||
int m_currentFrame = 0;
|
||||
|
||||
int m_selStart = -1;
|
||||
int m_selEnd = -1;
|
||||
@@ -83,4 +86,6 @@ private:
|
||||
|
||||
KeyKind m_selKeyKind = KeyKind::None;
|
||||
int m_selKeyFrame = -1;
|
||||
|
||||
TimelineEditorModel* m_model = nullptr; // non-owning
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user