Move tool not working with arrow keys?

Hi all,
I’m using Aseprite v1.2.16.3 after quite some time without using it.

I’m pretty sure I was used to moving layers with the keyboard: I selected a layer, the move tool and used the arrow keys to nudge it around pixel by pixel.

However, this seems not to be working in my current version. If I use the arrow keys, the only effect I get is that the layer selection changes (with up/down, left/right do nothing).

As it’s difficult to be pixel precise when moving things with the mouse, is there a way to make it work?

Thanks!

Hi there @guga2112, welcome! Actually Aseprite never supported moving with the arrow keys the current cel when the Move tool is active. There are plans to implement this in a near future (along with other options).

Anyway you might be confused with the selection tool, you can use the Rectangular Marquee (M key), then select the whole canvas (Ctrl+A) and then the arrow keys can move the selection content. I hope it helps!

2 Likes

Whoa, I might have mistaken with other editors then.

Thanks for the tip, I’ll go with the workaround. Looking forward to the addition :smiley:

Coming from PS I totally miss the possibility to move a layer with the keyboard without a selection. I hope this is still planned for Aseprite.

Could a script or extension perform this: permanently observe keyboard input and move the whole layer content?

Okay, solved with four little scripts assigned to different keys (assigned to the arrow keys in my case at the expense of selection movement resp. adding a modifier key).

Exemplary for the up direction (move_layer_up.lua):

-- Script to move the active layer content up by 1 pixel
local spr = app.activeSprite
if spr then
    for _, cel in ipairs(app.activeLayer.cels) do
        cel.position = Point(cel.position.x, cel.position.y - 1)
    end
end

I reworked my script and thought I’d share it here in case someone else likes to use it. When pressing an arrow key:

  • If there’s no selection at all: Shifts the entire cel by 1px.
  • If there’s a selection and the Move tool is active: Shifts the selection’s content by 1px.
  • If there’s a selection and any other tool is active: Shifts only the selection boundaries by 1px.

To use it, create one script file for each direction (up, down, left, right) and bind each file to the corresponding arrow key (or whichever keys you prefer). Just make sure those keys aren’t already bound to other actions.

Here’s the code for the up direction (AI assisted):

-- move_up.lua
-- Shifts the selection boundaries, selection content, or entire cel 
-- based on which tool is currently active (Move tool vs. others).

----------------------------------
-- DIRECTION CONFIG 
----------------------------------
local DIRECTION_MASK = "up" -- "up", "down", "left", or "right"

-- SHIFT_X, SHIFT_Y define how to move cels
--  e.g. moving "up" => SHIFT_X=0, SHIFT_Y=-1
--       moving "down" => SHIFT_X=0, SHIFT_Y=1
--       moving "left" => SHIFT_X=-1, SHIFT_Y=0
--       moving "right" => SHIFT_X=1, SHIFT_Y=0
local SHIFT_X = 0
local SHIFT_Y = -1

----------------------------------
-- HELPER FUNCTIONS
----------------------------------

-- Move the selection's content by 1px in DIRECTION_MASK
local function moveSelectionContent()
  app.command.MoveMask{
    target    = "content",
    direction = DIRECTION_MASK,
    units     = "pixel",
    quantity  = 1
  }
end

-- Move the selection boundary by 1px in DIRECTION_MASK
local function moveSelectionBoundary()
  app.command.MoveMask{
    target    = "boundaries", -- "boundaries" is more reliable than "selection"
    direction = DIRECTION_MASK,
    units     = "pixel",
    quantity  = 1
  }
end

-- Move all selected cels (if any), or just the active cel
local function moveSelectedCels()
  local sprite = app.activeSprite
  if not sprite then return end

  -- Check if multiple cels are selected in the timeline
  local cels = app.range.cels
  if #cels > 0 then
    -- Move all selected cels
    for _, cel in ipairs(cels) do
      if cel and cel.layer.isEditable then
        cel.position = Point(
          cel.position.x + SHIFT_X,
          cel.position.y + SHIFT_Y
        )
      end
    end
  else
    -- No timeline selection => move the single active cel
    local layer = app.activeLayer
    if layer and layer.isEditable then
      local cel = layer:cel(app.activeFrame)
      if cel then
        cel.position = Point(
          cel.position.x + SHIFT_X,
          cel.position.y + SHIFT_Y
        )
      end
    end
  end
end

----------------------------------
-- MAIN LOGIC
----------------------------------
local function moveSelectionOrCel()
  local sprite = app.activeSprite
  if not sprite then
    return
  end

  local toolId       = app.activeTool.id
  local hasSelection = not sprite.selection.isEmpty

  -- Known move tools
  local validMoveToolIds = { "moveCel", "movePixel", "moveMask", "move" }
  local isMoveTool       = false
  for _, t in ipairs(validMoveToolIds) do
    if toolId == t then
      isMoveTool = true
      break
    end
  end

  -- Decide what to move
  if hasSelection then
    -- There's a pixel selection
    if isMoveTool then
      moveSelectionContent()
    else
      moveSelectionBoundary()
    end
  else
    -- No pixel selection => if Move tool, shift cels
    if isMoveTool then
      moveSelectedCels()
    end
  end
end

moveSelectionOrCel()