Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets
Commits:
03872533 by ali kettab at 2025-10-19T15:25:56+01:00
wxMSW: Revert commit 288b208a0a4d306afa7c356af6e17105bd24485c
The reverted commit added in 2021-09-28 (Fix setting clipping region
for wxDC with RTL enabled (wxMSW)) because:
- SetDeviceClippingRegion() no longer works correctly (see Regions screen
in the drawing sample).
- It beaks the wxGrid control rendering (try scrolling the grid window
horizontally in the griddemo).
- - - - -
30de8f1e by ali kettab at 2025-10-19T15:25:56+01:00
wxMSW: Remove a hack from wxPaintDC
This hack is no longer needed as GetClippingRect() is working correctly
since a long time. It is added in commit e9f8a82 (in 2007-08-15) to fix
clipping box retrieval (see parent commit 27539df).
- - - - -
f5d326fc by ali kettab at 2025-10-19T15:25:56+01:00
wxMSW: Fix SetClippingRegion() after reverting commit 288b208a0a
- - - - -
8c8927d9 by AliKet at 2025-10-19T15:29:55+01:00
wxGTK: Fix SetLayoutDirection() not working with wxMemoryDC
The graphics context must be recreated for SetLayoutDirection() to take effect.
- - - - -
324f030f by AliKet at 2025-10-19T15:29:55+01:00
wxGTK: Fix ScreenToClient() returning incorrect results in RTL layout
- - - - -
6d692d09 by AliKet at 2025-10-19T15:29:55+01:00
wxGTK: Fix popup menu's position in RTL layout
- - - - -
ed15a498 by AliKet at 2025-10-19T15:29:55+01:00
wxGTK: Fix mouse position when initializing wxMouseEvent in RTL layout
The origin is always in the upper right corner for any type of GtkWidget
- - - - -
53dda62b by AliKet at 2025-10-19T15:29:55+01:00
wxGTK: Fix wxDataViewCtrl rendering in RTL layout
- - - - -
07c1da23 by ali kettab at 2025-10-20T14:44:29+01:00
wxGTK: Fix wxDataViewCustomRenderer in RTL layout
- - - - -
1b5dd41e by AliKet at 2025-10-20T14:44:29+01:00
wxAUI: Don't mirror hint rectangle in RTL layout
This is unnecessary and incorrect at least under wxMSW and wxGTK3,
because ClientToScreen() already returns the correct rectangle under
these platforms.
- - - - -
fcae63b6 by AliKet at 2025-10-20T14:44:29+01:00
Fix OneDevRegionRTL test case after the changes done to wxMSW and wxGTK3
- - - - -
701d35e5 by AliKet at 2025-10-20T14:44:29+01:00
Add tests to wxWindow::ClientToScreen() for RTL layout
- - - - -
3cee484a by Robert Roebling at 2025-10-25T15:57:48+02:00
Add new wxSizer::CalcMinUsingLayoutDirection()
This allows wxWrapSizer to just override the new function instead of
using m_lastUsed variable to distinguish between calculating min size
during the first and second layout passes.
Also add wxWindow::GetMinSizeUsingLayoutDirection() following similar
logic. This is not used yet but will be in the upcoming commit.
Co-authored-by: Vadim Zeitlin <
va...@wxwidgets.org>
- - - - -
bc3b22bb by Robert Roebling at 2025-10-25T15:58:20+02:00
Implement automatic wrapping in wxStaticText with wxST_WRAP
Override the new GetMinSizeUsingLayoutDirection() in wxStaticText to
compute the size needed to fit wxStaticText contents when wrapping it
at the specified width.
Modify wrapsizer sample to also show wxStaticText wrapping.
Co-authored-by: Vadim Zeitlin <
va...@wxwidgets.org>
- - - - -
1201f665 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Make it more visually clear where static text should wrap
Create a window of 150px width to check that the text really wraps at
150px.
- - - - -
8a6a4080 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Update comments in wxWrapSizer code to make more sense
They were semantically broken by the recent changes.
- - - - -
38424e5f by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Add support for wrapping on non-ASCII space characters
Break lines on zero width space and other Unicode characters with the
"white space" property, but not non-breaking ones.
Closes #751.
- - - - -
112df51e by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Make wxWindow::GetEffectiveMinSize() code a bit more clear
Use wxSize::SetDefaults() instead of reimplementing it to make it more
obvious what happens here.
No real changes.
- - - - -
3601c2d2 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Don't make "Static" page in the widgets sample so wide
Use multiple lines for the long label to prevent it from making the
contents of the page too wide.
Do make the static box take all available space, if there is any.
- - - - -
f2c4fb25 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Disable "Ellipsize" checkbox on the "static" widgets sample page
The text always fits initially, as the page size is computed using it,
so choosing to ellipsize it has no effect.
- - - - -
6e554f31 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Test wxST_WRAP in the "Static" page of the widgets sample too
Add a checkbox to allow turning on wxST_WRAP.
- - - - -
6b5c6cd9 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Rename variable with a nonsensical name in the widgets sample
No real changes, just don't use "dummy text" which doesn't mean anything
even though it has been present since 39bc0347fd (added support for
ellipsization and markup in wxStaticText (modified patch 1629946),
2007-04-01) and call the flags used for plain text and text with markup
just "flagsText" and "flagsMarkupText" respectively.
This makes the code a bit more understandable.
- - - - -
d2224c7c by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Return correct min size for wrapped wxStaticText
Don't return ridiculously small width and, even more importantly, don't
return single line height when a wrapped label typically has more than
one line.
- - - - -
19ed1ba7 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Remove unnecessary call to InvalidateBestSize()
This is already done by Wrap(), no need to redo it here.
- - - - -
5aa6ff17 by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Don't bother restoring wxStaticText flags if we didn't change them
Only call SetWindowStyleFlag() to restore the flags if they had been
changed in the first place.
No real changes.
- - - - -
11a8ed6c by Vadim Zeitlin at 2025-10-25T15:59:25+02:00
Replace InformFirstDirection() with better API
The functions InformFirstDirection() in wxWindow and wxSizer forced the
code overriding it to be written in unnatural way, by storing the
parameters passed to this function and then using them in other
functions later.
Add new functions wxWindow::GetMinSizeFromKnownDirection() and
wxSizer::CalcMinSizeFromKnownDirection() taking the same parameters as
InformFirstDirection() but which can use them directly to return the
desired result.
Change wxStaticText and wxWrapSizer to use them instead of the old
function to simplify their code (that of wxWrapSizer could probably be
still simplified further, as it doesn't seem necessary to store these
values at all any longer, but for now keep it as is).
- - - - -
68325253 by Vadim Zeitlin at 2025-10-25T16:01:56+02:00
Recognize wxST_WRAP in XRC handler for wxStaticText
Add support for the new style to XRC too.
- - - - -
36fa157d by Maarten Bent at 2025-10-25T16:05:43+02:00
Use wxLogWarning for changing appearance errors in drawing sample
wxLogStatus is unnoticeable in the drawing sample, see #25587.
Closes #25914.
- - - - -
43dcb1ba by Vadim Zeitlin at 2025-10-25T16:16:59+02:00
Merge branch 'msw-rtl2' of github.com:AliKet/wxWidgets
Multiple RTL layout fixes for wxMSW and wxGTK3.
See #25426.
- - - - -
65e8f2b7 by Vadim Zeitlin at 2025-10-25T17:40:16+02:00
Merge branch 'stattext-wrap'
Implement automatic wxStaticText wrapping.
See #25925.
Closes #25753.
- - - - -
28 changed files:
- include/wx/gtk/dc.h
- include/wx/gtk/private/event.h
- include/wx/msw/dc.h
- include/wx/sizer.h
- include/wx/stattext.h
- include/wx/window.h
- include/wx/wrapsizer.h
- interface/wx/sizer.h
- interface/wx/stattext.h
- interface/wx/window.h
- samples/drawing/drawing.cpp
- samples/widgets/static.cpp
- samples/wrapsizer/wrapsizer.cpp
- src/aui/framemanager.cpp
- src/common/sizer.cpp
- src/common/stattextcmn.cpp
- src/common/wincmn.cpp
- src/common/wrapsizer.cpp
- src/gtk/dataview.cpp
- src/gtk/dc.cpp
- src/gtk/window.cpp
- src/msw/dc.cpp
- src/msw/dcclient.cpp
- src/xrc/xh_sttxt.cpp
- tests/graphics/clippingbox.cpp
- tests/misc/guifuncs.cpp
- tests/misc/textwrap.cpp
- tests/sizers/wrapsizer.cpp
Changes:
=====================================
include/wx/gtk/dc.h
=====================================
@@ -100,6 +100,7 @@ public:
virtual void DoSelect(const wxBitmap& bitmap) override;
virtual const wxBitmap& GetSelectedBitmap() const override;
virtual wxBitmap& GetSelectedBitmap() override;
+ virtual void SetLayoutDirection(wxLayoutDirection dir) override;
private:
void Setup();
=====================================
include/wx/gtk/private/event.h
=====================================
@@ -74,11 +74,11 @@ template<typename T> void InitMouseEvent(wxWindowGTK *win,
event.m_y += posY - a.y;
}
- if ((win->m_wxwindow) && (win->GetLayoutDirection() == wxLayout_RightToLeft))
+ if (win->GetLayoutDirection() == wxLayout_RightToLeft)
{
// origin in the upper right corner
GtkAllocation a;
- gtk_widget_get_allocation(win->m_wxwindow, &a);
+ gtk_widget_get_allocation(win->m_wxwindow ? win->m_wxwindow : win->m_widget, &a);
int window_width = a.width;
event.m_x = window_width - event.m_x;
}
=====================================
include/wx/msw/dc.h
=====================================
@@ -282,7 +282,7 @@ protected:
void DrawAnyText(const wxString& text, wxCoord x, wxCoord y);
// common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
- void SetClippingHrgn(WXHRGN hrgn, bool doRtlOffset = false);
+ void SetClippingHrgn(WXHRGN hrgn);
// implementation of DoGetSize() for wxScreen/PrinterDC: this simply
// returns the size of the entire device this DC is associated with
=====================================
include/wx/sizer.h
=====================================
@@ -624,15 +624,6 @@ public:
virtual void Clear( bool delete_windows = false );
virtual void DeleteWindows();
- // Inform sizer about the first direction that has been decided (by parent item)
- // Returns true if it made use of the information (and recalculated min size)
- //
- // Note that while this method doesn't do anything by default, it should
- // almost always be overridden in the derived classes and should have been
- // pure virtual if not for backwards compatibility constraints.
- virtual bool InformFirstDirection( int WXUNUSED(direction), int WXUNUSED(size), int WXUNUSED(availableOtherDir) )
- { return false; }
-
void SetMinSize( int width, int height )
{ DoSetMinSize( width, height ); }
void SetMinSize( const wxSize& size )
@@ -669,6 +660,13 @@ public:
// this size to really update all the sizer items.
virtual wxSize CalcMin() = 0;
+ // Can be overridden to return adjusted minimal size when the size in the
+ // given direction is already known.
+ virtual wxSize
+ CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir);
+
// This method should be overridden but isn't pure virtual for backwards
// compatibility.
virtual void RepositionChildren(const wxSize& WXUNUSED(minSize))
@@ -705,10 +703,6 @@ public:
m_position = pos;
m_size = size;
Layout();
-
- // This call is required for wxWrapSizer to be able to calculate its
- // minimal size correctly.
- InformFirstDirection(wxHORIZONTAL, size.x, size.y);
}
void SetDimension(int x, int y, int width, int height)
{ SetDimension(wxPoint(x, y), wxSize(width, height)); }
@@ -747,6 +741,11 @@ public:
// items are shown.
virtual bool AreAnyItemsShown() const;
+ // Don't use in the new code, override CalcMinSizeFromKnownDirection()
+ // instead.
+ virtual bool InformFirstDirection( int WXUNUSED(direction), int WXUNUSED(size), int WXUNUSED(availableOtherDir) )
+ { return false; }
+
protected:
wxSize m_size;
wxSize m_minSize;
@@ -989,12 +988,12 @@ public:
// implementation of our resizing logic
virtual wxSize CalcMin() override;
+ virtual wxSize
+ CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir) override;
virtual void RepositionChildren(const wxSize& minSize) override;
- virtual bool InformFirstDirection(int direction,
- int size,
- int availableOtherDir) override;
-
protected:
// Only overridden to perform extra debugging checks.
virtual wxSizerItem *DoInsert(size_t index, wxSizerItem *item) override;
=====================================
include/wx/stattext.h
=====================================
@@ -20,7 +20,7 @@
* wxStaticText flags
*/
#define wxST_NO_AUTORESIZE 0x0001
-// free 0x0002 bit
+#define wxST_WRAP 0x0002
#define wxST_ELLIPSIZE_START 0x0004
#define wxST_ELLIPSIZE_MIDDLE 0x0008
#define wxST_ELLIPSIZE_END 0x0010
@@ -41,9 +41,16 @@ public:
void Wrap(int width);
// overridden base virtuals
+ virtual wxSize
+ GetMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir) override;
+
virtual bool AcceptsFocus() const override { return false; }
virtual bool HasTransparentBackground() override { return true; }
+ virtual void SetWindowStyleFlag(long style) override;
+
bool IsEllipsized() const
{
return (GetWindowStyle() & wxST_ELLIPSIZE_MASK) != 0;
@@ -72,6 +79,16 @@ protected: // functions required for wxST_ELLIPSIZE_* support
// display.
void UpdateLabel();
+ // If m_currentWrap is non-zero, contains the label value before wrapping it.
+ // This is used to allow re-wrapping it at different widths. Note that
+ // wxControlBase::m_labelOrig is changed when Wrap() is called and so can't
+ // be used.
+ wxString m_unwrappedLabel;
+
+ // The width at which the label is currently wrapped or 0 if not wrapped.
+ int m_currentWrap = 0;
+
+
// These functions are platform-specific and must be implemented in the
// platform-specific code. They must not use or update m_labelOrig.
=====================================
include/wx/window.h
=====================================
@@ -480,6 +480,22 @@ public:
int GetMaxWidth() const { return GetMaxSize().x; }
int GetMaxHeight() const { return GetMaxSize().y; }
+ // This function may be overridden to return the minimal size if it
+ // depends on the size known to be available in some direction, e.g.
+ // for text wrapping controls to compute the number of lines needed to
+ // fit all their text in the given width.
+ //
+ // It must return wxDefaultSize if the size of this window doesn't
+ // depend on the size in the given direction to avoid unnecessary
+ // relayouts.
+ //
+ // Default implementation calls InformFirstDirection() for
+ // compatibility and then returns either GetEffectiveMinSize() or
+ // wxDefaultSize depending on InformFirstDirection() return value.
+ virtual wxSize
+ GetMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir);
// Methods for accessing the virtual size of a window. For most
// windows this is just the client area of the window, but for
=====================================
include/wx/wrapsizer.h
=====================================
@@ -36,12 +36,12 @@ public:
// override base class virtual methods
virtual wxSize CalcMin() override;
+ virtual wxSize
+ CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir) override;
virtual void RepositionChildren(const wxSize& minSize) override;
- virtual bool InformFirstDirection(int direction,
- int size,
- int availableOtherDir) override;
-
protected:
// This method is called to decide if an item represents empty space or
// not. We do this to avoid having space-only items first or last on a
@@ -81,8 +81,6 @@ protected:
int m_dirInform; // Direction for size information
int m_availSize; // Size available in m_dirInform direction
int m_availableOtherDir; // Size available in the other direction
- bool m_lastUsed; // Indicates whether value from InformFirst... has
- // been used yet
// The sizes below are computed by RepositionChildren(), i.e. they don't have
// valid values during the initial call to CalcMin() and they are only
=====================================
interface/wx/sizer.h
=====================================
@@ -336,6 +336,46 @@ public:
*/
virtual wxSize CalcMin() = 0;
+ /**
+ May be overridden by sizers whose minimal size depends on the layout
+ direction.
+
+ It may be useful to override it if the sizer minimal size varies
+ depending on its size in some direction. For example, wxWrapSizer uses
+ it to determine the smallest size it can use and still show all of its
+ items when the size in some direction is fixed, e.g. it returns the
+ width of the widest control when @a direction is wxVERTICAL or the
+ total height of all controls wrapped at the given width when @a
+ direction is wxHORIZONTAL.
+
+ If the sizer minimal size doesn't depend on the size known in the given
+ direction, this function should return wxDefaultSize to avoid
+ unnecessary re-layouts.
+
+ The default implementation simply returns wxDefaultSize (after calling
+ InformFirstDirection() for backward compatibility).
+
+ @param direction
+ The direction in which the size is fixed, either ::wxHORIZONTAL or
+ ::wxVERTICAL.
+ @param size
+ The size in the direction given by the @a direction parameter,
+ always valid, i.e. positive.
+ @param availableOtherDir
+ The size available in the other direction, may be -1 if the
+ available size is not known.
+ @return
+ The minimal size of the sizer when its size in the given
+ @a direction is fixed to @a size or ::wxDefaultSize if the minimum
+ size doesn't depend on the layout direction and is always the same.
+
+ @since 3.3.2
+ */
+ virtual wxSize
+ CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir);
+
/**
Detaches all children from the sizer.
@@ -433,9 +473,10 @@ public:
void FitInside(wxWindow* window);
/**
- Inform sizer about the first direction that has been decided (by
- parent item). Returns true if it made use of the information (and
- recalculated min size).
+ Compatibility function called by CalcMinSizeFromKnownDirection().
+
+ This function shouldn't be used in the new code, please override
+ CalcMinSizeFromKnownDirection() instead.
*/
virtual bool InformFirstDirection(int direction, int size, int availableOtherDir);
=====================================
interface/wx/stattext.h
=====================================
@@ -6,6 +6,7 @@
/////////////////////////////////////////////////////////////////////////////
#define wxST_NO_AUTORESIZE 0x0001
+#define wxST_WRAP 0x0002
#define wxST_ELLIPSIZE_START 0x0004
#define wxST_ELLIPSIZE_MIDDLE 0x0008
#define wxST_ELLIPSIZE_END 0x0010
@@ -42,6 +43,10 @@
@style{wxST_ELLIPSIZE_END}
If the label text width exceeds the control width, replace the end
of the label with an ellipsis; uses wxControl::Ellipsize.
+ @style{wxST_WRAP}
+ Wrap label text on multiple lines if necessary, using the available
+ horizontal space. This style only works when the control is used
+ inside a sizer. It is available since wxWidgets 3.3.2.
@endStyleTable
@library{wxcore}
=====================================
interface/wx/window.h
=====================================
@@ -1401,6 +1401,48 @@ public:
*/
virtual wxSize GetEffectiveMinSize() const;
+ /**
+ May be overridden if the control minimal size depends on the layout
+ direction.
+
+ This function is called when using sizers for the layout to request
+ minimum controls size once its size in the specified @a direction is
+ fixed by the layout algorithm and known to be equal to @a size.
+
+ It may be useful to override it if the control minimal size varies
+ depending on its size in some direction. For example, controls showing
+ multi-line text may return the size needed to show their text after
+ wrapping the contents to fit the given width when @a direction is
+ wxHORIZONTAL and @a size is the available width.
+
+ The default implementation of this method returns wxDefaultSize
+ (to be precise, it may return GetEffectiveMinSize() if the deprecated
+ InformFirstDirection() is overridden and returns @true, but this
+ shouldn't be done in the new code).
+
+ @param direction
+ The direction in which the size is fixed, either ::wxHORIZONTAL or
+ ::wxVERTICAL.
+ @param size
+ The size in the direction given by the @a direction parameter,
+ always valid, i.e. positive.
+ @param availableOtherDir
+ The size available in the other direction, may be -1 if the
+ available size is not known.
+ @return
+ The minimal size of the window when its size in the given
+ @a direction is fixed to @a size or ::wxDefaultSize if the minimum
+ size doesn't depend on the layout direction and is always the same.
+
+ @since 3.3.2
+
+ @see wxSizer::CalcMinSizeFromKnownDirection()
+ */
+ virtual wxSize
+ GetMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir) const;
+
/**
Returns the maximum size of window's client area.
@@ -1590,12 +1632,10 @@ public:
virtual wxSize GetWindowBorderSize() const;
/**
- wxSizer and friends use this to give a chance to a component to recalc
- its min size once one of the final size components is known. Override
- this function when that is useful (such as for wxStaticText which can
- stretch over several lines). Parameter availableOtherDir
- tells the item how much more space there is available in the opposite
- direction (-1 if unknown).
+ Compatibility function called by GetMinSizeFromKnownDirection().
+
+ This function shouldn't be used in the new code, please override
+ GetMinSizeFromKnownDirection() instead.
*/
virtual bool
InformFirstDirection(int direction,
=====================================
samples/drawing/drawing.cpp
=====================================
@@ -594,7 +594,7 @@ bool MyApp::DoSetAppearance(int menuId)
switch ( SetAppearance(Appearance(menuId - Colour_AppearanceSystem)) )
{
case wxApp::AppearanceResult::Failure:
- wxLogStatus("Appearance couldn't be changed.");
+ wxLogWarning("Appearance couldn't be changed.");
break;
case wxApp::AppearanceResult::Ok:
@@ -602,7 +602,7 @@ bool MyApp::DoSetAppearance(int menuId)
return true;
case wxApp::AppearanceResult::CannotChange:
- wxLogStatus("Appearance cannot be changed dynamically.");
+ wxLogWarning("Appearance cannot be changed dynamically.");
break;
}
=====================================
samples/widgets/static.cpp
=====================================
@@ -136,6 +136,7 @@ protected:
*m_chkBoxWithCheck,
#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX
*m_chkAutoResize,
+ *m_chkWrap,
*m_chkEllipsize;
#if wxUSE_MARKUP
@@ -187,6 +188,7 @@ StaticWidgetsPage::StaticWidgetsPage(WidgetsBookCtrl *book,
// init everything
m_chkVert =
m_chkAutoResize =
+ m_chkWrap =
m_chkGeneric =
#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX
m_chkBoxWithCheck =
@@ -242,6 +244,9 @@ void StaticWidgetsPage::CreateContent()
m_chkAutoResize = CreateCheckBoxAndAddToSizer(sizerLeft, "&Fit to text", wxID_ANY, sizerLeftBox);
m_chkAutoResize->Bind(wxEVT_CHECKBOX, &StaticWidgetsPage::OnRecreate, this);
+ m_chkWrap = CreateCheckBoxAndAddToSizer(sizerLeft, "&Wrap", wxID_ANY, sizerLeftBox);
+ m_chkWrap->Bind(wxEVT_CHECKBOX, &StaticWidgetsPage::OnRecreate, this);
+
sizerLeft->Add(5, 5, 0, wxGROW | wxALL, 5); // spacer
static const wxString halign[] =
@@ -340,10 +345,10 @@ void StaticWidgetsPage::CreateContent()
m_textLabel->SetValue("And this is a\n\tlabel inside the box with a &mnemonic.\n"
"Only this text is affected by the ellipsize settings.");
#if wxUSE_MARKUP
- m_textLabelWithMarkup->SetValue("Another label, this time <b>decorated</b> "
- "with <u>markup</u>; here you need entities "
- "for the symbols: < > && ' " "
- " but you can still use &mnemonics too");
+ m_textLabelWithMarkup->SetValue("Another label, this time <b>decorated</b>\n"
+ "with <u>markup</u>; here you need entities\n"
+ "for the symbols: < > && ' "\n"
+ "but you can still use &mnemonics too");
#endif // wxUSE_MARKUP
// right pane
@@ -373,7 +378,9 @@ void StaticWidgetsPage::Reset()
#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX
m_chkVert->SetValue(false);
m_chkAutoResize->SetValue(true);
- m_chkEllipsize->SetValue(true);
+ m_chkWrap->SetValue(false);
+ m_chkEllipsize->SetValue(false);
+ m_radioEllipsize->Disable();
m_radioHAlign->SetSelection(StaticHAlign_Left);
m_radioVAlign->SetSelection(StaticVAlign_Top);
@@ -400,12 +407,18 @@ void StaticWidgetsPage::CreateStatic()
int flagsBox = 0,
flagsText = GetAttrs().m_defaultFlags,
- flagsDummyText = GetAttrs().m_defaultFlags;
+ flagsMarkupText = GetAttrs().m_defaultFlags;
if ( !m_chkAutoResize->GetValue() )
{
flagsText |= wxST_NO_AUTORESIZE;
- flagsDummyText |= wxST_NO_AUTORESIZE;
+ flagsMarkupText |= wxST_NO_AUTORESIZE;
+ }
+
+ if ( m_chkWrap->GetValue() )
+ {
+ flagsText |= wxST_WRAP;
+ flagsMarkupText |= wxST_WRAP;
}
int align = 0;
@@ -456,21 +469,21 @@ void StaticWidgetsPage::CreateStatic()
wxFALLTHROUGH;
case StaticEllipsize_Start:
- flagsDummyText |= wxST_ELLIPSIZE_START;
+ flagsText |= wxST_ELLIPSIZE_START;
break;
case StaticEllipsize_Middle:
- flagsDummyText |= wxST_ELLIPSIZE_MIDDLE;
+ flagsText |= wxST_ELLIPSIZE_MIDDLE;
break;
case StaticEllipsize_End:
- flagsDummyText |= wxST_ELLIPSIZE_END;
+ flagsText |= wxST_ELLIPSIZE_END;
break;
}
}
- flagsDummyText |= align;
flagsText |= align;
+ flagsMarkupText |= align;
flagsBox |= align;
wxStaticBox *staticBox;
@@ -503,12 +516,12 @@ void StaticWidgetsPage::CreateStatic()
m_statText = new wxGenericStaticText(staticBox, wxID_ANY,
m_textLabel->GetValue(),
wxDefaultPosition, wxDefaultSize,
- flagsDummyText);
+ flagsText);
#if wxUSE_MARKUP
m_statMarkup = new wxGenericStaticText(staticBox, wxID_ANY,
wxString(),
wxDefaultPosition, wxDefaultSize,
- flagsText);
+ flagsMarkupText);
#endif // wxUSE_MARKUP
}
else // use native versions
@@ -516,12 +529,12 @@ void StaticWidgetsPage::CreateStatic()
m_statText = new wxStaticText(staticBox, wxID_ANY,
m_textLabel->GetValue(),
wxDefaultPosition, wxDefaultSize,
- flagsDummyText);
+ flagsText);
#if wxUSE_MARKUP
m_statMarkup = new wxStaticText(staticBox, wxID_ANY,
wxString(),
wxDefaultPosition, wxDefaultSize,
- flagsText);
+ flagsMarkupText);
#endif // wxUSE_MARKUP
}
@@ -557,7 +570,7 @@ void StaticWidgetsPage::CreateStatic()
NotifyWidgetRecreation(m_statLine);
#endif
- m_sizerStatic->Add(m_sizerStatBox, 0, wxGROW);
+ m_sizerStatic->Add(m_sizerStatBox, 1, wxGROW);
m_sizerStatic->Layout();
=====================================
samples/wrapsizer/wrapsizer.cpp
=====================================
@@ -122,6 +122,27 @@ WrapSizerFrame::WrapSizerFrame()
sizerMid->Add(sizerMidWrap, wxSizerFlags(100).Expand());
sizerRoot->Add(sizerMid, wxSizerFlags(100).Expand().Border());
+ // A long wxStaticText that wraps like a wxWrapSizer
+ sizerRoot->Add( new wxStaticText( m_panel, -1,
+ "This is very long text that will wrap. This is very long text that will wrap. This is very long text that will wrap.",
+ wxDefaultPosition, wxDefaultSize, wxST_WRAP
+ ));
+ // A long wxStaticText that does not wrap
+ sizerRoot->Add( new wxStaticText( m_panel, -1,
+ "This is long text that will not wrap. This is long text that will not wrap."
+ ));
+
+ // A window of 150px width for comparison.
+ auto* const ruler = new wxStaticText(m_panel, wxID_ANY, "150px",
+ wxDefaultPosition, wxSize(150, -1),
+ wxALIGN_CENTER);
+ ruler->SetBackgroundColour(*wxYELLOW);
+ sizerRoot->Add(ruler);
+
+ // A long wxStaticText that wraps at 150px
+ wxStaticText *stattext = new wxStaticText( m_panel, -1, "This is very long text that will wrap at 150px. This is very long text that will wrap at 150px." );
+ stattext->Wrap( 150 );
+ sizerRoot->Add( stattext );
// A shaped item inside a box sizer
wxStaticBoxSizer *sizerBottom = new wxStaticBoxSizer(wxVERTICAL, m_panel,
=====================================
src/aui/framemanager.cpp
=====================================
@@ -3478,11 +3478,13 @@ wxRect wxAuiManager::CalculateHintRect(wxWindow* pane_window,
m_frame->ClientToScreen(&rect.x, &rect.y);
+#if !defined(__WXMSW__) && !defined(__WXGTK3__)
if ( m_frame->GetLayoutDirection() == wxLayout_RightToLeft )
{
// Mirror rectangle in RTL mode
rect.x -= rect.GetWidth();
}
+#endif // !__WXMSW__ && !__WXGTK3__
return rect;
}
=====================================
src/common/sizer.cpp
=====================================
@@ -538,15 +538,33 @@ bool wxSizerItem::InformFirstDirection(int direction, int size, int availableOth
// Pass the information along to the held object
if (IsSizer())
{
- didUse = GetSizer()->InformFirstDirection(direction,size,availableOtherDir);
- if (didUse)
- m_minSize = GetSizer()->CalcMin();
+ const wxSize minSize = GetSizer()->CalcMinSizeFromKnownDirection
+ (
+ direction,
+ size,
+ availableOtherDir
+ );
+
+ if (minSize != wxDefaultSize)
+ {
+ m_minSize = minSize;
+ didUse = true;
+ }
}
else if (IsWindow())
{
- didUse = GetWindow()->InformFirstDirection(direction,size,availableOtherDir);
- if (didUse)
- m_minSize = m_window->GetEffectiveMinSize();
+ const wxSize minSize = GetWindow()->GetMinSizeFromKnownDirection
+ (
+ direction,
+ size,
+ availableOtherDir
+ );
+
+ if (minSize != wxDefaultSize)
+ {
+ m_minSize = minSize;
+ didUse = true;
+ }
// This information is useful for items with wxSHAPED flag, since
// we can request an optimal min size for such an item. Even if
@@ -556,7 +574,7 @@ bool wxSizerItem::InformFirstDirection(int direction, int size, int availableOth
{
if ( m_ratio != 0 )
{
- wxCHECK_MSG( m_proportion==0, false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
+ wxCHECK_MSG( m_proportion==0, false, wxT("Shaped item, non-zero proportion in wxSizerItem::CalcMinSizeFromKnownDirection()") );
if ( direction == wxHORIZONTAL )
{
// Clip size so that we don't take too much
@@ -1229,6 +1247,21 @@ wxSize wxSizer::GetMinSize()
return ret;
}
+wxSize
+wxSizer::CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir)
+{
+ // For compatibility, call InformFirstDirection().
+ if ( !InformFirstDirection(direction, size, availableOtherDir) )
+ return wxDefaultSize;
+
+ // Old code overriding InformFirstDirection() must have stored the values
+ // passed to it internally, so call its CalcMin() again to recalculate the
+ // minimal size using them.
+ return CalcMin();
+}
+
void wxSizer::DoSetMinSize( int width, int height )
{
m_minSize.x = width;
@@ -2663,17 +2696,12 @@ wxSize wxBoxSizer::CalcMin()
return minSize;
}
-bool
-wxBoxSizer::InformFirstDirection(int direction, int size, int availableOtherDir)
-{
- // In principle, we could propagate the information about the size in the
- // sizer major direction too, but this would require refactoring CalcMin()
- // to determine the actual sizes all our items would have with the given
- // size and we don't do this yet, so for now handle only the simpler case
- // of informing all our items about their size in the orthogonal direction.
- if ( direction == GetOrientation() )
- return false;
+wxSize
+wxBoxSizer::CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir)
+{
bool didUse = false;
for ( wxSizerItem* item: m_children )
@@ -2681,7 +2709,11 @@ wxBoxSizer::InformFirstDirection(int direction, int size, int availableOtherDir)
didUse |= item->InformFirstDirection(direction, size, availableOtherDir);
}
- return didUse;
+ if ( !didUse )
+ return wxDefaultSize;
+
+ // Recalculate the min size now that items had a chance to adjust.
+ return CalcMin();
}
//---------------------------------------------------------------------------
=====================================
src/common/stattextcmn.cpp
=====================================
@@ -99,6 +99,36 @@ wxCONSTRUCTOR_6( wxStaticText, wxWindow*, Parent, wxWindowID, Id, \
// wxTextWrapper
// ----------------------------------------------------------------------------
+namespace
+{
+
+bool IsBreakableWhiteSpace(wxUniChar ch)
+{
+ // We don't take "\r" into account here as it's not supposed to be present
+ // in the labels and "\n" is not present because Wrap() splits text on it.
+ switch ( ch.GetValue() )
+ {
+ case ' ':
+ case '\t':
+ case 0x2000: // en quad
+ case 0x2001: // em quad
+ case 0x2002: // en space
+ case 0x2003: // em space
+ case 0x2004: // three-per-em space
+ case 0x2005: // four-per-em space
+ case 0x2006: // six-per-em space
+ case 0x2008: // punctuation space
+ case 0x2009: // thin space
+ case 0x200A: // hair space
+ case 0x200B: // zero width space
+ return true;
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
void wxTextWrapper::Wrap(wxWindow *win, const wxString& text, int widthMax)
{
const wxInfoDC dc(win);
@@ -143,7 +173,7 @@ void wxTextWrapper::Wrap(wxWindow *win, const wxString& text, int widthMax)
}
// If the overflowing character is a space, we can break right here.
- if ( line[posEnd] == ' ' )
+ if ( IsBreakableWhiteSpace(line[posEnd]) )
{
DoOutputLine(line.substr(0, posEnd));
line = line.substr(posEnd + 1);
@@ -151,20 +181,35 @@ void wxTextWrapper::Wrap(wxWindow *win, const wxString& text, int widthMax)
}
// Find the last word to chop off.
- size_t posSpace = line.rfind(' ', posEnd);
- if ( posSpace == wxString::npos )
+ //
+ // "Word" is defined here as just a sequence of non-space chars.
+ //
+ // TODO: Implement real Unicode word break algorithm.
+ size_t posSpace = posEnd;
+ for ( ;; posSpace-- )
{
- // No spaces, so can't wrap, output until the end of the word
- // which is defined here as just a sequence of non-space chars.
- //
- // TODO: Implement real Unicode word break algorithm.
- posSpace = line.find(' ', posEnd);
- if ( posSpace == wxString::npos )
+ if ( posSpace == 0 )
{
- // No more spaces at all, output the rest of the line.
- DoOutputLine(line);
+ // No spaces, so can't wrap, output until the end of the word.
+ posSpace = posEnd;
+ for ( ;; )
+ {
+ if ( ++posSpace == line.length() )
+ {
+ // No more spaces at all, output the rest of the line.
+ DoOutputLine(line);
+ return;
+ }
+
+ if ( IsBreakableWhiteSpace(line[posSpace]) )
+ break;
+ }
+
break;
}
+
+ if ( IsBreakableWhiteSpace(line[posSpace]) )
+ break;
}
// Output the part that fits.
@@ -213,8 +258,73 @@ private:
void wxStaticTextBase::Wrap(int width)
{
+ if (width == m_currentWrap)
+ return;
+
+ m_currentWrap = width;
+
+ // Allow for repeated calls to Wrap with different values
+ if (m_unwrappedLabel.empty())
+ {
+ m_unwrappedLabel = GetLabel();
+ }
+ else
+ {
+ SetLabel( m_unwrappedLabel );
+ }
wxLabelWrapper wrapper;
wrapper.WrapLabel(this, width);
+ InvalidateBestSize();
+}
+
+wxSize
+wxStaticTextBase::GetMinSizeFromKnownDirection(int direction,
+ int size,
+ int WXUNUSED(availableOtherDir))
+{
+ if ( !HasFlag(wxST_WRAP) || direction != wxHORIZONTAL )
+ return wxDefaultSize;
+
+ // Wrap at the given width to compute the required size.
+ const int style = GetWindowStyleFlag();
+ if ( !(style & wxST_NO_AUTORESIZE) )
+ SetWindowStyleFlag( style | wxST_NO_AUTORESIZE );
+
+ Wrap( size );
+
+ if ( !(style & wxST_NO_AUTORESIZE) )
+ SetWindowStyleFlag( style );
+
+ // Now compute the best size for the wrapped label.
+ int numLines = 0;
+ int maxLineWidth = 0;
+ for ( auto line : wxSplit(GetLabel(), '\n', '\0') )
+ {
+ const int w = GetTextExtent(line).x;
+ if ( w > maxLineWidth )
+ maxLineWidth = w;
+
+ ++numLines;
+ }
+
+ return wxSize( maxLineWidth, numLines*GetCharHeight() );
+}
+
+void wxStaticTextBase::SetWindowStyleFlag(long style)
+{
+ // Check if wxST_WRAP is being cleared.
+ if ( HasFlag(wxST_WRAP) && !(style & wxST_WRAP) )
+ {
+ // And unwrap the label in this case.
+ if ( m_currentWrap )
+ {
+ SetLabel(m_unwrappedLabel);
+ m_unwrappedLabel.clear();
+ m_currentWrap = 0;
+ }
+ }
+
+ wxControl::SetWindowStyleFlag(style);
}
void wxStaticTextBase::AutoResizeIfNecessary()
=====================================
src/common/wincmn.cpp
=====================================
@@ -854,17 +854,24 @@ wxWindowBase::InformFirstDirection(int direction,
availableOtherDir);
}
+wxSize
+wxWindowBase::GetMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir)
+{
+ if ( !InformFirstDirection(direction, size, availableOtherDir) )
+ return wxDefaultSize;
+
+ return GetEffectiveMinSize();
+}
+
wxSize wxWindowBase::GetEffectiveMinSize() const
{
// merge the best size with the min size, giving priority to the min size
wxSize min = GetMinSize();
- if (min.x == wxDefaultCoord || min.y == wxDefaultCoord)
- {
- wxSize best = GetBestSize();
- if (min.x == wxDefaultCoord) min.x = best.x;
- if (min.y == wxDefaultCoord) min.y = best.y;
- }
+ if ( !min.IsFullySpecified() )
+ min.SetDefaults(GetBestSize());
return min;
}
=====================================
src/common/wrapsizer.cpp
=====================================
@@ -72,7 +72,6 @@ wxWrapSizer::wxWrapSizer(int orient, int flags)
m_dirInform(0),
m_availSize(-1),
m_availableOtherDir(0),
- m_lastUsed(true),
m_minSizeMinor(0),
m_maxSizeMajor(0),
m_minItemMajor(INT_MAX),
@@ -128,24 +127,6 @@ wxSizer *wxWrapSizer::GetRowSizer(size_t n)
return sizer;
}
-bool wxWrapSizer::InformFirstDirection(int direction,
- int size,
- int availableOtherDir)
-{
- if ( !direction )
- return false;
-
- // Store the values for later use
- m_availSize = size;
- m_availableOtherDir = availableOtherDir +
- (direction == wxHORIZONTAL ? m_calculatedMinSize.y
- : m_calculatedMinSize.x);
- m_dirInform = direction;
- m_lastUsed = false;
- return true;
-}
-
-
void wxWrapSizer::AdjustLastRowItemProp(size_t n, wxSizerItem *itemLast)
{
if ( !itemLast || !(m_flags & wxEXTEND_LAST_ON_EACH_LINE) )
@@ -161,48 +142,64 @@ void wxWrapSizer::AdjustLastRowItemProp(size_t n, wxSizerItem *itemLast)
item->SetUserData(new wxPropChanger(*this, *itemLast));
}
+wxSize
+wxWrapSizer::CalcMinSizeFromKnownDirection(int direction,
+ int size,
+ int availableOtherDir)
+{
+ if ( m_children.empty() )
+ return wxDefaultSize;
+
+ // Store the parameters for use in CalcMin() later.
+ m_availSize = size;
+ if ( availableOtherDir == -1 )
+ {
+ m_availableOtherDir = -1;
+ }
+ else
+ {
+ m_availableOtherDir = availableOtherDir +
+ (direction == wxHORIZONTAL ? m_calculatedMinSize.y
+ : m_calculatedMinSize.x);
+ }
+
+ m_dirInform = direction;
+
+ // We're called to find a min size that uses one dimension maximally and
+ // the other direction minimally.
+ //
+ // There are two different algorithms for doing it, depending on whether
+ // the first reported size component is the opposite as our own orientation
+ // (the simpler case) or the same one (more complicated).
+ if ( m_dirInform == m_orient )
+ CalcMinFromMajor(m_availSize);
+ else
+ CalcMinFromMinor(m_availSize);
+
+ return m_calculatedMinSize;
+}
+
wxSize wxWrapSizer::CalcMin()
{
if ( m_children.empty() )
return wxSize();
- // We come here to calculate min size in two different situations:
- // 1 - Immediately after InformFirstDirection, then we find a min size that
- // uses one dimension maximally and the other direction minimally.
- // 2 - Ordinary case, get a sensible min size value using the current line
- // layout, trying to maintain the possibility to re-arrange lines by
- // sizing
+ // We're called to get a sensible min size value using the current line
+ // layout, trying to maintain the possibility to re-arrange lines by sizing
- if ( !m_lastUsed )
+ if ( m_availSize > 0 )
{
- // Case 1 above: InformFirstDirection() has just been called
- m_lastUsed = true;
-
- // There are two different algorithms for finding a useful min size for
- // a wrap sizer, depending on whether the first reported size component
- // is the opposite as our own orientation (the simpler case) or the same
- // one (more complicated).
+ wxSize szAvail; // Keep track of boundary so we don't overflow
if ( m_dirInform == m_orient )
- CalcMinFromMajor(m_availSize);
+ szAvail = SizeFromMajorMinor(m_availSize, m_availableOtherDir);
else
- CalcMinFromMinor(m_availSize);
+ szAvail = SizeFromMajorMinor(m_availableOtherDir, m_availSize);
+
+ CalcMinFittingSize(szAvail);
}
- else // Case 2 above: not immediately after InformFirstDirection()
+ else // Initial calculation, before we have size available to us
{
- if ( m_availSize > 0 )
- {
- wxSize szAvail; // Keep track of boundary so we don't overflow
- if ( m_dirInform == m_orient )
- szAvail = SizeFromMajorMinor(m_availSize, m_availableOtherDir);
- else
- szAvail = SizeFromMajorMinor(m_availableOtherDir, m_availSize);
-
- CalcMinFittingSize(szAvail);
- }
- else // Initial calculation, before we have size available to us
- {
- CalcMaxSingleItemSize();
- }
+ CalcMaxSingleItemSize();
}
return m_calculatedMinSize;
=====================================
src/gtk/dataview.cpp
=====================================
@@ -1655,7 +1655,14 @@ gtk_wx_cell_renderer_render (GtkCellRenderer *renderer,
wxDataViewCustomRenderer *cell = wxrenderer->cell;
wxDataViewCustomRenderer::GTKRenderParams renderParams;
+ wxRect rect(wxRectFromGDKRect(cell_area));
#ifdef __WXGTK3__
+ const bool isRTL = wxWindow::GTKGetLayout(widget);
+ if (isRTL)
+ {
+ cairo_scale(cr, -1, 1);
+ rect.x = -rect.x - rect.width;
+ }
renderParams.cr = cr;
#else
renderParams.window = window;
@@ -1666,7 +1673,6 @@ gtk_wx_cell_renderer_render (GtkCellRenderer *renderer,
renderParams.flags = flags;
cell->GTKSetRenderParams(&renderParams);
- wxRect rect(wxRectFromGDKRect(cell_area));
int xpad, ypad;
gtk_cell_renderer_get_padding(renderer, &xpad, &ypad);
rect = rect.Deflate(xpad, ypad);
@@ -1758,6 +1764,13 @@ gtk_wx_cell_renderer_activate(
wxMouseEvent mouse_event(wxEVT_LEFT_DOWN);
InitMouseEvent(ctrl, mouse_event, button_event);
+ if (ctrl->GetLayoutDirection() == wxLayout_RightToLeft)
+ {
+ int w;
+ ctrl->GetClientSize(&w, nullptr);
+ renderrect.x = w - (renderrect.x + renderrect.width);
+ }
+
mouse_event.m_x -= renderrect.x;
mouse_event.m_y -= renderrect.y;
@@ -2820,6 +2833,16 @@ void wxDataViewCustomRenderer::RenderText( const wxString &text,
cell_area.x += xoffset;
cell_area.width -= xoffset;
+#ifdef __WXGTK3__
+ const bool isRTL = wxWindow::GTKGetLayout(m_renderParams->widget);
+ if (isRTL)
+ {
+ cairo_save(m_renderParams->cr);
+ cairo_scale(m_renderParams->cr, -1, 1);
+ cell_area.x = -cell_area.x - cell_area.width;
+ }
+#endif // __WXGTK3__
+
gtk_cell_renderer_render( GTK_CELL_RENDERER(textRenderer),
#ifdef __WXGTK3__
m_renderParams->cr,
@@ -2833,6 +2856,11 @@ void wxDataViewCustomRenderer::RenderText( const wxString &text,
m_renderParams->expose_area,
#endif
GtkCellRendererState(m_renderParams->flags));
+
+#ifdef __WXGTK3__
+ if (isRTL)
+ cairo_restore(m_renderParams->cr);
+#endif
}
bool wxDataViewCustomRenderer::Init(wxDataViewCellMode mode, int align)
@@ -2871,7 +2899,7 @@ wxDC *wxDataViewCustomRenderer::GetDC()
wxASSERT(m_renderParams);
cairo_t* cr = m_renderParams->cr;
wxASSERT(cr && cairo_status(cr) == 0);
- m_dc = new wxGTKCairoDC(cr, ctrl);
+ m_dc = new wxGTKCairoDC(cr, ctrl, ctrl->GetLayoutDirection());
#else
if (ctrl == nullptr)
return nullptr;
@@ -4772,6 +4800,7 @@ bool wxDataViewCtrl::Create(wxWindow *parent,
m_parent->DoAddChild( this );
PostCreation(size);
+ GTKSetLayout(m_treeview, GetLayoutDirection());
g_signal_connect_after(gtk_tree_view_get_selection(GTK_TREE_VIEW(m_treeview)),
"changed", G_CALLBACK(wxdataview_selection_changed_callback), this);
=====================================
src/gtk/dc.cpp
=====================================
@@ -584,6 +584,13 @@ wxBitmap& wxMemoryDCImpl::GetSelectedBitmap()
return m_bitmap;
}
+void wxMemoryDCImpl::SetLayoutDirection(wxLayoutDirection dir)
+{
+ wxGTKCairoDCImpl::SetLayoutDirection(dir);
+
+ Setup();
+}
+
void wxMemoryDCImpl::Setup()
{
wxGraphicsContext* gc = nullptr;
=====================================
src/gtk/window.cpp
=====================================
@@ -4732,7 +4732,7 @@ void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
if (x)
{
if (GetLayoutDirection() == wxLayout_RightToLeft)
- *x = (GetClientSize().x - *x) - org_x;
+ *x = (GetClientSize().x - *x) + org_x;
else
*x -= org_x;
}
@@ -6440,6 +6440,10 @@ void wxPopupMenuPositionCallback( GtkMenu *menu,
rect = wxDisplay(data.menu->GetInvokingWindow()).GetClientArea();
wxPoint pos = data.pos;
+
+ if ( wxWindowGTK::GTKGetLayout(GTK_WIDGET(menu)) == wxLayout_RightToLeft )
+ pos.x -= req.width;
+
if ( pos.x < rect.x )
pos.x = rect.x;
if ( pos.y < rect.y )
@@ -6458,6 +6462,8 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
{
wxCHECK_MSG( m_widget != nullptr, false, wxT("invalid window") );
+ GTKSetLayout(menu->m_menu, GetLayoutDirection());
+
menu->SetupBitmaps(this);
wxPopupMenuPositionCallbackData data;
@@ -6516,6 +6522,10 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
gdk_window_get_device_position(window, device, &x, &y, nullptr);
}
}
+ else if (GetLayoutDirection() == wxLayout_RightToLeft)
+ {
+ x = gdk_window_get_width(window) - x;
+ }
const GdkRectangle rect = { x, y, 1, 1 };
gtk_menu_popup_at_rect(GTK_MENU(menu->m_menu),
=====================================
src/msw/dc.cpp
=====================================
@@ -404,60 +404,34 @@ bool wxMSWDCImpl::DoGetClippingRect(wxRect& rect) const
}
// common part of DoSetClippingRegion() and DoSetDeviceClippingRegion()
-void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn, bool doRtlOffset)
+void wxMSWDCImpl::SetClippingHrgn(WXHRGN hrgn)
{
wxCHECK_RET( hrgn, wxT("invalid clipping region") );
- HRGN hRgnRTL = nullptr;
- // DC with enabled RTL layout needs a mirrored region
- // so we have to create such a region temporarily.
- if ( GetLayoutDirection() == wxLayout_RightToLeft )
+ if ( m_clipping )
{
- DWORD bufLen = ::GetRegionData(hrgn, 0, nullptr); // Get the storage size
- wxScopedArray<char> pDataBuf(bufLen);
- RGNDATA* const rgndata = reinterpret_cast<RGNDATA*>(pDataBuf.get());
- if ( ::GetRegionData(hrgn, bufLen, rgndata) != bufLen )
+ // note that we combine the new clipping region with the existing one: this
+ // is compatible with what the other ports do and is the documented
+ // behaviour now (starting with 2.3.3)
+ if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)hrgn, RGN_AND) == ERROR )
{
- wxLogLastError("GetRegionData");
+ wxLogLastError(wxT("ExtSelectClipRgn"));
+
return;
}
- int dcw, dch;
- DoGetSize(&dcw, &dch);
- XFORM tr;
- tr.eM11 = -1;
- tr.eM12 = 0;
- tr.eM21 = 0;
- tr.eM22 = 1;
- // For region created directly with device coordinates
- // (regions passed to DoSetDeviceClippingRegion) we have to
- // apply additional 1-pixel offset because original right edge
- // passed to e.g. CreateRectRgn() (in wxRegion) is actually
- // not included in the clipping area but this edge will become
- // a left edge after mirroring and therefore its x-coordinates
- // shoulde be adjusted.
- tr.eDx = doRtlOffset ? dcw : dcw-1; // max X
- tr.eDy = 0;
- hRgnRTL = ::ExtCreateRegion(&tr, bufLen, rgndata);
- if ( !hRgnRTL )
+ }
+ else
+ {
+ if ( ::SelectClipRgn(GetHdc(), (HRGN)hrgn) == ERROR )
{
- wxLogLastError("ExtCreateRegion");
+ wxLogLastError(wxT("SelectClipRgn"));
+
return;
}
- }
- AutoHRGN rgnRTL(hRgnRTL);
-
- // note that we combine the new clipping region with the existing one: this
- // is compatible with what the other ports do and is the documented
- // behaviour now (starting with 2.3.3)
- if ( ::ExtSelectClipRgn(GetHdc(), (HRGN)rgnRTL ? (HRGN)rgnRTL : (HRGN)hrgn, RGN_AND) == ERROR )
- {
- wxLogLastError(wxT("ExtSelectClipRgn"));
- return;
+ m_clipping = true;
}
- m_clipping = true;
-
UpdateClipBox();
}
@@ -487,18 +461,28 @@ void wxMSWDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h
// 4 corners of the rectangle to create a polygonal clipping region
// in device coordinates.
POINT rect[4];
- wxPoint p = LogicalToDevice(x, y);
- rect[0].x = p.x;
- rect[0].y = p.y;
- p = LogicalToDevice(x + w, y);
- rect[1].x = p.x;
- rect[1].y = p.y;
- p = LogicalToDevice(x + w, y + h);
- rect[2].x = p.x;
- rect[2].y = p.y;
- p = LogicalToDevice(x, y + h);
- rect[3].x = p.x;
- rect[3].y = p.y;
+ {
+ // Forcing LTR layout to calculate _rect_, otherwise the region created
+ // by CreatePolygonRgn() will not be correct in RTL layout.
+ const auto oldLayoutDir = GetLayoutDirection();
+ SetLayoutDirection(wxLayout_LeftToRight);
+
+ wxPoint p = LogicalToDevice(x, y);
+ rect[0].x = p.x;
+ rect[0].y = p.y;
+ p = LogicalToDevice(x + w, y);
+ rect[1].x = p.x;
+ rect[1].y = p.y;
+ p = LogicalToDevice(x + w, y + h);
+ rect[2].x = p.x;
+ rect[2].y = p.y;
+ p = LogicalToDevice(x, y + h);
+ rect[3].x = p.x;
+ rect[3].y = p.y;
+
+ SetLayoutDirection(oldLayoutDir);
+ }
+
HRGN hrgn = ::CreatePolygonRgn(rect, WXSIZEOF(rect), WINDING);
if ( !hrgn )
{
@@ -506,7 +490,7 @@ void wxMSWDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h
}
else
{
- SetClippingHrgn((WXHRGN)hrgn, false);
+ SetClippingHrgn((WXHRGN)hrgn);
::DeleteObject(hrgn);
}
@@ -514,7 +498,7 @@ void wxMSWDCImpl::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h
void wxMSWDCImpl::DoSetDeviceClippingRegion(const wxRegion& region)
{
- SetClippingHrgn(region.GetHRGN(), true);
+ SetClippingHrgn(region.GetHRGN());
}
void wxMSWDCImpl::DestroyClippingRegion()
=====================================
src/msw/dcclient.cpp
=====================================
@@ -259,10 +259,6 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) :
// (re)set the DC parameters.
InitDC();
-
- // the HDC can have a clipping box (which we didn't set), make sure our
- // DoGetClippingRect() checks for it
- m_clipping = true;
}
wxPaintDCImpl::~wxPaintDCImpl()
=====================================
src/xrc/xh_sttxt.cpp
=====================================
@@ -25,6 +25,7 @@ wxStaticTextXmlHandler::wxStaticTextXmlHandler()
: wxXmlResourceHandler()
{
XRC_ADD_STYLE(wxST_NO_AUTORESIZE);
+ XRC_ADD_STYLE(wxST_WRAP);
XRC_ADD_STYLE(wxALIGN_LEFT);
XRC_ADD_STYLE(wxALIGN_RIGHT);
XRC_ADD_STYLE(wxALIGN_CENTER);
=====================================
tests/graphics/clippingbox.cpp
=====================================
@@ -852,10 +852,6 @@ static void OneRegionRTL(wxDC& dc, const wxBitmap& bmp)
return;
}
-#ifdef __WXGTK__
- wxUnusedVar(bmp);
- WARN("Skipping test known to fail in wxGTK");
-#else
// Setting one clipping region inside DC area.
const int x = 10;
const int y = 20;
@@ -869,7 +865,6 @@ static void OneRegionRTL(wxDC& dc, const wxBitmap& bmp)
CheckClipBox(dc, bmp,
x, y, w, h,
(s_dcSize.x-1)-x2, y, w, h);
-#endif
}
static void TwoRegionsOverlapping(wxDC& dc, const wxBitmap& bmp, const wxPoint& parentDcOrigin)
@@ -1033,13 +1028,9 @@ static void OneDevRegionRTL(wxDC& dc, const wxBitmap& bmp, bool useTransformMatr
return;
}
-#ifdef __WXGTK__
- wxUnusedVar(bmp);
- WARN("Skipping test known to fail in wxGTK");
-#else
// Setting one clipping region in device coordinates
// inside transformed DC area.
- const int x = 10;
+ const int x = 11;
const int y = 21;
const int w = 79;
const int h = 75;
@@ -1064,12 +1055,21 @@ static void OneDevRegionRTL(wxDC& dc, const wxBitmap& bmp, bool useTransformMatr
dc.SetDeviceClippingRegion(reg);
dc.SetBackground(wxBrush(s_fgColour, wxBRUSHSTYLE_SOLID));
dc.Clear();
- wxPoint pos = dc.DeviceToLogical(x+w-1, y); // right physical edge becomes left logical edge
+ // right physical edge becomes left logical edge in a mirrored DC.
+ const int x2 = s_dcSize.x - (x + w);
+ // In a mirrored DC, the device origin (0, 0) is always at the top left
+ // of the DC under wxMSW, but under wxGTK3 is at the top right.
+#if defined(__WXGTK3__)
+ wxPoint pos = dc.DeviceToLogical(x, y);
+ wxSize dim = dc.DeviceToLogicalRel(w, h);
+#else
+ wxPoint pos = dc.DeviceToLogical((s_dcSize.x-1)-x, y);
wxSize dim = dc.DeviceToLogicalRel(-w, h);
+#endif
+
CheckClipBox(dc, bmp,
pos.x, pos.y, dim.x, dim.y,
- x, y, w, h);
-#endif
+ x2, y, w, h);
}
static void OneLargeDevRegion(wxDC& dc, const wxBitmap& bmp, bool checkExtCoords, bool useTransformMatrix, const wxPoint& parentDcOrigin)
=====================================
tests/misc/guifuncs.cpp
=====================================
@@ -27,6 +27,7 @@
#include "wx/panel.h"
#include "asserthelper.h"
+#include "waitfor.h"
#include <memory>
@@ -171,6 +172,20 @@ TEST_CASE("GUI::ClientToScreen", "[guifuncs]")
wxWindow* const tlw = wxTheApp->GetTopWindow();
REQUIRE( tlw );
+ SECTION("Right to left layout [RTL]") { }
+ {
+ tlw->SetLayoutDirection(wxLayout_RightToLeft);
+ }
+
+ SECTION("Left to right layout [LTR]")
+ {
+ tlw->SetLayoutDirection(wxLayout_LeftToRight);
+ }
+
+ tlw->Refresh();
+ tlw->Update();
+ wxYield();
+
std::unique_ptr<wxPanel> const
p1(new wxPanel(tlw, wxID_ANY, wxPoint(0, 0), wxSize(100, 50)));
std::unique_ptr<wxPanel> const
@@ -179,13 +194,25 @@ TEST_CASE("GUI::ClientToScreen", "[guifuncs]")
b = new wxWindow(p2.get(), wxID_ANY, wxPoint(10, 10), wxSize(30, 10));
// We need this to realize the windows created above under wxGTK.
- wxYield();
+ YieldForAWhile();
const wxPoint tlwOrig = tlw->ClientToScreen(wxPoint(0, 0));
+ const int xx = tlw->GetLayoutDirection() == wxLayout_RightToLeft ? -10 : 10;
+
+ wxPoint c2sCoords = p2->ClientToScreen(wxPoint(0, 0));
+ wxPoint c2sCoordsExpected = tlwOrig + wxPoint(0, 50);
+
+ CHECK( c2sCoords == c2sCoordsExpected );
+
+ c2sCoords = b->ClientToScreen(wxPoint(0, 0));
+ c2sCoordsExpected = tlwOrig + wxPoint(xx, 60);
- CHECK( p2->ClientToScreen(wxPoint(0, 0)) == tlwOrig + wxPoint(0, 50) );
+ CHECK( c2sCoords == c2sCoordsExpected );
- CHECK( b->ClientToScreen(wxPoint(0, 0)) == tlwOrig + wxPoint(10, 60) );
+ // Ensure that "ScreenToClient(ClientToScreen(coords)) == coords" is also true.
+ c2sCoords = b->ScreenToClient(c2sCoords);
+ c2sCoordsExpected = wxPoint(0, 0);
+ CHECK( c2sCoords == c2sCoordsExpected );
}
namespace
=====================================
tests/misc/textwrap.cpp
=====================================
@@ -148,6 +148,21 @@ TEST_CASE("wxTextWrapper::Wrap", "[text]")
}
}
+TEST_CASE("wxTextWrapper::WrapUnicodeSpaces", "[text][unicode]")
+{
+ // Check that ZWSP (U+200B) is treated as a breakable space, but NBSP
+ // (U+00A0) and NNBSP (U+202F) are not.
+ const wxString text{L"Un\u00a0break\u200bable\u202fspace"};
+
+ HardBreakWrapper w;
+
+ const auto n = w.Do(text, 1);
+ INFO("Wrapped text:\n" << w.GetResult() << "\n");
+ REQUIRE( n == 2 );
+ CHECK( w.GetLine(0) == wxString{L"Un\u00a0break"} );
+ CHECK( w.GetLine(1) == wxString{L"able\u202fspace"} );
+}
+
// This pseudo test is disabled by default as it requires the environment
// variables WX_TEST_TEXT_WRAP WX_TEST_TEXT_WIDTH to be defined to test how the
// given text is wrapped.
=====================================
tests/sizers/wrapsizer.cpp
=====================================
@@ -33,6 +33,17 @@ TEST_CASE("wxWrapSizer::CalcMin", "[wxWrapSizer]")
wxSize sizeMinExpected;
+ auto const checkSizeIsExpected = [&](const char* desc)
+ {
+ INFO(desc);
+ CHECK( sizer->CalcMinSizeFromKnownDirection
+ (
+ wxHORIZONTAL,
+ win->GetClientSize().x,
+ win->GetClientSize().y
+ ) == sizeMinExpected );
+ };
+
// With a single child the min size must be the same as child size.
const wxSize sizeChild1 = wxSize(80, 60);
sizeMinExpected = sizeChild1;
@@ -43,7 +54,7 @@ TEST_CASE("wxWrapSizer::CalcMin", "[wxWrapSizer]")
sizer->Add(child1);
win->Layout();
- CHECK( sizeMinExpected == sizer->CalcMin() );
+ checkSizeIsExpected("Single child");
// If both children can fit in the same row, the minimal size of the sizer
// is determined by the sum of their minimal horizontal dimensions and
@@ -58,7 +69,7 @@ TEST_CASE("wxWrapSizer::CalcMin", "[wxWrapSizer]")
sizer->Add(child2);
win->Layout();
- CHECK( sizeMinExpected == sizer->CalcMin() );
+ checkSizeIsExpected("Two children in one row");
// Three children will take at least two rows so the minimal size in
// vertical direction must increase.
@@ -71,7 +82,7 @@ TEST_CASE("wxWrapSizer::CalcMin", "[wxWrapSizer]")
sizer->Add(child3);
win->Layout();
- CHECK( sizeMinExpected == sizer->CalcMin() );
+ checkSizeIsExpected("Three children in two rows");
}
TEST_CASE("wxWrapSizer::CalcMinFromMinor", "[wxWrapSizer]")
View it on GitLab:
https://gitlab.com/wxwidgets/wxwidgets/-/compare/b153aafee9d74ecfaef7b1447b79012e6e60c11a...65e8f2b7ca1a40d5234916b5365fca5d3bc28349
--
View it on GitLab:
https://gitlab.com/wxwidgets/wxwidgets/-/compare/b153aafee9d74ecfaef7b1447b79012e6e60c11a...65e8f2b7ca1a40d5234916b5365fca5d3bc28349
You're receiving this email because of your account on
gitlab.com.