NR> I'm having an issue with wxStaticText on Windows where
NR> any text after a tab character is not displayed.
How can this be reproduced? This patch:
--- a/samples/minimal/minimal.cpp
+++ b/samples/minimal/minimal.cpp
@@ -172,6 +172,9 @@ MyFrame::MyFrame(const wxString& title)
CreateStatusBar(2);
SetStatusText("Welcome to wxWidgets!");
#endif // wxUSE_STATUSBAR
+
+ new wxStaticText(this, -1, "Just a label", wxPoint(10, 10));
+ new wxStaticText(this, -1, "Another\tone", wxPoint(10, 40));
}
works just fine for me: both labels are displayed correctly. Please make a
patch (please see http://trac.wxwidgets.org/wiki/HowToSubmitPatches)
allowing to see the problem.
Regards,
VZ
--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/
NR> > NR> I'm having an issue with wxStaticText on Windows where
NR> > NR> any text after a tab character is not displayed.
NR> >
NR> > How can this be reproduced?
NR>
NR> The following patch to minimal.cpp of wxWidgets 2.8.10:
NR>
NR> 73a74,77
NR> >
NR> > wxPanel* panel;
NR> > wxSizer* sizer;
NR> > wxStaticText* text;
NR> 174a179,186
NR> >
NR> > panel = new wxPanel(this);
NR> > sizer = new wxBoxSizer(wxVERTICAL);
NR> > text = new wxStaticText(panel, wxID_ANY, "Word\tword");
NR> > sizer->Add(text);
NR> > sizer->Layout();
NR> > panel->SetSizer(sizer);
NR> > sizer->SetSizeHints(panel);
NR>
NR> displays only the first "Word". The second "word" is not displayed.
Thanks, I can see and easily debug it now (which is why posting patches is
so useful -- just please make them unified ones if possible in the future).
The problem is due to a bug in wxDC::GetTextExtent() under MSW which
doesn't handle TABs correctly and the following patch, which should apply
to 2.8.10 too, should fix it:
diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp
index 675447f..eedf917 100644
--- a/src/msw/dc.cpp
+++ b/src/msw/dc.cpp
@@ -1710,9 +1710,10 @@ wxCoord wxMSWDCImpl::GetCharWidth() const
return lpTextMetric.tmAveCharWidth;
}
-void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y,
- wxCoord *descent, wxCoord *externalLeading,
- const wxFont *font) const
+void wxMSWDCImpl::DoGetTextExtent(const wxString& stringOrig,
+ wxCoord *x, wxCoord *y,
+ wxCoord *descent, wxCoord *externalLeading,
+ const wxFont *font) const
{
#ifdef __WXMICROWIN__
if (!GetHDC())
@@ -1737,6 +1738,14 @@ void wxMSWDCImpl::DoGetTextExtent(const wxString& string, wxCoord *x, wxCoord *y
hfontOld = 0;
}
+ // GetTextExtentPoint32() doesn't handle TABs correctly so expand them
+ // manually or the returned string would be too small
+ //
+ // notice that MSW allows changing the number of spaces per TAB (e.g. using
+ // DT_TABSTOP flag of DrawText()) but wx API does not so we can safely
+ // assume that TABs are always rendered as 8 spaces
+ wxString string(stringOrig);
+ string.Replace(wxT("\t"), wxT(" "));
SIZE sizeRect;
const size_t len = string.length();
if ( !::GetTextExtentPoint32(GetHdc(), string.wx_str(), len, &sizeRect) )
Please let me know if you still have any problems,
NR> I'm a bit new to patches, so please bear with me...
NR>
NR> I'm having some trouble applying the patch.
NR> I copied your patch to a text file patch.txt in the root folder
NR> of wxWidgets, and in the same folder ran:
NR> patch -p1 < patch.txt
NR>
NR> but it's giving me an error:
NR>
NR> patching file `src/msw/dc.cpp'
NR> patch: **** malformed patch at line 5: return lpTextMetric.tmAveCharWidth;
Sorry, I don't know what could be wrong. You apply it exactly as it's
supposed to be done. Maybe it got mangled in transit? Let me attach it as a
file this time.
NR> Also, what do you mean by unified?
Done with "diff -u" (or "svn diff").
Regards,
NR> OK, I tried with the file, and this time it performed the patch,
NR> but it still gave some errors:
NR>
NR> patching file `src/msw/dc.cpp'
NR> Hunk #1 FAILED at 1710.
NR> Hunk #2 succeeded at 1763 with fuzz 1 (offset 25 lines).
NR> 1 out of 2 hunks FAILED -- saving rejects to src/msw/dc.cpp.rej
If you read the patch you can see that it just renames one of the function
parameters and adds an extra function call, so you can easily apply it
manually. The reason it doesn't apply automatically is that wxDC class had
been renamed/refactored to wxMSWDCImpl in the trunk (sorry for forgetting
about this...) and so it can't rename the parameter automatically.
NR> However, it does not appear to solve the problem in general.
NR> If I add a second "word" before the tab, the third "word"
NR> now does not get displayed:
Oops, you're right, thanks for testing!
I naively thought Windows replaced TABs with 8 spaces but it doesn't do
this at all, it just happened to work with the test I used (which wasn't
even your one, I used something like "foo\tbar\tbaz" which also just
happened to work).
My next idea was that it replaced each TAB with the number of spaces up to
the next multiple of 8. But it doesn't do this neither, although this does
work for fixed width fonts.
In general however it seems to align the characters after TAB on the next
value multiple to 8*average_font_char_width in pixels. This does make sense
now that I think about it, as otherwise columns created using TABs would
not align properly. However it also means that my patch is totally wrong
and that this needs to be done in a quite different way: modifying the
input string won't do it, we need to call GetTextExtentPoint32() for each
segment between the TABs and then increase the width to the next tab stop.
I don't have time to do it now, but it shouldn't be difficult to modify
DoGetTextExtent() to do it. If you have a possibility to spend some time on
it, patches fixing the problem would be very welcome. Otherwise please open
a ticket on Trac so that we don't forget to fix it later.
Thanks,
NR> Perhaps we can use GetTabbedTextExtent() instead of GetTextExtentPoint32()?
This looks like exactly the right function to use, I simply didn't know
about it, thanks!
Just to play a devil advocate it could be argued that maybe wxDC doesn't
need to use it as, after all, it doesn't use TabbedTextOut() in its
DrawText(). Maybe it would be better to provide separate function for text
with TABs just as MSW itself does (I'd prefer to append a flags parameter
to GetTextExtent() but this would require changing all derived wxDC
classes). Of course, this objection has nothing to do with whether we use
GetTabbedTextExtent() or the manual solution I initially thought of, it's
just that now that I see that MSW has different functions for normal and
tabbed text I start thinking that it might be a bad idea to always use the
tabbed versions.
NR> So I tried changing wxDC::DoGetTextExtent() to use GetTabbedTextExtent()
NR> instead of GetTextExtentPoint32(). Here is a patch containing
NR> the changes I made to dc.cpp (note: the changes are relative to the
NR> original version of dc.cpp, not the one with Vadim's patch applied).
NR>
NR> diff -u -b dc.cpp.orig dc.cpp
NR> --- dc.cpp.orig Fri Nov 9 15:16:00 2007
NR> +++ dc.cpp Mon Dec 14 02:32:50 2009
NR> @@ -1762,12 +1762,17 @@
NR> hfontOld = 0;
NR> }
NR>
NR> - SIZE sizeRect;
NR> + // Note: GetTabbedTextExtent() is used instead of GetTextExtentPoint32()
NR> + // because the latter does not account properly for tabs.
NR> const size_t len = string.length();
NR> - if ( !::GetTextExtentPoint32(GetHdc(), string, len, &sizeRect) )
NR> + DWORD dwSize = ::GetTabbedTextExtent(GetHdc(), string, len, 0, NULL);
NR> + if ( dwSize == 0 )
NR> {
NR> - wxLogLastError(_T("GetTextExtentPoint32()"));
NR> + wxLogLastError(_T("GetTabbedTextExtent()"));
NR> }
NR> + SIZE sizeRect;
NR> + sizeRect.cx = LOWORD(dwSize);
NR> + sizeRect.cy = HIWORD(dwSize);
NR>
NR> #if !defined(_WIN32_WCE) || (_WIN32_WCE>= 400)
NR> // the result computed by GetTextExtentPoint32() may be too small as it
NR>
NR>
NR> However, this still does not solve the problem in general.
NR> Now both "Word\tword" and "Word word\tword" display correctly,
NR> but for example "Word da doo da doo dam\tword" does not
NR> (using the same patch to the minimal sample as before).
NR>
NR> Any ideas on what might still be going wrong?
Not really, your code looks exactly as I would have tried to do it myself.
All I can offer is a patch for testing this function which I found
convenient to use as using wxBORDER_SIMPLE allows to see the computed
control size visually:
diff --git a/samples/minimal/minimal.cpp b/samples/minimal/minimal.cpp
index 80bf766..ed995f6 100644
--- a/samples/minimal/minimal.cpp
+++ b/samples/minimal/minimal.cpp
@@ -172,6 +172,31 @@ MyFrame::MyFrame(const wxString& title)
CreateStatusBar(2);
SetStatusText("Welcome to wxWidgets!");
#endif // wxUSE_STATUSBAR
+
+ class MyText : public wxStaticText
+ {
+ public:
+ MyText(wxWindow *parent, const char *label)
+ : wxStaticText(parent, wxID_ANY, label,
+ wxDefaultPosition, wxDefaultSize,
+ wxBORDER_SIMPLE)
+ {
+ }
+ };
+
+ //SetFont(wxFont(10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
+ wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+ sizer->Add(new MyText(this, "0123456789012345678901234567890123456789"));
+ sizer->Add(new MyText(this, "Word\tword "));
+ sizer->Add(new MyText(this, "mmm\tword "));
+ sizer->Add(new MyText(this, "MM\tword "));
+ sizer->Add(new MyText(this, "Word word"));
+ sizer->Add(new MyText(this, "Word word\tword "));
+ sizer->Add(new MyText(this, "Word word word"));
+ sizer->Add(new MyText(this, "Word word another one\tword "));
+ sizer->Add(new MyText(this, "Word word another one word"));
+ sizer->Add(new MyText(this, "0123456789012345678901234567890123456789"));
+ SetSizerAndFit(sizer);
}
Sorry for lack of any ideas...
On Mon, 14 Dec 2009 22:52:00 +0000 Nathan Ridge <zerat...@hotmail.com> wrote:
NR> It turns out GetTabbedTextExtent() barely ever returns the correct
NR> length for strings with tabs in them. Sometimes it leaves extra space
NR> at the end of the string, and sometimes it cuts the string off.
I still can't understand what is this function for if it can't be used
for, you know, the extend of tabbed text. So I still suspect there is
something we don't understand but even after looking at it anew I don't see
what could we be missing.
NR> I was not able to figure out how to get GetTabbedTextExtent()
NR> to use the same tab spacing as TextOut(), so I implemented
NR> your suggested manual solution. Here's the patch against
NR> the original dc.cpp:
...
NR> I've tried this on a variety of strings, including on strings with
NR> multiple consecutive tabs and trailing tabs. It works correctly
NR> for every string I tried. Also, it does not do any extra work
NR> for strings that do not contain tabs.
Thanks a lot for this patch, I guess we should just apply it. But I'd like
to look at this for one last time (to be honest, I want to try stepping
into GetTabbedTextExtent() to see what exactly does it do -- but this
requires more time than I have now) so could you please submit this patch
to the Trac so that it doesn't get lost until then?
NR> Let me know if I still should open a Trac ticket about this issue.
Please do it if possible.
Thanks,