nodeparamview: significant optimization
- Skips multiple calls to SetContexts in quick succession - Only add/remove contexts as necessary rather than resetting every time
This commit is contained in:
@@ -61,9 +61,9 @@ void ParamPanel::DeselectAll()
|
||||
static_cast<NodeParamView*>(GetTimeBasedWidget())->DeselectAll();
|
||||
}
|
||||
|
||||
void ParamPanel::SetContexts(const QVector<Node *> &contexts, bool group_mode)
|
||||
void ParamPanel::SetContexts(const QVector<Node *> &contexts)
|
||||
{
|
||||
static_cast<NodeParamView*>(GetTimeBasedWidget())->SetContexts(contexts, group_mode);
|
||||
static_cast<NodeParamView*>(GetTimeBasedWidget())->SetContexts(contexts);
|
||||
}
|
||||
|
||||
void ParamPanel::Retranslate()
|
||||
|
||||
@@ -58,7 +58,7 @@ public slots:
|
||||
|
||||
virtual void DeselectAll() override;
|
||||
|
||||
void SetContexts(const QVector<Node*> &contexts, bool group_mode);
|
||||
void SetContexts(const QVector<Node*> &contexts);
|
||||
|
||||
signals:
|
||||
void RequestSelectNode(const QVector<Node*>& target);
|
||||
|
||||
@@ -37,8 +37,7 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) :
|
||||
super(true, false, parent),
|
||||
last_scroll_val_(0),
|
||||
focused_node_(nullptr),
|
||||
time_target_(nullptr),
|
||||
group_mode_(false)
|
||||
time_target_(nullptr)
|
||||
{
|
||||
// Create horizontal layout to place scroll area in (and keyframe editing eventually)
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
@@ -155,6 +154,11 @@ NodeParamView::NodeParamView(bool create_keyframe_view, QWidget *parent) :
|
||||
&QApplication::focusChanged,
|
||||
this,
|
||||
&NodeParamView::FocusChanged);
|
||||
|
||||
ctx_update_timer_ = new QTimer(this);
|
||||
ctx_update_timer_->setInterval(1);
|
||||
ctx_update_timer_->setSingleShot(true);
|
||||
connect(ctx_update_timer_, &QTimer::timeout, this, &NodeParamView::UpdateContexts);
|
||||
}
|
||||
|
||||
NodeParamView::~NodeParamView()
|
||||
@@ -173,7 +177,7 @@ void NodeParamView::CloseContextsBelongingToProject(Project *p)
|
||||
}
|
||||
}
|
||||
|
||||
SetContexts(new_contexts, new_contexts.isEmpty() ? false : group_mode_);
|
||||
SetContexts(new_contexts);
|
||||
}
|
||||
|
||||
/*void NodeParamView::SelectNodes(const QVector<Node *> &nodes)
|
||||
@@ -235,70 +239,58 @@ void NodeParamView::DeselectNodes(const QVector<Node *> &nodes)
|
||||
}
|
||||
}*/
|
||||
|
||||
void NodeParamView::SetContexts(const QVector<Node *> &contexts, bool group_mode)
|
||||
void NodeParamView::UpdateContexts()
|
||||
{
|
||||
//TIME_THIS_FUNCTION;
|
||||
|
||||
foreach (NodeParamViewContext *ctx, context_items_) {
|
||||
ctx->Clear();
|
||||
ctx->setVisible(false);
|
||||
bool changes_made = false;
|
||||
|
||||
foreach (Node *ctx, current_contexts_) {
|
||||
if (!contexts_.contains(ctx)) {
|
||||
// Context is being removed
|
||||
RemoveContext(ctx);
|
||||
changes_made = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Node *ctx, contexts_) {
|
||||
disconnect(ctx, &Node::NodeAddedToContext, this, &NodeParamView::NodeAddedToContext);
|
||||
disconnect(ctx, &Node::NodeRemovedFromContext, this, &NodeParamView::NodeRemovedFromContext);
|
||||
if (!current_contexts_.contains(ctx)) {
|
||||
// Context is being added
|
||||
AddContext(ctx);
|
||||
changes_made = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changes_made) {
|
||||
current_contexts_ = contexts_;
|
||||
|
||||
if (IsGroupMode()) {
|
||||
// Check inputs that have been passed through
|
||||
NodeGroup *group = static_cast<NodeGroup*>(contexts_.first());
|
||||
for (auto it=group->GetInputPassthroughs().cbegin(); it!=group->GetInputPassthroughs().cend(); it++) {
|
||||
GroupInputPassthroughAdded(group, it->second);
|
||||
}
|
||||
|
||||
connect(group, &NodeGroup::InputPassthroughAdded, this, &NodeParamView::GroupInputPassthroughAdded);
|
||||
connect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeParamView::GroupInputPassthroughRemoved);
|
||||
}
|
||||
|
||||
foreach (NodeParamViewContext *ctx, context_items_) {
|
||||
SortItemsInContext(ctx);
|
||||
}
|
||||
|
||||
if (keyframe_view_) {
|
||||
QueueKeyframePositionUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::SetContexts(const QVector<Node *> &contexts)
|
||||
{
|
||||
// Setting contexts is expensive, so we queue it here to prevent multiple calls in a short timespan
|
||||
contexts_ = contexts;
|
||||
group_mode_ = group_mode;
|
||||
|
||||
Q_ASSERT((contexts_.size() == 1 && dynamic_cast<NodeGroup*>(contexts_.first())) || !group_mode_ || contexts_.isEmpty());
|
||||
|
||||
foreach (Node *ctx, contexts_) {
|
||||
// Queued so that if any further work is done in connecting this node to the context, it'll be
|
||||
// done before our sorting function is called
|
||||
connect(ctx, &Node::NodeAddedToContext, this, &NodeParamView::NodeAddedToContext, Qt::QueuedConnection);
|
||||
connect(ctx, &Node::NodeRemovedFromContext, this, &NodeParamView::NodeRemovedFromContext, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
if (keyframe_view_) {
|
||||
keyframe_view_->Clear();
|
||||
}
|
||||
|
||||
if (focused_node_) {
|
||||
focused_node_ = nullptr;
|
||||
emit FocusedNodeChanged(nullptr);
|
||||
}
|
||||
|
||||
foreach (Node *ctx, contexts) {
|
||||
NodeParamViewContext *item = GetContextItemFromContext(ctx);
|
||||
|
||||
item->AddContext(ctx);
|
||||
item->setVisible(true);
|
||||
|
||||
for (auto it=ctx->GetContextPositions().cbegin(); it!=ctx->GetContextPositions().cend(); it++) {
|
||||
AddNode(it.key(), ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
if (group_mode_) {
|
||||
// Check inputs that have been passed through
|
||||
NodeGroup *group = static_cast<NodeGroup*>(contexts_.first());
|
||||
for (auto it=group->GetInputPassthroughs().cbegin(); it!=group->GetInputPassthroughs().cend(); it++) {
|
||||
GroupInputPassthroughAdded(group, it->second);
|
||||
}
|
||||
|
||||
connect(group, &NodeGroup::InputPassthroughAdded, this, &NodeParamView::GroupInputPassthroughAdded);
|
||||
connect(group, &NodeGroup::InputPassthroughRemoved, this, &NodeParamView::GroupInputPassthroughRemoved);
|
||||
}
|
||||
|
||||
foreach (NodeParamViewContext *ctx, context_items_) {
|
||||
SortItemsInContext(ctx);
|
||||
}
|
||||
|
||||
if (keyframe_view_) {
|
||||
QueueKeyframePositionUpdate();
|
||||
}
|
||||
ctx_update_timer_->stop();
|
||||
ctx_update_timer_->start();
|
||||
}
|
||||
|
||||
void NodeParamView::resizeEvent(QResizeEvent *event)
|
||||
@@ -391,13 +383,45 @@ void NodeParamView::QueueKeyframePositionUpdate()
|
||||
QMetaObject::invokeMethod(this, &NodeParamView::UpdateElementY, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void NodeParamView::AddContext(Node *ctx)
|
||||
{
|
||||
// Queued so that if any further work is done in connecting this node to the context, it'll be
|
||||
// done before our sorting function is called
|
||||
connect(ctx, &Node::NodeAddedToContext, this, &NodeParamView::NodeAddedToContext, Qt::QueuedConnection);
|
||||
connect(ctx, &Node::NodeRemovedFromContext, this, &NodeParamView::NodeRemovedFromContext, Qt::QueuedConnection);
|
||||
|
||||
NodeParamViewContext *item = GetContextItemFromContext(ctx);
|
||||
|
||||
item->AddContext(ctx);
|
||||
item->setVisible(true);
|
||||
|
||||
for (auto it=ctx->GetContextPositions().cbegin(); it!=ctx->GetContextPositions().cend(); it++) {
|
||||
AddNode(it.key(), ctx, item);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::RemoveContext(Node *ctx)
|
||||
{
|
||||
disconnect(ctx, &Node::NodeAddedToContext, this, &NodeParamView::NodeAddedToContext);
|
||||
disconnect(ctx, &Node::NodeRemovedFromContext, this, &NodeParamView::NodeRemovedFromContext);
|
||||
|
||||
NodeParamViewContext *item = GetContextItemFromContext(ctx);
|
||||
|
||||
item->RemoveContext(ctx);
|
||||
item->RemoveNodesWithContext(ctx);
|
||||
|
||||
if (item->GetContexts().isEmpty()) {
|
||||
item->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::AddNode(Node *n, Node *ctx, NodeParamViewContext *context)
|
||||
{
|
||||
if ((n->GetFlags() & Node::kDontShowInParamView) && !group_mode_) {
|
||||
if ((n->GetFlags() & Node::kDontShowInParamView) && !IsGroupMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeParamViewItem* item = new NodeParamViewItem(n, group_mode_ ? kCheckBoxesOnNonConnected : kNoCheckBoxes, context);
|
||||
NodeParamViewItem* item = new NodeParamViewItem(n, IsGroupMode() ? kCheckBoxesOnNonConnected : kNoCheckBoxes, context);
|
||||
|
||||
connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::SetTimeAndSignal);
|
||||
connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode);
|
||||
@@ -428,6 +452,29 @@ void NodeParamView::AddNode(Node *n, Node *ctx, NodeParamViewContext *context)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::RemoveNode(Node *n, Node *ctx)
|
||||
{
|
||||
NodeParamViewContext *ctx_item = GetContextItemFromContext(ctx);
|
||||
NodeParamViewItem *item = ctx_item->GetItem(n, ctx);
|
||||
|
||||
if (focused_node_ == item) {
|
||||
focused_node_ = nullptr;
|
||||
emit FocusedNodeChanged(nullptr);
|
||||
}
|
||||
|
||||
if (keyframe_view_) {
|
||||
for (auto it=item->GetKeyframeConnections().begin(); it!=item->GetKeyframeConnections().end(); it++) {
|
||||
for (auto jt=it->begin(); jt!=it->end(); jt++) {
|
||||
for (auto kt=jt->begin(); kt!=jt->end(); kt++) {
|
||||
keyframe_view_->RemoveKeyframesOfTrack(*kt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx_item->RemoveNode(n, ctx);
|
||||
}
|
||||
|
||||
int GetDistanceBetweenNodes(Node *start, Node *end)
|
||||
{
|
||||
if (start == end) {
|
||||
@@ -449,9 +496,11 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item)
|
||||
QVector<QPair<NodeParamViewItem*, int> > distances;
|
||||
|
||||
for (auto it=context_item->GetItems().cbegin(); it!=context_item->GetItems().cend(); it++) {
|
||||
NodeParamViewItem *item = *it;
|
||||
|
||||
int distance = -1;
|
||||
foreach (Node *ctx, context_item->GetContexts()) {
|
||||
distance = qMax(distance, GetDistanceBetweenNodes(ctx, it.key()));
|
||||
distance = qMax(distance, GetDistanceBetweenNodes(ctx, item->GetNode()));
|
||||
}
|
||||
|
||||
if (distance == -1) {
|
||||
@@ -459,7 +508,7 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item)
|
||||
}
|
||||
|
||||
bool inserted = false;
|
||||
QPair<NodeParamViewItem*, int> dist(it.value(), distance);
|
||||
QPair<NodeParamViewItem*, int> dist(item, distance);
|
||||
|
||||
for (int i=0; i<distances.size(); i++) {
|
||||
if (distances.at(i).second < distance) {
|
||||
@@ -567,25 +616,26 @@ void NodeParamView::KeyframeViewDragged(int x, int y)
|
||||
{
|
||||
Q_UNUSED(y)
|
||||
|
||||
QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection,
|
||||
Q_ARG(int, x));
|
||||
QMetaObject::invokeMethod(this, "CatchUpScrollToPoint", Qt::QueuedConnection, Q_ARG(int, x));
|
||||
}
|
||||
|
||||
void NodeParamView::UpdateElementY()
|
||||
{
|
||||
foreach (NodeParamViewContext *ctx, context_items_) {
|
||||
for (auto it=ctx->GetItems().cbegin(); it!=ctx->GetItems().cend(); it++) {
|
||||
const KeyframeView::NodeConnections &connections = it.value()->GetKeyframeConnections();
|
||||
NodeParamViewItem *item = *it;
|
||||
Node *node = item->GetNode();
|
||||
const KeyframeView::NodeConnections &connections = item->GetKeyframeConnections();
|
||||
|
||||
if (!connections.isEmpty()) {
|
||||
foreach (const QString& input, it.key()->inputs()) {
|
||||
if (!(it.key()->GetInputFlags(input) & kInputFlagHidden)) {
|
||||
int arr_sz = NodeGroup::ResolveInput(NodeInput(it.key(), input)).GetArraySize();
|
||||
foreach (const QString& input, node->inputs()) {
|
||||
if (!(node->GetInputFlags(input) & kInputFlagHidden)) {
|
||||
int arr_sz = NodeGroup::ResolveInput(NodeInput(node, input)).GetArraySize();
|
||||
|
||||
for (int i=-1; i<arr_sz; i++) {
|
||||
NodeInput ic = {it.key(), input, i};
|
||||
NodeInput ic = {node, input, i};
|
||||
|
||||
int y = it.value()->GetElementY(ic);
|
||||
int y = item->GetElementY(ic);
|
||||
|
||||
// For some reason Qt's mapToGlobal doesn't seem to handle this, so we offset here
|
||||
y += vertical_scrollbar_->value();
|
||||
@@ -614,14 +664,21 @@ void NodeParamView::NodeAddedToContext(Node *n)
|
||||
AddNode(n, ctx, item);
|
||||
|
||||
SortItemsInContext(item);
|
||||
|
||||
if (keyframe_view_) {
|
||||
QueueKeyframePositionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::NodeRemovedFromContext(Node *n)
|
||||
{
|
||||
Node *ctx = static_cast<Node*>(sender());
|
||||
NodeParamViewContext *item = GetContextItemFromContext(ctx);
|
||||
|
||||
item->RemoveNode(n, ctx);
|
||||
RemoveNode(n, ctx);
|
||||
|
||||
if (keyframe_view_) {
|
||||
QueueKeyframePositionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::InputCheckBoxChanged(const NodeInput &input, bool e)
|
||||
@@ -638,20 +695,14 @@ void NodeParamView::InputCheckBoxChanged(const NodeInput &input, bool e)
|
||||
void NodeParamView::GroupInputPassthroughAdded(NodeGroup *group, const NodeInput &input)
|
||||
{
|
||||
foreach (NodeParamViewContext *pvctx, context_items_) {
|
||||
NodeParamViewItem *item = pvctx->GetItems().value(input.node());
|
||||
if (item) {
|
||||
item->SetInputChecked(input, true);
|
||||
}
|
||||
pvctx->SetInputChecked(input, true);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamView::GroupInputPassthroughRemoved(NodeGroup *group, const NodeInput &input)
|
||||
{
|
||||
foreach (NodeParamViewContext *pvctx, context_items_) {
|
||||
NodeParamViewItem *item = pvctx->GetItems().value(input.node());
|
||||
if (item) {
|
||||
item->SetInputChecked(input, false);
|
||||
}
|
||||
pvctx->SetInputChecked(input, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
}
|
||||
|
||||
public slots:
|
||||
void SetContexts(const QVector<Node*> &contexts, bool group_mode);
|
||||
void SetContexts(const QVector<Node*> &contexts);
|
||||
|
||||
void UpdateElementY();
|
||||
|
||||
@@ -93,12 +93,23 @@ private:
|
||||
|
||||
void QueueKeyframePositionUpdate();
|
||||
|
||||
void AddContext(Node *context);
|
||||
|
||||
void RemoveContext(Node *context);
|
||||
|
||||
void AddNode(Node* n, Node *ctx, NodeParamViewContext *context);
|
||||
|
||||
void RemoveNode(Node *n, Node *ctx);
|
||||
|
||||
void SortItemsInContext(NodeParamViewContext *context);
|
||||
|
||||
NodeParamViewContext *GetContextItemFromContext(Node *context);
|
||||
|
||||
bool IsGroupMode() const
|
||||
{
|
||||
return contexts_.size() == 1 && dynamic_cast<NodeGroup*>(contexts_.first());
|
||||
}
|
||||
|
||||
KeyframeView* keyframe_view_;
|
||||
|
||||
QVector<NodeParamViewContext*> context_items_;
|
||||
@@ -122,8 +133,9 @@ private:
|
||||
Node *time_target_;
|
||||
|
||||
QVector<Node*> contexts_;
|
||||
QVector<Node*> current_contexts_;
|
||||
|
||||
bool group_mode_;
|
||||
QTimer *ctx_update_timer_;
|
||||
|
||||
private slots:
|
||||
void UpdateGlobalScrollBar();
|
||||
@@ -144,6 +156,8 @@ private slots:
|
||||
|
||||
void GroupInputPassthroughRemoved(olive::NodeGroup *group, const olive::NodeInput &input);
|
||||
|
||||
void UpdateContexts();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -45,17 +45,46 @@ NodeParamViewContext::NodeParamViewContext(QWidget *parent) :
|
||||
connect(title_bar(), &NodeParamViewItemTitleBar::AddEffectButtonClicked, this, &NodeParamViewContext::AddEffectButtonClicked);
|
||||
}
|
||||
|
||||
NodeParamViewItem *NodeParamViewContext::GetItem(Node *node, Node *ctx)
|
||||
{
|
||||
for (auto it=items_.begin(); it!=items_.end(); ) {
|
||||
NodeParamViewItem *item = *it;
|
||||
|
||||
if (item->GetNode() == node && item->GetContext() == ctx) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeParamViewContext::AddNode(NodeParamViewItem *item)
|
||||
{
|
||||
items_.insert(item->GetNode(), item);
|
||||
items_.append(item);
|
||||
dock_area_->AddItem(item);
|
||||
}
|
||||
|
||||
void NodeParamViewContext::RemoveNode(Node *node, Node *ctx)
|
||||
{
|
||||
for (auto it=items_.begin(); it!=items_.end(); ) {
|
||||
if (it.value()->GetContext() == ctx) {
|
||||
delete it.value();
|
||||
NodeParamViewItem *item = *it;
|
||||
|
||||
if (item->GetNode() == node && item->GetContext() == ctx) {
|
||||
delete item;
|
||||
it = items_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewContext::RemoveNodesWithContext(Node *ctx)
|
||||
{
|
||||
for (auto it=items_.begin(); it!=items_.end(); ) {
|
||||
NodeParamViewItem *item = *it;
|
||||
|
||||
if (item->GetContext() == ctx) {
|
||||
delete item;
|
||||
it = items_.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
@@ -73,8 +102,10 @@ void NodeParamViewContext::Clear()
|
||||
|
||||
void NodeParamViewContext::SetInputChecked(const NodeInput &input, bool e)
|
||||
{
|
||||
if (NodeParamViewItem *item = items_.value(input.node())) {
|
||||
item->SetInputChecked(input, e);
|
||||
foreach (NodeParamViewItem *item, items_) {
|
||||
if (item->GetNode() == input.node()) {
|
||||
item->SetInputChecked(input, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,15 +43,19 @@ public:
|
||||
return contexts_;
|
||||
}
|
||||
|
||||
const QMap<Node*, NodeParamViewItem*> &GetItems() const
|
||||
const QVector<NodeParamViewItem*> &GetItems() const
|
||||
{
|
||||
return items_;
|
||||
}
|
||||
|
||||
NodeParamViewItem *GetItem(Node *node, Node *ctx);
|
||||
|
||||
void AddNode(NodeParamViewItem *item);
|
||||
|
||||
void RemoveNode(Node *node, Node *ctx);
|
||||
|
||||
void RemoveNodesWithContext(Node *ctx);
|
||||
|
||||
void Clear();
|
||||
|
||||
void SetInputChecked(const NodeInput &input, bool e);
|
||||
@@ -81,7 +85,7 @@ private:
|
||||
|
||||
QVector<Node*> contexts_;
|
||||
|
||||
QMap<Node*, NodeParamViewItem*> items_;
|
||||
QVector<NodeParamViewItem*> items_;
|
||||
|
||||
private slots:
|
||||
void AddEffectButtonClicked();
|
||||
|
||||
@@ -459,7 +459,7 @@ void MainWindow::StatusBarDoubleClicked()
|
||||
void MainWindow::NodePanelGroupOpenedOrClosed()
|
||||
{
|
||||
NodePanel *p = static_cast<NodePanel*>(sender());
|
||||
param_panel_->SetContexts(p->GetContexts(), p->IsGroupOverlay());
|
||||
param_panel_->SetContexts(p->GetContexts());
|
||||
}
|
||||
|
||||
void MainWindow::TimelinePanelSelectionChanged(const QVector<Block *> &blocks)
|
||||
@@ -728,7 +728,7 @@ void MainWindow::UpdateNodePanelContextFromTimelinePanel(TimelinePanel *panel)
|
||||
}
|
||||
|
||||
node_panel_->SetContexts(context);
|
||||
param_panel_->SetContexts(context, false);
|
||||
param_panel_->SetContexts(context);
|
||||
}
|
||||
|
||||
void MainWindow::FocusedPanelChanged(PanelWidget *panel)
|
||||
@@ -743,7 +743,7 @@ void MainWindow::FocusedPanelChanged(PanelWidget *panel)
|
||||
const QVector<Node*> &new_ctxs = node_panel->GetContexts();
|
||||
|
||||
if (new_ctxs != param_panel_->GetContexts()) {
|
||||
param_panel_->SetContexts(new_ctxs, node_panel->IsGroupOverlay());
|
||||
param_panel_->SetContexts(new_ctxs);
|
||||
}
|
||||
} else if (TimelinePanel* timeline = dynamic_cast<TimelinePanel*>(panel)) {
|
||||
// Signal timeline focus
|
||||
|
||||
Reference in New Issue
Block a user