Merge branch 'misc-render-fixes' of https://github.com/discnl/wxWidgets

Fixes for rendering text with bottom/right alignment for wxMSW and not
only.

See https://github.com/wxWidgets/wxWidgets/pull/2446
This commit is contained in:
Vadim Zeitlin
2021-08-15 18:57:04 +02:00
9 changed files with 346 additions and 44 deletions

View File

@@ -71,7 +71,8 @@ public:
void BuildDataViewCtrl(wxPanel* parent,
unsigned int nPanel,
unsigned long style = 0);
unsigned long style = 0,
int modelFlags = wxALIGN_CENTRE);
private:
// event handlers
@@ -167,6 +168,13 @@ private:
enum Lang { Lang_English, Lang_French };
void FillIndexList(Lang lang);
// Helper for checking ModelFlags of current panel (either currently
// building or selected one).
bool HasModelFlag(int flag) const
{
return (m_modelFlags[m_currentPanel] & flag) != 0;
}
// HasValue page.
void OnHasValueValueChanged(wxDataViewEvent& event);
@@ -186,7 +194,9 @@ private:
Page_Max
};
unsigned int m_currentPanel;
wxDataViewCtrl* m_ctrl[Page_Max];
int m_modelFlags[Page_Max];
// Some of the models associated with the controls:
@@ -406,6 +416,18 @@ enum
ID_HORIZ_RULES,
ID_VERT_RULES,
ID_ALIGN_LEFT,
ID_ALIGN_CENTRE_H,
ID_ALIGN_RIGHT,
ID_ALIGN_TOP,
ID_ALIGN_CENTRE_V,
ID_ALIGN_BOTTOM,
ID_TOGGLE_USE_TALL_ROWS,
ID_TOGGLE_KEEP_LOGO_SMALL,
ID_TOGGLE_USE_MULTI_LINE_TEXT,
ID_EXIT = wxID_EXIT,
// about menu
@@ -449,7 +471,8 @@ enum
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU_RANGE( ID_MULTIPLE, ID_VERT_RULES, MyFrame::OnStyleChange )
EVT_MENU_RANGE( ID_MULTIPLE, ID_TOGGLE_USE_MULTI_LINE_TEXT,
MyFrame::OnStyleChange )
EVT_MENU( ID_EXIT, MyFrame::OnQuit )
EVT_MENU( ID_ABOUT, MyFrame::OnAbout )
EVT_MENU( ID_CLEARLOG, MyFrame::OnClearLog )
@@ -552,6 +575,23 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int
style_menu->AppendCheckItem(ID_HORIZ_RULES, "Display horizontal rules");
style_menu->AppendCheckItem(ID_VERT_RULES, "Display vertical rules");
wxMenu* align_menu = new wxMenu;
align_menu->AppendRadioItem(ID_ALIGN_LEFT, "Left\tCtrl-1");
align_menu->AppendRadioItem(ID_ALIGN_CENTRE_H, "Centre Horizontal\tCtrl-2");
align_menu->AppendRadioItem(ID_ALIGN_RIGHT, "Right\tCtrl-3");
align_menu->AppendSeparator();
align_menu->AppendRadioItem(ID_ALIGN_TOP, "Top\tCtrl-4");
align_menu->AppendRadioItem(ID_ALIGN_CENTRE_V, "Centre Vertical\tCtrl-5");
align_menu->AppendRadioItem(ID_ALIGN_BOTTOM, "Bottom\tCtrl-6");
wxMenu* size_menu = new wxMenu;
size_menu->AppendCheckItem(ID_TOGGLE_USE_TALL_ROWS,
"Use Tall Rows\tCtrl-7");
size_menu->AppendCheckItem(ID_TOGGLE_KEEP_LOGO_SMALL,
"Keep Logo Size Small\tCtrl-8");
size_menu->AppendCheckItem(ID_TOGGLE_USE_MULTI_LINE_TEXT,
"Use Multi-line Text\tCtrl-9");
wxMenu *file_menu = new wxMenu;
file_menu->Append(ID_CLEARLOG, "&Clear log\tCtrl-L");
file_menu->Append(ID_GET_PAGE_INFO, "Show current &page info");
@@ -563,6 +603,8 @@ MyFrame::MyFrame(wxFrame *frame, const wxString &title, int x, int y, int w, int
file_menu->AppendCheckItem(ID_CUSTOM_HEADER_HEIGHT, "Custom header &height");
#endif // wxHAS_GENERIC_DATAVIEWCTRL
file_menu->Append(ID_STYLE_MENU, "&Style", style_menu);
file_menu->Append(wxID_ANY, "&Alignment", align_menu);
file_menu->Append(wxID_ANY, "Si&ze", size_menu);
file_menu->Append(ID_INC_INDENT, "&Increase indent\tCtrl-I");
file_menu->Append(ID_DEC_INDENT, "&Decrease indent\tShift-Ctrl-I");
file_menu->AppendSeparator();
@@ -749,10 +791,14 @@ MyFrame::~MyFrame()
delete wxLog::SetActiveTarget(m_logOld);
}
void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned long style)
void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel,
unsigned long style, int modelFlags)
{
wxASSERT(!m_ctrl[nPanel]); // should only be initialized once
m_currentPanel = nPanel;
m_modelFlags[nPanel] = modelFlags;
switch (nPanel)
{
case Page_Music:
@@ -844,7 +890,7 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l
m_ctrl[Page_List] = new wxDataViewCtrl( parent, ID_ATTR_CTRL, wxDefaultPosition,
wxDefaultSize, style );
m_list_model = new MyListModel;
m_list_model = new MyListModel(modelFlags);
m_ctrl[Page_List]->AssociateModel( m_list_model.get() );
wxDataViewColumn* const colCheckIconText = new wxDataViewColumn
@@ -855,13 +901,17 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l
wxCOL_WIDTH_AUTOSIZE
);
m_ctrl[Page_List]->AppendColumn(colCheckIconText);
const int alignment = modelFlags & wxALIGN_MASK;
colCheckIconText->GetRenderer()->SetAlignment(alignment);
m_ctrl[Page_List]->AppendTextColumn("editable string",
wxDataViewColumn* const colEditable =
m_ctrl[Page_List]->AppendTextColumn("editable string",
MyListModel::Col_EditableText,
wxDATAVIEW_CELL_EDITABLE,
wxCOL_WIDTH_AUTOSIZE,
wxALIGN_NOT,
wxDATAVIEW_COL_SORTABLE);
colEditable->GetRenderer()->SetAlignment(alignment);
m_ctrl[Page_List]->AppendDateColumn("date",
MyListModel::Col_Date);
@@ -936,7 +986,11 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l
wxDefaultSize, style | wxDV_NO_HEADER );
m_ctrl[Page_TreeStore] = tc;
wxImageList *ilist = new wxImageList( 16, 16 );
const bool useDefaultSize = !HasModelFlag(MODEL_USE_TALL_ROWS)
|| HasModelFlag(MODEL_KEEP_LOGO_SMALL);
const int imageSize = useDefaultSize ? 16 : 32;
wxImageList *ilist = new wxImageList( imageSize, imageSize );
ilist->Add( wxIcon(wx_small_xpm) );
tc->AssignImageList( ilist );
@@ -1045,6 +1099,8 @@ void MyFrame::BuildDataViewCtrl(wxPanel* parent, unsigned int nPanel, unsigned l
break;
}
if ( HasModelFlag(MODEL_USE_TALL_ROWS) )
m_ctrl[nPanel]->SetRowHeight(32);
}
@@ -1158,6 +1214,7 @@ void MyFrame::OnDecIndent(wxCommandEvent& WXUNUSED(event))
void MyFrame::OnPageChanged( wxBookCtrlEvent& WXUNUSED(event) )
{
unsigned int nPanel = m_notebook->GetSelection();
m_currentPanel = nPanel;
GetMenuBar()->FindItem(ID_STYLE_MENU)->SetItemLabel(
wxString::Format("Style of panel #%d", nPanel+1));
@@ -1189,6 +1246,49 @@ void MyFrame::OnPageChanged( wxBookCtrlEvent& WXUNUSED(event) )
GetMenuBar()->FindItem(id)->Check( m_ctrl[nPanel]->HasFlag(style) );
}
const int modelFlags = m_modelFlags[nPanel];
for (unsigned int id = ID_ALIGN_LEFT; id <= ID_ALIGN_BOTTOM; ++id)
{
int align = wxALIGN_NOT;
bool check = false;
switch (id)
{
case ID_ALIGN_LEFT:
check = !(modelFlags & (wxALIGN_CENTRE_HORIZONTAL | wxALIGN_RIGHT));
break;
case ID_ALIGN_CENTRE_H:
align = wxALIGN_CENTRE_HORIZONTAL;
break;
case ID_ALIGN_RIGHT:
align = wxALIGN_RIGHT;
break;
case ID_ALIGN_TOP:
check = !(modelFlags & (wxALIGN_CENTRE_VERTICAL | wxALIGN_BOTTOM));
break;
case ID_ALIGN_CENTRE_V:
align = wxALIGN_CENTRE_VERTICAL;
break;
case ID_ALIGN_BOTTOM:
align = wxALIGN_BOTTOM;
break;
default:
wxFAIL;
}
if ( align != wxALIGN_NOT )
check = (modelFlags & align) != 0;
GetMenuBar()->FindItem(id)->Check(check);
}
GetMenuBar()->FindItem(ID_TOGGLE_USE_TALL_ROWS)->Check(
HasModelFlag(MODEL_USE_TALL_ROWS));
GetMenuBar()->FindItem(ID_TOGGLE_KEEP_LOGO_SMALL)->Check(
HasModelFlag(MODEL_KEEP_LOGO_SMALL));
GetMenuBar()->FindItem(ID_TOGGLE_USE_MULTI_LINE_TEXT)->Check(
HasModelFlag(MODEL_USE_MULTI_LINE_TEXT));
GetMenuBar()->FindItem(ID_DISABLE)->Check(!m_ctrl[nPanel]->IsEnabled());
}
@@ -1223,8 +1323,26 @@ void MyFrame::OnStyleChange( wxCommandEvent& WXUNUSED(event) )
else if (nPanel == 4)
m_long_music_model.reset(NULL);
int flags = 0;
if ( GetMenuBar()->FindItem(ID_ALIGN_CENTRE_H)->IsChecked() )
flags |= wxALIGN_CENTRE_HORIZONTAL;
if ( GetMenuBar()->FindItem(ID_ALIGN_RIGHT)->IsChecked() )
flags |= wxALIGN_RIGHT;
if ( GetMenuBar()->FindItem(ID_ALIGN_CENTRE_V)->IsChecked() )
flags |= wxALIGN_CENTRE_VERTICAL;
if ( GetMenuBar()->FindItem(ID_ALIGN_BOTTOM)->IsChecked() )
flags |= wxALIGN_BOTTOM;
if ( GetMenuBar()->FindItem(ID_TOGGLE_USE_TALL_ROWS)->IsChecked() )
flags |= MODEL_USE_TALL_ROWS;
if ( GetMenuBar()->FindItem(ID_TOGGLE_KEEP_LOGO_SMALL)->IsChecked() )
flags |= MODEL_KEEP_LOGO_SMALL;
if ( GetMenuBar()->FindItem(ID_TOGGLE_USE_MULTI_LINE_TEXT)->IsChecked() )
flags |= MODEL_USE_MULTI_LINE_TEXT;
// rebuild the DVC for the selected panel:
BuildDataViewCtrl((wxPanel*)m_notebook->GetPage(nPanel), nPanel, style);
BuildDataViewCtrl((wxPanel*)m_notebook->GetPage(nPanel),
nPanel, style, flags);
sz->Prepend(m_ctrl[nPanel], 1, wxGROW|wxALL, 5);
sz->Layout();

