init dragdrop

contains a new class simple_dragdrop and changes of listbox for dragdrop
This commit is contained in:
Jinhao 2018-10-09 03:56:50 +08:00
parent 95ccc3cb7e
commit 943a9e444d
17 changed files with 812 additions and 19 deletions

View File

@ -66,6 +66,7 @@
<Unit filename="../../source/gui/detail/native_window_interface.cpp" /> <Unit filename="../../source/gui/detail/native_window_interface.cpp" />
<Unit filename="../../source/gui/detail/window_layout.cpp" /> <Unit filename="../../source/gui/detail/window_layout.cpp" />
<Unit filename="../../source/gui/detail/window_manager.cpp" /> <Unit filename="../../source/gui/detail/window_manager.cpp" />
<Unit filename="../../source/gui/dragdrop.cpp" />
<Unit filename="../../source/gui/dragger.cpp" /> <Unit filename="../../source/gui/dragger.cpp" />
<Unit filename="../../source/gui/drawing.cpp" /> <Unit filename="../../source/gui/drawing.cpp" />
<Unit filename="../../source/gui/effects.cpp" /> <Unit filename="../../source/gui/effects.cpp" />

View File

@ -202,6 +202,7 @@
<ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" />
<ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" />
<ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" />
<ClCompile Include="..\..\source\gui\dragdrop.cpp" />
<ClCompile Include="..\..\source\gui\dragger.cpp" /> <ClCompile Include="..\..\source\gui\dragger.cpp" />
<ClCompile Include="..\..\source\gui\drawing.cpp" /> <ClCompile Include="..\..\source\gui\drawing.cpp" />
<ClCompile Include="..\..\source\gui\effects.cpp" /> <ClCompile Include="..\..\source\gui\effects.cpp" />

View File

@ -333,6 +333,9 @@
<ClCompile Include="..\..\source\detail\platform_abstraction.cpp"> <ClCompile Include="..\..\source\detail\platform_abstraction.cpp">
<Filter>Source Files\nana\detail</Filter> <Filter>Source Files\nana\detail</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\source\gui\dragdrop.cpp">
<Filter>Source Files\nana\gui</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\source\gui\widgets\group.cpp"> <ClCompile Include="..\..\source\gui\widgets\group.cpp">

View File

@ -196,6 +196,7 @@
<ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" />
<ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" />
<ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" />
<ClCompile Include="..\..\source\gui\dragdrop.cpp" />
<ClCompile Include="..\..\source\gui\dragger.cpp" /> <ClCompile Include="..\..\source\gui\dragger.cpp" />
<ClCompile Include="..\..\source\gui\drawing.cpp" /> <ClCompile Include="..\..\source\gui\drawing.cpp" />
<ClCompile Include="..\..\source\gui\effects.cpp" /> <ClCompile Include="..\..\source\gui\effects.cpp" />

View File

@ -291,6 +291,9 @@
<ClCompile Include="..\..\source\detail\platform_abstraction.cpp"> <ClCompile Include="..\..\source\detail\platform_abstraction.cpp">
<Filter>Source Files\detail</Filter> <Filter>Source Files\detail</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\source\gui\dragdrop.cpp">
<Filter>Source Files\gui</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\include\nana\any.hpp"> <ClInclude Include="..\..\include\nana\any.hpp">

View File

@ -179,6 +179,7 @@
<ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" /> <ClCompile Include="..\..\source\gui\detail\native_window_interface.cpp" />
<ClCompile Include="..\..\source\gui\detail\window_layout.cpp" /> <ClCompile Include="..\..\source\gui\detail\window_layout.cpp" />
<ClCompile Include="..\..\source\gui\detail\window_manager.cpp" /> <ClCompile Include="..\..\source\gui\detail\window_manager.cpp" />
<ClCompile Include="..\..\source\gui\dragdrop.cpp" />
<ClCompile Include="..\..\source\gui\dragger.cpp" /> <ClCompile Include="..\..\source\gui\dragger.cpp" />
<ClCompile Include="..\..\source\gui\drawing.cpp" /> <ClCompile Include="..\..\source\gui\drawing.cpp" />
<ClCompile Include="..\..\source\gui\effects.cpp" /> <ClCompile Include="..\..\source\gui\effects.cpp" />

