How to Create Nice About Box in wxWidgets

After taking a look at wxWidgets samples I noticed that all of them have simple message box instaed of normal about box. However in real applications About dialog is important enough part of GUI.

So, in this post I’m going to tell a bit about creating About boxes for your software.

wxWidgets has builf-in API for creating “standard” dialog boxes. wxAboutBox() function is used for displaying About box and wxAboutDialogInfo object, which contains all necessary information, should be passed to wxAboutBox() function.

wxAboutDialogInfo can contain such information as:

  • Application name
  • Application icon
  • Application description
  • Application version
  • List of developers
  • List of artists
  • List of documentation writers
  • List of translators
  • Copyright message
  • Web-site URL
  • Application license

Only wxGTK port supports native way of displaying application license. On other platforms generic implementation will be used and it is better to create a separate dialog for this purpose.

Well, here is a small example of using API described above:

#include
...
// This object will contain all information displayed in About box
wxAboutDialogInfo info;
// Call of AddDeveloper() method adds a record to list of developers
info.AddDeveloper(_("John Doe"));
// Call of AddDocWriter() method adds a record to list of documentation writers
info.AddDocWriter(_("Donald Duck"));
// Call of AddArtist() method adds a record to list of artists
info.AddArtist(_("Scrooge Mc.Duck"));
// Call of AddTranslator() method adds a record to list of translators
info.AddTranslator(_("Mickey Mouse"));
// This method adds application description.
info.SetDescription(_("Sample wxWidgets application for testing wxAboutBox() function."));
// This method sets application version string
info.SetVersion(wxT("1.0.0 beta 1"));
// SetName() method sets application name displayed in About box.
// It is better to pass wxApp::GetAppName() to this method
info.SetName(wxTheApp->GetAppName());
// Sets application Web-site URL
info.SetWebSite(wxT("https://wxwidgets.info"));
// Sets the icon which will be displayed in About box.
info.SetIcon(wxICON(wxICON_AAA));
// Sets application license string. Only wxGTK port has native way of
// displaying application license. All other ports will use generic way for this purpose.
info.SetLicence(_("Public Domain"));

// At last, we can display about box
wxAboutBox(info);

wxAboutBox and wxAboutDialogLinfo usage sample - Collapsed
As you can see, there are several collapsible panels placed to dialog. The amount of these panels changes according theinformation specified in wxAboutDialogInfo object.
After expanding collapsible panels we’ll get something like this:
wxAboutBox Sample - Expanded
Looks nice but a bit boring. So I decided to create more fancy About box. In fact, I was inspired by this post at wxForum but used my own way and also made some changes.

wxMozillaLikeAboutBoxDialog.h

#ifndef _WXMOZILLALIKEABOUTBOXDIALOG_H_
#define _WXMOZILLALIKEABOUTBOXDIALOG_H_

#include "wx/gbsizer.h"
#include "wx/statline.h"

#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_STYLE wxCAPTION|wxSYSTEM_MENU|wxCLOSE_BOX|wxTAB_TRAVERSAL
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_TITLE _("About ")
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_IDNAME ID_WXMOZILLALIKEABOUTBOXDIALOG
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_SIZE wxSize(400, 300)
#define SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_POSITION wxDefaultPosition

class wxMozillaLikeAboutBoxDialog: public wxDialog
{    
    DECLARE_DYNAMIC_CLASS( wxMozillaLikeAboutBoxDialog )
    DECLARE_EVENT_TABLE()
public:
    wxMozillaLikeAboutBoxDialog();
    wxMozillaLikeAboutBoxDialog( wxWindow* parent, 
		wxWindowID id = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_IDNAME, 
		const wxString& caption = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_TITLE, 
		const wxPoint& pos = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_POSITION, 
		const wxSize& size = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_SIZE, 
		long style = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_STYLE );

    bool Create( wxWindow* parent, 
		wxWindowID id = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_IDNAME, 
		const wxString& caption = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_TITLE, 
		const wxPoint& pos = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_POSITION, 
		const wxSize& size = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_SIZE, 
		long style = SYMBOL_WXMOZILLALIKEABOUTBOXDIALOG_STYLE );

    ~wxMozillaLikeAboutBoxDialog();

