Compare commits

...

14 Commits

Author SHA1 Message Date
Christian Kaiser 44d6735ecf Improve StatusBar tooltips, add app.tip 2025-08-08 12:34:16 -03:00
David Capello b2b2583176 [lua] Update scripting API version to 35 2025-08-07 20:18:10 -03:00
David Capello b535212642 Update laf module 2025-08-07 20:14:43 -03:00
Christian Kaiser 229a3cdf65 [lua] Add onchecked parameter to Commands 2025-08-07 18:57:28 -03:00
Christian Kaiser b3814ec912 Unify ContextBar updates when moving pixels, tooltips (fix #5329) 2025-08-06 20:20:42 -03:00
David Capello e88f3bb413 Show error if curl/unzip tools aren't available (fix #5309) 2025-08-06 14:44:21 -03:00
Christian Kaiser eaa2bdf0af [lua] Process mnemonics consistently for plugins (fix #5250) 2025-08-04 15:58:29 -03:00
Christian Kaiser 57309e5aa5 Allow gif encoding to be stopped (fix #2619) 2025-08-01 20:46:32 -03:00
Christian Kaiser 4bb9239f50 [lua] Add `resizeable` property to Dialog constructor (fix #5177)
build / build (Debug, macos-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, macos-latest, noscripts, cli) (push) Has been cancelled Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Has been cancelled Details
build / build (Debug, windows-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, windows-latest, noscripts, cli) (push) Has been cancelled Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Has been cancelled Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Has been cancelled Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Has been cancelled Details
2025-08-01 18:57:50 -03:00
David Capello cef92c1a38 Add .plist files for macOS
build-auto / build-auto (Debug, macos-latest) (push) Has been cancelled Details
build-auto / build-auto (Debug, ubuntu-latest) (push) Has been cancelled Details
build-auto / build-auto (Debug, windows-latest) (push) Has been cancelled Details
build-auto / build-auto (RelWithDebInfo, macos-latest) (push) Has been cancelled Details
build-auto / build-auto (RelWithDebInfo, ubuntu-latest) (push) Has been cancelled Details
build-auto / build-auto (RelWithDebInfo, windows-latest) (push) Has been cancelled Details
build / build (Debug, macos-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, macos-latest, noscripts, cli) (push) Has been cancelled Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Has been cancelled Details
build / build (Debug, windows-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, windows-latest, noscripts, cli) (push) Has been cancelled Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Has been cancelled Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Has been cancelled Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Has been cancelled Details
We don't have an Aseprite.app target in cmake files yet, but we might
add it in a near future.
2025-07-28 16:18:19 -03:00
Christian Kaiser 22e72ab5cb [win] Fix includeDesktopDir returning the default path
build / build (Debug, macos-latest, lua, cli) (push) Waiting to run Details
build / build (Debug, macos-latest, noscripts, cli) (push) Waiting to run Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Waiting to run Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Waiting to run Details
build / build (Debug, windows-latest, lua, cli) (push) Waiting to run Details
build / build (Debug, windows-latest, noscripts, cli) (push) Waiting to run Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Waiting to run Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Waiting to run Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Waiting to run Details
Uses SHGFP_TYPE_CURRENT which returns the Desktop that the user has configured instead of the default, fixes Windows 11's OneDrive Desktop folder.
2025-07-28 10:47:53 -03:00
Christian Kaiser 80fa065bd5 [lua] Add sprite.undoHistory
build / build (Debug, macos-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, macos-latest, noscripts, cli) (push) Has been cancelled Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Has been cancelled Details
build / build (Debug, windows-latest, lua, cli) (push) Has been cancelled Details
build / build (Debug, windows-latest, noscripts, cli) (push) Has been cancelled Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Has been cancelled Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Has been cancelled Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Has been cancelled Details
2025-07-25 13:58:52 -03:00
David Capello de1ccb24dd [win] Don't drop text when IME dialog composition is accepted w/Enter
build / build (Debug, macos-latest, lua, cli) (push) Waiting to run Details
build / build (Debug, macos-latest, noscripts, cli) (push) Waiting to run Details
build / build (Debug, ubuntu-latest, lua, cli) (push) Waiting to run Details
build / build (Debug, ubuntu-latest, noscripts, cli) (push) Waiting to run Details
build / build (Debug, windows-latest, lua, cli) (push) Waiting to run Details
build / build (Debug, windows-latest, noscripts, cli) (push) Waiting to run Details
build / build (RelWithDebInfo, macos-latest, lua, gui) (push) Waiting to run Details
build / build (RelWithDebInfo, ubuntu-latest, lua, gui) (push) Waiting to run Details
build / build (RelWithDebInfo, windows-latest, lua, gui) (push) Waiting to run Details
build-auto / build-auto (Debug, macos-latest) (push) Has been cancelled Details
build-auto / build-auto (Debug, ubuntu-latest) (push) Has been cancelled Details
build-auto / build-auto (Debug, windows-latest) (push) Has been cancelled Details
build-auto / build-auto (RelWithDebInfo, macos-latest) (push) Has been cancelled Details
build-auto / build-auto (RelWithDebInfo, ubuntu-latest) (push) Has been cancelled Details
build-auto / build-auto (RelWithDebInfo, windows-latest) (push) Has been cancelled Details
With #5230, now that we can show the IME dialog on Windows, when we
are selecting a specific word/composition in the IME dialog, if we
press Enter we'll receive that Enter onKeyUp(). It's better if we
process the Enter key onKeyDown() (as the IME enter key is not
received in that case).
2025-07-25 09:19:50 -03:00
David Capello 7d91c4b9d9 [win] Fix dead keys on Windows 2025-07-24 17:45:46 -03:00
24 changed files with 374 additions and 47 deletions

View File

@ -428,9 +428,17 @@ if [ ! -d "$skia_library_dir" ] ; then
skia_url=$(bash laf/misc/skia-url.sh $skia_build)
skia_file=$(basename $skia_url)
if [ ! -f "$skia_dir/$skia_file" ] ; then
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

View File

@ -621,6 +621,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

2
laf

@ -1 +1 @@
Subproject commit a2bb9ec7fb98354279a2c49870a4a47a67a8e86e
Subproject commit 01571537bc6002a2e039a66497837365c394d7fa

View File

@ -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}

View File

@ -106,8 +106,6 @@ void UndoCommand::onExecute(Context* context)
msg = "Redid " + undo->nextRedoLabel();
if (Preferences::instance().undo.showTooltip())
statusbar->showTip(1000, msg);
else
statusbar->setStatusText(0, msg);
}
// Effectively undo/redo.

View File

@ -1095,6 +1095,9 @@ public:
gifframe_t nframes = totalFrames();
for (gifframe_t gifFrame = 0; gifFrame < nframes; ++gifFrame) {
ASSERT(frame_it != frame_end);
if (m_fop->isStop())
break;
frame_t frame = *frame_it;
++frame_it;

View File

@ -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));
}

