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
WarningsAsErrors: ''
CheckOptions:
- key: readability-implicit-bool-conversion.AllowIntegerConditions
value: true
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: true
- 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
run: |
if [[ "${{ runner.os }}" == "Windows" ]] ; then
choco install wget -y --no-progress
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
this_dir=$(cygpath "${{ github.workspace }}")
else
wget https://github.com/aseprite/skia/releases/download/m124-08a5439a6b/Skia-Linux-Release-x64.zip
unzip Skia-Linux-Release-x64.zip -d skia
this_dir="${{ github.workspace }}"
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
uses: hendrikmuhs/ccache-action@v1.2.17
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}

View File

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

2
.gitignore vendored
View File

@ -12,7 +12,9 @@
*.res
.DS_Store
.vs
.vscode
tests/_test*
build
.build
.deps
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})
else()
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_INCLUDE_DIRS
${LIBPNG_DIR}

View File

@ -6,7 +6,8 @@
* [Windows dependencies](#windows-dependencies)
* [macOS dependencies](#macos-dependencies)
* [Linux dependencies](#linux-dependencies)
* [Compiling](#compiling)
* [Automatic Building](#automatic-building)
* [Manual Building](#manual-building)
* [Windows details](#windows-details)
* [MinGW](#mingw)
* [macOS details](#macos-details)
@ -17,11 +18,12 @@
# Platforms
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))
* macOS 13.0.1 Ventura + Xcode 14.1 + macOS 11.3 SDK (older version might work)
* Linux Ubuntu Bionic 18.04 + clang 10.0
* Windows 11 + [Visual Studio Community 2022 + Windows 11 SDK](https://imgur.com/a/7zs51IT)
* *Important*: We don't support [MinGW](#mingw)
* macOS 15.2 Sequoia + Xcode 16.3 + macOS 15.4 SDK
* Linux Ubuntu Focal Fossa 20.04 + clang 12
# Get the source code
@ -49,7 +51,7 @@ clone the repository on Windows.
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
* And a compiled version of the `aseprite-m124` branch of
the [Skia library](https://github.com/aseprite/skia#readme).
@ -59,25 +61,24 @@ To compile Aseprite you will need:
## 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))
* The [Desktop development with C++ item + Windows 10.0.18362.0 SDK](https://imgur.com/a/7zs51IT)
from the Visual Studio installer
* The [Desktop development with C++ item + Windows 10.0.26100.0 SDK](https://imgur.com/a/7zs51IT)
from Visual Studio installer
## macOS dependencies
On macOS you will need macOS 11.3 SDK and Xcode 13.1 (older versions
might work).
On macOS you will need macOS 15.4 SDK and Xcode 16.3 (older versions might work).
## Linux dependencies
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:
@ -85,13 +86,24 @@ On Fedora:
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:
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
`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
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
mkdir build
@ -232,8 +246,8 @@ You need to use clang and libc++ to compile Aseprite:
export CXX=clang++
cmake \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_FLAGS:STRING=-stdlib=libc++ \
-DCMAKE_EXE_LINKER_FLAGS:STRING=-stdlib=libc++ \
-DCMAKE_CXX_FLAGS:STRING=-stdlib=libstdc++ \
-DCMAKE_EXE_LINKER_FLAGS:STRING=-stdlib=libstdc++ \
-DLAF_BACKEND=skia \
-DSKIA_DIR=$HOME/deps/skia \
-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
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
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),
[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
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/)
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.
Check the [AUTHORS](AUTHORS.md) file for details about the active team
of developers working on Aseprite.
## License

141
build.sh
View File

@ -56,31 +56,6 @@ if [ "$1" == "--norun" ] ; then
norun=1
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.
if ! cmake --version >/dev/null ; then
echo ""
@ -137,6 +112,23 @@ if [ $run_submodule_update ] ; then
echo "Done"
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.
if [ ! -d "$pwd/.build" ] ; then
mkdir "$pwd/.build"
@ -150,25 +142,25 @@ if [ ! -f "$pwd/.build/userkind" ] ; then
echo "user" > $pwd/.build/userkind
else
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 " [U]ser: give a try to Aseprite"
echo " [D]eveloper: develop/modify Aseprite"
echo ""
read -sN 1 -p "[U/D]? "
echo ""
if [[ "$REPLY" == "d" || "$REPLY" == "D" ]] ; then
read -p "[U/D]? "
REPLY=$(echo $REPLY | tr '[:upper:]' '[:lower:]')
if [[ "$REPLY" == "d" || "$REPLY" == "dev" || "$REPLY" == "developer" ]] ; then
echo "developer" > $pwd/.build/userkind
elif [[ "$REPLY" == "u" || "$REPLY" == "U" ]] ; then
elif [[ "$REPLY" == "u" || "$REPLY" == "user" ]] ; then
echo "user" > $pwd/.build/userkind
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
fi
fi
fi
userkind=$(echo -n $(cat $pwd/.build/userkind))
userkind=$(cat $pwd/.build/userkind)
if [ "$userkind" == "developer" ] ; then
echo "======================= BUILDING FOR DEVELOPER ======================="
else
@ -229,7 +221,7 @@ if [ ! -f "$pwd/.build/builds_dir" ] ; then
echo "$builds_dir" > "$pwd/.build/builds_dir"
fi
# 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.
builds_list="$(mktemp)"
@ -265,7 +257,8 @@ else
# New build
if [[ "$build_n" == "n" || "$build_n" == "N" ]] ; then
read -p "Select build type [RELEASE/debug]? "
if [[ "${REPLY,,}" == "debug" ]] ; then
REPLY=$(echo $REPLY | tr '[:upper:]' '[:lower:]')
if [[ "${REPLY}" == "debug" ]] ; then
build_type=Debug
new_build_name=aseprite-debug
else
@ -348,10 +341,7 @@ else
elif git --git-dir="$source_dir/.git" branch --contains "$remote/main" | grep -q "^\* $branch_name\$" ; then
base_branch_name=main
else
echo ""
echo "Error: Branch $branch_name looks like doesn't belong to main or beta"
echo ""
exit 1
base_branch_name=$branch_name
fi
fi
@ -366,15 +356,9 @@ else
fi
# Required Skia for the base branch.
if [ "$base_branch_name" == "beta" ] ; then
skia_tag=m124-08a5439a6b
file_skia_dir=beta_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
skia_tag=$(cat "$pwd/laf/misc/skia-tag.txt")
possible_skia_dir_name=skia-$(echo $skia_tag | cut -d "-" -f 1)
file_skia_dir="$base_branch_name"_skia_dir
# Check Skia dependency.
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"
fi
# Set default location if not found
if [ ! -d "$skia_dir" ] ; then
echo ""
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
# Use .deps directory to download Skia for users (which is a
# simple setup). In case of developers we'd prefer the shared
# directory by default.
if [ "$userkind" == "user" ] ; then
skia_dir="$pwd/.deps/$possible_skia_dir_name"
fi
if [ ! -d "$skia_dir" ] ; then
echo ""
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
mkdir -p $skia_dir || exit 1
fi
echo $skia_dir > "$pwd/.build/$file_skia_dir"
fi
skia_dir=$(echo -n $(cat $pwd/.build/$file_skia_dir))
skia_dir=$(cat $pwd/.build/$file_skia_dir)
if [ ! -d "$skia_dir" ] ; then
mkdir "$skia_dir"
fi
@ -421,27 +415,30 @@ if [ ! -d "$skia_library_dir" ] ; then
echo "Skia library wasn't found."
echo ""
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
if [[ $auto || "$REPLY" == "" || "$REPLY" == "y" || "$REPLY" == "Y" ]] ; then
if [[ $auto || "$REPLY" == "" || "$REPLY" == "y" || "$REPLY" == "yes" ]] ; then
if [[ $is_win && "$build_type" == "Debug" ]] ; then
skia_build=Debug
else
skia_build=Release
fi
if [ $is_win ] ; then
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
skia_url=$(bash laf/misc/skia-url.sh $skia_build)
skia_file=$(basename $skia_url)
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
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"
fi
else
@ -468,7 +465,7 @@ if [ ! -f "$active_build_dir/ninja.build" ] ; then
echo "This will take some minutes."
echo ""
if [ ! $auto ] ; then
read -sN 1 -p "Press any key to continue. "
read -p "Press Enter to continue."
fi
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_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_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_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" />
@ -374,6 +374,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_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_size" x="152" 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_selected_text" state="selected" />
</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 />
<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_selected_text" state="selected" />
</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 />
<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_selected_text" state="selected" />
</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 />
<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_selected_text" state="selected" />
</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 />
<icon part="window_help_icon" color="button_normal_text" />
<icon part="window_help_icon" color="button_hot_text" state="mouse" />
@ -550,10 +551,13 @@
<text color="disabled" align="left" state="disabled" />
</style>
<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">
<text color="text" align="left" x="2" />
</style>
<style id="link">
<style id="link" padding="1">
<text color="link_text" align="left" />
<text color="link_hover" align="left" state="mouse" />
</style>
@ -706,10 +710,11 @@
<style id="workspace_splitter">
<background color="workspace" />
</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-border part="separator_horz" align="middle" />
<text color="separator_label" x="4" align="left middle" />
<text color="disabled" x="4" align="left middle" state="disabled"/>
</style>
<style id="menu_separator" 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_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_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_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" />
@ -370,6 +370,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_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_size" x="152" 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_selected_text" state="selected" />
</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 />
<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_selected_text" state="selected" />
</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 />
<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_selected_text" state="selected" />
</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 />
<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_selected_text" state="selected" />
</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 />
<icon part="window_help_icon" color="button_normal_text" />
<icon part="window_help_icon" color="button_hot_text" state="mouse" />
@ -543,10 +544,13 @@
<text color="disabled" align="left" state="disabled" />
</style>
<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">
<text color="text" align="left" x="2" />
</style>
<style id="link">
<style id="link" padding="1">
<text color="link_text" align="left" />
<text color="link_hover" align="left" state="mouse" />
</style>
@ -699,10 +703,11 @@
<style id="workspace_splitter">
<background color="workspace" />
</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-border part="separator_horz" align="middle" />
<text color="separator_label" x="4" align="left middle" />
<text color="disabled" x="4" align="left middle" state="disabled"/>
</style>
<style id="menu_separator" 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"
type="spritesheet"
descent="2"
file="aseprite_font.png">
<fallback font="Unicode" size="8" />
<fallback font="Unicode" size="14" />
</font>
<font name="Aseprite Mini"
type="spritesheet"
descent="1"
file="aseprite_mini.png">
<fallback font="Unicode" size="6" />
<fallback font="Unicode" size="10" />
</font>
</fonts>

View File

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

View File

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

View File

@ -1,5 +1,5 @@
# Aseprite
# Copyright (C) 2018-2024 Igara Studio S.A.
# Copyright (C) 2018-2025 Igara Studio S.A.
# Copyright (C) 2016-2018 David Capello
#
# 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
layer_x_is_hidden = Layer "{}" is hidden
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
nothing_to_move = Nothing to move
recovery_task_using_sprite = Sprite is used by a backup/data recovery task
@ -489,6 +488,7 @@ TilesetDuplicate = Duplicate Tileset
Undo = Undo
UndoHistory = Undo History
UnlinkCel = Unlink Cel
ToggleWorkspaceLayout = Toggle Workspace Layout
Zoom = Zoom
Zoom_In = Zoom In
Zoom_Out = Zoom Out
@ -622,6 +622,14 @@ current_layer = Current Layer
first_ref_layer = First Reference Layer
pick = Pick:
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]
reload_stock = &Reload Stock
@ -766,6 +774,7 @@ pinned_folders = Pinned Folders
recent_folders = Recent Folders
all_formats = All formats
all_files = All files
show_hidden = Show hidden
[filters]
selected_cels = Selected
@ -786,6 +795,11 @@ load = Load External Font
select_truetype_fonts = Select a Font File
empty_fonts = No system fonts were found
[font_style]
antialias = Antialias
hinting = Hinting
ligatures = Ligatures
[frame_combo]
all_frames = All frames
selected_frames = Selected frames
@ -813,6 +827,12 @@ opacity = Opacity:
tolerance = Tolerance:
show_more = Show more...
[general_text]
copy = &Copy
cut = Cu&t
paste = &Paste
select_all = Select &All
[gif_options]
title = GIF Options
general_options = General Options:
@ -981,6 +1001,7 @@ visible_layers = Visible layers
[layer_properties]
title = Layer Properties
name = Name:
uuid = UUID:
mode = Mode:
opacity = Opacity:
tileset_tooltip = Tileset
@ -1155,6 +1176,7 @@ select_load_from_file = &Load from MSK file
select_save_to_file = &Save to MSK file
view = &View
view_duplicate_view = Duplicate &View
view_workspace_layout = Workspace &Layout
view_show_extras = &Extras
view_show = &Show
view_show_layer_edges = &Layer Edges
@ -1196,6 +1218,14 @@ help_twitter = Twitter
help_enter_license = Enter &License
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]
title = Select Color
label_color = Color:
@ -1224,6 +1254,20 @@ name = Name:
tileset = Tileset:
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]
more = More...
problem_loading = Problems loading news. Please retry.
@ -1326,7 +1370,7 @@ theme_mode = Theme Mode:
screen_scaling = Screen Scaling:
ui_scaling = UI Element Scaling:
language = Language:
download_translations = Download Translations
font_warning = Customize font for this language
gpu_acceleration = GPU acceleration [DEVMODE/INTERNAL TESTING ONLY]
gpu_acceleration_tooltip = Check this option to enable hardware acceleration
show_menu_bar = Show Aseprite menu bar
@ -1513,6 +1557,12 @@ alpha_range = Alpha Range:
opacity_range = Opacity Range:
8bit_value = 0-255
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
extension_themes = Extension Themes
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.
wintab_more_info = (More Information)
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:
cel_content_format = Cel content format:
cel_format_compressed = Compressed
cel_format_keep = Keep format as is in file
cel_format_raw = Raw image
cel_format_raw_warning = This will increase .aseprite file sizes considerably
reset_title = Reset Preferences
reset_default = Reset configuration options available in the Preferences window
reset_tools = Reset all tool preferences
@ -1659,6 +1711,11 @@ n_slices_removed = {} slice(s) removed
x_removed = Layer "{}" removed
layers_removed = Layers removed
[resource_listbox]
loading = Loading
pin = Pin this item
unpin = Unpin this item
[save_file]
title = Save File
save_as = Save As
@ -1675,7 +1732,8 @@ title = Save Selection (.msk file)
[script_access]
title = Security
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:
websocket_label = wants to open a WebSocket connection to this URL:
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
stop_script = &Stop Script
[select_accelerator]
[select_shortcut]
title = Keyboard Shortcut
key = Key:
clear = Clear
@ -1769,6 +1827,8 @@ change_sprite_props = Change Sprite Properties
tilesets = Tilesets
delete_tileset = Delete
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]
title = Sprite Size
@ -1814,16 +1874,26 @@ pixel_scale = Pixel Scale
with_vars = Use CSS3 Variables
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]
position = Position:
left = &Left
right = &Right
bottom = &Bottom
frame_header = Frame Header:
first_frame = First Frame:
thumbnails = Thumbnails
thumbnail_size = Thumbnail Size:
overlay_size = Overlay Size:
scale_up_to_fit = Scale up to fit
onion_skin = Onion Skin:
merge_frames = Merge Frames
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.
in_front = In front of sprite
in_front_toolip = For all kind of layers (background and transparent)
set_as_defaults = Set as Defaults
[tools]
rectangular_marquee = Rectangular Marquee Tool
@ -1874,6 +1945,7 @@ timeline_show = Show Timeline
[undo_history]
title = Undo History
initial_state = Initial State
[user_data]
user_data = User Data:

View File

@ -1,29 +1,14 @@
<!-- Aseprite -->
<!-- Copyright (C) 2018-2024 Igara Studio S.A. -->
<!-- Copyright (C) 2018-2025 Igara Studio S.A. -->
<!-- Copyright (C) 2018 David Capello -->
<gui i18nwarnings="false">
<window id="about" text="About Aseprite">
<vbox>
<label text="" id="title" />
<label text="Animated sprite editor &amp;&amp; pixel art tool" />
<hbox homogeneous="true">
<hbox>
<vbox expansive="true">
<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>
<link text="Authors &amp;&amp; Credits" url="" id="credits" />
<link text="Translators" url="" id="i18n_credits" />
<link text="Open Source Projects" url="" id="licenses" />
<separator horizontal="true" />
<hbox>
<label text="Copyright (C) 2001-2025" />

View File

@ -23,6 +23,7 @@
<combobox id="location" expansive="true" />
<button text="" id="refresh_button" style="refresh_button"
tooltip="@.refresh_button_tooltip" tooltip_dir="bottom" />
<check id="show_hidden_check" text="@.show_hidden" />
</box>
<vbox id="file_view_placeholder" expansive="true" />
<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" />
<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>
</vbox>
</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 -->
<!-- Copyright (C) 2018-2024 Igara Studio S.A. -->
<!-- Copyright (C) 2018-2025 Igara Studio S.A. -->
<!-- Copyright (C) 2001-2018 David Capello -->
<gui>
<window id="options" text="@.title" help="preferences">
@ -34,7 +35,7 @@
<!-- General -->
<vbox id="section_general">
<separator text="@.section_general" horizontal="true" />
<grid columns="3">
<grid columns="2">
<label text="@.ui_windows" />
<hbox>
<buttonset columns="2" id="ui_windows">
@ -45,7 +46,6 @@
<label text="@.theme_mode" />
</hbox>
</hbox>
<boxfiller />
<label text="@.screen_scaling" />
<combobox id="screen_scale">
@ -54,7 +54,6 @@
<listitem text="300%" value="3" />
<listitem text="400%" value="4" />
</combobox>
<boxfiller />
<label text="@.ui_scaling" />
<combobox id="ui_scale">
@ -63,12 +62,14 @@
<listitem text="300%" value="3" />
<listitem text="400%" value="4" />
</combobox>
<boxfiller />
<label text="@.language" />
<link text="@.language" url="https://www.aseprite.org/languages/" />
<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>
<separator horizontal="true" />
<check id="gpu_acceleration"
text="@.gpu_acceleration"
tooltip="@.gpu_acceleration_tooltip" />
@ -545,6 +546,20 @@
<!-- 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" />
<view expansive="true" maxsize="true">
<listbox id="theme_list" />
@ -581,6 +596,10 @@
<listitem text="@.cel_format_keep" />
<listitem text="@.cel_format_raw" />
</combobox>
<boxfiller />
<label id="cel_format_warning"
text="@.cel_format_raw_warning" style="warning_label" />
</grid>
</vbox>
@ -613,6 +632,7 @@
text="@.hue_with_sat_value"
pref="experimental.hue_with_sat_value_for_color_selector" />
<check id="flash_layer" text="@.flash_selected_layer" />
<check id="use_selection_tool_loop" text="@.use_selection_tool_loop" />
<hbox>
<label text="@.non_active_layer_opacity" />
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" />

View File

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

View File

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

View File

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

View File

@ -1,5 +1,34 @@
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/)
```

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

2
laf

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

View File

@ -1,5 +1,5 @@
# Aseprite
# Copyright (C) 2019-2024 Igara Studio S.A.
# Copyright (C) 2019-2025 Igara Studio S.A.
# Copyright (C) 2001-2018 David Capello
######################################################################
@ -152,6 +152,12 @@ add_custom_command(
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_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(
OUTPUT ${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)
set(main_resources
main/resources_win32.rc
main/settings.manifest)
main/win/resources_win32.rc
main/win/settings.manifest)
endif()
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):
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
You can check the platform using some `laf` macros:

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -26,6 +27,7 @@ using namespace doc;
class ClearRect : public Cmd {
public:
ClearRect(Cel* cel, const gfx::Rect& bounds);
ClearRect(Cel* cel, const gfx::Rect& bounds, color_t color);
protected:
void onExecute() override;
@ -37,6 +39,7 @@ protected:
}
private:
void initialize(Cel* cel, const gfx::Rect& bounds, color_t color);
void clear();
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
// Copyright (C) 2020-2024 Igara Studio S.A.
// Copyright (C) 2020-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -11,12 +11,12 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/modules/gui.h"
#include "app/ui/main_window.h"
#include "fmt/format.h"
#include "ver/info.h"
#include "about.xml.h"
#include "app/context.h"
namespace app {
@ -27,13 +27,19 @@ public:
AboutCommand();
protected:
bool onEnabled(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)
{
gen::About window;
@ -44,7 +50,7 @@ void AboutCommand::onExecute(Context* context)
});
window.credits()->Click.connect([&window] {
window.closeWindow(nullptr);
App::instance()->mainWindow()->showBrowser("README.md", "Authors");
App::instance()->mainWindow()->showBrowser("AUTHORS.md", "Authors");
});
window.i18nCredits()->Click.connect([&window] {
window.closeWindow(nullptr);

View File

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

View File

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

View File

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

View File

@ -37,7 +37,7 @@ private:
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_context.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "ui/ui.h"
@ -282,8 +281,7 @@ protected:
void onExecute(Context* context) override;
};
CanvasSizeCommand::CanvasSizeCommand()
: CommandWithNewParams(CommandId::CanvasSize(), CmdRecordableFlag)
CanvasSizeCommand::CanvasSizeCommand() : CommandWithNewParams(CommandId::CanvasSize())
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ protected:
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 "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace app {
@ -32,7 +31,7 @@ protected:
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();
}
if (nonEditableLayers)
if (context->isUIAvailable() && nonEditableLayers)
StatusBar::instance()->showTip(1000, Strings::statusbar_tips_locked_layers());
update_screen_for_document(document);

View File

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

View File

@ -12,15 +12,9 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/context_access.h"
#include "app/ui/doc_view.h"
#include "app/ui/status_bar.h"
#include "app/ui/workspace.h"
#include "app/ui_context.h"
#include "doc/sprite.h"
#include "ui/ui.h"
#include <memory>
namespace app {
@ -28,7 +22,7 @@ using namespace ui;
class CloseFileCommand : public Command {
public:
CloseFileCommand() : Command(CommandId::CloseFile(), CmdUIOnlyFlag) {}
CloseFileCommand() : Command(CommandId::CloseFile()) {}
protected:
bool onEnabled(Context* context) override
@ -51,12 +45,15 @@ protected:
class CloseAllFilesCommand : public Command {
public:
CloseAllFilesCommand() : Command(CommandId::CloseAllFiles(), CmdRecordableFlag)
{
m_quitting = false;
}
CloseAllFilesCommand() : Command(CommandId::CloseAllFiles()) { m_quitting = false; }
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 onExecute(Context* context) override

View File

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

View File

@ -24,7 +24,7 @@ protected:
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;
};
CopyCommand::CopyCommand() : Command(CommandId::Copy(), CmdUIOnlyFlag)
CopyCommand::CopyCommand() : Command(CommandId::Copy())
{
}

View File

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

View File

@ -24,7 +24,7 @@ protected:
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/tx.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/sprite.h"
namespace app {
@ -37,7 +33,7 @@ private:
gfx::Rect m_bounds;
};
CropSpriteCommand::CropSpriteCommand() : Command(CommandId::CropSprite(), CmdRecordableFlag)
CropSpriteCommand::CropSpriteCommand() : Command(CommandId::CropSprite())
{
}
@ -95,8 +91,7 @@ private:
bool m_byGrid = false;
};
AutocropSpriteCommand::AutocropSpriteCommand()
: Command(CommandId::AutocropSprite(), CmdRecordableFlag)
AutocropSpriteCommand::AutocropSpriteCommand() : Command(CommandId::AutocropSprite())
{
}

View File

@ -23,7 +23,7 @@ protected:
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/modules/gui.h"
#include "app/tx.h"
#include "doc/mask.h"
#include "doc/sprite.h"
namespace app {
@ -28,7 +26,7 @@ protected:
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);
};
DeveloperConsoleCommand::DeveloperConsoleCommand()
: Command(CommandId::DeveloperConsole(), CmdUIOnlyFlag)
DeveloperConsoleCommand::DeveloperConsoleCommand() : Command(CommandId::DeveloperConsole())
{
}

View File

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

View File

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

View File

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

View File

@ -13,8 +13,6 @@
#include "app/commands/command.h"
#include "app/ui/workspace.h"
#include <cstdio>
namespace app {
// using namespace ui;
@ -28,7 +26,7 @@ protected:
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.
#include "app/commands/command.h"
#include "app/context.h"
#ifdef ENABLE_DRM
#include "app/ui/enter_license.h"
@ -24,7 +23,7 @@ protected:
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/job.h"
#include "app/ui/main_window.h"
#include "ui/alert.h"
#ifdef ENABLE_SCRIPTING
#include "app/commands/debugger.h"
@ -32,7 +31,7 @@ protected:
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/timeline/timeline.h"
#include "app/util/layer_utils.h"
#include "base/convert_to.h"
#include "base/fs.h"
#include "base/string.h"
#include "base/thread.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/tag.h"
#include "doc/tileset.h"
#include "doc/tilesets.h"
#include "fmt/format.h"
#include "ui/message.h"
#include "ui/system.h"
@ -1187,8 +1184,7 @@ private:
} // anonymous namespace
ExportSpriteSheetCommand::ExportSpriteSheetCommand(const char* id)
: CommandWithNewParams(id, CmdRecordableFlag)
ExportSpriteSheetCommand::ExportSpriteSheetCommand(const char* id) : CommandWithNewParams(id)
{
}

View File

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

View File

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

View File

@ -11,7 +11,6 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/ui/editor/editor.h"
namespace app {
@ -25,7 +24,7 @@ protected:
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;
};
FlattenLayersCommand::FlattenLayersCommand() : Command(CommandId::FlattenLayers(), CmdUIOnlyFlag)
FlattenLayersCommand::FlattenLayersCommand() : Command(CommandId::FlattenLayers())
{
m_visibleOnly = false;
}

View File

@ -20,7 +20,6 @@
#include "app/commands/params.h"
#include "app/context_access.h"
#include "app/doc_api.h"
#include "app/doc_range.h"
#include "app/i18n/strings.h"
#include "app/modules/gui.h"
#include "app/tools/tool_box.h"
@ -38,11 +37,10 @@
#include "doc/layer.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "gfx/size.h"
namespace app {
FlipCommand::FlipCommand() : Command(CommandId::Flip(), CmdRecordableFlag)
FlipCommand::FlipCommand() : Command(CommandId::Flip())
{
m_flipMask = false;
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
// number indicating a specific frame (1 is the first frame).
Target m_target;
frame_t m_frame;
Target m_target = CURRENT_RANGE;
frame_t m_frame = 1;
};
FramePropertiesCommand::FramePropertiesCommand()
: Command(CommandId::FrameProperties(), CmdUIOnlyFlag)
FramePropertiesCommand::FramePropertiesCommand() : Command(CommandId::FrameProperties())
{
}
@ -59,7 +58,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
if (frame == "all") {
m_target = ALL_FRAMES;
}
else if (frame == "current") {
else if (frame == "current" || !params.has_param("frame")) {
m_target = CURRENT_RANGE;
}
else {
@ -70,7 +69,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
bool FramePropertiesCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
return context->isUIAvailable() && context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
}
void FramePropertiesCommand::onExecute(Context* context)

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ protected:
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;
};
GotoPreviousTabCommand::GotoPreviousTabCommand()
: Command(CommandId::GotoPreviousTab(), CmdRecordableFlag)
GotoPreviousTabCommand::GotoPreviousTabCommand() : Command(CommandId::GotoPreviousTab())
{
}

View File

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

View File

@ -10,6 +10,7 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context.h"
#include "app/ui/main_window.h"
namespace app {
@ -26,7 +27,7 @@ protected:
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)
{
return !App::instance()->mainWindow()->isHomeSelected();
return context->isUIAvailable() && !App::instance()->mainWindow()->isHomeSelected();
}
Command* CommandFactory::createHomeCommand()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,11 +9,9 @@
#include "config.h"
#endif
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/modules/gui.h"
#include "doc/image.h"
#include "doc/layer.h"
namespace app {
@ -30,7 +28,7 @@ protected:
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"
#endif
#include "app/app.h"
#include "app/cmd/set_layer_opacity.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
@ -40,7 +39,7 @@ private:
int m_opacity;
};
LayerOpacityCommand::LayerOpacityCommand() : Command(CommandId::LayerOpacity(), CmdUIOnlyFlag)
LayerOpacityCommand::LayerOpacityCommand() : Command(CommandId::LayerOpacity())
{
m_opacity = 255;
}

View File

@ -32,8 +32,8 @@
#include "app/ui/timeline/timeline.h"
#include "app/ui/user_data_view.h"
#include "app/ui_context.h"
#include "base/convert_to.h"
#include "base/scoped_value.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/sprite.h"
@ -134,8 +134,16 @@ public:
remapWindow();
centerWindow();
gfx::Rect originalBounds = bounds();
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);
}
@ -158,6 +166,11 @@ public:
if (countLayers() > 0) {
m_userDataView.configureAndSet(m_layer->userData(), g_window->propertiesGrid());
if (m_remapAfterConfigure) {
remapWindow();
centerWindow();
m_remapAfterConfigure = false;
}
}
updateFromLayer();
@ -463,6 +476,13 @@ private:
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) {
tileset()->setVisible(tilemapVisibility);
tileset()->parent()->layout();
@ -476,16 +496,17 @@ private:
view::RealRange m_range;
bool m_selfUpdate = false;
UserDataView m_userDataView;
bool m_remapAfterConfigure = false;
};
LayerPropertiesCommand::LayerPropertiesCommand()
: Command(CommandId::LayerProperties(), CmdRecordableFlag)
LayerPropertiesCommand::LayerPropertiesCommand() : Command(CommandId::LayerProperties())
{
}
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)

View File

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

View File

@ -18,7 +18,6 @@
#include "app/ui/status_bar.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace app {
@ -31,7 +30,7 @@ protected:
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();
}
if (nonEditableLayers)
if (context->isUIAvailable() && nonEditableLayers)
StatusBar::instance()->showTip(1000, Strings::statusbar_tips_locked_layers());
update_screen_for_document(document);

View File

@ -26,6 +26,7 @@ namespace app {
class LoadMaskCommand : public Command {
std::string m_filename;
bool m_ui = true;
public:
LoadMaskCommand();
@ -36,13 +37,16 @@ protected:
void onExecute(Context* context) override;
};
LoadMaskCommand::LoadMaskCommand() : Command(CommandId::LoadMask(), CmdRecordableFlag)
LoadMaskCommand::LoadMaskCommand() : Command(CommandId::LoadMask())
{
m_filename = "";
}
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");
}
@ -55,7 +59,7 @@ void LoadMaskCommand::onExecute(Context* context)
{
const ContextReader reader(context);
if (context->isUIAvailable()) {
if (context->isUIAvailable() && m_ui) {
base::paths exts = { "msk" };
base::paths selectedFilename;
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()));
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;
}

View File

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

View File

@ -29,7 +29,7 @@ protected:
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/commands/command.h"
#include "app/commands/new_params.h"
#include "app/console.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/doc.h"
@ -26,9 +25,6 @@
#include "app/ui/color_bar.h"
#include "app/ui/color_button.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/mask.h"
#include "doc/sprite.h"
@ -203,8 +199,7 @@ protected:
void onExecute(Context* context) override;
};
MaskByColorCommand::MaskByColorCommand()
: CommandWithNewParams(CommandId::MaskByColor(), CmdUIOnlyFlag)
MaskByColorCommand::MaskByColorCommand() : CommandWithNewParams(CommandId::MaskByColor())
{
}

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