add simple_dragdrop feature

This commit is contained in:
Jinhao 2018-10-27 17:46:22 +08:00
parent ff9b90a766
commit 711dff56de
3 changed files with 182 additions and 115 deletions

View File

@ -1111,7 +1111,7 @@ namespace detail
} }
bool platform_spec::register_dragdrop(native_window_type wd, dragdrop_interface* ddrop) bool platform_spec::register_dragdrop(native_window_type wd, x11_dragdrop_interface* ddrop)
{ {
platform_scope_guard lock; platform_scope_guard lock;
if(0 != xdnd_.dragdrop.count(wd)) if(0 != xdnd_.dragdrop.count(wd))
@ -1121,13 +1121,11 @@ namespace detail
::XChangeProperty(display_, reinterpret_cast<Window>(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8, ::XChangeProperty(display_, reinterpret_cast<Window>(wd), atombase_.xdnd_aware, XA_ATOM, sizeof(int) * 8,
PropModeReplace, reinterpret_cast<unsigned char*>(&dndver), 1); PropModeReplace, reinterpret_cast<unsigned char*>(&dndver), 1);
auto & ref_drop = xdnd_.dragdrop[wd]; xdnd_.dragdrop[wd] = ddrop;
ref_drop.dragdrop = ddrop;
ref_drop.ref_count = 1;
return true; return true;
} }
dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd) x11_dragdrop_interface* platform_spec::remove_dragdrop(native_window_type wd)
{ {
platform_scope_guard lock; platform_scope_guard lock;
auto i = xdnd_.dragdrop.find(wd); auto i = xdnd_.dragdrop.find(wd);
@ -1135,13 +1133,9 @@ namespace detail
return nullptr; return nullptr;
auto ddrop = i->second; auto ddrop = i->second;
if(ddrop.ref_count <= 1) xdnd_.dragdrop.erase(i);
{
xdnd_.dragdrop.erase(i); return ddrop;
return ddrop.dragdrop;
}
--ddrop.ref_count;
return nullptr;
} }
//_m_msg_filter //_m_msg_filter

View File

@ -177,10 +177,13 @@ namespace detail
~platform_scope_guard(); ~platform_scope_guard();
}; };
class dragdrop_interface class x11_dragdrop_interface
{ {
public: public:
virtual ~dragdrop_interface() = default; virtual ~x11_dragdrop_interface() = default;
virtual void add_ref() = 0;
virtual std::size_t release() = 0;
}; };
class platform_spec class platform_spec
@ -264,8 +267,8 @@ namespace detail
// the image object is release in remove() method. // the image object is release in remove() method.
const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&); const nana::paint::graphics& keep_window_icon(native_window_type, const nana::paint::image&);
bool register_dragdrop(native_window_type, dragdrop_interface*); bool register_dragdrop(native_window_type, x11_dragdrop_interface*);
dragdrop_interface* remove_dragdrop(native_window_type); x11_dragdrop_interface* remove_dragdrop(native_window_type);
private: private:
static int _m_msg_filter(XEvent&, msg_packet_tag&); static int _m_msg_filter(XEvent&, msg_packet_tag&);
void _m_caret_routine(); void _m_caret_routine();
@ -323,13 +326,7 @@ namespace detail
Window wd_src; Window wd_src;
nana::point pos; nana::point pos;
struct refcount_dragdrop std::map<native_window_type, x11_dragdrop_interface*> dragdrop;
{
dragdrop_interface* dragdrop{nullptr};
std::size_t ref_count{0};
};
std::map<native_window_type, refcount_dragdrop> dragdrop;
}xdnd_; }xdnd_;
msg_dispatcher * msg_dispatcher_; msg_dispatcher * msg_dispatcher_;

View File

