mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 13:21:34 +00:00
Introduce drag & drop events to widgets
This commit is contained in:
parent
2cf880eaf9
commit
65ec2bd7b7
@ -29,8 +29,9 @@ namespace ui {
|
||||
DOUBLE_BUFFERED = 0x00002000, // The widget is painted in a back-buffer and then flipped to the main display
|
||||
TRANSPARENT = 0x00004000, // The widget has transparent parts that needs the background painted before
|
||||
CTRL_RIGHT_CLICK = 0x00008000, // The widget should transform Ctrl+click to right-click on OS X.
|
||||
ALLOW_DROP = 0x40000000, // The widget can participate as a drop target in a drag & drop operation.
|
||||
IGNORE_MOUSE = 0x80000000, // Don't process mouse messages for this widget (useful for labels, boxes, grids, etc.)
|
||||
PROPERTIES_MASK = 0x8000ffff,
|
||||
PROPERTIES_MASK = 0xC000ffff,
|
||||
|
||||
HORIZONTAL = 0x00010000,
|
||||
VERTICAL = 0x00020000,
|
||||
@ -43,7 +44,7 @@ namespace ui {
|
||||
HOMOGENEOUS = 0x01000000,
|
||||
WORDWRAP = 0x02000000,
|
||||
CHARWRAP = 0x04000000,
|
||||
ALIGN_MASK = 0x7fff0000,
|
||||
ALIGN_MASK = 0x3fff0000,
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
48
src/ui/drag_event.h
Normal file
48
src/ui/drag_event.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2024 Igara Studio S.A.
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef UI_DRAG_EVENT_H_INCLUDED
|
||||
#define UI_DRAG_EVENT_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "os/dnd.h"
|
||||
#include "ui/event.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class DragEvent : public Event {
|
||||
public:
|
||||
DragEvent(Component* source, ui::Widget* target, os::DragEvent& ev)
|
||||
: Event(source)
|
||||
, m_position(ev.position() - target->bounds().origin())
|
||||
, m_ev(ev) {}
|
||||
|
||||
bool handled() const { return m_handled; }
|
||||
void handled(bool value) { m_handled = value; }
|
||||
|
||||
// Operations allowed by the source of the drag & drop operation. Can be a
|
||||
// bitwise combination of values.
|
||||
os::DropOperation allowedOperations() const { return m_ev.supportedOperations(); }
|
||||
|
||||
// Operation supported by the target of the drag & drop operation. Cannot
|
||||
// be a bitwise combination of values.
|
||||
os::DropOperation supportsOperation() const { return m_ev.dropResult(); }
|
||||
// Set the operation supported by the target of the drag & drop operation.
|
||||
// Cannot be a bitwise combination of values.
|
||||
void supportsOperation(os::DropOperation operation) { m_ev.dropResult(operation); }
|
||||
|
||||
const gfx::Point& position() const { return m_position; }
|
||||
|
||||
private:
|
||||
bool m_handled = false;
|
||||
gfx::Point m_position;
|
||||
os::DragEvent& m_ev;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
#endif // UI_DRAG_EVENT_H_INCLUDED
|
@ -28,6 +28,8 @@
|
||||
#include "os/system.h"
|
||||
#include "os/window.h"
|
||||
#include "os/window_spec.h"
|
||||
#include "ui/base.h"
|
||||
#include "ui/drag_event.h"
|
||||
#include "ui/intern.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
@ -205,8 +207,10 @@ Manager::Manager(const os::WindowRef& nativeWindow)
|
||||
, m_mouseButton(kButtonNone)
|
||||
{
|
||||
// The native window can be nullptr when running tests
|
||||
if (nativeWindow)
|
||||
if (nativeWindow) {
|
||||
nativeWindow->setUserData(&m_display);
|
||||
nativeWindow->setDragTarget(this);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_UI_THREADS
|
||||
ASSERT(manager_thread == std::thread::id());
|
||||
@ -2037,6 +2041,76 @@ bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
|
||||
return used;
|
||||
}
|
||||
|
||||
void Manager::dragEnter(os::DragEvent& ev)
|
||||
{
|
||||
Widget* widget = pick(ev.position());
|
||||
if (widget && widget->hasFlags(ALLOW_DROP)) {
|
||||
m_dragOverWidget = widget;
|
||||
DragEvent uiev(this, widget, ev);
|
||||
widget->onDragEnter(uiev);
|
||||
ev.dropResult(uiev.supportsOperation());
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::dragLeave(os::DragEvent& ev)
|
||||
{
|
||||
Widget* widget = m_dragOverWidget;
|
||||
if (widget) {
|
||||
DragEvent uiev(this, widget, ev);
|
||||
widget->onDragLeave(uiev);
|
||||
m_dragOverWidget = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::drag(os::DragEvent& ev)
|
||||
{
|
||||
Widget* widget = pick(ev.position());
|
||||
|
||||
if (m_dragOverWidget && m_dragOverWidget != widget) {
|
||||
DragEvent uiev(this, m_dragOverWidget, ev);
|
||||
m_dragOverWidget->onDragLeave(uiev);
|
||||
m_dragOverWidget = nullptr;
|
||||
}
|
||||
|
||||
if (widget && widget->hasFlags(ALLOW_DROP)) {
|
||||
DragEvent uiev(this, widget, ev);
|
||||
if (m_dragOverWidget != widget) {
|
||||
m_dragOverWidget = widget;
|
||||
widget->onDragEnter(uiev);
|
||||
}
|
||||
widget->onDrag(uiev);
|
||||
ev.dropResult(uiev.supportsOperation());
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::drop(os::DragEvent& ev)
|
||||
{
|
||||
m_dragOverWidget = nullptr;
|
||||
Widget* widget = pick(ev.position());
|
||||
if (widget && widget->hasFlags(ALLOW_DROP)) {
|
||||
DragEvent uiev(this, widget, ev);
|
||||
widget->onDrop(uiev);
|
||||
if (uiev.handled()) {
|
||||
ev.acceptDrop(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// There were no widget that accepted the drop, then see if we can treat it
|
||||
// like a DropFiles event.
|
||||
if (ev.dataProvider()->contains(os::DragDataItemType::Paths)) {
|
||||
ev.acceptDrop(true);
|
||||
// We must queue an os::Event to wakeup the underlying system queue on
|
||||
// masOS. If we had used the enqueueMessage() method instead, it could
|
||||
// happen that the program might look unresponsive because it is waiting
|
||||
// for an OS event.
|
||||
os::Event dropFilesEv;
|
||||
dropFilesEv.setType(os::Event::DropFiles);
|
||||
dropFilesEv.setFiles(ev.dataProvider()->getPaths());
|
||||
os::System::instance()->eventQueue()->queueEvent(dropFilesEv);
|
||||
}
|
||||
}
|
||||
|
||||
// It's like Widget::onInvalidateRegion() but optimized for the
|
||||
// Manager (as we know that all children in a Manager will be windows,
|
||||
// we can use this knowledge to avoid some calculations).
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "gfx/region.h"
|
||||
#include "os/dnd.h"
|
||||
#include "ui/display.h"
|
||||
#include "ui/keys.h"
|
||||
#include "ui/message_type.h"
|
||||
@ -28,7 +29,8 @@ namespace ui {
|
||||
class Timer;
|
||||
class Window;
|
||||
|
||||
class Manager : public Widget {
|
||||
class Manager : public Widget
|
||||
, public os::DragTarget {
|
||||
public:
|
||||
static Manager* getDefault() { return m_defaultManager; }
|
||||
static bool widgetAssociatedToManager(Widget* widget);
|
||||
@ -161,6 +163,11 @@ namespace ui {
|
||||
int pumpQueue();
|
||||
bool sendMessageToWidget(Message* msg, Widget* widget);
|
||||
|
||||
void dragEnter(os::DragEvent& ev) override;
|
||||
void dragLeave(os::DragEvent& ev) override;
|
||||
void drag(os::DragEvent& ev) override;
|
||||
void drop(os::DragEvent& ev) override;
|
||||
|
||||
static Widget* findLowestCommonAncestor(Widget* a, Widget* b);
|
||||
static bool someParentIsFocusStop(Widget* widget);
|
||||
static Widget* findMagneticWidget(Widget* widget);
|
||||
@ -189,6 +196,9 @@ namespace ui {
|
||||
|
||||
// Last pressed mouse button.
|
||||
MouseButton m_mouseButton;
|
||||
|
||||
// Widget over which the drag is being hovered in a drag & drop operation.
|
||||
Widget* m_dragOverWidget = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "ui/cursor.h"
|
||||
#include "ui/cursor_type.h"
|
||||
#include "ui/display.h"
|
||||
#include "ui/drag_event.h"
|
||||
#include "ui/entry.h"
|
||||
#include "ui/event.h"
|
||||
#include "ui/fit_bounds.h"
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "text/font.h"
|
||||
#include "text/font_mgr.h"
|
||||
#include "ui/app_state.h"
|
||||
#include "ui/drag_event.h"
|
||||
#include "ui/init_theme_event.h"
|
||||
#include "ui/intern.h"
|
||||
#include "ui/layout_io.h"
|
||||
@ -1800,6 +1801,38 @@ text::ShaperFeatures Widget::onGetTextShaperFeatures() const
|
||||
return text::ShaperFeatures();
|
||||
}
|
||||
|
||||
void Widget::onDragEnter(DragEvent& e)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LOG(VERBOSE, "UI: [id=%s, type=%d]: onDragEnter(), position: (%d, %d)\n",
|
||||
id().c_str(), type(), e.position().x, e.position().y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Widget::onDragLeave(DragEvent& e)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LOG(VERBOSE, "UI: [id=%s, type=%d]: onDragLeave(), position: (%d, %d)\n",
|
||||
id().c_str(), type(), e.position().x, e.position().y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Widget::onDrag(DragEvent& e)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LOG(VERBOSE, "UI: [id=%s, type=%d]: onDrag(), position: (%d, %d)\n",
|
||||
id().c_str(), type(), e.position().x, e.position().y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Widget::onDrop(DragEvent& e)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
LOG(VERBOSE, "UI: [id=%s, type=%d]: onDrop(), position: (%d, %d)\n",
|
||||
id().c_str(), type(), e.position().x, e.position().y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Widget::offsetWidgets(int dx, int dy)
|
||||
{
|
||||
if (dx == 0 && dy == 0)
|
||||
|
@ -44,6 +44,7 @@ namespace ui {
|
||||
class Style;
|
||||
class Theme;
|
||||
class Window;
|
||||
class DragEvent;
|
||||
|
||||
class Widget : public Component {
|
||||
public:
|
||||
@ -445,6 +446,11 @@ namespace ui {
|
||||
virtual text::TextBlobRef onMakeTextBlob() const;
|
||||
virtual text::ShaperFeatures onGetTextShaperFeatures() const;
|
||||
|
||||
virtual void onDragEnter(DragEvent& e);
|
||||
virtual void onDragLeave(DragEvent& e);
|
||||
virtual void onDrag(DragEvent& e);
|
||||
virtual void onDrop(DragEvent& e);
|
||||
|
||||
private:
|
||||
void removeChild(const WidgetsList::iterator& it);
|
||||
void paint(Graphics* graphics,
|
||||
@ -483,6 +489,8 @@ namespace ui {
|
||||
|
||||
gfx::Border m_border; // Border separation with the parent
|
||||
int m_childSpacing; // Separation between children
|
||||
|
||||
friend Manager;
|
||||
};
|
||||
|
||||
WidgetType register_widget_type();
|
||||
|
Loading…
x
Reference in New Issue
Block a user