wxSplitterWindow

Introduction

The wxSplitterWindow (wxWidgets: wxSplitter Class Reference) class implements a window containing one or two subwindows. If two subwindows are displayed they can be displayed side-by-side or on top of each other.

Figure 1 shows an example of what can be done with the wxSplitterWindow class.

Figure 1: Example of a wxSplitterWindow split vertically

We will now look at two examples. The first one simply shows how to create a wxSplitterWindow window with two subwindows displayed side-by-side. The second one shows how to detect that the user has unsplit the window by handling the wxEVT_SPLITTER_UNSPLIT event.

Vertical Split Example

We will start with a simple example of a wxSplitterWindow window containing two subwindows displayed side-by-side. The example is a simple modification of the MinimalApp2 example we presented in the minimal application tutorial. The full source for this example is available from our GitHub repository: wxWidgetsTutorials/Windows/WxSplitterWindow1.

In the code below you can see that:

  1. We add a wxSplitterWindow to the frame.
  2. We then create two panels containing a wxTextCtrl control.
  3. We split the window vertically by calling the SplitVertically method and passing the two panels we created as arguments.
 File: Windows/WxSplitterWindow1/src/WxSplitterWindow1Frame.cpp
#include "WxSplitterWindow1Frame.h"
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/splitter.h>
#include <wx/textctrl.h>

WxSplitterWindow1Frame::WxSplitterWindow1Frame(const wxString& title)
    : wxFrame(NULL, wxID_ANY, title)
{
    // Create the wxSplitterWindow window
    // and set a minimum pane size to prevent unsplitting
    wxSplitterWindow* splitterWindow = new wxSplitterWindow(this, wxID_ANY);
    splitterWindow->SetMinimumPaneSize(50);

    // Create the left panel
    wxPanel* panel1 = new wxPanel(splitterWindow, wxID_ANY);
    wxTextCtrl* textCtrl1 = new wxTextCtrl(panel1, wxID_ANY, L"Panel 1 Text",
        wxDefaultPosition, wxSize(150, 150));
    wxBoxSizer* panel1Sizer = new wxBoxSizer(wxHORIZONTAL);
    panel1Sizer->Add(textCtrl1, 1, wxEXPAND);
    panel1->SetSizer(panel1Sizer);

    // Create the right panel
    wxPanel* panel2 = new wxPanel(splitterWindow, wxID_ANY);
    wxTextCtrl* textCtrl2 = new wxTextCtrl(panel2, wxID_ANY, L"Panel 2 Text",
        wxDefaultPosition, wxSize(150, 150));
    wxBoxSizer* panel2Sizer = new wxBoxSizer(wxHORIZONTAL);
    panel2Sizer->Add(textCtrl2, 1, wxEXPAND);
    panel2->SetSizer(panel2Sizer);

    // Split the window vertically and set the left and right panes
    splitterWindow->SplitVertically(panel1, panel2);

    // Set up the sizer for the frame and resize the frame
    // according to its contents
    wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL);
    topSizer->Add(splitterWindow, 1, wxEXPAND);
    SetSizerAndFit(topSizer);
}

Figure 2 shows what the application produced by the code above looks like. Because we have set a minimum pane size the window cannot be unsplit by moving the sash. If we still had wanted the user to be able to do so despite the minimum size we could have specified the wxSP_PERMIT_UNSPLIT style.

Figure 1: The WxSplitterWindow1 Application

The rest of the files don't have any significant changes compared to the MinimalApp2 code but are shown below for completeness.

 File: Windows/WxSplitterWindow1/src/WxSplitterWindow1Frame.h
#ifndef _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW1FRAME_H_
#define _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW1FRAME_H_

#include <wx/frame.h>

class WxSplitterWindow1Frame : public wxFrame
{
public:
    WxSplitterWindow1Frame(const wxString& title);
};

#endif
 File: Windows/WxSplitterWindow1/src/WxSplitterWindow1App.h
