mirror of https://github.com/aseprite/aseprite.git
Compare commits
15 Commits
bc39d7d38e
...
a081eb9fb8
| Author | SHA1 | Date |
|---|---|---|
|
|
a081eb9fb8 | |
|
|
22e72ab5cb | |
|
|
80fa065bd5 | |
|
|
de1ccb24dd | |
|
|
7d91c4b9d9 | |
|
|
6d89a6bc15 | |
|
|
d4e97b5a96 | |
|
|
205b18dc0f | |
|
|
2ba051b59b | |
|
|
3fcb000eb1 | |
|
|
af9dc3c817 | |
|
|
250dfdc86a | |
|
|
c904c41b39 | |
|
|
9e941e9a8b | |
|
|
bbab4d5875 |
|
|
@ -356,6 +356,7 @@
|
||||||
<section id="file_selector">
|
<section id="file_selector">
|
||||||
<option id="current_folder" type="std::string" default=""<empty>"" />
|
<option id="current_folder" type="std::string" default=""<empty>"" />
|
||||||
<option id="zoom" type="double" default="1.0" />
|
<option id="zoom" type="double" default="1.0" />
|
||||||
|
<option id="show_hidden" type="bool" default="false" />
|
||||||
</section>
|
</section>
|
||||||
<section id="text_tool">
|
<section id="text_tool">
|
||||||
<option id="font_face" type="std::string" />
|
<option id="font_face" type="std::string" />
|
||||||
|
|
|
||||||
|
|
@ -765,6 +765,7 @@ pinned_folders = Pinned Folders
|
||||||
recent_folders = Recent Folders
|
recent_folders = Recent Folders
|
||||||
all_formats = All formats
|
all_formats = All formats
|
||||||
all_files = All files
|
all_files = All files
|
||||||
|
show_hidden = Show hidden
|
||||||
|
|
||||||
[filters]
|
[filters]
|
||||||
selected_cels = Selected
|
selected_cels = Selected
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
<combobox id="location" expansive="true" />
|
<combobox id="location" expansive="true" />
|
||||||
<button text="" id="refresh_button" style="refresh_button"
|
<button text="" id="refresh_button" style="refresh_button"
|
||||||
tooltip="@.refresh_button_tooltip" tooltip_dir="bottom" />
|
tooltip="@.refresh_button_tooltip" tooltip_dir="bottom" />
|
||||||
|
<check id="show_hidden_check" text="@.show_hidden" />
|
||||||
</box>
|
</box>
|
||||||
<vbox id="file_view_placeholder" expansive="true" />
|
<vbox id="file_view_placeholder" expansive="true" />
|
||||||
<grid columns="2">
|
<grid columns="2">
|
||||||
|
|
|
||||||
2
laf
2
laf
|
|
@ -1 +1 @@
|
||||||
Subproject commit a2bb9ec7fb98354279a2c49870a4a47a67a8e86e
|
Subproject commit 8ec4b553f1618f7a4b47cdcf4cfc2663266111ac
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
|
|
@ -82,6 +82,9 @@ public:
|
||||||
unsigned int m_version;
|
unsigned int m_version;
|
||||||
bool m_removed;
|
bool m_removed;
|
||||||
mutable bool m_is_folder;
|
mutable bool m_is_folder;
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool m_isHidden = false;
|
||||||
|
#endif
|
||||||
std::atomic<double> m_thumbnailProgress;
|
std::atomic<double> m_thumbnailProgress;
|
||||||
std::atomic<os::Surface*> m_thumbnail;
|
std::atomic<os::Surface*> m_thumbnail;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
@ -266,7 +269,7 @@ IFileItem* FileSystemModule::getRootFileItem()
|
||||||
fileitem->m_pidl = pidl;
|
fileitem->m_pidl = pidl;
|
||||||
fileitem->m_fullpidl = pidl;
|
fileitem->m_fullpidl = pidl;
|
||||||
|
|
||||||
SFGAOF attrib = SFGAO_FOLDER;
|
SFGAOF attrib = SFGAO_FOLDER | SFGAO_HIDDEN;
|
||||||
shl_idesktop->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &attrib);
|
shl_idesktop->GetAttributesOf(1, (LPCITEMIDLIST*)&pidl, &attrib);
|
||||||
|
|
||||||
update_by_pidl(fileitem, attrib);
|
update_by_pidl(fileitem, attrib);
|
||||||
|
|
@ -357,7 +360,7 @@ bool FileItem::isHidden() const
|
||||||
ASSERT(m_displayname != NOTINITIALIZED);
|
ASSERT(m_displayname != NOTINITIALIZED);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return false;
|
return m_isHidden;
|
||||||
#else
|
#else
|
||||||
return m_displayname[0] == '.';
|
return m_displayname[0] == '.';
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -462,7 +465,7 @@ const FileItemList& FileItem::children()
|
||||||
// Get the interface to enumerate subitems
|
// Get the interface to enumerate subitems
|
||||||
hr = pFolder->EnumObjects(
|
hr = pFolder->EnumObjects(
|
||||||
reinterpret_cast<HWND>(os::System::instance()->defaultWindow()->nativeHandle()),
|
reinterpret_cast<HWND>(os::System::instance()->defaultWindow()->nativeHandle()),
|
||||||
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
|
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN,
|
||||||
&pEnum);
|
&pEnum);
|
||||||
|
|
||||||
if (hr == S_OK && pEnum) {
|
if (hr == S_OK && pEnum) {
|
||||||
|
|
@ -473,10 +476,9 @@ const FileItemList& FileItem::children()
|
||||||
while (pEnum->Next(256, itempidl, &fetched) == S_OK && fetched > 0) {
|
while (pEnum->Next(256, itempidl, &fetched) == S_OK && fetched > 0) {
|
||||||
// Request the SFGAO_FOLDER attribute to know what of the
|
// Request the SFGAO_FOLDER attribute to know what of the
|
||||||
// item is file or a folder
|
// item is file or a folder
|
||||||
for (c = 0; c < fetched; ++c) {
|
for (c = 0; c < fetched; ++c)
|
||||||
attribs[c] = SFGAO_FOLDER;
|
attribs[c] = SFGAO_FOLDER | SFGAO_HIDDEN;
|
||||||
pFolder->GetAttributesOf(1, (LPCITEMIDLIST*)itempidl, attribs + c);
|
pFolder->GetAttributesOf(fetched, (LPCITEMIDLIST*)itempidl, attribs);
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the FileItems
|
// Generate the FileItems
|
||||||
for (c = 0; c < fetched; ++c) {
|
for (c = 0; c < fetched; ++c) {
|
||||||
|
|
@ -755,6 +757,9 @@ static void update_by_pidl(FileItem* fileitem, SFGAOF attrib)
|
||||||
// Is it a folder?
|
// Is it a folder?
|
||||||
|
|
||||||
fileitem->m_is_folder = calc_is_folder(fileitem->m_filename, attrib);
|
fileitem->m_is_folder = calc_is_folder(fileitem->m_filename, attrib);
|
||||||
|
#if _WIN32
|
||||||
|
fileitem->m_isHidden = (attrib & SFGAO_HIDDEN ? true : false);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Get the name to display
|
// Get the name to display
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ void ResourceFinder::includeDesktopDir(const char* filename)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
std::vector<wchar_t> buf(MAX_PATH);
|
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) {
|
if (hr == S_OK) {
|
||||||
addPath(base::join_path(base::to_utf8(&buf[0]), filename));
|
addPath(base::join_path(base::to_utf8(&buf[0]), filename));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
#include "app/console.h"
|
#include "app/console.h"
|
||||||
#include "app/context.h"
|
#include "app/context.h"
|
||||||
#include "app/context_observer.h"
|
#include "app/context_observer.h"
|
||||||
|
#include "app/doc.h"
|
||||||
|
#include "app/doc_undo.h"
|
||||||
#include "app/script/docobj.h"
|
#include "app/script/docobj.h"
|
||||||
#include "app/script/engine.h"
|
#include "app/script/engine.h"
|
||||||
#include "app/script/luacpp.h"
|
#include "app/script/luacpp.h"
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@
|
||||||
#include "doc/tag.h"
|
#include "doc/tag.h"
|
||||||
#include "doc/tileset.h"
|
#include "doc/tileset.h"
|
||||||
#include "doc/tilesets.h"
|
#include "doc/tilesets.h"
|
||||||
|
#include "undo/undo_state.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
@ -1029,6 +1030,42 @@ int Sprite_set_useLayerUuids(lua_State* L)
|
||||||
return 0;
|
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[] = {
|
const luaL_Reg Sprite_methods[] = {
|
||||||
{ "__eq", Sprite_eq },
|
{ "__eq", Sprite_eq },
|
||||||
{ "resize", Sprite_resize },
|
{ "resize", Sprite_resize },
|
||||||
|
|
@ -1094,6 +1131,7 @@ const Property Sprite_properties[] = {
|
||||||
{ "events", Sprite_get_events, nullptr },
|
{ "events", Sprite_get_events, nullptr },
|
||||||
{ "tileManagementPlugin", Sprite_get_tileManagementPlugin, Sprite_set_tileManagementPlugin },
|
{ "tileManagementPlugin", Sprite_get_tileManagementPlugin, Sprite_set_tileManagementPlugin },
|
||||||
{ "useLayerUuids", Sprite_get_useLayerUuids, Sprite_set_useLayerUuids },
|
{ "useLayerUuids", Sprite_get_useLayerUuids, Sprite_set_useLayerUuids },
|
||||||
|
{ "undoHistory", Sprite_get_undoHistory, nullptr },
|
||||||
{ nullptr, nullptr, nullptr }
|
{ nullptr, nullptr, nullptr }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1857,7 +1857,7 @@ private:
|
||||||
|
|
||||||
class ContextBar::FontSelector : public FontEntry {
|
class ContextBar::FontSelector : public FontEntry {
|
||||||
public:
|
public:
|
||||||
FontSelector(ContextBar* contextBar)
|
FontSelector(ContextBar* contextBar) : FontEntry(true) // With stroke and fill options
|
||||||
{
|
{
|
||||||
// Load the font from the preferences
|
// Load the font from the preferences
|
||||||
setInfo(FontInfo::getFromPreferences(), FontEntry::From::Init);
|
setInfo(FontInfo::getFromPreferences(), FontEntry::From::Init);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
|
|
@ -457,6 +457,8 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
||||||
|
|
||||||
// Here we re-use the cached surface
|
// Here we re-use the cached surface
|
||||||
if (!cached && m_uiLayer->surface()) {
|
if (!cached && m_uiLayer->surface()) {
|
||||||
|
m_uiLayer->surface()->clear();
|
||||||
|
|
||||||
gfx::Rect layerBounds = m_uiLayer->surface()->bounds();
|
gfx::Rect layerBounds = m_uiLayer->surface()->bounds();
|
||||||
ui::Graphics g(display, m_uiLayer->surface(), 0, 0);
|
ui::Graphics g(display, m_uiLayer->surface(), 0, 0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@
|
||||||
#include "app/util/tile_flags_utils.h"
|
#include "app/util/tile_flags_utils.h"
|
||||||
#include "base/chrono.h"
|
#include "base/chrono.h"
|
||||||
#include "base/convert_to.h"
|
#include "base/convert_to.h"
|
||||||
|
#include "base/scoped_value.h"
|
||||||
#include "doc/doc.h"
|
#include "doc/doc.h"
|
||||||
#include "doc/mask_boundaries.h"
|
#include "doc/mask_boundaries.h"
|
||||||
#include "doc/slice.h"
|
#include "doc/slice.h"
|
||||||
|
|
@ -266,6 +267,23 @@ void Editor::setStateInternal(const EditorStatePtr& newState)
|
||||||
{
|
{
|
||||||
m_brushPreview.hide();
|
m_brushPreview.hide();
|
||||||
|
|
||||||
|
// Some onLeaveState impls (like the ones from MovingPixelsState,
|
||||||
|
// WritingTextState, MovingSelectionState) might generate a
|
||||||
|
// Tx/Transaction::commit(), which will add a new undo state,
|
||||||
|
// triggering a sprite change scripting event
|
||||||
|
// (SpriteEvents::onAddUndoState). This event could be handled by an
|
||||||
|
// extension and that extension might want to save the current
|
||||||
|
// sprite (e.g. calling Sprite_saveCopyAs, the kind of extension
|
||||||
|
// that takes snapshots after each sprite change). That will be a
|
||||||
|
// new Context::executeCommand() for the save command, generating a
|
||||||
|
// BeforeCommandExecution signal, getting back to onLeaveState
|
||||||
|
// again. In that case, we just ignore the reentry as the first
|
||||||
|
// onLeaveState should handle everything (to avoid an stack
|
||||||
|
// overflow/infinite recursion).
|
||||||
|
if (m_leavingState)
|
||||||
|
return;
|
||||||
|
base::ScopedValue leaving(m_leavingState, true);
|
||||||
|
|
||||||
// Fire before change state event, set the state, and fire after
|
// Fire before change state event, set the state, and fire after
|
||||||
// change state event.
|
// change state event.
|
||||||
EditorState::LeaveAction leaveAction = m_state->onLeaveState(this, newState.get());
|
EditorState::LeaveAction leaveAction = m_state->onLeaveState(this, newState.get());
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
|
|
@ -458,6 +458,13 @@ private:
|
||||||
|
|
||||||
DocView* m_docView;
|
DocView* m_docView;
|
||||||
|
|
||||||
|
// Special flag to avoid re-entering a new state when we are leaving
|
||||||
|
// the current one. This avoids an infinite onLeaveState() recursion
|
||||||
|
// in some special cases when an extension (third-party code)
|
||||||
|
// creates a new sprite change in the same sprite change scripting
|
||||||
|
// event.
|
||||||
|
bool m_leavingState = false;
|
||||||
|
|
||||||
// Last known mouse position received by this editor when the
|
// Last known mouse position received by this editor when the
|
||||||
// mouse button was pressed. Used for auto-scrolling. To get the
|
// mouse button was pressed. Used for auto-scrolling. To get the
|
||||||
// current mouse position on the editor you can use
|
// current mouse position on the editor you can use
|
||||||
|
|
|
||||||
|
|
@ -428,8 +428,8 @@ bool MovingSliceState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||||
if (editor->slicesTransforms())
|
if (editor->slicesTransforms())
|
||||||
drawExtraCel();
|
drawExtraCel();
|
||||||
|
|
||||||
// Redraw the editor.
|
// Notify changes
|
||||||
editor->invalidate();
|
m_site.document()->notifyGeneralUpdate();
|
||||||
|
|
||||||
// Use StandbyState implementation
|
// Use StandbyState implementation
|
||||||
return StandbyState::onMouseMove(editor, msg);
|
return StandbyState::onMouseMove(editor, msg);
|
||||||
|
|
|
||||||
|
|
@ -443,12 +443,7 @@ bool WritingTextState::onSetCursor(Editor* editor, const gfx::Point& mouseScreen
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WritingTextState::onKeyDown(Editor*, KeyMessage*)
|
bool WritingTextState::onKeyDown(Editor*, KeyMessage* msg)
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WritingTextState::onKeyUp(Editor*, KeyMessage* msg)
|
|
||||||
{
|
{
|
||||||
// Cancel loop pressing Esc key
|
// Cancel loop pressing Esc key
|
||||||
if (msg->scancode() == ui::kKeyEsc) {
|
if (msg->scancode() == ui::kKeyEsc) {
|
||||||
|
|
@ -457,7 +452,17 @@ bool WritingTextState::onKeyUp(Editor*, KeyMessage* msg)
|
||||||
// Drop text pressing Enter key
|
// Drop text pressing Enter key
|
||||||
else if (msg->scancode() == ui::kKeyEnter) {
|
else if (msg->scancode() == ui::kKeyEnter) {
|
||||||
drop();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ FileList::FileList()
|
||||||
, m_multiselect(false)
|
, m_multiselect(false)
|
||||||
, m_zoom(1.0)
|
, m_zoom(1.0)
|
||||||
, m_itemsPerRow(0)
|
, m_itemsPerRow(0)
|
||||||
|
, m_showHidden(Preferences::instance().fileSelector.showHidden())
|
||||||
{
|
{
|
||||||
setFocusStop(true);
|
setFocusStop(true);
|
||||||
setDoubleBuffered(true);
|
setDoubleBuffered(true);
|
||||||
|
|
@ -173,6 +174,14 @@ void FileList::animateToZoom(const double zoom)
|
||||||
startAnimation(ANI_ZOOM, 10);
|
startAnimation(ANI_ZOOM, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileList::setShowHidden(const bool show)
|
||||||
|
{
|
||||||
|
m_showHidden = show;
|
||||||
|
m_req_valid = false;
|
||||||
|
m_selected = nullptr;
|
||||||
|
regenerateList();
|
||||||
|
}
|
||||||
|
|
||||||
bool FileList::onProcessMessage(Message* msg)
|
bool FileList::onProcessMessage(Message* msg)
|
||||||
{
|
{
|
||||||
switch (msg->type()) {
|
switch (msg->type()) {
|
||||||
|
|
@ -825,7 +834,7 @@ void FileList::regenerateList()
|
||||||
for (FileItemList::iterator it = m_list.begin(); it != m_list.end();) {
|
for (FileItemList::iterator it = m_list.begin(); it != m_list.end();) {
|
||||||
IFileItem* fileitem = *it;
|
IFileItem* fileitem = *it;
|
||||||
|
|
||||||
if (fileitem->isHidden())
|
if (fileitem->isHidden() && !m_showHidden)
|
||||||
it = m_list.erase(it);
|
it = m_list.erase(it);
|
||||||
else if (!fileitem->isFolder() && !fileitem->hasExtension(m_exts)) {
|
else if (!fileitem->isFolder() && !fileitem->hasExtension(m_exts)) {
|
||||||
it = m_list.erase(it);
|
it = m_list.erase(it);
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ public:
|
||||||
double zoom() const { return m_zoom; }
|
double zoom() const { return m_zoom; }
|
||||||
void setZoom(const double zoom);
|
void setZoom(const double zoom);
|
||||||
void animateToZoom(const double zoom);
|
void animateToZoom(const double zoom);
|
||||||
|
void setShowHidden(const bool show);
|
||||||
|
|
||||||
obs::signal<void()> FileSelected;
|
obs::signal<void()> FileSelected;
|
||||||
obs::signal<void()> FileAccepted;
|
obs::signal<void()> FileAccepted;
|
||||||
|
|
@ -137,6 +138,7 @@ private:
|
||||||
double m_toZoom;
|
double m_toZoom;
|
||||||
|
|
||||||
int m_itemsPerRow;
|
int m_itemsPerRow;
|
||||||
|
bool m_showHidden;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,7 @@ FileSelector::FileSelector(FileSelectorType type) : m_type(type), m_navigationLo
|
||||||
for (auto child : viewType()->children())
|
for (auto child : viewType()->children())
|
||||||
child->setFocusStop(false);
|
child->setFocusStop(false);
|
||||||
|
|
||||||
|
showHiddenCheck()->setSelected(Preferences::instance().fileSelector.showHidden());
|
||||||
m_fileList = new FileList();
|
m_fileList = new FileList();
|
||||||
m_fileList->setId("fileview");
|
m_fileList->setId("fileview");
|
||||||
m_fileName->setAssociatedFileList(m_fileList);
|
m_fileName->setAssociatedFileList(m_fileList);
|
||||||
|
|
@ -334,6 +335,10 @@ FileSelector::FileSelector(FileSelectorType type) : m_type(type), m_navigationLo
|
||||||
viewType()->ItemChange.connect([this] { onChangeViewType(); });
|
viewType()->ItemChange.connect([this] { onChangeViewType(); });
|
||||||
location()->CloseListBox.connect([this] { onLocationCloseListBox(); });
|
location()->CloseListBox.connect([this] { onLocationCloseListBox(); });
|
||||||
fileType()->Change.connect([this] { onFileTypeChange(); });
|
fileType()->Change.connect([this] { onFileTypeChange(); });
|
||||||
|
showHiddenCheck()->Click.connect([this] {
|
||||||
|
Preferences::instance().fileSelector.showHidden(showHiddenCheck()->isSelected());
|
||||||
|
m_fileList->setShowHidden(showHiddenCheck()->isSelected());
|
||||||
|
});
|
||||||
m_fileList->FileSelected.connect([this] { onFileListFileSelected(); });
|
m_fileList->FileSelected.connect([this] { onFileListFileSelected(); });
|
||||||
m_fileList->FileAccepted.connect([this] { onFileListFileAccepted(); });
|
m_fileList->FileAccepted.connect([this] { onFileListFileAccepted(); });
|
||||||
m_fileList->CurrentFolderChanged.connect([this] { onFileListCurrentFolderChanged(); });
|
m_fileList->CurrentFolderChanged.connect([this] { onFileListCurrentFolderChanged(); });
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,9 @@ int FontEntry::FontStroke::WidthEntry::onGetValueFromText(const std::string& tex
|
||||||
return int(10.0 * base::convert_to<double>(text));
|
return int(10.0 * base::convert_to<double>(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
FontEntry::FontEntry() : m_style(&m_tooltips), m_stroke(&m_tooltips)
|
FontEntry::FontEntry(const bool withStrokeAndFill)
|
||||||
|
: m_style(&m_tooltips)
|
||||||
|
, m_stroke(withStrokeAndFill ? std::make_unique<FontStroke>(&m_tooltips) : nullptr)
|
||||||
{
|
{
|
||||||
m_face.setExpansive(true);
|
m_face.setExpansive(true);
|
||||||
m_size.setExpansive(false);
|
m_size.setExpansive(false);
|
||||||
|
|
@ -342,7 +344,8 @@ FontEntry::FontEntry() : m_style(&m_tooltips), m_stroke(&m_tooltips)
|
||||||
addChild(&m_face);
|
addChild(&m_face);
|
||||||
addChild(&m_size);
|
addChild(&m_size);
|
||||||
addChild(&m_style);
|
addChild(&m_style);
|
||||||
addChild(&m_stroke);
|
if (m_stroke)
|
||||||
|
addChild(m_stroke.get());
|
||||||
|
|
||||||
m_tooltips.addTooltipFor(&m_face, Strings::text_tool_font_family(), BOTTOM);
|
m_tooltips.addTooltipFor(&m_face, Strings::text_tool_font_family(), BOTTOM);
|
||||||
m_tooltips.addTooltipFor(m_size.getEntryWidget(), Strings::text_tool_font_size(), BOTTOM);
|
m_tooltips.addTooltipFor(m_size.getEntryWidget(), Strings::text_tool_font_size(), BOTTOM);
|
||||||
|
|
@ -367,7 +370,8 @@ FontEntry::FontEntry() : m_style(&m_tooltips), m_stroke(&m_tooltips)
|
||||||
});
|
});
|
||||||
|
|
||||||
m_style.ItemChange.connect(&FontEntry::onStyleItemClick, this);
|
m_style.ItemChange.connect(&FontEntry::onStyleItemClick, this);
|
||||||
m_stroke.Change.connect(&FontEntry::onStrokeChange, this);
|
if (m_stroke)
|
||||||
|
m_stroke->Change.connect(&FontEntry::onStrokeChange, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defined here as FontPopup type is not fully defined in the header
|
// Defined here as FontPopup type is not fully defined in the header
|
||||||
|
|
@ -399,22 +403,23 @@ void FontEntry::setInfo(const FontInfo& info, const From fromField)
|
||||||
ui::Paint FontEntry::paint()
|
ui::Paint FontEntry::paint()
|
||||||
{
|
{
|
||||||
ui::Paint paint;
|
ui::Paint paint;
|
||||||
const float stroke = m_stroke.stroke();
|
ui::Paint::Style style = ui::Paint::Fill;
|
||||||
|
|
||||||
ui::Paint::Style style = ui::Paint::StrokeAndFill;
|
if (m_stroke) {
|
||||||
if (m_stroke.fill()) {
|
const float stroke = m_stroke->stroke();
|
||||||
if (stroke <= 0.0f)
|
if (m_stroke->fill()) {
|
||||||
style = ui::Paint::Fill;
|
if (stroke > 0.0f) {
|
||||||
else
|
style = ui::Paint::StrokeAndFill;
|
||||||
|
paint.strokeWidth(stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
style = ui::Paint::Stroke;
|
||||||
paint.strokeWidth(stroke);
|
paint.strokeWidth(stroke);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
style = ui::Paint::Stroke;
|
|
||||||
paint.strokeWidth(stroke);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paint.style(style);
|
paint.style(style);
|
||||||
|
|
||||||
return paint;
|
return paint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include "ui/paint.h"
|
#include "ui/paint.h"
|
||||||
#include "ui/tooltips.h"
|
#include "ui/tooltips.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
@ -36,7 +37,7 @@ public:
|
||||||
Paint,
|
Paint,
|
||||||
};
|
};
|
||||||
|
|
||||||
FontEntry();
|
FontEntry(bool withStrokeAndFill);
|
||||||
~FontEntry();
|
~FontEntry();
|
||||||
|
|
||||||
FontInfo info() { return m_info; }
|
FontInfo info() { return m_info; }
|
||||||
|
|
@ -113,7 +114,7 @@ private:
|
||||||
FontFace m_face;
|
FontFace m_face;
|
||||||
FontSize m_size;
|
FontSize m_size;
|
||||||
FontStyle m_style;
|
FontStyle m_style;
|
||||||
FontStroke m_stroke;
|
std::unique_ptr<FontStroke> m_stroke;
|
||||||
bool m_lockFace = false;
|
bool m_lockFace = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,7 @@ Widget* WidgetLoader::convertXmlElementToWidget(const XMLElement* elem,
|
||||||
}
|
}
|
||||||
else if (elem_name == "font") {
|
else if (elem_name == "font") {
|
||||||
if (!widget)
|
if (!widget)
|
||||||
widget = new FontEntry;
|
widget = new FontEntry(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Was the widget created?
|
// Was the widget created?
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2016 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
|
|
@ -20,11 +20,12 @@
|
||||||
#include "doc/primitives.h"
|
#include "doc/primitives.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
|
|
||||||
static int generation = 0;
|
static std::atomic<int> g_generation = 0;
|
||||||
|
|
||||||
Brush::Brush()
|
Brush::Brush()
|
||||||
{
|
{
|
||||||
|
|
@ -300,7 +301,7 @@ void Brush::setCenter(const gfx::Point& center)
|
||||||
// Cleans the brush's data (image and region).
|
// Cleans the brush's data (image and region).
|
||||||
void Brush::clean()
|
void Brush::clean()
|
||||||
{
|
{
|
||||||
m_gen = ++generation;
|
m_gen = ++g_generation;
|
||||||
m_image.reset();
|
m_image.reset();
|
||||||
m_maskBitmap.reset();
|
m_maskBitmap.reset();
|
||||||
m_backupImage.reset();
|
m_backupImage.reset();
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,15 @@ int Entry::lastCaretPos() const
|
||||||
return int(m_boxes.size() - 1);
|
return int(m_boxes.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gfx::Point Entry::caretPosOnScreen() const
|
||||||
|
{
|
||||||
|
const gfx::Point caretPos = getCharBoxBounds(m_caret).point2();
|
||||||
|
const os::Window* nativeWindow = display()->nativeWindow();
|
||||||
|
const gfx::Point pos = nativeWindow->pointToScreen(caretPos + bounds().origin());
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
void Entry::setCaretPos(const int pos)
|
void Entry::setCaretPos(const int pos)
|
||||||
{
|
{
|
||||||
gfx::Size caretSize = theme()->getEntryCaretSize(this);
|
gfx::Size caretSize = theme()->getEntryCaretSize(this);
|
||||||
|
|
@ -160,6 +169,8 @@ void Entry::setCaretPos(const int pos)
|
||||||
startTimer();
|
startTimer();
|
||||||
m_state = true;
|
m_state = true;
|
||||||
|
|
||||||
|
os::System::instance()->setTextInput(true, caretPosOnScreen());
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,7 +262,7 @@ gfx::Rect Entry::getEntryTextBounds() const
|
||||||
return onGetEntryTextBounds();
|
return onGetEntryTextBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx::Rect Entry::getCharBoxBounds(const int i)
|
gfx::Rect Entry::getCharBoxBounds(const int i) const
|
||||||
{
|
{
|
||||||
ASSERT(i >= 0 && i < int(m_boxes.size()));
|
ASSERT(i >= 0 && i < int(m_boxes.size()));
|
||||||
if (i >= 0 && i < int(m_boxes.size()))
|
if (i >= 0 && i < int(m_boxes.size()))
|
||||||
|
|
@ -288,8 +299,9 @@ bool Entry::onProcessMessage(Message* msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start processing dead keys
|
// Start processing dead keys
|
||||||
if (m_translate_dead_keys)
|
if (m_translate_dead_keys) {
|
||||||
os::System::instance()->setTextInput(true);
|
os::System::instance()->setTextInput(true, caretPosOnScreen());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kFocusLeaveMessage:
|
case kFocusLeaveMessage:
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ public:
|
||||||
|
|
||||||
int caretPos() const { return m_caret; }
|
int caretPos() const { return m_caret; }
|
||||||
int lastCaretPos() const;
|
int lastCaretPos() const;
|
||||||
|
gfx::Point caretPosOnScreen() const;
|
||||||
|
|
||||||
void setCaretPos(int pos);
|
void setCaretPos(int pos);
|
||||||
void setCaretToEnd();
|
void setCaretToEnd();
|
||||||
|
|
@ -76,7 +77,7 @@ public:
|
||||||
obs::signal<void()> Change;
|
obs::signal<void()> Change;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gfx::Rect getCharBoxBounds(int i);
|
gfx::Rect getCharBoxBounds(int i) const;
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
bool onProcessMessage(Message* msg) override;
|
bool onProcessMessage(Message* msg) override;
|
||||||
|
|
|
||||||
|
|
@ -228,3 +228,69 @@ do
|
||||||
c = app.open(fn)
|
c = app.open(fn)
|
||||||
assert(c.tileManagementPlugin == nil)
|
assert(c.tileManagementPlugin == nil)
|
||||||
end
|
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