    void Init();
    void CreateControls();

    wxString GetAppName() const { return m_AppName ; }
    void SetAppName(wxString value) { m_AppName = value ; }

    wxString GetVersion() const { return m_Version ; }
    void SetVersion(wxString value) { m_Version = value ; }

    wxString GetCopyright() const { return m_Copyright ; }
    void SetCopyright(wxString value) { m_Copyright = value ; }

    wxString GetCustomBuildInfo() const { return m_CustomBuildInfo ; }
    void SetCustomBuildInfo(wxString value) { m_CustomBuildInfo = value ; }

    wxBitmap GetBitmapResource( const wxString& name );
    wxIcon GetIconResource( const wxString& name );

    enum wxBuildInfoFormat 
    {
        wxBUILDINFO_SHORT,
        wxBUILDINFO_LONG
    };

    static wxString GetBuildInfo(wxBuildInfoFormat format);
    
    void SetHeaderBitmap(const wxBitmap & value);
    void ApplyInfo();

private:
    wxPanel* m_ContentPanel;
    wxStaticBitmap* m_HeaderStaticBitmap;
    wxStaticText* m_AppNameStaticText;
    wxStaticText* m_CopyrightStaticText;
    wxStaticText* m_VersionStaticText;
    wxStaticText* m_BuildInfoStaticText;
    wxString m_AppName;
    wxString m_Version;
    wxString m_Copyright;
    wxString m_CustomBuildInfo;
    /// Control identifiers
    enum {
        ID_WXMOZILLALIKEABOUTBOXDIALOG = 10000,
        ID_ContentPanel = 10001
    };
};

wxBitmap wxGetBitmapFromMemory(const void * data, size_t length);

#endif

wxMozillaLikeAboutBoxDialog.cpp

#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wxMozillaLikeAboutBoxDialog.h"
#include 

IMPLEMENT_DYNAMIC_CLASS( wxMozillaLikeAboutBoxDialog, wxDialog )

BEGIN_EVENT_TABLE( wxMozillaLikeAboutBoxDialog, wxDialog )
END_EVENT_TABLE()

wxMozillaLikeAboutBoxDialog::wxMozillaLikeAboutBoxDialog()
{
    Init();
}

wxMozillaLikeAboutBoxDialog::wxMozillaLikeAboutBoxDialog( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
    Init();
    Create(parent, id, caption, pos, size, style);
}

bool wxMozillaLikeAboutBoxDialog::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
    SetExtraStyle(wxWS_EX_BLOCK_EVENTS);
    wxDialog::Create( parent, id, caption, pos, size, style );
    CreateControls();
    if (GetSizer())
    {
        GetSizer()->SetSizeHints(this);
    }
    Centre();
    return true;
}

wxMozillaLikeAboutBoxDialog::~wxMozillaLikeAboutBoxDialog()
{
}

void wxMozillaLikeAboutBoxDialog::Init()
{
    m_ContentPanel = NULL;
    m_HeaderStaticBitmap = NULL;
    m_AppNameStaticText = NULL;
    m_CopyrightStaticText = NULL;
    m_VersionStaticText = NULL;
    m_BuildInfoStaticText = NULL;
}

