Compare commits

...

8 Commits

Author SHA1 Message Date
Gaspar Capello 2ffdf04ecc
Merge 1a5361b637 into 2ba051b59b 2025-07-23 18:49:17 -04:00
David Capello 2ba051b59b Fix crash deselecting moved pixels when using certain extensions (fix #5280)
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
Although the issue refers to deselecting MovingPixelsState, the same
crash could happen when canceling/finishing WritingTextState or
MovingSelectionState. This fixes the crash for all these states.
2025-07-23 19:01:37 -03:00
Christian Kaiser 3fcb000eb1 Fix slice transformations not updating editors
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-22 02:11:07 -03:00
David Capello af9dc3c817 Fix brush boundaries accumulation switching brush type only (fix #5281)
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
2025-07-21 17:28:11 -03:00
David Capello 250dfdc86a Convert the brush generation counter into an atomic var 2025-07-21 17:27:23 -03:00
David Capello c904c41b39 Don't show stroke/fill option for theme fonts
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-18 18:48:35 -03:00
David Capello 9e941e9a8b [win32] Fix listing hidden files on Windows (related to #5269 / #3079) 2025-07-18 18:37:44 -03:00
Liebranca bbab4d5875 Add 'Show hidden' check to file selector 2025-07-18 18:26:35 -03:00
16 changed files with 92 additions and 34 deletions

View File

@ -356,6 +356,7 @@
<section id="file_selector"> <section id="file_selector">
<option id="current_folder" type="std::string" default="&quot;&lt;empty&gt;&quot;" /> <option id="current_folder" type="std::string" default="&quot;&lt;empty&gt;&quot;" />
<option id="zoom" type="double" default="1.0" /> <option id="zoom" type="double" default="1.0" />
<option id="show_hidden" type="bool" default="false" />
</section> </section>
<section id="text_tool"> <section id="text_tool">
<option id="font_face" type="std::string" /> <option id="font_face" type="std::string" />

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2024 Igara Studio S.A. // Copyright (C) 2019-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -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);

View File

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

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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