implement images support for wxButton under XP and later

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@61054 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2009-06-15 04:23:54 +00:00
parent 81cb7b5a98
commit 233f10bf15
5 changed files with 359 additions and 16 deletions

View File

@ -99,7 +99,7 @@ public:
// set the image position relative to the text, i.e. wxLEFT means that the
// image is to the left of the text (this is the default)
virtual void SetBitmapPosition(wxDirection WXUNUSED(dir)) { }
void SetBitmapPosition(wxDirection dir);
// make this button the default button in its top level window
@ -130,20 +130,26 @@ public:
void SetBitmapSelected(const wxBitmap& bitmap) { SetBitmapPressed(bitmap); }
void SetBitmapHover(const wxBitmap& bitmap) { SetBitmapCurrent(bitmap); }
protected:
// choose the default border for this window
virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; }
// this enum is not part of wx public API, it is public because it is used
// in non wxButton-derived classes internally
//
// also notice that MSW code relies on the values of the enum elements, do
// not change them without revising src/msw/button.cpp
enum State
{
State_Normal,
State_Pressed, // a.k.a. "selected" in public API for some reason
State_Current, // a.k.a. hot or "hovering"
State_Pressed, // a.k.a. "selected" in public API for some reason
State_Disabled,
State_Focused,
State_Max
};
protected:
// choose the default border for this window
virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; }
virtual wxBitmap DoGetBitmap(State WXUNUSED(which)) const
{ return wxBitmap(); }
virtual void DoSetBitmap(const wxBitmap& WXUNUSED(bitmap),
@ -151,6 +157,8 @@ protected:
{ }
virtual void DoSetBitmapMargins(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
{ }
virtual void DoSetBitmapPosition(wxDirection WXUNUSED(dir))
{ }
wxDECLARE_NO_COPY_CLASS(wxButtonBase);

View File

@ -19,7 +19,7 @@
class WXDLLIMPEXP_CORE wxButton : public wxButtonBase
{
public:
wxButton() { }
wxButton() { m_imageData = NULL; }
wxButton(wxWindow *parent,
wxWindowID id,
const wxString& label = wxEmptyString,
@ -29,6 +29,8 @@ public:
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxButtonNameStr)
{
m_imageData = NULL;
Create(parent, id, label, pos, size, style, validator, name);
}
@ -78,6 +80,13 @@ protected:
// usually overridden base class virtuals
virtual wxSize DoGetBestSize() const;
virtual wxBitmap DoGetBitmap(State which) const;
virtual void DoSetBitmap(const wxBitmap& bitmap, State which);
virtual void DoSetBitmapMargins(wxCoord x, wxCoord y);
virtual void DoSetBitmapPosition(wxDirection dir);
class wxButtonImageData *m_imageData;
private:
DECLARE_DYNAMIC_CLASS_NO_COPY(wxButton)
};

View File

@ -58,6 +58,14 @@ enum
};
// radio boxes
enum
{
ButtonImagePos_Left,
ButtonImagePos_Right,
ButtonImagePos_Top,
ButtonImagePos_Bottom
};
enum
{
ButtonHAlign_Left,
@ -124,6 +132,9 @@ protected:
*m_chkUseHover,
*m_chkUseDisabled;
// and an image position choice used if m_chkImage is on
wxRadioBox *m_radioImagePos;
wxRadioBox *m_radioHAlign,
*m_radioVAlign;
@ -179,6 +190,7 @@ ButtonWidgetsPage::ButtonWidgetsPage(WidgetsBookCtrl *book,
m_chkUseHover =
m_chkUseDisabled = (wxCheckBox *)NULL;
m_radioImagePos =
m_radioHAlign =
m_radioVAlign = (wxRadioBox *)NULL;
@ -202,21 +214,26 @@ void ButtonWidgetsPage::CreateContent()
m_chkFit = CreateCheckBoxAndAddToSizer(sizerLeft, _T("&Fit exactly"));
m_chkDefault = CreateCheckBoxAndAddToSizer(sizerLeft, _T("&Default"));
#ifndef __WXUNIVERSAL__
// only wxUniv currently supports buttons with images
m_chkImage->Disable();
#endif // !wxUniv
sizerLeft->AddSpacer(5);
wxSizer *sizerUseLabels =
new wxStaticBoxSizer(wxVERTICAL, this, _T("&Use the following labels?"));
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"));
sizerLeft->Add(sizerUseLabels, wxSizerFlags().Expand().Border());
sizerLeft->AddSpacer(10);
static const wxString dirs[] =
{
"left", "right", "top", "bottom",
};
m_radioImagePos = new wxRadioBox(this, wxID_ANY, "Image &position",
wxDefaultPosition, wxDefaultSize,
WXSIZEOF(dirs), dirs);
sizerLeft->Add(m_radioImagePos, 0, wxGROW | wxALL, 5);
sizerLeft->AddSpacer(15);
// should be in sync with enums Button[HV]Align!
@ -293,6 +310,7 @@ void ButtonWidgetsPage::Reset()
m_chkUseHover->SetValue(true);
m_chkUseDisabled->SetValue(true);
m_radioImagePos->SetSelection(ButtonImagePos_Left);
m_radioHAlign->SetSelection(ButtonHAlign_Centre);
m_radioVAlign->SetSelection(ButtonVAlign_Centre);
}
@ -385,12 +403,16 @@ void ButtonWidgetsPage::CreateButton()
m_chkUseHover->Enable(isBitmapButton);
m_chkUseDisabled->Enable(isBitmapButton);
#ifdef __WXUNIVERSAL__
if ( m_chkImage->GetValue() )
{
m_button->SetImageLabel(wxArtProvider::GetIcon(wxART_INFORMATION));
static const wxDirection positions[] =
{
wxLEFT, wxRIGHT, wxTOP, wxBOTTOM
};
m_button->SetBitmap(wxArtProvider::GetIcon(wxART_INFORMATION),
positions[m_radioImagePos->GetSelection()]);
}
#endif // wxUniv
if ( m_chkDefault->GetValue() )
{

View File

@ -44,4 +44,16 @@ wxWindow *wxButtonBase::SetDefault()
return tlw->SetDefaultItem(this);
}
void wxButtonBase::SetBitmapPosition(wxDirection dir)
{
wxASSERT_MSG( !(dir & ~wxDIRECTION_MASK), "non-direction flag used" );
wxASSERT_MSG( !!(dir & wxLEFT) +
!!(dir & wxRIGHT) +
!!(dir & wxTOP) +
!!(dir & wxBOTTOM) == 1,
"exactly one direction flag must be set" );
DoSetBitmapPosition(dir);
}
#endif // wxUSE_BUTTON

View File

@ -37,6 +37,7 @@
#include "wx/dcscreen.h"
#include "wx/dcclient.h"
#include "wx/toplevel.h"
#include "wx/imaglist.h"
#endif
#include "wx/stockitem.h"
@ -59,9 +60,26 @@
#define TMT_CONTENTMARGINS 3602
#endif
// provide the necessary declarations ourselves if they're missing from
// headers
#ifndef BCM_SETIMAGELIST
#define BCM_SETIMAGELIST 0x1602
#define BCM_SETTEXTMARGIN 0x1604
enum
{
BUTTON_IMAGELIST_ALIGN_LEFT,
BUTTON_IMAGELIST_ALIGN_RIGHT,
BUTTON_IMAGELIST_ALIGN_TOP,
BUTTON_IMAGELIST_ALIGN_BOTTOM
};
struct BUTTON_IMAGELIST
{
HIMAGELIST himl;
RECT margin;
UINT uAlign;
};
#endif
#endif // wxUSE_UXTHEME
@ -81,6 +99,213 @@
#define DT_HIDEPREFIX 0x00100000
#endif
// ----------------------------------------------------------------------------
// button image data
// ----------------------------------------------------------------------------
// we use different data classes for owner drawn buttons and for themed XP ones
class wxButtonImageData
{
public:
wxButtonImageData() { }
virtual wxBitmap GetBitmap(wxButton::State which) const = 0;
virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) = 0;
virtual wxSize GetBitmapMargins() const = 0;
virtual void SetBitmapMargins(wxCoord x, wxCoord y) = 0;
virtual bool IsHorizontal() const = 0;
virtual void SetBitmapPosition(wxDirection dir) = 0;
private:
wxDECLARE_NO_COPY_CLASS(wxButtonImageData);
};
namespace
{
class wxODButtonImageData : public wxButtonImageData
{
public:
wxODButtonImageData() { m_dir = wxLEFT; }
virtual wxBitmap GetBitmap(wxButton::State which) const
{
return m_bitmaps[which];
}
virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which)
{
m_bitmaps[which] = bitmap;
}
virtual wxSize GetBitmapMargins() const
{
return m_margin;
}
virtual void SetBitmapMargins(wxCoord x, wxCoord y)
{
m_margin = wxSize(x, y);
}
virtual bool IsHorizontal() const
{
return m_dir == wxLEFT || m_dir == wxRIGHT;
}
virtual void SetBitmapPosition(wxDirection dir)
{
m_dir = dir;
}
private:
// just store the values passed to us to be able to retrieve them later
// from the drawing code
wxBitmap m_bitmaps[wxButton::State_Max];
wxSize m_margin;
wxDirection m_dir;
wxDECLARE_NO_COPY_CLASS(wxODButtonImageData);
};
#if wxUSE_UXTHEME
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),
m_hwndBtn(GetHwndOf(btn))
{
m_data.himl = GetHimagelistOf(&m_iml);
// use default margins
m_data.margin.left =
m_data.margin.right = btn->GetCharWidth();
m_data.margin.top =
m_data.margin.bottom = btn->GetCharHeight() / 2;
// and default alignment
m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
}
virtual wxBitmap GetBitmap(wxButton::State which) const
{
return m_iml.GetBitmap(which);
}
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);
}
UpdateImageInfo();
}
virtual wxSize GetBitmapMargins() const
{
return wxSize(m_data.margin.left, m_data.margin.top);
}
virtual void SetBitmapMargins(wxCoord x, wxCoord y)
{
RECT& margin = m_data.margin;
margin.left =
margin.right = x;
margin.top =
margin.bottom = y;
if ( !::SendMessage(m_hwndBtn, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin) )
{
wxLogDebug("SendMessage(BCM_SETTEXTMARGIN) failed");
}
}
virtual bool IsHorizontal() const
{
return m_data.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT ||
m_data.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT;
}
virtual void SetBitmapPosition(wxDirection dir)
{
UINT alignNew;
switch ( dir )
{
default:
wxFAIL_MSG( "invalid direction" );
// fall through
case wxLEFT:
alignNew = BUTTON_IMAGELIST_ALIGN_LEFT;
break;
case wxRIGHT:
alignNew = BUTTON_IMAGELIST_ALIGN_RIGHT;
break;
case wxTOP:
alignNew = BUTTON_IMAGELIST_ALIGN_TOP;
break;
case wxBOTTOM:
alignNew = BUTTON_IMAGELIST_ALIGN_BOTTOM;
break;
}
if ( alignNew != m_data.uAlign )
{
m_data.uAlign = alignNew;
UpdateImageInfo();
}
}
private:
void UpdateImageInfo()
{
if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) )
{
wxLogDebug("SendMessage(BCM_SETIMAGELIST) failed");
}
}
// we store image list separately to be able to use convenient wxImageList
// methods instead of working with raw HIMAGELIST
wxImageList m_iml;
// store the rest of the data in BCM_SETIMAGELIST-friendly form
BUTTON_IMAGELIST m_data;
// the button we're associated with
const HWND m_hwndBtn;
wxDECLARE_NO_COPY_CLASS(wxXPButtonImageData);
};
#endif // wxUSE_UXTHEME
} // anonymous namespace
// ----------------------------------------------------------------------------
// macros
// ----------------------------------------------------------------------------
@ -259,6 +484,8 @@ wxButton::~wxButton()
{
UnsetTmpDefault();
}
delete m_imageData;
}
// ----------------------------------------------------------------------------
@ -310,7 +537,29 @@ void wxButton::SetLabel(const wxString& label)
wxSize wxButton::DoGetBestSize() const
{
return wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this));
wxSize size = wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this));
if ( m_imageData )
{
const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize();
if ( m_imageData->IsHorizontal() )
{
size.x += sizeBmp.x;
if ( sizeBmp.y > size.y )
size.y = sizeBmp.y;
}
else // bitmap on top/below the text
{
size.y += sizeBmp.y;
if ( sizeBmp.x > size.x )
size.x = sizeBmp.x;
}
size += 2*m_imageData->GetBitmapMargins();
CacheBestSize(size);
}
return size;
}
/* static */
@ -590,6 +839,49 @@ WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
return wxControl::MSWWindowProc(nMsg, wParam, lParam);
}
// ----------------------------------------------------------------------------
// button images
// ----------------------------------------------------------------------------
wxBitmap wxButton::DoGetBitmap(State which) const
{
return m_imageData ? m_imageData->GetBitmap(which) : wxBitmap();
}
void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
{
// allocate the image data when the first bitmap is set
if ( !m_imageData )
{
#if wxUSE_UXTHEME
if ( wxUxThemeEngine::GetIfActive() )
m_imageData = new wxXPButtonImageData(this, bitmap.GetSize());
else
#endif // wxUSE_UXTHEME
m_imageData = new wxODButtonImageData;
// if a bitmap was assigned to the bitmap, its best size must be
// changed to account for it
InvalidateBestSize();
}
m_imageData->SetBitmap(bitmap, which);
}
void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
{
wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
m_imageData->SetBitmapMargins(x, y);
}
void wxButton::DoSetBitmapPosition(wxDirection dir)
{
wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
m_imageData->SetBitmapPosition(dir);
}
// ----------------------------------------------------------------------------
// owner-drawn buttons support
// ----------------------------------------------------------------------------