View File

@@ -339,9 +339,12 @@ static int my_sort( int *v1, int *v2 )
#define INITIAL_NUMBER_OF_ITEMS 10000
MyListModel::MyListModel() :
MyListModel::MyListModel(int modelFlags) :
wxDataViewVirtualListModel( INITIAL_NUMBER_OF_ITEMS )
{
const wxString multiLineText = L"top (\u1ED6)\ncentre\nbottom (g)";
const bool useMultiLine = (modelFlags & MODEL_USE_MULTI_LINE_TEXT) != 0;
// the first 100 items are really stored in this model;
// all the others are synthesized on request
static const unsigned NUMBER_REAL_ITEMS = 100;
@@ -349,17 +352,32 @@ MyListModel::MyListModel() :
m_toggleColValues.reserve(NUMBER_REAL_ITEMS);
m_textColValues.reserve(NUMBER_REAL_ITEMS);
m_toggleColValues.push_back(false);
m_textColValues.push_back("first row with long label to test ellipsization");
m_textColValues.push_back(useMultiLine
? multiLineText
: wxString("first row with long label to test ellipsization"));
for (unsigned int i = 1; i < NUMBER_REAL_ITEMS; i++)
{
m_toggleColValues.push_back(false);
m_textColValues.push_back(wxString::Format("real row %d", i));
}
m_iconColValues.assign(NUMBER_REAL_ITEMS, "test");
m_iconColValues.assign(NUMBER_REAL_ITEMS,
useMultiLine ? multiLineText : wxString("test"));
m_icon[0] = wxIcon( null_xpm );
m_icon[1] = wxIcon( wx_small_xpm );
const int newSize = m_icon[0].GetWidth() * 2;
const bool useTallRows = (modelFlags & MODEL_USE_TALL_ROWS) != 0;
if ( useTallRows )
m_icon[0].CopyFromBitmap(
wxImage(null_xpm).Rescale(newSize, newSize));
if ( !useTallRows || (modelFlags & MODEL_KEEP_LOGO_SMALL) )
m_icon[1] = wxIcon( wx_small_xpm );
else
m_icon[1].CopyFromBitmap(
wxImage(wx_small_xpm).Rescale(newSize, newSize));
}
void MyListModel::Prepend( const wxString &text )

