Implement wxDC mirroring for RTL layout with GTK3

Co-Authored-By: AliKet <aliket1435@gmail.com>
This commit is contained in:
Paul Cornett
2021-01-31 20:59:29 -08:00
parent 550bb1e2ce
commit e85f89e522
4 changed files with 229 additions and 43 deletions

View File

@@ -25,17 +25,14 @@
wxGTKCairoDCImpl::wxGTKCairoDCImpl(wxDC* owner)
: wxGCDCImpl(owner)
{
m_layoutDir = wxLayout_Default;
}
wxGTKCairoDCImpl::wxGTKCairoDCImpl(wxDC* owner, double scaleFactor)
: wxGCDCImpl(owner, 0)
{
m_contentScaleFactor = scaleFactor;
}
wxGTKCairoDCImpl::wxGTKCairoDCImpl(wxDC* owner, wxWindow* window)
wxGTKCairoDCImpl::wxGTKCairoDCImpl(wxDC* owner, wxWindow* window, wxLayoutDirection dir, int width)
: wxGCDCImpl(owner, 0)
, m_size(width, 0)
{
m_layoutDir = dir;
if ( window )
{
m_window = window;
@@ -62,6 +59,12 @@ void wxGTKCairoDCImpl::DoDrawBitmap(const wxBitmap& bitmap, int x, int y, bool u
if (cr)
{
cairo_save(cr);
if (m_layoutDir == wxLayout_RightToLeft)
{
// bitmap is not mirrored
cairo_scale(cr, -1, 1);
x = -x - bitmap.GetWidth();
}
bitmap.Draw(cr, x, y, useMask, &m_textForegroundColour, &m_textBackgroundColour);
cairo_restore(cr);
}
@@ -81,6 +84,108 @@ bool wxGTKCairoDCImpl::DoFloodFill(int x, int y, const wxColour& col, wxFloodFil
}
#endif
void wxGTKCairoDCImpl::DoDrawText(const wxString& text, int x, int y)
{
wxCHECK_RET(IsOk(), "invalid DC");
if (text.empty())
return;
if (m_layoutDir == wxLayout_RightToLeft && text.find('\n') != wxString::npos)
{
// RTL needs each line separately to position text properly.
// DrawLabel() will split the text and call back for each line.
GetOwner()->DrawLabel(text, wxRect(x, y, 0, 0));
return;
}
int w, h;
DoGetTextExtent(text, &w, &h);
CalcBoundingBox(x, y);
CalcBoundingBox(x + w, y + h);
if (m_layoutDir == wxLayout_RightToLeft)
{
m_graphicContext->PushState();
// text is not mirrored
m_graphicContext->Scale(-1, 1);
x = -x - w;
}
wxCompositionMode curMode = m_graphicContext->GetCompositionMode();
m_graphicContext->SetCompositionMode(wxCOMPOSITION_OVER);
if (m_backgroundMode == wxBRUSHSTYLE_TRANSPARENT)
m_graphicContext->DrawText(text, x, y);
else
m_graphicContext->DrawText(text, x, y, m_graphicContext->CreateBrush(m_textBackgroundColour));
m_graphicContext->SetCompositionMode(curMode);
if (m_layoutDir == wxLayout_RightToLeft)
m_graphicContext->PopState();
}
void wxGTKCairoDCImpl::DoDrawRotatedText(const wxString& text, int x, int y, double angle)
{
wxCHECK_RET(IsOk(), "invalid DC");
// save current bounding box
// rotation will cause DoDrawText() to update it incorrectly
const bool isBBoxValid = m_isBBoxValid;
const int minX = m_minX;
const int minY = m_minY;
const int maxX = m_maxX;
const int maxY = m_maxY;
const double rad = wxDegToRad(-angle);
m_graphicContext->PushState();
m_graphicContext->Translate(x, y);
m_graphicContext->Rotate(rad);
DoDrawText(text, 0, 0);
m_graphicContext->PopState();
// restore bounding box and update it correctly
m_isBBoxValid = isBBoxValid;
m_minX = minX;
m_minY = minY;
m_maxX = maxX;
m_maxY = maxY;
CalcBoundingBox(x, y);
int w, h;
DoGetTextExtent(text, &w, &h);
cairo_matrix_t m;
cairo_matrix_init_translate(&m, x, y);
cairo_matrix_rotate(&m, rad);
double xx = w, yy = 0;
cairo_matrix_transform_point(&m, &xx, &yy);
CalcBoundingBox(int(xx), int(yy));
xx = w; yy = h;
cairo_matrix_transform_point(&m, &xx, &yy);
CalcBoundingBox(int(xx), int(yy));
xx = 0; yy = h;
cairo_matrix_transform_point(&m, &xx, &yy);
CalcBoundingBox(int(xx), int(yy));
}
void wxGTKCairoDCImpl::DoDrawCheckMark(int x, int y, int width, int height)
{
if (m_layoutDir == wxLayout_RightToLeft)
{
wxCHECK_RET(IsOk(), "invalid DC");
// checkmark is not mirrored
m_graphicContext->PushState();
m_graphicContext->Scale(-1, 1);
BaseType::DoDrawCheckMark(-x - width, y, width, height);
m_graphicContext->PopState();
}
else
BaseType::DoDrawCheckMark(x, y, width, height);
}
wxBitmap wxGTKCairoDCImpl::DoGetAsBitmap(const wxRect* /*subrect*/) const
{
wxFAIL_MSG("DoGetAsBitmap not implemented");
@@ -183,6 +288,12 @@ bool wxGTKCairoDCImpl::DoStretchBlit(int xdest, int ydest, int dstWidth, int dst
}
}
cairo_save(cr);
if (m_layoutDir == wxLayout_RightToLeft)
{
// blit is not mirrored
cairo_scale(cr, -1, 1);
xdest = -xdest - dstWidth;
}
cairo_translate(cr, xdest, ydest);
cairo_rectangle(cr, 0, 0, dstWidth, dstHeight);
double sx, sy;
@@ -250,6 +361,40 @@ wxSize wxGTKCairoDCImpl::GetPPI() const
return wxGCDCImpl::GetPPI();
}
void wxGTKCairoDCImpl::SetLayoutDirection(wxLayoutDirection dir)
{
if (dir == wxLayout_Default && m_window)
dir = m_window->GetLayoutDirection();
if (m_layoutDir != dir)
{
if (m_graphicContext)
{
if (dir == wxLayout_RightToLeft)
{
// wxDC is mirrored for RTL
m_graphicContext->Translate(m_size.x, 0);
m_graphicContext->Scale(-1, 1);
}
else if (m_layoutDir == wxLayout_RightToLeft)
{
m_graphicContext->Scale(-1, 1);
m_graphicContext->Translate(-m_size.x, 0);
}
}
m_layoutDir = dir;
}
}
wxLayoutDirection wxGTKCairoDCImpl::GetLayoutDirection() const
{
// LTR unless explicitly RTL
return
m_layoutDir == wxLayout_RightToLeft
? wxLayout_RightToLeft
: wxLayout_LeftToRight;
}
//-----------------------------------------------------------------------------
wxWindowDCImpl::wxWindowDCImpl(wxWindowDC* owner, wxWindow* window)
@@ -292,6 +437,8 @@ wxWindowDCImpl::wxWindowDCImpl(wxWindowDC* owner, wxWindow* window)
}
if (x || y)
SetDeviceLocalOrigin(x, y);
SetLayoutDirection(wxLayout_Default);
}
else
SetGraphicsContext(wxGraphicsContext::Create());
@@ -326,6 +473,7 @@ wxClientDCImpl::wxClientDCImpl(wxClientDC* owner, wxWindow* window)
cairo_clip(cr);
SetDeviceLocalOrigin(a.x, a.y);
}
SetLayoutDirection(wxLayout_Default);
}
else
SetGraphicsContext(wxGraphicsContext::Create());
@@ -342,6 +490,8 @@ wxPaintDCImpl::wxPaintDCImpl(wxPaintDC* owner, wxWindow* window)
wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr);
gc->EnableOffset(m_contentScaleFactor <= 1);
SetGraphicsContext(gc);
// context is already adjusted for RTL
m_layoutDir = window->GetLayoutDirection();
}
void wxPaintDCImpl::DestroyClippingRegion()
@@ -433,15 +583,23 @@ void wxMemoryDCImpl::Setup()
gc->EnableOffset(m_contentScaleFactor <= 1);
}
SetGraphicsContext(gc);
// re-apply layout direction
const wxLayoutDirection dir = m_layoutDir;
m_layoutDir = wxLayout_Default;
SetLayoutDirection(dir);
}
//-----------------------------------------------------------------------------
wxGTKCairoDC::wxGTKCairoDC(cairo_t* cr, wxWindow* window)
: wxDC(new wxGTKCairoDCImpl(this, window->GetContentScaleFactor()))
wxGTKCairoDC::wxGTKCairoDC(cairo_t* cr, wxWindow* window, wxLayoutDirection dir, int width)
: wxDC(new wxGTKCairoDCImpl(this, window, dir, width))
{
wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(cr);
gc->EnableOffset(window->GetContentScaleFactor() <= 1);
SetGraphicsContext(gc);
if (dir == wxLayout_Default)
SetLayoutDirection(window->GetLayoutDirection());
// else context is already adjusted for RTL
}
#else