hi everyone,
i want to have drag and drop inside a wxTreeCtrl, but the way it is now, only nodes can be highlighted as the drag target, not the space between. so, can i somehow:
a) disable highlighting a node when some other node is dragged on top of it
b) show a line between nodes so the user can see where the dragged node would end up
thanx!
reorder wxTreeCtrl (drag in spaces between nodes) Topic is solved
-
- Ultimate wxWidgets Guru
- Posts: 542
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
Re: reorder wxTreeCtrl (drag in spaces between nodes)
got it to work.
just in case anyone has the same problem:
a) do not use wxTreeCtrl´s own EVT_TREE_BEGIN_DRAG events, so the item under the dragging mouse is not highlighted. instead, use wxDragImage (see documentation) although i am not sure why it should be of any more use than a simple wxStaticBitmap.
b) to show a line, in the EVT_MOTION while dragging use HitTest to see if the mouse is over a treeItem and if so, get its position with GetBoundingRect and place a line below/above the item. i reposition a custom marker derived from wxPanel because i had problems with flickering when drawing in the wxTreeCtrl´s paint event.
just in case anyone has the same problem:
a) do not use wxTreeCtrl´s own EVT_TREE_BEGIN_DRAG events, so the item under the dragging mouse is not highlighted. instead, use wxDragImage (see documentation) although i am not sure why it should be of any more use than a simple wxStaticBitmap.
b) to show a line, in the EVT_MOTION while dragging use HitTest to see if the mouse is over a treeItem and if so, get its position with GetBoundingRect and place a line below/above the item. i reposition a custom marker derived from wxPanel because i had problems with flickering when drawing in the wxTreeCtrl´s paint event.
- xaviou
- Super wx Problem Solver
- Posts: 437
- Joined: Mon Aug 21, 2006 3:18 pm
- Location: Annecy - France
- Contact:
Re: reorder wxTreeCtrl (drag in spaces between nodes)
Hi.
Thank you for sharing
Perhaps (if you have time for this) could you post the code of a "minimal" sample to easily see it in action.
Regards
Xav'
Thank you for sharing
Perhaps (if you have time for this) could you post the code of a "minimal" sample to easily see it in action.
Regards
Xav'
My wxWidgets stuff web page : X@v's wxStuff
-
- Ultimate wxWidgets Guru
- Posts: 542
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
Re: reorder wxTreeCtrl (drag in spaces between nodes)
Code: Select all
#ifndef __app_h__
#define __app_h__
#include <wx/treectrl.h>
#include <wx/dragimag.h>
#include <wx/dcbuffer.h>
// little red arrow to the left/right to mark the drag position
class PositionMarker : public wxPanel {
public:
PositionMarker(wxWindow *par, bool isRight) :
wxPanel(par, wxID_ANY, wxDefaultPosition, wxSize(7, 7)){
Connect(wxEVT_PAINT, wxPaintEventHandler(PositionMarker::onPaint));
if (isRight){
points[0] = wxPoint(7, 0);
points[1] = wxPoint(7, 7);
points[2] = wxPoint(0, 3);
}
else {
points[0] = wxPoint(0, 0);
points[1] = wxPoint(7, 3);
points[2] = wxPoint(0, 7);
}
}
private:
void onPaint(wxPaintEvent &evt){
wxBufferedPaintDC dc(this);
dc.SetBrush(*wxWHITE_BRUSH);
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(wxPoint(0, 0), GetClientSize());
// draw arrow
dc.SetBrush(*wxRED_BRUSH);
dc.DrawPolygon(3, points);
}
wxPoint points[3] = {};
};
// custom wxTreeCtrl for handling the mouse events
class MyTree : public wxTreeCtrl {
public:
// this is to separate a single click on an item from
// dragging so that dragging only really starts in EVT_MOTION
typedef enum {
DRAG_NONE,
DRAG_START,
DRAGGING
} TDragState;
MyTree(wxWindow *par) : wxTreeCtrl(par, wxID_ANY, wxDefaultPosition, wxSize(200, -1),
wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT | wxTR_EDIT_LABELS){
Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(MyTree::onLeftDown));
Connect(wxEVT_LEFT_UP, wxMouseEventHandler(MyTree::onLeftUp));
Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(MyTree::onLeaveWindow));
Connect(wxEVT_MOTION, wxMouseEventHandler(MyTree::onMouseMotion));
AddRoot(_("root"));
AppendItem(GetRootItem(), _("one"));
AppendItem(GetRootItem(), _("two"));
AppendItem(GetRootItem(), _("three"));
AppendItem(GetRootItem(), _("four"));
AppendItem(GetRootItem(), _("five"));
Expand(GetRootItem());
posMarkerLeft = new PositionMarker(this, false);
posMarkerLeft->Hide();
posMarkerRight = new PositionMarker(this, true);
posMarkerRight->Hide();
}
~MyTree(){
delete posMarkerLeft;
delete posMarkerRight;
}
// two red arrows to mark the insert position
PositionMarker *posMarkerLeft = nullptr, *posMarkerRight = nullptr;
// the image of the item being dragged shown under the cursor
wxDragImage *dragImg = nullptr;
private:
TDragState state = DRAG_NONE;
// the item being dragged at the moment
wxTreeItemId itemDragging = nullptr;
void onLeftDown(wxMouseEvent &evt);
void onLeftUp(wxMouseEvent &evt);
void onLeaveWindow(wxMouseEvent &evt){
if (dragImg)
endDragging();
}
// finish the drag action
void endDragging(){
state = DRAG_NONE;
dragImg->EndDrag();
dragImg->Hide();
if (dragImg)
delete dragImg;
dragImg = nullptr;
itemDragging = nullptr;
}
void onMouseMotion(wxMouseEvent &evt);
};
class MyFrame1 : public wxFrame
{
public:
MyFrame1( wxWindow* parent = NULL,
wxWindowID id = wxID_ANY,
const wxString& title = wxEmptyString,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxSize( 300,300 ),
long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
MyTree *tree = nullptr;
};
class App : public wxApp {
virtual bool OnInit();
virtual int OnExit();
};
#endif
Code: Select all
#include "app.h"
bool App::OnInit() {
MyFrame1 *mainFrame = new MyFrame1();
mainFrame->Show();
SetTopWindow(mainFrame);
return TRUE;
}
int App::OnExit() {
return 0;
}
IMPLEMENT_APP(App)
MyFrame1::MyFrame1( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) :
wxFrame( parent, id, title, pos, size, style )
{
tree = new MyTree(this);
wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(tree, 1, wxEXPAND);
SetSizer(sizer);
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
this->Centre( wxBOTH );
}
void MyTree::onLeftDown(wxMouseEvent &evt)
{
state = DRAG_START;
}
void MyTree::onMouseMotion(wxMouseEvent &evt)
{
this;
wxPoint evtPos = evt.GetPosition();
MyTree *mt = (MyTree*)evt.GetEventObject();
wxTreeItemId itemHovered = HitTest(evtPos);
// switch to dragging mode if mouseDown was on item
if (state == DRAG_START){
itemDragging = HitTest(evtPos);
if (itemDragging){
// create dragImage
if (dragImg)
delete dragImg;
dragImg = new wxDragImage(this, itemDragging);
// position the dragImage right on top of the dragged item and
// start dragging
wxRect rect;
GetBoundingRect(itemDragging, rect, true);
dragImg->BeginDrag(evtPos - rect.GetLeftTop(), this);
dragImg->Show();
state = DRAGGING;
}
else
state = DRAG_NONE;
}
// show markers only when dragging over an item
bool showMarkers = state == DRAGGING && itemHovered && itemHovered != GetRootItem();
posMarkerLeft->Show(showMarkers);
posMarkerRight->Show(showMarkers);
if (showMarkers){
dragImg->Hide();
wxRect rect;
GetBoundingRect(itemHovered, rect, true);
if (evtPos.y > rect.GetTop() + rect.GetHeight() / 2){
// below
posMarkerLeft->SetPosition(rect.GetBottomLeft() - wxPoint(7, 3));
posMarkerRight->SetPosition(rect.GetBottomRight() - wxPoint(0, 3));
}
else {
// above
posMarkerLeft->SetPosition(rect.GetTopLeft() - wxPoint(7, 3));
posMarkerRight->SetPosition(rect.GetTopRight() - wxPoint(0, 3));
}
mt->Refresh(false);
mt->Update();
dragImg->Show();
dragImg->Move(evtPos);
}
}
void MyTree::onLeftUp(wxMouseEvent &evt)
{
wxPoint evtPos = evt.GetPosition();
wxTreeItemId itemUnderMouse = HitTest(evtPos), previous;
if (itemUnderMouse && itemUnderMouse != GetRootItem()){
wxRect rect;
GetBoundingRect(itemUnderMouse, rect, true);
if (evtPos.y > rect.GetTop() + rect.GetHeight() / 2){
// below
previous = itemUnderMouse;
}
else {
// above
previous = GetPrevSibling(itemUnderMouse);
}
InsertItem(GetRootItem(), previous, GetItemText(itemDragging));
Delete(itemDragging);
}
if (dragImg)
endDragging();
}
Re: reorder wxTreeCtrl (drag in spaces between nodes)
I just wanted to say thanks for this. A few little tweaks and I've got it working in my code.
Some notes to others
1. On the Mac, you need to edit the endDragging() function and swap the order of dragImg->EndDrag(); and dragImg->Hide();
2. On the Mac, I cannot get the drag image to display for some reason.
Some notes to others
1. On the Mac, you need to edit the endDragging() function and swap the order of dragImg->EndDrag(); and dragImg->Hide();
2. On the Mac, I cannot get the drag image to display for some reason.
-
- Ultimate wxWidgets Guru
- Posts: 542
- Joined: Fri May 22, 2009 8:52 am
- Location: Bremen, Germany
Re: reorder wxTreeCtrl (drag in spaces between nodes)
i am happy that it could help someone! i have updated it myself since then and am still using it. i hope something like this will be part of wxwidgets someday.