+class MyCanvas : public wxScrolled<wxPanel>
+{
+public:
+ MyCanvas(wxWindow *parent);
+
+private:
+ void OnPaint(wxPaintEvent& event);
+ void OnQueryPosition(wxCommandEvent& event);
+ void OnAddButton(wxCommandEvent& event);
+ void OnDeleteButton(wxCommandEvent& event);
+ void OnMoveButton(wxCommandEvent& event);
+ void OnScrollWin(wxCommandEvent& event);
+ void OnMouseRightDown(wxMouseEvent& event);
+ void OnMouseWheel(wxMouseEvent& event);
+
+ wxButton *m_button;
+
+ DECLARE_EVENT_TABLE()
+};
+
+class MyCanvasFrame : public wxFrame
+{
+public:
+ MyCanvasFrame(wxWindow *parent)
+ : wxFrame(parent, wxID_ANY, "MyCanvas")
+ {
+ m_canvas = new MyCanvas(this);
+
+ wxMenu *menuFile = new wxMenu();
+ menuFile->Append(wxID_DELETE, "&Delete all");
+ menuFile->Append(wxID_NEW, "Insert &new");
+
+ wxMenuBar *mbar = new wxMenuBar();
+ mbar->Append(menuFile, "&File");
+ SetMenuBar( mbar );
+
+ Connect(wxID_DELETE, wxEVT_MENU,
+ wxCommandEventHandler(MyCanvasFrame::OnDeleteAll));
+ Connect(wxID_NEW, wxEVT_MENU,
+ wxCommandEventHandler(MyCanvasFrame::OnInsertNew));
+
+ Show();
+ }
+
+private:
+ void OnDeleteAll(wxCommandEvent& WXUNUSED(event))
+ {
+ m_canvas->DestroyChildren();
+ }
+
+ void OnInsertNew(wxCommandEvent& WXUNUSED(event))
+ {
+ (void)new wxButton(m_canvas, wxID_ANY, "Hello", wxPoint(100,100));
+ }
+
+ MyCanvas *m_canvas;
+};
+
+// ----------------------------------------------------------------------------
+// example using sizers with wxScrolled
+// ----------------------------------------------------------------------------
+
+const wxSize SMALL_BUTTON( 100, 50 );
+const wxSize LARGE_BUTTON( 300, 200 );
+
+class MySizerScrolledWindow : public wxScrolled<wxWindow>
+{
+public:
+ MySizerScrolledWindow(wxWindow *parent);
+
+private:
+ // this button can be clicked to change its own size in the handler below,
+ // the window size will be automatically adjusted to fit the button
+ wxButton *m_button;
+
+ void OnResizeClick(wxCommandEvent& event);
+};
+
+class MySizerFrame : public wxFrame
+{
+public:
+ MySizerFrame(wxWindow *parent)
+ : wxFrame(parent, wxID_ANY, "MySizerScrolledWindow")
+ {
+ new MySizerScrolledWindow(this);
+
+ // ensure that the scrollbars appear when the button becomes large
+ SetClientSize(LARGE_BUTTON/2);
+ Show();
+ }
+};
+
+// ----------------------------------------------------------------------------
+// example showing scrolling only part of the window
+// ----------------------------------------------------------------------------
+
+// this window consists of an empty space in its corner, column labels window
+// along its top, row labels window along its left hand side and a canvas in
+// the remaining space
+
+class MySubColLabels : public wxWindow
+{
+public:
+ MySubColLabels(wxScrolled<wxWindow> *parent)
+ : wxWindow(parent, wxID_ANY)
+ {
+ m_owner = parent;
+
+ Connect(wxEVT_PAINT, wxPaintEventHandler(MySubColLabels::OnPaint));
+ }
+
+private:
+ void OnPaint(wxPaintEvent& WXUNUSED(event))
+ {
+ wxPaintDC dc(this);
+
+ // This is wrong.. it will translate both x and y if the
+ // window is scrolled, the label windows are active in one
+ // direction only. Do the action below instead -- RL.
+ //m_owner->PrepareDC( dc );
+
+ int xScrollUnits, xOrigin;
+
+ m_owner->GetViewStart( &xOrigin, 0 );
+ m_owner->GetScrollPixelsPerUnit( &xScrollUnits, 0 );
+ dc.SetDeviceOrigin( -xOrigin * xScrollUnits, 0 );
+
+ dc.DrawText("Column 1", 5, 5);
+ dc.DrawText("Column 2", 105, 5);
+ dc.DrawText("Column 3", 205, 5);
+ }
+
+ wxScrolled<wxWindow> *m_owner;
+};
+
+class MySubRowLabels : public wxWindow
+{
+public:
+ MySubRowLabels(wxScrolled<wxWindow> *parent)
+ : wxWindow(parent, wxID_ANY)
+ {
+ m_owner = parent;
+
+ Connect(wxEVT_PAINT, wxPaintEventHandler(MySubRowLabels::OnPaint));
+ }
+
+private:
+ void OnPaint(wxPaintEvent& WXUNUSED(event))
+ {
+ wxPaintDC dc(this);
+
+ // This is wrong.. it will translate both x and y if the
+ // window is scrolled, the label windows are active in one
+ // direction only. Do the action below instead -- RL.
+ //m_owner->PrepareDC( dc );
+
+ int yScrollUnits, yOrigin;
+
+ m_owner->GetViewStart( 0, &yOrigin );
+ m_owner->GetScrollPixelsPerUnit( 0, &yScrollUnits );
+ dc.SetDeviceOrigin( 0, -yOrigin * yScrollUnits );
+
+ dc.DrawText("Row 1", 5, 5);
+ dc.DrawText("Row 2", 5, 30);
+ dc.DrawText("Row 3", 5, 55);
+ dc.DrawText("Row 4", 5, 80);
+ dc.DrawText("Row 5", 5, 105);
+ dc.DrawText("Row 6", 5, 130);
+ }
+
+ wxScrolled<wxWindow> *m_owner;
+};
+
+class MySubCanvas : public wxPanel
+{
+public:
+ MySubCanvas(wxScrolled<wxWindow> *parent, wxWindow *cols, wxWindow *rows)
+ : wxPanel(parent, wxID_ANY)
+ {
+ m_owner = parent;
+ m_colLabels = cols;
+ m_rowLabels = rows;
+
+ (void)new wxButton(this, wxID_ANY, "Hallo I",
+ wxPoint(0,50), wxSize(100,25) );
+ (void)new wxButton(this, wxID_ANY, "Hallo II",
+ wxPoint(200,50), wxSize(100,25) );
+
+ (void)new wxTextCtrl(this, wxID_ANY, "Text I",
+ wxPoint(0,100), wxSize(100,25) );
+ (void)new wxTextCtrl(this, wxID_ANY, "Text II",
+ wxPoint(200,100), wxSize(100,25) );
+
+ (void)new wxComboBox(this, wxID_ANY, "ComboBox I",
+ wxPoint(0,150), wxSize(100,25));
+ (void)new wxComboBox(this, wxID_ANY, "ComboBox II",
+ wxPoint(200,150), wxSize(100,25));
+
+ SetBackgroundColour("WHEAT");
+
+ Connect(wxEVT_PAINT, wxPaintEventHandler(MySubCanvas::OnPaint));
+ }
+
+ // override the base class function so that when this window is scrolled,
+ // the labels are scrolled in sync
+ virtual void ScrollWindow(int dx, int dy, const wxRect *rect)
+ {
+ wxPanel::ScrollWindow( dx, dy, rect );
+ m_colLabels->ScrollWindow( dx, 0, rect );
+ m_rowLabels->ScrollWindow( 0, dy, rect );
+ }
+
+private:
+ void OnPaint(wxPaintEvent& WXUNUSED(event))
+ {
+ wxPaintDC dc( this );
+ m_owner->PrepareDC( dc );
+
+ dc.SetPen( *wxBLACK_PEN );
+
+ // OK, let's assume we are a grid control and we have two
+ // grid cells. Here in OnPaint we want to know which cell
+ // to redraw so that we prevent redrawing cells that don't
+ // need to get redrawn. We have one cell at (0,0) and one
+ // more at (200,0), both having a size of (100,25).
+
+ // We can query how much the window has been scrolled
+ // by calling CalcUnscrolledPosition()
+
+ int scroll_x = 0;
+ int scroll_y = 0;
+ m_owner->CalcUnscrolledPosition( scroll_x, scroll_y, &scroll_x, &scroll_y );
+
+ // We also need to know the size of the window to see which
+ // cells are completely hidden and not get redrawn
+
+ int size_x = 0;
+ int size_y = 0;
+ GetClientSize( &size_x, &size_y );
+
+ // First cell: (0,0)(100,25)
+ // It it on screen?
+ if ((0+100-scroll_x > 0) && (0+25-scroll_y > 0) &&
+ (0-scroll_x < size_x) && (0-scroll_y < size_y))
+ {
+ // Has the region on screen been exposed?
+ if (IsExposed(0,0,100,25))
+ {
+ dc.DrawRectangle( 0, 0, 100, 25 );
+ dc.DrawText("First Cell", 5, 5);
+ }
+ }
+
+
+ // Second cell: (0,200)(100,25)
+ // It it on screen?
+ if ((200+100-scroll_x > 0) && (0+25-scroll_y > 0) &&
+ (200-scroll_x < size_x) && (0-scroll_y < size_y))
+ {
+ // Has the region on screen been exposed?
+ if (IsExposed(200,0,100,25))
+ {
+ dc.DrawRectangle( 200, 0, 100, 25 );
+ dc.DrawText("Second Cell", 205, 5);
+ }
+ }
+ }
+
+ wxScrolled<wxWindow> *m_owner;
+ wxWindow *m_colLabels,
+ *m_rowLabels;
+};
+
+class MySubScrolledWindow : public wxScrolled<wxWindow>
+{
+public:
+ enum
+ {
+ CORNER_WIDTH = 60,
+ CORNER_HEIGHT = 25
+ };
+
+ MySubScrolledWindow(wxWindow *parent)
+ : wxScrolled<wxWindow>(parent, wxID_ANY)
+ {
+ // create the children
+ MySubColLabels *cols = new MySubColLabels(this);
+ MySubRowLabels *rows = new MySubRowLabels(this);
+
+ m_canvas = new MySubCanvas(this, cols, rows);
+
+ // lay them out
+ wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 2, 10, 10);
+ sizer->Add(CORNER_WIDTH, CORNER_HEIGHT); // just a spacer
+ sizer->Add(cols, wxSizerFlags().Expand());
+ sizer->Add(rows, wxSizerFlags().Expand());
+ sizer->Add(m_canvas, wxSizerFlags().Expand());
+ sizer->AddGrowableRow(1);
+ sizer->AddGrowableCol(1);
+ SetSizer(sizer);
+
+ // this is the key call: it means that only m_canvas will be scrolled
+ // and not this window itself
+ SetTargetWindow(m_canvas);
+
+ SetScrollbars(10, 10, 50, 50);
+
+ Connect(wxEVT_SIZE, wxSizeEventHandler(MySubScrolledWindow::OnSize));
+ }
+
+protected:
+ // scrolled windows which use scroll target different from the window
+ // itself must override this virtual method
+ virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size)
+ {
+ // decrease the total size by the size of the non-scrollable parts
+ // above/to the left of the canvas
+ wxSize sizeCanvas(size);
+ sizeCanvas.x -= 60;
+ sizeCanvas.y -= 25;
+ return sizeCanvas;
+ }
+
+private:
+ void OnSize(wxSizeEvent& WXUNUSED(event))
+ {
+ // We need to override OnSize so that our scrolled
+ // window a) does call Layout() to use sizers for
+ // positioning the controls but b) does not query
+ // the sizer for their size and use that for setting
+ // the scrollable area as set that ourselves by
+ // calling SetScrollbar() further down.
+
+ Layout();
+
+ AdjustScrollbars();
+ }
+
+ MySubCanvas *m_canvas;
+};
+
+class MySubFrame : public wxFrame
+{
+public:
+ MySubFrame(wxWindow *parent)
+ : wxFrame(parent, wxID_ANY, "MySubScrolledWindow")
+ {
+ new MySubScrolledWindow(this);
+
+ Show();
+ }
+};
+
+// ----------------------------------------------------------------------------
+// more simple examples of wxScrolled usage
+// ----------------------------------------------------------------------------
+
+// base class for both of them
+class MyScrolledWindowBase : public wxScrolled<wxWindow>
+{
+public:
+ MyScrolledWindowBase(wxWindow *parent)
+ : wxScrolled<wxWindow>(parent, wxID_ANY,
+ wxDefaultPosition, wxDefaultSize,
+ wxBORDER_SUNKEN)
+ {
+ m_nLines = 50;
+ m_winSync = NULL;
+ m_inDoSync = false;
+
+ wxClientDC dc(this);
+ dc.GetTextExtent("Line 17", NULL, &m_hLine);
+ }
+
+ // this scrolled window can be synchronized with another one: if this
+ // function is called with a non-NULL pointer, the given window will be
+ // scrolled to the same position as this one
+ void SyncWith(MyScrolledWindowBase *win)
+ {
+ m_winSync = win;
+
+ DoSyncIfNecessary();
+ }
+
+ virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL)
+ {
+ wxScrolled<wxWindow>::ScrollWindow(dx, dy, rect);
+
+ DoSyncIfNecessary();
+ }
+
+protected:
+ // the height of one line on screen
+ int m_hLine;
+
+ // the number of lines we draw
+ size_t m_nLines;
+
+private:
+ bool WasScrolledFirst() const { return m_inDoSync; }
+
+ void DoSyncIfNecessary()
+ {
+ if ( m_winSync && !m_winSync->WasScrolledFirst() )
+ {
+ m_inDoSync = true;
+
+ m_winSync->Scroll(GetViewStart());
+
+ m_inDoSync = false;
+ }
+ }
+
+ // the window to synchronize with this one or NULL
+ MyScrolledWindowBase *m_winSync;
+
+ // the flag preventing infinite recursion which would otherwise happen if
+ // one window synchronized the other one which in turn synchronized this
+ // one and so on
+ bool m_inDoSync;
+};
+
+// this class does "stupid" redrawing - it redraws everything each time
+// and sets the scrollbar extent directly.
+
+class MyScrolledWindowDumb : public MyScrolledWindowBase
+{
+public:
+ MyScrolledWindowDumb(wxWindow *parent) : MyScrolledWindowBase(parent)
+ {
+ // no horz scrolling
+ SetScrollbars(0, m_hLine, 0, m_nLines + 1, 0, 0, true /* no refresh */);
+ }
+
+ virtual void OnDraw(wxDC& dc);
+};
+
+// this class does "smart" redrawing - only redraws the lines which must be
+// redrawn and sets the scroll rate and virtual size to affect the
+// scrollbars.
+//
+// Note that this class should produce identical results to the one above.