Support drag and drop images as new documents

This commit is contained in:
Martín Capello 2024-09-20 10:58:48 -03:00
parent 573822d9f1
commit 365bad61d5
8 changed files with 139 additions and 5 deletions

View File

@ -689,6 +689,7 @@ target_sources(app-lib PRIVATE
util/cel_ops.cpp
util/clipboard.cpp
util/clipboard_native.cpp
util/conversion_to_image.cpp
util/conversion_to_surface.cpp
util/expand_cel_canvas.cpp
util/filetoks.cpp

View File

@ -46,6 +46,7 @@ struct NewFileParams : public NewParams {
Param<int> height { this, 0, "height" };
Param<ColorMode> colorMode { this, ColorMode::RGB, "colorMode" };
Param<bool> fromClipboard { this, false, "fromClipboard" };
Param<bool> fromDraggedData { this, false, "fromDraggedData" };
};
class NewFileCommand : public CommandWithNewParams<NewFileParams> {
@ -86,8 +87,9 @@ void NewFileCommand::onExecute(Context* ctx)
doc::Palette clipboardPalette(0, 256);
const int ncolors = get_default_palette()->size();
if (params().fromClipboard()) {
clipboardImage = ctx->clipboard()->getImage(&clipboardPalette);
if (params().fromClipboard() || params().fromDraggedData()) {
clipboardImage = (params().fromClipboard() ? ctx->clipboard()->getImage(&clipboardPalette)
: ctx->draggedData()->getImage());
if (!clipboardImage)
return;

View File

@ -14,11 +14,14 @@
#include "app/context_observer.h"
#include "app/docs.h"
#include "app/docs_observer.h"
#include "app/util/conversion_to_image.h"
#include "base/disable_copying.h"
#include "base/exception.h"
#include "doc/frame.h"
#include "doc/image_ref.h"
#include "obs/observable.h"
#include "obs/signal.h"
#include "os/surface.h"
#include <memory>
#include <vector>
@ -86,6 +89,22 @@ namespace app {
bool m_canceled;
};
class DraggedData
{
public:
DraggedData(const doc::ImageRef& image) {
m_image = image;
}
DraggedData(const os::SurfaceRef& surface) {
convert_surface_to_image(surface.get(), 0, 0, surface->width(), surface->height(), m_image);
}
const doc::ImageRef& getImage() const { return m_image; }
private:
doc::ImageRef m_image = nullptr;
};
class Context : public obs::observable<ContextObserver>,
public DocsObserver {
public:
@ -120,6 +139,11 @@ namespace app {
bool hasModifiedDocuments() const;
void notifyActiveSiteChanged();
void setDraggedData(std::unique_ptr<DraggedData> draggedData) {
m_draggedData = std::move(draggedData);
}
const DraggedData* draggedData() const { return m_draggedData.get(); }
void executeCommandFromMenuOrShortcut(Command* command, const Params& params = Params());
virtual void executeCommand(Command* command, const Params& params = Params());
@ -159,6 +183,7 @@ namespace app {
ContextFlags m_flags; // Last updated flags.
Doc* m_lastSelectedDoc;
mutable std::unique_ptr<Preferences> m_preferences;
std::unique_ptr<DraggedData> m_draggedData = nullptr;
// Result of the execution of a command.
CommandResult m_result;

View File

@ -39,7 +39,10 @@
#include "app/ui/workspace_tabs.h"
#include "app/ui_context.h"
#include "base/fs.h"
#include "os/event.h"
#include "os/event_queue.h"
#include "os/system.h"
#include "ui/drag_event.h"
#include "ui/message.h"
#include "ui/splitter.h"
#include "ui/system.h"
@ -91,6 +94,7 @@ MainWindow::MainWindow()
, m_devConsoleView(nullptr)
#endif
{
enableFlags(ALLOW_DROP);
}
// This 'initialize' function is a way to split the creation of the
@ -434,6 +438,22 @@ void MainWindow::onActiveViewChange()
UIContext::instance()->setActiveView(nullptr);
}
void MainWindow::onDrop(ui::DragEvent& e)
{
if (e.hasImage()) {
auto* cmd = Commands::instance()->byId(CommandId::NewFile());
Params params;
params.set("fromDraggedData", "true");
UIContext::instance()->setDraggedData(std::make_unique<DraggedData>(e.getImage()));
UIContext::instance()->executeCommand(cmd, params);
e.handled(true);
invalidate();
flushRedraw();
os::Event ev;
os::System::instance()->eventQueue()->queueEvent(ev);
}
}
bool MainWindow::isTabModified(Tabs* tabs, TabView* tabView)
{
if (DocView* docView = dynamic_cast<DocView*>(tabView)) {

View File

@ -119,6 +119,8 @@ namespace app {
void onActiveViewChange();
void onLanguageChange();
void onDrop(ui::DragEvent& e) override;
private:
DocView* getDocView();
HomeView* getHomeView();

View File

@ -0,0 +1,53 @@
// Aseprite
// Copyright (c) 2024 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/util/conversion_to_image.h"
#include "doc/image_traits.h"
#include "doc/pixel_format.h"
#include "os/surface.h"
#include <memory>
namespace app {
using namespace doc;
// TODO: This implementation has a lot of room for improvement, I made the bare
// minimum to make it work. Right now it only supports converting RGBA surfaces,
// other kind of surfaces won't be converted to an image as expected.
void convert_surface_to_image(
const os::Surface* surface,
int src_x, int src_y,
int w, int h,
ImageRef& image)
{
gfx::Rect srcBounds(src_x, src_y, w, h);
srcBounds = srcBounds.createIntersection(surface->getClipBounds());
if (srcBounds.isEmpty())
return;
src_x = srcBounds.x;
src_y = srcBounds.y;
w = srcBounds.w;
h = srcBounds.h;
image.reset(Image::create(PixelFormat::IMAGE_RGB, w, h));
for (int v=0; v<h; ++v, ++src_y) {
uint8_t* src_address = surface->getData(0, v);
uint8_t* dst_address = image->getPixelAddress(src_x, src_y);
std::copy(src_address,
src_address + RgbTraits::bytes_per_pixel * w,
dst_address);
}
}
} // namespace app

View File

@ -0,0 +1,31 @@
// Aseprite
// Copyright (c) 2024 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UTIL_CONVERSION_TO_IMAGE_H_INCLUDED
#define APP_UTIL_CONVERSION_TO_IMAGE_H_INCLUDED
#pragma once
#include "doc/image_ref.h"
namespace os {
class Surface;
}
namespace doc {
class Palette;
}
namespace app {
void convert_surface_to_image(
const os::Surface* surface,
int src_x, int src_y,
int w, int h,
doc::ImageRef& image);
} // namespace app
#endif

View File

@ -2056,7 +2056,7 @@ void Manager::dragEnter(os::DragEvent& ev)
{
Widget* widget = pickForDragAndDrop(ev.position());
ASSERT(widget->hasFlags(ALLOW_DROP));
ASSERT(!widget || widget && widget->hasFlags(ALLOW_DROP));
if (widget) {
m_dragOverWidget = widget;
@ -2080,7 +2080,7 @@ void Manager::drag(os::DragEvent& ev)
{
Widget* widget = pickForDragAndDrop(ev.position());
ASSERT(widget->hasFlags(ALLOW_DROP));
ASSERT(!widget || widget && widget->hasFlags(ALLOW_DROP));
if (m_dragOverWidget && m_dragOverWidget != widget) {
DragEvent uiev(this, m_dragOverWidget, ev);
@ -2104,7 +2104,7 @@ void Manager::drop(os::DragEvent& ev)
m_dragOverWidget = nullptr;
Widget* widget = pickForDragAndDrop(ev.position());
ASSERT(widget->hasFlags(ALLOW_DROP));
ASSERT(!widget || widget && widget->hasFlags(ALLOW_DROP));
if (widget) {
DragEvent uiev(this, widget, ev);