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: 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:
-------

View File

@@ -75,6 +75,12 @@ enum
wxCC_POPUP_ON_MOUSE_UP = 0x0002,
// All text is not automatically selected on click
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
wxCC_IFLAG_CREATED = 0x0100,
@@ -426,7 +432,8 @@ protected:
// flags for DrawButton()
enum
{
Draw_PaintBg = 1
Draw_PaintBg = 1,
Draw_BitmapOnly = 2
};
// Draws dropbutton. Using wxRenderer or bitmaps, as appropriate.

View File

@@ -1326,7 +1326,7 @@ void wxComboCtrlBase::PrepareBackground( wxDC&, const wxRect&, int ) const
}
#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;
@@ -1353,8 +1353,11 @@ void wxComboCtrlBase::DrawButton( wxDC& dc, const wxRect& rect, int paintBg )
if ( !m_bmpNormal.Ok() )
{
if ( flags & Draw_BitmapOnly )
return;
// Need to clear button background even if m_btn is present
if ( paintBg )
if ( flags & Draw_PaintBg )
{
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
// 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 = 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
// (assume non-button background was cleared just before this call so brushes are good)
if ( paintBg )
if ( flags & Draw_PaintBg )
dc.DrawRectangle(rect);
}

View File

@@ -47,6 +47,7 @@
// parameters.
#if 0
#include <tmschema.h>
#include <VSStyle.h>
#else
//----------------------------------
#define EP_EDITTEXT 1
@@ -61,9 +62,59 @@
#define TMT_TEXTCOLOR 3803
#define TMT_BORDERCOLOR 3801
#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
// In wx2.9, this is defined in msw\private.h
#define wxWinVersion_Vista 0x0600
#define NATIVE_TEXT_INDENT_XP 4
#define NATIVE_TEXT_INDENT_CLASSIC 2
@@ -290,11 +341,10 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
#if wxUSE_UXTHEME
wxUxThemeHandle hTheme(this, L"COMBOBOX");
#endif
//COLORREF cref;
wxSize sz = GetClientSize();
bool isEnabled;
bool isFocused; // also selected
bool doDrawFocusRect; // also selected
// For smaller size control (and for disabled background) use less spacing
int focusSpacingX;
@@ -304,7 +354,7 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
{
// Drawing control
isEnabled = IsEnabled();
isFocused = ShouldDrawFocus();
doDrawFocusRect = ShouldDrawFocus();
#if wxUSE_UXTHEME
// 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
isEnabled = true; // they are never disabled
isFocused = flags & wxCONTROL_SELECTED ? true : false;
doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
focusSpacingX = 0;
focusSpacingY = 0;
@@ -353,53 +403,70 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
selRect.x += wcp + focusSpacingX;
selRect.width -= wcp + (focusSpacingX*2);
//wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
//wxUxThemeEngine* theme = NULL;
//if ( hTheme )
// theme = wxUxThemeEngine::GetIfActive();
wxColour bgCol;
wxColour fgCol;
bool drawDottedEdge = false;
wxColour bgCol;
bool doDrawDottedEdge = false;
bool doDrawSelRect = true;
if ( isEnabled && isFocused && HasFlag(wxCB_READONLY) )
drawDottedEdge = true;
// TODO: doDrawDottedEdge = true when focus has arrived to control via tab.
// (and other cases which are not that apparent).
if ( isEnabled )
{
if ( isFocused )
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
else if ( m_hasFgCol )
// Honour the custom foreground colour
fgCol = GetForegroundColour();
// If popup is hidden and this control is focused,
// then draw the focus-indicator (selbgcolor background etc.).
if ( doDrawFocusRect )
{
// 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();
bgCol = GetBackgroundColour();
doDrawSelRect = false;
doDrawDottedEdge = true;
}
else
{
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
}
}
else
fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
{
fgCol = GetForegroundColour();
bgCol = GetBackgroundColour();
doDrawSelRect = false;
}
}
else
{
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);
}
dc.SetTextForeground(fgCol);
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(selRect);
if ( drawDottedEdge )
wxMSWDrawFocusRect(dc,selRect);
if ( doDrawSelRect )
{
dc.SetPen(bgCol);
dc.DrawRectangle(selRect);
}
if ( doDrawDottedEdge )
wxMSWDrawFocusRect(dc, selRect);
// Don't clip exactly to the selection rectangle so we can draw
// to the non-selected area in front of it.
@@ -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
wxSize sz = GetClientSize();
wxAutoBufferedPaintDC dc(this);
const wxRect& rectb = m_btnArea;
wxRect rect = m_tcArea;
bool isEnabled = IsEnabled();
const wxRect& rectButton = m_btnArea;
wxRect rectTextField = m_tcArea;
wxColour bgCol = GetBackgroundColour();
wxColour fgCol;
#if wxUSE_UXTHEME
const bool isEnabled = IsEnabled();
HDC hDc = GetHdcOf(dc);
HWND hWnd = GetHwndOf(this);
wxUxThemeEngine* theme = NULL;
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 )
{
rect2 = m_tcArea;
rect2.Inflate(1);
borderRect = m_tcArea;
borderRect.Inflate(1);
}
int drawButFlags = 0;
#if wxUSE_UXTHEME
// Use theme to draw border on XP
if ( hTheme )
{
theme = wxUxThemeEngine::GetIfActive();
COLORREF cref;
const bool useVistaComboBox = ::wxGetWinVersion() >= wxWinVersion_Vista;
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 )
etsState = ETS_DISABLED;
else
etsState = ETS_NORMAL;
if ( m_widthCustomBorder )
{
theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref);
// Set border colour
dc.SetPen( wxRGBToColour(cref) );
dc.SetBrush( *wxTRANSPARENT_BRUSH );
dc.DrawRectangle(rect2);
butState = CBXS_DISABLED;
}
// Vista will display the drop-button as depressed always
// when the popup window is visilbe
else if ( (m_btnState & wxCONTROL_PRESSED) ||
(useVistaComboBox && !IsPopupWindowState(Hidden)) )
{
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);
fgCol = wxRGBToColour(cref);
int comboBoxPart = 0; // For XP, use the 'default' part
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
#endif
{
// draw regular background
fgCol = GetForegroundColour();
// Windows 2000 and earlier
drawButFlags = Draw_PaintBg;
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
dc.DrawRectangle(borderRect);
}
rect2.Deflate(m_widthCustomBorder);
// Button rendering (may only do the bitmap on button, depending on the flags)
DrawButton( dc, rectButton, drawButFlags );
dc.SetBrush(bgCol);
dc.SetPen(bgCol);
// 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
DrawButton(dc,rectb,drawButFlags);
// paint required portion on the control
// Paint required portion of the custom image on the control
if ( (!m_text || m_widthCustomPaint) )
{
wxASSERT( m_widthCustomPaint >= 0 );
@@ -507,15 +676,15 @@ void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
// this is intentionally here to allow drawed rectangle's
// right edge to be hidden
if ( m_text )
rect.width = m_widthCustomPaint;
rectTextField.width = m_widthCustomPaint;
dc.SetFont( GetFont() );
dc.SetClippingRegion(rect);
dc.SetClippingRegion(rectTextField);
if ( m_popupInterface )
m_popupInterface->PaintComboControl(dc,rect);
m_popupInterface->PaintComboControl(dc,rectTextField);
else
wxComboPopup::DefaultPaintComboControl(this,dc,rect);
wxComboPopup::DefaultPaintComboControl(this,dc,rectTextField);
}
}