From d5b98eb928bfd29060cd5974d5346b1bfac34012 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 15 Jun 2009 20:18:10 +0000 Subject: [PATCH] implement support for per-state bitmaps in wxMSW wxButton git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61067 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- samples/widgets/button.cpp | 82 +++++++++++++--------- src/msw/button.cpp | 139 ++++++++++++++++++++----------------- 2 files changed, 126 insertions(+), 95 deletions(-) diff --git a/samples/widgets/button.cpp b/samples/widgets/button.cpp index c01bc1cd27..e6fb8662fb 100644 --- a/samples/widgets/button.cpp +++ b/samples/widgets/button.cpp @@ -121,18 +121,18 @@ protected: // ------------ // the check/radio boxes for styles - wxCheckBox *m_chkBitmap, - *m_chkImage, + wxCheckBox *m_chkBitmapOnly, + *m_chkTextAndBitmap, *m_chkFit, *m_chkDefault; // more checkboxes for wxBitmapButton only - wxCheckBox *m_chkUseSelected, + wxCheckBox *m_chkUsePressed, *m_chkUseFocused, - *m_chkUseHover, + *m_chkUseCurrent, *m_chkUseDisabled; - // and an image position choice used if m_chkImage is on + // and an image position choice used if m_chkTextAndBitmap is on wxRadioBox *m_radioImagePos; wxRadioBox *m_radioHAlign, @@ -181,13 +181,13 @@ ButtonWidgetsPage::ButtonWidgetsPage(WidgetsBookCtrl *book, : WidgetsPage(book, imaglist, button_xpm) { // init everything - m_chkBitmap = - m_chkImage = + m_chkBitmapOnly = + m_chkTextAndBitmap = m_chkFit = m_chkDefault = - m_chkUseSelected = + m_chkUsePressed = m_chkUseFocused = - m_chkUseHover = + m_chkUseCurrent = m_chkUseDisabled = (wxCheckBox *)NULL; m_radioImagePos = @@ -209,19 +209,24 @@ void ButtonWidgetsPage::CreateContent() wxSizer *sizerLeft = new wxStaticBoxSizer(box, wxVERTICAL); - m_chkBitmap = CreateCheckBoxAndAddToSizer(sizerLeft, _T("&Bitmap button")); - m_chkImage = CreateCheckBoxAndAddToSizer(sizerLeft, _T("With &image")); + m_chkBitmapOnly = CreateCheckBoxAndAddToSizer(sizerLeft, "&Bitmap only"); + m_chkTextAndBitmap = CreateCheckBoxAndAddToSizer(sizerLeft, "Text &and &bitmap"); m_chkFit = CreateCheckBoxAndAddToSizer(sizerLeft, _T("&Fit exactly")); m_chkDefault = CreateCheckBoxAndAddToSizer(sizerLeft, _T("&Default")); sizerLeft->AddSpacer(5); wxSizer *sizerUseLabels = - new wxStaticBoxSizer(wxVERTICAL, this, _T("&Use the following bitmaps?")); - m_chkUseSelected = CreateCheckBoxAndAddToSizer(sizerUseLabels, _T("&Pushed")); - m_chkUseFocused = CreateCheckBoxAndAddToSizer(sizerUseLabels, _T("&Focused")); - m_chkUseHover = CreateCheckBoxAndAddToSizer(sizerUseLabels, _T("&Hover")); - m_chkUseDisabled = CreateCheckBoxAndAddToSizer(sizerUseLabels, _T("&Disabled")); + new wxStaticBoxSizer(wxVERTICAL, this, + "&Use the following bitmaps in addition to the normal one?"); + m_chkUsePressed = CreateCheckBoxAndAddToSizer(sizerUseLabels, + "&Pressed (small help icon)"); + m_chkUseFocused = CreateCheckBoxAndAddToSizer(sizerUseLabels, + "&Focused (small error icon)"); + m_chkUseCurrent = CreateCheckBoxAndAddToSizer(sizerUseLabels, + "&Current (small warning icon)"); + m_chkUseDisabled = CreateCheckBoxAndAddToSizer(sizerUseLabels, + "&Disabled (broken image icon)"); sizerLeft->Add(sizerUseLabels, wxSizerFlags().Expand().Border()); sizerLeft->AddSpacer(10); @@ -300,14 +305,14 @@ void ButtonWidgetsPage::CreateContent() void ButtonWidgetsPage::Reset() { - m_chkBitmap->SetValue(false); + m_chkBitmapOnly->SetValue(false); m_chkFit->SetValue(true); - m_chkImage->SetValue(false); + m_chkTextAndBitmap->SetValue(false); m_chkDefault->SetValue(false); - m_chkUseSelected->SetValue(true); + m_chkUsePressed->SetValue(true); m_chkUseFocused->SetValue(true); - m_chkUseHover->SetValue(true); + m_chkUseCurrent->SetValue(true); m_chkUseDisabled->SetValue(true); m_radioImagePos->SetSelection(ButtonImagePos_Left); @@ -376,17 +381,19 @@ void ButtonWidgetsPage::CreateButton() break; } - const bool isBitmapButton = m_chkBitmap->GetValue(); - if ( isBitmapButton ) + bool showsBitmap = false; + if ( m_chkBitmapOnly->GetValue() ) { + showsBitmap = true; + wxBitmapButton *bbtn = new wxBitmapButton(this, ButtonPage_Button, CreateBitmap(_T("normal"))); - if ( m_chkUseSelected->GetValue() ) - bbtn->SetBitmapSelected(CreateBitmap(_T("pushed"))); + if ( m_chkUsePressed->GetValue() ) + bbtn->SetBitmapPressed(CreateBitmap(_T("pushed"))); if ( m_chkUseFocused->GetValue() ) bbtn->SetBitmapFocus(CreateBitmap(_T("focused"))); - if ( m_chkUseHover->GetValue() ) - bbtn->SetBitmapHover(CreateBitmap(_T("hover"))); + if ( m_chkUseCurrent->GetValue() ) + bbtn->SetBitmapCurrent(CreateBitmap(_T("hover"))); if ( m_chkUseDisabled->GetValue() ) bbtn->SetBitmapDisabled(CreateBitmap(_T("disabled"))); m_button = bbtn; @@ -398,13 +405,10 @@ void ButtonWidgetsPage::CreateButton() flags); } - m_chkUseSelected->Enable(isBitmapButton); - m_chkUseFocused->Enable(isBitmapButton); - m_chkUseHover->Enable(isBitmapButton); - m_chkUseDisabled->Enable(isBitmapButton); - - if ( m_chkImage->GetValue() ) + if ( !showsBitmap && m_chkTextAndBitmap->GetValue() ) { + showsBitmap = true; + static const wxDirection positions[] = { wxLEFT, wxRIGHT, wxTOP, wxBOTTOM @@ -412,8 +416,22 @@ void ButtonWidgetsPage::CreateButton() m_button->SetBitmap(wxArtProvider::GetIcon(wxART_INFORMATION), positions[m_radioImagePos->GetSelection()]); + + if ( m_chkUsePressed->GetValue() ) + m_button->SetBitmapPressed(wxArtProvider::GetIcon(wxART_HELP)); + if ( m_chkUseFocused->GetValue() ) + m_button->SetBitmapFocus(wxArtProvider::GetIcon(wxART_ERROR)); + if ( m_chkUseCurrent->GetValue() ) + m_button->SetBitmapCurrent(wxArtProvider::GetIcon(wxART_WARNING)); + if ( m_chkUseDisabled->GetValue() ) + m_button->SetBitmapDisabled(wxArtProvider::GetIcon(wxART_MISSING_IMAGE)); } + m_chkUsePressed->Enable(showsBitmap); + m_chkUseFocused->Enable(showsBitmap); + m_chkUseCurrent->Enable(showsBitmap); + m_chkUseDisabled->Enable(showsBitmap); + if ( m_chkDefault->GetValue() ) { m_button->SetDefault(); diff --git a/src/msw/button.cpp b/src/msw/button.cpp index 2b0243b249..b17908b014 100644 --- a/src/msw/button.cpp +++ b/src/msw/button.cpp @@ -137,8 +137,10 @@ const int OD_BUTTON_MARGIN = 4; class wxODButtonImageData : public wxButtonImageData { public: - wxODButtonImageData(wxButton *btn) + wxODButtonImageData(wxButton *btn, const wxBitmap& bitmap) { + SetBitmap(bitmap, wxButton::State_Normal); + m_dir = wxLEFT; m_margin.x = btn->GetCharWidth(); @@ -192,10 +194,17 @@ class wxXPButtonImageData : public wxButtonImageData public: // we must be constructed with the size of our images as we need to create // the image list - wxXPButtonImageData(wxButton *btn, const wxSize& size) - : m_iml(size.x, size.y, true /* use mask */, wxButton::State_Max), + wxXPButtonImageData(wxButton *btn, const wxBitmap& bitmap) + : m_iml(bitmap.GetWidth(), bitmap.GetHeight(), true /* use mask */, + wxButton::State_Max), m_hwndBtn(GetHwndOf(btn)) { + // initialize all bitmaps to normal state + for ( int n = 0; n < wxButton::State_Max; n++ ) + { + m_iml.Add(bitmap); + } + m_data.himl = GetHimagelistOf(&m_iml); // use default margins @@ -215,22 +224,7 @@ public: virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) { - const int imagesToAdd = which - m_iml.GetImageCount(); - if ( imagesToAdd >= 0 ) - { - if ( imagesToAdd > 0 ) - { - const wxBitmap bmpNormal = GetBitmap(wxButton::State_Normal); - for ( int n = 0; n < imagesToAdd; n++ ) - m_iml.Add(bmpNormal); - } - - m_iml.Add(bitmap); - } - else // we already have this bitmap - { - m_iml.Replace(which, bitmap); - } + m_iml.Replace(which, bitmap); UpdateImageInfo(); } @@ -851,20 +845,23 @@ WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // as the theme size might have changed InvalidateBestSize(); } - else if ( wxUxThemeEngine::GetIfActive() ) +#endif // wxUSE_UXTHEME + // must use m_mouseInWindow here instead of IsMouseInWindow() + // since we need to know the first time the mouse enters the window + // and IsMouseInWindow() would return true in this case + else if ( (nMsg == WM_MOUSEMOVE && !m_mouseInWindow) || + nMsg == WM_MOUSELEAVE ) { - // we need to Refresh() if mouse has entered or left window - // so we can update the hot tracking state - // must use m_mouseInWindow here instead of IsMouseInWindow() - // since we need to know the first time the mouse enters the window - // and IsMouseInWindow() would return true in this case - if ( ( nMsg == WM_MOUSEMOVE && !m_mouseInWindow ) || - nMsg == WM_MOUSELEAVE ) + if ( +#if wxUSE_UXTHEME + wxUxThemeEngine::GetIfActive() || +#endif // wxUSE_UXTHEME + m_imageData && m_imageData->GetBitmap(State_Current).IsOk() + ) { Refresh(); } } -#endif // wxUSE_UXTHEME // let the base class do all real processing return wxControl::MSWWindowProc(nMsg, wParam, lParam); @@ -887,12 +884,12 @@ void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which) #if wxUSE_UXTHEME if ( wxUxThemeEngine::GetIfActive() ) { - m_imageData = new wxXPButtonImageData(this, bitmap.GetSize()); + m_imageData = new wxXPButtonImageData(this, bitmap); } else #endif // wxUSE_UXTHEME { - m_imageData = new wxODButtonImageData(this); + m_imageData = new wxODButtonImageData(this, bitmap); MakeOwnerDrawn(); } @@ -900,8 +897,10 @@ void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which) // changed to account for it InvalidateBestSize(); } - - m_imageData->SetBitmap(bitmap, which); + else + { + m_imageData->SetBitmap(bitmap, which); + } } void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y) @@ -926,6 +925,25 @@ void wxButton::DoSetBitmapPosition(wxDirection dir) namespace { +// return the button state using both the ODS_XXX flags specified in state +// parameter and the current button state +wxButton::State GetButtonState(wxButton *btn, UINT state) +{ + if ( state & ODS_DISABLED ) + return wxButton::State_Disabled; + + if ( state & ODS_SELECTED ) + return wxButton::State_Pressed; + + if ( btn->HasCapture() || btn->IsMouseInWindow() ) + return wxButton::State_Current; + + if ( state & ODS_FOCUS ) + return wxButton::State_Focused; + + return wxButton::State_Normal; +} + void DrawButtonText(HDC hdc, RECT *pRect, const wxString& text, @@ -1064,48 +1082,40 @@ void DrawButtonFrame(HDC hdc, RECT& rectBtn, } #if wxUSE_UXTHEME -void MSWDrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state) +void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state) { wxUxThemeHandle theme(button, L"BUTTON"); - int iState; - if ( state & ODS_SELECTED ) + // this array is indexed by wxButton::State values and so must be kept in + // sync with it + static const uxStates[] = { - iState = PBS_PRESSED; - } - else if ( button->HasCapture() || button->IsMouseInWindow() ) - { - iState = PBS_HOT; - } - else if ( state & ODS_FOCUS ) - { - iState = PBS_DEFAULTED; - } - else if ( state & ODS_DISABLED ) - { - iState = PBS_DISABLED; - } - else - { - iState = PBS_NORMAL; - } + PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED + }; + + int iState = uxStates[GetButtonState(button, state)]; + + wxUxThemeEngine * const engine = wxUxThemeEngine::Get(); // draw parent background if needed - if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme, - BP_PUSHBUTTON, - iState) ) + if ( engine->IsThemeBackgroundPartiallyTransparent + ( + theme, + BP_PUSHBUTTON, + iState + ) ) { - wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn); + engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn); } // draw background - wxUxThemeEngine::Get()->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState, - &rectBtn, NULL); + engine->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState, + &rectBtn, NULL); // calculate content area margins MARGINS margins; - wxUxThemeEngine::Get()->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState, - TMT_CONTENTMARGINS, &rectBtn, &margins); + engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState, + TMT_CONTENTMARGINS, &rectBtn, &margins); ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight); if ( button->UseBgCol() ) @@ -1184,7 +1194,7 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) #if wxUSE_UXTHEME if ( wxUxThemeEngine::GetIfActive() ) { - MSWDrawXPBackground(this, hdc, rectBtn, state); + DrawXPBackground(this, hdc, rectBtn, state); } else #endif // wxUSE_UXTHEME @@ -1231,7 +1241,10 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) // draw the image, if any if ( m_imageData ) { - wxBitmap bmp = m_imageData->GetBitmap(State_Normal); + wxBitmap bmp = m_imageData->GetBitmap(GetButtonState(this, state)); + if ( !bmp.IsOk() ) + bmp = m_imageData->GetBitmap(State_Normal); + const wxSize sizeBmp = bmp.GetSize(); const wxSize margin = m_imageData->GetBitmapMargins(); const wxSize sizeBmpWithMargins(sizeBmp + 2*margin); -- 2.45.2