View File

@@ -216,7 +216,7 @@ public:
Col_Max
};
MyListModel();
MyListModel(int modelFlags);
// helper methods to change the model
@@ -310,3 +310,10 @@ private:
wxDECLARE_NO_COPY_CLASS(MyIndexListModel);
};
enum ModelFlags
{
MODEL_USE_TALL_ROWS = 1 << 0,
MODEL_KEEP_LOGO_SMALL = 1 << 1,
MODEL_USE_MULTI_LINE_TEXT = 1 << 2
};

View File

@@ -246,6 +246,26 @@ private:
dc.DrawText("Using flags: " + flagsString, x1, y);
y += lineHeight*3;
const wxCoord heightListItem = FromDIP(48);
const wxCoord widthListItem = 30*GetCharWidth();
{
dc.DrawText("DrawItemText() alignment", x1, y);
wxRect textRect(x2, y, widthListItem, heightListItem);
wxDCBrushChanger setBrush(dc, *wxTRANSPARENT_BRUSH);
wxDCPenChanger setPen(dc, *wxGREEN_PEN);
dc.DrawRectangle(textRect);
renderer.DrawItemText(this, dc, L"Top Left (\u1ED6)", textRect);
renderer.DrawItemText(this, dc, "Bottom right", textRect,
wxALIGN_BOTTOM | wxALIGN_RIGHT);
y += lineHeight + heightListItem;
}
const wxCoord heightHdr = renderer.GetHeaderButtonHeight(this);
const wxCoord width = 15*GetCharWidth();
@@ -288,9 +308,15 @@ private:
wxRect(wxPoint(x2, y), sizeMark), m_flags);
y += lineHeight + sizeMark.y;
const wxString notImplementedText = "(generic version unimplemented)";
dc.DrawText("DrawRadioBitmap()", x1, y);
renderer.DrawRadioBitmap(this, dc,
wxRect(wxPoint(x2, y), sizeCheck), m_flags);
if ( m_useGeneric )
dc.DrawText(notImplementedText, x2, y);
else
renderer.DrawRadioBitmap(this, dc,
wxRect(wxPoint(x2, y), sizeCheck), m_flags);
y += lineHeight + sizeCheck.y;
dc.DrawText("DrawCollapseButton()", x1, y);
@@ -340,9 +366,6 @@ private:
y += lineHeight + heightGauge;
const wxCoord heightListItem = FromDIP(48);
const wxCoord widthListItem = 30*GetCharWidth();
dc.DrawText("DrawItemSelectionRect()", x1, y);
renderer.DrawItemSelectionRect(this, dc,
wxRect(x2, y, widthListItem, heightListItem), m_flags | wxCONTROL_SELECTED);
@@ -354,8 +377,11 @@ private:
y += lineHeight;
dc.DrawText("DrawChoice()", x1, y);
renderer.DrawChoice(this, dc,
wxRect(x2, y, width, 1.5*GetCharHeight()), m_flags);
if ( m_useGeneric )
dc.DrawText(notImplementedText, x2, y);
else
renderer.DrawChoice(this, dc,
wxRect(x2, y, width, 1.5*GetCharHeight()), m_flags);
}
int m_flags;