View File

@ -292,6 +292,9 @@
<ClCompile Include="..\..\source\gui\wvl.cpp"> <ClCompile Include="..\..\source\gui\wvl.cpp">
<Filter>Sources\gui</Filter> <Filter>Sources\gui</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\source\gui\dragdrop.cpp">
<Filter>Sources\gui</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\include\nana\gui\widgets\spinbox.hpp"> <ClInclude Include="..\..\include\nana\gui\widgets\spinbox.hpp">

View File

@ -65,6 +65,13 @@ namespace nana
blend blend
}; };
enum class dragdrop_status
{
not_ready,
ready,
in_progress
};
namespace category namespace category
{ {
enum class flags enum class flags

View File

@ -1,7 +1,7 @@
/** /**
* A Basic Window Widget Definition * A Basic Window Widget Definition
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -179,7 +179,8 @@ namespace detail
bool ignore_menubar_focus : 1; ///< A flag indicates whether the menubar sets the focus. bool ignore_menubar_focus : 1; ///< A flag indicates whether the menubar sets the focus.
bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it bool ignore_mouse_focus : 1; ///< A flag indicates whether the widget accepts focus when clicking on it
bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key. bool space_click_enabled : 1; ///< A flag indicates whether enable mouse_down/click/mouse_up when pressing and releasing whitespace key.
unsigned Reserved :18; bool draggable : 1;
unsigned Reserved :17;
unsigned char tab; ///< indicate a window that can receive the keyboard TAB unsigned char tab; ///< indicate a window that can receive the keyboard TAB
mouse_action action; mouse_action action;
mouse_action action_before; mouse_action action_before;
@ -234,6 +235,7 @@ namespace detail
///< if the active_window is null, the parent of this window keeps focus. ///< if the active_window is null, the parent of this window keeps focus.
paint::graphics glass_buffer; ///< if effect.bground is avaiable. Refer to window_layout::make_bground. paint::graphics glass_buffer; ///< if effect.bground is avaiable. Refer to window_layout::make_bground.
update_state upd_state; update_state upd_state;
dragdrop_status dnd_state{ dragdrop_status::not_ready };
union union
{ {

View File

@ -0,0 +1,45 @@
/**
* Drag and Drop Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/dragdrop.hpp
* @author: Jinhao(cnjinhao@hotmail.com)
*/
#ifndef NANA_GUI_DRAGDROP_INCLUDED
#define NANA_GUI_DRAGDROP_INCLUDED
#include <nana/push_ignore_diagnostic>
#include <functional>
#include "basis.hpp"
#include <memory>
namespace nana
{
class simple_dragdrop
{
struct implementation;
simple_dragdrop(const simple_dragdrop&) = delete;
simple_dragdrop& operator=(const simple_dragdrop&) = delete;
simple_dragdrop(simple_dragdrop&&) = delete;
simple_dragdrop& operator=(simple_dragdrop&&) = delete;
public:
simple_dragdrop(window drag_wd);
~simple_dragdrop();
/// Sets a condition that determines whether the drag&drop can start
void condition(std::function<bool()> predicate_fn);
void make_drop(window target, std::function<void()> drop_fn);
private:
implementation* const impl_;
};
}
#endif

View File

@ -1,7 +1,7 @@
/* /*
* Nana GUI Programming Interface Implementation * Nana GUI Programming Interface Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -117,6 +117,9 @@ namespace API
void lazy_refresh(); void lazy_refresh();
void draw_shortkey_underline(paint::graphics&, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color&); void draw_shortkey_underline(paint::graphics&, const std::string& text, wchar_t shortkey, std::size_t shortkey_position, const point& text_pos, const color&);
void window_draggable(window, bool enabled);
bool window_draggable(window);
}//end namespace dev }//end namespace dev
@ -476,6 +479,8 @@ namespace API
::std::optional<std::pair<::nana::size, ::nana::size>> content_extent(window wd, unsigned limited_px, bool limit_width); ::std::optional<std::pair<::nana::size, ::nana::size>> content_extent(window wd, unsigned limited_px, bool limit_width);
unsigned screen_dpi(bool x_requested); unsigned screen_dpi(bool x_requested);
dragdrop_status window_dragdrop_status(::nana::window);
}//end namespace API }//end namespace API
}//end namespace nana }//end namespace nana

View File

@ -1497,6 +1497,9 @@ the nana::detail::basic_window member pointer scheme
void erase(index_pairs indexes); ///<Erases specified items. void erase(index_pairs indexes); ///<Erases specified items.
item_proxy erase(item_proxy); item_proxy erase(item_proxy);
/// Returns the item which is hovered
index_pair hovered() const;
bool sortable() const; bool sortable() const;
void sortable(bool enable); void sortable(bool enable);

View File

@ -1,7 +1,7 @@
/* /*
* A Basic Window Widget Definition * A Basic Window Widget Definition
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -410,6 +410,7 @@ namespace nana
flags.enabled = true; flags.enabled = true;
flags.modal = false; flags.modal = false;
flags.take_active = true; flags.take_active = true;
flags.draggable = false;
flags.dropable = false; flags.dropable = false;
flags.fullscreen = false; flags.fullscreen = false;
flags.tab = nana::detail::tab_type::none; flags.tab = nana::detail::tab_type::none;

609
source/gui/dragdrop.cpp Normal file
View File

@ -0,0 +1,609 @@
/**
* Drag and Drop Implementation
* Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2018 Jinhao(cnjinhao@hotmail.com)
*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* @file: nana/gui/dragdrop.cpp
* @author: Jinhao(cnjinhao@hotmail.com)
*/
#include <nana/gui/dragdrop.hpp>
#include <nana/gui/programming_interface.hpp>
#include <nana/gui/detail/bedrock.hpp>
#include <nana/gui/detail/basic_window.hpp>
#include <map>
#include <set>
#ifdef NANA_WINDOWS
# include <windows.h>
# include <oleidl.h>
# include <comdef.h>
# include <Shlobj.h>
#endif
namespace nana
{
/// drop_association
/**
* This class is used for querying whether tow windows have a connection of drag and drop
*/
class drop_association
{
public:
void add(window source, window target)
{
assoc_[source].insert(target);
}
void erase(window wd)
{
assoc_.erase(wd);
for (auto & assoc : assoc_)
assoc.second.erase(wd);
}
bool has(window source, window target) const
{
auto i = assoc_.find(source);
if (i != assoc_.end())
return (0 != i->second.count(target));
return false;
}
private:
std::map<window, std::set<window>> assoc_;
};
#ifdef NANA_WINDOWS
template<typename Interface, const IID& iid>
class win32com_iunknown : public Interface
{
public:
//Implements IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown || riid == iid) {
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&ref_count_);
}
STDMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement(&ref_count_);
if (cRef == 0) delete this;
return cRef;
}
private:
LONG ref_count_{ 1 };
};
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:
//Implements IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown || riid == IID_IDropTarget) {
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&ref_count_);
}
STDMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement(&ref_count_);
if (cRef == 0) delete this;
return cRef;
}
private:
// IDropTarget
STDMETHODIMP DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{
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))
*pdwEffect &= DROPEFFECT_COPY;
else
*pdwEffect = DROPEFFECT_NONE;
return S_OK;
}
STDMETHODIMP DragLeave()
{
return E_NOTIMPL;
}
STDMETHODIMP Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
{
return E_NOTIMPL;
}
private:
LONG ref_count_{ 1 };
window source_window_{ nullptr };
const drop_association& drop_assoc_;
};
class drop_source : public win32com_iunknown<IDropSource, IID_IDropSource>
{
public:
drop_source(window wd) :
window_handle_(wd)
{}
window source() const
{
return window_handle_;
}
private:
// IDropSource
STDMETHODIMP QueryContinueDrag(BOOL esc_pressed, DWORD key_state) override
{
if (esc_pressed)
return DRAGDROP_S_CANCEL;
//Drop the object if left button is released.
if (0 == (key_state & (MK_LBUTTON)))
return DRAGDROP_S_DROP;
return S_OK;
}
STDMETHODIMP GiveFeedback(DWORD effect) override
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}
private:
window const window_handle_;
};
class drop_data : public win32com_iunknown<IDataObject, IID_IDataObject>
{
struct medium
{
STGMEDIUM * stgmedium;
FORMATETC * format;
};
public:
STDMETHODIMP GetData(FORMATETC *request_format, STGMEDIUM *pmedium) override
{
if (!(request_format && pmedium))
return E_INVALIDARG;
pmedium->hGlobal = nullptr;
for (auto & med : mediums_)
{
if ((request_format->tymed & med.format->tymed) &&
(request_format->dwAspect == med.format->dwAspect) &&
(request_format->cfFormat == med.format->cfFormat))
{
return _m_copy_medium(pmedium, med.stgmedium, med.format);
}
}
return DV_E_FORMATETC;
}
STDMETHODIMP GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium) override
{
return E_NOTIMPL;
}
STDMETHODIMP QueryGetData(FORMATETC *pformatetc) override
{
if (NULL == pformatetc)
return E_INVALIDARG;
if (!(DVASPECT_CONTENT & pformatetc->dwAspect))
return DV_E_DVASPECT;
HRESULT result = DV_E_TYMED;
for(auto & med : mediums_)
{
if (med.format->tymed & pformatetc->tymed)
{
if (med.format->cfFormat == pformatetc->cfFormat)
return S_OK;
result = DV_E_FORMATETC;
}
}
return result;
}
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pformatectIn, FORMATETC *pformatetcOut) override
{
return E_NOTIMPL;
}
STDMETHODIMP SetData(FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) override
{
if (!(pformatetc && pmedium))
return E_INVALIDARG;
if (pformatetc->tymed != pmedium->tymed)
return E_FAIL;
medium retain;
retain.format = new FORMATETC;
retain.stgmedium = new (std::nothrow) STGMEDIUM;
if (nullptr == retain.stgmedium)
{
delete retain.format;
return E_FAIL;
}
std::memset(retain.format, 0, sizeof(FORMATETC));
std::memset(retain.stgmedium, 0, sizeof(STGMEDIUM));
*retain.format = *pformatetc;
_m_copy_medium(retain.stgmedium, pmedium, pformatetc);
if (TRUE == fRelease)
::ReleaseStgMedium(pmedium);
mediums_.emplace_back(retain);
return S_OK;
}
STDMETHODIMP EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) override
{
if (NULL == ppenumFormatEtc)
return E_INVALIDARG;
if (DATADIR_GET != dwDirection)
return E_NOTIMPL;
*ppenumFormatEtc = NULL;
FORMATETC rgfmtetc[] =
{
{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, 0, TYMED_HGLOBAL },
};
return ::SHCreateStdEnumFmtEtc(ARRAYSIZE(rgfmtetc), rgfmtetc, ppenumFormatEtc);
}
STDMETHODIMP DAdvise(FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP DUnadvise(DWORD dwConnection) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
private:
static HRESULT _m_copy_medium(STGMEDIUM* stgmed_dst, STGMEDIUM* stgmed_src, FORMATETC* fmt_src)
{
if (!(stgmed_dst && stgmed_src && fmt_src))
return E_INVALIDARG;
switch (stgmed_src->tymed)
{
case TYMED_HGLOBAL:
stgmed_dst->hGlobal = (HGLOBAL)OleDuplicateData(stgmed_src->hGlobal, fmt_src->cfFormat, 0);
break;
case TYMED_GDI:
stgmed_dst->hBitmap = (HBITMAP)OleDuplicateData(stgmed_src->hBitmap, fmt_src->cfFormat, 0);
break;
case TYMED_MFPICT:
stgmed_dst->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(stgmed_src->hMetaFilePict, fmt_src->cfFormat, 0);
break;
case TYMED_ENHMF:
stgmed_dst->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(stgmed_src->hEnhMetaFile, fmt_src->cfFormat, 0);
break;
case TYMED_FILE:
stgmed_dst->lpszFileName = (LPOLESTR)OleDuplicateData(stgmed_src->lpszFileName, fmt_src->cfFormat, 0);
break;
case TYMED_ISTREAM:
stgmed_dst->pstm = stgmed_src->pstm;
stgmed_src->pstm->AddRef();
break;
case TYMED_ISTORAGE:
stgmed_dst->pstg = stgmed_src->pstg;
stgmed_src->pstg->AddRef();
break;
case TYMED_NULL:
default:
break;
}
stgmed_dst->tymed = stgmed_src->tymed;
stgmed_dst->pUnkForRelease = nullptr;
if (stgmed_src->pUnkForRelease)
{
stgmed_dst->pUnkForRelease = stgmed_src->pUnkForRelease;
stgmed_src->pUnkForRelease->AddRef();
}
return S_OK;
}
private:
std::vector<medium> mediums_;
};
#endif
class dragdrop_service
{
dragdrop_service() = default;
public:
static dragdrop_service& instance()
{
static dragdrop_service serv;
return serv;
}
void create_dragdrop(window wd)
{
auto native_wd = API::root(wd);
if (nullptr == native_wd)
return;
if(table_.empty())
::OleInitialize(nullptr);
win32com_drop_target* drop_target = nullptr;
auto i = table_.find(native_wd);
if (i == table_.end())
{
drop_target = new win32com_drop_target{drop_assoc_};
::RegisterDragDrop(reinterpret_cast<HWND>(native_wd), drop_target);
table_[native_wd] = drop_target;
}
else
{
drop_target = i->second;
drop_target->AddRef();
}
}
void remove(window wd)
{
auto i = table_.find(API::root(wd));
if (i != table_.end())
{
if (0 == i->second->Release())
table_.erase(i);
}
drop_assoc_.erase(wd);
}
bool dragdrop(window drag_wd)
{
auto i = table_.find(API::root(drag_wd));
if (table_.end() == i)
return false;
auto drop_src = new drop_source{ drag_wd };
auto drop_dat = new (std::nothrow) drop_data;
if (!drop_dat)
{
delete drop_src;
return false;
}
i->second->set_source(drag_wd);
DWORD eff;
auto status = ::DoDragDrop(drop_dat, drop_src, DROPEFFECT_COPY, &eff);
i->second->set_source(nullptr);
return true;
}
drop_association& drop_assoc()
{
return drop_assoc_;
}
private:
std::map<native_window_type, win32com_drop_target*> table_;
drop_association drop_assoc_;
};
struct simple_dragdrop::implementation
{
window window_handle;
std::function<bool()> predicate;
std::map<window, std::function<void()>> targets;
bool dragging{ false };
#if 0
bool cancel()
{
if (!dragging)
return false;
if (API::is_window(window_handle))
{
dragging = true;
using basic_window = ::nana::detail::basic_window;
auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(window_handle);
real_wd->other.dnd_state = dragdrop_status::not_ready;
}
API::release_capture(window_handle);
dragging = false;
return true;
}
#endif
};
simple_dragdrop::simple_dragdrop(window drag_wd) :
impl_(new implementation)
{
dragdrop_service::instance().create_dragdrop(drag_wd);
if (!API::is_window(drag_wd))
{
delete impl_;
throw std::invalid_argument("simple_dragdrop: invalid window handle");
}
impl_->window_handle = drag_wd;
API::dev::window_draggable(drag_wd, true);
auto & events = API::events<>(drag_wd);
#ifdef NANA_WINDOWS
events.mouse_down.connect_unignorable([this](const arg_mouse& arg){
if (arg.is_left_button() && API::is_window(impl_->window_handle))
{
impl_->dragging = ((!impl_->predicate) || impl_->predicate());
}
});
events.mouse_move.connect_unignorable([this](const arg_mouse& arg) {
if (!(arg.is_left_button() && impl_->dragging && API::is_window(arg.window_handle)))
return;
using basic_window = ::nana::detail::basic_window;
auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle);
real_wd->other.dnd_state = dragdrop_status::in_progress;
auto has_dropped = dragdrop_service::instance().dragdrop(arg.window_handle);
real_wd->other.dnd_state = dragdrop_status::not_ready;
impl_->dragging = false;
if (has_dropped)
{
auto drop_wd = API::find_window(API::cursor_position());
auto i = impl_->targets.find(drop_wd);
if ((impl_->targets.end() != i) && i->second)
i->second();
}
});
#else
events.mouse_down.connect_unignorable([drag_wd](const arg_mouse& arg){
if (arg.is_left_button() && API::is_window(drag_wd))
{
API::set_capture(drag_wd, true);
using basic_window = ::nana::detail::basic_window;
auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(drag_wd);
real_wd->other.dnd_state = dragdrop_status::ready;
}
});
events.mouse_move.connect_unignorable([this](const arg_mouse& arg){
if (!arg.is_left_button())
return;
if (impl_->dragging)
{
auto drop_wd = API::find_window(API::cursor_position());
auto i = impl_->targets.find(drop_wd);
}
else
{
if ((!impl_->predicate) || impl_->predicate())
{
if (API::is_window(arg.window_handle))
{
impl_->dragging = true;
using basic_window = ::nana::detail::basic_window;
auto real_wd = reinterpret_cast<::nana::detail::basic_window*>(arg.window_handle);
real_wd->other.dnd_state = dragdrop_status::in_progress;
dragdrop_service::instance().dragdrop(arg.window_handle);
return;
}
}
API::release_capture(impl_->window_handle);
}
});
events.mouse_up.connect_unignorable([this]{
if (impl_->cancel())
{
auto drop_wd = API::find_window(API::cursor_position());
auto i = impl_->targets.find(drop_wd);
if (impl_->targets.end() == i || !i->second)
return;
i->second();
}
});
events.key_press.connect_unignorable([this]{
impl_->cancel();
});
#endif
}
simple_dragdrop::~simple_dragdrop()
{
dragdrop_service::instance().remove(impl_->window_handle);
API::dev::window_draggable(impl_->window_handle, false);
delete impl_;
}
void simple_dragdrop::condition(std::function<bool()> predicate_fn)
{
impl_->predicate.swap(predicate_fn);
}
void simple_dragdrop::make_drop(window target, std::function<void()> drop_fn)
{
dragdrop_service::instance().drop_assoc().add(impl_->window_handle, target);
impl_->targets[target].swap(drop_fn);
}
}

