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);
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:
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 <wx/mstream.h> 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 <code>wxVERSION_STRING</code> macro which returns a string with wxWidgets version. [sourcecode language="cpp"] 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:
Download the source code for this tutorial.
2 Comments
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)
T-Rex
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