View File

@@ -32,6 +32,10 @@
#include "wx/access.h"
#endif // wxUSE_ACCESSIBILITY
// Uncomment this line to, for custom renderers, visually show the extent
// of both a cell and its item.
//#define DEBUG_RENDER_EXTENTS
const char wxDataViewCtrlNameStr[] = "dataviewCtrl";
namespace
@@ -1035,6 +1039,20 @@ wxDataViewCustomRendererBase::WXCallRender(wxRect rectCell, wxDC *dc, int state)
if ( m_attr.HasFont() )
changeFont.Set(m_attr.GetEffectiveFont(dc->GetFont()));
#ifdef DEBUG_RENDER_EXTENTS
{
wxDCBrushChanger changeBrush(*dc, *wxTRANSPARENT_BRUSH);
wxDCPenChanger changePen(*dc, *wxRED);
dc->DrawRectangle(rectCell);
dc->SetPen(*wxGREEN);
dc->DrawRectangle(rectItem);
}
#endif
Render(rectItem, dc, state);
}
@@ -1072,17 +1090,12 @@ wxDataViewCustomRendererBase::RenderText(const wxString& text,
if ( !(GetOwner()->GetOwner()->IsEnabled() && GetEnabled()) )
flags |= wxCONTROL_DISABLED;
// Notice that we intentionally don't use any alignment here: it is not
// necessary because the cell rectangle had been already adjusted to
// account for the alignment in WXCallRender() and using the alignment here
// results in problems with ellipsization when using native MSW renderer,
// see https://trac.wxwidgets.org/ticket/17363, so just don't do it.
wxRendererNative::Get().DrawItemText(
GetOwner()->GetOwner(),
*dc,
text,
rectText,
wxALIGN_NOT,
GetEffectiveAlignment(),
flags,
GetEllipsizeMode());
}