View File

@ -411,6 +411,26 @@ namespace API
} }
} }
void window_draggable(window wd, bool enabled)
{
auto real_wd = reinterpret_cast<basic_window*>(wd);
internal_scope_guard lock;
if (restrict::wd_manager().available(real_wd))
real_wd->flags.draggable = enabled;
}
bool window_draggable(window wd)
{
auto real_wd = reinterpret_cast<basic_window*>(wd);
internal_scope_guard lock;
if (restrict::wd_manager().available(real_wd))
return real_wd->flags.draggable;
return false;
}
}//end namespace dev }//end namespace dev
widget* get_widget(window wd) widget* get_widget(window wd)
@ -1518,5 +1538,16 @@ namespace API
{ {
return ::nana::platform_abstraction::screen_dpi(x_requested); return ::nana::platform_abstraction::screen_dpi(x_requested);
} }
dragdrop_status window_dragdrop_status(::nana::window wd)
{
auto real_wd = reinterpret_cast<basic_window*>(wd);
internal_scope_guard lock;
if (restrict::wd_manager().available(real_wd))
return real_wd->other.dnd_state;
return dragdrop_status::not_ready;
}
}//end namespace API }//end namespace API
}//end namespace nana }//end namespace nana

View File

@ -1968,6 +1968,12 @@ namespace nana
std::vector<inline_pane*> active_panes_; std::vector<inline_pane*> active_panes_;
};//end class es_lister };//end class es_lister
enum class operation_states
{
none,
msup_deselect
};
/// created and live by the trigger, holds data for listbox: the state of the struct does not effect on member funcions, therefore all data members are public. /// created and live by the trigger, holds data for listbox: the state of the struct does not effect on member funcions, therefore all data members are public.
struct essence struct essence
@ -1981,7 +1987,9 @@ namespace nana
bool auto_draw{true}; bool auto_draw{true};
bool checkable{false}; bool checkable{false};
bool if_image{false}; bool if_image{false};
#if 0 //deprecated
bool deselect_deferred{ false }; //deselects items when mouse button is released. bool deselect_deferred{ false }; //deselects items when mouse button is released.
#endif
unsigned text_height; unsigned text_height;
::nana::listbox::export_options def_exp_options; ::nana::listbox::export_options def_exp_options;
@ -1999,6 +2007,12 @@ namespace nana
std::function<void(paint::graphics&, const rectangle&, bool)> ctg_icon_renderer; ///< Renderer for the category icon std::function<void(paint::graphics&, const rectangle&, bool)> ctg_icon_renderer; ///< Renderer for the category icon
struct operation_rep
{
operation_states state{operation_states::none};
index_pair item;
}operation;
struct mouse_selection_part struct mouse_selection_part
{ {
bool started{ false }; bool started{ false };
@ -2121,6 +2135,10 @@ namespace nana
void update_mouse_selection(const point& screen_pos) void update_mouse_selection(const point& screen_pos)
{ {
//Don't update if it is not started
if (!mouse_selection.started)
return;
mouse_selection.screen_pos = screen_pos; mouse_selection.screen_pos = screen_pos;
auto logic_pos = coordinate_cast(screen_pos, true); auto logic_pos = coordinate_cast(screen_pos, true);
@ -4132,8 +4150,13 @@ namespace nana
using item_state = essence::item_state; using item_state = essence::item_state;
using parts = essence::parts; using parts = essence::parts;
#if 0 //deprecated
//Cancel deferred deselection operation when mouse moves. //Cancel deferred deselection operation when mouse moves.
essence_->deselect_deferred = false; essence_->deselect_deferred = false;
#else
if (operation_states::msup_deselect == essence_->operation.state)
essence_->operation.state = operation_states::none;
#endif
bool need_refresh = false; bool need_refresh = false;
@ -4293,21 +4316,44 @@ namespace nana
essence_->mouse_selection.reverse_selection = true; essence_->mouse_selection.reverse_selection = true;
new_selected_status = !essence_->cs_status(abs_item_pos, true); new_selected_status = !essence_->cs_status(abs_item_pos, true);
} }
#if 0 //deprecated
else if (nana::mouse::right_button == arg.button)
{
//Unselects all selected items if the current item is not selected before selecting.
auto selected = lister.pick_items(true);
if (selected.cend() == std::find(selected.cbegin(), selected.cend(), item_pos))
lister.select_for_all(false, abs_item_pos);
}
else else
{ {
if (nana::mouse::right_button == arg.button) //Unselects all selected items except current item if right button clicked.
lister.select_for_all(false, abs_item_pos); //cancel all selections
}
#else
else
{
auto selected = lister.pick_items(true);
if (selected.cend() != std::find(selected.cbegin(), selected.cend(), item_pos))
{ {
//Unselects all selected items if the current item is not selected before selecting. //If the current selected one has been selected before selecting, remains the selection states for all
auto selected = lister.pick_items(true); //selected items. But these items will be unselected when the mouse is released.
if (selected.cend() == std::find(selected.cbegin(), selected.cend(), item_pos))
lister.select_for_all(false, abs_item_pos); //Other items will be unselected if multiple items are selected.
if (selected.size() > 1)
{
essence_->operation.item = abs_item_pos;
//Don't deselect the selections if the listbox is draggable.
//It should remain the selections for drag-and-drop
if (!API::dev::window_draggable(arg.window_handle))
essence_->operation.state = operation_states::msup_deselect;
}
} }
else else
{ lister.select_for_all(false, abs_item_pos);
//Unselects all selected items except current item if right button clicked.
lister.select_for_all(false, abs_item_pos); //cancel all selections
}
} }
#endif
} }
else else
{ {
@ -4377,7 +4423,15 @@ namespace nana
//Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed //Deselection of all items is deferred to the mouse up event when ctrl or shift is not pressed
//Pressing ctrl or shift is to selects other items without deselecting current selections. //Pressing ctrl or shift is to selects other items without deselecting current selections.
#if 0 //deprecated
essence_->deselect_deferred = !(arg.ctrl || arg.shift); essence_->deselect_deferred = !(arg.ctrl || arg.shift);
#else
if (!(arg.ctrl || arg.shift))
{
essence_->operation.state = operation_states::msup_deselect;
essence_->operation.item = index_pair{nana::npos, nana::npos};
}
#endif
} }
if(update) if(update)
@ -4419,11 +4473,19 @@ namespace nana
need_refresh = true; need_refresh = true;
} }
#if 0 //deprecated
if (essence_->deselect_deferred) if (essence_->deselect_deferred)
{ {
essence_->deselect_deferred = false; essence_->deselect_deferred = false;
need_refresh |= (essence_->lister.select_for_all(false)); need_refresh |= (essence_->lister.select_for_all(false));
} }
#endif
if (operation_states::msup_deselect == essence_->operation.state)
{
essence_->operation.state = operation_states::none;
need_refresh |= essence_->lister.select_for_all(false, essence_->operation.item);
}
if (need_refresh) if (need_refresh)
{ {
@ -5790,6 +5852,18 @@ namespace nana
return item_proxy(ess); return item_proxy(ess);
} }
listbox::index_pair listbox::hovered() const
{
internal_scope_guard lock;
using parts = drawerbase::listbox::essence::parts;
auto & ptr_where = _m_ess().pointer_where;
if ((ptr_where.first == parts::list || ptr_where.first == parts::checker) && ptr_where.second != npos)
return _m_ess().lister.advance(_m_ess().first_display(), static_cast<int>(ptr_where.second));
return index_pair{ npos, npos };
}
bool listbox::sortable() const bool listbox::sortable() const
{ {
internal_scope_guard lock; internal_scope_guard lock;

View File

@ -65,7 +65,7 @@ namespace nana {
bool passive{ true }; //The passive mode determines whether to update if scrollbar changes. It updates the client window if passive is true. bool passive{ true }; //The passive mode determines whether to update if scrollbar changes. It updates the client window if passive is true.
bool drag_started{ false }; bool drag_view_move{ false }; //indicates the status of the content-view if it moves origin by dragging
point origin; point origin;
std::shared_ptr<cv_scroll_rep> cv_scroll; std::shared_ptr<cv_scroll_rep> cv_scroll;
@ -120,19 +120,22 @@ namespace nana {
if (!arg.is_left_button()) if (!arg.is_left_button())
return; return;
this->drag_started = this->view.view_area().is_hit(arg.pos); this->drag_view_move = this->view.view_area().is_hit(arg.pos);
} }
else if (event_code::mouse_move == arg.evt_code) else if (event_code::mouse_move == arg.evt_code)
{ {
if (this->drag_started && this->drive(arg.pos)) if (this->drag_view_move && (dragdrop_status::not_ready == API::window_dragdrop_status(this->window_handle)))
{ {
tmr.interval(16); if (this->drive(arg.pos))
tmr.start(); {
tmr.interval(16);
tmr.start();
}
} }
} }
else if (event_code::mouse_up == arg.evt_code) else if (event_code::mouse_up == arg.evt_code)
{ {
this->drag_started = false; this->drag_view_move = false;
tmr.stop(); tmr.stop();
} }
}; };