#ifndef _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW1APP_H_
#define _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW1APP_H_

#include <wx/app.h>

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

#endif
 File: Windows/WxSplitterWindow1/src/WxSplitterWindow1App.cpp
#include "WxSplitterWindow1App.h"
#include "WxSplitterWindow1Frame.h"

wxIMPLEMENT_APP(WxSplitterWindow1App);

bool WxSplitterWindow1App::OnInit()
{
    WxSplitterWindow1Frame* frame = new WxSplitterWindow1Frame("WxSplitterWindow1");
    frame->Show(true);
    return true;
}

Splitting and Unsplitting

In this example we will improve the previous example by letting the user split or unsplit the window. A checked menu item will let the user split or unsplit the window and reflect its current state. The full source for this example is available from our GitHub repository: wxWidgetsTutorials/Windows/WxSplitterWindow2.

The new header file is shown below.

  1. The menu, splitter window and panes need to be accessible from the event handlers so they are members of the WxSplitterWindow2Frame class.
  2. The OnMenuSplit event handler will be called when the user activates the menu item.
  3. The OnUnsplit event handler will be called when the splitter window becomes unsplit.
 File: Windows/WxSplitterWindow2/src/WxSplitterWindow2Frame.h
#ifndef _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW2FRAME_H_
#define _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW2FRAME_H_

#include <wx/frame.h>
#include <wx/splitter.h>
#include <wx/menu.h>
#include <wx/panel.h>

class WxSplitterWindow2Frame : public wxFrame
{
public:
    WxSplitterWindow2Frame(const wxString& title);

private:
    void OnMenuSplit(wxCommandEvent& evt);
    void OnUnsplit(wxSplitterEvent& evt);

private:
    wxMenu* m_viewMenu;
    wxSplitterWindow* m_splitterWindow;
    wxPanel* m_panel1;
    wxPanel* m_panel2;

    wxDECLARE_EVENT_TABLE();
};

#endif

The new source file is shown below.

  1. The SetMinimumPaneSize call has been removed so that the sash can be used to unsplit the window.
  2. A menu with a checked menu item has now been added. The checked menu item is checked when the window is split and unchecked when the window is unsplit.
  3. The OnUnsplit event handler updates the checked menu item when the user unsplits the window using the sash and not the menu. Note that the handler will also be called when the window is unsplit using the menu. This is unnecessary as the checked menu item will be updated when the user activates it but as it is harmless there is no need to complicate the code by trying to distinguish between the cases.
  4. The OnMenuSplit event handler gets called when the checked menu item is activated. It uses the Unsplit method to unsplit the window.
 File: Windows/WxSplitterWindow2/src/WxSplitterWindow2Frame.cpp
#include "WxSplitterWindow2Frame.h"
#include "WindowIDs.h"
#include <wx/sizer.h>
#include <wx/textctrl.h>

