From: Robin Dunn Date: Wed, 13 Sep 2006 20:28:43 +0000 (+0000) Subject: More support for drawing native column headers, adds more states X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/4b94ddc44ced056bb862938d069d9148c4c782b1 More support for drawing native column headers, adds more states (selected, mouse-over) and also optionally drawing the contents of the header (label and/or bitmap, sort arrow) in a consistent way. Also added a method to determine the default height of the column header. This is based on work done for OSAF. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41201 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/renderer.h b/include/wx/renderer.h index 0f603773e0..ebda812285 100644 --- a/include/wx/renderer.h +++ b/include/wx/renderer.h @@ -55,8 +55,10 @@ enum wxCONTROL_CHECKED = 0x00000040, // (check/radio button) is checked wxCONTROL_CHECKABLE = 0x00000080, // (menu) item can be checked wxCONTROL_UNDETERMINED = wxCONTROL_CHECKABLE, // (check) undetermined state + wxCONTROL_UPICON = 0x00000100, // header button has an up arrow icon + wxCONTROL_DOWNICON = 0x00000200, // header button has a down arrow icon - wxCONTROL_FLAGS_MASK = 0x000000ff, + wxCONTROL_FLAGS_MASK = 0x000002ff, // this is a pseudo flag not used directly by wxRenderer but rather by some // controls internally @@ -86,6 +88,24 @@ struct WXDLLEXPORT wxSplitterRenderParams const bool isHotSensitive; }; + +// extra optional parameters for DrawHeaderButton +struct WXDLLEXPORT wxHeaderButtonParams +{ + wxHeaderButtonParams() + : m_labelAlignment(wxALIGN_LEFT) + { } + + wxColour m_arrowColour; + wxColour m_selectionColour; + wxString m_labelText; + wxFont m_labelFont; + wxColour m_labelColour; + wxBitmap m_labelBitmap; + int m_labelAlignment; +}; + + // wxRendererNative interface version struct WXDLLEXPORT wxRendererVersion { @@ -131,7 +151,22 @@ public: virtual void DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags = 0) = 0; + int flags = 0, + wxHeaderButtonParams* params=NULL) = 0; + + + // Draw the contents of a header control button (label, sort arrows, etc.) + // Normally only called by DrawHeaderButton. + virtual void DrawHeaderButtonContents(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0, + wxHeaderButtonParams* params=NULL) = 0; + + // Returns the default height of a header button, either a fixed platform + // height if available, or a generic height based on the window's font. + virtual int GetHeaderButtonHeight(wxWindow *win) = 0; + // draw the expanded/collapsed icon for a tree control item virtual void DrawTreeItemButton(wxWindow *win, @@ -264,8 +299,21 @@ public: virtual void DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags = 0) - { m_rendererNative.DrawHeaderButton(win, dc, rect, flags); } + int flags = 0, + wxHeaderButtonParams* params = NULL) + { m_rendererNative.DrawHeaderButton(win, dc, rect, flags, params); } + + + virtual void DrawHeaderButtonContents(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0, + wxHeaderButtonParams* params = NULL) + { m_rendererNative.DrawHeaderButtonContents(win, dc, rect, flags, params); } + + + virtual int GetHeaderButtonHeight(wxWindow *win) + { return m_rendererNative.GetHeaderButtonHeight(win); } virtual void DrawTreeItemButton(wxWindow *win, wxDC& dc, diff --git a/src/generic/renderg.cpp b/src/generic/renderg.cpp index 11c05ae4b3..6f84f6b904 100644 --- a/src/generic/renderg.cpp +++ b/src/generic/renderg.cpp @@ -49,7 +49,16 @@ public: virtual void DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags = 0); + int flags = 0, + wxHeaderButtonParams* params = NULL); + + virtual void DrawHeaderButtonContents(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0, + wxHeaderButtonParams* params = NULL); + + virtual int GetHeaderButtonHeight(wxWindow *win); virtual void DrawTreeItemButton(wxWindow *win, wxDC& dc, @@ -194,10 +203,11 @@ wxRendererGeneric::DrawShadedRect(wxDC& dc, // ---------------------------------------------------------------------------- void -wxRendererGeneric::DrawHeaderButton(wxWindow * WXUNUSED(win), +wxRendererGeneric::DrawHeaderButton(wxWindow* win, wxDC& dc, const wxRect& rect, - int WXUNUSED(flags)) + int flags, + wxHeaderButtonParams* params) { const int CORNER = 1; @@ -206,6 +216,10 @@ wxRendererGeneric::DrawHeaderButton(wxWindow * WXUNUSED(win), w = rect.width, h = rect.height; + dc.SetBrush(wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE))); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRectangle(rect); + dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.SetPen(m_penBlack); @@ -221,8 +235,180 @@ wxRendererGeneric::DrawHeaderButton(wxWindow * WXUNUSED(win), dc.DrawRectangle( x, y, 1, h ); // left (outer) dc.DrawLine( x, y+h-1, x+1, y+h-1 ); dc.DrawLine( x+w-1, y, x+w-1, y+1 ); + + DrawHeaderButtonContents(win, dc, rect, flags, params); } +void +wxRendererGeneric::DrawHeaderButtonContents(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags, + wxHeaderButtonParams* params) +{ + // Mark this item as selected. For the generic version we'll just draw an + // underline + if ( flags & wxCONTROL_SELECTED ) + { + // draw a line at the bottom of the header button, overlaying the + // native hot-tracking line (on XP) + const int penwidth = 3; + int y = rect.y + rect.height + 1 - penwidth; + wxColour c = (params && params->m_selectionColour.Ok()) ? + params->m_selectionColour : wxColour(0x66, 0x66, 0x66); + wxPen pen(c, penwidth); + pen.SetCap(wxCAP_BUTT); + dc.SetPen(pen); + dc.DrawLine(rect.x, y, rect.x + rect.width, y); + } + + // Draw an up or down arrow + int arrowSpace = 0; + if (flags & (wxCONTROL_UPICON | wxCONTROL_DOWNICON) ) + { + wxRect ar = rect; + + // make a rect for the arrow + ar.height = 4; + ar.width = 8; + ar.y += (rect.height - ar.height)/2; + ar.x = ar.x + rect.width - 3*ar.width/2; + arrowSpace = 3*ar.width/2; // space to preserve when drawing the label + + wxPoint triPt[3]; + if ( flags & wxCONTROL_UPICON ) + { + triPt[0].x = ar.width / 2; + triPt[0].y = 0; + triPt[1].x = ar.width; + triPt[1].y = ar.height; + triPt[2].x = 0; + triPt[2].y = ar.height; + } + else + { + triPt[0].x = 0; + triPt[0].y = 0; + triPt[1].x = ar.width; + triPt[1].y = 0; + triPt[2].x = ar.width / 2; + triPt[2].y = ar.height; + } + + wxColour c = (params && params->m_arrowColour.Ok()) ? + params->m_arrowColour : wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW); + dc.SetPen(wxPen(c)); + dc.SetBrush(wxBrush(c)); + dc.DrawPolygon( 3, triPt, ar.x, ar.y); + } + + const int margin = 5; // number of pixels to reserve on either side of the label + int bmpWidth = 0; + int txtEnd = 0; + + if ( params && params->m_labelBitmap.Ok() ) + bmpWidth = params->m_labelBitmap.GetWidth() + 2; + + // Draw a label if one is given + if ( params && !params->m_labelText.empty() ) + { + wxFont font = params->m_labelFont.Ok() ? + params->m_labelFont : win->GetFont(); + wxColour clr = params->m_labelColour.Ok() ? + params->m_labelColour : win->GetForegroundColour(); + + wxString label( params->m_labelText ); + + dc.SetFont(font); + dc.SetTextForeground(clr); + dc.SetBackgroundMode(wxTRANSPARENT); + + int tw, th, td, x, y; + dc.GetTextExtent( label, &tw, &th, &td); + y = rect.y + wxMax(0, (rect.height - (th+td)) / 2); + + // truncate and add an ellipsis (...) if the text is too wide. + int targetWidth = rect.width - arrowSpace - bmpWidth - 2*margin; + if ( tw > targetWidth ) + { + int ellipsisWidth; + dc.GetTextExtent( wxT("..."), &ellipsisWidth, NULL); + do { + label.Truncate( label.length() - 1 ); + dc.GetTextExtent( label, &tw, &th); + } while (tw + ellipsisWidth > targetWidth && label.length() ); + label.append( wxT("...") ); + tw += ellipsisWidth; + } + + switch (params->m_labelAlignment) + { + default: + case wxALIGN_LEFT: + x = rect.x + margin; + break; + case wxALIGN_CENTER: + x = rect.x + wxMax(0, (rect.width - arrowSpace - tw - bmpWidth)/2); + break; + case wxALIGN_RIGHT: + x = rect.x + wxMax(0, rect.width - arrowSpace - margin - tw - bmpWidth); + break; + } + + dc.DrawText(label, x, y); + txtEnd = x + tw + 2; + } + + // draw the bitmap if there is one + if ( params && params->m_labelBitmap.Ok() ) + { + int w, h, x, y; + w = params->m_labelBitmap.GetWidth(); + h = params->m_labelBitmap.GetHeight(); + + y = rect.y + wxMax(1, (rect.height - h) / 2); + + // if there is a text label, then put the bitmap at the end of the label + if ( txtEnd != 0 ) + { + x = txtEnd; + } + // otherwise use the alignment flags + else + { + switch (params->m_labelAlignment) + { + default: + case wxALIGN_LEFT: + x = rect.x + margin; + break; + case wxALIGN_CENTER: + x = rect.x + wxMax(1, (rect.width - arrowSpace - w)/2); + break; + case wxALIGN_RIGHT: + x = rect.x + wxMax(1, rect.width - arrowSpace - margin - w); + break; + } + } + dc.DrawBitmap(params->m_labelBitmap, x, y, true); + } +} + + +int wxRendererGeneric::GetHeaderButtonHeight(wxWindow *win) +{ + // Copied and adapted from src/generic/listctrl.cpp + const int HEADER_OFFSET_Y = 1; + const int EXTRA_HEIGHT = 4; + + int w=0, h=14, d=0; + if (win) + win->GetTextExtent(wxT("Hg"), &w, &h, &d); + + return h + d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT; +} + + // draw the plus or minus sign void wxRendererGeneric::DrawTreeItemButton(wxWindow * WXUNUSED(win), diff --git a/src/gtk/renderer.cpp b/src/gtk/renderer.cpp index f1cd95c856..b5200d0d35 100644 --- a/src/gtk/renderer.cpp +++ b/src/gtk/renderer.cpp @@ -46,7 +46,8 @@ public: virtual void DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags = 0); + int flags = 0, + wxHeaderButtonParams* params = NULL); // draw the expanded/collapsed icon for a tree control item virtual void DrawTreeItemButton(wxWindow *win, @@ -183,7 +184,8 @@ void wxRendererGTK::DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags) + int flags, + wxHeaderButtonParams* params) { GtkWidget *button = GetButtonWidget(); @@ -201,6 +203,8 @@ wxRendererGTK::DrawHeaderButton(wxWindow *win, "button", dc.LogicalToDeviceX(rect.x), rect.y, rect.width, rect.height ); + + DrawHeaderButtonContents(win, dc, rect, flags, params); } // draw a ">" or "v" button diff --git a/src/mac/carbon/renderer.cpp b/src/mac/carbon/renderer.cpp index ad3be942d7..ee4ec9022d 100644 --- a/src/mac/carbon/renderer.cpp +++ b/src/mac/carbon/renderer.cpp @@ -36,8 +36,11 @@ public: virtual void DrawHeaderButton( wxWindow *win, wxDC& dc, const wxRect& rect, - int flags = 0 ); + int flags = 0, + wxHeaderButtonParams* params = NULL ); + virtual int GetHeaderButtonHeight(wxWindow *win); + // draw the expanded/collapsed icon for a tree control item virtual void DrawTreeItemButton( wxWindow *win, wxDC& dc, @@ -125,14 +128,11 @@ wxRendererNative& wxRendererNative::GetDefault() void wxRendererMac::DrawHeaderButton( wxWindow *win, wxDC& dc, const wxRect& rect, - int flags ) + int flags, + wxHeaderButtonParams* params ) { - int major, minor; - - wxGetOsVersion( &major, &minor ); - - const wxCoord x = dc.XLOG2DEV(rect.x - 1); - const wxCoord y = dc.YLOG2DEV(rect.y - 1); + const wxCoord x = dc.XLOG2DEV(rect.x /*- 1*/); + const wxCoord y = dc.YLOG2DEV(rect.y /*- 1*/); const wxCoord w = dc.XLOG2DEVREL(rect.width); const wxCoord h = dc.YLOG2DEVREL(rect.height); @@ -179,11 +179,28 @@ void wxRendererMac::DrawHeaderButton( wxWindow *win, memset( &drawInfo, 0, sizeof(drawInfo) ); drawInfo.version = 0; - drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive; drawInfo.kind = kThemeListHeaderButton; - drawInfo.value = 0; + drawInfo.state = (flags & wxCONTROL_DISABLED) ? kThemeStateInactive : kThemeStateActive; + drawInfo.value = (flags & wxCONTROL_SELECTED) ? kThemeButtonOn : kThemeButtonOff; drawInfo.adornment = kThemeAdornmentNone; + + // The down arrow is drawn automatically, change it to an up arrow if needed. + if ( flags & wxCONTROL_UPICON ) + drawInfo.adornment = kThemeAdornmentHeaderButtonSortUp; + HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect ); + + // If we don't want any arrows we need to draw over the one already there + if ( (flags & wxCONTROL_SELECTED) && !(flags & (wxCONTROL_UPICON|wxCONTROL_DOWNICON)) ) + { + // clip to the header rectangle + CGContextSaveGState( cgContext ); + CGContextClipToRect( cgContext, headerRect ); + // but draw bigger than that so the arrow will get clipped off + headerRect.size.width += 25; + HIThemeDrawButton( &headerRect, &drawInfo, cgContext, kHIThemeOrientationNormal, &labelRect ); + CGContextRestoreGState( cgContext ); + } } #if wxMAC_USE_CORE_GRAPHICS @@ -191,8 +208,34 @@ void wxRendererMac::DrawHeaderButton( wxWindow *win, QDEndCGContext( (CGrafPtr) dc.m_macPort, &cgContext ); #endif } + + // Reserve room for the arrows before writing the label, and turn off the + // flags we've already handled + wxRect newRect(rect); + if ( (flags & wxCONTROL_SELECTED) && (flags & (wxCONTROL_UPICON|wxCONTROL_DOWNICON)) ) + { + newRect.width -= 12; + } + flags &= ~(wxCONTROL_SELECTED | wxCONTROL_UPICON | wxCONTROL_DOWNICON); + + DrawHeaderButtonContents(win, dc, newRect, flags, params); +} + + +int wxRendererMac::GetHeaderButtonHeight(wxWindow* WXUNUSED(win)) +{ + SInt32 standardHeight; + OSStatus errStatus; + + errStatus = GetThemeMetric( kThemeMetricListHeaderHeight, &standardHeight ); + if (errStatus == noErr) + { + return standardHeight; + } + return -1; } + void wxRendererMac::DrawTreeItemButton( wxWindow *win, wxDC& dc, const wxRect& rect, diff --git a/src/msw/renderer.cpp b/src/msw/renderer.cpp index 0ec34ab54c..15f86caaa3 100644 --- a/src/msw/renderer.cpp +++ b/src/msw/renderer.cpp @@ -68,6 +68,12 @@ #define HIS_NORMAL 1 #define HIS_HOT 2 #define HIS_PRESSED 3 + + #define TMT_HEIGHT 2417 + + #define HP_HEADERSORTARROW 4 + #define HSAS_SORTEDUP 1 + #define HSAS_SORTEDDOWN 2 #endif #if defined(__WXWINCE__) && !defined(DFCS_FLAT) @@ -113,9 +119,12 @@ public: static wxRendererNative& Get(); virtual void DrawHeaderButton(wxWindow *win, - wxDC& dc, - const wxRect& rect, - int flags = 0); + wxDC& dc, + const wxRect& rect, + int flags = 0, + wxHeaderButtonParams* params = NULL); + virtual int GetHeaderButtonHeight(wxWindow *win); + virtual void DrawTreeItemButton(wxWindow *win, wxDC& dc, const wxRect& rect, @@ -276,12 +285,13 @@ void wxRendererXP::DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags) + int flags, + wxHeaderButtonParams* params) { wxUxThemeHandle hTheme(win, L"HEADER"); if ( !hTheme ) { - m_rendererNative.DrawHeaderButton(win, dc, rect, flags); + m_rendererNative.DrawHeaderButton(win, dc, rect, flags, params); return; } @@ -304,8 +314,41 @@ wxRendererXP::DrawHeaderButton(wxWindow *win, &r, NULL ); + + // NOTE: Using the theme to draw HP_HEADERSORTARROW doesn't do anything. + // Why? If this can be fixed then draw the sort arrows using the theme + // and then clear those flags before calling DrawHeaderButtonContents. + + // Add any extras that are specified in flags and params + DrawHeaderButtonContents(win, dc, rect, flags, params); +} + + +int +wxRendererXP::GetHeaderButtonHeight(wxWindow *win) +{ + wxUxThemeHandle hTheme(win, L"HEADER"); + if ( !hTheme ) + { + return m_rendererNative.GetHeaderButtonHeight(win); + } + + HRESULT hr; + int value = -1; + + hr = wxUxThemeEngine::Get()->GetThemeMetric( hTheme, + NULL, + HP_HEADERITEM, + HIS_NORMAL, + TMT_HEIGHT, + &value ); + if ( hr == S_OK ) + return value; + else + return 20; } + void wxRendererXP::DrawTreeItemButton(wxWindow *win, wxDC& dc, diff --git a/wxPython/src/_renderer.i b/wxPython/src/_renderer.i index 7c0754a968..705abbba80 100644 --- a/wxPython/src/_renderer.i +++ b/wxPython/src/_renderer.i @@ -34,8 +34,10 @@ enum wxCONTROL_CHECKED = 0x00000040, // (check/radio button) is checked wxCONTROL_CHECKABLE = 0x00000080, // (menu) item can be checked wxCONTROL_UNDETERMINED = wxCONTROL_CHECKABLE, // (check) undetermined state + wxCONTROL_UPICON = 0x00000100, // header button has an up arrow icon + wxCONTROL_DOWNICON = 0x00000200, // header button has a down arrow icon - wxCONTROL_FLAGS_MASK = 0x000000ff, + wxCONTROL_FLAGS_MASK = 0x000002ff, // this is a pseudo flag not used directly by wxRenderer but rather by some // controls internally @@ -73,6 +75,29 @@ struct wxSplitterRenderParams +DocStr(wxHeaderButtonParams, +"Extra (optional) parameters for `wx.RendererNative.DrawHeaderButton`", ""); + +struct wxHeaderButtonParams +{ + wxHeaderButtonParams(); + ~wxHeaderButtonParams(); + + // So wxColour_helper will be used when assigning to the colour items in the struct + %typemap(in) wxColour* (wxColour temp) { + $1 = &temp; + if ( ! wxColour_helper($input, &$1)) SWIG_fail; + } + wxColour m_arrowColour; + wxColour m_selectionColour; + wxString m_labelText; + wxFont m_labelFont; + wxColour m_labelColour; + wxBitmap m_labelBitmap; + int m_labelAlignment; +}; + + DocStr(wxRendererVersion, "This simple struct represents the `wx.RendererNative` interface @@ -102,11 +127,12 @@ struct wxRendererVersion DocStr(wxRendererNative, -"One of the design principles of wxWidgets is to use the native widgets -on every platform in order to be as close to the native look and feel -on every platform. However there are still cases when some generic -widgets are needed for various reasons, but it can sometimes take a -lot of messy work to make them conform to the native LnF. +"One of the design principles of wxWidgets is to use the native +widgets on every platform in order to be as close as possible to +the native look and feel on every platform. However there are +still cases when some generic widgets are needed for various +reasons, but it can sometimes take a lot of messy work to make +them conform to the native LnF. The wx.RendererNative class is a collection of functions that have platform-specific implementations for drawing certain parts of @@ -128,12 +154,27 @@ public: virtual void , DrawHeaderButton(wxWindow *win, wxDC& dc, const wxRect& rect, - int flags = 0), + int flags = 0, + wxHeaderButtonParams* params=NULL), "Draw the header control button (such as what is used by `wx.ListCtrl` in report mode.)", ""); - + DocDeclStr( + virtual void , DrawHeaderButtonContents(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags = 0, + wxHeaderButtonParams* params=NULL), + "Draw the contents of a header control button, (label, sort +arrows, etc.) Normally this is only called by `DrawHeaderButton`.", ""); + + DocDeclStr( + virtual int , GetHeaderButtonHeight(wxWindow *win), + "Returns the default height of a header button, either a fixed platform +height if available, or a generic height based on the window's font.", ""); + + DocDeclStr( virtual void , DrawTreeItemButton(wxWindow *win, wxDC& dc,