mirror of https://github.com/aseprite/aseprite.git
Compare commits
7 Commits
442f82bf64
...
a57f70f9ae
Author | SHA1 | Date |
---|---|---|
|
a57f70f9ae | |
|
cef92c1a38 | |
|
22e72ab5cb | |
|
80fa065bd5 | |
|
de1ccb24dd | |
|
7d91c4b9d9 | |
|
f1fb569983 |
2
laf
2
laf
|
@ -1 +1 @@
|
|||
Subproject commit a2bb9ec7fb98354279a2c49870a4a47a67a8e86e
|
||||
Subproject commit 8ec4b553f1618f7a4b47cdcf4cfc2663266111ac
|
|
@ -180,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}
|
||||
|
|
|
@ -213,7 +213,7 @@ void ResourceFinder::includeDesktopDir(const char* filename)
|
|||
#ifdef _WIN32
|
||||
|
||||
std::vector<wchar_t> buf(MAX_PATH);
|
||||
HRESULT hr = SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_DEFAULT, &buf[0]);
|
||||
HRESULT hr = SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, &buf[0]);
|
||||
if (hr == S_OK) {
|
||||
addPath(base::join_path(base::to_utf8(&buf[0]), filename));
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "app/console.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_observer.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_undo.h"
|
||||
#include "app/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include "doc/tag.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "doc/tilesets.h"
|
||||
#include "undo/undo_state.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -1029,6 +1030,42 @@ int Sprite_set_useLayerUuids(lua_State* L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Sprite_get_undoHistory(lua_State* L)
|
||||
{
|
||||
const auto* sprite = get_docobj<Sprite>(L, 1);
|
||||
const auto* doc = static_cast<Doc*>(sprite->document());
|
||||
const auto* history = doc->undoHistory();
|
||||
|
||||
if (!history) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const undo::UndoState* currentState = history->currentState();
|
||||
const undo::UndoState* s = history->firstState();
|
||||
const bool canRedo = history->canRedo();
|
||||
bool pastCurrent = !currentState && canRedo;
|
||||
|
||||
int undoSteps = 0;
|
||||
int redoSteps = 0;
|
||||
while (s) {
|
||||
if (pastCurrent && canRedo)
|
||||
redoSteps++;
|
||||
else if (currentState || !canRedo)
|
||||
undoSteps++;
|
||||
|
||||
if (s == currentState || !currentState)
|
||||
pastCurrent = true;
|
||||
|
||||
s = s->next();
|
||||
}
|
||||
|
||||
lua_newtable(L);
|
||||
setfield_integer(L, "undoSteps", undoSteps);
|
||||
setfield_integer(L, "redoSteps", redoSteps);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const luaL_Reg Sprite_methods[] = {
|
||||
{ "__eq", Sprite_eq },
|
||||
{ "resize", Sprite_resize },
|
||||
|
@ -1094,6 +1131,7 @@ const Property Sprite_properties[] = {
|
|||
{ "events", Sprite_get_events, nullptr },
|
||||
{ "tileManagementPlugin", Sprite_get_tileManagementPlugin, Sprite_set_tileManagementPlugin },
|
||||
{ "useLayerUuids", Sprite_get_useLayerUuids, Sprite_set_useLayerUuids },
|
||||
{ "undoHistory", Sprite_get_undoHistory, nullptr },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
|
|
@ -443,12 +443,7 @@ bool WritingTextState::onSetCursor(Editor* editor, const gfx::Point& mouseScreen
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WritingTextState::onKeyDown(Editor*, KeyMessage*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WritingTextState::onKeyUp(Editor*, KeyMessage* msg)
|
||||
bool WritingTextState::onKeyDown(Editor*, KeyMessage* msg)
|
||||
{
|
||||
// Cancel loop pressing Esc key
|
||||
if (msg->scancode() == ui::kKeyEsc) {
|
||||
|
@ -457,7 +452,17 @@ bool WritingTextState::onKeyUp(Editor*, KeyMessage* msg)
|
|||
// Drop text pressing Enter key
|
||||
else if (msg->scancode() == ui::kKeyEnter) {
|
||||
drop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WritingTextState::onKeyUp(Editor*, KeyMessage* msg)
|
||||
{
|
||||
// Note: We cannot process kKeyEnter key here to drop the text as it
|
||||
// could be received after the Enter key is pressed in the IME
|
||||
// dialog to accept the composition (not to accept the text). So we
|
||||
// process kKeyEnter in onKeyDown().
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,37 +1,13 @@
|
|||
# Desktop Integration
|
||||
# Copyright (c) 2022 Igara Studio S.A.
|
||||
# Copyright (c) 2022-2025 Igara Studio S.A.
|
||||
|
||||
find_library(QUARTZ_LIBRARY Quartz)
|
||||
find_library(QUICKLOOK_LIBRARY QuickLookThumbnailing)
|
||||
|
||||
add_library(AsepriteThumbnailer SHARED
|
||||
main.mm
|
||||
thumbnail.mm)
|
||||
add_library(AsepriteThumbnailer MODULE
|
||||
appex/thumbnails.mm)
|
||||
|
||||
target_link_libraries(AsepriteThumbnailer
|
||||
laf-base
|
||||
dio-lib
|
||||
render-lib
|
||||
${QUARTZ_LIBRARY})
|
||||
|
||||
set_target_properties(AsepriteThumbnailer PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/AsepriteThumbnailer.qlgenerator
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/AsepriteThumbnailer.qlgenerator/Contents
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory lib/AsepriteThumbnailer.qlgenerator/Contents/MacOS
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
lib/AsepriteThumbnailer.framework/Versions/A/AsepriteThumbnailer
|
||||
lib/AsepriteThumbnailer.qlgenerator/Contents/MacOS
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
lib/AsepriteThumbnailer.framework/Versions/A/Resources/Info.plist
|
||||
lib/AsepriteThumbnailer.qlgenerator/Contents
|
||||
BYPRODUCTS ${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator/Contents/MacOS/AsepriteThumbnailer
|
||||
${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator/Contents/Info.plist
|
||||
DEPENDS AsepriteThumbnailer)
|
||||
|
||||
add_custom_target(AsepriteThumbnailer.qlgenerator
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/lib/AsepriteThumbnailer.qlgenerator)
|
||||
${QUICKLOOK_LIBRARY}
|
||||
)
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>QLGenerator</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>dyn.ah62d4rv4ge80c65f</string>
|
||||
<string>dyn.ah62d4rv4ge80c65fsb3gw7df</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.aseprite.AsepriteThumbnailer</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CFPlugInFactories</key>
|
||||
<dict>
|
||||
<key>A5E9417E-6E7A-4B2D-85A4-84E114D7A960</key>
|
||||
<string>QuickLookGeneratorPluginFactory</string>
|
||||
</dict>
|
||||
<key>CFPlugInTypes</key>
|
||||
<dict>
|
||||
<key>5E2D9680-5022-40FA-B806-43349622E5B9</key>
|
||||
<array>
|
||||
<string>A5E9417E-6E7A-4B2D-85A4-84E114D7A960</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>CFPlugInUnloadFunction</key>
|
||||
<string></string>
|
||||
<key>QLThumbnailMinimumSize</key>
|
||||
<real>32</real>
|
||||
<key>QLPreviewWidth</key>
|
||||
<real>256</real>
|
||||
<key>QLPreviewHeight</key>
|
||||
<real>256</real>
|
||||
<key>QLNeedsToBeRunInMainThread</key>
|
||||
<true/>
|
||||
<key>QLSupportsConcurrentRequests</key>
|
||||
<false/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © Igara Studio S.A. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.aseprite.aseprite.sprite</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Aseprite Sprite</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.image</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>aseprite</string>
|
||||
<string>ase</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>image/aseprite</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.aseprite.aseprite</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.3.dev</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.dev</string>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>24C101</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>aseprite</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>1.3.dev</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSHasPlugIns</key>
|
||||
<true/>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>24C94</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>15.2</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>24C94</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx15.2</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1620</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>16C5032a</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>$ASEPRITE_BUNDLE_ID.sprite</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Aseprite Sprite</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.image</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>aseprite</string>
|
||||
<string>ase</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<string>image/aseprite</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$ASEPRITE_BUNDLE_ID</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$VERSION</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$VERSION</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>aseprite</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSHasPlugIns</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.download.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.pictures.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.movies.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.music.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.aseprite.aseprite.AsepriteThumbnailer</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.3.dev</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3.dev</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>QLSupportedContentTypes</key>
|
||||
<array>
|
||||
<string>org.aseprite.aseprite.sprite</string>
|
||||
</array>
|
||||
<key>QLThumbnailMinimumDimension</key>
|
||||
<integer>32</integer>
|
||||
</dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.quicklook.thumbnail</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>ThumbnailProvider</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>AsepriteThumbnailer</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${THUMBNAILS_BUNDLE_ID}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${VERSION}</string>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>QLSupportedContentTypes</key>
|
||||
<array>
|
||||
<string>${ASEPRITE_BUNDLE_ID}.sprite</string>
|
||||
</array>
|
||||
<key>QLThumbnailMinimumDimension</key>
|
||||
<integer>32</integer>
|
||||
</dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.quicklook.thumbnail</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>ThumbnailProvider</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,11 +1,10 @@
|
|||
// Desktop Integration
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
// Aseprite
|
||||
// Copyright (c) 2025 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include "thumbnail.h"
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#include "app/file_system.h"
|
||||
#include "dio/decode_delegate.h"
|
||||
#include "dio/decode_file.h"
|
||||
#include "dio/file_interface.h"
|
||||
|
@ -13,10 +12,7 @@
|
|||
#include "render/render.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace desktop {
|
||||
#include <QuickLookThumbnailing/QuickLookThumbnailing.h>
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -38,13 +34,13 @@ class StreamAdaptor : public dio::FileInterface {
|
|||
public:
|
||||
StreamAdaptor(NSData* data) : m_data(data), m_ok(m_data != nullptr), m_pos(0) {}
|
||||
|
||||
bool ok() const { return m_ok; }
|
||||
bool ok() const override { return m_ok; }
|
||||
|
||||
size_t tell() { return m_pos; }
|
||||
size_t tell() override { return m_pos; }
|
||||
|
||||
void seek(size_t absPos) { m_pos = absPos; }
|
||||
void seek(size_t absPos) override { m_pos = absPos; }
|
||||
|
||||
uint8_t read8()
|
||||
uint8_t read8() override
|
||||
{
|
||||
if (!m_ok)
|
||||
return 0;
|
||||
|
@ -57,7 +53,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
size_t readBytes(uint8_t* buf, size_t n)
|
||||
size_t readBytes(uint8_t* buf, size_t n) override
|
||||
{
|
||||
if (!m_ok)
|
||||
return 0;
|
||||
|
@ -75,21 +71,16 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void write8(uint8_t value)
|
||||
{
|
||||
// Do nothing, we don't write in the file
|
||||
}
|
||||
void write8(uint8_t value) override {};
|
||||
|
||||
NSData* m_data;
|
||||
bool m_ok;
|
||||
size_t m_pos;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
CGImageRef get_thumbnail(CFURLRef url, CFDictionaryRef options, CGSize maxSize)
|
||||
CGImageRef get_thumbnail(CFURLRef url, CGSize maxSize)
|
||||
{
|
||||
auto data = [[NSData alloc] initWithContentsOfURL:(NSURL*)url];
|
||||
auto data = [[NSData alloc] initWithContentsOfURL:(__bridge NSURL*)url];
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
|
@ -152,4 +143,52 @@ CGImageRef get_thumbnail(CFURLRef url, CFDictionaryRef options, CGSize maxSize)
|
|||
return img;
|
||||
}
|
||||
|
||||
} // namespace desktop
|
||||
} // namespace
|
||||
|
||||
@interface ThumbnailProvider : QLThumbnailProvider
|
||||
@end
|
||||
|
||||
@implementation ThumbnailProvider
|
||||
|
||||
- (void)provideThumbnailForFileRequest:(QLFileThumbnailRequest*)request
|
||||
completionHandler:
|
||||
(void (^)(QLThumbnailReply* _Nullable, NSError* _Nullable))handler
|
||||
{
|
||||
CFURLRef url = (__bridge CFURLRef)(request.fileURL);
|
||||
CGSize maxSize = request.maximumSize;
|
||||
CGImageRef cgImage = get_thumbnail(url, maxSize);
|
||||
if (!cgImage) {
|
||||
handler(nil, nil);
|
||||
return;
|
||||
}
|
||||
CGSize imageSize = CGSizeMake(CGImageGetWidth(cgImage), CGImageGetHeight(cgImage));
|
||||
handler([QLThumbnailReply replyWithContextSize:maxSize
|
||||
currentContextDrawingBlock:^BOOL {
|
||||
CGContextRef ctx = [[NSGraphicsContext currentContext] CGContext];
|
||||
CGContextSaveGState(ctx);
|
||||
CGFloat scale = MIN(maxSize.width / imageSize.width,
|
||||
maxSize.height / imageSize.height);
|
||||
CGSize scaledSize = CGSizeMake(imageSize.width * scale,
|
||||
imageSize.height * scale);
|
||||
CGRect drawRect = CGRectMake((maxSize.width - scaledSize.width) / 2,
|
||||
(maxSize.height - scaledSize.height) / 2,
|
||||
scaledSize.width,
|
||||
scaledSize.height);
|
||||
CGContextDrawImage(ctx, drawRect, cgImage);
|
||||
CGContextRestoreGState(ctx);
|
||||
CGImageRelease(cgImage);
|
||||
return YES;
|
||||
}],
|
||||
nil);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// This constructor ensures that the ThumbnailProvider class is explicitly referenced
|
||||
// during linking. Without this, the linker may discard the symbol, preventing the QuickLook
|
||||
// from discovering the provider. This is required to ensure the symbol
|
||||
// OBJC_CLASS_$_ThumbnailProvider is exported correctly from the .appex binary.
|
||||
__attribute__((constructor)) static void forceExportThumbnailProvider()
|
||||
{
|
||||
[ThumbnailProvider class];
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
// Desktop Integration
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#include <CoreFoundation/CFPlugInCOM.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <QuickLook/QuickLook.h>
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "thumbnail.h"
|
||||
|
||||
// Just as a side note: We're using the same UUID as the Windows
|
||||
// Aseprite thumbnailer.
|
||||
//
|
||||
// If you're going to use this code, remember to change this UUID and
|
||||
// change it in the Info.plist file.
|
||||
#define PLUGIN_ID "A5E9417E-6E7A-4B2D-85A4-84E114D7A960"
|
||||
|
||||
static HRESULT Plugin_QueryInterface(void*, REFIID, LPVOID*);
|
||||
static ULONG Plugin_AddRef(void*);
|
||||
static ULONG Plugin_Release(void*);
|
||||
static OSStatus Plugin_GenerateThumbnailForURL(void*,
|
||||
QLThumbnailRequestRef,
|
||||
CFURLRef,
|
||||
CFStringRef,
|
||||
CFDictionaryRef,
|
||||
CGSize);
|
||||
static void Plugin_CancelThumbnailGeneration(void*, QLThumbnailRequestRef);
|
||||
static OSStatus Plugin_GeneratePreviewForURL(void*,
|
||||
QLPreviewRequestRef,
|
||||
CFURLRef,
|
||||
CFStringRef,
|
||||
CFDictionaryRef);
|
||||
static void Plugin_CancelPreviewGeneration(void*, QLPreviewRequestRef);
|
||||
|
||||
static QLGeneratorInterfaceStruct Plugin_vtbl = { // kQLGeneratorTypeID interface
|
||||
// IUnknown
|
||||
nullptr, // void* reserved
|
||||
Plugin_QueryInterface,
|
||||
Plugin_AddRef,
|
||||
Plugin_Release,
|
||||
// QLGeneratorInterface
|
||||
Plugin_GenerateThumbnailForURL,
|
||||
Plugin_CancelThumbnailGeneration,
|
||||
Plugin_GeneratePreviewForURL,
|
||||
Plugin_CancelPreviewGeneration
|
||||
};
|
||||
|
||||
// TODO it would be nice to create a C++ smart pointer/wrapper for CFUUIDRef type
|
||||
|
||||
struct Plugin {
|
||||
QLGeneratorInterfaceStruct* interface; // Must be a pointer
|
||||
CFUUIDRef factoryID;
|
||||
ULONG refCount = 1; // Starts with one reference when it's created
|
||||
|
||||
Plugin(CFUUIDRef factoryID)
|
||||
: interface(new QLGeneratorInterfaceStruct(Plugin_vtbl))
|
||||
, factoryID(factoryID)
|
||||
{
|
||||
CFPlugInAddInstanceForFactory(factoryID);
|
||||
}
|
||||
|
||||
~Plugin()
|
||||
{
|
||||
delete interface;
|
||||
if (factoryID) {
|
||||
CFPlugInRemoveInstanceForFactory(factoryID);
|
||||
CFRelease(factoryID);
|
||||
}
|
||||
}
|
||||
|
||||
// IUnknown impl
|
||||
|
||||
HRESULT QueryInterface(REFIID iid, LPVOID* ppv)
|
||||
{
|
||||
CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, iid);
|
||||
|
||||
if (CFEqual(interfaceID, kQLGeneratorCallbacksInterfaceID)) {
|
||||
*ppv = this;
|
||||
AddRef();
|
||||
CFRelease(interfaceID);
|
||||
return S_OK;
|
||||
}
|
||||
else {
|
||||
*ppv = nullptr;
|
||||
CFRelease(interfaceID);
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
ULONG AddRef() { return ++refCount; }
|
||||
|
||||
ULONG Release()
|
||||
{
|
||||
if (refCount == 1) {
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
ASSERT(refCount != 0);
|
||||
return --refCount;
|
||||
}
|
||||
}
|
||||
|
||||
// QLGeneratorInterfaceStruct impl
|
||||
|
||||
static OSStatus GenerateThumbnailForURL(QLThumbnailRequestRef thumbnail,
|
||||
CFURLRef url,
|
||||
CFStringRef contentTypeUTI,
|
||||
CFDictionaryRef options,
|
||||
CGSize maxSize)
|
||||
{
|
||||
CGImageRef image = desktop::get_thumbnail(url, options, maxSize);
|
||||
if (!image)
|
||||
return -1;
|
||||
|
||||
QLThumbnailRequestSetImage(thumbnail, image, nullptr);
|
||||
CGImageRelease(image);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void CancelThumbnailGeneration(QLThumbnailRequestRef thumbnail)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
OSStatus GeneratePreviewForURL(QLPreviewRequestRef preview,
|
||||
CFURLRef url,
|
||||
CFStringRef contentTypeUTI,
|
||||
CFDictionaryRef options)
|
||||
{
|
||||
CGImageRef image = desktop::get_thumbnail(url, options, CGSizeMake(0, 0));
|
||||
if (!image)
|
||||
return -1;
|
||||
|
||||
int w = CGImageGetWidth(image);
|
||||
int h = CGImageGetHeight(image);
|
||||
int wh = std::min(w, h);
|
||||
if (wh < 128) {
|
||||
w = 128 * w / wh;
|
||||
h = 128 * h / wh;
|
||||
}
|
||||
|
||||
CGContextRef cg = QLPreviewRequestCreateContext(preview, CGSizeMake(w, h), YES, options);
|
||||
CGContextSetInterpolationQuality(cg, kCGInterpolationNone);
|
||||
CGContextDrawImage(cg, CGRectMake(0, 0, w, h), image);
|
||||
QLPreviewRequestFlushContext(preview, cg);
|
||||
CGImageRelease(image);
|
||||
CGContextRelease(cg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CancelPreviewGeneration(QLPreviewRequestRef preview)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
};
|
||||
|
||||
static HRESULT Plugin_QueryInterface(void* p, REFIID iid, LPVOID* ppv)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->QueryInterface(iid, ppv);
|
||||
}
|
||||
|
||||
static ULONG Plugin_AddRef(void* p)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->AddRef();
|
||||
}
|
||||
|
||||
static ULONG Plugin_Release(void* p)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->Release();
|
||||
}
|
||||
|
||||
static OSStatus Plugin_GenerateThumbnailForURL(void* p,
|
||||
QLThumbnailRequestRef thumbnail,
|
||||
CFURLRef url,
|
||||
CFStringRef contentTypeUTI,
|
||||
CFDictionaryRef options,
|
||||
CGSize maxSize)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->GenerateThumbnailForURL(thumbnail,
|
||||
url,
|
||||
contentTypeUTI,
|
||||
options,
|
||||
maxSize);
|
||||
}
|
||||
|
||||
static void Plugin_CancelThumbnailGeneration(void* p, QLThumbnailRequestRef thumbnail)
|
||||
{
|
||||
ASSERT(p);
|
||||
reinterpret_cast<Plugin*>(p)->CancelThumbnailGeneration(thumbnail);
|
||||
}
|
||||
|
||||
static OSStatus Plugin_GeneratePreviewForURL(void* p,
|
||||
QLPreviewRequestRef preview,
|
||||
CFURLRef url,
|
||||
CFStringRef contentTypeUTI,
|
||||
CFDictionaryRef options)
|
||||
{
|
||||
ASSERT(p);
|
||||
return reinterpret_cast<Plugin*>(p)->GeneratePreviewForURL(preview, url, contentTypeUTI, options);
|
||||
}
|
||||
|
||||
static void Plugin_CancelPreviewGeneration(void* p, QLPreviewRequestRef preview)
|
||||
{
|
||||
ASSERT(p);
|
||||
reinterpret_cast<Plugin*>(p)->CancelPreviewGeneration(preview);
|
||||
}
|
||||
|
||||
// This is the only public entry point of the framework/plugin (the
|
||||
// "QuickLookGeneratorPluginFactory" name is specified in the
|
||||
// Info.list file): the factory of objects. Similar than the Win32 COM
|
||||
// IClassFactory::CreateInstance()
|
||||
//
|
||||
// This function is used to create an instance of an object of
|
||||
// kQLGeneratorTypeID type, which should implement the
|
||||
// QLGeneratorInterfaceStruct interface.
|
||||
extern "C" void* QuickLookGeneratorPluginFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
|
||||
{
|
||||
if (CFEqual(typeID, kQLGeneratorTypeID)) {
|
||||
CFUUIDRef uuid = CFUUIDCreateFromString(kCFAllocatorDefault, CFSTR(PLUGIN_ID));
|
||||
auto plugin = new Plugin(uuid);
|
||||
CFRelease(uuid);
|
||||
return plugin;
|
||||
}
|
||||
return nullptr; // Unknown typeID
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Desktop Integration
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DESKTOP_THUMBNAIL_H_INCLUDED
|
||||
#define DESKTOP_THUMBNAIL_H_INCLUDED
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
namespace desktop {
|
||||
|
||||
CGImageRef get_thumbnail(CFURLRef url, CFDictionaryRef options, CGSize maxSize);
|
||||
|
||||
} // namespace desktop
|
||||
|
||||
#endif
|
|
@ -2,9 +2,9 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,86 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>aseprite</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>Document.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Aseprite Sprite</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>ase</string>
|
||||
<string>bmp</string>
|
||||
<string>flc</string>
|
||||
<string>fli</string>
|
||||
<string>gif</string>
|
||||
<string>ico</string>
|
||||
<string>jpeg</string>
|
||||
<string>jpg</string>
|
||||
<string>pcx</string>
|
||||
<string>png</string>
|
||||
<string>tga</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>Document.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Aseprite Sprite</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>aseprite-extension</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>Extension.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Aseprite Extension</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Aseprite</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>aseprite</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.aseprite.Aseprite</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Aseprite</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.3</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Aseprite.icns</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.graphics-design</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2001-2025, Igara Studio S.A.
|
||||
All rights reserved.</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSRequiresAquaSystemAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -228,3 +228,69 @@ do
|
|||
c = app.open(fn)
|
||||
assert(c.tileManagementPlugin == nil)
|
||||
end
|
||||
|
||||
-- Undo History
|
||||
|
||||
function test_undo_history()
|
||||
local sprite = Sprite(1, 1)
|
||||
|
||||
assert(sprite.undoHistory.undoSteps == 0)
|
||||
assert(sprite.undoHistory.redoSteps == 0)
|
||||
|
||||
sprite:resize(10, 10)
|
||||
|
||||
assert(sprite.undoHistory.undoSteps == 1)
|
||||
assert(sprite.undoHistory.redoSteps == 0)
|
||||
|
||||
sprite:resize(10, 15)
|
||||
|
||||
assert(sprite.undoHistory.undoSteps == 2)
|
||||
assert(sprite.undoHistory.redoSteps == 0)
|
||||
|
||||
sprite:resize(10, 30)
|
||||
|
||||
assert(sprite.undoHistory.undoSteps == 3)
|
||||
assert(sprite.undoHistory.redoSteps == 0)
|
||||
|
||||
app.undo()
|
||||
assert(sprite.undoHistory.undoSteps == 2)
|
||||
assert(sprite.undoHistory.redoSteps == 1)
|
||||
|
||||
app.undo()
|
||||
assert(sprite.undoHistory.undoSteps == 1)
|
||||
assert(sprite.undoHistory.redoSteps == 2)
|
||||
|
||||
app.redo()
|
||||
assert(sprite.undoHistory.undoSteps == 2)
|
||||
assert(sprite.undoHistory.redoSteps == 1)
|
||||
|
||||
app.undo()
|
||||
app.undo()
|
||||
|
||||
assert(sprite.undoHistory.undoSteps == 0)
|
||||
assert(sprite.undoHistory.redoSteps == 3)
|
||||
|
||||
sprite:resize(10, 30)
|
||||
|
||||
if (app.preferences.undo.allow_nonlinear_history) then
|
||||
assert(sprite.undoHistory.undoSteps == 4)
|
||||
assert(sprite.undoHistory.redoSteps == 0)
|
||||
else
|
||||
assert(sprite.undoHistory.undoSteps == 1)
|
||||
assert(sprite.undoHistory.redoSteps == 0)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local prevSetting = app.preferences.undo.allow_nonlinear_history
|
||||
app.preferences.undo.allow_nonlinear_history = true
|
||||
test_undo_history()
|
||||
app.preferences.undo.allow_nonlinear_history = prevSetting
|
||||
end
|
||||
|
||||
do
|
||||
local prevSetting = app.preferences.undo.allow_nonlinear_history
|
||||
app.preferences.undo.allow_nonlinear_history = false
|
||||
test_undo_history()
|
||||
app.preferences.undo.allow_nonlinear_history = prevSetting
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue