Scrolling

From WxWiki
Jump to: navigation, search

Scrolling widgets

#include "wx/wx.h"
#include <iostream>
 
class ScrolledWidgetsPane : public wxScrolledWindow
{
public:
    ScrolledWidgetsPane(wxWindow* parent, wxWindowID id) : wxScrolledWindow(parent, id)
    {
        // the sizer will take care of determining the needed scroll size
        // (if you don't use sizers you will need to manually set the viewport size)
        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
 
        // add a series of widgets
        for (int w=1; w<=120; w++)
        {
            wxButton* b = new wxButton(this, wxID_ANY, wxString::Format(wxT("Button %i"), w));
            sizer->Add(b, 0, wxALL, 3);
        }
 
        this->SetSizer(sizer);
 
        // this part makes the scrollbars show up
        this->FitInside(); // ask the sizer about the needed size
        this->SetScrollRate(5, 5);
    }
 
};
 
// A sample app that adds the scrolled pane to a frame to make this code runnable
class MyApp: public wxApp
{
    wxFrame *frame;
public:
 
    bool OnInit()
    {
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
        frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Scrolling Widgets"), wxPoint(50,50), wxSize(650,650));
 
        ScrolledWidgetsPane* my_image = new ScrolledWidgetsPane(frame, wxID_ANY);
        sizer->Add(my_image, 1, wxEXPAND);
        frame->SetSizer(sizer);
 
        frame->Show();
        return true;
    } 
};
 
IMPLEMENT_APP(MyApp)

Scrolling a render / an image

Scrolling a render (e.g. an image) can be done using a wxScrolledWindow, like in the following example :

#include "wx/wx.h"
#include <iostream>
 
class ScrolledImageComponent : public wxScrolledWindow
{
    wxBitmap* bitmap;
    int w,h;
public:
    ScrolledImageComponent(wxWindow* parent, wxWindowID id, wxString image_path) : wxScrolledWindow(parent, id)
    {
        wxImage image(image_path);
        if(!image.IsOk())
        {
            wxMessageBox(wxT("there was an error loading the image"));
            return;
        }
 
        w = image.GetWidth();
        h = image.GetHeight();
 
        /* init scrolled area size, scrolling speed, etc. */
        SetScrollbars(1,1, w, h, 0, 0);
 
        bitmap = new wxBitmap( image );
    }
    ~ScrolledImageComponent()
    {
        delete bitmap;
    }
    void OnDraw(wxDC& dc)
    {
        /* render the image - in a real app, if your scrolled area
           is somewhat big, you will want to draw only visible parts,
           not everything like below */
        dc.DrawBitmap(*bitmap, 0, 0, false);
 
        // also check wxScrolledWindow::PrepareDC
    }
};
 
 
class MyApp: public wxApp
{
    wxFrame *frame;
public:
 
    bool OnInit()
    {
        wxInitAllImageHandlers();
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
        frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Scrolling an Image"), wxPoint(50,50), wxSize(650,650));
 
        ScrolledImageComponent* my_image = new ScrolledImageComponent(frame, wxID_ANY, wxT("my_image.jpg") );
        sizer->Add(my_image, 1, wxALL | wxEXPAND, 120);
        frame->SetSizer(sizer);
 
        frame->Show();
        return true;
    } 
};
 
IMPLEMENT_APP(MyApp)

If you render lots of stuff, you will want to use wxScrolledWindow::GetViewStart along with the size of the scrolled window to render only the parts which are visible, in order to maintain good performance.

Synchronizing 2 scrolled panes

The question is often asked, here is a way to do it

#include "wx/wx.h"
#include <iostream>
 
class ScrolledImageComponent : public wxScrolledWindow
{
    wxBitmap* bitmap;
    int w,h;
    ScrolledImageComponent* m_peer;
 
public:
    ScrolledImageComponent(wxWindow* parent, wxWindowID id, wxString image_path) : wxScrolledWindow(parent, id)
    {
        m_peer = NULL;
 
        wxImage image(image_path);
        if(!image.IsOk())
        {
            wxMessageBox(wxT("there was an error loading the image"));
            return;
        }
 
        w = image.GetWidth();
        h = image.GetHeight();
 
        /* init scrolled area size, scrolling speed, etc. */
        SetScrollbars(1,1, w, h, 0, 0);
 
        bitmap = new wxBitmap( image );
 
        Connect(wxID_ANY, wxEVT_SCROLLWIN_THUMBTRACK, wxScrollWinEventHandler(ScrolledImageComponent::onScroll), NULL, this);
        Connect(wxID_ANY, wxEVT_SCROLLWIN_THUMBRELEASE, wxScrollWinEventHandler(ScrolledImageComponent::onScroll), NULL, this);
        Connect(wxID_ANY, wxEVT_SCROLLWIN_LINEUP, wxScrollWinEventHandler(ScrolledImageComponent::onScroll), NULL, this);
        Connect(wxID_ANY, wxEVT_SCROLLWIN_LINEDOWN, wxScrollWinEventHandler(ScrolledImageComponent::onScroll), NULL, this);
    }
    ~ScrolledImageComponent()
    {
        delete bitmap;
    }
 
    void SetPeer(ScrolledImageComponent* other)
    {
        m_peer = other;
    }
 
    void onScroll(wxScrollWinEvent& evt)
    {
        if (m_peer != NULL)
        {
            m_peer->Scroll(GetScrollPos(wxHORIZONTAL), GetScrollPos(wxVERTICAL));
        }
        evt.Skip(); // let the event go
    }
 
    void OnDraw(wxDC& dc)
    {
        /* render the image - in a real app, if your scrolled area
         is somewhat big, you will want to draw only visible parts,
         not everything like below */
        dc.DrawBitmap(*bitmap, 0, 0, false);
 
        // also check wxScrolledWindow::PrepareDC
    }
};
 
 
class MyApp: public wxApp
{
    wxFrame *frame;
public:
 
    bool OnInit()
    {
        wxInitAllImageHandlers();
        wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
        frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Scrolling an Image"), wxPoint(50,50), wxSize(650,650));
 
        ScrolledImageComponent* my_image = new ScrolledImageComponent(frame, wxID_ANY, wxT("my_image.jpg") );
        sizer->Add(my_image, 1, wxALL | wxEXPAND, 5);
 
        ScrolledImageComponent* my_image2 = new ScrolledImageComponent(frame, wxID_ANY, wxT("my_image.jpg") );
        sizer->Add(my_image2, 1, wxALL | wxEXPAND, 5);
 
        my_image->SetPeer(my_image2);
        my_image2->SetPeer(my_image);
 
        frame->SetSizer(sizer);
 
        frame->Show();
        return true;
    } 
};
 
IMPLEMENT_APP(MyApp)