View File

@@ -1196,7 +1196,7 @@ void wxDC::DrawLabel(const wxString& text,
wxCoord x, y;
if ( alignment & wxALIGN_RIGHT )
{
x = rect.GetRight() - width;
x = rect.GetRight() - width + 1;
}
else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
{
@@ -1209,7 +1209,7 @@ void wxDC::DrawLabel(const wxString& text,
if ( alignment & wxALIGN_BOTTOM )
{
y = rect.GetBottom() - height;
y = rect.GetBottom() - height + 1;
}
else if ( alignment & wxALIGN_CENTRE_VERTICAL )
{

View File

@@ -1279,13 +1279,13 @@ void wxGCDCImpl::DoGetTextExtent( const wxString &str, wxCoord *width, wxCoord *
);
if ( height )
*height = (wxCoord)wxRound(h);
*height = (wxCoord)ceil(h);
if ( descent )
*descent = (wxCoord)wxRound(d);
*descent = (wxCoord)ceil(d);
if ( externalLeading )
*externalLeading = (wxCoord)wxRound(e);
*externalLeading = (wxCoord)ceil(e);
if ( width )
*width = (wxCoord)wxRound(w);
*width = (wxCoord)ceil(w);
if ( theFont )
{

View File

@@ -1043,7 +1043,10 @@ void wxRendererXP::DrawItemText(wxWindow* win,
const int itemState = GetListItemState(flags);
typedef HRESULT(__stdcall *DrawThemeTextEx_t)(HTHEME, HDC, int, int, const wchar_t *, int, DWORD, RECT *, const WXDTTOPTS *);
typedef HRESULT(__stdcall *GetThemeTextExtent_t)(HTHEME, HDC, int, int, const wchar_t *, int, DWORD, RECT *, RECT *);
static DrawThemeTextEx_t s_DrawThemeTextEx = NULL;
static GetThemeTextExtent_t s_GetThemeTextExtent = NULL;
static bool s_initDone = false;
if ( !s_initDone )
@@ -1052,12 +1055,13 @@ void wxRendererXP::DrawItemText(wxWindow* win,
{
wxLoadedDLL dllUxTheme(wxS("uxtheme.dll"));
wxDL_INIT_FUNC(s_, DrawThemeTextEx, dllUxTheme);
wxDL_INIT_FUNC(s_, GetThemeTextExtent, dllUxTheme);
}
s_initDone = true;
}
if ( s_DrawThemeTextEx && // Might be not available if we're under XP
if ( s_DrawThemeTextEx && // Not available under XP
::IsThemePartDefined(hTheme, LVP_LISTITEM, 0) )
{
RECT rc = ConvertToRECT(dc, rect);
@@ -1082,23 +1086,139 @@ void wxRendererXP::DrawItemText(wxWindow* win,
textOpts.crText = textColour.GetPixel();
}
DWORD textFlags = DT_NOPREFIX;
const DWORD defTextFlags = DT_NOPREFIX;
DWORD textFlags = defTextFlags;
// Always use DT_* flags for horizontal alignment.
if ( align & wxALIGN_CENTER_HORIZONTAL )
textFlags |= DT_CENTER;
else if ( align & wxALIGN_RIGHT )
{
textFlags |= DT_RIGHT;
rc.right--; // Alignment is inconsistent with DrawLabel otherwise
}
else
textFlags |= DT_LEFT;
if ( align & wxALIGN_BOTTOM )
textFlags |= DT_BOTTOM;
else if ( align & wxALIGN_CENTER_VERTICAL )
textFlags |= DT_VCENTER;
else
textFlags |= DT_TOP;
/*
Bottom (DT_BOTTOM) and centered vertical (DT_VCENTER) alignment
are documented to be only used with the DT_SINGLELINE flag which
doesn't handle multi-lines. In case of drawing multi-lines with
such alignment use DT_TOP (0), which does work for multi-lines,
and deal with the actual desired vertical alignment ourselves with
the help of GetThemeTextExtent().
[TODO] Ideally text measurement should only be needed for the above
mentioned situations but because there can be a difference between
the extent from GetThemeTextExtent() and the rect received by this
function could have involved other text measurements (e.g. with wxDVC,
see #18487), use it in all cases for now.
*/
bool useTopDrawing = false;
if ( s_GetThemeTextExtent != NULL )
{
/*
Get the actual text extent using GetThemeTextExtent() and adjust
drawing rect if needed.
Note that DrawThemeTextEx() in combination with DT_CALCRECT
and DTT_CALCRECT can also be used to get the text extent.
This seems to always result in the exact same extent (checked
with an assert) as using GetThemeTextExtent(), despite having
an additional WXDTTOPTS argument for various effects.
Some effects have been tried (DTT_BORDERSIZE, DTT_SHADOWTYPE
and DTT_SHADOWOFFSET) and while rendered correctly with effects
the returned extent remains the same as without effects.
Official docs don't seem to prefer one method over the other
though a possibly outdated note for DrawThemeText() recommends
using GetThemeTextExtent(). Because Wine as of writing doesn't
support DT_CALCRECT with DrawThemeTextEx() while it does support
GetThemeTextExtent(), opt to use the latter.
*/
/*
It's important for the dwTextFlags parameter passed to
GetThemeTextExtent() not to have some DT_* flags because they
influence the extent size in unwanted ways: Using
DT_SINGLELINE combined with either DT_VCENTER or DT_BOTTOM
results in a height that can't be used (either halved or 0),
and having DT_END_ELLIPSIS ends up always ellipsizing.
Passing a non-NULL rect solves these problems but is not
really a good option as it doesn't make the rectangle extent
a tight fit and calculations would have to be done with large
numbers needlessly (provided the passed rect is set to
something like {0, 0, LONG_MAX, LONG_MAX} ).
*/
RECT rcExtent;
HRESULT hr = s_GetThemeTextExtent(hTheme, dc.GetHDC(),
LVP_LISTITEM, itemState, text.wchar_str(), -1,
defTextFlags, NULL, &rcExtent);
if ( SUCCEEDED(hr) )
{
/*
Compensate for rare cases where the horizontal extents differ
slightly. Don't use the width of the passed rect here to deal
with horizontal alignment as it results in the text always
fitting and ellipsization then can't occur. Instead check for
width differences by comparing with the extent as calculated
by wxDC.
*/
const int textWidthDc = dc.GetMultiLineTextExtent(text).x;
const int widthDiff = textWidthDc - rcExtent.right;
if ( widthDiff )
{
if ( align & wxALIGN_CENTRE_HORIZONTAL )
{
const int widthOffset = widthDiff / 2;
rc.left += widthOffset;
rc.right -= widthOffset;
}
else if ( align & wxALIGN_RIGHT )
rc.left += widthDiff;
else // left aligned
rc.right -= widthDiff;
}
/*
For height compare with the height of the passed rect and use
the difference for handling vertical alignment. This has
consequences for particularly multi-line text: it will now
always try to fit vertically while a rect received from wxDVC
may have its extent based on calculations for a single line
only and therefore couldn't show more than one line.
As a result of the expanded height clipping may be needed
which at least already happens with wxDVC which (out of
necessity) confines rendering to a cell's bounds.
*/
const int heightDiff = rect.GetHeight() - rcExtent.bottom;
if ( heightDiff
&& (align & (wxALIGN_BOTTOM | wxALIGN_CENTRE_VERTICAL))
&& text.Contains(wxS('\n')) )
{
useTopDrawing = true;
if ( align & wxALIGN_CENTRE_VERTICAL )
{
const int heightOffset = heightDiff / 2;
rc.top += heightOffset;
rc.bottom -= heightOffset;
}
else if ( align & wxALIGN_BOTTOM )
rc.top += heightDiff;
else // top aligned
rc.bottom -= heightDiff;
}
}
}
if ( !useTopDrawing )
{
if ( align & wxALIGN_BOTTOM )
textFlags |= DT_BOTTOM | DT_SINGLELINE;
else if ( align & wxALIGN_CENTER_VERTICAL )
textFlags |= DT_VCENTER | DT_SINGLELINE;
else
textFlags |= DT_TOP;
}
const wxString* drawText = &text;
wxString ellipsizedText;

View File

@@ -185,7 +185,7 @@ void wxControlRenderer::DrawBitmap(wxDC &dc,
{
if ( alignment & wxALIGN_RIGHT )
{
x = rect.GetRight() - width;
x = rect.GetRight() - width + 1;
}
else if ( alignment & wxALIGN_CENTRE )
{
@@ -198,7 +198,7 @@ void wxControlRenderer::DrawBitmap(wxDC &dc,
if ( alignment & wxALIGN_BOTTOM )
{
y = rect.GetBottom() - height;
y = rect.GetBottom() - height + 1;
}
else if ( alignment & wxALIGN_CENTRE_VERTICAL )
{