View File

@ -10,6 +10,6 @@
// Increment this value if the scripting API is modified between two
// released Aseprite versions.
#define API_VERSION 34
#define API_VERSION 35
#endif

View File

@ -34,10 +34,10 @@
#include "app/tools/tool_loop_manager.h"
#include "app/tx.h"
#include "app/ui/context_bar.h"
#include "app/ui/doc_view.h"
#include "app/ui/editor/editor.h"
#include "app/ui/editor/tool_loop_impl.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui_context.h"
#include "base/fs.h"
@ -498,6 +498,30 @@ int App_useTool(lua_State* L)
return 0;
}
int App_tip(lua_State* L)
{
const auto* ctx = App::instance()->context();
if (!ctx || !ctx->isUIAvailable() || !StatusBar::instance())
return 0; // No UI to show the tooltip
if (!lua_isstring(L, 1))
return luaL_error(L, "app.tip() message parameter must be a string");
const std::string message = lua_tostring(L, 1);
if (message.empty())
return luaL_error(L, "app.tip() message cannot be empty");
int duration = 2000;
if (lua_isinteger(L, 2)) {
const int durationInput = lua_tointeger(L, 2);
if (durationInput > 0 && durationInput < 30000) // Reasonable limits
duration = lua_tointeger(L, 2);
}
StatusBar::instance()->showTip(duration, message);
return 0;
}
int App_get_events(lua_State* L)
{
push_app_events(L);
@ -820,6 +844,7 @@ const luaL_Reg App_methods[] = {
{ "alert", App_alert },
{ "refresh", App_refresh },
{ "useTool", App_useTool },
{ "tip", App_tip },
{ nullptr, nullptr }
};

View File

@ -127,12 +127,13 @@ struct Dialog {
int showRef = LUA_REFNIL;
lua_State* L = nullptr;
Dialog(const ui::Window::Type windowType, const std::string& title)
Dialog(const ui::Window::Type windowType, const std::string& title, bool sizeable)
: window(windowType, title)
, grid(2, false)
, currentGrid(&grid)
{
window.addChild(&grid);
window.setSizeable(sizeable);
all_dialogs.push_back(this);
}
@ -365,6 +366,7 @@ int Dialog_new(lua_State* L)
// Get the title and the type of window (with or without title bar)
ui::Window::Type windowType = ui::Window::WithTitleBar;
std::string title = "Script";
bool sizeable = true;
if (lua_isstring(L, 1)) {
title = lua_tostring(L, 1);
}
@ -378,9 +380,14 @@ int Dialog_new(lua_State* L)
if (type != LUA_TNIL && lua_toboolean(L, -1))
windowType = ui::Window::WithoutTitleBar;
lua_pop(L, 1);
type = lua_getfield(L, 1, "resizeable");
if (type != LUA_TNIL && !lua_toboolean(L, -1))
sizeable = false;
lua_pop(L, 1);
}
auto dlg = push_new<Dialog>(L, windowType, title);
auto dlg = push_new<Dialog>(L, windowType, title, sizeable);
// The uservalue of the dialog userdata will contain a table that
// stores all the callbacks to handle events. As these callbacks can
@ -1509,6 +1516,10 @@ int Dialog_modify(lua_State* L)
type = lua_getfield(L, 2, "text");
if (const char* s = lua_tostring(L, -1)) {
widget->setText(s);
// Re-process mnemonics for buttons
if (widget->type() == WidgetType::kButtonWidget)
widget->processMnemonicFromText();
relayout = true;
}
lua_pop(L, 1);

View File

@ -29,11 +29,16 @@ struct Plugin {
class PluginCommand : public Command {
public:
PluginCommand(const std::string& id, const std::string& title, int onclickRef, int onenabledRef)
PluginCommand(const std::string& id,
const std::string& title,
int onclickRef,
int onenabledRef,
int oncheckedRef)
: Command(id.c_str(), CmdUIOnlyFlag)
, m_title(title)
, m_onclickRef(onclickRef)
, m_onenabledRef(onenabledRef)
, m_oncheckedRef(oncheckedRef)
{
}
@ -72,28 +77,44 @@ protected:
bool onEnabled(Context* context) override
{
if (m_onenabledRef) {
script::Engine* engine = App::instance()->scriptEngine();
lua_State* L = engine->luaState();
lua_rawgeti(L, LUA_REGISTRYINDEX, m_onenabledRef);
if (lua_pcall(L, 0, 1, 0)) {
if (const char* s = lua_tostring(L, -1)) {
Console().printf("Error: %s", s);
return false;
}
}
else {
bool ret = lua_toboolean(L, -1);
lua_pop(L, 1);
return ret;
}
return callScriptRef(m_onenabledRef);
}
return true;
}
bool onChecked(Context* context) override
{
if (m_oncheckedRef) {
return callScriptRef(m_oncheckedRef);
}
return false;
}
private:
bool callScriptRef(int ref)
{
ASSERT(ref);
script::Engine* engine = App::instance()->scriptEngine();
lua_State* L = engine->luaState();
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
if (lua_pcall(L, 0, 1, 0)) {
if (const char* s = lua_tostring(L, -1)) {
Console().printf("Error: %s", s);
return false;
}
}
else {
bool ret = lua_toboolean(L, -1);
lua_pop(L, 1);
return ret;
}
}
std::string m_title;
int m_onclickRef;
int m_onenabledRef;
int m_oncheckedRef;
};
void deleteCommandIfExistent(Extension* ext, const std::string& id)
@ -126,6 +147,7 @@ int Plugin_newCommand(lua_State* L)
if (lua_istable(L, 2)) {
std::string id, title, group;
int onenabledRef = 0;
int oncheckedRef = 0;
lua_getfield(L, 2, "id");
if (const char* s = lua_tostring(L, -1)) {
@ -156,6 +178,14 @@ int Plugin_newCommand(lua_State* L)
lua_pop(L, 1);
}
type = lua_getfield(L, 2, "onchecked");
if (type == LUA_TFUNCTION) {
oncheckedRef = luaL_ref(L, LUA_REGISTRYINDEX); // does a pop
}
else {
lua_pop(L, 1);
}
type = lua_getfield(L, 2, "onclick");
if (type == LUA_TFUNCTION) {
int onclickRef = luaL_ref(L, LUA_REGISTRYINDEX);
@ -164,7 +194,7 @@ int Plugin_newCommand(lua_State* L)
// overwriting a previous registered command)
deleteCommandIfExistent(plugin->ext, id);
auto cmd = new PluginCommand(id, title, onclickRef, onenabledRef);
auto cmd = new PluginCommand(id, title, onclickRef, onenabledRef, oncheckedRef);
Commands::instance()->add(cmd);
plugin->ext->addCommand(id);
@ -172,6 +202,7 @@ int Plugin_newCommand(lua_State* L)
if (!group.empty() && App::instance()->isGui()) { // On CLI menus do not make sense
if (auto appMenus = AppMenus::instance()) {
auto menuItem = std::make_unique<AppMenuItem>(title, id);
menuItem->processMnemonicFromText();
appMenus->addMenuItemIntoGroup(group, std::move(menuItem));
}
}

View File

@ -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"

View File

@ -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 }
};

View File

@ -1001,12 +1001,12 @@ public:
m_angle.setSuffix("°");
m_skew.setSuffix("°");
addChild(new Label("P:"));
addChild(new Label(Strings::context_bar_position_label()));
addChild(&m_x);
addChild(&m_y);
addChild(&m_w);
addChild(&m_h);
addChild(new Label("R:"));
addChild(new Label(Strings::context_bar_rotation_label()));
addChild(&m_angle);
addChild(&m_skew);
@ -1047,6 +1047,16 @@ public:
m_skew.Change.connect([this] { onChangeSkew(); });
}
void setupTooltips(TooltipManager* tooltipManager)
{
tooltipManager->addTooltipFor(&m_x, Strings::context_bar_position_x(), BOTTOM);
tooltipManager->addTooltipFor(&m_y, Strings::context_bar_position_y(), BOTTOM);
tooltipManager->addTooltipFor(&m_w, Strings::context_bar_size_width(), BOTTOM);
tooltipManager->addTooltipFor(&m_h, Strings::context_bar_size_height(), BOTTOM);
tooltipManager->addTooltipFor(&m_angle, Strings::context_bar_rotation_angle(), BOTTOM);
tooltipManager->addTooltipFor(&m_skew, Strings::context_bar_rotation_skew(), BOTTOM);
}
void update(const Transformation& t)
{
auto rc = t.bounds();
@ -2626,6 +2636,7 @@ void ContextBar::setupTooltips(TooltipManager* tooltipManager)
m_dropPixels->setupTooltips(tooltipManager);
m_symmetry->setupTooltips(tooltipManager);
m_sliceFields->setupTooltips(tooltipManager);
m_transformation->setupTooltips(tooltipManager);
}
void ContextBar::registerCommands()

View File

@ -286,9 +286,6 @@ bool MovingPixelsState::onMouseDown(Editor* editor, MouseMessage* msg)
UIContext* ctx = UIContext::instance();
ctx->setActiveView(editor->getDocView());
ContextBar* contextBar = App::instance()->contextBar();
contextBar->updateForMovingPixels(getTransformation(editor));
// Start scroll loop
if (editor->checkForScroll(msg) || editor->checkForZoom(msg))
return true;
@ -442,10 +439,6 @@ void MovingPixelsState::onCommitMouseMove(Editor* editor, const gfx::PointF& spr
// Drag the image to that position
m_pixelsMovement->moveImage(spritePos, moveModifier);
// Update context bar and status bar
ContextBar* contextBar = App::instance()->contextBar();
contextBar->updateForMovingPixels(transformation);
m_editor->updateStatusBar();
}
@ -529,6 +522,10 @@ bool MovingPixelsState::onUpdateStatusBar(Editor* editor)
const Transformation& transform(getTransformation(editor));
gfx::Size imageSize = m_pixelsMovement->getInitialImageSize();
// Update the context bar along with the status bar
ContextBar* contextBar = App::instance()->contextBar();
contextBar->updateForMovingPixels(transform);
int w = int(transform.bounds().w);
int h = int(transform.bounds().h);
int gcd = base::gcd(w, h);

View File

@ -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;
}

