summaryrefslogtreecommitdiff
path: root/wx+/rtl.h
blob: 84009a86f3673bb3d3d331717b1470991d15dbb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html       *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved        *
// **************************************************************************

#ifndef RTL_H_0183487180058718273432148
#define RTL_H_0183487180058718273432148

#include <memory>
#include <wx/dcmemory.h>
#include <wx/dcmirror.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/app.h>
#include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER

namespace zen
{
//functions supporting right-to-left GUI layout

void drawBitmapRtlMirror(wxDC& dc,
                         const wxBitmap& image,
                         const wxRect& rect,
                         int alignment,
                         std::unique_ptr<wxBitmap>& buffer); //mirror image if layout is RTL + fix some strange wxDC::Blit bug on RTL

void drawBitmapRtlNoMirror(wxDC& dc,  //wxDC::DrawLabel does already NOT mirror by default (but does a crappy job at it, surprise)
                           const wxBitmap& image,
                           const wxRect& rect,
                           int alignment,
                           std::unique_ptr<wxBitmap>& buffer);

void drawIconRtlNoMirror(wxDC& dc, //wxDC::DrawIcon DOES mirror by default
                         const wxIcon& icon,
                         const wxPoint& pt,
                         std::unique_ptr<wxBitmap>& buffer);

wxBitmap mirrorIfRtl(const wxBitmap& bmp);


/*
class BufferedPaintDC
{
public:
    BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer);
};
*/
//a fix for a poor wxWidgets implementation (wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active)


//manual text flow correction: http://www.w3.org/International/articles/inline-bidi-markup/








//---------------------- implementation ------------------------
namespace
{
template <class DrawImageFun>
void drawRtlImpl(wxDC& dc, const wxRect& rect, std::unique_ptr<wxBitmap>& buffer, bool doMirror, DrawImageFun draw)
{
    if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
    {
        if (!buffer || buffer->GetWidth() != rect.width || buffer->GetHeight() < rect.height) //[!] since we do a mirror, width needs to match exactly!
            buffer.reset(new wxBitmap(rect.width, rect.height));

        wxMemoryDC memDc(*buffer);
        memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction!

        if (!doMirror)
        {
            *buffer = wxBitmap(buffer->ConvertToImage().Mirror());
            memDc.SelectObject(*buffer);
        }

        draw(memDc, wxRect(0, 0, rect.width, rect.height));
        //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug! so it's a quadruple mirror! :>

        if (!doMirror)
        {
            *buffer = wxBitmap(buffer->ConvertToImage().Mirror());
            memDc.SelectObject(*buffer);
        }

        dc.Blit(rect.GetTopLeft(), rect.GetSize(), &memDc, wxPoint(0, 0)); //blit out: mirror once again
    }
    else
        draw(dc, rect);
}
}


inline
void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::unique_ptr<wxBitmap>& buffer)
{
    return drawRtlImpl(dc, rect, buffer, true, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); });
}

inline
void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::unique_ptr<wxBitmap>& buffer)
{
    if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
        if ((alignment & wxALIGN_CENTER_HORIZONTAL) == 0) //we still *do* want to mirror alignment!
            alignment ^= wxALIGN_RIGHT;
    static_assert(wxALIGN_LEFT == 0, "doh");
    return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawLabel(wxString(), image, rect2, alignment); });
}

inline
void drawIconRtlNoMirror(wxDC& dc, const wxIcon& icon, const wxPoint& pt, std::unique_ptr<wxBitmap>& buffer)
{
    wxRect rect(pt.x, pt.y, icon.GetWidth(), icon.GetHeight());
    return drawRtlImpl(dc, rect, buffer, false, [&](wxDC& dc2, const wxRect& rect2) { dc2.DrawIcon(icon, rect2.GetTopLeft()); });
}


inline
wxBitmap mirrorIfRtl(const wxBitmap& bmp)
{
    if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
        return bmp.ConvertToImage().Mirror();
    else
        return bmp;
}


#ifndef wxALWAYS_NATIVE_DOUBLE_BUFFER
#error we need this one!
#endif

#if wxALWAYS_NATIVE_DOUBLE_BUFFER
struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };

#else
class BufferedPaintDC : public wxMemoryDC
{
public:
    BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer) : buffer_(buffer), paintDc(&wnd)
    {
        const wxSize clientSize = wnd.GetClientSize();
        if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight()))
            buffer.reset(new wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()));

        SelectObject(*buffer);

        if (paintDc.IsOk() && paintDc.GetLayoutDirection() == wxLayout_RightToLeft)
            SetLayoutDirection(wxLayout_RightToLeft);
    }

    ~BufferedPaintDC()
    {
        if (GetLayoutDirection() == wxLayout_RightToLeft)
        {
            paintDc.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit()
            SetLayoutDirection(wxLayout_LeftToRight);         //
        }

        const wxPoint origin = GetDeviceOrigin();
        paintDc.Blit(0, 0, buffer_->GetWidth(), buffer_->GetHeight(), this, -origin.x, -origin.y);
    }

private:
    std::unique_ptr<wxBitmap>& buffer_;
    wxPaintDC paintDc;
};
#endif
}

#endif //RTL_H_0183487180058718273432148
bgstack15