Merge branch 'beta' into isometric-grid

This commit is contained in:
David Capello 2025-08-21 08:09:33 -03:00 committed by GitHub
commit 1ac451de03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
443 changed files with 10016 additions and 4034 deletions

View File

@ -60,6 +60,8 @@ Checks: >
-readability-uppercase-literal-suffix -readability-uppercase-literal-suffix
WarningsAsErrors: '' WarningsAsErrors: ''
CheckOptions: CheckOptions:
- key: readability-implicit-bool-conversion.AllowIntegerConditions
value: true
- key: readability-implicit-bool-conversion.AllowPointerConditions - key: readability-implicit-bool-conversion.AllowPointerConditions
value: true value: true
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic

43
.github/workflows/build-auto.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: build-auto
on:
push:
paths:
- '.github/workflows/build-auto.yml'
- 'build.sh'
- 'laf'
jobs:
build-auto:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
build_type: [RelWithDebInfo, Debug]
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Install Dependencies
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update -qq
sudo apt-get install -y \
libpixman-1-dev libfreetype6-dev libharfbuzz-dev zlib1g-dev \
libx11-dev libxcursor-dev libxi-dev libgl1-mesa-dev libfontconfig1-dev
- uses: aseprite/get-ninja@main
- uses: ilammy/msvc-dev-cmd@v1
if: runner.os == 'Windows'
- name: Building
shell: bash
run: |
bash build.sh --auto --norun
- name: Running CLI Tests
shell: bash
run: |
if [[ "${{ runner.os }}" == "Linux" ]] ; then
export XVFB=xvfb-run
fi
export ASEPRITE=$PWD/build/bin/aseprite
cd tests
$XVFB bash run-tests.sh

View File

@ -34,16 +34,14 @@ jobs:
shell: bash shell: bash
run: | run: |
if [[ "${{ runner.os }}" == "Windows" ]] ; then if [[ "${{ runner.os }}" == "Windows" ]] ; then
choco install wget -y --no-progress this_dir=$(cygpath "${{ github.workspace }}")
wget https://github.com/aseprite/skia/releases/download/m124-08a5439a6b/Skia-Windows-Release-x64.zip
unzip Skia-Windows-Release-x64.zip -d skia
elif [[ "${{ runner.os }}" == "macOS" ]] ; then
wget https://github.com/aseprite/skia/releases/download/m124-08a5439a6b/Skia-macOS-Release-arm64.zip
unzip Skia-macOS-Release-arm64.zip -d skia
else else
wget https://github.com/aseprite/skia/releases/download/m124-08a5439a6b/Skia-Linux-Release-x64.zip this_dir="${{ github.workspace }}"
unzip Skia-Linux-Release-x64.zip -d skia
fi fi
skia_url=$(source $this_dir/laf/misc/skia-url.sh | xargs)
skia_file=$(basename $skia_url)
curl --ssl-revoke-best-effort -L -o "$skia_file" "$skia_url"
unzip "$skia_file" -d skia
- name: ccache - name: ccache
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.17
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }} if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}

View File

@ -13,6 +13,6 @@ jobs:
post-comments: post-comments:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: ZedThree/clang-tidy-review/post@v0.20.1 - uses: ZedThree/clang-tidy-review/post@v0.21.0
with: with:
token: ${{ secrets.CLANG_TIDY_TOKEN }} token: ${{ secrets.CLANG_TIDY_TOKEN }}

2
.gitignore vendored
View File

@ -12,7 +12,9 @@
*.res *.res
.DS_Store .DS_Store
.vs .vs
.vscode
tests/_test* tests/_test*
build build
.build .build
.deps
CMakeSettings.json CMakeSettings.json

98
AUTHORS.md Normal file
View File