View File

@ -549,7 +549,10 @@ private:
class StatusBar::CustomizedTipWindow : public ui::TipWindow {
public:
CustomizedTipWindow(const std::string& text) : ui::TipWindow(text) {}
CustomizedTipWindow(const std::string& text) : ui::TipWindow(text)
{
setClickBehavior(ClickBehavior::CloseOnClick);
}
void setInterval(int msecs)
{
@ -557,9 +560,11 @@ public:
m_timer.reset(new ui::Timer(msecs, this));
else
m_timer->setInterval(msecs);
m_originalInterval = msecs;
}
void startTimer() { m_timer->start(); }
void startTimer() const { m_timer->start(); }
int originalInterval() const { return m_originalInterval; }
protected:
bool onProcessMessage(Message* msg) override
@ -569,12 +574,18 @@ protected:
closeWindow(nullptr);
m_timer->stop();
break;
case kMouseEnterMessage: m_timer->stop(); break;
case kMouseLeaveMessage:
m_timer->setInterval(m_originalInterval);
m_timer->start();
return true; // Avoid PopupWindow closing us.
}
return ui::TipWindow::onProcessMessage(msg);
}
private:
std::unique_ptr<ui::Timer> m_timer;
int m_originalInterval = 0;
};
// TODO Use a ui::TipWindow with rounded borders, when we add support
@ -797,12 +808,21 @@ bool StatusBar::setStatusText(int msecs, const std::string& msg)
void StatusBar::showTip(int msecs, const std::string& msg)
{
ASSERT(msecs > 0);
if (m_tipwindow == NULL) {
m_tipwindow = new CustomizedTipWindow(msg);
}
else {
m_tipwindow->setText(msg);
if (m_tipwindow->isVisible() && m_tipwindow->text() != msg) {
// If the tip window is still visible, append the text on top so
// the old message is still readable (unless it's the exact same text)
m_tipwindow->setText(m_tipwindow->text() + "\n" + msg);
// Always use the longest interval to give the user more time to read.
msecs = m_tipwindow->originalInterval() > msecs ? m_tipwindow->originalInterval() : msecs;
}
else {
m_tipwindow->setText(msg);
}
}
m_tipwindow->setInterval(msecs);

View File

@ -0,0 +1,10 @@
<?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.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

86
src/main/osx/Info.plist Normal file
View File

@ -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>

View File

@ -157,6 +157,13 @@ bool PopupWindow::onProcessMessage(Message* msg)
}
break;
}
case ClickBehavior::CloseOnClick: {
if (bounds().contains(mouseMsg->position())) {
closeWindow(nullptr);
}
break;
}
}
}
break;

View File

@ -18,7 +18,8 @@ public:
enum class ClickBehavior {
DoNothingOnClick,
CloseOnClickInOtherWindow,
CloseOnClickOutsideHotRegion
CloseOnClickOutsideHotRegion,
CloseOnClick
};
enum class EnterBehavior {

View File

@ -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