@ -32,38 +32,65 @@
# include <fstream> # include <fstream>
#endif #endif
namespace nana namespace nana
{ {
/// drop_association class dragdrop_session
/**
* This class is used for querying whether tow windows have a connection of drag and drop
*/
class drop_association
{ {
public: public:
void add(window source, window target) virtual ~dragdrop_session() = default;
void insert(window source, window target)
{ {
assoc_[source].insert(target); table_[source].insert(target);
} }
void erase(window wd) void erase(window source, window target)
{ {
assoc_.erase(wd); auto i = table_.find(source);
if (table_.end() == i)
return;
for (auto & assoc : assoc_) i->second.erase(target);
assoc.second.erase(wd);
if ((nullptr == target) || i->second.empty())
table_.erase(i);
} }
bool has(window source, window target) const bool has(window source, window target) const
{ {
auto i = assoc_.find(source); auto i = table_.find(source);
if (i != assoc_.end()) if (i != table_.end())
return (0 != i->second.count(target)); return (0 != i->second.count(target));
return false; return false;
} }
bool has_source(window source) const
{
return (table_.count(source) != 0);
}
bool empty() const
{
return table_.empty();
}
void set_current_source(window source)
{
if (table_.count(source))
current_source_ = source;
else
current_source_ = nullptr;
}
window current_source() const
{
return current_source_;
}
private: private:
std::map<window, std::set<window>> assoc_; std::map<window, std::set<window>> table_;
window current_source_{ nullptr };
}; };
#ifdef NANA_WINDOWS #ifdef NANA_WINDOWS
@ -98,18 +125,8 @@ namespace nana
LONG ref_count_{ 1 }; LONG ref_count_{ 1 };
}; };
class win32com_drop_target : public IDropTarget, public dragdrop_session
class win32com_drop_target : public IDropTarget
{ {
public:
win32com_drop_target(const drop_association& drop_assoc) :
drop_assoc_(drop_assoc)
{}
void set_source(window wd)
{
source_window_ = wd;
}
public: public:
//Implements IUnknown //Implements IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
@ -146,11 +163,11 @@ namespace nana
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect) STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{ {
auto hovered_wd = API::find_window(point(pt.x, pt.y)); auto hovered_wd = API::find_window(point(pt.x, pt.y));
if ((hovered_wd && (hovered_wd == source_window_)) || drop_assoc_.has(source_window_, hovered_wd))
if ((hovered_wd && (hovered_wd == this->current_source())) || this->has(this->current_source(), hovered_wd))
*pdwEffect &= DROPEFFECT_COPY; *pdwEffect &= DROPEFFECT_COPY;
else else
*pdwEffect = DROPEFFECT_NONE; *pdwEffect = DROPEFFECT_NONE;
return S_OK; return S_OK;
} }
@ -165,9 +182,6 @@ namespace nana
} }
private: private:
LONG ref_count_{ 1 }; LONG ref_count_{ 1 };
window source_window_{ nullptr };
const drop_association& drop_assoc_;
}; };
class drop_source : public win32com_iunknown<IDropSource, IID_IDropSource> class drop_source : public win32com_iunknown<IDropSource, IID_IDropSource>
@ -374,9 +388,25 @@ namespace nana
std::vector<medium> mediums_; std::vector<medium> mediums_;
}; };
#elif defined(NANA_X11) #elif defined(NANA_X11)
class x11_dragdrop: public detail::dragdrop_interface class x11_dragdrop: public detail::x11_dragdrop_interface, public dragdrop_session
{ {
public:
//Implement x11_dragdrop_interface
void add_ref() override
{
++ref_count_;
}
std::size_t release() override
{
std::size_t val = --ref_count_;
if(0 == val)
delete this;
return val;
}
private:
std::atomic<std::size_t> ref_count_{ 1 };
}; };
class shared_icons class shared_icons
@ -433,68 +463,89 @@ namespace nana
{ {
dragdrop_service() = default; dragdrop_service() = default;
public: public:
#ifdef NANA_WINDOWS
using dragdrop_target = win32com_drop_target;
#else
using dragdrop_target = x11_dragdrop;
#endif
static dragdrop_service& instance() static dragdrop_service& instance()
{ {
static dragdrop_service serv; static dragdrop_service serv;
return serv; return serv;
} }
void create_dragdrop(window wd) dragdrop_session* create_dragdrop(window wd)
{ {
auto native_wd = API::root(wd); auto native_wd = API::root(wd);
if (nullptr == native_wd) if (nullptr == native_wd)
return; return nullptr;
#ifdef NANA_WINDOWS dragdrop_target * ddrop = nullptr;
if(table_.empty())
::OleInitialize(nullptr);
win32com_drop_target* drop_target = nullptr;
auto i = table_.find(native_wd); auto i = table_.find(native_wd);
if (i == table_.end()) if(table_.end() == i)
{ {
drop_target = new win32com_drop_target{drop_assoc_}; ddrop = new dragdrop_target;
::RegisterDragDrop(reinterpret_cast<HWND>(native_wd), drop_target); #ifdef NANA_WINDOWS
if (table_.empty())
::OleInitialize(nullptr);
table_[native_wd] = drop_target; ::RegisterDragDrop(reinterpret_cast<HWND>(native_wd), ddrop);
#else
if(!_m_spec().register_dragdrop(native_wd, ddrop))
{
delete ddrop;
return nullptr;
}
#endif
table_[native_wd] = ddrop;
} }
else else
{ {
drop_target = i->second; ddrop = dynamic_cast<dragdrop_target*>(i->second);
drop_target->AddRef();
} #ifdef NANA_WINDOWS
#elif defined(NANA_X11) ddrop->AddRef();
auto ddrop = new x11_dragdrop; #else
if(!_m_spec().register_dragdrop(native_wd, ddrop)) ddrop->add_ref();
delete ddrop;
#endif #endif
}
return ddrop;
} }
void remove(window wd) void remove(window source)
{ {
#ifdef NANA_WINDOWS auto native_src = API::root(source);
auto i = table_.find(API::root(wd));
if (i != table_.end())
{
if (0 == i->second->Release())
table_.erase(i);
}
#elif defined(NANA_X11)
auto ddrop = _m_spec().remove_dragdrop(API::root(wd));
delete ddrop;
#endif
drop_assoc_.erase(wd);
auto i = table_.find(API::root(source));
if (i == table_.end())
return;
i->second->erase(source, nullptr);
if (i->second->empty())
{
auto ddrop = dynamic_cast<dragdrop_target*>(i->second);
table_.erase(i);
#ifdef NANA_WINDOWS
::RevokeDragDrop(reinterpret_cast<HWND>(native_src));
ddrop->Release();
#elif defined(NANA_X11)
_m_spec().remove_dragdrop(native_src);
ddrop->release();
#endif
}
} }
bool dragdrop(window drag_wd) bool dragdrop(window drag_wd)
{ {
#ifdef NANA_WINDOWS
auto i = table_.find(API::root(drag_wd)); auto i = table_.find(API::root(drag_wd));
if (table_.end() == i) if (table_.end() == i)
return false; return false;
#ifdef NANA_WINDOWS
auto drop_src = new drop_source{ drag_wd }; auto drop_src = new drop_source{ drag_wd };
auto drop_dat = new (std::nothrow) drop_data; auto drop_dat = new (std::nothrow) drop_data;
if (!drop_dat) if (!drop_dat)
@ -503,21 +554,25 @@ namespace nana
return false; return false;
} }
i->second->set_source(drag_wd); i->second->set_current_source(drag_wd);
DWORD eff; DWORD eff;
auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff); auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff);
i->second->set_source(nullptr); i->second->set_current_source(nullptr);
delete drop_src;
delete drop_dat;
return true; return true;
#elif defined(NANA_X11) #elif defined(NANA_X11)
auto const native_wd = reinterpret_cast<Window>(API::root(drag_wd)); auto ddrop = dynamic_cast<x11_dragdrop*>(i->second);
auto const native_source = reinterpret_cast<Window>(API::root(drag_wd));
{ {
detail::platform_scope_guard lock; detail::platform_scope_guard lock;
::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_wd, CurrentTime); ::XSetSelectionOwner(_m_spec().open_display(), _m_spec().atombase().xdnd_selection, native_source, CurrentTime);
} }
@ -528,7 +583,7 @@ namespace nana
//while(true) //while(true)
{ {
_m_spec().msg_dispatch([this, drag_wd, native_wd, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{ _m_spec().msg_dispatch([this, ddrop, drag_wd, native_source, &target_wd, &atombase](const detail::msg_packet_tag& msg_pkt) mutable{
if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind) if(detail::msg_packet_tag::pkt_family::xevent == msg_pkt.kind)
{ {
auto const disp = _m_spec().open_display(); auto const disp = _m_spec().open_display();
@ -545,23 +600,22 @@ namespace nana
::XUndefineCursor(disp, hovered_.native_wd); ::XUndefineCursor(disp, hovered_.native_wd);
} }
_m_client_msg(native_cur_wd, native_wd, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING); _m_client_msg(native_cur_wd, native_source, atombase.xdnd_enter, atombase.text_uri_list, XA_STRING);
hovered_.native_wd = native_cur_wd; hovered_.native_wd = native_cur_wd;
} }
auto cur_wd = API::find_window(API::cursor_position());
auto cur_wd = API::find_window(API::cursor_position());
if(hovered_.window_handle != cur_wd) if(hovered_.window_handle != cur_wd)
{ {
_m_free_cursor(); _m_free_cursor();
hovered_.window_handle = cur_wd; hovered_.window_handle = cur_wd;
if((drag_wd == cur_wd) || drop_assoc_.has(drag_wd, cur_wd)) const char* icon = (((drag_wd == cur_wd) || ddrop->has(drag_wd, cur_wd)) ? "dnd-move" : "dnd-none");
hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-move").c_str()); hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor(icon).c_str());
else ::XDefineCursor(disp, native_cur_wd, hovered_.cursor);
hovered_.cursor = ::XcursorFilenameLoadCursor(disp, icons_.cursor("dnd-none").c_str());
::XDefineCursor(disp, native_cur_wd, hovered_.cursor);
} }
} }
else if(msg_pkt.u.xevent.type == ButtonRelease) else if(msg_pkt.u.xevent.type == ButtonRelease)
@ -582,10 +636,6 @@ namespace nana
return false; return false;
} }
drop_association& drop_assoc()
{
return drop_assoc_;
}
#ifdef NANA_X11 #ifdef NANA_X11
private: private:
static nana::detail::platform_spec & _m_spec() static nana::detail::platform_spec & _m_spec()
@ -640,13 +690,12 @@ namespace nana
::XFreeCursor(_m_spec().open_display(), hovered_.cursor); ::XFreeCursor(_m_spec().open_display(), hovered_.cursor);
hovered_.cursor = 0; hovered_.cursor = 0;
} }
} }
#endif #endif
private: private:
drop_association drop_assoc_; std::map<native_window_type, dragdrop_session*> table_;
#ifdef NANA_WINDOWS #ifdef NANA_WINDOWS
std::map<native_window_type, win32com_drop_target*> table_;
#elif defined (NANA_X11) #elif defined (NANA_X11)
shared_icons icons_; shared_icons icons_;
struct hovered_status struct hovered_status
@ -664,12 +713,20 @@ namespace nana
struct simple_dragdrop::implementation struct simple_dragdrop::implementation
{ {
window window_handle; window window_handle{ nullptr };
dragdrop_session * ddrop{ nullptr };
std::function<bool()> predicate; std::function<bool()> predicate;
std::map<window, std::function<void()>> targets; std::map<window, std::function<void()>> targets;
bool dragging{ false }; bool dragging{ false };
struct event_handlers
{
nana::event_handle destroy;
nana::event_handle mouse_move;
nana::event_handle mouse_down;
}events;
#ifdef NANA_X11 #ifdef NANA_X11
bool cancel() bool cancel()
{ {
@ -697,21 +754,26 @@ namespace nana
simple_dragdrop::simple_dragdrop(window drag_wd) : simple_dragdrop::simple_dragdrop(window drag_wd) :
impl_(new implementation) impl_(new implementation)
{ {
dragdrop_service::instance().create_dragdrop(drag_wd);
if (!API::is_window(drag_wd)) if (!API::is_window(drag_wd))
{ {
delete impl_; delete impl_;
throw std::invalid_argument("simple_dragdrop: invalid window handle"); throw std::invalid_argument("simple_dragdrop: invalid window handle");
} }
impl_->ddrop = dragdrop_service::instance().create_dragdrop(drag_wd);
impl_->window_handle = drag_wd; impl_->window_handle = drag_wd;
API::dev::window_draggable(drag_wd, true); API::dev::window_draggable(drag_wd, true);
auto & events = API::events<>(drag_wd); auto & events = API::events<>(drag_wd);
#if 1 //#ifdef NANA_WINDOWS #if 1 //#ifdef NANA_WINDOWS
events.mouse_down.connect_unignorable([this](const arg_mouse& arg){ impl_->events.destroy = events.destroy.connect_unignorable([this](const arg_destroy&) {
dragdrop_service::instance().remove(impl_->window_handle);
API::dev::window_draggable(impl_->window_handle, false);
});
impl_->events.mouse_down = events.mouse_down.connect_unignorable([this](const arg_mouse& arg){
if (arg.is_left_button() && API::is_window(impl_->window_handle)) if (arg.is_left_button() && API::is_window(impl_->window_handle))
{ {
impl_->dragging = ((!impl_->predicate) || impl_->predicate()); impl_->dragging = ((!impl_->predicate) || impl_->predicate());
@ -722,7 +784,7 @@ namespace nana
} }
}); });
events.mouse_move.connect_unignorable([this](const arg_mouse& arg) { impl_->events.mouse_move = events.mouse_move.connect_unignorable([this](const arg_mouse& arg) {
if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle))) if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle)))
return; return;
@ -806,19 +868,33 @@ namespace nana
simple_dragdrop::~simple_dragdrop() simple_dragdrop::~simple_dragdrop()
{ {
dragdrop_service::instance().remove(impl_->window_handle); if (impl_->window_handle)
API::dev::window_draggable(impl_->window_handle, false); {
dragdrop_service::instance().remove(impl_->window_handle);
API::dev::window_draggable(impl_->window_handle, false);
API::umake_event(impl_->events.destroy);
API::umake_event(impl_->events.mouse_down);
API::umake_event(impl_->events.mouse_move);
}
delete impl_; delete impl_;
} }
void simple_dragdrop::condition(std::function<bool()> predicate_fn) void simple_dragdrop::condition(std::function<bool()> predicate_fn)
{ {
if (nullptr == impl_)
throw std::logic_error("simple_dragdrop is empty");
impl_->predicate.swap(predicate_fn); impl_->predicate.swap(predicate_fn);
} }
void simple_dragdrop::make_drop(window target, std::function<void()> drop_fn) void simple_dragdrop::make_drop(window target, std::function<void()> drop_fn)
{ {
dragdrop_service::instance().drop_assoc().add(impl_->window_handle, target); if (nullptr == impl_)
throw std::logic_error("simple_dragdrop is empty");
impl_->ddrop->insert(impl_->window_handle, target);
impl_->targets[target].swap(drop_fn); impl_->targets[target].swap(drop_fn);
} }
} }