@ -0,0 +1,98 @@
# Credits
Aseprite is being developed and maintained currently by [Igara Studio](https://igara.com/).
The active team of developers is:
* [David Capello](https://github.com/dacap)
* [Gaspar Capello](https://github.com/Gasparoken)
* [Martín Capello](https://github.com/martincapello)
* [Christian Kaiser](https://github.com/ckaiser)
* [Dante Paola](https://github.com/Liebranca)
Previous team members that contributed with code/docs/scripts/graphics:
* [Kacper Woźniak](https://github.com/thkwznk)
* [Joshua Ogunyinka](https://github.com/iamOgunyinka)
* [David Campo](https://github.com/dncampo)
## Translations
The translation work of Aseprite is only possible thanks to the
contribution, help, and good will of several translators coordinated
through our Weblate project:
* [Translation Credits](strings/README.md)
## Graphics
Aseprite logo was created by David Capello. Graphics used as background
of [Aseprite home page](https://www.aseprite.org),
on [Steam Store](https://store.steampowered.com/app/431730/Aseprite/),
and [social media channels](https://bsky.app/profile/aseprite.org),
were created by:
* [Ilija Melentijevic](https://ilkke.net/)
## Themes
The default Aseprite font was created by David Capello, and the
default Aseprite theme was introduced in v0.8, originally created by:
* Ilija Melentijevic
A modified dark version of this theme was introduced in v1.3-beta1, created by:
* [Nicolas Desilets](https://twitter.com/MapleGecko)
These themes are now being maintained by Igara Studio and external
contributors from time to time.
## Palettes
Aseprite includes color palettes created by:
* [Richard "DawnBringer" Fhager](http://pixeljoint.com/p/23821.htm), [DB16](http://pixeljoint.com/forum/forum_posts.asp?TID=12795), [DB32](http://pixeljoint.com/forum/forum_posts.asp?TID=16247) (default Aseprite color palette)
* [Arne Niklas Jansson](http://androidarts.com/), [16 colors](http://androidarts.com/palette/16pal.htm), [32 colors](http://wayofthepixel.net/index.php?topic=15824.msg144494)
* [ENDESGA Studios](https://twitter.com/ENDESGA), [EDG16 and EDG32](https://forums.tigsource.com/index.php?topic=46126.msg1279124#msg1279124), and [other palettes](https://twitter.com/ENDESGA/status/865812366931353600)
* [Hyohnoo Games](https://twitter.com/Hyohnoo), [mail24](https://twitter.com/Hyohnoo/status/797472587974639616) palette
* [Davit Masia](https://twitter.com/DavitMasia), [matriax8c](https://twitter.com/DavitMasia/status/834862452164612096) palette
* [Javier Guerrero](https://twitter.com/Xavier_Gd), [nyx8](https://twitter.com/Xavier_Gd/status/868519467864686594) palette
* [Adigun A. Polack](https://twitter.com/adigunpolack), [AAP-64](http://pixeljoint.com/pixelart/119466.htm), [AAP-Splendor128](http://pixeljoint.com/pixelart/120714.htm), [SimpleJPC-16](http://pixeljoint.com/pixelart/119844.htm), and [AAP-Micro12](http://pixeljoint.com/pixelart/121151.htm) palette
* [PineTreePizza](https://twitter.com/PineTreePizza), [Rosy-42](https://twitter.com/PineTreePizza/status/1006536191955623938) palette
## Pixel-art Features
Aseprite tries to replicate some pixel-art algorithms:
* [Shading Ink](https://aseprite.org/docs/shading/): created as a simplification of GrafX2 shade mode, thanks to Ilija Melentijevic for introducing me to this feature in 2009
* [RotSprite](http://forums.sonicretro.org/index.php?showtopic=8848&st=15&p=159754&#entry159754) by Xenowhirl.
* [Pixel perfect drawing algorithm](https://deepnight.net/blog/tools/pixel-perfect-drawing/)
by [Sébastien Bénard](https://twitter.com/deepnightfr) and
[Carduus](https://twitter.com/CarduusHimself/status/420554200737935361).
## Community
A special thanks to @Outlander for helping us moderating our [Discord server](https://discord.gg/Yb2CeX8).
Thanks to all the people that hung around for such a long time.
## Contributors
Thank you everyone who contributed to Aseprite with ideas, patches,
code, bug reports, new features, donations, tutorials, videos,
personal messages, chats, emails, tweets, posts, questions, libraries,
compilers, and any other tools that made this program possible today.
* Thanks to all [contributors](https://github.com/aseprite/aseprite/graphs/contributors)
* Thanks to all developers and maintainers behind [other open source projects](docs/LICENSES.md) used by Aseprite
* Thanks to all early PayPal donors and donors from our Pledgie Campaign (before Aseprite was commercialized)
* Thanks to every who support our business model: this source-available / sell-binaries combo
* Thanks to schools and [educational institutions](https://aseprite.org/educational)
that are using Aseprite in their classrooms <3
* Thanks to our family and friends who always support our work
It's been more years than I can remember, sorry if we missed someone,
please drop me a line to [david@igara.com](mailto:david@igara.com) to
fix something or say hi. We'll try to keep this updated (for past and
future contributors).
Sincerely, David.

View File

@ -243,7 +243,13 @@ if(USE_SHARED_LIBPNG)
add_definitions(${PNG_DEFINITIONS}) add_definitions(${PNG_DEFINITIONS})
else() else()
set(PNG_FOUND ON) set(PNG_FOUND ON)
set(PNG_LIBRARY png_static)
# Skia on Linux includes libpng symbols
if(UNIX AND NOT APPLE AND LAF_BACKEND STREQUAL "skia")
set(PNG_LIBRARY skia)
else()
set(PNG_LIBRARY png_static)
endif()
set(PNG_LIBRARIES ${PNG_LIBRARY}) set(PNG_LIBRARIES ${PNG_LIBRARY})
set(PNG_INCLUDE_DIRS set(PNG_INCLUDE_DIRS
${LIBPNG_DIR} ${LIBPNG_DIR}

View File

@ -6,7 +6,8 @@
* [Windows dependencies](#windows-dependencies) * [Windows dependencies](#windows-dependencies)
* [macOS dependencies](#macos-dependencies) * [macOS dependencies](#macos-dependencies)
* [Linux dependencies](#linux-dependencies) * [Linux dependencies](#linux-dependencies)
* [Compiling](#compiling) * [Automatic Building](#automatic-building)
* [Manual Building](#manual-building)
* [Windows details](#windows-details) * [Windows details](#windows-details)
* [MinGW](#mingw) * [MinGW](#mingw)
* [macOS details](#macos-details) * [macOS details](#macos-details)
@ -17,11 +18,12 @@
# Platforms # Platforms
You should be able to compile Aseprite successfully on the following You should be able to compile Aseprite successfully on the following
platforms: platforms (older and newer versions might work):
* Windows 11 + [Visual Studio Community 2022 + Windows 10.0 SDK (the latest version available)](https://imgur.com/a/7zs51IT) (we don't support [MinGW](#mingw)) * Windows 11 + [Visual Studio Community 2022 + Windows 11 SDK](https://imgur.com/a/7zs51IT)
* macOS 13.0.1 Ventura + Xcode 14.1 + macOS 11.3 SDK (older version might work) * *Important*: We don't support [MinGW](#mingw)
* Linux Ubuntu Bionic 18.04 + clang 10.0 * macOS 15.2 Sequoia + Xcode 16.3 + macOS 15.4 SDK
* Linux Ubuntu Focal Fossa 20.04 + clang 12
# Get the source code # Get the source code
@ -49,7 +51,7 @@ clone the repository on Windows.
To compile Aseprite you will need: To compile Aseprite you will need:
* The latest version of [CMake](https://cmake.org) (3.16 or greater) * The latest version of [CMake](https://cmake.org)
* [Ninja](https://ninja-build.org) build system * [Ninja](https://ninja-build.org) build system
* And a compiled version of the `aseprite-m124` branch of * And a compiled version of the `aseprite-m124` branch of
the [Skia library](https://github.com/aseprite/skia#readme). the [Skia library](https://github.com/aseprite/skia#readme).
@ -59,25 +61,24 @@ To compile Aseprite you will need:
## Windows dependencies ## Windows dependencies
* Windows 10/11 (we don't support cross-compiling) * Windows 11 (we don't support cross-compiling)
* [Visual Studio Community 2022](https://visualstudio.microsoft.com/downloads/) (we don't support [MinGW](#mingw)) * [Visual Studio Community 2022](https://visualstudio.microsoft.com/downloads/) (we don't support [MinGW](#mingw))
* The [Desktop development with C++ item + Windows 10.0.18362.0 SDK](https://imgur.com/a/7zs51IT) * The [Desktop development with C++ item + Windows 10.0.26100.0 SDK](https://imgur.com/a/7zs51IT)
from the Visual Studio installer from Visual Studio installer
## macOS dependencies ## macOS dependencies
On macOS you will need macOS 11.3 SDK and Xcode 13.1 (older versions On macOS you will need macOS 15.4 SDK and Xcode 16.3 (older versions might work).
might work).
## Linux dependencies ## Linux dependencies
You will need the following dependencies on Ubuntu/Debian: You will need the following dependencies on Ubuntu/Debian:
sudo apt-get install -y g++ clang libc++-dev libc++abi-dev cmake ninja-build libx11-dev libxcursor-dev libxi-dev libgl1-mesa-dev libfontconfig1-dev sudo apt-get install -y g++ clang cmake ninja-build libx11-dev libxcursor-dev libxi-dev libgl1-mesa-dev libfontconfig1-dev
Or use clang-10 packages (or newer) in case that clang in your distribution is older than clang 10.0: Or use clang-12 packages (or newer) in case that clang in your distribution is older than clang 12.0:
sudo apt-get install -y clang-10 libc++-10-dev libc++abi-10-dev sudo apt-get install -y clang-12
On Fedora: On Fedora:
@ -85,13 +86,24 @@ On Fedora:
On Arch: On Arch:
sudo pacman -S gcc clang libc++ cmake ninja libx11 libxcursor mesa-libgl fontconfig libwebp sudo pacman -S gcc clang cmake ninja libx11 libxcursor mesa-libgl fontconfig libwebp
On SUSE: On SUSE:
sudo zypper install gcc-c++ clang libc++-devel libc++abi-devel cmake ninja libX11-devel libXcursor-devel libXi-devel Mesa-libGL-devel fontconfig-devel sudo zypper install gcc-c++ clang cmake ninja libX11-devel libXcursor-devel libXi-devel Mesa-libGL-devel fontconfig-devel
# Compiling # Automatic Building
We offer a new [build script](build.sh) that automates and help you to
compile Aseprite following instructions on screen. This will be the
preferred method for new users and developers to compile Aseprite.
After you get [get Aseprite code](#get-the-source-code) and install
[its dependencies](#dependencies), you can run [build.cmd](build.cmd)
file on Windows double-clicking it, or [build.sh](build.sh) on macOS or
Linux running it from the terminal from the same Aseprite folder.
# Manual Building
1. [Get Aseprite code](#get-the-source-code), put it in a folder like 1. [Get Aseprite code](#get-the-source-code), put it in a folder like
`C:\aseprite`, and create a `build` directory inside to leave all `C:\aseprite`, and create a `build` directory inside to leave all
@ -223,7 +235,9 @@ If you have a Retina display, check the following issue:
## Linux details ## Linux details
You need to use clang and libc++ to compile Aseprite: You can compile Aseprite with gcc or clang. In case that you are using
the [pre-compiled Skia version](https://github.com/aseprite/skia/releases/),
you must use libstdc++ to compile Aseprite:
cd aseprite cd aseprite
mkdir build mkdir build
@ -232,8 +246,8 @@ You need to use clang and libc++ to compile Aseprite:
export CXX=clang++ export CXX=clang++
cmake \ cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_FLAGS:STRING=-stdlib=libc++ \ -DCMAKE_CXX_FLAGS:STRING=-stdlib=libstdc++ \
-DCMAKE_EXE_LINKER_FLAGS:STRING=-stdlib=libc++ \ -DCMAKE_EXE_LINKER_FLAGS:STRING=-stdlib=libstdc++ \
-DLAF_BACKEND=skia \ -DLAF_BACKEND=skia \
-DSKIA_DIR=$HOME/deps/skia \ -DSKIA_DIR=$HOME/deps/skia \
-DSKIA_LIBRARY_DIR=$HOME/deps/skia/out/Release-x64 \ -DSKIA_LIBRARY_DIR=$HOME/deps/skia/out/Release-x64 \
@ -245,13 +259,6 @@ You need to use clang and libc++ to compile Aseprite:
In this case, `$HOME/deps/skia` is the directory where Skia was In this case, `$HOME/deps/skia` is the directory where Skia was
compiled or uncompressed. compiled or uncompressed.
### GCC compiler
In case that you are using the pre-compiled Skia version, you must use
the clang compiler and libc++ to compile Aseprite. Only if you compile
Skia with GCC, you will be able to compile Aseprite with GCC, and this
is not recommended as you will have a performance penalty doing so.
# Using shared third party libraries # Using shared third party libraries
If you don't want to use the embedded code of third party libraries If you don't want to use the embedded code of third party libraries

View File

@ -48,45 +48,14 @@ You can ask for help in:
[YouTube](https://www.youtube.com/user/aseprite), [YouTube](https://www.youtube.com/user/aseprite),
[Instagram](https://www.instagram.com/aseprite/). [Instagram](https://www.instagram.com/aseprite/).
## Authors
Aseprite is being developed by [Igara Studio](https://igara.com/):
* [David Capello](https://davidcapello.com/)
* [Gaspar Capello](https://github.com/Gasparoken)
* [Martín Capello](https://github.com/martincapello)
## Credits ## Credits
The default Aseprite theme was introduced in v0.8, created by: Aseprite was originally created by [David Capello](https://davidcapello.com/)
and is now being developed and maintained by [Igara Studio](https://igara.com/)
and contributors.
* [Ilija Melentijevic](https://ilkke.net/) Check the [AUTHORS](AUTHORS.md) file for details about the active team
of developers working on Aseprite.
A modified dark version of this theme introduced in v1.3-beta1 was created by:
* [Nicolas Desilets](https://twitter.com/MapleGecko)
* [David Capello](https://twitter.com/davidcapello)
Aseprite includes color palettes created by:
* [Richard "DawnBringer" Fhager](http://pixeljoint.com/p/23821.htm), [16 colors](http://pixeljoint.com/forum/forum_posts.asp?TID=12795), [32 colors](http://pixeljoint.com/forum/forum_posts.asp?TID=16247).
* [Arne Niklas Jansson](http://androidarts.com/), [16 colors](http://androidarts.com/palette/16pal.htm), [32 colors](http://wayofthepixel.net/index.php?topic=15824.msg144494).
* [ENDESGA Studios](https://twitter.com/ENDESGA), [EDG16 and EDG32](https://forums.tigsource.com/index.php?topic=46126.msg1279124#msg1279124), and [other palettes](https://twitter.com/ENDESGA/status/865812366931353600).
* [Hyohnoo Games](https://twitter.com/Hyohnoo), [mail24](https://twitter.com/Hyohnoo/status/797472587974639616) palette.
* [Davit Masia](https://twitter.com/DavitMasia), [matriax8c](https://twitter.com/DavitMasia/status/834862452164612096) palette.
* [Javier Guerrero](https://twitter.com/Xavier_Gd), [nyx8](https://twitter.com/Xavier_Gd/status/868519467864686594) palette.
* [Adigun A. Polack](https://twitter.com/adigunpolack), [AAP-64](http://pixeljoint.com/pixelart/119466.htm), [AAP-Splendor128](http://pixeljoint.com/pixelart/120714.htm), [SimpleJPC-16](http://pixeljoint.com/pixelart/119844.htm), and [AAP-Micro12](http://pixeljoint.com/pixelart/121151.htm) palette.
* [PineTreePizza](https://twitter.com/PineTreePizza), [Rosy-42](https://twitter.com/PineTreePizza/status/1006536191955623938) palette.
It tries to replicate some pixel-art algorithms:
* [RotSprite](http://forums.sonicretro.org/index.php?showtopic=8848&st=15&p=159754&#entry159754) by Xenowhirl.
* [Pixel perfect drawing algorithm](https://deepnight.net/blog/tools/pixel-perfect-drawing/) by [Sébastien Bénard](https://twitter.com/deepnightfr) and [Carduus](https://twitter.com/CarduusHimself/status/420554200737935361).
Thanks to [third-party open source projects](docs/LICENSES.md), to
[contributors](https://www.aseprite.org/contributors/), and all the
people who have contributed ideas, patches, bugs report, feature
requests, donations, and help us to develop Aseprite.
## License ## License

141
build.sh
View File

@ -56,31 +56,6 @@ if [ "$1" == "--norun" ] ; then
norun=1 norun=1
fi fi
# Platform.
if [[ "$(uname)" =~ "MINGW32" ]] || [[ "$(uname)" =~ "MINGW64" ]] || [[ "$(uname)" =~ "MSYS_NT-10.0" ]] ; then
is_win=1
cpu=x64
if ! cl.exe >/dev/null 2>/dev/null ; then
echo ""
echo "MSVC compiler (cl.exe) not found in PATH"
echo ""
echo " PATH=$PATH"
echo ""
exit 1
fi
elif [[ "$(uname)" == "Linux" ]] ; then
is_linux=1
cpu=x64
elif [[ "$(uname)" =~ "Darwin" ]] ; then
is_macos=1
if [[ $(uname -m) == "arm64" ]]; then
cpu=arm64
else
cpu=x64
fi
fi
# Check utilities. # Check utilities.
if ! cmake --version >/dev/null ; then if ! cmake --version >/dev/null ; then
echo "" echo ""
@ -137,6 +112,23 @@ if [ $run_submodule_update ] ; then
echo "Done" echo "Done"
fi fi
# Platform.
if ! source "$pwd/laf/misc/platform.sh" ; then
exit $?
fi
if [ $is_win ] ; then
# Check MSVC compiler.
if ! cl.exe >/dev/null 2>/dev/null ; then
echo ""
echo "MSVC compiler (cl.exe) not found in PATH"
echo ""
echo " PATH=$PATH"
echo ""
exit 1
fi
fi
# Create the directory to store the configuration. # Create the directory to store the configuration.
if [ ! -d "$pwd/.build" ] ; then if [ ! -d "$pwd/.build" ] ; then
mkdir "$pwd/.build" mkdir "$pwd/.build"
@ -150,25 +142,25 @@ if [ ! -f "$pwd/.build/userkind" ] ; then
echo "user" > $pwd/.build/userkind echo "user" > $pwd/.build/userkind
else else
echo "" echo ""
echo "Select what kind of user you are (press U or D keys):" echo "Select what kind of user you are (press U or D key and then Enter):"
echo "" echo ""
echo " [U]ser: give a try to Aseprite" echo " [U]ser: give a try to Aseprite"
echo " [D]eveloper: develop/modify Aseprite" echo " [D]eveloper: develop/modify Aseprite"
echo "" echo ""
read -sN 1 -p "[U/D]? " read -p "[U/D]? "
echo "" REPLY=$(echo $REPLY | tr '[:upper:]' '[:lower:]')
if [[ "$REPLY" == "d" || "$REPLY" == "D" ]] ; then if [[ "$REPLY" == "d" || "$REPLY" == "dev" || "$REPLY" == "developer" ]] ; then
echo "developer" > $pwd/.build/userkind echo "developer" > $pwd/.build/userkind
elif [[ "$REPLY" == "u" || "$REPLY" == "U" ]] ; then elif [[ "$REPLY" == "u" || "$REPLY" == "user" ]] ; then
echo "user" > $pwd/.build/userkind echo "user" > $pwd/.build/userkind
else else
echo "Use U or D keys to select kind of user/build process" echo "Use U or D keys (and press Enter) to select kind of user/build process"
exit 1 exit 1
fi fi
fi fi
fi fi
userkind=$(echo -n $(cat $pwd/.build/userkind)) userkind=$(cat $pwd/.build/userkind)
if [ "$userkind" == "developer" ] ; then if [ "$userkind" == "developer" ] ; then
echo "======================= BUILDING FOR DEVELOPER =======================" echo "======================= BUILDING FOR DEVELOPER ======================="
else else
@ -229,7 +221,7 @@ if [ ! -f "$pwd/.build/builds_dir" ] ; then
echo "$builds_dir" > "$pwd/.build/builds_dir" echo "$builds_dir" > "$pwd/.build/builds_dir"
fi fi
# Overwrite $builds_dir variable from the config content. # Overwrite $builds_dir variable from the config content.
builds_dir="$(echo -n $(cat $pwd/.build/builds_dir))" builds_dir="$(cat $pwd/.build/builds_dir)"
# List all builds. # List all builds.
builds_list="$(mktemp)" builds_list="$(mktemp)"
@ -265,7 +257,8 @@ else
# New build # New build
if [[ "$build_n" == "n" || "$build_n" == "N" ]] ; then if [[ "$build_n" == "n" || "$build_n" == "N" ]] ; then
read -p "Select build type [RELEASE/debug]? " read -p "Select build type [RELEASE/debug]? "
if [[ "${REPLY,,}" == "debug" ]] ; then REPLY=$(echo $REPLY | tr '[:upper:]' '[:lower:]')
if [[ "${REPLY}" == "debug" ]] ; then
build_type=Debug build_type=Debug
new_build_name=aseprite-debug new_build_name=aseprite-debug
else else
@ -348,10 +341,7 @@ else
elif git --git-dir="$source_dir/.git" branch --contains "$remote/main" | grep -q "^\* $branch_name\$" ; then elif git --git-dir="$source_dir/.git" branch --contains "$remote/main" | grep -q "^\* $branch_name\$" ; then
base_branch_name=main base_branch_name=main
else else
echo "" base_branch_name=$branch_name
echo "Error: Branch $branch_name looks like doesn't belong to main or beta"
echo ""
exit 1
fi fi
fi fi
@ -366,15 +356,9 @@ else
fi fi
# Required Skia for the base branch. # Required Skia for the base branch.
if [ "$base_branch_name" == "beta" ] ; then skia_tag=$(cat "$pwd/laf/misc/skia-tag.txt")
skia_tag=m124-08a5439a6b possible_skia_dir_name=skia-$(echo $skia_tag | cut -d "-" -f 1)
file_skia_dir=beta_skia_dir file_skia_dir="$base_branch_name"_skia_dir
possible_skia_dir_name=skia-m124
else
skia_tag=m102-861e4743af
file_skia_dir=main_skia_dir
possible_skia_dir_name=skia
fi
# Check Skia dependency. # Check Skia dependency.
if [ ! -f "$pwd/.build/$file_skia_dir" ] ; then if [ ! -f "$pwd/.build/$file_skia_dir" ] ; then
@ -385,23 +369,33 @@ if [ ! -f "$pwd/.build/$file_skia_dir" ] ; then
skia_dir="$HOME/deps/$possible_skia_dir_name" skia_dir="$HOME/deps/$possible_skia_dir_name"
fi fi
# Set default location if not found
if [ ! -d "$skia_dir" ] ; then if [ ! -d "$skia_dir" ] ; then
echo "" # Use .deps directory to download Skia for users (which is a
echo "Skia directory wasn't found." # simple setup). In case of developers we'd prefer the shared
echo "" # directory by default.
if [ "$userkind" == "user" ] ; then
echo "Select Skia directory to create [$skia_dir]? " skia_dir="$pwd/.deps/$possible_skia_dir_name"
if [ ! $auto ] ; then fi
read skia_dir_read
if [ "$skia_dir_read" != "" ] ; then if [ ! -d "$skia_dir" ] ; then
skia_dir="$skia_dir_read" echo ""
fi echo "Skia directory wasn't found."
echo ""
echo "Select Skia directory to create [$skia_dir]? "
if [ ! $auto ] ; then
read skia_dir_read
if [ "$skia_dir_read" != "" ] ; then
skia_dir="$skia_dir_read"
fi
fi
mkdir -p $skia_dir || exit 1
fi fi
mkdir -p $skia_dir || exit 1
fi fi
echo $skia_dir > "$pwd/.build/$file_skia_dir" echo $skia_dir > "$pwd/.build/$file_skia_dir"
fi fi
skia_dir=$(echo -n $(cat $pwd/.build/$file_skia_dir)) skia_dir=$(cat $pwd/.build/$file_skia_dir)
if [ ! -d "$skia_dir" ] ; then if [ ! -d "$skia_dir" ] ; then
mkdir "$skia_dir" mkdir "$skia_dir"
fi fi
@ -421,27 +415,30 @@ if [ ! -d "$skia_library_dir" ] ; then
echo "Skia library wasn't found." echo "Skia library wasn't found."
echo "" echo ""
if [ ! $auto ] ; then if [ ! $auto ] ; then
read -sN 1 -p "Download pre-compiled Skia automatically [Y/n]? " read -p "Download pre-compiled Skia automatically [Y/n]? "
# Convert the Enter key as the default option: an empty string
REPLY=$(echo $REPLY | tr '[:upper:]' '[:lower:]')
fi fi
if [[ $auto || "$REPLY" == "" || "$REPLY" == "y" || "$REPLY" == "Y" ]] ; then if [[ $auto || "$REPLY" == "" || "$REPLY" == "y" || "$REPLY" == "yes" ]] ; then
if [[ $is_win && "$build_type" == "Debug" ]] ; then if [[ $is_win && "$build_type" == "Debug" ]] ; then
skia_build=Debug skia_build=Debug
else else
skia_build=Release skia_build=Release
fi fi
skia_url=$(bash laf/misc/skia-url.sh $skia_build)
if [ $is_win ] ; then skia_file=$(basename $skia_url)
skia_file=Skia-Windows-$skia_build-$cpu.zip
elif [ $is_macos ] ; then
skia_file=Skia-macOS-$skia_build-$cpu.zip
else
skia_file=Skia-Linux-$skia_build-$cpu-libstdc++.zip
fi
skia_url=https://github.com/aseprite/skia/releases/download/$skia_tag/$skia_file
if [ ! -f "$skia_dir/$skia_file" ] ; then if [ ! -f "$skia_dir/$skia_file" ] ; then
curl -L -o "$skia_dir/$skia_file" "$skia_url" if ! command -v curl >/dev/null 2>&1 ; then
echo "Error: 'curl' command line tool is not available in PATH"
exit 1
fi
curl --ssl-revoke-best-effort -L -o "$skia_dir/$skia_file" "$skia_url"
fi fi
if [ ! -d "$skia_library_dir" ] ; then if [ ! -d "$skia_library_dir" ] ; then
if ! command -v unzip >/dev/null 2>&1 ; then
echo "Error: 'unzip' command line tool is not available in PATH"
exit 1
fi
unzip -n -d "$skia_dir" "$skia_dir/$skia_file" unzip -n -d "$skia_dir" "$skia_dir/$skia_file"
fi fi
else else
@ -468,7 +465,7 @@ if [ ! -f "$active_build_dir/ninja.build" ] ; then
echo "This will take some minutes." echo "This will take some minutes."
echo "" echo ""
if [ ! $auto ] ; then if [ ! $auto ] ; then
read -sN 1 -p "Press any key to continue. " read -p "Press Enter to continue."
fi fi
if [ $is_macos ] ; then if [ $is_macos ] ; then

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -216,7 +216,7 @@
<part id="tab_normal" x="2" y="112" w1="4" w2="5" w3="5" h1="4" h2="6" h3="2" /> <part id="tab_normal" x="2" y="112" w1="4" w2="5" w3="5" h1="4" h2="6" h3="2" />
<part id="tab_active" x="16" y="112" w1="4" w2="7" w3="5" h1="4" h2="6" h3="2" /> <part id="tab_active" x="16" y="112" w1="4" w2="7" w3="5" h1="4" h2="6" h3="2" />
<part id="tab_bottom_active" x="16" y="124" w1="4" w2="7" w3="5" h1="2" h2="1" h3="2" /> <part id="tab_bottom_active" x="16" y="124" w1="4" w2="7" w3="5" h1="2" h2="1" h3="2" />
<part id="tab_bottom_normal" x="2" y="124" w="12" h="5" /> <part id="tab_bottom_normal" x="2" y="124" w1="4" w2="5" w3="3" h1="2" h2="1" h3="2" />
<part id="tab_filler" x="0" y="112" w="2" h="12" /> <part id="tab_filler" x="0" y="112" w="2" h="12" />
<part id="tab_modified_icon_normal" x="32" y="112" w="5" h="5" /> <part id="tab_modified_icon_normal" x="32" y="112" w="5" h="5" />
<part id="tab_modified_icon_active" x="32" y="117" w="5" h="5" /> <part id="tab_modified_icon_active" x="32" y="117" w="5" h="5" />
@ -374,6 +374,7 @@
<part id="icon_close" x="152" y="256" w="7" h="7" /> <part id="icon_close" x="152" y="256" w="7" h="7" />
<part id="icon_search" x="160" y="256" w="8" h="8" /> <part id="icon_search" x="160" y="256" w="8" h="8" />
<part id="icon_user_data" x="168" y="256" w="8" h="8" /> <part id="icon_user_data" x="168" y="256" w="8" h="8" />
<part id="icon_layout" x="176" y="256" w="5" h="6" />
<part id="icon_pos" x="144" y="264" w="8" h="8" /> <part id="icon_pos" x="144" y="264" w="8" h="8" />
<part id="icon_size" x="152" y="264" w="8" h="8" /> <part id="icon_size" x="152" y="264" w="8" h="8" />
<part id="icon_selsize" x="160" y="264" w="8" h="8" /> <part id="icon_selsize" x="160" y="264" w="8" h="8" />
@ -485,25 +486,25 @@
<icon part="window_close_icon" color="button_hot_text" state="mouse" /> <icon part="window_close_icon" color="button_hot_text" state="mouse" />
<icon part="window_close_icon" color="button_selected_text" state="selected" /> <icon part="window_close_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_center_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_center_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_center_icon" color="button_normal_text" /> <icon part="window_center_icon" color="button_normal_text" />
<icon part="window_center_icon" color="button_hot_text" state="mouse" /> <icon part="window_center_icon" color="button_hot_text" state="mouse" />
<icon part="window_center_icon" color="button_selected_text" state="selected" /> <icon part="window_center_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_play_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_play_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_play_icon" color="button_normal_text" /> <icon part="window_play_icon" color="button_normal_text" />
<icon part="window_play_icon" color="button_hot_text" state="mouse" /> <icon part="window_play_icon" color="button_hot_text" state="mouse" />
<icon part="window_play_icon" color="button_selected_text" state="selected" /> <icon part="window_play_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_stop_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_stop_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_stop_icon" color="button_normal_text" /> <icon part="window_stop_icon" color="button_normal_text" />
<icon part="window_stop_icon" color="button_hot_text" state="mouse" /> <icon part="window_stop_icon" color="button_hot_text" state="mouse" />
<icon part="window_stop_icon" color="button_selected_text" state="selected" /> <icon part="window_stop_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_help_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_help_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_help_icon" color="button_normal_text" /> <icon part="window_help_icon" color="button_normal_text" />
<icon part="window_help_icon" color="button_hot_text" state="mouse" /> <icon part="window_help_icon" color="button_hot_text" state="mouse" />
@ -550,10 +551,13 @@
<text color="disabled" align="left" state="disabled" /> <text color="disabled" align="left" state="disabled" />
</style> </style>
<style id="mini_label" extends="label" font="mini" /> <style id="mini_label" extends="label" font="mini" />
<style id="warning_label" extends="label">
<icon part="warning_box" align="right" />
</style>
<style id="list_header_label" padding="2"> <style id="list_header_label" padding="2">
<text color="text" align="left" x="2" /> <text color="text" align="left" x="2" />
</style> </style>
<style id="link"> <style id="link" padding="1">
<text color="link_text" align="left" /> <text color="link_text" align="left" />
<text color="link_hover" align="left" state="mouse" /> <text color="link_hover" align="left" state="mouse" />
</style> </style>
@ -706,10 +710,11 @@
<style id="workspace_splitter"> <style id="workspace_splitter">
<background color="workspace" /> <background color="workspace" />
</style> </style>
<style id="horizontal_separator" border-left="2" border-top="4" border-right="2" border-bottom="0"> <style id="horizontal_separator" border="2">
<background color="window_face" /> <background color="window_face" />
<background-border part="separator_horz" align="middle" /> <background-border part="separator_horz" align="middle" />
<text color="separator_label" x="4" align="left middle" /> <text color="separator_label" x="4" align="left middle" />
<text color="disabled" x="4" align="left middle" state="disabled"/>
</style> </style>
<style id="menu_separator" extends="horizontal_separator" /> <style id="menu_separator" extends="horizontal_separator" />
<style id="separator_in_view" extends="horizontal_separator"> <style id="separator_in_view" extends="horizontal_separator">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -212,7 +212,7 @@
<part id="tab_normal" x="2" y="112" w1="4" w2="5" w3="5" h1="4" h2="6" h3="2" /> <part id="tab_normal" x="2" y="112" w1="4" w2="5" w3="5" h1="4" h2="6" h3="2" />
<part id="tab_active" x="16" y="112" w1="4" w2="7" w3="5" h1="4" h2="6" h3="2" /> <part id="tab_active" x="16" y="112" w1="4" w2="7" w3="5" h1="4" h2="6" h3="2" />
<part id="tab_bottom_active" x="16" y="124" w1="4" w2="7" w3="5" h1="2" h2="1" h3="2" /> <part id="tab_bottom_active" x="16" y="124" w1="4" w2="7" w3="5" h1="2" h2="1" h3="2" />
<part id="tab_bottom_normal" x="2" y="124" w="12" h="5" /> <part id="tab_bottom_normal" x="2" y="124" w1="4" w2="5" w3="3" h1="2" h2="1" h3="2" />
<part id="tab_filler" x="0" y="112" w="2" h="12" /> <part id="tab_filler" x="0" y="112" w="2" h="12" />
<part id="tab_modified_icon_normal" x="32" y="112" w="5" h="5" /> <part id="tab_modified_icon_normal" x="32" y="112" w="5" h="5" />
<part id="tab_modified_icon_active" x="32" y="117" w="5" h="5" /> <part id="tab_modified_icon_active" x="32" y="117" w="5" h="5" />
@ -370,6 +370,7 @@
<part id="icon_close" x="152" y="256" w="7" h="7" /> <part id="icon_close" x="152" y="256" w="7" h="7" />
<part id="icon_search" x="160" y="256" w="8" h="8" /> <part id="icon_search" x="160" y="256" w="8" h="8" />
<part id="icon_user_data" x="168" y="256" w="8" h="8" /> <part id="icon_user_data" x="168" y="256" w="8" h="8" />
<part id="icon_layout" x="176" y="256" w="5" h="6" />
<part id="icon_pos" x="144" y="264" w="8" h="8" /> <part id="icon_pos" x="144" y="264" w="8" h="8" />
<part id="icon_size" x="152" y="264" w="8" h="8" /> <part id="icon_size" x="152" y="264" w="8" h="8" />
<part id="icon_selsize" x="160" y="264" w="8" h="8" /> <part id="icon_selsize" x="160" y="264" w="8" h="8" />
@ -481,25 +482,25 @@
<icon part="window_close_icon" color="button_hot_text" state="mouse" /> <icon part="window_close_icon" color="button_hot_text" state="mouse" />
<icon part="window_close_icon" color="button_selected_text" state="selected" /> <icon part="window_close_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_center_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_center_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_center_icon" color="button_normal_text" /> <icon part="window_center_icon" color="button_normal_text" />
<icon part="window_center_icon" color="button_hot_text" state="mouse" /> <icon part="window_center_icon" color="button_hot_text" state="mouse" />
<icon part="window_center_icon" color="button_selected_text" state="selected" /> <icon part="window_center_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_play_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_play_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_play_icon" color="button_normal_text" /> <icon part="window_play_icon" color="button_normal_text" />
<icon part="window_play_icon" color="button_hot_text" state="mouse" /> <icon part="window_play_icon" color="button_hot_text" state="mouse" />
<icon part="window_play_icon" color="button_selected_text" state="selected" /> <icon part="window_play_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_stop_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_stop_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_stop_icon" color="button_normal_text" /> <icon part="window_stop_icon" color="button_normal_text" />
<icon part="window_stop_icon" color="button_hot_text" state="mouse" /> <icon part="window_stop_icon" color="button_hot_text" state="mouse" />
<icon part="window_stop_icon" color="button_selected_text" state="selected" /> <icon part="window_stop_icon" color="button_selected_text" state="selected" />
</style> </style>
<style id="window_help_button" extends="window_button" margin-top="3" margin-right="2"> <style id="window_help_button" extends="window_button" margin-top="3" margin-right="1">
<newlayer /> <newlayer />
<icon part="window_help_icon" color="button_normal_text" /> <icon part="window_help_icon" color="button_normal_text" />
<icon part="window_help_icon" color="button_hot_text" state="mouse" /> <icon part="window_help_icon" color="button_hot_text" state="mouse" />
@ -543,10 +544,13 @@
<text color="disabled" align="left" state="disabled" /> <text color="disabled" align="left" state="disabled" />
</style> </style>
<style id="mini_label" extends="label" font="mini" /> <style id="mini_label" extends="label" font="mini" />
<style id="warning_label" extends="label">
<icon part="warning_box" align="right" />
</style>
<style id="list_header_label" padding="2"> <style id="list_header_label" padding="2">
<text color="text" align="left" x="2" /> <text color="text" align="left" x="2" />
</style> </style>
<style id="link"> <style id="link" padding="1">
<text color="link_text" align="left" /> <text color="link_text" align="left" />
<text color="link_hover" align="left" state="mouse" /> <text color="link_hover" align="left" state="mouse" />
</style> </style>
@ -699,10 +703,11 @@
<style id="workspace_splitter"> <style id="workspace_splitter">
<background color="workspace" /> <background color="workspace" />
</style> </style>
<style id="horizontal_separator" border-left="2" border-top="4" border-right="2" border-bottom="0"> <style id="horizontal_separator" border="2">
<background color="window_face" /> <background color="window_face" />
<background-border part="separator_horz" align="middle" /> <background-border part="separator_horz" align="middle" />
<text color="separator_label" x="4" align="left middle" /> <text color="separator_label" x="4" align="left middle" />
<text color="disabled" x="4" align="left middle" state="disabled"/>
</style> </style>
<style id="menu_separator" extends="horizontal_separator" /> <style id="menu_separator" extends="horizontal_separator" />
<style id="separator_in_view" extends="horizontal_separator"> <style id="separator_in_view" extends="horizontal_separator">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -32,14 +32,16 @@
<font name="Aseprite" <font name="Aseprite"
type="spritesheet" type="spritesheet"
descent="2"
file="aseprite_font.png"> file="aseprite_font.png">
<fallback font="Unicode" size="8" /> <fallback font="Unicode" size="14" />
</font> </font>
<font name="Aseprite Mini" <font name="Aseprite Mini"
type="spritesheet" type="spritesheet"
descent="1"
file="aseprite_mini.png"> file="aseprite_mini.png">
<fallback font="Unicode" size="6" /> <fallback font="Unicode" size="10" />
</font> </font>
</fonts> </fonts>

View File

@ -175,6 +175,7 @@
<param name="popup" value="background" /> <param name="popup" value="background" />
</key> </key>
<key command="ShowExtras" shortcut="Ctrl+H" /> <key command="ShowExtras" shortcut="Ctrl+H" />
<key command="ToggleWorkspaceLayout" shortcut="Shift+W" />
<!-- Tabs --> <!-- Tabs -->
<key command="GotoNextTab" shortcut="Ctrl+Tab" /> <key command="GotoNextTab" shortcut="Ctrl+Tab" />
<key command="GotoPreviousTab" shortcut="Ctrl+Shift+Tab" /> <key command="GotoPreviousTab" shortcut="Ctrl+Shift+Tab" />
@ -1002,6 +1003,7 @@
</menu> </menu>
<menu text="@.view" id="view_menu"> <menu text="@.view" id="view_menu">
<item command="DuplicateView" text="@.view_duplicate_view" group="view_new" /> <item command="DuplicateView" text="@.view_duplicate_view" group="view_new" />
<item command="ToggleWorkspaceLayout" text="@.view_workspace_layout" />
<separator /> <separator />
<item command="ShowExtras" text="@.view_show_extras" /> <item command="ShowExtras" text="@.view_show_extras" />
<menu text="@.view_show" group="view_extras"> <menu text="@.view_show" group="view_extras">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2018-2024 Igara Studio S.A. --> <!-- Copyright (C) 2018-2025 Igara Studio S.A. -->
<!-- Copyright (C) 2014-2018 David Capello --> <!-- Copyright (C) 2014-2018 David Capello -->
<preferences> <preferences>
@ -167,6 +167,7 @@
<option id="keep_closed_sprite_on_memory_for" type="double" default="15.0" /> <option id="keep_closed_sprite_on_memory_for" type="double" default="15.0" />
<option id="show_full_path" type="bool" default="true" /> <option id="show_full_path" type="bool" default="true" />
<option id="edit_full_path" type="bool" default="false" /> <option id="edit_full_path" type="bool" default="false" />
<option id="workspace_layout" type="std::string" />
<option id="timeline_position" type="TimelinePosition" default="TimelinePosition::BOTTOM" /> <option id="timeline_position" type="TimelinePosition" default="TimelinePosition::BOTTOM" />
<option id="timeline_layer_panel_width" type="int" default="100" /> <option id="timeline_layer_panel_width" type="int" default="100" />
<option id="show_menu_bar" type="bool" default="true" /> <option id="show_menu_bar" type="bool" default="true" />
@ -255,6 +256,7 @@
<option id="one_finger_as_mouse_movement" type="bool" default="true" /> <option id="one_finger_as_mouse_movement" type="bool" default="true" />
<option id="load_wintab_driver" type="bool" default="false" /> <option id="load_wintab_driver" type="bool" default="false" />
<option id="flash_layer" type="bool" default="false" /> <option id="flash_layer" type="bool" default="false" />
<option id="use_selection_tool_loop" type="bool" default="false" />
<option id="nonactive_layers_opacity" type="int" default="255" /> <option id="nonactive_layers_opacity" type="int" default="255" />
<option id="nonactive_layers_opacity_preview" type="int" default="255" /> <option id="nonactive_layers_opacity_preview" type="int" default="255" />
</section> </section>
@ -356,6 +358,7 @@
<section id="file_selector"> <section id="file_selector">
<option id="current_folder" type="std::string" default="&quot;&lt;empty&gt;&quot;" /> <option id="current_folder" type="std::string" default="&quot;&lt;empty&gt;&quot;" />
<option id="zoom" type="double" default="1.0" /> <option id="zoom" type="double" default="1.0" />
<option id="show_hidden" type="bool" default="false" />
</section> </section>
<section id="text_tool"> <section id="text_tool">
<option id="font_face" type="std::string" /> <option id="font_face" type="std::string" />
@ -551,6 +554,7 @@
<option id="enabled" type="bool" default="false" /> <option id="enabled" type="bool" default="false" />
<option id="overlay_enabled" type="bool" default="false" /> <option id="overlay_enabled" type="bool" default="false" />
<option id="overlay_size" type="int" default="5" /> <option id="overlay_size" type="int" default="5" />
<option id="scale_up_to_fit" type="bool" default="false" />
</section> </section>
<section id="onionskin"> <section id="onionskin">
<option id="active" type="bool" default="false" /> <option id="active" type="bool" default="false" />

View File

@ -1,5 +1,5 @@
# Aseprite # Aseprite
# Copyright (C) 2018-2024 Igara Studio S.A. # Copyright (C) 2018-2025 Igara Studio S.A.
# Copyright (C) 2016-2018 David Capello # Copyright (C) 2016-2018 David Capello
# #
# This work is licensed under the Creative Commons Attribution 4.0 # This work is licensed under the Creative Commons Attribution 4.0
@ -43,7 +43,6 @@ locked_layers = There are locked layers
no_active_layers = There is no active layer no_active_layers = There is no active layer
layer_x_is_hidden = Layer "{}" is hidden layer_x_is_hidden = Layer "{}" is hidden
unmodifiable_reference_layer = Layer "{}" is reference, cannot be modified unmodifiable_reference_layer = Layer "{}" is reference, cannot be modified
filter_no_unlocked_layer = No unlocked layers to apply filter
cannot_move_bg_layer = The background layer cannot be moved cannot_move_bg_layer = The background layer cannot be moved
nothing_to_move = Nothing to move nothing_to_move = Nothing to move
recovery_task_using_sprite = Sprite is used by a backup/data recovery task recovery_task_using_sprite = Sprite is used by a backup/data recovery task
@ -489,6 +488,7 @@ TilesetDuplicate = Duplicate Tileset
Undo = Undo Undo = Undo
UndoHistory = Undo History UndoHistory = Undo History
UnlinkCel = Unlink Cel UnlinkCel = Unlink Cel
ToggleWorkspaceLayout = Toggle Workspace Layout
Zoom = Zoom Zoom = Zoom
Zoom_In = Zoom In Zoom_In = Zoom In
Zoom_Out = Zoom Out Zoom_Out = Zoom Out
@ -622,6 +622,14 @@ current_layer = Current Layer
first_ref_layer = First Reference Layer first_ref_layer = First Reference Layer
pick = Pick: pick = Pick:
sample = Sample: sample = Sample:
position_label = P:
rotation_label = R:
position_x = X Position
position_y = Y Position
size_width = Width
size_height = Height
rotation_angle = Angle
rotation_skew = Skew
[convolution_matrix] [convolution_matrix]
reload_stock = &Reload Stock reload_stock = &Reload Stock
@ -766,6 +774,7 @@ pinned_folders = Pinned Folders
recent_folders = Recent Folders recent_folders = Recent Folders
all_formats = All formats all_formats = All formats
all_files = All files all_files = All files
show_hidden = Show hidden
[filters] [filters]
selected_cels = Selected selected_cels = Selected
@ -786,6 +795,11 @@ load = Load External Font
select_truetype_fonts = Select a Font File select_truetype_fonts = Select a Font File
empty_fonts = No system fonts were found empty_fonts = No system fonts were found
[font_style]
antialias = Antialias
hinting = Hinting
ligatures = Ligatures
[frame_combo] [frame_combo]
all_frames = All frames all_frames = All frames
selected_frames = Selected frames selected_frames = Selected frames
@ -813,6 +827,12 @@ opacity = Opacity:
tolerance = Tolerance: tolerance = Tolerance:
show_more = Show more... show_more = Show more...
[general_text]
copy = &Copy
cut = Cu&t
paste = &Paste
select_all = Select &All
[gif_options] [gif_options]
title = GIF Options title = GIF Options
general_options = General Options: general_options = General Options:
@ -981,6 +1001,7 @@ visible_layers = Visible layers
[layer_properties] [layer_properties]
title = Layer Properties title = Layer Properties
name = Name: name = Name:
uuid = UUID:
mode = Mode: mode = Mode:
opacity = Opacity: opacity = Opacity:
tileset_tooltip = Tileset tileset_tooltip = Tileset
@ -1155,6 +1176,7 @@ select_load_from_file = &Load from MSK file
select_save_to_file = &Save to MSK file select_save_to_file = &Save to MSK file
view = &View view = &View
view_duplicate_view = Duplicate &View view_duplicate_view = Duplicate &View
view_workspace_layout = Workspace &Layout
view_show_extras = &Extras view_show_extras = &Extras
view_show = &Show view_show = &Show
view_show_layer_edges = &Layer Edges view_show_layer_edges = &Layer Edges
@ -1196,6 +1218,14 @@ help_twitter = Twitter
help_enter_license = Enter &License help_enter_license = Enter &License
help_about = &About help_about = &About
[main_window]
layout = Workspace Layout
default_layout = Default
mirrored_default_layout = Mirrored Default
timeline = Timeline
user_layouts = User Layouts
new_layout = New Layout...
[mask_by_color] [mask_by_color]
title = Select Color title = Select Color
label_color = Color: label_color = Color:
@ -1224,6 +1254,20 @@ name = Name:
tileset = Tileset: tileset = Tileset:
default_new_layer_name = New Layer default_new_layer_name = New Layer
[new_layout]
title = New Workspace Layout
name = Name:
deleting_layout = Deleting Layout
deleting_layout_confirmation = Are you sure you want to delete the '{}' layout?
restoring_layout = Restoring Layout
restoring_layout_confirmation = Are you sure you want to restore the '{}' layout?
[dock]
left = Dock Left
right = Dock Right
top = Dock Top
bottom = Dock Bottom
[news_listbox] [news_listbox]
more = More... more = More...
problem_loading = Problems loading news. Please retry. problem_loading = Problems loading news. Please retry.
@ -1326,7 +1370,7 @@ theme_mode = Theme Mode:
screen_scaling = Screen Scaling: screen_scaling = Screen Scaling:
ui_scaling = UI Element Scaling: ui_scaling = UI Element Scaling:
language = Language: language = Language:
download_translations = Download Translations font_warning = Customize font for this language
gpu_acceleration = GPU acceleration [DEVMODE/INTERNAL TESTING ONLY] gpu_acceleration = GPU acceleration [DEVMODE/INTERNAL TESTING ONLY]
gpu_acceleration_tooltip = Check this option to enable hardware acceleration gpu_acceleration_tooltip = Check this option to enable hardware acceleration
show_menu_bar = Show Aseprite menu bar show_menu_bar = Show Aseprite menu bar
@ -1513,6 +1557,12 @@ alpha_range = Alpha Range:
opacity_range = Opacity Range: opacity_range = Opacity Range:
8bit_value = 0-255 8bit_value = 0-255
percentage = 0%-100% percentage = 0%-100%
customize_theme = Customize Theme
font = Font:
mini_font = Mini Font:
preview = Preview:
font_preview = Font Preview
mini_font_preview = Mini Font Preview
available_themes = Available Themes available_themes = Available Themes
extension_themes = Extension Themes extension_themes = Extension Themes
select_theme = &Select select_theme = &Select
@ -1550,11 +1600,13 @@ set_cursor_fix = Set cursor position from stylus location
set_cursor_fix_tooltip = Sets the mouse position to the pen location when\nyou have two pointers available (e.g. mouse and pen)\n\nUseful to zoom in/out from the pen position and to get the\ncorrect cursor location when screencasting/live streaming. set_cursor_fix_tooltip = Sets the mouse position to the pen location when\nyou have two pointers available (e.g. mouse and pen)\n\nUseful to zoom in/out from the pen position and to get the\ncorrect cursor location when screencasting/live streaming.
wintab_more_info = (More Information) wintab_more_info = (More Information)
flash_selected_layer = Flash layer when it is selected flash_selected_layer = Flash layer when it is selected
use_selection_tool_loop = New selection tools implementation
non_active_layer_opacity = Opacity for non-active layers: non_active_layer_opacity = Opacity for non-active layers:
cel_content_format = Cel content format: cel_content_format = Cel content format:
cel_format_compressed = Compressed cel_format_compressed = Compressed
cel_format_keep = Keep format as is in file cel_format_keep = Keep format as is in file
cel_format_raw = Raw image cel_format_raw = Raw image
cel_format_raw_warning = This will increase .aseprite file sizes considerably
reset_title = Reset Preferences reset_title = Reset Preferences
reset_default = Reset configuration options available in the Preferences window reset_default = Reset configuration options available in the Preferences window
reset_tools = Reset all tool preferences reset_tools = Reset all tool preferences
@ -1659,6 +1711,11 @@ n_slices_removed = {} slice(s) removed
x_removed = Layer "{}" removed x_removed = Layer "{}" removed
layers_removed = Layers removed layers_removed = Layers removed
[resource_listbox]
loading = Loading
pin = Pin this item
unpin = Unpin this item
[save_file] [save_file]
title = Save File title = Save File
save_as = Save As save_as = Save As
@ -1675,7 +1732,8 @@ title = Save Selection (.msk file)
[script_access] [script_access]
title = Security title = Security
script_label = The following script: script_label = The following script:
file_label = wants to access to this file: file_label = wants to access this file:
file_write_label = wants to write to this file:
command_label = wants to execute the following command: command_label = wants to execute the following command:
websocket_label = wants to open a WebSocket connection to this URL: websocket_label = wants to open a WebSocket connection to this URL:
clipboard_label = wants to access the system clipboard clipboard_label = wants to access the system clipboard
@ -1689,7 +1747,7 @@ allow_load_lib_access = &Allow Load External Library
give_full_access = Give Script Full &Access give_full_access = Give Script Full &Access
stop_script = &Stop Script stop_script = &Stop Script
[select_accelerator] [select_shortcut]
title = Keyboard Shortcut title = Keyboard Shortcut
key = Key: key = Key:
clear = Clear clear = Clear
@ -1769,6 +1827,8 @@ change_sprite_props = Change Sprite Properties
tilesets = Tilesets tilesets = Tilesets
delete_tileset = Delete delete_tileset = Delete
duplicate_tileset = Duplicate duplicate_tileset = Duplicate
use_uuid_for_layers = Create UUID for layers
use_uuid_for_layers_tooltip = By checking this an UUID (Universally Unique Identifiers)\nwill be automatically assigned to each layer\nand saved in .aseprite files
[sprite_size] [sprite_size]
title = Sprite Size title = Sprite Size
@ -1814,16 +1874,26 @@ pixel_scale = Pixel Scale
with_vars = Use CSS3 Variables with_vars = Use CSS3 Variables
generate_html = Generate Sample HTML File generate_html = Generate Sample HTML File
[shape]
fill = Fill
stroke = Stroke
stroke_width = Stroke Width
[text_tool]
font_family = Font Family
font_size = Font Size
bold = Bold
italic = Italic
more_options = More Options
[timeline_conf] [timeline_conf]
position = Position: position = Position:
left = &Left
right = &Right
bottom = &Bottom
frame_header = Frame Header: frame_header = Frame Header:
first_frame = First Frame: first_frame = First Frame:
thumbnails = Thumbnails thumbnails = Thumbnails
thumbnail_size = Thumbnail Size: thumbnail_size = Thumbnail Size:
overlay_size = Overlay Size: overlay_size = Overlay Size:
scale_up_to_fit = Scale up to fit
onion_skin = Onion Skin: onion_skin = Onion Skin:
merge_frames = Merge Frames merge_frames = Merge Frames
red_blue_tint = Red/Blue Tint red_blue_tint = Red/Blue Tint
@ -1836,6 +1906,7 @@ behind_sprite = Behind sprite
behind_sprite_toolip = Only for transparent layers.\nBackground is not included in this onion skin mode. behind_sprite_toolip = Only for transparent layers.\nBackground is not included in this onion skin mode.
in_front = In front of sprite in_front = In front of sprite
in_front_toolip = For all kind of layers (background and transparent) in_front_toolip = For all kind of layers (background and transparent)
set_as_defaults = Set as Defaults
[tools] [tools]
rectangular_marquee = Rectangular Marquee Tool rectangular_marquee = Rectangular Marquee Tool
@ -1874,6 +1945,7 @@ timeline_show = Show Timeline
[undo_history] [undo_history]
title = Undo History title = Undo History
initial_state = Initial State
[user_data] [user_data]
user_data = User Data: user_data = User Data:

View File

@ -1,29 +1,14 @@
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2018-2024 Igara Studio S.A. --> <!-- Copyright (C) 2018-2025 Igara Studio S.A. -->
<!-- Copyright (C) 2018 David Capello --> <!-- Copyright (C) 2018 David Capello -->
<gui i18nwarnings="false"> <gui i18nwarnings="false">
<window id="about" text="About Aseprite"> <window id="about" text="About Aseprite">
<vbox> <vbox>
<label text="" id="title" /> <label text="" id="title" />
<label text="Animated sprite editor &amp;&amp; pixel art tool" /> <label text="Animated sprite editor &amp;&amp; pixel art tool" />
<hbox homogeneous="true"> <link text="Authors &amp;&amp; Credits" url="" id="credits" />
<hbox> <link text="Translators" url="" id="i18n_credits" />
<vbox expansive="true"> <link text="Open Source Projects" url="" id="licenses" />
<separator text="Developer Team" horizontal="true" />
<link text="David Capello" url="https://twitter.com/davidcapello" />
<link text="Gaspar Capello" url="https://twitter.com/Gasparoken" />
<link text="Martín Capello" url="https://twitter.com/martincapell0" />
<vbox minheight="8" />
</vbox>
<separator vertical="true" />
</hbox>
<vbox>
<separator text="Credits" horizontal="true" cell_hspan="2" />
<link text="Contributors" url="" id="credits" />
<link text="Translators" url="" id="i18n_credits" />
<link text="Open Source Projects" url="" id="licenses" />
</vbox>
</hbox>
<separator horizontal="true" /> <separator horizontal="true" />
<hbox> <hbox>
<label text="Copyright (C) 2001-2025" /> <label text="Copyright (C) 2001-2025" />

View File

@ -23,6 +23,7 @@
<combobox id="location" expansive="true" /> <combobox id="location" expansive="true" />
<button text="" id="refresh_button" style="refresh_button" <button text="" id="refresh_button" style="refresh_button"
tooltip="@.refresh_button_tooltip" tooltip_dir="bottom" /> tooltip="@.refresh_button_tooltip" tooltip_dir="bottom" />
<check id="show_hidden_check" text="@.show_hidden" />
</box> </box>
<vbox id="file_view_placeholder" expansive="true" /> <vbox id="file_view_placeholder" expansive="true" />
<grid columns="2"> <grid columns="2">

View File

@ -0,0 +1,10 @@
<!-- Aseprite -->
<!-- Copyright (C) 2025 by Igara Studio S.A. -->
<gui>
<vbox id="font_style">
<check id="antialias" text="@.antialias" />
<check id="hinting" text="@.hinting" />
<separator horizontal="true" />
<check id="ligatures" text="@.ligatures" />
</vbox>
</gui>

View File

@ -15,6 +15,9 @@
<label text="@.opacity" /> <label text="@.opacity" />
<opacityslider id="opacity" width="128" cell_align="horizontal" cell_hspan="2" /> <opacityslider id="opacity" width="128" cell_align="horizontal" cell_hspan="2" />
<label id="uuid_label" text="@.uuid" visible="false" />
<entry id="uuid" readonly="true" maxsize="36" cell_hspan="2" visible="false"/>
</grid> </grid>
</vbox> </vbox>
</window> </window>

View File

@ -1,30 +0,0 @@
<!-- Aseprite -->
<!-- Copyright (C) 2001-2017 by David Capello -->
<gui>
<window id="main_window" noborders="true" desktop="true">
<vbox noborders="true" expansive="true">
<hbox noborders="true" id="menu_bar_placeholder" />
<hbox noborders="true" id="tabs_placeholder" />
<splitter id="color_bar_splitter"
horizontal="true" expansive="true"
by="pixel"
style="workspace_splitter">
<vbox noborders="true" id="color_bar_placeholder" />
<vbox noborders="true" expansive="true">
<vbox noborders="true" id="context_bar_placeholder" />
<hbox noborders="true" expansive="true">
<splitter id="timeline_splitter"
vertical="true" expansive="true"
by="percetage" position="100"
style="workspace_splitter">
<hbox noborders="true" id="workspace_placeholder" expansive="true" />
<vbox noborders="true" id="timeline_placeholder" expansive="true" />
</splitter>
<vbox noborders="true" id="tool_bar_placeholder" />
</hbox>
</vbox>
</splitter>
<hbox noborders="true" id="status_bar_placeholder" />
</vbox>
</window>
</gui>

View File

@ -0,0 +1,19 @@
<!-- Aseprite -->
<!-- Copyright (C) 2025 by Igara Studio S.A. -->
<gui>
<window id="new_layout" text="@.title">
<vbox>
<hbox>
<label text="@.name" />
<entry maxsize="128" id="name" magnet="true" expansive="true" />
</hbox>
<separator horizontal="true" />
<hbox homogeneous="true" cell_align="right">
<button text="@general.ok" closewindow="true" id="ok" disabled="true" minwidth="60" magnet="true" />
<button text="@general.cancel" closewindow="true" />
</hbox>
</vbox>
</window>
</gui>

View File

@ -1,5 +1,6 @@
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2018-2024 Igara Studio S.A. --> <!-- Copyright (C) 2018-2025 Igara Studio S.A. -->
<!-- Copyright (C) 2001-2018 David Capello --> <!-- Copyright (C) 2001-2018 David Capello -->
<gui> <gui>
<window id="options" text="@.title" help="preferences"> <window id="options" text="@.title" help="preferences">
@ -34,7 +35,7 @@
<!-- General --> <!-- General -->
<vbox id="section_general"> <vbox id="section_general">
<separator text="@.section_general" horizontal="true" /> <separator text="@.section_general" horizontal="true" />
<grid columns="3"> <grid columns="2">
<label text="@.ui_windows" /> <label text="@.ui_windows" />
<hbox> <hbox>
<buttonset columns="2" id="ui_windows"> <buttonset columns="2" id="ui_windows">
@ -45,7 +46,6 @@
<label text="@.theme_mode" /> <label text="@.theme_mode" />
</hbox> </hbox>
</hbox> </hbox>
<boxfiller />
<label text="@.screen_scaling" /> <label text="@.screen_scaling" />
<combobox id="screen_scale"> <combobox id="screen_scale">
@ -54,7 +54,6 @@
<listitem text="300%" value="3" /> <listitem text="300%" value="3" />
<listitem text="400%" value="4" /> <listitem text="400%" value="4" />
</combobox> </combobox>
<boxfiller />
<label text="@.ui_scaling" /> <label text="@.ui_scaling" />
<combobox id="ui_scale"> <combobox id="ui_scale">
@ -63,12 +62,14 @@
<listitem text="300%" value="3" /> <listitem text="300%" value="3" />
<listitem text="400%" value="4" /> <listitem text="400%" value="4" />
</combobox> </combobox>
<boxfiller />
<label text="@.language" /> <link text="@.language" url="https://www.aseprite.org/languages/" />
<combobox id="language" /> <combobox id="language" />
<link text="@.download_translations" url="https://www.aseprite.org/languages/" />
<boxfiller id="font_warning_filler" visible="false" />
<link text="@.font_warning" id="font_warning" visible="false" />
</grid> </grid>
<separator horizontal="true" />
<check id="gpu_acceleration" <check id="gpu_acceleration"
text="@.gpu_acceleration" text="@.gpu_acceleration"
tooltip="@.gpu_acceleration_tooltip" /> tooltip="@.gpu_acceleration_tooltip" />
@ -545,6 +546,20 @@
<!-- Theme --> <!-- Theme -->
<vbox id="section_theme"> <vbox id="section_theme">
<separator text="@.customize_theme" horizontal="true" />
<grid columns="2">
<label text="@.preview" />
<vbox cell_align="horizontal" >
<label id="font_preview" text="@.font_preview" />
<label id="mini_font_preview" text="@.mini_font_preview" />
</vbox>
<check id="custom_theme_font" text="@.font" />
<font id="theme_font" cell_align="horizontal" />
<check id="custom_mini_font" text="@.mini_font" />
<font id="theme_mini_font" cell_align="horizontal" />
</grid>
<separator text="@.available_themes" horizontal="true" /> <separator text="@.available_themes" horizontal="true" />
<view expansive="true" maxsize="true"> <view expansive="true" maxsize="true">
<listbox id="theme_list" /> <listbox id="theme_list" />
@ -581,6 +596,10 @@
<listitem text="@.cel_format_keep" /> <listitem text="@.cel_format_keep" />
<listitem text="@.cel_format_raw" /> <listitem text="@.cel_format_raw" />
</combobox> </combobox>
<boxfiller />
<label id="cel_format_warning"
text="@.cel_format_raw_warning" style="warning_label" />
</grid> </grid>
</vbox> </vbox>
@ -613,6 +632,7 @@
text="@.hue_with_sat_value" text="@.hue_with_sat_value"
pref="experimental.hue_with_sat_value_for_color_selector" /> pref="experimental.hue_with_sat_value_for_color_selector" />
<check id="flash_layer" text="@.flash_selected_layer" /> <check id="flash_layer" text="@.flash_selected_layer" />
<check id="use_selection_tool_loop" text="@.use_selection_tool_loop" />
<hbox> <hbox>
<label text="@.non_active_layer_opacity" /> <label text="@.non_active_layer_opacity" />
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" /> <slider id="nonactive_layers_opacity" min="0" max="255" width="128" />

View File

@ -1,7 +1,8 @@
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2025 by Igara Studio S.A. -->
<!-- Copyright (C) 2001-2016 by David Capello --> <!-- Copyright (C) 2001-2016 by David Capello -->
<gui> <gui>
<window id="select_accelerator" text="@.title"> <window id="select_shortcut" text="@.title">
<vbox expansive="true"> <vbox expansive="true">
<grid columns="3"> <grid columns="3">
<label text="@.key" /> <label text="@.key" />

View File

@ -42,6 +42,8 @@
<button id="convert_color_profile" text="@.convert" /> <button id="convert_color_profile" text="@.convert" />
</hbox> </hbox>
</hbox> </hbox>
<check text="@.use_uuid_for_layers" id="use_uuid_for_layers" tooltip="@.use_uuid_for_layers_tooltip" cell_hspan="2" />
</grid> </grid>
<vbox expansive="true" id="tilesets_placeholder"> <vbox expansive="true" id="tilesets_placeholder">

View File

@ -1,17 +1,12 @@
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2025 Igara Studio S.A. -->
<!-- Copyright (C) 2014-2018 by David Capello --> <!-- Copyright (C) 2014-2018 by David Capello -->
<gui> <gui>
<vbox id="timeline_conf"> <vbox id="timeline_conf">
<hbox> <hbox>
<vbox> <vbox>
<separator cell_hspan="2" text="@.position" left="true" horizontal="true" /> <separator cell_hspan="2" text="@.position" left="true" horizontal="true" />
<hbox> <button id="layout" icon="icon_layout" />
<buttonset columns="2" id="position">
<item text="@.left" />
<item text="@.right" />
<item text="@.bottom" hspan="2" />
</buttonset>
</hbox>
</vbox> </vbox>
<vbox> <vbox>
<separator text="@.frame_header" left="true" horizontal="true" /> <separator text="@.frame_header" left="true" horizontal="true" />
@ -30,6 +25,7 @@
<check id="thumb_overlay_enabled" text="@.overlay_size"/> <check id="thumb_overlay_enabled" text="@.overlay_size"/>
<slider min="2" max="10" id="thumb_overlay_size" cell_align="horizontal" width="128" /> <slider min="2" max="10" id="thumb_overlay_size" cell_align="horizontal" width="128" />
<check id="thumb_scale_up_to_fit" text="@.scale_up_to_fit" cell_hspan="2" />
</grid> </grid>
</vbox> </vbox>
</hbox> </hbox>
@ -55,5 +51,11 @@
<radio group="2" text="@.in_front" id="infront" tooltip="@.in_front_toolip" /> <radio group="2" text="@.in_front" id="infront" tooltip="@.in_front_toolip" />
</hbox> </hbox>
</grid> </grid>
<separator horizontal="true" />
<hbox>
<boxfiller />
<button id="defaults" text="@.set_as_defaults" />
</hbox>
</vbox> </vbox>
</gui> </gui>

View File

@ -1,5 +1,34 @@
Aseprite uses the following open source projects: Aseprite uses the following open source projects:
* [Allegro 4](http://liballeg.org/)
* [Bresenham algorithm implementations by Alois Zingl](http://members.chello.at/easyfilter/bresenham.html)
* [cityhash](https://github.com/google/cityhash)
* [cmark](https://github.com/jgm/cmark)
* [curl](http://curl.haxx.se/)
* [fmt](https://github.com/fmtlib/fmt)
* [FreeType](http://www.freetype.org/)
* [giflib](http://sourceforge.net/projects/giflib/)
* [Google Test](https://github.com/google/googletest)
* [harfbuzz](http://harfbuzz.org)
* [IXWebSocket](https://github.com/machinezone/IXWebSocket)
* [json11](https://github.com/dropbox/json11/)
* [libarchive](http://www.libarchive.org/)
* [libjpeg-turbo](https://libjpeg-turbo.org/)
* [libpng](http://www.libpng.org/pub/png/)
* [libwebp](https://developers.google.com/speed/webp/)
* [Lua](https://www.lua.org/)
* [pixman](http://www.pixman.org/)
* [qoi](https://github.com/phoboslab/qoi)
* [Sentry](https://sentry.io)
* [skia](https://skia.org)
* [simpleini](https://github.com/aseprite/simpleini/)
* [TinyEXIF](https://github.com/cdcseacave/TinyEXIF)
* [tinyexpr](https://github.com/codeplea/tinyexpr)
* [tinyxml2](https://github.com/leethomason/tinyxml2)
* [ucdn](https://github.com/grigorig/ucdn)
* [Wintab API](http://www.wacomeng.com/windows/docs/WintabBackground.htm)
* [zlib](http://www.zlib.net/)
# [Allegro 4](http://liballeg.org/) # [Allegro 4](http://liballeg.org/)
``` ```

View File

@ -79,6 +79,7 @@ A 128-byte header (same as FLC/FLI header, but with other magic number):
1 = Layer opacity has valid value 1 = Layer opacity has valid value
2 = Layer blend mode/opacity is valid for groups 2 = Layer blend mode/opacity is valid for groups
(composite groups separately first when rendering) (composite groups separately first when rendering)
4 = Layers have an UUID
WORD Speed (milliseconds between frame, like in FLC files) WORD Speed (milliseconds between frame, like in FLC files)
DEPRECATED: You should use the frame duration field DEPRECATED: You should use the frame duration field
from each frame header from each frame header
@ -202,6 +203,8 @@ entire layers layout:
STRING Layer name STRING Layer name
+ If layer type = 2 + If layer type = 2
DWORD Tileset index DWORD Tileset index
+ If file header flags have bit 4:
UUID Layer's universally unique identifier
### Cel Chunk (0x2005) ### Cel Chunk (0x2005)

2
laf

@ -1 +1 @@
Subproject commit 339a0fa13584853bda8559de486e715e743a5763 Subproject commit 01571537bc6002a2e039a66497837365c394d7fa

View File

@ -1,5 +1,5 @@
# Aseprite # Aseprite
# Copyright (C) 2019-2024 Igara Studio S.A. # Copyright (C) 2019-2025 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello # Copyright (C) 2001-2018 David Capello
###################################################################### ######################################################################
@ -152,6 +152,12 @@ add_custom_command(
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/../README.md) MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/../README.md)
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/README.md) list(APPEND out_data_files ${DATA_OUTPUT_DIR}/README.md)
add_custom_command(
OUTPUT ${DATA_OUTPUT_DIR}/AUTHORS.md
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS.md ${DATA_OUTPUT_DIR}/AUTHORS.md
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS.md)
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/AUTHORS.md)
add_custom_command( add_custom_command(
OUTPUT ${DATA_OUTPUT_DIR}/EULA.txt OUTPUT ${DATA_OUTPUT_DIR}/EULA.txt
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../EULA.txt ${DATA_OUTPUT_DIR}/EULA.txt COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../EULA.txt ${DATA_OUTPUT_DIR}/EULA.txt
@ -174,8 +180,8 @@ if(ENABLE_ASEPRITE_EXE)
if(WIN32) if(WIN32)
set(main_resources set(main_resources
main/resources_win32.rc main/win/resources_win32.rc
main/settings.manifest) main/win/settings.manifest)
endif() endif()
add_executable(${main_target} add_executable(${main_target}

View File

@ -72,6 +72,10 @@ In Debug mode (`_DEBUG`):
* [`TRACEARGS`](https://github.com/aseprite/laf/blob/f3222bdee2d21556e9da55343e73803c730ecd97/base/debug.h#L40): * [`TRACEARGS`](https://github.com/aseprite/laf/blob/f3222bdee2d21556e9da55343e73803c730ecd97/base/debug.h#L40):
in debug mode, it prints in the terminal/console each given argument in debug mode, it prints in the terminal/console each given argument
In release mode you can use a similar function:
* `PRINTARGS` prints in the terminal/console each given argument
# Detect Platform # Detect Platform
You can check the platform using some `laf` macros: You can check the platform using some `laf` macros:

View File

@ -1,5 +1,5 @@
# Aseprite # Aseprite
# Copyright (C) 2018-2024 Igara Studio S.A. # Copyright (C) 2018-2025 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello # Copyright (C) 2001-2018 David Capello
# Generate a ui::Widget for each widget in a XML file # Generate a ui::Widget for each widget in a XML file
@ -98,17 +98,17 @@ add_library(app-lib ${generated_files})
# These specific-platform files should be in an external library # These specific-platform files should be in an external library
# (e.g. "base" or "os"). # (e.g. "base" or "os").
if(WIN32) if(WIN32)
target_sources(app-lib PRIVATE font_path_win.cpp) target_sources(app-lib PRIVATE fonts/font_path_win.cpp)
elseif(APPLE) elseif(APPLE)
target_sources(app-lib PRIVATE font_path_osx.mm) target_sources(app-lib PRIVATE fonts/font_path_osx.mm)
else() else()
target_sources(app-lib PRIVATE font_path_unix.cpp) target_sources(app-lib PRIVATE fonts/font_path_unix.cpp)
endif() endif()
# This defines a specific webp decoding utility function for using # This defines a specific webp decoding utility function for using
# in Windows when dragging and dropping images that are stored as # in Windows when dragging and dropping images that are stored as
# webp files (like Chrome does). # webp files (like Chrome does).
if(WIN32 AND ENABLE_WEBP) if(WIN32 AND ENABLE_WEBP AND LAF_BACKEND STREQUAL "skia")
target_sources(app-lib PRIVATE util/decode_webp.cpp) target_sources(app-lib PRIVATE util/decode_webp.cpp)
endif() endif()
@ -290,7 +290,6 @@ target_sources(app-lib PRIVATE
cmd/copy_region.cpp cmd/copy_region.cpp
cmd/crop_cel.cpp cmd/crop_cel.cpp
cmd/deselect_mask.cpp cmd/deselect_mask.cpp
cmd/drop_on_timeline.cpp
cmd/flatten_layers.cpp cmd/flatten_layers.cpp
cmd/flip_image.cpp cmd/flip_image.cpp
cmd/flip_mask.cpp cmd/flip_mask.cpp
@ -522,11 +521,13 @@ target_sources(app-lib PRIVATE
commands/tileset_mode.cpp commands/tileset_mode.cpp
commands/toggle_other_layers_opacity.cpp commands/toggle_other_layers_opacity.cpp
commands/toggle_play_option.cpp commands/toggle_play_option.cpp
commands/toggle_workspace_layout.cpp
console.cpp console.cpp
context.cpp context.cpp
context_flags.cpp context_flags.cpp
doc.cpp doc.cpp
doc_api.cpp doc_api.cpp
doc_api_dnd_helper.cpp
doc_diff.cpp doc_diff.cpp
doc_exporter.cpp doc_exporter.cpp
doc_range_ops.cpp doc_range_ops.cpp
@ -545,8 +546,10 @@ target_sources(app-lib PRIVATE
file_system.cpp file_system.cpp
filename_formatter.cpp filename_formatter.cpp
flatten.cpp flatten.cpp
font_info.cpp fonts/font_data.cpp
font_path.cpp fonts/font_info.cpp
fonts/font_path.cpp
fonts/fonts.cpp
gui_xml.cpp gui_xml.cpp
i18n/strings.cpp i18n/strings.cpp
i18n/xml_translator.cpp i18n/xml_translator.cpp
@ -608,6 +611,7 @@ target_sources(app-lib PRIVATE
ui/context_bar.cpp ui/context_bar.cpp
ui/dithering_selector.cpp ui/dithering_selector.cpp
ui/doc_view.cpp ui/doc_view.cpp
ui/dock.cpp
ui/drop_down_button.cpp ui/drop_down_button.cpp
ui/dynamics_popup.cpp ui/dynamics_popup.cpp
ui/editor/brush_preview.cpp ui/editor/brush_preview.cpp
@ -652,6 +656,9 @@ target_sources(app-lib PRIVATE
ui/input_chain.cpp ui/input_chain.cpp
ui/keyboard_shortcuts.cpp ui/keyboard_shortcuts.cpp
ui/layer_frame_comboboxes.cpp ui/layer_frame_comboboxes.cpp
ui/layout.cpp
ui/layouts.cpp
ui/layout_selector.cpp
ui/main_menu_bar.cpp ui/main_menu_bar.cpp
ui/main_window.cpp ui/main_window.cpp
ui/mini_help_button.cpp ui/mini_help_button.cpp
@ -668,9 +675,8 @@ target_sources(app-lib PRIVATE
ui/rgbmap_algorithm_selector.cpp ui/rgbmap_algorithm_selector.cpp
ui/sampling_selector.cpp ui/sampling_selector.cpp
ui/search_entry.cpp ui/search_entry.cpp
ui/select_accelerator.cpp ui/select_shortcut.cpp
ui/selection_mode_field.cpp ui/selection_mode_field.cpp
ui/skin/font_data.cpp
ui/skin/skin_part.cpp ui/skin/skin_part.cpp
ui/skin/skin_property.cpp ui/skin/skin_property.cpp
ui/skin/skin_slider_property.cpp ui/skin/skin_slider_property.cpp
@ -684,6 +690,7 @@ target_sources(app-lib PRIVATE
ui/tile_button.cpp ui/tile_button.cpp
ui/tileset_selector.cpp ui/tileset_selector.cpp
ui/timeline/ani_controls.cpp ui/timeline/ani_controls.cpp
ui/timeline/doc_providers.cpp
ui/timeline/timeline.cpp ui/timeline/timeline.cpp
ui/toolbar.cpp ui/toolbar.cpp
ui/user_data_view.cpp ui/user_data_view.cpp
@ -706,6 +713,7 @@ target_sources(app-lib PRIVATE
util/layer_utils.cpp util/layer_utils.cpp
util/msk_file.cpp util/msk_file.cpp
util/new_image_from_mask.cpp util/new_image_from_mask.cpp
util/open_file_job.cpp
util/pal_ops.cpp util/pal_ops.cpp
util/pic_file.cpp util/pic_file.cpp
util/pixel_ratio.cpp util/pixel_ratio.cpp

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -79,7 +79,7 @@
#include "os/x11/system.h" #include "os/x11/system.h"
#endif #endif
#if ENABLE_WEBP && LAF_WINDOWS #if ENABLE_WEBP && LAF_WINDOWS && LAF_SKIA
#include "app/util/decode_webp.h" #include "app/util/decode_webp.h"
#endif #endif
@ -485,7 +485,7 @@ void App::run(const bool runGuiManager)
// How to interpret one finger on Windows tablets. // How to interpret one finger on Windows tablets.
manager->display()->nativeWindow()->setInterpretOneFingerGestureAsMouseMovement( manager->display()->nativeWindow()->setInterpretOneFingerGestureAsMouseMovement(
preferences().experimental.oneFingerAsMouseMovement()); preferences().experimental.oneFingerAsMouseMovement());
#if ENABLE_WEBP #if ENABLE_WEBP && LAF_SKIA
// In Windows we use a custom webp decoder for drag & drop operations. // In Windows we use a custom webp decoder for drag & drop operations.
os::set_decode_webp(util::decode_webp); os::set_decode_webp(util::decode_webp);
#endif #endif

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2024 Igara Studio S.A. // Copyright (C) 2019-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -113,11 +113,11 @@ bool can_call_global_shortcut(const AppMenuItem::Native* native)
(focus == nullptr || focus->type() != ui::kEntryWidget || (focus == nullptr || focus->type() != ui::kEntryWidget ||
!is_text_entry_shortcut(native->shortcut)) && !is_text_entry_shortcut(native->shortcut)) &&
(native->keyContext == KeyContext::Any || (native->keyContext == KeyContext::Any ||
native->keyContext == KeyboardShortcuts::instance()->getCurrentKeyContext()); native->keyContext == KeyboardShortcuts::getCurrentKeyContext());
} }
// TODO this should be on "she" library (or we should use // TODO this should be on laf-os library (or we should use
// os::Shortcut instead of ui::Accelerators) // os::Shortcut instead of ui::Shortcuts)
int from_scancode_to_unicode(KeyScancode scancode) int from_scancode_to_unicode(KeyScancode scancode)
{ {
static int map[] = { static int map[] = {
@ -284,22 +284,21 @@ void destroy_menu_item(ui::Widget* item)
os::Shortcut get_os_shortcut_from_key(const Key* key) os::Shortcut get_os_shortcut_from_key(const Key* key)
{ {
if (key && !key->accels().empty()) { if (key && !key->shortcuts().empty()) {
const ui::Accelerator& accel = key->accels().front(); const ui::Shortcut& shortcut = key->shortcuts().front();
#if LAF_MACOS #if LAF_MACOS
// Shortcuts with spacebar as modifier do not work well in macOS // Shortcuts with spacebar as modifier do not work well in macOS
// (they will be called when the space bar is unpressed too). // (they will be called when the space bar is unpressed too).
if ((accel.modifiers() & ui::kKeySpaceModifier) == ui::kKeySpaceModifier) if ((shortcut.modifiers() & ui::kKeySpaceModifier) == ui::kKeySpaceModifier)
return os::Shortcut(); return os::Shortcut();
#endif #endif
return os::Shortcut( return os::Shortcut((shortcut.unicodeChar() ? shortcut.unicodeChar() :
(accel.unicodeChar() ? accel.unicodeChar() : from_scancode_to_unicode(accel.scancode())), from_scancode_to_unicode(shortcut.scancode())),
accel.modifiers()); shortcut.modifiers());
} }
else return {};
return os::Shortcut();
} }
AppMenus* AppMenus::s_instance = nullptr; AppMenus* AppMenus::s_instance = nullptr;
@ -719,7 +718,6 @@ Widget* AppMenus::convertXmlelemToMenuitem(XMLElement* elem, Menu* menu)
{ {
const char* id = elem->Attribute("id"); const char* id = elem->Attribute("id");
const char* group = elem->Attribute("group"); const char* group = elem->Attribute("group");
const char* standard = elem->Attribute("standard");
// is it a <separator>? // is it a <separator>?
if (strcmp(elem->Value(), "separator") == 0) { if (strcmp(elem->Value(), "separator") == 0) {
@ -781,6 +779,7 @@ Widget* AppMenus::convertXmlelemToMenuitem(XMLElement* elem, Menu* menu)
} }
#if LAF_MACOS #if LAF_MACOS
const char* standard = elem->Attribute("standard");
if (standard && strcmp(standard, "edit") == 0) if (standard && strcmp(standard, "edit") == 0)
menuitem->setAsStandardEditMenu(); menuitem->setAsStandardEditMenu();
#endif #endif

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -20,7 +21,20 @@ namespace app { namespace cmd {
using namespace doc; using namespace doc;
ClearRect::ClearRect(Cel* cel, const gfx::Rect& bounds, color_t color)
{
ASSERT(cel);
initialize(cel, bounds, color);
}
ClearRect::ClearRect(Cel* cel, const gfx::Rect& bounds) ClearRect::ClearRect(Cel* cel, const gfx::Rect& bounds)
{
ASSERT(cel);
Doc* doc = static_cast<Doc*>(cel->document());
initialize(cel, bounds, doc->bgColor(cel->layer()));
}
void ClearRect::initialize(Cel* cel, const gfx::Rect& bounds, color_t color)
{ {
ASSERT(cel); ASSERT(cel);
@ -37,9 +51,7 @@ ClearRect::ClearRect(Cel* cel, const gfx::Rect& bounds)
return; return;
m_dstImage.reset(new WithImage(image)); m_dstImage.reset(new WithImage(image));
m_bgcolor = color;
Doc* doc = static_cast<Doc*>(cel->document());
m_bgcolor = doc->bgColor(cel->layer());
m_copy.reset(crop_image(image, bounds2.x, bounds2.y, bounds2.w, bounds2.h, m_bgcolor)); m_copy.reset(crop_image(image, bounds2.x, bounds2.y, bounds2.w, bounds2.h, m_bgcolor));
} }

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -26,6 +27,7 @@ using namespace doc;
class ClearRect : public Cmd { class ClearRect : public Cmd {
public: public:
ClearRect(Cel* cel, const gfx::Rect& bounds); ClearRect(Cel* cel, const gfx::Rect& bounds);
ClearRect(Cel* cel, const gfx::Rect& bounds, color_t color);
protected: protected:
void onExecute() override; void onExecute() override;
@ -37,6 +39,7 @@ protected:
} }
private: private:
void initialize(Cel* cel, const gfx::Rect& bounds, color_t color);
void clear(); void clear();
void restore(); void restore();

View File

@ -1,397 +0,0 @@
// Aseprite
// Copyright (C) 2024 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/drop_on_timeline.h"
#include "app/cmd/add_layer.h"
#include "app/cmd/move_cel.h"
#include "app/cmd/set_pixel_format.h"
#include "app/console.h"
#include "app/context_flags.h"
#include "app/doc.h"
#include "app/doc_event.h"
#include "app/file/file.h"
#include "app/tx.h"
#include "app/util/layer_utils.h"
#include "app/util/open_file_job.h"
#include "base/serialization.h"
#include "doc/layer_io.h"
#include "doc/layer_list.h"
#include "doc/subobjects_io.h"
#include "render/dithering.h"
#include <algorithm>
namespace app { namespace cmd {
using namespace base::serialization::little_endian;
DropOnTimeline::DropOnTimeline(app::Doc* doc,
doc::frame_t frame,
doc::layer_t layerIndex,
InsertionPoint insert,
DroppedOn droppedOn,
const base::paths& paths)
: WithDocument(doc)
, m_size(0)
, m_paths(paths)
, m_frame(frame)
, m_layerIndex(layerIndex)
, m_insert(insert)
, m_droppedOn(droppedOn)
{
ASSERT(m_layerIndex >= 0);
for (const auto& path : m_paths)
m_size += path.size();
// Zero layers stored.
write32(m_stream, 0);
m_size += sizeof(uint32_t);
}
DropOnTimeline::DropOnTimeline(app::Doc* doc,
doc::frame_t frame,
doc::layer_t layerIndex,
InsertionPoint insert,
DroppedOn droppedOn,
const doc::ImageRef& image)
: WithDocument(doc)
, m_size(0)
, m_image(image)
, m_frame(frame)
, m_layerIndex(layerIndex)
, m_insert(insert)
, m_droppedOn(droppedOn)
{
ASSERT(m_layerIndex >= 0);
// Zero layers stored.
write32(m_stream, 0);
m_size += sizeof(uint32_t);
}
void DropOnTimeline::onExecute()
{
Doc* destDoc = document();
m_previousTotalFrames = destDoc->sprite()->totalFrames();
int docsProcessed = 0;
while (hasPendingWork()) {
std::unique_ptr<Doc> srcDoc;
if (!getNextDoc(srcDoc))
return;
if (srcDoc) {
docsProcessed++;
// If source document doesn't match the destination document's color
// mode, change it.
if (srcDoc->colorMode() != destDoc->colorMode()) {
// Execute in a source doc transaction because we don't need undo/redo
// this.
Tx tx(srcDoc.get());
tx(new cmd::SetPixelFormat(srcDoc->sprite(),
destDoc->sprite()->pixelFormat(),
render::Dithering(),
Preferences::instance().quantization.rgbmapAlgorithm(),
nullptr,
nullptr,
FitCriteria::DEFAULT));
tx.commit();
}
// If there is only one source document to process and it has a cel that
// can be moved, then move the cel from the source doc into the
// destination doc's selected frame.
const bool isJustOneDoc = (docsProcessed == 1 && !hasPendingWork());
if (isJustOneDoc && canMoveCelFrom(srcDoc.get())) {
auto* srcLayer = static_cast<LayerImage*>(srcDoc->sprite()->firstLayer());
auto* destLayer = static_cast<LayerImage*>(destDoc->sprite()->allLayers()[m_layerIndex]);
executeAndAdd(new MoveCel(srcLayer, 0, destLayer, m_frame, false));
break;
}
// If there is no room for the source frames, add frames to the
// destination sprite.
if (m_frame + srcDoc->sprite()->totalFrames() > destDoc->sprite()->totalFrames()) {
destDoc->sprite()->setTotalFrames(m_frame + srcDoc->sprite()->totalFrames());
}
// Save dropped layers from source document.
saveDroppedLayers(srcDoc->sprite()->root()->layers(), destDoc->sprite());
}
}
if (m_droppedLayersIds.empty())
return;
destDoc->sprite()->incrementVersion();
destDoc->incrementVersion();
insertDroppedLayers();
}
void DropOnTimeline::onUndo()
{
CmdSequence::onUndo();
if (m_droppedLayersIds.empty()) {
notifyGeneralUpdate();
return;
}
Doc* doc = document();
frame_t currentTotalFrames = doc->sprite()->totalFrames();
for (auto id : m_droppedLayersIds) {
auto* layer = doc::get<Layer>(id);
ASSERT(layer);
if (layer) {
DocEvent ev(doc);
ev.sprite(layer->sprite());
ev.layer(layer);
doc->notify_observers<DocEvent&>(&DocObserver::onBeforeRemoveLayer, ev);
LayerGroup* group = layer->parent();
group->removeLayer(layer);
group->incrementVersion();
group->sprite()->incrementVersion();
doc->notify_observers<DocEvent&>(&DocObserver::onAfterRemoveLayer, ev);
delete layer;
}
}
doc->sprite()->setTotalFrames(m_previousTotalFrames);
doc->sprite()->incrementVersion();
m_previousTotalFrames = currentTotalFrames;
}
void DropOnTimeline::onRedo()
{
CmdSequence::onRedo();
if (m_droppedLayersIds.empty()) {
notifyGeneralUpdate();
return;
}
Doc* doc = document();
frame_t currentTotalFrames = doc->sprite()->totalFrames();
doc->sprite()->setTotalFrames(m_previousTotalFrames);
doc->sprite()->incrementVersion();
m_previousTotalFrames = currentTotalFrames;
insertDroppedLayers();
}
void DropOnTimeline::setupInsertionLayer(Layer*& layer, LayerGroup*& group)
{
const LayerList& allLayers = document()->sprite()->allLayers();
layer = allLayers[m_layerIndex];
if (m_insert == InsertionPoint::BeforeLayer && layer->isGroup()) {
group = static_cast<LayerGroup*>(layer);
// The user is trying to drop layers into an empty group, so there is no after
// nor before layer...
if (group->layersCount() == 0) {
layer = nullptr;
return;
}
layer = group->lastLayer();
m_insert = InsertionPoint::AfterLayer;
}
group = layer->parent();
}
bool DropOnTimeline::hasPendingWork()
{
return m_image || !m_paths.empty();
}
bool DropOnTimeline::getNextDocFromImage(std::unique_ptr<Doc>& srcDoc)
{
if (!m_image)
return true;
Sprite* sprite = new Sprite(m_image->spec(), 256);
LayerImage* layer = new LayerImage(sprite);
sprite->root()->addLayer(layer);
Cel* cel = new Cel(0, m_image);
layer->addCel(cel);
srcDoc = std::make_unique<Doc>(sprite);
m_image = nullptr;
return true;
}
bool DropOnTimeline::getNextDocFromPaths(std::unique_ptr<Doc>& srcDoc)
{
Console console;
Context* context = document()->context();
int flags = FILE_LOAD_DATA_FILE | FILE_LOAD_AVOID_BACKGROUND_LAYER | FILE_LOAD_CREATE_PALETTE |
FILE_LOAD_SEQUENCE_YES;
std::unique_ptr<FileOp> fop(FileOp::createLoadDocumentOperation(context, m_paths.front(), flags));
// Remove the path that is currently being processed
m_paths.erase(m_paths.begin());
// Do nothing (the user cancelled or something like that)
if (!fop)
return false;
if (fop->hasError()) {
console.printf(fop->error().c_str());
return true;
}
base::paths fopFilenames;
fop->getFilenameList(fopFilenames);
// Remove paths that will be loaded by the current file operation.
for (const auto& filename : fopFilenames) {
auto it = std::find(m_paths.begin(), m_paths.end(), filename);
if (it != m_paths.end())
m_paths.erase(it);
}
OpenFileJob task(fop.get(), true);
task.showProgressWindow();
// Post-load processing, it is called from the GUI because may require user intervention.
fop->postLoad();
// Show any error
if (fop->hasError() && !fop->isStop())
console.printf(fop->error().c_str());
srcDoc.reset(fop->releaseDocument());
return true;
}
bool DropOnTimeline::getNextDoc(std::unique_ptr<Doc>& srcDoc)
{
if (m_image == nullptr && !m_paths.empty())
return getNextDocFromPaths(srcDoc);
return getNextDocFromImage(srcDoc);
}
void DropOnTimeline::storeDroppedLayerIds(const Layer* layer)
{
if (layer->isGroup()) {
const auto* group = static_cast<const LayerGroup*>(layer);
for (auto* child : group->layers())
storeDroppedLayerIds(child);
m_droppedLayersIds.push_back(group->id());
}
else {
m_droppedLayersIds.push_back(layer->id());
}
}
void DropOnTimeline::saveDroppedLayers(const LayerList& layers, Sprite* sprite)
{
size_t start = m_stream.tellp();
// Calculate the new number of layers.
m_stream.seekg(0);
uint32_t nLayers = read32(m_stream) + layers.size();
// Flat list of all the dropped layers.
LayerList allDroppedLayers;
// Write number of layers (at the beginning of the stream).
m_stream.seekp(0);
write32(m_stream, nLayers);
// Move to where we must start writing.
m_stream.seekp(start);
for (auto it = layers.cbegin(); it != layers.cend(); ++it) {
auto* layer = *it;
// TODO: If we could "relocate" a layer from the source document to the
// destination document we could avoid making a copy here.
std::unique_ptr<Layer> layerCopy(copy_layer_with_sprite(layer, sprite));
layerCopy->displaceFrames(0, m_frame);
write_layer(m_stream, layerCopy.get());
storeDroppedLayerIds(layerCopy.get());
}
size_t end = m_stream.tellp();
m_size += end - start;
}
void DropOnTimeline::insertDroppedLayers()
{
// Layer used as a reference to determine if the dropped layers will be
// inserted after or before it.
Layer* refLayer = nullptr;
// Parent group of the reference layer layer.
LayerGroup* group = nullptr;
// Keep track of the current insertion point.
InsertionPoint insert = m_insert;
setupInsertionLayer(refLayer, group);
SubObjectsFromSprite io(group->sprite());
m_stream.seekg(0);
auto nLayers = read32(m_stream);
for (int i = 0; i < nLayers; ++i) {
auto* layer = read_layer(m_stream, &io);
if (!refLayer) {
group->addLayer(layer);
refLayer = layer;
insert = InsertionPoint::AfterLayer;
}
else if (insert == InsertionPoint::AfterLayer) {
group->insertLayer(layer, refLayer);
refLayer = layer;
}
else if (insert == InsertionPoint::BeforeLayer) {
group->insertLayerBefore(layer, refLayer);
refLayer = layer;
insert = InsertionPoint::AfterLayer;
}
group->incrementVersion();
group->sprite()->incrementVersion();
Doc* doc = static_cast<Doc*>(group->sprite()->document());
DocEvent ev(doc);
ev.sprite(group->sprite());
ev.layer(layer);
doc->notify_observers<DocEvent&>(&DocObserver::onAddLayer, ev);
}
}
// Returns true if the document srcDoc has a cel that can be moved.
// The cel from the srcDoc can be moved only when all of the following
// conditions are met:
// * Drop took place in a cel.
// * Source doc has only one layer with just one frame.
// * The layer from the source doc and the destination cel's layer are both
// Image layers.
// Otherwise this function returns false.
bool DropOnTimeline::canMoveCelFrom(app::Doc* srcDoc)
{
auto* srcLayer = srcDoc->sprite()->firstLayer();
auto* destLayer = document()->sprite()->allLayers()[m_layerIndex];
return m_droppedOn == DroppedOn::Cel && srcDoc->sprite()->allLayersCount() == 1 &&
srcDoc->sprite()->totalFrames() == 1 && srcLayer->isImage() && destLayer->isImage();
}
void DropOnTimeline::notifyGeneralUpdate()
{
Doc* doc = document();
if (!doc)
return;
doc->notifyGeneralUpdate();
}
}} // namespace app::cmd

View File

@ -1,95 +0,0 @@
// Aseprite
// Copyright (C) 2024 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_drop_on_timeline_H_INCLUDED
#define APP_CMD_drop_on_timeline_H_INCLUDED
#pragma once
#include "app/cmd/with_document.h"
#include "app/cmd_sequence.h"
#include "app/doc_observer.h"
#include "base/paths.h"
#include "doc/frame.h"
#include "doc/image_ref.h"
#include "doc/layer.h"
#include "doc/layer_list.h"
namespace app { namespace cmd {
class DropOnTimeline : public CmdSequence,
public WithDocument {
public:
enum class InsertionPoint {
BeforeLayer,
AfterLayer,
};
enum class DroppedOn {
Unspecified,
Frame,
Layer,
Cel,
};
// Inserts the layers and frames of the documents pointed by the specified
// paths, at the specified frame and before or after the specified layer index.
DropOnTimeline(app::Doc* doc,
doc::frame_t frame,
doc::layer_t layerIndex,
InsertionPoint insert,
DroppedOn droppedOn,
const base::paths& paths);
// Inserts the image as if it were a document with just one layer and one
// frame, at the specified frame and before or after the specified layer index.
DropOnTimeline(app::Doc* doc,
doc::frame_t frame,
doc::layer_t layerIndex,
InsertionPoint insert,
DroppedOn droppedOn,
const doc::ImageRef& image);
protected:
void onExecute() override;
void onUndo() override;
void onRedo() override;
size_t onMemSize() const override { return sizeof(*this) + m_size; }
private:
void setupInsertionLayer(doc::Layer*& layer, doc::LayerGroup*& group);
void insertDroppedLayers();
bool canMoveCelFrom(app::Doc* srcDoc);
void notifyGeneralUpdate();
bool hasPendingWork();
// Returns the next document to be processed.
// Returns false when the user cancelled the process, or true when the
// process must go on.
bool getNextDoc(std::unique_ptr<Doc>& srcDoc);
bool getNextDocFromImage(std::unique_ptr<Doc>& srcDoc);
bool getNextDocFromPaths(std::unique_ptr<Doc>& srcDoc);
void storeDroppedLayerIds(const doc::Layer* layer);
void saveDroppedLayers(const doc::LayerList& layers, doc::Sprite* sprite);
size_t m_size;
base::paths m_paths;
doc::ImageRef m_image = nullptr;
doc::frame_t m_frame;
doc::layer_t m_layerIndex;
InsertionPoint m_insert;
DroppedOn m_droppedOn;
// Serialized dropped layers' data. Used for redo operation.
std::stringstream m_stream;
// Holds the Object IDs of the dropped layers. Used when determining which
// layers should be removed in an undo operation.
std::vector<doc::ObjectId> m_droppedLayersIds;
// Number of frames the doc had before dropping.
doc::frame_t m_previousTotalFrames;
};
}} // namespace app::cmd
#endif

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020-2024 Igara Studio S.A. // Copyright (C) 2020-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -11,12 +11,12 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/modules/gui.h"
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "ver/info.h" #include "ver/info.h"
#include "about.xml.h" #include "about.xml.h"
#include "app/context.h"
namespace app { namespace app {
@ -27,13 +27,19 @@ public:
AboutCommand(); AboutCommand();
protected: protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
AboutCommand::AboutCommand() : Command(CommandId::About(), CmdUIOnlyFlag) AboutCommand::AboutCommand() : Command(CommandId::About())
{ {
} }
bool AboutCommand::onEnabled(Context* context)
{
return context->isUIAvailable();
}
void AboutCommand::onExecute(Context* context) void AboutCommand::onExecute(Context* context)
{ {
gen::About window; gen::About window;
@ -44,7 +50,7 @@ void AboutCommand::onExecute(Context* context)
}); });
window.credits()->Click.connect([&window] { window.credits()->Click.connect([&window] {
window.closeWindow(nullptr); window.closeWindow(nullptr);
App::instance()->mainWindow()->showBrowser("README.md", "Authors"); App::instance()->mainWindow()->showBrowser("AUTHORS.md", "Authors");
}); });
window.i18nCredits()->Click.connect([&window] { window.i18nCredits()->Click.connect([&window] {
window.closeWindow(nullptr); window.closeWindow(nullptr);

View File

@ -63,8 +63,7 @@ protected:
std::string onGetFriendlyName() const override; std::string onGetFriendlyName() const override;
}; };
AddColorCommand::AddColorCommand() AddColorCommand::AddColorCommand() : CommandWithNewParams<AddColorParams>(CommandId::AddColor())
: CommandWithNewParams<AddColorParams>(CommandId::AddColor(), CmdUIOnlyFlag)
{ {
} }

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -16,8 +17,7 @@
#include "ui/ui.h" #include "ui/ui.h"
#include "advanced_mode.xml.h" #include "advanced_mode.xml.h"
#include "app/context.h"
#include <cstdio>
namespace app { namespace app {
@ -28,13 +28,19 @@ public:
AdvancedModeCommand(); AdvancedModeCommand();
protected: protected:
bool onEnabled(Context* context) override;
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
AdvancedModeCommand::AdvancedModeCommand() : Command(CommandId::AdvancedMode(), CmdUIOnlyFlag) AdvancedModeCommand::AdvancedModeCommand() : Command(CommandId::AdvancedMode())
{ {
} }
bool AdvancedModeCommand::onEnabled(Context* context)
{
return context->isUIAvailable();
}
void AdvancedModeCommand::onExecute(Context* context) void AdvancedModeCommand::onExecute(Context* context)
{ {
// Switch advanced mode. // Switch advanced mode.
@ -54,11 +60,11 @@ void AdvancedModeCommand::onExecute(Context* context)
if (oldMode == MainWindow::NormalMode && pref.advancedMode.showAlert()) { if (oldMode == MainWindow::NormalMode && pref.advancedMode.showAlert()) {
KeyPtr key = KeyboardShortcuts::instance()->command(this->id().c_str()); KeyPtr key = KeyboardShortcuts::instance()->command(this->id().c_str());
if (!key->accels().empty()) { if (!key->shortcuts().empty()) {
app::gen::AdvancedMode window; app::gen::AdvancedMode window;
window.warningLabel()->setTextf("You can go back pressing \"%s\" key.", window.warningLabel()->setTextf("You can go back pressing \"%s\" key.",
key->accels().front().toString().c_str()); key->shortcuts().front().toString().c_str());
window.openWindowInForeground(); window.openWindowInForeground();

View File

@ -15,7 +15,6 @@
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tx.h" #include "app/tx.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/sprite.h"
namespace app { namespace app {
@ -28,8 +27,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
BackgroundFromLayerCommand::BackgroundFromLayerCommand() BackgroundFromLayerCommand::BackgroundFromLayerCommand() : Command(CommandId::BackgroundFromLayer())
: Command(CommandId::BackgroundFromLayer(), CmdRecordableFlag)
{ {
} }

View File

@ -37,7 +37,7 @@ private:
Type m_type; Type m_type;
}; };
CancelCommand::CancelCommand() : Command(CommandId::Cancel(), CmdUIOnlyFlag), m_type(NoOp) CancelCommand::CancelCommand() : Command(CommandId::Cancel()), m_type(NoOp)
{ {
} }

View File

@ -22,7 +22,6 @@
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "doc/image.h" #include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "ui/ui.h" #include "ui/ui.h"
@ -282,8 +281,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
CanvasSizeCommand::CanvasSizeCommand() CanvasSizeCommand::CanvasSizeCommand() : CommandWithNewParams(CommandId::CanvasSize())
: CommandWithNewParams(CommandId::CanvasSize(), CmdRecordableFlag)
{ {
} }

View File

@ -20,8 +20,6 @@
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "doc/cel.h" #include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/sprite.h"
#include <string> #include <string>
@ -42,7 +40,7 @@ private:
int m_opacity; int m_opacity;
}; };
CelOpacityCommand::CelOpacityCommand() : Command(CommandId::CelOpacity(), CmdUIOnlyFlag) CelOpacityCommand::CelOpacityCommand() : Command(CommandId::CelOpacity())
{ {
m_opacity = 255; m_opacity = 255;
} }

View File

@ -23,7 +23,6 @@
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "app/ui/user_data_view.h" #include "app/ui/user_data_view.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "base/mem_utils.h"
#include "base/scoped_value.h" #include "base/scoped_value.h"
#include "doc/cel.h" #include "doc/cel.h"
#include "doc/cels_range.h" #include "doc/cels_range.h"
@ -385,14 +384,14 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
CelPropertiesCommand::CelPropertiesCommand() : Command(CommandId::CelProperties(), CmdUIOnlyFlag) CelPropertiesCommand::CelPropertiesCommand() : Command(CommandId::CelProperties())
{ {
} }
bool CelPropertiesCommand::onEnabled(Context* context) bool CelPropertiesCommand::onEnabled(Context* context)
{ {
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | return context->isUIAvailable() && context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::ActiveLayerIsImage); ContextFlags::ActiveLayerIsImage);
} }
void CelPropertiesCommand::onExecute(Context* context) void CelPropertiesCommand::onExecute(Context* context)

View File

@ -21,7 +21,6 @@
#include "app/tools/tool.h" #include "app/tools/tool.h"
#include "app/ui/context_bar.h" #include "app/ui/context_bar.h"
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
#include "base/convert_to.h"
#include "doc/algorithm/flip_image.h" #include "doc/algorithm/flip_image.h"
#include "doc/brush.h" #include "doc/brush.h"
#include "doc/image_ref.h" #include "doc/image_ref.h"
@ -64,7 +63,7 @@ private:
int m_slot; int m_slot;
}; };
ChangeBrushCommand::ChangeBrushCommand() : Command(CommandId::ChangeBrush(), CmdUIOnlyFlag) ChangeBrushCommand::ChangeBrushCommand() : Command(CommandId::ChangeBrush())
{ {
m_change = None; m_change = None;
m_slot = 0; m_slot = 0;

View File

@ -14,6 +14,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/params.h" #include "app/commands/params.h"
#include "app/context.h"
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "app/modules/palettes.h" #include "app/modules/palettes.h"
#include "app/ui/color_bar.h" #include "app/ui/color_bar.h"
@ -39,18 +40,24 @@ public:
ChangeColorCommand(); ChangeColorCommand();
protected: protected:
bool onEnabled(Context* context) override;
bool onNeedsParams() const override { return true; } bool onNeedsParams() const override { return true; }
void onLoadParams(const Params& params) override; void onLoadParams(const Params& params) override;
void onExecute(Context* context) override; void onExecute(Context* context) override;
std::string onGetFriendlyName() const override; std::string onGetFriendlyName() const override;
}; };
ChangeColorCommand::ChangeColorCommand() : Command(CommandId::ChangeColor(), CmdUIOnlyFlag) ChangeColorCommand::ChangeColorCommand() : Command(CommandId::ChangeColor())
{ {
m_background = false; m_background = false;
m_change = None; m_change = None;
} }
bool ChangeColorCommand::onEnabled(Context* context)
{
return context->isUIAvailable();
}
void ChangeColorCommand::onLoadParams(const Params& params) void ChangeColorCommand::onLoadParams(const Params& params)
{ {
std::string target = params.get("target"); std::string target = params.get("target");

View File

@ -21,9 +21,7 @@
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "app/load_matrix.h" #include "app/load_matrix.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/sprite_job.h" #include "app/sprite_job.h"
#include "app/transaction.h"
#include "app/ui/best_fit_criteria_selector.h" #include "app/ui/best_fit_criteria_selector.h"
#include "app/ui/dithering_selector.h" #include "app/ui/dithering_selector.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
@ -37,12 +35,10 @@
#include "fmt/format.h" #include "fmt/format.h"
#include "render/dithering.h" #include "render/dithering.h"
#include "render/dithering_algorithm.h" #include "render/dithering_algorithm.h"
#include "render/ordered_dither.h"
#include "render/quantization.h" #include "render/quantization.h"
#include "render/render.h" #include "render/render.h"
#include "render/task_delegate.h" #include "render/task_delegate.h"
#include "ui/listitem.h" #include "ui/listitem.h"
#include "ui/paint_event.h"
#include "ui/size_hint_event.h" #include "ui/size_hint_event.h"
#include "color_mode.xml.h" #include "color_mode.xml.h"
@ -507,7 +503,7 @@ private:
}; };
ChangePixelFormatCommand::ChangePixelFormatCommand() ChangePixelFormatCommand::ChangePixelFormatCommand()
: CommandWithNewParams(CommandId::ChangePixelFormat(), CmdUIOnlyFlag) : CommandWithNewParams(CommandId::ChangePixelFormat())
{ {
} }

View File

@ -23,7 +23,7 @@ protected:
void onExecute(Context* ctx) override; void onExecute(Context* ctx) override;
}; };
ClearCommand::ClearCommand() : Command(CommandId::Clear(), CmdUIOnlyFlag) ClearCommand::ClearCommand() : Command(CommandId::Clear())
{ {
} }

View File

@ -19,7 +19,6 @@
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "doc/cel.h" #include "doc/cel.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/sprite.h"
namespace app { namespace app {
@ -32,7 +31,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
ClearCelCommand::ClearCelCommand() : Command(CommandId::ClearCel(), CmdRecordableFlag) ClearCelCommand::ClearCelCommand() : Command(CommandId::ClearCel())
{ {
} }
@ -76,7 +75,7 @@ void ClearCelCommand::onExecute(Context* context)
tx.commit(); tx.commit();
} }
if (nonEditableLayers) if (context->isUIAvailable() && nonEditableLayers)
StatusBar::instance()->showTip(1000, Strings::statusbar_tips_locked_layers()); StatusBar::instance()->showTip(1000, Strings::statusbar_tips_locked_layers());
update_screen_for_document(document); update_screen_for_document(document);

View File

@ -23,8 +23,7 @@ protected:
void onExecute(Context* ctx) override; void onExecute(Context* ctx) override;
}; };
ClearRecentFilesCommand::ClearRecentFilesCommand() ClearRecentFilesCommand::ClearRecentFilesCommand() : Command(CommandId::ClearRecentFiles())
: Command(CommandId::ClearRecentFiles(), CmdUIOnlyFlag)
{ {
} }

View File

@ -12,15 +12,9 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/commands.h" #include "app/commands/commands.h"
#include "app/context_access.h"
#include "app/ui/doc_view.h" #include "app/ui/doc_view.h"
#include "app/ui/status_bar.h"
#include "app/ui/workspace.h" #include "app/ui/workspace.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "doc/sprite.h"
#include "ui/ui.h"
#include <memory>
namespace app { namespace app {
@ -28,7 +22,7 @@ using namespace ui;
class CloseFileCommand : public Command { class CloseFileCommand : public Command {
public: public:
CloseFileCommand() : Command(CommandId::CloseFile(), CmdUIOnlyFlag) {} CloseFileCommand() : Command(CommandId::CloseFile()) {}
protected: protected:
bool onEnabled(Context* context) override bool onEnabled(Context* context) override
@ -51,12 +45,15 @@ protected:
class CloseAllFilesCommand : public Command { class CloseAllFilesCommand : public Command {
public: public:
CloseAllFilesCommand() : Command(CommandId::CloseAllFiles(), CmdRecordableFlag) CloseAllFilesCommand() : Command(CommandId::CloseAllFiles()) { m_quitting = false; }
{
m_quitting = false;
}
protected: protected:
bool onEnabled(Context* context) override
{
// Null if we are in --batch mode
return App::instance()->workspace() != nullptr;
}
void onLoadParams(const Params& params) override { m_quitting = params.get_as<bool>("quitting"); } void onLoadParams(const Params& params) override { m_quitting = params.get_as<bool>("quitting"); }
void onExecute(Context* context) override void onExecute(Context* context) override

View File

@ -21,7 +21,6 @@
#include "app/transaction.h" #include "app/transaction.h"
#include "app/ui/color_bar.h" #include "app/ui/color_bar.h"
#include "app/ui/rgbmap_algorithm_selector.h" #include "app/ui/rgbmap_algorithm_selector.h"
#include "app/ui_context.h"
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "render/quantization.h" #include "render/quantization.h"
@ -71,7 +70,7 @@ protected:
}; };
ColorQuantizationCommand::ColorQuantizationCommand() ColorQuantizationCommand::ColorQuantizationCommand()
: CommandWithNewParams<ColorQuantizationParams>(CommandId::ColorQuantization(), CmdRecordableFlag) : CommandWithNewParams<ColorQuantizationParams>(CommandId::ColorQuantization())
{ {
} }

View File

@ -24,7 +24,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
ContiguousFillCommand::ContiguousFillCommand() : Command(CommandId::ContiguousFill(), CmdUIOnlyFlag) ContiguousFillCommand::ContiguousFillCommand() : Command(CommandId::ContiguousFill())
{ {
} }

View File

@ -23,7 +23,7 @@ protected:
void onExecute(Context* ctx) override; void onExecute(Context* ctx) override;
}; };
CopyCommand::CopyCommand() : Command(CommandId::Copy(), CmdUIOnlyFlag) CopyCommand::CopyCommand() : Command(CommandId::Copy())
{ {
} }

View File

@ -10,9 +10,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/context_access.h"
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "ui/base.h"
namespace app { namespace app {
@ -25,13 +23,13 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
CopyCelCommand::CopyCelCommand() : Command(CommandId::CopyCel(), CmdUIOnlyFlag) CopyCelCommand::CopyCelCommand() : Command(CommandId::CopyCel())
{ {
} }
bool CopyCelCommand::onEnabled(Context* context) bool CopyCelCommand::onEnabled(Context* context)
{ {
return App::instance()->timeline()->isMovingCel(); return App::instance()->timeline() && App::instance()->timeline()->isMovingCel();
} }
void CopyCelCommand::onExecute(Context* context) void CopyCelCommand::onExecute(Context* context)

View File

@ -24,7 +24,7 @@ protected:
void onExecute(Context* ctx) override; void onExecute(Context* ctx) override;
}; };
CopyMergedCommand::CopyMergedCommand() : Command(CommandId::CopyMerged(), CmdUIOnlyFlag) CopyMergedCommand::CopyMergedCommand() : Command(CommandId::CopyMerged())
{ {
} }

View File

@ -16,11 +16,7 @@
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/color_bar.h" #include "app/ui/color_bar.h"
#include "app/util/autocrop.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/mask.h" #include "doc/mask.h"
#include "doc/sprite.h"
namespace app { namespace app {
@ -37,7 +33,7 @@ private:
gfx::Rect m_bounds; gfx::Rect m_bounds;
}; };
CropSpriteCommand::CropSpriteCommand() : Command(CommandId::CropSprite(), CmdRecordableFlag) CropSpriteCommand::CropSpriteCommand() : Command(CommandId::CropSprite())
{ {
} }
@ -95,8 +91,7 @@ private:
bool m_byGrid = false; bool m_byGrid = false;
}; };
AutocropSpriteCommand::AutocropSpriteCommand() AutocropSpriteCommand::AutocropSpriteCommand() : Command(CommandId::AutocropSprite())
: Command(CommandId::AutocropSprite(), CmdRecordableFlag)
{ {
} }

View File

@ -23,7 +23,7 @@ protected:
void onExecute(Context* ctx) override; void onExecute(Context* ctx) override;
}; };
CutCommand::CutCommand() : Command(CommandId::Cut(), CmdUIOnlyFlag) CutCommand::CutCommand() : Command(CommandId::Cut())
{ {
} }

View File

@ -14,8 +14,6 @@
#include "app/context_access.h" #include "app/context_access.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tx.h" #include "app/tx.h"
#include "doc/mask.h"
#include "doc/sprite.h"
namespace app { namespace app {
@ -28,7 +26,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
DeselectMaskCommand::DeselectMaskCommand() : Command(CommandId::DeselectMask(), CmdRecordableFlag) DeselectMaskCommand::DeselectMaskCommand() : Command(CommandId::DeselectMask())
{ {
} }

View File

@ -29,8 +29,7 @@ protected:
void onExecute(Context* context); void onExecute(Context* context);
}; };
DeveloperConsoleCommand::DeveloperConsoleCommand() DeveloperConsoleCommand::DeveloperConsoleCommand() : Command(CommandId::DeveloperConsole())
: Command(CommandId::DeveloperConsole(), CmdUIOnlyFlag)
{ {
} }

View File

@ -12,8 +12,6 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/commands.h" #include "app/commands/commands.h"
#include "app/context_access.h"
#include "app/tools/tool_box.h"
#include "app/ui/context_bar.h" #include "app/ui/context_bar.h"
#include "app/ui_context.h" #include "app/ui_context.h"
@ -28,12 +26,15 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
DiscardBrushCommand::DiscardBrushCommand() : Command(CommandId::DiscardBrush(), CmdUIOnlyFlag) DiscardBrushCommand::DiscardBrushCommand() : Command(CommandId::DiscardBrush())
{ {
} }
bool DiscardBrushCommand::onEnabled(Context* context) bool DiscardBrushCommand::onEnabled(Context* context)
{ {
if (!context->isUIAvailable())
return false;
ContextBar* ctxBar = App::instance()->contextBar(); ContextBar* ctxBar = App::instance()->contextBar();
return (ctxBar->activeBrush()->type() == kImageBrushType); return (ctxBar->activeBrush()->type() == kImageBrushType);
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2025 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -10,16 +10,11 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/console.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc_api.h" #include "app/doc_api.h"
#include "app/doc_undo.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/editor/editor.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/sprite.h"
#include "ui/ui.h"
namespace app { namespace app {
@ -32,8 +27,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
DuplicateLayerCommand::DuplicateLayerCommand() DuplicateLayerCommand::DuplicateLayerCommand() : Command(CommandId::DuplicateLayer())
: Command(CommandId::DuplicateLayer(), CmdRecordableFlag)
{ {
} }
@ -51,7 +45,7 @@ void DuplicateLayerCommand::onExecute(Context* context)
Tx tx(writer, "Layer Duplication"); Tx tx(writer, "Layer Duplication");
LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer()); LayerImage* sourceLayer = static_cast<LayerImage*>(writer.layer());
DocApi api = document->getApi(tx); DocApi api = document->getApi(tx);
api.duplicateLayerAfter(sourceLayer, sourceLayer->parent(), sourceLayer); api.duplicateLayerAfter(sourceLayer, sourceLayer->parent(), sourceLayer, " Copy");
tx.commit(); tx.commit();
} }

View File

@ -16,13 +16,10 @@
#include "app/ini_file.h" #include "app/ini_file.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "base/fs.h" #include "base/fs.h"
#include "doc/sprite.h"
#include "ui/ui.h" #include "ui/ui.h"
#include "duplicate_sprite.xml.h" #include "duplicate_sprite.xml.h"
#include <cstdio>
namespace app { namespace app {
using namespace ui; using namespace ui;
@ -43,7 +40,7 @@ protected:
}; };
DuplicateSpriteCommand::DuplicateSpriteCommand() DuplicateSpriteCommand::DuplicateSpriteCommand()
: CommandWithNewParams<DuplicateSpriteParams>(CommandId::DuplicateSprite(), CmdRecordableFlag) : CommandWithNewParams<DuplicateSpriteParams>(CommandId::DuplicateSprite())
{ {
} }

View File

@ -13,8 +13,6 @@
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/ui/workspace.h" #include "app/ui/workspace.h"
#include <cstdio>
namespace app { namespace app {
// using namespace ui; // using namespace ui;
@ -28,7 +26,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
DuplicateViewCommand::DuplicateViewCommand() : Command(CommandId::DuplicateView(), CmdUIOnlyFlag) DuplicateViewCommand::DuplicateViewCommand() : Command(CommandId::DuplicateView())
{ {
} }

View File

@ -5,7 +5,6 @@
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/context.h"
#ifdef ENABLE_DRM #ifdef ENABLE_DRM
#include "app/ui/enter_license.h" #include "app/ui/enter_license.h"
@ -24,7 +23,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
EnterLicenseCommand::EnterLicenseCommand() : Command(CommandId::EnterLicense(), CmdUIOnlyFlag) EnterLicenseCommand::EnterLicenseCommand() : Command(CommandId::EnterLicense())
{ {
} }

View File

@ -16,7 +16,6 @@
#include "app/doc.h" #include "app/doc.h"
#include "app/job.h" #include "app/job.h"
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
#include "ui/alert.h"
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
#include "app/commands/debugger.h" #include "app/commands/debugger.h"
@ -32,7 +31,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
ExitCommand::ExitCommand() : Command(CommandId::Exit(), CmdUIOnlyFlag) ExitCommand::ExitCommand() : Command(CommandId::Exit())
{ {
} }

View File

@ -33,15 +33,12 @@
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "app/util/layer_utils.h" #include "app/util/layer_utils.h"
#include "base/convert_to.h"
#include "base/fs.h" #include "base/fs.h"
#include "base/string.h" #include "base/string.h"
#include "base/thread.h" #include "base/thread.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/tag.h" #include "doc/tag.h"
#include "doc/tileset.h" #include "doc/tileset.h"
#include "doc/tilesets.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/system.h" #include "ui/system.h"
@ -1187,8 +1184,7 @@ private:
} // anonymous namespace } // anonymous namespace
ExportSpriteSheetCommand::ExportSpriteSheetCommand(const char* id) ExportSpriteSheetCommand::ExportSpriteSheetCommand(const char* id) : CommandWithNewParams(id)
: CommandWithNewParams(id, CmdRecordableFlag)
{ {
} }

View File

@ -17,7 +17,6 @@
#include "app/context.h" #include "app/context.h"
#include "app/pref/preferences.h" #include "app/pref/preferences.h"
#include "app/site.h" #include "app/site.h"
#include "app/tools/tool.h"
#include "app/tools/tool_box.h" #include "app/tools/tool_box.h"
#include "app/ui/color_bar.h" #include "app/ui/color_bar.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
@ -28,7 +27,7 @@ namespace app {
using namespace ui; using namespace ui;
EyedropperCommand::EyedropperCommand() : Command(CommandId::Eyedropper(), CmdUIOnlyFlag) EyedropperCommand::EyedropperCommand() : Command(CommandId::Eyedropper())
{ {
m_background = false; m_background = false;
} }

View File

@ -39,7 +39,7 @@ private:
}; };
FillCommand::FillCommand(Type type) FillCommand::FillCommand(Type type)
: Command(type == Stroke ? CommandId::Stroke() : CommandId::Fill(), CmdUIOnlyFlag) : Command(type == Stroke ? CommandId::Stroke() : CommandId::Fill())
, m_type(type) , m_type(type)
{ {
} }

View File

@ -11,7 +11,6 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/context_access.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
namespace app { namespace app {
@ -25,7 +24,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
FitScreenCommand::FitScreenCommand() : Command(CommandId::FitScreen(), CmdUIOnlyFlag) FitScreenCommand::FitScreenCommand() : Command(CommandId::FitScreen())
{ {
} }

View File

@ -36,7 +36,7 @@ protected:
bool m_visibleOnly; bool m_visibleOnly;
}; };
FlattenLayersCommand::FlattenLayersCommand() : Command(CommandId::FlattenLayers(), CmdUIOnlyFlag) FlattenLayersCommand::FlattenLayersCommand() : Command(CommandId::FlattenLayers())
{ {
m_visibleOnly = false; m_visibleOnly = false;
} }

View File

@ -20,7 +20,6 @@
#include "app/commands/params.h" #include "app/commands/params.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc_api.h" #include "app/doc_api.h"
#include "app/doc_range.h"
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tools/tool_box.h" #include "app/tools/tool_box.h"
@ -38,11 +37,10 @@
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/mask.h" #include "doc/mask.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "gfx/size.h"
namespace app { namespace app {
FlipCommand::FlipCommand() : Command(CommandId::Flip(), CmdRecordableFlag) FlipCommand::FlipCommand() : Command(CommandId::Flip())
{ {
m_flipMask = false; m_flipMask = false;
m_flipType = doc::algorithm::FlipHorizontal; m_flipType = doc::algorithm::FlipHorizontal;

View File

@ -44,12 +44,11 @@ private:
// Frame to be shown. It can be ALL_FRAMES, CURRENT_RANGE, or a // Frame to be shown. It can be ALL_FRAMES, CURRENT_RANGE, or a
// number indicating a specific frame (1 is the first frame). // number indicating a specific frame (1 is the first frame).
Target m_target; Target m_target = CURRENT_RANGE;
frame_t m_frame; frame_t m_frame = 1;
}; };
FramePropertiesCommand::FramePropertiesCommand() FramePropertiesCommand::FramePropertiesCommand() : Command(CommandId::FrameProperties())
: Command(CommandId::FrameProperties(), CmdUIOnlyFlag)
{ {
} }
@ -59,7 +58,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
if (frame == "all") { if (frame == "all") {
m_target = ALL_FRAMES; m_target = ALL_FRAMES;
} }
else if (frame == "current") { else if (frame == "current" || !params.has_param("frame")) {
m_target = CURRENT_RANGE; m_target = CURRENT_RANGE;
} }
else { else {
@ -70,7 +69,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
bool FramePropertiesCommand::onEnabled(Context* context) bool FramePropertiesCommand::onEnabled(Context* context)
{ {
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable); return context->isUIAvailable() && context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
} }
void FramePropertiesCommand::onExecute(Context* context) void FramePropertiesCommand::onExecute(Context* context)

View File

@ -15,7 +15,6 @@
#include "app/cmd/set_tag_range.h" #include "app/cmd/set_tag_range.h"
#include "app/cmd/set_tag_repeat.h" #include "app/cmd/set_tag_repeat.h"
#include "app/cmd/set_user_data.h" #include "app/cmd/set_user_data.h"
#include "app/color.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/params.h" #include "app/commands/params.h"
#include "app/context_access.h" #include "app/context_access.h"
@ -48,7 +47,7 @@ private:
}; };
FrameTagPropertiesCommand::FrameTagPropertiesCommand() FrameTagPropertiesCommand::FrameTagPropertiesCommand()
: Command(CommandId::FrameTagProperties(), CmdUIOnlyFlag) : Command(CommandId::FrameTagProperties())
, m_tagId(NullId) , m_tagId(NullId)
{ {
} }
@ -66,7 +65,7 @@ void FrameTagPropertiesCommand::onLoadParams(const Params& params)
bool FrameTagPropertiesCommand::onEnabled(Context* context) bool FrameTagPropertiesCommand::onEnabled(Context* context)
{ {
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable); return context->isUIAvailable() && context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
} }
void FrameTagPropertiesCommand::onExecute(Context* context) void FrameTagPropertiesCommand::onExecute(Context* context)

View File

@ -27,7 +27,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
FullscreenModeCommand::FullscreenModeCommand() : Command(CommandId::FullscreenMode(), CmdUIOnlyFlag) FullscreenModeCommand::FullscreenModeCommand() : Command(CommandId::FullscreenMode())
{ {
} }

View File

@ -23,10 +23,7 @@
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
#include "app/ui/preview_editor.h" #include "app/ui/preview_editor.h"
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "app/util/conversion_to_surface.h"
#include "doc/image.h"
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "gfx/matrix.h" #include "gfx/matrix.h"
#include "os/surface.h" #include "os/surface.h"
@ -308,8 +305,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
FullscreenPreviewCommand::FullscreenPreviewCommand() FullscreenPreviewCommand::FullscreenPreviewCommand() : Command(CommandId::FullscreenPreview())
: Command(CommandId::FullscreenPreview(), CmdUIOnlyFlag)
{ {
} }

View File

@ -31,7 +31,7 @@ using namespace doc;
class GotoCommand : public Command { class GotoCommand : public Command {
protected: protected:
GotoCommand(const char* id) : Command(id, CmdRecordableFlag) {} GotoCommand(const char* id) : Command(id) {}
bool onEnabled(Context* context) override { return (Editor::activeEditor() != nullptr); } bool onEnabled(Context* context) override { return (Editor::activeEditor() != nullptr); }

View File

@ -24,11 +24,7 @@ namespace app {
class GotoLayerCommand : public Command { class GotoLayerCommand : public Command {
public: public:
GotoLayerCommand(int offset, const char* id, CommandFlags flags) GotoLayerCommand(int offset, const char* id) : Command(id), m_offset(offset) {}
: Command(id, flags)
, m_offset(offset)
{
}
protected: protected:
bool onEnabled(Context* context) override bool onEnabled(Context* context) override
@ -88,12 +84,12 @@ private:
class GotoPreviousLayerCommand : public GotoLayerCommand { class GotoPreviousLayerCommand : public GotoLayerCommand {
public: public:
GotoPreviousLayerCommand() : GotoLayerCommand(-1, "GotoPreviousLayer", CmdUIOnlyFlag) {} GotoPreviousLayerCommand() : GotoLayerCommand(-1, CommandId::GotoPreviousLayer()) {}
}; };
class GotoNextLayerCommand : public GotoLayerCommand { class GotoNextLayerCommand : public GotoLayerCommand {
public: public:
GotoNextLayerCommand() : GotoLayerCommand(+1, "GotoNextLayer", CmdUIOnlyFlag) {} GotoNextLayerCommand() : GotoLayerCommand(+1, CommandId::GotoNextLayer()) {}
}; };
Command* CommandFactory::createGotoPreviousLayerCommand() Command* CommandFactory::createGotoPreviousLayerCommand()

View File

@ -24,7 +24,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
GotoNextTabCommand::GotoNextTabCommand() : Command(CommandId::GotoNextTab(), CmdUIOnlyFlag) GotoNextTabCommand::GotoNextTabCommand() : Command(CommandId::GotoNextTab())
{ {
} }
@ -50,8 +50,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
GotoPreviousTabCommand::GotoPreviousTabCommand() GotoPreviousTabCommand::GotoPreviousTabCommand() : Command(CommandId::GotoPreviousTab())
: Command(CommandId::GotoPreviousTab(), CmdRecordableFlag)
{ {
} }

View File

@ -16,13 +16,9 @@
#include "app/context.h" #include "app/context.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc.h" #include "app/doc.h"
#include "app/find_widget.h"
#include "app/load_widget.h"
#include "app/pref/preferences.h" #include "app/pref/preferences.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "doc/document.h"
#include "doc/mask.h" #include "doc/mask.h"
#include "ui/window.h" #include "ui/window.h"
@ -37,7 +33,7 @@ using namespace gfx;
class SnapToGridCommand : public Command { class SnapToGridCommand : public Command {
public: public:
SnapToGridCommand() : Command(CommandId::SnapToGrid(), CmdUIOnlyFlag) {} SnapToGridCommand() : Command(CommandId::SnapToGrid()) {}
protected: protected:
bool onChecked(Context* ctx) override bool onChecked(Context* ctx) override
@ -52,13 +48,14 @@ protected:
bool newValue = !docPref.grid.snap(); bool newValue = !docPref.grid.snap();
docPref.grid.snap(newValue); docPref.grid.snap(newValue);
StatusBar::instance()->showSnapToGridWarning(newValue); if (ctx->isUIAvailable())
StatusBar::instance()->showSnapToGridWarning(newValue);
} }
}; };
class SelectionAsGridCommand : public Command { class SelectionAsGridCommand : public Command {
public: public:
SelectionAsGridCommand() : Command(CommandId::SelectionAsGrid(), CmdUIOnlyFlag) {} SelectionAsGridCommand() : Command(CommandId::SelectionAsGrid()) {}
protected: protected:
bool onEnabled(Context* ctx) override bool onEnabled(Context* ctx) override
@ -93,13 +90,13 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
GridSettingsCommand::GridSettingsCommand() : Command(CommandId::GridSettings(), CmdUIOnlyFlag) GridSettingsCommand::GridSettingsCommand() : Command(CommandId::GridSettings())
{ {
} }
bool GridSettingsCommand::onEnabled(Context* context) bool GridSettingsCommand::onEnabled(Context* context)
{ {
return true; return context->isUIAvailable();
} }
void GridSettingsCommand::onExecute(Context* context) void GridSettingsCommand::onExecute(Context* context)

View File

@ -10,6 +10,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/context.h"
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
namespace app { namespace app {
@ -26,7 +27,7 @@ protected:
bool onEnabled(Context* context) override; bool onEnabled(Context* context) override;
}; };
HomeCommand::HomeCommand() : Command(CommandId::Home(), CmdUIOnlyFlag) HomeCommand::HomeCommand() : Command(CommandId::Home())
{ {
} }
@ -41,7 +42,7 @@ void HomeCommand::onExecute(Context* context)
bool HomeCommand::onEnabled(Context* context) bool HomeCommand::onEnabled(Context* context)
{ {
return !App::instance()->mainWindow()->isHomeSelected(); return context->isUIAvailable() && !App::instance()->mainWindow()->isHomeSelected();
} }
Command* CommandFactory::createHomeCommand() Command* CommandFactory::createHomeCommand()

View File

@ -20,20 +20,14 @@
#include "app/doc_api.h" #include "app/doc_api.h"
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/pref/preferences.h" #include "app/pref/preferences.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/drop_down_button.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
#include "app/ui/editor/editor_decorator.h"
#include "app/ui/editor/select_box_state.h" #include "app/ui/editor/select_box_state.h"
#include "app/ui/editor/standby_state.h"
#include "app/ui/workspace.h" #include "app/ui/workspace.h"
#include "doc/cel.h" #include "doc/cel.h"
#include "doc/image.h" #include "doc/image.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "render/render.h" #include "render/render.h"
#include "ui/ui.h" #include "ui/ui.h"
@ -518,7 +512,7 @@ protected:
}; };
ImportSpriteSheetCommand::ImportSpriteSheetCommand() ImportSpriteSheetCommand::ImportSpriteSheetCommand()
: CommandWithNewParams(CommandId::ImportSpriteSheet(), CmdRecordableFlag) : CommandWithNewParams(CommandId::ImportSpriteSheet())
{ {
} }

View File

@ -31,7 +31,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
InvertMaskCommand::InvertMaskCommand() : Command(CommandId::InvertMask(), CmdRecordableFlag) InvertMaskCommand::InvertMaskCommand() : Command(CommandId::InvertMask())
{ {
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -23,25 +23,20 @@
#include "app/ui/app_menuitem.h" #include "app/ui/app_menuitem.h"
#include "app/ui/keyboard_shortcuts.h" #include "app/ui/keyboard_shortcuts.h"
#include "app/ui/search_entry.h" #include "app/ui/search_entry.h"
#include "app/ui/select_accelerator.h" #include "app/ui/select_shortcut.h"
#include "app/ui/separator_in_view.h" #include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "base/fs.h"
#include "base/pi.h" #include "base/pi.h"
#include "base/scoped_value.h" #include "base/scoped_value.h"
#include "base/split_string.h"
#include "base/string.h"
#include "ui/alert.h" #include "ui/alert.h"
#include "ui/fit_bounds.h" #include "ui/fit_bounds.h"
#include "ui/graphics.h" #include "ui/graphics.h"
#include "ui/listitem.h" #include "ui/listitem.h"
#include "ui/message.h" #include "ui/message.h"
#include "ui/paint_event.h" #include "ui/paint_event.h"
#include "ui/resize_event.h"
#include "ui/separator.h" #include "ui/separator.h"
#include "ui/size_hint_event.h" #include "ui/size_hint_event.h"
#include "ui/splitter.h" #include "ui/splitter.h"
#include "ui/system.h"
#include "keyboard_shortcuts.xml.h" #include "keyboard_shortcuts.xml.h"
@ -151,7 +146,7 @@ public:
, m_keyOrig(key ? new Key(*key) : nullptr) , m_keyOrig(key ? new Key(*key) : nullptr)
, m_menuitem(menuitem) , m_menuitem(menuitem)
, m_level(level) , m_level(level)
, m_hotAccel(-1) , m_hotShortcut(-1)
, m_lockButtons(false) , m_lockButtons(false)
, m_headerItem(headerItem) , m_headerItem(headerItem)
{ {
@ -204,45 +199,45 @@ public:
} }
private: private:
void onChangeAccel(int index) void onChangeShortcut(int index)
{ {
LockButtons lock(this); LockButtons lock(this);
Accelerator origAccel = m_key->accels()[index]; Shortcut origShortcut = m_key->shortcuts()[index];
SelectAccelerator window(origAccel, m_key->keycontext(), m_keys); SelectShortcut window(origShortcut, m_key->keycontext(), m_keys);
window.openWindowInForeground(); window.openWindowInForeground();
if (window.isModified()) { if (window.isModified()) {
m_key->disableAccel(origAccel, KeySource::UserDefined); m_key->disableShortcut(origShortcut, KeySource::UserDefined);
if (!window.accel().isEmpty()) if (!window.shortcut().isEmpty())
m_key->add(window.accel(), KeySource::UserDefined, m_keys); m_key->add(window.shortcut(), KeySource::UserDefined, m_keys);
} }
this->window()->layout(); this->window()->layout();
} }
void onDeleteAccel(int index) void onDeleteShortcut(int index)
{ {
LockButtons lock(this); LockButtons lock(this);
// We need to create a copy of the accelerator because // We need to create a copy of the shortcut because
// Key::disableAccel() will modify the accels() collection itself. // Key::disableShortcut() will modify the shortcuts() collection itself.
ui::Accelerator accel = m_key->accels()[index]; ui::Shortcut shortcut = m_key->shortcuts()[index];
if (ui::Alert::show(Strings::alerts_delete_shortcut(accel.toString())) != 1) if (ui::Alert::show(Strings::alerts_delete_shortcut(shortcut.toString())) != 1)
return; return;
m_key->disableAccel(accel, KeySource::UserDefined); m_key->disableShortcut(shortcut, KeySource::UserDefined);
window()->layout(); window()->layout();
} }
void onAddAccel() void onAddShortcut()
{ {
LockButtons lock(this); LockButtons lock(this);
ui::Accelerator accel; ui::Shortcut shortcut;
SelectAccelerator window(accel, m_key ? m_key->keycontext() : KeyContext::Any, m_keys); SelectShortcut window(shortcut, m_key ? m_key->keycontext() : KeyContext::Any, m_keys);
window.openWindowInForeground(); window.openWindowInForeground();
if ((window.isModified()) || if ((window.isModified()) ||
// We can assign a "None" accelerator to mouse wheel actions // We can assign a "None" shortcut to mouse wheel actions
(m_key && m_key->type() == KeyType::WheelAction && window.isOK())) { (m_key && m_key->type() == KeyType::WheelAction && window.isOK())) {
if (!m_key) { if (!m_key) {
ASSERT(m_menuitem); ASSERT(m_menuitem);
@ -256,7 +251,7 @@ private:
m_menuKeys[m_menuitem] = m_key; m_menuKeys[m_menuitem] = m_key;
} }
m_key->add(window.accel(), KeySource::UserDefined, m_keys); m_key->add(window.shortcut(), KeySource::UserDefined, m_keys);
} }
this->window()->layout(); this->window()->layout();
@ -273,8 +268,8 @@ private:
size.w = std::max(size.w, w); size.w = std::max(size.w, w);
} }
if (m_key && !m_key->accels().empty()) { if (m_key && !m_key->shortcuts().empty()) {
size_t combos = m_key->accels().size(); size_t combos = m_key->shortcuts().size();
if (combos > 1) if (combos > 1)
size.h *= combos; size.h *= combos;
} }
@ -315,7 +310,7 @@ private:
} }
} }
if (m_key && !m_key->accels().empty()) { if (m_key && !m_key->shortcuts().empty()) {
if (m_key->keycontext() != KeyContext::Any) { if (m_key->keycontext() != KeyContext::Any) {
g->drawText(convertKeyContextToUserFriendlyString(m_key->keycontext()), g->drawText(convertKeyContextToUserFriendlyString(m_key->keycontext()),
fg, fg,
@ -324,13 +319,14 @@ private:
} }
const int dh = th + 4 * guiscale(); const int dh = th + 4 * guiscale();
IntersectClip clip(g, IntersectClip clip(
gfx::Rect(keyXPos, y, contextXPos - keyXPos, dh * m_key->accels().size())); g,
gfx::Rect(keyXPos, y, contextXPos - keyXPos, dh * m_key->shortcuts().size()));
if (clip) { if (clip) {
int i = 0; int i = 0;
for (const Accelerator& accel : m_key->accels()) { for (const Shortcut& shortcut : m_key->shortcuts()) {
if (i != m_hotAccel || !m_changeButton) { if (i != m_hotShortcut || !m_changeButton) {
g->drawText(getAccelText(accel), fg, bg, gfx::Point(keyXPos, y)); g->drawText(getShortcutText(shortcut), fg, bg, gfx::Point(keyXPos, y));
} }
y += dh; y += dh;
++i; ++i;
@ -361,40 +357,41 @@ private:
gfx::Rect bounds = this->bounds(); gfx::Rect bounds = this->bounds();
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg); MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
const Accelerators* accels = (m_key ? &m_key->accels() : NULL); const Shortcuts* shortcuts = (m_key ? &m_key->shortcuts() : NULL);
int y = bounds.y; int y = bounds.y;
int dh = textSize().h + 4 * guiscale(); int dh = textSize().h + 4 * guiscale();
int maxi = (accels && accels->size() > 1 ? accels->size() : 1); int maxi = (shortcuts && shortcuts->size() > 1 ? shortcuts->size() : 1);
auto theme = SkinTheme::get(this); auto* theme = SkinTheme::get(this);
for (int i = 0; i < maxi; ++i, y += dh) { for (int i = 0; i < maxi; ++i, y += dh) {
int w = font()->textLength( int w = font()->textLength((shortcuts && i < (int)shortcuts->size() ?
(accels && i < (int)accels->size() ? getAccelText((*accels)[i]) : std::string())); getShortcutText((*shortcuts)[i]) :
std::string()));
gfx::Rect itemBounds(bounds.x + m_headerItem->keyXPos(), y, w, dh); gfx::Rect itemBounds(bounds.x + m_headerItem->keyXPos(), y, w, dh);
itemBounds = itemBounds.enlarge( itemBounds = itemBounds.enlarge(
gfx::Border(4 * guiscale(), 0, 6 * guiscale(), 1 * guiscale())); gfx::Border(4 * guiscale(), 0, 6 * guiscale(), 1 * guiscale()));
if (accels && i < (int)accels->size() && mouseMsg->position().y >= itemBounds.y && if (shortcuts && i < (int)shortcuts->size() && mouseMsg->position().y >= itemBounds.y &&
mouseMsg->position().y < itemBounds.y + itemBounds.h) { mouseMsg->position().y < itemBounds.y + itemBounds.h) {
if (m_hotAccel != i) { if (m_hotShortcut != i) {
m_hotAccel = i; m_hotShortcut = i;
m_changeConn = obs::connection(); m_changeConn = obs::connection();
m_changeButton.reset(new Button("")); m_changeButton.reset(new Button(""));
m_changeConn = m_changeButton->Click.connect([this, i] { onChangeAccel(i); }); m_changeConn = m_changeButton->Click.connect([this, i] { onChangeShortcut(i); });
m_changeButton->setStyle(theme->styles.miniButton()); m_changeButton->setStyle(theme->styles.miniButton());
addChild(m_changeButton.get()); addChild(m_changeButton.get());
m_deleteConn = obs::connection(); m_deleteConn = obs::connection();
m_deleteButton.reset(new Button("")); m_deleteButton.reset(new Button(""));
m_deleteConn = m_deleteButton->Click.connect([this, i] { onDeleteAccel(i); }); m_deleteConn = m_deleteButton->Click.connect([this, i] { onDeleteShortcut(i); });
m_deleteButton->setStyle(theme->styles.miniButton()); m_deleteButton->setStyle(theme->styles.miniButton());
addChild(m_deleteButton.get()); addChild(m_deleteButton.get());
m_changeButton->setBgColor(gfx::ColorNone); m_changeButton->setBgColor(gfx::ColorNone);
m_changeButton->setBounds(itemBounds); m_changeButton->setBounds(itemBounds);
m_changeButton->setText(getAccelText((*accels)[i])); m_changeButton->setText(getShortcutText((*shortcuts)[i]));
const char* label = "x"; const char* label = "x";
m_deleteButton->setBgColor(gfx::ColorNone); m_deleteButton->setBgColor(gfx::ColorNone);
@ -411,7 +408,7 @@ private:
if (i == 0 && !m_addButton && (!m_menuitem || m_menuitem->getCommand())) { if (i == 0 && !m_addButton && (!m_menuitem || m_menuitem->getCommand())) {
m_addConn = obs::connection(); m_addConn = obs::connection();
m_addButton.reset(new Button("")); m_addButton.reset(new Button(""));
m_addConn = m_addButton->Click.connect([this] { onAddAccel(); }); m_addConn = m_addButton->Click.connect([this] { onAddShortcut(); });
m_addButton->setStyle(theme->styles.miniButton()); m_addButton->setStyle(theme->styles.miniButton());
addChild(m_addButton.get()); addChild(m_addButton.get());
@ -452,17 +449,15 @@ private:
m_addButton->setVisible(false); m_addButton->setVisible(false);
} }
m_hotAccel = -1; m_hotShortcut = -1;
} }
std::string getAccelText(const Accelerator& accel) const std::string getShortcutText(const Shortcut& shortcut) const
{ {
if (m_key && m_key->type() == KeyType::WheelAction && accel.isEmpty()) { if (m_key && m_key->type() == KeyType::WheelAction && shortcut.isEmpty()) {
return Strings::keyboard_shortcuts_default_action(); return Strings::keyboard_shortcuts_default_action();
} }
else { return shortcut.toString();
return accel.toString();
}
} }
KeyboardShortcuts& m_keys; KeyboardShortcuts& m_keys;
@ -471,14 +466,14 @@ private:
KeyPtr m_keyOrig; KeyPtr m_keyOrig;
AppMenuItem* m_menuitem; AppMenuItem* m_menuitem;
int m_level; int m_level;
ui::Accelerators m_newAccels; ui::Shortcuts m_newShortcuts;
std::shared_ptr<ui::Button> m_changeButton; std::shared_ptr<ui::Button> m_changeButton;
std::shared_ptr<ui::Button> m_deleteButton; std::shared_ptr<ui::Button> m_deleteButton;
std::shared_ptr<ui::Button> m_addButton; std::shared_ptr<ui::Button> m_addButton;
obs::scoped_connection m_changeConn; obs::scoped_connection m_changeConn;
obs::scoped_connection m_deleteConn; obs::scoped_connection m_deleteConn;
obs::scoped_connection m_addConn; obs::scoped_connection m_addConn;
int m_hotAccel; int m_hotShortcut;
bool m_lockButtons; bool m_lockButtons;
HeaderItem* m_headerItem; HeaderItem* m_headerItem;
}; };
@ -977,6 +972,7 @@ public:
KeyboardShortcutsCommand(); KeyboardShortcutsCommand();
protected: protected:
bool onEnabled(Context* context) override;
void onLoadParams(const Params& params) override; void onLoadParams(const Params& params) override;
void onExecute(Context* context) override; void onExecute(Context* context) override;
@ -986,11 +982,15 @@ private:
std::string m_search; std::string m_search;
}; };
KeyboardShortcutsCommand::KeyboardShortcutsCommand() KeyboardShortcutsCommand::KeyboardShortcutsCommand() : Command(CommandId::KeyboardShortcuts())
: Command(CommandId::KeyboardShortcuts(), CmdUIOnlyFlag)
{ {
} }
bool KeyboardShortcutsCommand::onEnabled(Context* context)
{
return context->isUIAvailable();
}
void KeyboardShortcutsCommand::onLoadParams(const Params& params) void KeyboardShortcutsCommand::onLoadParams(const Params& params)
{ {
m_search = params.get("search"); m_search = params.get("search");

View File

@ -34,10 +34,7 @@ private:
std::string m_path; std::string m_path;
}; };
LaunchCommand::LaunchCommand() LaunchCommand::LaunchCommand() : Command(CommandId::Launch()), m_type(Url), m_path("")
: Command(CommandId::Launch(), CmdUIOnlyFlag)
, m_type(Url)
, m_path("")
{ {
} }

View File

@ -14,8 +14,6 @@
#include "app/context_access.h" #include "app/context_access.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tx.h" #include "app/tx.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace app { namespace app {
@ -28,8 +26,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
LayerFromBackgroundCommand::LayerFromBackgroundCommand() LayerFromBackgroundCommand::LayerFromBackgroundCommand() : Command(CommandId::LayerFromBackground())
: Command(CommandId::LayerFromBackground(), CmdRecordableFlag)
{ {
} }

View File

@ -9,11 +9,9 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "doc/image.h"
#include "doc/layer.h" #include "doc/layer.h"
namespace app { namespace app {
@ -30,7 +28,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
LayerLockCommand::LayerLockCommand() : Command(CommandId::LayerLock(), CmdRecordableFlag) LayerLockCommand::LayerLockCommand() : Command(CommandId::LayerLock())
{ {
} }

View File

@ -9,7 +9,6 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/app.h"
#include "app/cmd/set_layer_opacity.h" #include "app/cmd/set_layer_opacity.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/params.h" #include "app/commands/params.h"
@ -40,7 +39,7 @@ private:
int m_opacity; int m_opacity;
}; };
LayerOpacityCommand::LayerOpacityCommand() : Command(CommandId::LayerOpacity(), CmdUIOnlyFlag) LayerOpacityCommand::LayerOpacityCommand() : Command(CommandId::LayerOpacity())
{ {
m_opacity = 255; m_opacity = 255;
} }

View File

@ -32,8 +32,8 @@
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "app/ui/user_data_view.h" #include "app/ui/user_data_view.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "base/convert_to.h"
#include "base/scoped_value.h" #include "base/scoped_value.h"
#include "doc/image.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/layer_tilemap.h" #include "doc/layer_tilemap.h"
#include "doc/sprite.h" #include "doc/sprite.h"
@ -134,8 +134,16 @@ public:
remapWindow(); remapWindow();
centerWindow(); centerWindow();
gfx::Rect originalBounds = bounds();
load_window_pos(this, "LayerProperties"); load_window_pos(this, "LayerProperties");
// Queue a remap for after the user data view is configured
// if the window size has been reset and user data is visible
if (originalBounds == bounds() && Preferences::instance().layers.userDataVisibility())
m_remapAfterConfigure = true;
UIContext::instance()->add_observer(this); UIContext::instance()->add_observer(this);
} }
@ -158,6 +166,11 @@ public:
if (countLayers() > 0) { if (countLayers() > 0) {
m_userDataView.configureAndSet(m_layer->userData(), g_window->propertiesGrid()); m_userDataView.configureAndSet(m_layer->userData(), g_window->propertiesGrid());
if (m_remapAfterConfigure) {
remapWindow();
centerWindow();
m_remapAfterConfigure = false;
}
} }
updateFromLayer(); updateFromLayer();
@ -463,6 +476,13 @@ private:
m_userDataView.setVisible(false, false); m_userDataView.setVisible(false, false);
} }
bool uuidVisible = m_document && m_document->sprite() && m_document->sprite()->useLayerUuids();
uuidLabel()->setVisible(uuidVisible);
uuid()->setVisible(uuidVisible);
if (uuidVisible)
uuid()->setText(m_layer ? base::convert_to<std::string>(m_layer->uuid()) : std::string());
if (tileset()->isVisible() != tilemapVisibility) { if (tileset()->isVisible() != tilemapVisibility) {
tileset()->setVisible(tilemapVisibility); tileset()->setVisible(tilemapVisibility);
tileset()->parent()->layout(); tileset()->parent()->layout();
@ -476,16 +496,17 @@ private:
view::RealRange m_range; view::RealRange m_range;
bool m_selfUpdate = false; bool m_selfUpdate = false;
UserDataView m_userDataView; UserDataView m_userDataView;
bool m_remapAfterConfigure = false;
}; };
LayerPropertiesCommand::LayerPropertiesCommand() LayerPropertiesCommand::LayerPropertiesCommand() : Command(CommandId::LayerProperties())
: Command(CommandId::LayerProperties(), CmdRecordableFlag)
{ {
} }
bool LayerPropertiesCommand::onEnabled(Context* context) bool LayerPropertiesCommand::onEnabled(Context* context)
{ {
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | ContextFlags::HasActiveLayer); return context->isUIAvailable() &&
context->checkFlags(ContextFlags::ActiveDocumentIsWritable | ContextFlags::HasActiveLayer);
} }
void LayerPropertiesCommand::onExecute(Context* context) void LayerPropertiesCommand::onExecute(Context* context)

View File

@ -12,7 +12,6 @@
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "doc/image.h"
#include "doc/layer.h" #include "doc/layer.h"
namespace app { namespace app {
@ -29,8 +28,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
LayerVisibilityCommand::LayerVisibilityCommand() LayerVisibilityCommand::LayerVisibilityCommand() : Command(CommandId::LayerVisibility())
: Command(CommandId::LayerVisibility(), CmdRecordableFlag)
{ {
} }

View File

@ -18,7 +18,6 @@
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "doc/cel.h" #include "doc/cel.h"
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/sprite.h"
namespace app { namespace app {
@ -31,7 +30,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
LinkCelsCommand::LinkCelsCommand() : Command(CommandId::LinkCels(), CmdRecordableFlag) LinkCelsCommand::LinkCelsCommand() : Command(CommandId::LinkCels())
{ {
} }
@ -88,7 +87,7 @@ void LinkCelsCommand::onExecute(Context* context)
tx.commit(); tx.commit();
} }
if (nonEditableLayers) if (context->isUIAvailable() && nonEditableLayers)
StatusBar::instance()->showTip(1000, Strings::statusbar_tips_locked_layers()); StatusBar::instance()->showTip(1000, Strings::statusbar_tips_locked_layers());
update_screen_for_document(document); update_screen_for_document(document);

View File

@ -26,6 +26,7 @@ namespace app {
class LoadMaskCommand : public Command { class LoadMaskCommand : public Command {
std::string m_filename; std::string m_filename;
bool m_ui = true;
public: public:
LoadMaskCommand(); LoadMaskCommand();
@ -36,13 +37,16 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
LoadMaskCommand::LoadMaskCommand() : Command(CommandId::LoadMask(), CmdRecordableFlag) LoadMaskCommand::LoadMaskCommand() : Command(CommandId::LoadMask())
{ {
m_filename = "";
} }
void LoadMaskCommand::onLoadParams(const Params& params) void LoadMaskCommand::onLoadParams(const Params& params)
{ {
if (params.has_param("ui"))
m_ui = params.get_as<bool>("ui");
else
m_ui = true;
m_filename = params.get("filename"); m_filename = params.get("filename");
} }
@ -55,7 +59,7 @@ void LoadMaskCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); const ContextReader reader(context);
if (context->isUIAvailable()) { if (context->isUIAvailable() && m_ui) {
base::paths exts = { "msk" }; base::paths exts = { "msk" };
base::paths selectedFilename; base::paths selectedFilename;
if (!app::show_file_selector(Strings::load_selection_title(), if (!app::show_file_selector(Strings::load_selection_title(),
@ -70,7 +74,8 @@ void LoadMaskCommand::onExecute(Context* context)
std::unique_ptr<Mask> mask(load_msk_file(m_filename.c_str())); std::unique_ptr<Mask> mask(load_msk_file(m_filename.c_str()));
if (!mask) { if (!mask) {
ui::Alert::show(Strings::alerts_error_loading_file(m_filename)); if (context->isUIAvailable())
ui::Alert::show(Strings::alerts_error_loading_file(m_filename));
return; return;
} }

View File

@ -37,14 +37,19 @@ protected:
private: private:
std::string m_preset; std::string m_preset;
std::string m_filename; std::string m_filename;
bool m_ui = true;
}; };
LoadPaletteCommand::LoadPaletteCommand() : Command(CommandId::LoadPalette(), CmdRecordableFlag) LoadPaletteCommand::LoadPaletteCommand() : Command(CommandId::LoadPalette())
{ {
} }
void LoadPaletteCommand::onLoadParams(const Params& params) void LoadPaletteCommand::onLoadParams(const Params& params)
{ {
if (params.has_param("ui"))
m_ui = params.get_as<bool>("ui");
else
m_ui = true;
m_preset = params.get("preset"); m_preset = params.get("preset");
m_filename = params.get("filename"); m_filename = params.get("filename");
} }
@ -58,20 +63,20 @@ void LoadPaletteCommand::onExecute(Context* context)
if (!base::is_file(filename)) if (!base::is_file(filename))
filename = get_preset_palette_filename(m_preset, ".gpl"); filename = get_preset_palette_filename(m_preset, ".gpl");
} }
else if (!m_filename.empty()) { else if (context->isUIAvailable() && m_ui) {
filename = m_filename; const base::paths exts = get_readable_palette_extensions();
}
else if (context->isUIAvailable()) {
base::paths exts = get_readable_palette_extensions();
base::paths filenames; base::paths filenames;
if (app::show_file_selector(Strings::load_palette_title(), if (app::show_file_selector(Strings::load_palette_title(),
"", m_filename,
exts, exts,
FileSelectorType::Open, FileSelectorType::Open,
filenames)) { filenames)) {
filename = filenames.front(); filename = filenames.front();
} }
} }
else if (!m_filename.empty()) {
filename = m_filename;
}
// Do nothing // Do nothing
if (filename.empty()) if (filename.empty())

View File

@ -29,7 +29,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
MaskAllCommand::MaskAllCommand() : Command(CommandId::MaskAll(), CmdRecordableFlag) MaskAllCommand::MaskAllCommand() : Command(CommandId::MaskAll())
{ {
} }

View File

@ -15,7 +15,6 @@
#include "app/color_utils.h" #include "app/color_utils.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/new_params.h" #include "app/commands/new_params.h"
#include "app/console.h"
#include "app/context.h" #include "app/context.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc.h" #include "app/doc.h"
@ -26,9 +25,6 @@
#include "app/ui/color_bar.h" #include "app/ui/color_bar.h"
#include "app/ui/color_button.h" #include "app/ui/color_button.h"
#include "app/ui/selection_mode_field.h" #include "app/ui/selection_mode_field.h"
#include "base/chrono.h"
#include "base/convert_to.h"
#include "base/scoped_value.h"
#include "doc/image.h" #include "doc/image.h"
#include "doc/mask.h" #include "doc/mask.h"
#include "doc/sprite.h" #include "doc/sprite.h"
@ -203,8 +199,7 @@ protected:
void onExecute(Context* context) override; void onExecute(Context* context) override;
}; };
MaskByColorCommand::MaskByColorCommand() MaskByColorCommand::MaskByColorCommand() : CommandWithNewParams(CommandId::MaskByColor())
: CommandWithNewParams(CommandId::MaskByColor(), CmdUIOnlyFlag)
{ {
} }

Some files were not shown because too many files have changed in this diff Show More