Dragging a wxWindow around

From WxWiki
Jump to: navigation, search

In this example, you can drag a button around the frame by simply clicking on it and dragging.

Note: normally you would need to catch the capture lost event, which was omitted below for the sake of simplicity, don't forget to do it if you use capture in real code

#include "wx/wx.h"


class MovableButton : public wxButton
    {
        bool dragging;
        int x,y;
        wxPanel* parent;
        
    public:
        
        MovableButton(wxPanel* parent) : wxButton(parent, wxID_ANY, wxT("Drag me around"))
        {
            MovableButton::parent = parent;
            dragging = false;
        }
        
        void onMouseDown(wxMouseEvent& evt)
        {
            CaptureMouse();
            x = evt.GetX();
            y = evt.GetY();
            dragging=true;
            //evt.Veto();
        }
        void onMouseUp(wxMouseEvent& evt)
        {
            ReleaseMouse();
            dragging=false;
        }
        void onMove(wxMouseEvent& evt)
        {
            if(dragging)
            {
                wxPoint mouseOnScreen = wxGetMousePosition();
                int newx = mouseOnScreen.x - x;
                int newy = mouseOnScreen.y - y;
                this->Move( parent->ScreenToClient( wxPoint(newx, newy) ) );
            }
        }
        
        DECLARE_EVENT_TABLE()
    };

BEGIN_EVENT_TABLE(MovableButton,wxButton)
EVT_LEFT_DOWN(MovableButton::onMouseDown)
EVT_LEFT_UP(MovableButton::onMouseUp)
EVT_MOTION(MovableButton::onMove)
END_EVENT_TABLE()

class MyFrame : public wxFrame
    {
        wxPanel* mainPanel;
    public:
        MyFrame(wxWindow* ptr, int id, wxString name, wxPoint pos, wxSize size) : wxFrame(ptr,id,name,pos,size)
        {
            mainPanel = new wxPanel(this, wxID_ANY);
            MovableButton* btn = new MovableButton(mainPanel);
            
            Center();
            Show();
        }
    };

class MyApp: public wxApp
    {
        virtual bool OnInit();
    };


IMPLEMENT_APP(MyApp)


bool MyApp::OnInit()
{
    MyFrame* frame = new MyFrame(NULL, wxID_ANY,  wxT("Dragging a widget"), wxDefaultPosition, wxSize(640, 480));
    
    return true;
}


A "soft" drag

Instead of dragging an actual component around, you may want to just draw a drawing inside a panel. Here is a sample of how to do this :

#include "wx/wx.h"
#include "wx/sizer.h"

/** The panel we will be drawing on */
class BasicDrawPane : public wxPanel
{
    
    bool m_dragging;
    int m_x, m_y;
    int m_previous_mouse_x, m_previous_mouse_y;
    
public:
    BasicDrawPane(wxFrame* parent);
    
    void paintEvent(wxPaintEvent & evt);
    
    void render(wxDC& dc);
    
    void mouseMoved(wxMouseEvent& event);
    void mouseDown(wxMouseEvent& event);
    void mouseWheelMoved(wxMouseEvent& event);
    void mouseReleased(wxMouseEvent& event);
    void rightClick(wxMouseEvent& event);
    void mouseLeftWindow(wxMouseEvent& event);

    
    DECLARE_EVENT_TABLE()
};


class MyApp: public wxApp
{
    bool OnInit();
    
    wxFrame *frame;
    BasicDrawPane * drawPane;
public:
    
};

IMPLEMENT_APP(MyApp)


bool MyApp::OnInit()
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello wxDC"), wxPoint(50,50), wxSize(800,600));
	
    drawPane = new BasicDrawPane( (wxFrame*) frame );
    sizer->Add(drawPane, 1, wxEXPAND);
	
    frame->SetSizer(sizer);
    frame->SetAutoLayout(true);
	
    frame->Show();
    return true;
} 

BEGIN_EVENT_TABLE(BasicDrawPane, wxPanel)

EVT_MOTION(BasicDrawPane::mouseMoved)
EVT_LEFT_DOWN(BasicDrawPane::mouseDown)
EVT_LEFT_UP(BasicDrawPane::mouseReleased)
EVT_RIGHT_DOWN(BasicDrawPane::rightClick)
EVT_LEAVE_WINDOW(BasicDrawPane::mouseLeftWindow)
EVT_MOUSEWHEEL(BasicDrawPane::mouseWheelMoved)

// catch paint events
EVT_PAINT(BasicDrawPane::paintEvent)

END_EVENT_TABLE()


const int WIDTH = 100;
const int HEIGHT = 100;


void BasicDrawPane::mouseDown(wxMouseEvent& event)
{
    if (event.GetPosition().x >= m_x && event.GetPosition().x <= m_x + WIDTH &&
        event.GetPosition().y >= m_y && event.GetPosition().y <= m_y + HEIGHT)
    {
        m_dragging = true;
        m_previous_mouse_x = event.GetPosition().x;
        m_previous_mouse_y = event.GetPosition().y;
    }
}
void BasicDrawPane::mouseWheelMoved(wxMouseEvent& event) {}

void BasicDrawPane::mouseReleased(wxMouseEvent& event)
{
    m_dragging = false;
}

void BasicDrawPane::mouseMoved(wxMouseEvent& event)
{
    if (m_dragging && event.Dragging())
    {
        int delta_x = event.GetPosition().x - m_previous_mouse_x;
        int delta_y = event.GetPosition().y - m_previous_mouse_y;
        
        m_x += delta_x;
        m_y += delta_y;
        
        m_previous_mouse_x = event.GetPosition().x;
        m_previous_mouse_y = event.GetPosition().y;
        Refresh(); // trigger paint event
    }
}

void BasicDrawPane::mouseLeftWindow(wxMouseEvent& event)
{
    m_dragging = false;
}

void BasicDrawPane::rightClick(wxMouseEvent& event) {}


BasicDrawPane::BasicDrawPane(wxFrame* parent) :
wxPanel(parent)
{
    m_dragging = false;
    m_x = 100;
    m_y = 100;
}

/*
 * Called by the system of by wxWidgets when the panel needs
 * to be redrawn. You can also trigger this call by
 * calling Refresh()/Update().
 */
void BasicDrawPane::paintEvent(wxPaintEvent & evt)
{
    wxPaintDC dc(this);
    render(dc);
}

/*
 * Here we do the actual rendering. I put it in a separate
 * method so that it can work no matter what type of DC
 * (e.g. wxPaintDC or wxClientDC) is used.
 */
void BasicDrawPane::render(wxDC&  dc)
{
    // draw some text
    dc.DrawText(wxT("Drag the rectangle around"), 40, 60); 
    
    dc.SetBrush(*wxBLUE_BRUSH); // blue filling
    dc.SetPen( wxPen( wxColor(255,175,175), 10 ) ); // 10-pixels-thick pink outline
    dc.DrawRectangle( m_x, m_y, WIDTH, HEIGHT );
}