summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2018-09-10 02:46:25 +0000
committerB Stack <bgstack15@gmail.com>2018-09-10 02:46:25 +0000
commit728d32e6da9ce66968f8eef47a59505d613e2c1b (patch)
tree0f0441755ff0e6d65e12222d4502c648bffd6a7c /wx+
parent10.3 (diff)
parentpull in latest 10.4 from upstream (diff)
downloadFreeFileSync-728d32e6da9ce66968f8eef47a59505d613e2c1b.tar.gz
FreeFileSync-728d32e6da9ce66968f8eef47a59505d613e2c1b.tar.bz2
FreeFileSync-728d32e6da9ce66968f8eef47a59505d613e2c1b.zip
Merge branch '10.4' into 'master'10.4
pull in latest 10.4 from upstream See merge request opensource-tracking/FreeFileSync!1
Diffstat (limited to 'wx+')
-rwxr-xr-xwx+/choice_enum.h4
-rwxr-xr-xwx+/dc.h11
-rwxr-xr-xwx+/focus.h29
-rwxr-xr-xwx+/graph.cpp4
-rwxr-xr-xwx+/graph.h29
-rwxr-xr-xwx+/grid.cpp56
-rwxr-xr-xwx+/grid.h27
-rwxr-xr-xwx+/image_resources.cpp4
-rwxr-xr-xwx+/image_tools.cpp149
-rwxr-xr-xwx+/image_tools.h2
-rwxr-xr-xwx+/rtl.h5
11 files changed, 158 insertions, 162 deletions
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h
index 97c40b68..2c424b9f 100755
--- a/wx+/choice_enum.h
+++ b/wx+/choice_enum.h
@@ -77,7 +77,9 @@ void setEnumVal(const EnumDescrList<Enum>& mapping, wxChoice& ctrl, Enum value)
{
selectedPos = it - mapping.descrList.begin();
- if (!it->second.second.empty())
+ if (it->second.second.empty())
+ ctrl.UnsetToolTip();
+ else
ctrl.SetToolTip(it->second.second);
}
}
diff --git a/wx+/dc.h b/wx+/dc.h
index 23c70d3f..ff2f81bd 100755
--- a/wx+/dc.h
+++ b/wx+/dc.h
@@ -8,7 +8,6 @@
#define DC_H_4987123956832143243214
#include <unordered_map>
-#include <zen/optional.h>
#include <zen/basic_math.h>
#include <wx/dcbuffer.h> //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER
#include <wx/dcscreen.h>
@@ -104,7 +103,7 @@ private:
//associate "active" clipping area with each DC
static std::unordered_map<wxDC*, wxRect>& refDcToAreaMap() { static std::unordered_map<wxDC*, wxRect> clippingAreas; return clippingAreas; }
- Opt<wxRect> oldRect_;
+ std::optional<wxRect> oldRect_;
wxDC& dc_;
};
@@ -114,13 +113,13 @@ private:
#endif
#if wxALWAYS_NATIVE_DOUBLE_BUFFER
-struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };
+struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::optional<wxBitmap>& buffer) : wxPaintDC(&wnd) {} };
#else
class BufferedPaintDC : public wxMemoryDC
{
public:
- BufferedPaintDC(wxWindow& wnd, Opt<wxBitmap>& buffer) : buffer_(buffer), paintDc_(&wnd)
+ BufferedPaintDC(wxWindow& wnd, std::optional<wxBitmap>& buffer) : buffer_(buffer), paintDc_(&wnd)
{
const wxSize clientSize = wnd.GetClientSize();
if (clientSize.GetWidth() > 0 && clientSize.GetHeight() > 0) //wxBitmap asserts this!! width may be 0; test case "Grid::CornerWin": compare both sides, then change config
@@ -134,7 +133,7 @@ public:
SetLayoutDirection(wxLayout_RightToLeft);
}
else
- buffer = NoValue();
+ buffer = {};
}
~BufferedPaintDC()
@@ -153,7 +152,7 @@ public:
}
private:
- Opt<wxBitmap>& buffer_;
+ std::optional<wxBitmap>& buffer_;
wxPaintDC paintDc_;
};
#endif
diff --git a/wx+/focus.h b/wx+/focus.h
index cd99d010..e2daef79 100755
--- a/wx+/focus.h
+++ b/wx+/focus.h
@@ -44,22 +44,37 @@ Preserving input focus has to be more clever than:
*/
struct FocusPreserver
{
+ FocusPreserver()
+ {
+ if (wxWindow* win = wxWindow::FindFocus())
+ setFocus(win);
+ }
+
~FocusPreserver()
{
//wxTopLevelWindow::IsActive() does NOT call Win32 ::GetActiveWindow()!
//Instead it checks if ::GetFocus() is set somewhere inside the top level
//Note: Both Win32 active and focus windows are *thread-local* values, while foreground window is global! https://blogs.msdn.microsoft.com/oldnewthing/20131016-00/?p=2913
- if (oldFocus_)
- if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocus_))
- if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows!
- oldFocus_->SetFocus();
+
+ if (oldFocusId_ != wxID_ANY)
+ if (wxWindow* oldFocusWin = wxWindow::FindWindowById(oldFocusId_))
+ if (wxTopLevelWindow* topWin = getTopLevelWindow(oldFocusWin))
+ if (topWin->IsActive()) //Linux/macOS: already behaves just like ::GetForegroundWindow() on Windows!
+ oldFocusWin->SetFocus();
}
- wxWindow* getFocus() const { return oldFocus_; }
- void setFocus(wxWindow* win) { oldFocus_ = win; }
+ wxWindowID getFocusId() const { return oldFocusId_; }
+
+ void setFocus(wxWindow* win)
+ {
+ oldFocusId_ = win->GetId();
+ assert(oldFocusId_ != wxID_ANY);
+ }
private:
- wxWindow* oldFocus_ = wxWindow::FindFocus();
+ wxWindowID oldFocusId_ = wxID_ANY;
+ //don't store wxWindow* which may be dangling during ~FocusPreserver()!
+ //test: click on delete folder pair and immediately press F5 => focus window (= FP del button) is defer-deleted during sync
};
}
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index b006cda0..38846523 100755
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -387,8 +387,8 @@ std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, con
for (int i = posFrom; i <= posTo; ++i)
{
const double x = cvrtX.screenToReal(i);
- Opt<CurvePoint> ptLe = getLessEq(x);
- Opt<CurvePoint> ptGe = getGreaterEq(x);
+ std::optional<CurvePoint> ptLe = getLessEq(x);
+ std::optional<CurvePoint> ptGe = getGreaterEq(x);
//both non-existent and invalid return values are mapped to out of expected range: => check on posLe/posGe NOT ptLe/ptGe in the following!
const int posLe = ptLe ? cvrtX.realToScreenRound(ptLe->x) : i + 1;
const int posGe = ptGe ? cvrtX.realToScreenRound(ptGe->x) : i - 1;
diff --git a/wx+/graph.h b/wx+/graph.h
index e0c2c12b..f1ae5d5a 100755
--- a/wx+/graph.h
+++ b/wx+/graph.h
@@ -14,7 +14,6 @@
#include <wx/settings.h>
#include <wx/bitmap.h>
#include <zen/string_tools.h>
-#include <zen/optional.h>
//elegant 2D graph as wxPanel specialization
@@ -63,8 +62,8 @@ struct SparseCurveData : public CurveData
{
SparseCurveData(bool addSteps = false) : addSteps_(addSteps) {} //addSteps: add points to get a staircase effect or connect points via a direct line
- virtual Opt<CurvePoint> getLessEq (double x) const = 0;
- virtual Opt<CurvePoint> getGreaterEq(double x) const = 0;
+ virtual std::optional<CurvePoint> getLessEq (double x) const = 0;
+ virtual std::optional<CurvePoint> getGreaterEq(double x) const = 0;
private:
std::vector<CurvePoint> getPoints(double minX, double maxX, const wxSize& areaSizePx) const override;
@@ -80,21 +79,21 @@ struct ArrayCurveData : public SparseCurveData
private:
std::pair<double, double> getRangeX() const override { const size_t sz = getSize(); return { 0.0, sz == 0 ? 0.0 : sz - 1.0}; }
- Opt<CurvePoint> getLessEq(double x) const override
+ std::optional<CurvePoint> getLessEq(double x) const override
{
const size_t sz = getSize();
const size_t pos = std::min<ptrdiff_t>(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty!
if (pos < sz)
return CurvePoint(pos, getValue(pos));
- return NoValue();
+ return {};
}
- Opt<CurvePoint> getGreaterEq(double x) const override
+ std::optional<CurvePoint> getGreaterEq(double x) const override
{
const size_t pos = std::max<ptrdiff_t>(std::ceil(x), 0); //[!] use std::max with signed type!
if (pos < getSize())
return CurvePoint(pos, getValue(pos));
- return NoValue();
+ return {};
}
};
@@ -246,7 +245,7 @@ public:
MainAttributes& setMinY(double newMinY) { minY = newMinY; return *this; }
MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; return *this; }
- MainAttributes& setAutoSize() { minX = maxX = minY = maxY = NoValue(); return *this; }
+ MainAttributes& setAutoSize() { minX = maxX = minY = maxY = {}; return *this; }
MainAttributes& setLabelX(PosLabelX posX, int height = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr)
{
@@ -272,18 +271,18 @@ public:
private:
friend class Graph2D;
- Opt<double> minX; //x-range to visualize
- Opt<double> maxX; //
+ std::optional<double> minX; //x-range to visualize
+ std::optional<double> maxX; //
- Opt<double> minY; //y-range to visualize
- Opt<double> maxY; //
+ std::optional<double> minY; //y-range to visualize
+ std::optional<double> maxY; //
PosLabelX labelposX = LABEL_X_BOTTOM;
- Opt<int> xLabelHeight;
+ std::optional<int> xLabelHeight;
std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>();
PosLabelY labelposY = LABEL_Y_LEFT;
- Opt<int> yLabelWidth;
+ std::optional<int> yLabelWidth;
std::shared_ptr<LabelFormatter> labelFmtY = std::make_shared<DecimalNumberFormatter>();
std::map<PosCorner, wxString> cornerTexts;
@@ -337,7 +336,7 @@ private:
MainAttributes attr_; //global attributes
- Opt<wxBitmap> doubleBuffer_;
+ std::optional<wxBitmap> doubleBuffer_;
using CurveList = std::vector<std::pair<std::shared_ptr<CurveData>, CurveAttributes>>;
CurveList curves_;
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 52ee6e6b..65311ffa 100755
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -310,23 +310,21 @@ public:
protected:
void setToolTip(const std::wstring& text) //proper fix for wxWindow
{
- wxToolTip* tt = GetToolTip();
-
- const wxString oldText = tt ? tt->GetTip() : wxString();
- if (text != oldText)
+ if (text != GetToolTipText())
{
if (text.empty())
- SetToolTip(nullptr); //wxGTK doesn't allow wxToolTip with empty text!
+ UnsetToolTip(); //wxGTK doesn't allow wxToolTip with empty text!
else
{
- //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width)
+ wxToolTip* tt = GetToolTip();
if (!tt)
- SetToolTip(new wxToolTip(L"a b\n\
- a b")); //ugly, but working (on Windows)
- tt = GetToolTip(); //should be bound by now
- assert(tt);
- if (tt)
- tt->SetTip(text);
+ {
+ //wxWidgets bug: tooltip multiline property is defined by first tooltip text containing newlines or not (same is true for maximum width)
+ tt = new wxToolTip(L"a b\n\
+ a b"); //ugly, but working (on Windows)
+ SetToolTip(tt); //pass ownership
+ }
+ tt->SetTip(text);
}
}
}
@@ -391,7 +389,7 @@ private:
}
Grid& parent_;
- Opt<wxBitmap> doubleBuffer_;
+ std::optional<wxBitmap> doubleBuffer_;
};
//----------------------------------------------------------------------------------------------------------------
@@ -689,12 +687,12 @@ private:
activeResizing_.reset();
activeClickOrMove_.reset();
- if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
+ if (std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
if (action->wantResize)
{
if (!event.LeftDClick()) //double-clicks never seem to arrive here; why is this checked at all???
- if (Opt<int> colWidth = refParent().getColWidth(action->col))
+ if (std::optional<int> colWidth = refParent().getColWidth(action->col))
activeResizing_ = std::make_unique<ColumnResizing>(*this, action->col, *colWidth, event.GetPosition().x);
}
else //a move or single click
@@ -724,7 +722,7 @@ private:
}
else //notify single label click
{
- if (const Opt<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom()))
+ if (const std::optional<ColumnType> colType = refParent().colToType(activeClickOrMove_->getColumnFrom()))
sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_LEFT, event, *colType));
}
activeClickOrMove_.reset();
@@ -745,7 +743,7 @@ private:
void onMouseLeftDouble(wxMouseEvent& event) override
{
- if (Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
+ if (std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
if (action->wantResize)
{
//auto-size visible range on double-click
@@ -790,7 +788,7 @@ private:
}
else
{
- if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
+ if (const std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
highlightCol_ = action->col;
@@ -801,7 +799,7 @@ private:
}
else
{
- highlightCol_ = NoValue();
+ highlightCol_ = {};
SetCursor(*wxSTANDARD_CURSOR);
}
}
@@ -823,7 +821,7 @@ private:
void onLeaveWindow(wxMouseEvent& event) override
{
- highlightCol_ = NoValue(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize!
+ highlightCol_ = {}; //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight_ is drawn unconditionally during move/resize!
Refresh();
event.Skip();
@@ -831,9 +829,9 @@ private:
void onMouseRightDown(wxMouseEvent& event) override
{
- if (const Opt<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
+ if (const std::optional<ColAction> action = refParent().clientPosToColumnAction(event.GetPosition()))
{
- if (const Opt<ColumnType> colType = refParent().colToType(action->col))
+ if (const std::optional<ColumnType> colType = refParent().colToType(action->col))
sendEventNow(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, event, *colType)); //notify right click
else assert(false);
}
@@ -847,7 +845,7 @@ private:
std::unique_ptr<ColumnResizing> activeResizing_;
std::unique_ptr<ColumnMove> activeClickOrMove_;
- Opt<size_t> highlightCol_; //column during mouse-over
+ std::optional<size_t> highlightCol_; //column during mouse-over
};
//----------------------------------------------------------------------------------------------------------------
@@ -1080,8 +1078,8 @@ private:
activeSelection_.reset();
}
- highlight_.row = -1;
- Refresh();
+ highlight_.row = -1;
+ Refresh();
//event.Skip(); -> we DID handle it!
}
@@ -1146,8 +1144,8 @@ private:
size_t getStartRow () const { return rowStart_; }
size_t getCurrentRow () const { return rowCurrent_; }
bool isPositiveSelect() const { return positiveSelect_; } //are we selecting or unselecting?
- bool gridWasCleared () const { return gridWasCleared_; }
-
+ bool gridWasCleared () const { return gridWasCleared_; }
+
const GridClickEvent& getFirstClick() const { return firstClick_; }
void evalMousePos()
@@ -1823,7 +1821,7 @@ wxWindow& Grid::getMainWin () { return *mainWin_; }
const wxWindow& Grid::getMainWin() const { return *mainWin_; }
-Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
+std::optional<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
{
const int absPosX = CalcUnscrolledPosition(pos).x;
if (absPosX >= 0)
@@ -1851,7 +1849,7 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
}
}
}
- return NoValue();
+ return {};
}
diff --git a/wx+/grid.h b/wx+/grid.h
index b9a04842..732a4fcb 100755
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -12,7 +12,6 @@
#include <vector>
#include <wx/scrolwin.h>
#include <zen/basic_math.h>
-#include <zen/optional.h>
//a user-friendly, extensible and high-performance grid control
@@ -52,13 +51,13 @@ struct GridSelectEvent : public wxCommandEvent
{
GridSelectEvent(size_t rowFirst, size_t rowLast, bool positive, const GridClickEvent* mouseClick) :
wxCommandEvent(EVENT_GRID_SELECT_RANGE), rowFirst_(rowFirst), rowLast_(rowLast), positive_(positive),
- mouseClick_(mouseClick ? *mouseClick : Opt<GridClickEvent>()) { assert(rowFirst <= rowLast); }
+ mouseClick_(mouseClick ? *mouseClick : std::optional<GridClickEvent>()) { assert(rowFirst <= rowLast); }
GridSelectEvent* Clone() const override { return new GridSelectEvent(*this); }
const size_t rowFirst_; //selected range: [rowFirst_, rowLast_)
const size_t rowLast_;
const bool positive_; //"false" when clearing selection!
- const Opt<GridClickEvent> mouseClick_; //filled unless selection was performed via keyboard shortcuts
+ const std::optional<GridClickEvent> mouseClick_; //filled unless selection was performed via keyboard shortcuts
};
struct GridLabelClickEvent : public wxMouseEvent
@@ -299,12 +298,12 @@ private:
int getColWidthsSum(int mainWinWidth) const;
std::vector<int> getColStretchedWidths(int clientWidth) const; //final width = (normalized) (stretchedWidth + offset)
- Opt<int> getColWidth(size_t col) const
+ std::optional<int> getColWidth(size_t col) const
{
const auto& widths = getColWidths();
if (col < widths.size())
return widths[col].width;
- return NoValue();
+ return {};
}
void setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEventPolicy, bool notifyAsync = false);
@@ -321,7 +320,7 @@ private:
bool wantResize = false; //"!wantResize" means "move" or "single click"
size_t col = 0;
};
- Opt<ColAction> clientPosToColumnAction(const wxPoint& pos) const;
+ std::optional<ColAction> clientPosToColumnAction(const wxPoint& pos) const;
void moveColumn(size_t colFrom, size_t colTo);
ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos) const; //return < 0 on error
@@ -363,18 +362,10 @@ private:
template <class ColAttrReal>
std::vector<ColAttrReal> makeConsistent(const std::vector<ColAttrReal>& attribs, const std::vector<ColAttrReal>& defaults)
{
- using ColTypeReal = decltype(ColAttrReal().type);
- std::vector<ColAttrReal> output;
-
- std::set<ColTypeReal> usedTypes; //remove duplicates
- auto appendUnique = [&](const std::vector<ColAttrReal>& attr)
- {
- std::copy_if(attr.begin(), attr.end(), std::back_inserter(output),
- [&](const ColAttrReal& a) { return usedTypes.insert(a.type).second; });
- };
- appendUnique(attribs);
- appendUnique(defaults); //make sure each type is existing!
-
+ std::vector<ColAttrReal> output = attribs;
+ //make sure each type is existing!
+ output.insert(output.end(), defaults.begin(), defaults.end());
+ removeDuplicates(output, [](const ColAttrReal& lhs, const ColAttrReal& rhs) { return lhs.type < rhs.type; });
return output;
}
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index bbeeff8b..d0449fe5 100755
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -106,7 +106,7 @@ class DpiParallelScaler
public:
DpiParallelScaler(int hqScale) : hqScale_(hqScale) { assert(hqScale > 1); }
- ~DpiParallelScaler() { threadGroup_ = zen::NoValue(); } //DpiParallelScaler must out-live threadGroup!!!
+ ~DpiParallelScaler() { threadGroup_ = {}; } //DpiParallelScaler must out-live threadGroup!!!
void add(const wxString& name, const wxImage& img)
{
@@ -141,7 +141,7 @@ private:
Protected<std::vector<std::pair<std::wstring, ImageHolder>>> result_;
using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>;
- Opt<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") };
+ std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") };
//hardware_concurrency() == 0 if "not computable or well defined"
};
diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp
index 0cb0e328..4748a590 100755
--- a/wx+/image_tools.cpp
+++ b/wx+/image_tools.cpp
@@ -14,38 +14,45 @@ using namespace zen;
namespace
{
-void writeToImage(const wxImage& source, wxImage& target, const wxPoint& pos)
+void writeToImage(wxImage& output, const wxImage& top, const wxPoint& pos)
{
- const int srcWidth = source.GetWidth ();
- const int srcHeight = source.GetHeight();
- const int trgWidth = target.GetWidth ();
+ const int topWidth = top.GetWidth ();
+ const int topHeight = top.GetHeight();
+ const int outWidth = output.GetWidth();
- if (srcWidth > 0 && srcHeight > 0)
- {
- assert(0 <= pos.x && pos.x + srcWidth <= trgWidth ); //draw area must be a
- assert(0 <= pos.y && pos.y + srcHeight <= target.GetHeight()); //subset of target image!
- assert(target.HasAlpha());
+ assert(0 <= pos.x && pos.x + topWidth <= outWidth ); //draw area must be a
+ assert(0 <= pos.y && pos.y + topHeight <= output.GetHeight()); //subset of output image!
+ assert(top.HasAlpha() && output.HasAlpha());
- {
- const unsigned char* sourcePtr = source.GetData();
- unsigned char* targetPtr = target.GetData() + 3 * (pos.x + pos.y * trgWidth);
+ //https://en.wikipedia.org/wiki/Alpha_compositing
+ const unsigned char* topRgb = top.GetData();
+ const unsigned char* topAlpha = top.GetAlpha();
- for (int row = 0; row < srcHeight; ++row)
- ::memcpy(targetPtr + 3 * row * trgWidth, sourcePtr + 3 * row * srcWidth, 3 * srcWidth);
- }
+ for (int y = 0; y < topHeight; ++y)
+ {
+ unsigned char* outRgb = output.GetData () + 3 * (pos.x + (pos.y + y) * outWidth);
+ unsigned char* outAlpha = output.GetAlpha() + pos.x + (pos.y + y) * outWidth;
- //handle alpha channel
+ for (int x = 0; x < topWidth; ++x)
{
- unsigned char* targetPtr = target.GetAlpha() + pos.x + pos.y * trgWidth;
- if (source.HasAlpha())
+ const int w1 = *topAlpha; //alpha-composition interpreted as weighted average
+ const int w2 = *outAlpha * (255 - w1) / 255;
+ const int wSum = w1 + w2;
+
+ auto calcColor = [w1, w2, wSum](unsigned char colTop, unsigned char colBot)
{
- const unsigned char* sourcePtr = source.GetAlpha();
- for (int row = 0; row < srcHeight; ++row)
- ::memcpy(targetPtr + row * trgWidth, sourcePtr + row * srcWidth, srcWidth);
- }
- else
- for (int row = 0; row < srcHeight; ++row)
- ::memset(targetPtr + row * trgWidth, wxIMAGE_ALPHA_OPAQUE, srcWidth);
+ return static_cast<unsigned char>(wSum == 0 ? 0 : (colTop * w1 + colBot * w2) / wSum);
+ };
+ outRgb[0] = calcColor(topRgb[0], outRgb[0]);
+ outRgb[1] = calcColor(topRgb[1], outRgb[1]);
+ outRgb[2] = calcColor(topRgb[2], outRgb[2]);
+
+ *outAlpha = static_cast<unsigned char>(wSum);
+
+ topRgb += 3;
+ outRgb += 3;
+ ++topAlpha;
+ ++outAlpha;
}
}
}
@@ -64,16 +71,12 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
int width = std::max(img1Width, img2Width);
int height = std::max(img1Height, img2Height);
- switch (dir)
- {
- case ImageStackLayout::HORIZONTAL:
- width = img1Width + gap + img2Width;
- break;
- case ImageStackLayout::VERTICAL:
- height = img1Height + gap + img2Height;
- break;
- }
+ if (dir == ImageStackLayout::HORIZONTAL)
+ width = img1Width + gap + img2Width;
+ else
+ height = img1Height + gap + img2Height;
+
wxImage output(width, height);
output.SetAlpha();
::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height);
@@ -96,13 +99,13 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
switch (dir)
{
case ImageStackLayout::HORIZONTAL:
- writeToImage(img1, output, wxPoint(0, calcPos(img1Height, height)));
- writeToImage(img2, output, wxPoint(img1Width + gap, calcPos(img2Height, height)));
+ writeToImage(output, img1, wxPoint(0, calcPos(img1Height, height)));
+ writeToImage(output, img2, wxPoint(img1Width + gap, calcPos(img2Height, height)));
break;
case ImageStackLayout::VERTICAL:
- writeToImage(img1, output, wxPoint(calcPos(img1Width, width), 0));
- writeToImage(img2, output, wxPoint(calcPos(img2Width, width), img1Height + gap));
+ writeToImage(output, img1, wxPoint(calcPos(img1Width, width), 0));
+ writeToImage(output, img2, wxPoint(calcPos(img2Width, width), img1Height + gap));
break;
}
return output;
@@ -111,26 +114,6 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
namespace
{
-void calcAlphaForBlackWhiteImage(wxImage& image) //assume black text on white background
-{
- assert(image.HasAlpha());
- if (unsigned char* alphaPtr = image.GetAlpha())
- {
- const int pixelCount = image.GetWidth() * image.GetHeight();
- const unsigned char* dataPtr = image.GetData();
- for (int i = 0; i < pixelCount; ++ i)
- {
- const unsigned char r = *dataPtr++;
- const unsigned char g = *dataPtr++;
- const unsigned char b = *dataPtr++;
-
- //black(0,0,0) becomes fully opaque(255), while white(255,255,255) becomes transparent(0)
- alphaPtr[i] = static_cast<unsigned char>((255 - r + 255 - g + 255 - b) / 3); //mixed mode arithmetics!
- }
- }
-}
-
-
std::vector<std::pair<wxString, wxSize>> getTextExtentInfo(const wxString& text, const wxFont& font)
{
wxMemoryDC dc; //the context used for bitmaps
@@ -201,32 +184,39 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
wxImage output(newBitmap.ConvertToImage());
output.SetAlpha();
- calcAlphaForBlackWhiteImage(output);
-
- //apply actual text color
- unsigned char* dataPtr = output.GetData();
+ unsigned char* rgb = output.GetData();
+ unsigned char* alpha = output.GetAlpha();
const int pixelCount = output.GetWidth() * output.GetHeight();
- for (int i = 0; i < pixelCount; ++ i)
+
+ for (int i = 0; i < pixelCount; ++i)
{
- *dataPtr++ = col.Red();
- *dataPtr++ = col.Green();
- *dataPtr++ = col.Blue();
+ //black(0,0,0) becomes wxIMAGE_ALPHA_OPAQUE(255), while white(255,255,255) becomes wxIMAGE_ALPHA_TRANSPARENT(0)
+ *alpha++ = static_cast<unsigned char>((255 - rgb[0] + 255 - rgb[1] + 255 - rgb[2]) / 3); //mixed mode arithmetics!
+
+ rgb[0] = col.Red (); //
+ rgb[1] = col.Green(); //apply actual text color
+ rgb[2] = col.Blue (); //
+
+ rgb += 3;
}
return output;
}
-wxBitmap zen::layOver(const wxBitmap& background, const wxBitmap& foreground, int alignment)
+wxBitmap zen::layOver(const wxBitmap& back, const wxBitmap& front, int alignment)
{
- if (!foreground.IsOk()) return background;
+ if (!front.IsOk()) return back;
+
+ const int width = std::max(back.GetWidth(), front.GetWidth());
+ const int height = std::max(back.GetHeight(), front.GetHeight());
- assert(foreground.HasAlpha() == background.HasAlpha()); //we don't support mixed-mode brittleness!
+ assert(front.HasAlpha() == back.HasAlpha()); //we don't support mixed-mode brittleness!
const int offsetX = [&]
{
if (alignment & wxALIGN_RIGHT)
- return background.GetWidth() - foreground.GetWidth();
+ return back.GetWidth() - front.GetWidth();
if (alignment & wxALIGN_CENTER_HORIZONTAL)
- return (background.GetWidth() - foreground.GetWidth()) / 2;
+ return (back.GetWidth() - front.GetWidth()) / 2;
static_assert(wxALIGN_LEFT == 0);
return 0;
@@ -235,19 +225,22 @@ wxBitmap zen::layOver(const wxBitmap& background, const wxBitmap& foreground, i
const int offsetY = [&]
{
if (alignment & wxALIGN_BOTTOM)
- return background.GetHeight() - foreground.GetHeight();
+ return back.GetHeight() - front.GetHeight();
if (alignment & wxALIGN_CENTER_VERTICAL)
- return (background.GetHeight() - foreground.GetHeight()) / 2;
+ return (back.GetHeight() - front.GetHeight()) / 2;
static_assert(wxALIGN_TOP == 0);
return 0;
}();
- wxBitmap output(background.ConvertToImage()); //attention: wxBitmap/wxImage use ref-counting without copy on write!
- {
- wxMemoryDC dc(output);
- dc.DrawBitmap(foreground, offsetX, offsetY);
- }
+ //can't use wxMemoryDC and wxDC::DrawBitmap(): no alpha channel support on wxGTK!
+ wxImage output(width, height);
+ output.SetAlpha();
+ ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height);
+
+ const wxPoint posBack(std::max(-offsetX, 0), std::max(-offsetY, 0));
+ writeToImage(output, back .ConvertToImage(), posBack);
+ writeToImage(output, front.ConvertToImage(), posBack + wxPoint(offsetX, offsetY));
return output;
}
diff --git a/wx+/image_tools.h b/wx+/image_tools.h
index ca82a031..06d7f0ba 100755
--- a/wx+/image_tools.h
+++ b/wx+/image_tools.h
@@ -34,7 +34,7 @@ wxImage stackImages(const wxImage& img1, const wxImage& img2, ImageStackLayout d
wxImage createImageFromText(const wxString& text, const wxFont& font, const wxColor& col, ImageStackAlignment textAlign = ImageStackAlignment::LEFT); //CENTER/LEFT/RIGHT
-wxBitmap layOver(const wxBitmap& background, const wxBitmap& foreground, int alignment = wxALIGN_CENTER);
+wxBitmap layOver(const wxBitmap& back, const wxBitmap& front, int alignment = wxALIGN_CENTER);
wxImage greyScale(const wxImage& img); //greyscale + brightness adaption
wxBitmap greyScale(const wxBitmap& bmp); //
diff --git a/wx+/rtl.h b/wx+/rtl.h
index e479c5a8..26380f9d 100755
--- a/wx+/rtl.h
+++ b/wx+/rtl.h
@@ -7,7 +7,6 @@
#ifndef RTL_H_0183487180058718273432148
#define RTL_H_0183487180058718273432148
-#include <zen/optional.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/app.h>
@@ -16,7 +15,7 @@
namespace zen
{
//functions supporting right-to-left GUI layout
-void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer);
+void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer);
void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment);
//wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed
@@ -56,7 +55,7 @@ void drawBitmapAligned(wxDC& dc, const wxBitmap& image, const wxRect& rect, int
inline
-void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, Opt<wxBitmap>& buffer)
+void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer)
{
if (dc.GetLayoutDirection() == wxLayout_RightToLeft)
{
bgstack15