void wxMozillaLikeAboutBoxDialog::CreateControls()
{    
    wxMozillaLikeAboutBoxDialog* itemDialog1 = this;
    wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
    itemDialog1->SetSizer(itemBoxSizer2);
    m_ContentPanel = new wxPanel( itemDialog1, ID_ContentPanel, wxDefaultPosition, wxSize(200, 300), wxNO_BORDER|wxTAB_TRAVERSAL );
    m_ContentPanel->SetBackgroundColour(wxColour(255, 255, 255));
    itemBoxSizer2->Add(m_ContentPanel, 0, wxGROW, 0);
    wxBoxSizer* itemBoxSizer4 = new wxBoxSizer(wxVERTICAL);
    m_ContentPanel->SetSizer(itemBoxSizer4);
    m_HeaderStaticBitmap = new wxStaticBitmap( m_ContentPanel, wxID_STATIC, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
    itemBoxSizer4->Add(m_HeaderStaticBitmap, 0, wxGROW, 0);
    wxGridBagSizer* itemGridBagSizer6 = new wxGridBagSizer(0, 0);
    itemGridBagSizer6->AddGrowableRow(2);
    itemGridBagSizer6->AddGrowableRow(3);
    itemGridBagSizer6->SetEmptyCellSize(wxSize(10, 20));
    itemBoxSizer4->Add(itemGridBagSizer6, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 10);
    m_AppNameStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(""), wxDefaultPosition, wxDefaultSize, 0 );
    m_AppNameStaticText->SetForegroundColour(wxColour(255, 0, 0));
    m_AppNameStaticText->SetFont(wxFont(28, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Arial Narrow")));
    itemGridBagSizer6->Add(m_AppNameStaticText, wxGBPosition(0, 0), wxGBSpan(1, 2), wxALIGN_LEFT|wxALIGN_BOTTOM|wxLEFT|wxRIGHT|wxTOP, 5);
    wxStaticText* itemStaticText8 = new wxStaticText( m_ContentPanel, wxID_STATIC, _("version"), wxDefaultPosition, wxDefaultSize, 0 );
    itemStaticText8->SetForegroundColour(wxColour(192, 192, 192));
    itemStaticText8->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT("Arial")));
    itemGridBagSizer6->Add(itemStaticText8, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_RIGHT|wxALIGN_TOP|wxLEFT|wxBOTTOM, 5);
    m_CopyrightStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(""), wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE );
    itemGridBagSizer6->Add(m_CopyrightStaticText, wxGBPosition(2, 0), wxGBSpan(1, 2), wxGROW|wxGROW|wxALL, 5);
    m_VersionStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(""), wxDefaultPosition, wxDefaultSize, 0 );
    m_VersionStaticText->SetForegroundColour(wxColour(192, 192, 192));
    m_VersionStaticText->SetFont(wxFont(8, wxSWISS, wxNORMAL, wxBOLD, false, wxT("Arial")));
    itemGridBagSizer6->Add(m_VersionStaticText, wxGBPosition(1, 1), wxGBSpan(1, 1), wxALIGN_LEFT|wxALIGN_TOP|wxLEFT|wxRIGHT|wxBOTTOM, 5);
    m_BuildInfoStaticText = new wxStaticText( m_ContentPanel, wxID_STATIC, _T(""), wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE );
    itemGridBagSizer6->Add(m_BuildInfoStaticText, wxGBPosition(3, 0), wxGBSpan(1, 2), wxGROW|wxGROW|wxALL, 5);
    itemBoxSizer4->Add(5, 5, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);
    wxStaticLine* itemStaticLine13 = new wxStaticLine( itemDialog1, wxID_STATIC, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
    itemBoxSizer2->Add(itemStaticLine13, 0, wxGROW, 0);
    wxStdDialogButtonSizer* itemStdDialogButtonSizer14 = new wxStdDialogButtonSizer;
    itemBoxSizer2->Add(itemStdDialogButtonSizer14, 0, wxALIGN_RIGHT|wxALL, 5);
    wxButton* itemButton15 = new wxButton( itemDialog1, wxID_OK, _("&OK"), wxDefaultPosition, wxDefaultSize, 0 );
    itemStdDialogButtonSizer14->AddButton(itemButton15);
    itemStdDialogButtonSizer14->Realize();
    m_BuildInfoStaticText->SetLabel(wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxBUILDINFO_LONG));
}

And here is a method which returns a string with build information

wxString wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxBuildInfoFormat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == wxBUILDINFO_LONG)
    {
#if defined(__WXMSW__)
        wxbuild < < _T("-Windows");
#elif defined(__WXMAC__)
        wxbuild << _T("-Mac");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode");
#else
        wxbuild << _T("-ANSI");
#endif // wxUSE_UNICODE
    }
	wxbuild << _(" build");
    return wxbuild;
}
[/sourcecode]
This method utilizes wxVERSION_STRING macro which returns a string with wxWidgets version.

wxBitmap wxGetBitmapFromMemory(const void * data, size_t length)
{
	wxMemoryInputStream stream(data, length);
	return wxBitmap(stream, wxBITMAP_TYPE_ANY);
}