WxSplitterWindow2Frame::WxSplitterWindow2Frame(const wxString& title)
    : wxFrame(NULL, wxID_ANY, title), m_viewMenu(0), m_splitterWindow(0),
    m_panel1(0), m_panel2(0)
{
    // Create a menu with a checkable item to
    // toggle between the split and unsplit states
    wxMenuBar* menuBar = new wxMenuBar;
    m_viewMenu = new wxMenu;
    m_viewMenu->AppendCheckItem(SplitMenuID, "Split");
    m_viewMenu->Check(SplitMenuID, true);
    menuBar->Append(m_viewMenu, "&View");
    SetMenuBar(menuBar);

    // Create the wxSplitterWindow window
    m_splitterWindow = new wxSplitterWindow(this, SplitterWindowID);

    // Create the left panel
    m_panel1 = new wxPanel(m_splitterWindow, wxID_ANY);
    wxTextCtrl* textCtrl1 = new wxTextCtrl(m_panel1, wxID_ANY, L"Panel 1 Text",
        wxDefaultPosition, wxSize(150, 150));
    wxBoxSizer* panel1Sizer = new wxBoxSizer(wxHORIZONTAL);
    panel1Sizer->Add(textCtrl1, 1, wxEXPAND);
    m_panel1->SetSizer(panel1Sizer);

    // Create the right panel
    m_panel2 = new wxPanel(m_splitterWindow, wxID_ANY);
    wxTextCtrl* textCtrl2 = new wxTextCtrl(m_panel2, wxID_ANY, L"Panel 2 Text",
        wxDefaultPosition, wxSize(150, 150));
    wxBoxSizer* panel2Sizer = new wxBoxSizer(wxHORIZONTAL);
    panel2Sizer->Add(textCtrl2, 1, wxEXPAND);
    m_panel2->SetSizer(panel2Sizer);

    // Split the window vertically and set the left and right panes
    m_splitterWindow->SplitVertically(m_panel1, m_panel2, 150);

    // Set up the sizer for the frame and resize the frame
    // according to its contents
    wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL);
    topSizer->Add(m_splitterWindow, 1, wxEXPAND);
    SetSizerAndFit(topSizer);
}

// Split or unsplit the window when the "Split" menu item
// is activated
void WxSplitterWindow2Frame::OnMenuSplit(wxCommandEvent& evt)
{
    if (!m_splitterWindow || !m_panel1 || !m_panel2)
    {
        return;
    }

    if (evt.IsChecked())
    {
        m_splitterWindow->SplitVertically(m_panel1, m_panel2, 150);
    }
    else
    {
        m_splitterWindow->Unsplit();
    }
}

// When the window becomes unsplit this event handler
// ensures the checkable menu item gets unchecked
void WxSplitterWindow2Frame::OnUnsplit(wxSplitterEvent& evt)
{
    if (m_viewMenu)
    {
        m_viewMenu->Check(SplitMenuID, false);
    }

    evt.Skip();
}

// Add the event handler to the event table. As you can see we use
// the window ID to link the event handler to the wxSplitterWindow we created.
wxBEGIN_EVENT_TABLE(WxSplitterWindow2Frame, wxFrame)
    EVT_MENU(SplitMenuID, WxSplitterWindow2Frame::OnMenuSplit)
    EVT_SPLITTER_UNSPLIT(SplitterWindowID, WxSplitterWindow2Frame::OnUnsplit)
wxEND_EVENT_TABLE()

The following video shows the application in action. First the window is unsplit and then split again using the menu. Then the sash is used to unsplit the window and the menu is used to split it again. Throughout this the state of the checked menu item remains in sync with the actual window state.

For completeness here is the rest of the source files but they do not contain anything out of the ordinary.

 File: Windows/WxSplitterWindow2/src/WindowIDs.h
#ifndef _TUTORIALS_WXWIDGETS_WINDOWIDS_H_
#define _TUTORIALS_WXWIDGETS_WINDOWIDS_H_

#include <wx/defs.h>

const wxWindowID SplitterWindowID = wxID_HIGHEST + 1;

const int SplitMenuID = wxID_HIGHEST + 2;

#endif
 File: Windows/WxSplitterWindow2/src/WxSplitterWindow2App.h
#ifndef _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW2APP_H_
#define _TUTORIALS_WXWIDGETS_WXSPLITTERWINDOW2APP_H_

#include <wx/app.h>

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

#endif
 File: Windows/WxSplitterWindow2/src/WxSplitterWindow2App.cpp
#include "WxSplitterWindow2App.h"
#include "WxSplitterWindow2Frame.h"

wxIMPLEMENT_APP(WxSplitterWindow2App);

bool WxSplitterWindow2App::OnInit()
{
    WxSplitterWindow2Frame* frame = new WxSplitterWindow2Frame("WxSplitterWindow2");
    frame->Show(true);
    return true;
}

Further Reading

  1. wxWiki: WxSplitterWindow