Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions app/src/actioncommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -844,12 +844,17 @@ void ActionCommands::duplicateKey()
void ActionCommands::moveFrameForward()
{
Layer* layer = mEditor->layers()->currentLayer();
if (layer)
if (layer == nullptr) { return; }

// Prevent moving keyframe if the layer is not visible, as it can cause confusion to users when they don't see their keyframe move.
if (!layer->visible()) {
mEditor->getScribbleArea()->showLayerNotVisibleWarning();
return;
}

if (layer->moveKeyFrame(mEditor->currentFrame(), 1))
{
if (layer->moveKeyFrame(mEditor->currentFrame(), 1))
{
mEditor->scrubForward();
}
mEditor->scrubForward();
}
mEditor->layers()->notifyAnimationLengthChanged();
emit mEditor->framesModified();
Expand All @@ -858,12 +863,17 @@ void ActionCommands::moveFrameForward()
void ActionCommands::moveFrameBackward()
{
Layer* layer = mEditor->layers()->currentLayer();
if (layer)
if (layer == nullptr) { return; }

// Moving keyframes on hidden layers is not allowed, as it can cause confusion. We show a warning to the user to let them know they need to unhide the layer first.
if (!layer->visible()) {
mEditor->getScribbleArea()->showLayerNotVisibleWarning();
return;
}

if (layer->moveKeyFrame(mEditor->currentFrame(), -1))
{
if (layer->moveKeyFrame(mEditor->currentFrame(), -1))
{
mEditor->scrubBackward();
}
mEditor->scrubBackward();
}
emit mEditor->framesModified();
}
Expand Down
23 changes: 23 additions & 0 deletions core_lib/src/interface/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ void Editor::pasteFromPreviousFrame()
return;
}

// Prevents pasting on an invisible layer, as this is likely a mistake and can be confusing for users, so show a warning and prevent the action
if (!currentLayer->visible()) {
mScribbleArea->showLayerNotVisibleWarning();
return;
}

if (currentLayer->type() == Layer::BITMAP)
{
backup(tr("Paste from Previous Keyframe"));
Expand Down Expand Up @@ -357,6 +363,12 @@ void Editor::paste()

if (!canPaste()) { return; }

// Prevents pasting on an invisible layer, as this is likely a mistake and can be confusing for users, so show a warning and prevent the action
if (!currentLayer->visible()) {
mScribbleArea->showLayerNotVisibleWarning();
return;
}

if (clipboards()->framesIsEmpty()) {

backup(tr("Paste"));
Expand Down Expand Up @@ -806,6 +818,13 @@ void Editor::selectAll() const
Layer* layer = layers()->currentLayer();

QRectF rect;

// Prevents Selection of an invisible layer, as this is likely a mistake and can be confusing for users, so show a warning and prevent the action
if (!layer->visible()) {
mScribbleArea->showLayerNotVisibleWarning();
return;
}

if (layer->type() == Layer::BITMAP)
{
// Selects the drawn area (bigger or smaller than the screen). It may be more accurate to select all this way
Expand Down Expand Up @@ -989,6 +1008,10 @@ void Editor::switchVisibilityOfLayer(int layerNumber)
{
Layer* layer = mObject->getLayer(layerNumber);
if (layer != nullptr) layer->switchVisibility();

// Deselect all to prevent confusion, as the user might have selected something on a layer that is now invisible, and this could lead to confusion
deselectAll();

mScribbleArea->onLayerChanged();

emit updateTimeLine();
Expand Down
6 changes: 6 additions & 0 deletions core_lib/src/interface/scribblearea.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,12 @@ void ScribbleArea::clearImage()
Layer* layer = mEditor->layers()->currentLayer();
if (layer == nullptr) { return; }

// Clearing an invisible layer is likely a mistake, so show a warning and prevent the action
if (!layer->visible()) {
showLayerNotVisibleWarning();
return;
}

if (layer->type() == Layer::VECTOR)
{
mEditor->backup(tr("Clear Image", "Undo step text"));
Expand Down
4 changes: 4 additions & 0 deletions core_lib/src/structure/layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ void Layer::removeFromSelectionList(int position)

bool Layer::moveKeyFrame(int position, int offset)
{
if (!visible()) {
return false;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep this logic out of the Layer class.

Restricting frame movement based on visibility is a UI-level behavior designed to prevent accidental edits. however, from a data perspective, the model should allow moving frames regardless of visibility. We should probably move this check to the Interaction or Controller layer.

@MrStevns MrStevns Feb 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's up for debate. Imo. This is exactly where the logic should be, rather than in the UI. If we decide to build two UI clients, then the logic should be handled by our core rather than each respective client?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we plan to add script system or API. I believe we did discuss the feasibility before.


int newPos = position + offset;
if (newPos < 1) { return false; }

Expand Down
1 change: 1 addition & 0 deletions core_lib/src/tool/selecttool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ bool SelectTool::maybeDeselect(const QPointF& pos)
*/
void SelectTool::keepSelection(Layer* currentLayer)
{

if (currentLayer->type() == Layer::VECTOR)
{
VectorImage* vectorImage = static_cast<LayerVector*>(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0);
Expand Down
Loading