void wxMozillaLikeAboutBoxDialog::SetHeaderBitmap(const wxBitmap & value)
{
    m_HeaderStaticBitmap->SetBitmap(value);
}

wxGetBitmapFromMemory() method creates wxBitmap from memory buffer using wxMemoryInputStream.

void wxMozillaLikeAboutBoxDialog::ApplyInfo()
{
	wxASSERT_MSG(m_HeaderStaticBitmap->GetBitmap().IsOk(), _("Header bitmap for About box is empty"));
	SetTitle(wxString::Format(wxT("%s %s"), _("About"), m_AppName.GetData()));
    m_AppNameStaticText->SetLabel(m_AppName);
    m_VersionStaticText->SetLabel(m_Version);
    m_CopyrightStaticText->SetLabel(m_Copyright);
	wxString buildInfo;
	if(m_CustomBuildInfo.IsEmpty())
	{
		buildInfo = wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxBUILDINFO_LONG);
	}
	else
	{
		buildInfo = m_CustomBuildInfo;
	}
	m_BuildInfoStaticText->SetLabel(buildInfo);
	int labelWidth = m_HeaderStaticBitmap->GetSize().GetWidth() - 20;
	m_VersionStaticText->Wrap(labelWidth);
	m_CopyrightStaticText->Wrap(labelWidth);
	m_BuildInfoStaticText->Wrap(labelWidth);
	m_ContentPanel->Layout();
	m_ContentPanel->GetSizer()->Fit(m_ContentPanel);
    GetSizer()->Fit(this);
	Centre();
}

ApplyInfo method sets labels of all wxStaticText controls, applies header image and then fixes the layout according to changes.

Here is an example of usage of our Mozilla-like About box:

wxAboutBoxTestMainFrame.cpp

void wxAboutBoxTestMainFrame::OnMOZILLALIKEABOUTBOXClick( wxCommandEvent& event )
{
	// Create About box 
	wxMozillaLikeAboutBoxDialog * dlg = new wxMozillaLikeAboutBoxDialog(this);
	// Set application name
	dlg->SetAppName(wxTheApp->GetAppName());
	// Set application version
	dlg->SetVersion(wxT("1.0.0 b1"));
	// Set copyright message
	dlg->SetCopyright(wxString::Format(wxT("%c %i %s"),
		(wxChar) 0x00A9, wxDateTime::Now().GetYear(),
		_("Volodymir (T-Rex) Tryapichko. All rights reserved. Please contact author if you have any copyright-related questions.")));
	// Set build info message. This is optional step. If you don't specify build info message then
	// default one will be used
	dlg->SetCustomBuildInfo(wxString::Format(wxT("%s. %s"),
		wxMozillaLikeAboutBoxDialog::GetBuildInfo(wxMozillaLikeAboutBoxDialog::wxBUILDINFO_LONG).GetData(),
		_("Compiled by T-Rex personally :)")));
	// Set header bitmap
	dlg->SetHeaderBitmap(wxGetBitmapFromMemory(header_png, sizeof(header_png)));
	// Apply changes
	dlg->ApplyInfo();
	// Show dialog
	dlg->ShowModal();
	// Destroy dialog
	dlg->Destroy();
}

header_png is a char array created from PNG image using Bin2C utility:

unsigned char header_png[] ={
	0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D,0x49,0x48,
	...
};

And here is a screenshot of what we get after compiling the source code listed above:
Mozilla-like About Box in wxWidgets
Download the source code for this tutorial.

2 comments

  1. Gerich   •  

    If you get this link error
    wxAboutBoxTestApp.obj : error LNK2001: unresolved external symbol “__declspec(dllimport) public: void __thiscall wxStringData::Free(void)” (__imp_?Free@wxStringData@@QAEXXZ)””

    just go to Project Setting\C++\Code Generation\ and change Runtime Library to Multi-threaded Debug DLL (/MDd)

  2. T-Rex   •     Author

    C/C++ -> Runtime Library property depends only on way how you compiled wxWidgets. I’m using static build which uses CRT statically.
    Anyway the simplest way is double check that your project settings correspond settings of wxWidgets projects

Leave a Reply

Your email address will not be published. Required fields are marked *

Please leave these two fields as-is: