Backported from wx2.9 improved wxComboCtrl/wxOwnerDrawnComboBox appearance under Vista/Win7. Should have been done years ago, really.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@64396 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Jaakko Salli
2010-05-26 14:54:27 +00:00
parent 8fe08e5275
commit a01ef3cc40
4 changed files with 286 additions and 102 deletions

View File

@@ -96,6 +96,11 @@ All (GUI):
- wxRTC: fixed style selection resetting after editing a style. - wxRTC: fixed style selection resetting after editing a style.
- wxRTC: can now edit line spacing in .1 increments from 1 to 2. - wxRTC: can now edit line spacing in .1 increments from 1 to 2.
wxMSW:
- Backported improved wxOwnerDrawnComboBox and wxComboCtrl appearance under
Windows Vista/7.
2.8.11: 2.8.11:
------- -------

View File

@@ -75,6 +75,12 @@ enum
wxCC_POPUP_ON_MOUSE_UP = 0x0002, wxCC_POPUP_ON_MOUSE_UP = 0x0002,
// All text is not automatically selected on click // All text is not automatically selected on click
wxCC_NO_TEXT_AUTO_SELECT = 0x0004, wxCC_NO_TEXT_AUTO_SELECT = 0x0004,
// Drop-button stays down as long as popup is displayed.
wxCC_BUTTON_STAYS_DOWN = 0x0008,
// Drop-button covers the entire control.
wxCC_FULL_BUTTON = 0x0010,
// Drop-button goes over the custom-border (used under WinVista).
wxCC_BUTTON_COVERS_BORDER = 0x0020,
// Internal use: signals creation is complete // Internal use: signals creation is complete
wxCC_IFLAG_CREATED = 0x0100, wxCC_IFLAG_CREATED = 0x0100,
@@ -426,7 +432,8 @@ protected:
// flags for DrawButton() // flags for DrawButton()
enum enum
{ {
Draw_PaintBg = 1 Draw_PaintBg = 1,
Draw_BitmapOnly = 2
}; };
// Draws dropbutton. Using wxRenderer or bitmaps, as appropriate. // Draws dropbutton. Using wxRenderer or bitmaps, as appropriate.

View File

@@ -1326,7 +1326,7 @@ void wxComboCtrlBase::PrepareBackground( wxDC&, const wxRect&, int ) const
} }
#endif #endif
void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int paintBg ) void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int flags )
{ {
int drawState = m_btnState; int drawState = m_btnState;
@@ -1353,8 +1353,11 @@ void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int paintBg )
if ( !m_bmpNormal.Ok() ) if ( !m_bmpNormal.Ok() )
{ {
if ( flags & Draw_BitmapOnly )
return;
// Need to clear button background even if m_btn is present // Need to clear button background even if m_btn is present
if ( paintBg ) if ( flags & Draw_PaintBg )
{ {
wxColour bgCol; wxColour bgCol;
@@ -1393,7 +1396,7 @@ void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int paintBg )
{ {
// If using blank button background, we need to clear its background // If using blank button background, we need to clear its background
// with button face colour instead of colour for rest of the control. // with button face colour instead of colour for rest of the control.
if ( paintBg ) if ( flags & Draw_PaintBg )
{ {
wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); wxColour bgCol = GetParent()->GetBackgroundColour(); //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
//wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); //wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
@@ -1413,7 +1416,7 @@ void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int paintBg )
{ {
// Need to clear button background even if m_btn is present // Need to clear button background even if m_btn is present
// (assume non-button background was cleared just before this call so brushes are good) // (assume non-button background was cleared just before this call so brushes are good)
if ( paintBg ) if ( flags & Draw_PaintBg )
dc.DrawRectangle(rect); dc.DrawRectangle(rect);
} }

View File

@@ -47,6 +47,7 @@
// parameters. // parameters.
#if 0 #if 0
#include <tmschema.h> #include <tmschema.h>
#include <VSStyle.h>
#else #else
//---------------------------------- //----------------------------------
#define EP_EDITTEXT 1 #define EP_EDITTEXT 1
@@ -61,9 +62,59 @@
#define TMT_TEXTCOLOR 3803 #define TMT_TEXTCOLOR 3803
#define TMT_BORDERCOLOR 3801 #define TMT_BORDERCOLOR 3801
#define TMT_EDGEFILLCOLOR 3808 #define TMT_EDGEFILLCOLOR 3808
//---------------------------------- #define TMT_BGTYPE 4001
#define BT_IMAGEFILE 0
#define BT_BORDERFILL 1
#define CP_DROPDOWNBUTTON 1
#define CP_BACKGROUND 2 // This and above are Vista and later only
#define CP_TRANSPARENTBACKGROUND 3
#define CP_BORDER 4
#define CP_READONLY 5
#define CP_DROPDOWNBUTTONRIGHT 6
#define CP_DROPDOWNBUTTONLEFT 7
#define CP_CUEBANNER 8
#define CBXS_NORMAL 1
#define CBXS_HOT 2
#define CBXS_PRESSED 3
#define CBXS_DISABLED 4
#define CBXSR_NORMAL 1
#define CBXSR_HOT 2
#define CBXSR_PRESSED 3
#define CBXSR_DISABLED 4
#define CBXSL_NORMAL 1
#define CBXSL_HOT 2
#define CBXSL_PRESSED 3
#define CBXSL_DISABLED 4
#define CBTBS_NORMAL 1
#define CBTBS_HOT 2
#define CBTBS_DISABLED 3
#define CBTBS_FOCUSED 4
#define CBB_NORMAL 1
#define CBB_HOT 2
#define CBB_FOCUSED 3
#define CBB_DISABLED 4
#define CBRO_NORMAL 1
#define CBRO_HOT 2
#define CBRO_PRESSED 3
#define CBRO_DISABLED 4
#define CBCB_NORMAL 1
#define CBCB_HOT 2
#define CBCB_PRESSED 3
#define CBCB_DISABLED 4
#endif #endif
// In wx2.9, this is defined in msw\private.h
#define wxWinVersion_Vista 0x0600
#define NATIVE_TEXT_INDENT_XP 4 #define NATIVE_TEXT_INDENT_XP 4
#define NATIVE_TEXT_INDENT_CLASSIC 2 #define NATIVE_TEXT_INDENT_CLASSIC 2
@@ -290,11 +341,10 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
#if wxUSE_UXTHEME #if wxUSE_UXTHEME
wxUxThemeHandle hTheme(this, L"COMBOBOX"); wxUxThemeHandle hTheme(this, L"COMBOBOX");
#endif #endif
//COLORREF cref;
wxSize sz = GetClientSize(); wxSize sz = GetClientSize();
bool isEnabled; bool isEnabled;
bool isFocused; // also selected bool doDrawFocusRect; // also selected
// For smaller size control (and for disabled background) use less spacing // For smaller size control (and for disabled background) use less spacing
int focusSpacingX; int focusSpacingX;
@@ -304,7 +354,7 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
{ {
// Drawing control // Drawing control
isEnabled = IsEnabled(); isEnabled = IsEnabled();
isFocused = ShouldDrawFocus(); doDrawFocusRect = ShouldDrawFocus();
#if wxUSE_UXTHEME #if wxUSE_UXTHEME
// Windows-style: for smaller size control (and for disabled background) use less spacing // Windows-style: for smaller size control (and for disabled background) use less spacing
@@ -334,7 +384,7 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
{ {
// Drawing a list item // Drawing a list item
isEnabled = true; // they are never disabled isEnabled = true; // they are never disabled
isFocused = flags & wxCONTROL_SELECTED ? true : false; doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
focusSpacingX = 0; focusSpacingX = 0;
focusSpacingY = 0; focusSpacingY = 0;
@@ -353,52 +403,69 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
selRect.x += wcp + focusSpacingX; selRect.x += wcp + focusSpacingX;
selRect.width -= wcp + (focusSpacingX*2); selRect.width -= wcp + (focusSpacingX*2);
//wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL; //wxUxThemeEngine* theme = NULL;
//if ( hTheme ) //if ( hTheme )
// theme = wxUxThemeEngine::GetIfActive(); // theme = wxUxThemeEngine::GetIfActive();
wxColour bgCol;
wxColour fgCol; wxColour fgCol;
bool drawDottedEdge = false; wxColour bgCol;
bool doDrawDottedEdge = false;
bool doDrawSelRect = true;
if ( isEnabled && isFocused && HasFlag(wxCB_READONLY) ) // TODO: doDrawDottedEdge = true when focus has arrived to control via tab.
drawDottedEdge = true; // (and other cases which are not that apparent).
if ( isEnabled ) if ( isEnabled )
{ {
if ( isFocused ) // If popup is hidden and this control is focused,
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); // then draw the focus-indicator (selbgcolor background etc.).
else if ( m_hasFgCol ) if ( doDrawFocusRect )
// Honour the custom foreground colour {
// NB: We can't really use XP visual styles to get TMT_TEXTCOLOR since
// it is not properly defined for combo boxes. Instead, they expect
// you to use DrawThemeText.
//
// Here is, however, sample code how to get theme colours:
//
// COLORREF cref;
// theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
// dc.SetTextForeground( wxRGBToColour(cref) );
if ( (m_iFlags & wxCC_FULL_BUTTON) && !(flags & wxCONTROL_ISSUBMENU) )
{
// Vista style read-only combo
fgCol = GetForegroundColour(); fgCol = GetForegroundColour();
bgCol = GetBackgroundColour();
doDrawSelRect = false;
doDrawDottedEdge = true;
}
else else
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); {
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
}
}
else
{
fgCol = GetForegroundColour();
bgCol = GetBackgroundColour();
doDrawSelRect = false;
}
} }
else else
{ {
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
}
if ( isEnabled )
{
if ( isFocused )
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
else if ( m_hasBgCol )
// Honour the custom background colour
bgCol = GetBackgroundColour();
else
bgCol = GetBackgroundColour();
}
else
{
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
} }
dc.SetTextForeground(fgCol); dc.SetTextForeground(fgCol);
dc.SetBrush(bgCol); dc.SetBrush(bgCol);
if ( doDrawSelRect )
{
dc.SetPen(bgCol); dc.SetPen(bgCol);
dc.DrawRectangle(selRect); dc.DrawRectangle(selRect);
if ( drawDottedEdge ) }
if ( doDrawDottedEdge )
wxMSWDrawFocusRect(dc, selRect); wxMSWDrawFocusRect(dc, selRect);
// Don't clip exactly to the selection rectangle so we can draw // Don't clip exactly to the selection rectangle so we can draw
@@ -412,94 +479,196 @@ void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
{ {
// TODO: Convert drawing in this function to Windows API Code // TODO: Convert drawing in this function to Windows API Code
// TODO: Convert drawing in this function to Windows API Code
wxSize sz = GetClientSize(); wxSize sz = GetClientSize();
wxAutoBufferedPaintDC dc(this); wxAutoBufferedPaintDC dc(this);
const wxRect& rectb = m_btnArea; const wxRect& rectButton = m_btnArea;
wxRect rect = m_tcArea; wxRect rectTextField = m_tcArea;
bool isEnabled = IsEnabled();
wxColour bgCol = GetBackgroundColour(); wxColour bgCol = GetBackgroundColour();
wxColour fgCol;
#if wxUSE_UXTHEME #if wxUSE_UXTHEME
const bool isEnabled = IsEnabled();
HDC hDc = GetHdcOf(dc);
HWND hWnd = GetHwndOf(this);
wxUxThemeEngine* theme = NULL; wxUxThemeEngine* theme = NULL;
wxUxThemeHandle hTheme(this, L"COMBOBOX"); wxUxThemeHandle hTheme(this, L"COMBOBOX");
#endif
int etsState; if ( hTheme )
theme = wxUxThemeEngine::GetIfActive();
#endif // wxUSE_UXTHEME
wxRect borderRect(0,0,sz.x,sz.y);
// area around both controls
wxRect rect2(0,0,sz.x,sz.y);
if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE ) if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
{ {
rect2 = m_tcArea; borderRect = m_tcArea;
rect2.Inflate(1); borderRect.Inflate(1);
} }
int drawButFlags = 0;
#if wxUSE_UXTHEME #if wxUSE_UXTHEME
// Use theme to draw border on XP
if ( hTheme ) if ( hTheme )
{ {
theme = wxUxThemeEngine::GetIfActive(); const bool useVistaComboBox = ::wxGetWinVersion() >= wxWinVersion_Vista;
COLORREF cref;
RECT rFull;
wxCopyRectToRECT(borderRect, rFull);
RECT rButton;
wxCopyRectToRECT(rectButton, rButton);
RECT rBorder;
wxCopyRectToRECT(borderRect, rBorder);
wxUint32 isNonStdButton = (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE);
//
// Get some states for themed drawing
int butState;
// Select correct border colour
if ( !isEnabled ) if ( !isEnabled )
etsState = ETS_DISABLED;
else
etsState = ETS_NORMAL;
if ( m_widthCustomBorder )
{ {
theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref); butState = CBXS_DISABLED;
}
// Set border colour // Vista will display the drop-button as depressed always
dc.SetPen( wxRGBToColour(cref) ); // when the popup window is visilbe
else if ( (m_btnState & wxCONTROL_PRESSED) ||
dc.SetBrush( *wxTRANSPARENT_BRUSH ); (useVistaComboBox && !IsPopupWindowState(Hidden)) )
dc.DrawRectangle(rect2); {
butState = CBXS_PRESSED;
}
else if ( m_btnState & wxCONTROL_CURRENT )
{
butState = CBXS_HOT;
}
else
{
butState = CBXS_NORMAL;
} }
theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_TEXTCOLOR,&cref); int comboBoxPart = 0; // For XP, use the 'default' part
fgCol = wxRGBToColour(cref); RECT* rUseForBg = &rBorder;
bool drawFullButton = false;
int bgState = butState;
const bool isFocused = (FindFocus() == GetMainWindowOfCompositeControl()) ? true : false;
if ( useVistaComboBox )
{
// FIXME: Either SetBackgroundColour or GetBackgroundColour
// doesn't work under Vista, so here's a temporary
// workaround.
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
// Draw the entire control as a single button?
if ( !isNonStdButton )
{
if ( HasFlag(wxCB_READONLY) )
drawFullButton = true;
}
if ( drawFullButton )
{
comboBoxPart = CP_READONLY;
rUseForBg = &rFull;
// It should be safe enough to update this flag here.
m_iFlags |= wxCC_FULL_BUTTON;
}
else
{
comboBoxPart = CP_BORDER;
m_iFlags &= ~wxCC_FULL_BUTTON;
if ( isFocused )
bgState = CBB_FOCUSED;
else
bgState = CBB_NORMAL;
}
}
//
// Draw parent's background, if necessary
RECT* rUseForTb = NULL;
if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) )
rUseForTb = &rFull;
else if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
rUseForTb = &rButton;
if ( rUseForTb )
theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb );
//
// Draw the control background (including the border)
if ( m_widthCustomBorder > 0 )
{
theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL );
}
else
{
// No border. We can't use theme, since it cannot be relied on
// to deliver borderless drawing, even with DrawThemeBackgroundEx.
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(borderRect);
}
//
// Draw the drop-button
if ( !isNonStdButton )
{
drawButFlags = Draw_BitmapOnly;
int butPart = CP_DROPDOWNBUTTON;
if ( useVistaComboBox )
{
if ( drawFullButton )
{
// We need to alter the button style slightly before
// drawing the actual button (but it was good above
// when background etc was done).
if ( butState == CBXS_HOT || butState == CBXS_PRESSED )
butState = CBXS_NORMAL;
}
if ( m_btnSide == wxRIGHT )
butPart = CP_DROPDOWNBUTTONRIGHT;
else
butPart = CP_DROPDOWNBUTTONLEFT;
}
theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL );
}
else if ( useVistaComboBox &&
(m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) )
{
// We'll do this, because DrawThemeParentBackground
// doesn't seem to be reliable on Vista.
drawButFlags |= Draw_PaintBg;
}
} }
else else
#endif #endif
{ {
// draw regular background // Windows 2000 and earlier
fgCol = GetForegroundColour(); drawButFlags = Draw_PaintBg;
}
rect2.Deflate(m_widthCustomBorder);
dc.SetBrush(bgCol); dc.SetBrush(bgCol);
dc.SetPen(bgCol); dc.SetPen(bgCol);
dc.DrawRectangle(borderRect);
// clear main background
dc.DrawRectangle(rect);
// Button background with theme?
int drawButFlags = Draw_PaintBg;
#if wxUSE_UXTHEME
if ( hTheme && m_blankButtonBg )
{
RECT r;
wxCopyRectToRECT(rectb, r);
// Draw parent background if needed (since button looks like its out of
// the combo, this is preferred).
theme->DrawThemeParentBackground(GetHwndOf(this),
GetHdcOf(dc),
&r);
drawButFlags = 0;
} }
#endif
// Standard button rendering // Button rendering (may only do the bitmap on button, depending on the flags)
DrawButton(dc,rectb,drawButFlags); DrawButton( dc, rectButton, drawButFlags );
// paint required portion on the control // Paint required portion of the custom image on the control
if ( (!m_text || m_widthCustomPaint) ) if ( (!m_text || m_widthCustomPaint) )
{ {
wxASSERT( m_widthCustomPaint >= 0 ); wxASSERT( m_widthCustomPaint >= 0 );
@@ -507,15 +676,15 @@ void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
// this is intentionally here to allow drawed rectangle's // this is intentionally here to allow drawed rectangle's
// right edge to be hidden // right edge to be hidden
if ( m_text ) if ( m_text )
rect.width = m_widthCustomPaint; rectTextField.width = m_widthCustomPaint;
dc.SetFont( GetFont() ); dc.SetFont( GetFont() );
dc.SetClippingRegion(rect); dc.SetClippingRegion(rectTextField);
if ( m_popupInterface ) if ( m_popupInterface )
m_popupInterface->PaintComboControl(dc,rect); m_popupInterface->PaintComboControl(dc,rectTextField);
else else
wxComboPopup::DefaultPaintComboControl(this,dc,rect); wxComboPopup::DefaultPaintComboControl(this,dc,rectTextField);
} }
} }