diff --git a/docs/changes.txt b/docs/changes.txt index 78bdab73ce..5cc7000284 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -56,6 +56,7 @@ Unix: All (GUI): - Allow requesting modern (3.x+) OpenGL version in wxGLCanvas (Fabio Arnold). +- Allow customizing window shown by wxBusyInfo. - XRC handler for wxAuiToolBar added (Kinaou Hervé, David Hart). - Add wxFD_NO_FOLLOW style for wxFileDialog (Luca Bacci). - Add support for embedding bitmaps in generated SVG in wxSVGFileDC (iwbnwif). diff --git a/include/wx/busyinfo.h b/include/wx/busyinfo.h index 600c86c634..7de6f22fef 100644 --- a/include/wx/busyinfo.h +++ b/include/wx/busyinfo.h @@ -13,6 +13,59 @@ #if wxUSE_BUSYINFO +// This class is used to pass all the various parameters to wxBusyInfo ctor. +// According to the usual naming conventions (see wxAboutDialogInfo, +// wxFontInfo, ...) it would be called wxBusyInfoInfo, but this would have been +// rather strange, so we call it wxBusyInfoFlags instead. +// +// Methods are mostly self-explanatory except for the difference between "Text" +// and "Label": the former can contain markup, while the latter is just plain +// string which is not parsed in any way. +class wxBusyInfoFlags +{ +public: + wxBusyInfoFlags() + { + m_parent = NULL; + m_alpha = wxALPHA_OPAQUE; + } + + wxBusyInfoFlags& Parent(wxWindow* parent) + { m_parent = parent; return *this; } + + wxBusyInfoFlags& Icon(const wxIcon& icon) + { m_icon = icon; return *this; } + wxBusyInfoFlags& Title(const wxString& title) + { m_title = title; return *this; } + wxBusyInfoFlags& Text(const wxString& text) + { m_text = text; return *this; } + wxBusyInfoFlags& Label(const wxString& label) + { m_label = label; return *this; } + + wxBusyInfoFlags& Foreground(const wxColour& foreground) + { m_foreground = foreground; return *this; } + wxBusyInfoFlags& Background(const wxColour& background) + { m_background = background; return *this; } + + wxBusyInfoFlags& Transparency(wxByte alpha) + { m_alpha = alpha; return *this; } + +private: + wxWindow* m_parent; + + wxIcon m_icon; + wxString m_title, + m_text, + m_label; + + wxColour m_foreground, + m_background; + + wxByte m_alpha; + + friend class wxBusyInfo; +}; + #include "wx/generic/busyinfo.h" #endif // wxUSE_BUSYINFO diff --git a/include/wx/generic/busyinfo.h b/include/wx/generic/busyinfo.h index 7f56edb26d..d910668c86 100644 --- a/include/wx/generic/busyinfo.h +++ b/include/wx/generic/busyinfo.h @@ -27,11 +27,21 @@ class WXDLLIMPEXP_FWD_CORE wxWindow; class WXDLLIMPEXP_CORE wxBusyInfo : public wxObject { public: - wxBusyInfo(const wxString& message, wxWindow *parent = NULL); + wxBusyInfo(const wxBusyInfoFlags& flags) + { + Init(flags); + } + + wxBusyInfo(const wxString& message, wxWindow *parent = NULL) + { + Init(wxBusyInfoFlags().Parent(parent).Label(message)); + } virtual ~wxBusyInfo(); private: + void Init(const wxBusyInfoFlags& flags); + wxFrame *m_InfoFrame; wxDECLARE_NO_COPY_CLASS(wxBusyInfo); diff --git a/interface/wx/busyinfo.h b/interface/wx/busyinfo.h index 0953d49f9d..59cd50d5fc 100644 --- a/interface/wx/busyinfo.h +++ b/interface/wx/busyinfo.h @@ -9,8 +9,13 @@ @class wxBusyInfo This class makes it easy to tell your user that the program is temporarily busy. - Just create a wxBusyInfo object on the stack, and within the current scope, - a message window will be shown. + + Normally the main thread should always return to the main loop to continue + dispatching events as quickly as possible, hence this class shouldn't be + needed. However if the main thread does need to block, this class provides + a simple way to at least show this to the user: just create a wxBusyInfo + object on the stack, and within the current scope, a message window will be + shown. For example: @@ -26,6 +31,28 @@ It works by creating a window in the constructor, and deleting it in the destructor. + This window is rather plain by default but can be customized by passing + wxBusyInfo constructor an object of wxBusyInfoFlags class instead of a + simple message. Here is an example from the @ref page_samples_dialogs: + @code + wxBusyInfo info + ( + wxBusyInfoFlags() + .Parent(this) + .Icon(wxArtProvider::GetIcon(wxART_PRINT, + wxART_OTHER, wxSize(128, 128))) + .Title("Printing your document") + .Text("Please wait...") + .Foreground(*wxWHITE) + .Background(*wxBLACK) + .Transparency(4*wxALPHA_OPAQUE/5) + ); + @endcode + showing that separate title and text can be set, and that simple markup + (@ref wxControl::SetLabelMarkup()) can be used in them, and that it's also + possible to add an icon and customize the colours and transparency of the + window. + You may also want to call wxTheApp->Yield() to refresh the window periodically (in case it had been obscured by other windows, for example) like this: @@ -57,7 +84,25 @@ class wxBusyInfo { public: /** - Constructs a busy info window as child of @a parent and displays @e msg in it. + General constructor. + + This constructor allows to specify all supported attributes by calling + the appropriate methods on wxBusyInfoFlags object passed to it as + parameter. All of them are optional but usually at least the message + should be specified. + + @since 3.1.0 + */ + wxBusyInfo(const wxBusyInfoFlags& flags); + + /** + Simple constructor specifying only the message and the parent. + + This constructs a busy info window as child of @a parent and displays + @a msg in it. It is exactly equivalent to using + @code + wxBusyInfo(wxBusyInfoFlags().Parent(parent).Label(message)) + @endcode @note If @a parent is not @NULL you must ensure that it is not closed while the busy info is shown. @@ -70,3 +115,86 @@ public: virtual ~wxBusyInfo(); }; +/** + Parameters for wxBusyInfo. + + This class exists only in order to make passing attributes to wxBusyInfo + constructor easier and the code doing it more readable. + + All methods of this class return the reference to the object on which they + are called, making it possible to chain them together, e.g. typically you + would just create a temporary wxBusyInfoFlags object and then call the + methods corresponding to the attributes you want to set, before finally + passing the result to wxBusyInfo constructor, e.g.: + @code + wxBusyInfo info + ( + wxBusyInfoFlags() + .Parent(window) + .Icon(icon) + .Title("Some text") + .Text("Some more text") + .Foreground(wxColour(...)) + .Background(wxColour(...)) + ); + @endcode + + @since 3.1.0 + */ +class wxBusyInfoFlags +{ +public: + /** + Default constructor initializes all attributes to default values. + + Call the other methods to really fill in the object. + */ + wxBusyInfoFlags(); + + /// Sets the parent for wxBusyInfo. + wxBusyInfoFlags& Parent(wxWindow* parent); + + /// Sets the icon to show in wxBusyInfo. + wxBusyInfoFlags& Icon(const wxIcon& icon); + + /** + Sets the title, shown prominently in wxBusyInfo window. + + The @a title string may contain markup as described in + wxControl::SetLabelMarkup(). + */ + wxBusyInfoFlags& Title(const wxString& title); + + /** + Sets the more detailed text, shown under the title, if any. + + The @a text string may contain markup as described in + wxControl::SetLabelMarkup(). + */ + wxBusyInfoFlags& Text(const wxString& text); + + /** + Same as Text() but doesn't interpret the string as containing markup. + + This method should be used if the text shown in wxBusyInfo comes from + external source and so may contain characters having special meaning in + simple markup, e.g. '<'. + */ + wxBusyInfoFlags& Label(const wxString& label); + + /// Sets the foreground colour of the title and text strings. + wxBusyInfoFlags& Foreground(const wxColour& foreground); + + /// Sets the background colour of wxBusyInfo window. + wxBusyInfoFlags& Background(const wxColour& background); + + /** + Sets the transparency of wxBusyInfo window. + + @param alpha Value in wxALPHA_TRANSPARENT (0) to wxALPHA_OPAQUE (255) + range. + + @see wxTopLevelWindow::SetTransparent() + */ + wxBusyInfoFlags& Transparency(wxByte alpha); +}; diff --git a/samples/dialogs/dialogs.cpp b/samples/dialogs/dialogs.cpp index 7804fc425c..0022327b54 100644 --- a/samples/dialogs/dialogs.cpp +++ b/samples/dialogs/dialogs.cpp @@ -233,6 +233,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame) #if wxUSE_BUSYINFO EVT_MENU(DIALOGS_BUSYINFO, MyFrame::ShowBusyInfo) + EVT_MENU(DIALOGS_BUSYINFO_RICH, MyFrame::ShowRichBusyInfo) #endif // wxUSE_BUSYINFO #if wxUSE_FINDREPLDLG @@ -474,6 +475,7 @@ bool MyApp::OnInit() #if wxUSE_BUSYINFO info_menu->Append(DIALOGS_BUSYINFO, wxT("&Busy info dialog\tCtrl-B")); + info_menu->Append(DIALOGS_BUSYINFO_RICH, wxT("&Rich busy info dialog\tShift-Ctrl-B")); #endif // wxUSE_BUSYINFO #if wxUSE_LOG_DIALOG @@ -2422,7 +2424,28 @@ void MyFrame::ShowBusyInfo(wxCommandEvent& WXUNUSED(event)) } wxSleep(2); - //wxWakeUpIdle(); +} + +void MyFrame::ShowRichBusyInfo(wxCommandEvent& WXUNUSED(event)) +{ + wxWindowDisabler disableAll; + + // This is just an example and not an encouragement for printing + // synchronously from the main thread. + wxBusyInfo info + ( + wxBusyInfoFlags() + .Parent(this) + .Icon(wxArtProvider::GetIcon(wxART_PRINT, + wxART_OTHER, wxSize(128, 128))) + .Title("Printing your document") + .Text("Please wait...") + .Foreground(*wxWHITE) + .Background(*wxBLACK) + .Transparency(4*wxALPHA_OPAQUE/5) + ); + + wxSleep(5); } #endif // wxUSE_BUSYINFO diff --git a/samples/dialogs/dialogs.h b/samples/dialogs/dialogs.h index 71f022161d..06d6b46ca2 100644 --- a/samples/dialogs/dialogs.h +++ b/samples/dialogs/dialogs.h @@ -441,6 +441,7 @@ public: #if wxUSE_BUSYINFO void ShowBusyInfo(wxCommandEvent& event); + void ShowRichBusyInfo(wxCommandEvent& event); #endif // wxUSE_BUSYINFO #if wxUSE_FINDREPLDLG @@ -577,6 +578,7 @@ enum DIALOGS_ABOUTDLG_FULL, DIALOGS_ABOUTDLG_CUSTOM, DIALOGS_BUSYINFO, + DIALOGS_BUSYINFO_RICH, DIALOGS_FIND, DIALOGS_REPLACE, DIALOGS_REQUEST, diff --git a/src/generic/busyinfo.cpp b/src/generic/busyinfo.cpp index 3e9e728875..ec9798cd01 100644 --- a/src/generic/busyinfo.cpp +++ b/src/generic/busyinfo.cpp @@ -16,57 +16,106 @@ // for all others, include the necessary headers #ifndef WX_PRECOMP - #include "wx/frame.h" + #include "wx/m_InfoFrame.h" #include "wx/stattext.h" #include "wx/panel.h" + #include "wx/sizer.h" + #include "wx/statbmp.h" #include "wx/utils.h" #endif #include "wx/busyinfo.h" -#include "wx/generic/stattextg.h" -class WXDLLEXPORT wxInfoFrame : public wxFrame -{ -public: - wxInfoFrame(wxWindow *parent, const wxString& message); +// wxStaticText currently supports markup only in wxGTK and wxOSX/Cocoa, so use +// the generic version for markup support in the other ports. +#if wxUSE_MARKUP && !(defined(__WXGTK__) || defined(__WXOSX_COCOA__)) + #include "wx/generic/stattextg.h" -private: - wxDECLARE_NO_COPY_CLASS(wxInfoFrame); -}; - - -wxInfoFrame::wxInfoFrame(wxWindow *parent, const wxString& message) - : wxFrame(parent, wxID_ANY, wxString(), - wxDefaultPosition, wxDefaultSize, - wxSIMPLE_BORDER | wxFRAME_TOOL_WINDOW | wxSTAY_ON_TOP) -{ - wxPanel *panel = new wxPanel( this ); -#ifdef __WXGTK__ - wxGenericStaticText *text = new wxGenericStaticText(panel, wxID_ANY, message); + #define wxStaticTextWithMarkupSupport wxGenericStaticText #else - wxStaticText *text = new wxStaticText(panel, wxID_ANY, message); + #define wxStaticTextWithMarkupSupport wxStaticText #endif - panel->SetCursor(*wxHOURGLASS_CURSOR); - text->SetCursor(*wxHOURGLASS_CURSOR); - - // make the frame of at least the standard size (400*80) but big enough - // for the text we show - wxSize size = text->GetBestSize(); - size.IncBy(ConvertDialogToPixels(wxPoint(10, 10))); - size.IncTo(wxSize(400, 80)); - SetClientSize(size); - - // need to size the panel correctly first so that text->Centre() works - panel->SetSize(GetClientSize()); - - text->Centre(wxBOTH); - Centre(wxBOTH); -} - -wxBusyInfo::wxBusyInfo(const wxString& message, wxWindow *parent) +void wxBusyInfo::Init(const wxBusyInfoFlags& flags) { - m_InfoFrame = new wxInfoFrame(parent, message); + m_InfoFrame = new wxFrame(flags.m_parent, wxID_ANY, wxString(), + wxDefaultPosition, wxDefaultSize, + wxSIMPLE_BORDER | + wxFRAME_TOOL_WINDOW | + wxSTAY_ON_TOP); + + wxPanel* const panel = new wxPanel(m_InfoFrame); + + wxBoxSizer* const sizer = new wxBoxSizer(wxVERTICAL); + + const wxSizerFlags sizerFlags = wxSizerFlags().DoubleBorder().Centre(); + + if ( flags.m_icon.IsOk() ) + { + sizer->Add(new wxStaticBitmap(panel, wxID_ANY, flags.m_icon), sizerFlags); + } + + wxControl* title; + if ( !flags.m_title.empty() ) + { + title = new wxStaticTextWithMarkupSupport(panel, wxID_ANY, wxString()); + title->SetFont(title->GetFont().Scaled(2)); + title->SetLabelMarkup(flags.m_title); + + sizer->Add(title, sizerFlags); + } + else + { + title = NULL; + } + + // Vertically center the text in the window. + sizer->AddStretchSpacer(); + + wxControl* text; +#if wxUSE_MARKUP + if ( !flags.m_text.empty() ) + { + text = new wxStaticTextWithMarkupSupport(panel, wxID_ANY, wxString()); + text->SetLabelMarkup(flags.m_text); + } + else +#endif // wxUSE_MARKUP + { + text = new wxStaticText(panel, wxID_ANY, wxString()); + text->SetLabelText(flags.m_label); + } + + sizer->Add(text, sizerFlags); + + sizer->AddStretchSpacer(); + + panel->SetSizer(sizer); + + if ( flags.m_foreground.IsOk() ) + { + if ( title ) + title->SetForegroundColour(flags.m_foreground); + text->SetForegroundColour(flags.m_foreground); + } + + if ( flags.m_background.IsOk() ) + panel->SetBackgroundColour(flags.m_background); + + if ( flags.m_alpha != wxALPHA_OPAQUE ) + m_InfoFrame->SetTransparent(flags.m_alpha); + + m_InfoFrame->SetCursor(*wxHOURGLASS_CURSOR); + + // We need to accommodate our contents, but also impose some minimal size + // to make the busy info frame more noticeable. + wxSize size = panel->GetBestSize(); + size.IncTo(wxSize(400, 80)); + + m_InfoFrame->SetClientSize(size); + m_InfoFrame->Layout(); + + m_InfoFrame->Centre(wxBOTH); m_InfoFrame->Show(true); m_InfoFrame->Refresh(); m_InfoFrame->Update();