summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2018-05-09 00:11:35 +0200
committerDaniel Wilhelm <shieldwed@outlook.com>2018-05-09 00:11:35 +0200
commit015bb675d6eb177900c8ac94a6d35edc5ad90576 (patch)
treeedde4153ce9b2ba6bdaf9d3c0af0966ed6dfd717 /wx+
parent9.8 (diff)
downloadFreeFileSync-015bb675d6eb177900c8ac94a6d35edc5ad90576.tar.gz
FreeFileSync-015bb675d6eb177900c8ac94a6d35edc5ad90576.tar.bz2
FreeFileSync-015bb675d6eb177900c8ac94a6d35edc5ad90576.zip
9.9
Diffstat (limited to 'wx+')
-rwxr-xr-xwx+/bitmap_button.h3
-rwxr-xr-xwx+/choice_enum.h2
-rwxr-xr-xwx+/dc.h21
-rwxr-xr-xwx+/font_size.h1
-rwxr-xr-xwx+/graph.cpp72
-rwxr-xr-xwx+/graph.h56
-rwxr-xr-xwx+/grid.cpp82
-rwxr-xr-xwx+/grid.h8
-rwxr-xr-xwx+/image_holder.h52
-rwxr-xr-xwx+/image_resources.cpp258
-rwxr-xr-xwx+/image_resources.h2
-rwxr-xr-xwx+/image_tools.cpp2
-rwxr-xr-xwx+/popup_dlg.cpp23
-rwxr-xr-xwx+/popup_dlg_generated.cpp5
-rwxr-xr-xwx+/popup_dlg_generated.h4
-rwxr-xr-xwx+/std_button_layout.h9
-rwxr-xr-xwx+/tooltip.cpp22
-rwxr-xr-xwx+/zlib_wrap.cpp4
18 files changed, 457 insertions, 169 deletions
diff --git a/wx+/bitmap_button.h b/wx+/bitmap_button.h
index 379cf52d..dc7615c1 100755
--- a/wx+/bitmap_button.h
+++ b/wx+/bitmap_button.h
@@ -9,6 +9,7 @@
#include <wx/bmpbuttn.h>
#include "image_tools.h"
+#include "dc.h"
namespace zen
@@ -28,7 +29,7 @@ public:
wxBitmapButton(parent, id, wxNullBitmap, pos, size, style | wxBU_AUTODRAW, validator, name) { SetLabel(label); }
};
-void setBitmapTextLabel(wxBitmapButton& btn, const wxImage& img, const wxString& text, int gap = 5, int border = 5);
+void setBitmapTextLabel(wxBitmapButton& btn, const wxImage& img, const wxString& text, int gap = fastFromDIP(5), int border = fastFromDIP(5));
//set bitmap label flicker free:
void setImage(wxBitmapButton& button, const wxBitmap& bmp);
diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h
index 6b2edd01..97c40b68 100755
--- a/wx+/choice_enum.h
+++ b/wx+/choice_enum.h
@@ -107,8 +107,6 @@ template <class Enum> void updateTooltipEnumVal(const EnumDescrList<Enum>& mappi
if (item.first == value)
ctrl.SetToolTip(item.second.second);
}
-
}
-
#endif //CHOICE_ENUM_H_132413545345687
diff --git a/wx+/dc.h b/wx+/dc.h
index 55047a53..23c70d3f 100755
--- a/wx+/dc.h
+++ b/wx+/dc.h
@@ -9,7 +9,9 @@
#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>
namespace zen
@@ -41,6 +43,25 @@ void clearArea(wxDC& dc, const wxRect& rect, const wxColor& col)
}
+/*
+Standard DPI:
+ Windows/Ubuntu: 96 x 96
+ macOS: wxWidgets uses DIP (note: wxScreenDC().GetPPI() returns 72 x 72 which is a lie; looks like 96 x 96)
+*/
+inline
+int fastFromDIP(int d) //like wxWindow::FromDIP (but tied to primary monitor and buffered)
+{
+
+#ifdef wxHAVE_DPI_INDEPENDENT_PIXELS //pulled from wx/window.h
+ return d; //e.g. macOS, GTK3
+#else
+ assert(wxTheApp); //only call after wxWidgets was initalized!
+ static const int dpiY = wxScreenDC().GetPPI().y; //perf: buffering for calls to ::GetDeviceCaps() needed!?
+ const int defaultDpi = 96;
+ return numeric::round(1.0 * d * dpiY / defaultDpi);
+#endif
+}
+
diff --git a/wx+/font_size.h b/wx+/font_size.h
index 636b07aa..2f2d377c 100755
--- a/wx+/font_size.h
+++ b/wx+/font_size.h
@@ -10,6 +10,7 @@
#include <zen/basic_math.h>
#include <wx/window.h>
#include <zen/scope_guard.h>
+#include "dc.h"
namespace zen
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index dbce3769..b006cda0 100755
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -203,12 +203,11 @@ void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const Conver
void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, Graph2D::PosCorner pos, const wxColor& backgroundColor)
{
if (txt.empty()) return;
- const int borderX = 5;
- const int borderY = 2; //it looks like wxDC::GetMultiLineTextExtent() precisely returns width, but too large a height: maybe they consider "text row height"?
- wxSize txtExtent = dc.GetMultiLineTextExtent(txt);
- txtExtent.x += 2 * borderX;
- txtExtent.y += 2 * borderY;
+ const wxSize border(fastFromDIP(5), fastFromDIP(2));
+ //it looks like wxDC::GetMultiLineTextExtent() precisely returns width, but too large a height: maybe they consider "text row height"?
+
+ const wxSize boxExtent = dc.GetMultiLineTextExtent(txt) + 2 * border;
wxPoint drawPos = graphArea.GetTopLeft();
switch (pos)
@@ -216,24 +215,24 @@ void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, Grap
case Graph2D::CORNER_TOP_LEFT:
break;
case Graph2D::CORNER_TOP_RIGHT:
- drawPos.x += graphArea.width - txtExtent.GetWidth();
+ drawPos.x += graphArea.width - boxExtent.GetWidth();
break;
case Graph2D::CORNER_BOTTOM_LEFT:
- drawPos.y += graphArea.height - txtExtent.GetHeight();
+ drawPos.y += graphArea.height - boxExtent.GetHeight();
break;
case Graph2D::CORNER_BOTTOM_RIGHT:
- drawPos.x += graphArea.width - txtExtent.GetWidth();
- drawPos.y += graphArea.height - txtExtent.GetHeight();
+ drawPos.x += graphArea.width - boxExtent.GetWidth();
+ drawPos.y += graphArea.height - boxExtent.GetHeight();
break;
}
{
//add text shadow to improve readability:
wxDCTextColourChanger dummy(dc, backgroundColor);
- dc.DrawText(txt, drawPos + wxPoint(borderX + 1, borderY + 1));
+ dc.DrawText(txt, drawPos + border + wxSize(fastFromDIP(1), fastFromDIP(1)));
}
wxDCTextColourChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
- dc.DrawText(txt, drawPos + wxPoint(borderX, borderY));
+ dc.DrawText(txt, drawPos + border);
}
@@ -452,8 +451,7 @@ Graph2D::Graph2D(wxWindow* parent,
{
Connect(wxEVT_PAINT, wxPaintEventHandler(Graph2D::onPaintEvent), nullptr, this);
Connect(wxEVT_SIZE, wxSizeEventHandler (Graph2D::onSizeEvent ), nullptr, this);
- //http://wiki.wxwidgets.org/Flicker-Free_Drawing
- Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(Graph2D::onEraseBackGround), nullptr, this);
+ Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) {}); //http://wiki.wxwidgets.org/Flicker-Free_Drawing
//SetDoubleBuffered(true); slow as hell!
@@ -548,6 +546,9 @@ void Graph2D::render(wxDC& dc) const
//wxPanel::GetClassDefaultAttributes().colBg :
//wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
+ const int xLabelHeight = attr_.xLabelHeight ? *attr_.xLabelHeight : GetCharHeight() + fastFromDIP(2) /*margin*/;
+ const int yLabelWidth = attr_.yLabelWidth ? *attr_.yLabelWidth : dc.GetTextExtent(L"1,23457e+07").x;
+
/*
-----------------------
| | x-label |
@@ -562,12 +563,12 @@ void Graph2D::render(wxDC& dc) const
switch (attr_.labelposX)
{
case LABEL_X_TOP:
- graphArea.y += attr_.xLabelHeight;
- graphArea.height -= attr_.xLabelHeight;
+ graphArea.y += xLabelHeight;
+ graphArea.height -= xLabelHeight;
break;
case LABEL_X_BOTTOM:
- xLabelPosY += clientRect.height - attr_.xLabelHeight;
- graphArea.height -= attr_.xLabelHeight;
+ xLabelPosY += clientRect.height - xLabelHeight;
+ graphArea.height -= xLabelHeight;
break;
case LABEL_X_NONE:
break;
@@ -575,17 +576,20 @@ void Graph2D::render(wxDC& dc) const
switch (attr_.labelposY)
{
case LABEL_Y_LEFT:
- graphArea.x += attr_.yLabelWidth;
- graphArea.width -= attr_.yLabelWidth;
+ graphArea.x += yLabelWidth;
+ graphArea.width -= yLabelWidth;
break;
case LABEL_Y_RIGHT:
- yLabelPosX += clientRect.width - attr_.yLabelWidth;
- graphArea.width -= attr_.yLabelWidth;
+ yLabelPosX += clientRect.width - yLabelWidth;
+ graphArea.width -= yLabelWidth;
break;
case LABEL_Y_NONE:
break;
}
+ assert(attr_.labelposX == LABEL_X_NONE || attr_.labelFmtX);
+ assert(attr_.labelposY == LABEL_Y_NONE || attr_.labelFmtY);
+
{
//paint graph background (excluding label area)
wxDCPenChanger dummy (dc, getBorderColor());
@@ -597,13 +601,13 @@ void Graph2D::render(wxDC& dc) const
}
//set label areas respecting graph area border!
- const wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, attr_.xLabelHeight);
- const wxRect yLabelArea(yLabelPosX, graphArea.y, attr_.yLabelWidth, graphArea.height);
+ const wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, xLabelHeight);
+ const wxRect yLabelArea(yLabelPosX, graphArea.y, yLabelWidth, graphArea.height);
const wxPoint graphAreaOrigin = graphArea.GetTopLeft();
//detect x value range
- double minX = attr_.minXauto ? std::numeric_limits<double>::infinity() : attr_.minX; //automatic: ensure values are initialized by first curve
- double maxX = attr_.maxXauto ? -std::numeric_limits<double>::infinity() : attr_.maxX; //
+ double minX = attr_.minX ? *attr_.minX : std::numeric_limits<double>::infinity(); //automatic: ensure values are initialized by first curve
+ double maxX = attr_.maxX ? *attr_.maxX : -std::numeric_limits<double>::infinity(); //
for (auto it = curves_.begin(); it != curves_.end(); ++it)
if (const CurveData* curve = it->first.get())
{
@@ -611,9 +615,9 @@ void Graph2D::render(wxDC& dc) const
assert(rangeX.first <= rangeX.second + 1.0e-9);
//GCC fucks up badly when comparing two *binary identical* doubles and finds "begin > end" with diff of 1e-18
- if (attr_.minXauto)
+ if (!attr_.minX)
minX = std::min(minX, rangeX.first);
- if (attr_.maxXauto)
+ if (!attr_.maxX)
maxX = std::max(maxX, rangeX.second);
}
@@ -630,8 +634,8 @@ void Graph2D::render(wxDC& dc) const
*attr_.labelFmtX);
//get raw values + detect y value range
- double minY = attr_.minYauto ? std::numeric_limits<double>::infinity() : attr_.minY; //automatic: ensure values are initialized by first curve
- double maxY = attr_.maxYauto ? -std::numeric_limits<double>::infinity() : attr_.maxY; //
+ double minY = attr_.minY ? *attr_.minY : std::numeric_limits<double>::infinity(); //automatic: ensure values are initialized by first curve
+ double maxY = attr_.maxY ? *attr_.maxY : -std::numeric_limits<double>::infinity(); //
std::vector<std::vector<CurvePoint>> curvePoints(curves_.size());
std::vector<std::vector<char>> oobMarker (curves_.size()); //effectively a std::vector<bool> marking points that start an out-of-bounds line
@@ -650,12 +654,12 @@ void Graph2D::render(wxDC& dc) const
const bool doPolygonCut = curves_[index].second.fillMode == CurveAttributes::FILL_POLYGON; //impacts auto minY/maxY!!
cutPointsOutsideX(points, marker, minX, maxX, doPolygonCut);
- if (attr_.minYauto || attr_.maxYauto)
+ if (!attr_.minY || !attr_.maxY)
{
auto itPair = std::minmax_element(points.begin(), points.end(), [](const CurvePoint& lhs, const CurvePoint& rhs) { return lhs.y < rhs.y; });
- if (attr_.minYauto)
+ if (!attr_.minY)
minY = std::min(minY, itPair.first->y);
- if (attr_.maxYauto)
+ if (!attr_.maxY)
maxY = std::max(maxY, itPair.second->y);
}
}
@@ -805,8 +809,8 @@ void Graph2D::render(wxDC& dc) const
}
//3. draw labels and background grid
- drawXLabel(dc, minX, maxX, blockCountX, cvrtX, graphArea, xLabelArea, *attr_.labelFmtX);
- drawYLabel(dc, minY, maxY, blockCountY, cvrtY, graphArea, yLabelArea, *attr_.labelFmtY);
+ if (attr_.labelFmtX) drawXLabel(dc, minX, maxX, blockCountX, cvrtX, graphArea, xLabelArea, *attr_.labelFmtX);
+ if (attr_.labelFmtY) drawYLabel(dc, minY, maxY, blockCountY, cvrtY, graphArea, yLabelArea, *attr_.labelFmtY);
//4. finally draw curves
{
diff --git a/wx+/graph.h b/wx+/graph.h
index 45129c4b..bf0e7a70 100755
--- a/wx+/graph.h
+++ b/wx+/graph.h
@@ -101,12 +101,12 @@ private:
struct VectorCurveData : public ArrayCurveData
{
- std::vector<double>& refData() { return data; }
+ std::vector<double>& refData() { return data_; }
private:
- double getValue(size_t pos) const override { return pos < data.size() ? data[pos] : 0; }
- size_t getSize() const override { return data.size(); }
+ double getValue(size_t pos) const override { return pos < data_.size() ? data_[pos] : 0; }
+ size_t getSize() const override { return data_.size(); }
- std::vector<double> data;
+ std::vector<double> data_;
};
//------------------------------------------------------------------------------------------------------------
@@ -128,7 +128,7 @@ double nextNiceNumber(double blockSize); //round to next number which is conveni
struct DecimalNumberFormatter : public LabelFormatter
{
double getOptimalBlockSize(double sizeProposed ) const override { return nextNiceNumber(sizeProposed); }
- wxString formatText (double value, double optimalBlockSize) const override { return zen::numberTo<wxString>(value); }
+ wxString formatText (double value, double optimalBlockSize) const override { return numberTo<wxString>(value); }
};
//------------------------------------------------------------------------------------------------------------
@@ -178,7 +178,7 @@ public:
{
public:
CurveAttributes() {} //required by GCC
- CurveAttributes& setColor (const wxColor& col) { color = col; autoColor = false; return *this; }
+ CurveAttributes& setColor (const wxColor& col) { color = col; autoColor = false; return *this; }
CurveAttributes& fillCurveArea (const wxColor& col) { fillColor = col; fillMode = FILL_CURVE; return *this; }
CurveAttributes& fillPolygonArea(const wxColor& col) { fillColor = col; fillMode = FILL_POLYGON; return *this; }
CurveAttributes& setLineWidth(size_t width) { lineWidth = static_cast<int>(width); return *this; }
@@ -240,26 +240,26 @@ public:
class MainAttributes
{
public:
- MainAttributes& setMinX(double newMinX) { minX = newMinX; minXauto = false; return *this; }
- MainAttributes& setMaxX(double newMaxX) { maxX = newMaxX; maxXauto = false; return *this; }
+ MainAttributes& setMinX(double newMinX) { minX = newMinX; return *this; }
+ MainAttributes& setMaxX(double newMaxX) { maxX = newMaxX; return *this; }
- MainAttributes& setMinY(double newMinY) { minY = newMinY; minYauto = false; return *this; }
- MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; maxYauto = false; return *this; }
+ MainAttributes& setMinY(double newMinY) { minY = newMinY; return *this; }
+ MainAttributes& setMaxY(double newMaxY) { maxY = newMaxY; return *this; }
- MainAttributes& setAutoSize() { minXauto = maxXauto = minYauto = maxYauto = true; return *this; }
+ MainAttributes& setAutoSize() { minX = maxX = minY = maxY = NoValue(); return *this; }
- MainAttributes& setLabelX(PosLabelX posX, size_t height = 25, std::shared_ptr<LabelFormatter> newLabelFmt = std::make_shared<DecimalNumberFormatter>())
+ MainAttributes& setLabelX(PosLabelX posX, int height = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr)
{
- labelposX = posX;
- xLabelHeight = static_cast<int>(height);
- labelFmtX = newLabelFmt;
+ labelposX = posX;
+ if (height >= 0) xLabelHeight = height;
+ if (newLabelFmt) labelFmtX = newLabelFmt;
return *this;
}
- MainAttributes& setLabelY(PosLabelY posY, size_t width = 60, std::shared_ptr<LabelFormatter> newLabelFmt = std::make_shared<DecimalNumberFormatter>())
+ MainAttributes& setLabelY(PosLabelY posY, int width = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr)
{
- labelposY = posY;
- yLabelWidth = static_cast<int>(width);
- labelFmtY = newLabelFmt;
+ labelposY = posY;
+ if (width >= 0) yLabelWidth = width;
+ if (newLabelFmt) labelFmtY = newLabelFmt;
return *this;
}
@@ -272,22 +272,18 @@ public:
private:
friend class Graph2D;
- bool minXauto = true; //autodetect range for X value
- bool maxXauto = true;
- double minX = 0; //x-range to visualize
- double maxX = 0; //
+ Opt<double> minX; //x-range to visualize
+ Opt<double> maxX; //
- bool minYauto = true; //autodetect range for Y value
- bool maxYauto = true;
- double minY = 0; //y-range to visualize
- double maxY = 0; //
+ Opt<double> minY; //y-range to visualize
+ Opt<double> maxY; //
PosLabelX labelposX = LABEL_X_BOTTOM;
- int xLabelHeight = 25;
+ Opt<int> xLabelHeight;
std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>();
PosLabelY labelposY = LABEL_Y_LEFT;
- int yLabelWidth = 60;
+ Opt<int> yLabelWidth;
std::shared_ptr<LabelFormatter> labelFmtY = std::make_shared<DecimalNumberFormatter>();
std::map<PosCorner, wxString> cornerTexts;
@@ -295,6 +291,7 @@ public:
wxColor backgroundColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
SelMode mouseSelMode = SELECT_RECTANGLE;
};
+
void setAttributes(const MainAttributes& newAttr) { attr_ = newAttr; Refresh(); }
MainAttributes getAttributes() const { return attr_; }
@@ -315,7 +312,6 @@ private:
void onPaintEvent(wxPaintEvent& event);
void onSizeEvent(wxSizeEvent& event) { Refresh(); event.Skip(); }
- void onEraseBackGround(wxEraseEvent& event) {}
void render(wxDC& dc) const;
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 64f7f4a6..a1beee01 100755
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -25,15 +25,15 @@
using namespace zen;
-wxColor Grid::getColorSelectionGradientFrom() { return { 137, 172, 255 }; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1
-wxColor Grid::getColorSelectionGradientTo () { return { 225, 234, 255 }; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1
+//let's NOT create wxWidgets objects statically:
+wxColor GridData::getColorSelectionGradientFrom() { return { 137, 172, 255 }; } //blue: HSL: 158, 255, 196 HSV: 222, 0.46, 1
+wxColor GridData::getColorSelectionGradientTo () { return { 225, 234, 255 }; } // HSL: 158, 255, 240 HSV: 222, 0.12, 1
-const int GridData::COLUMN_GAP_LEFT = 4;
+int GridData::getColumnGapLeft() { return fastFromDIP(4); }
namespace
{
-//let's NOT create wxWidgets objects statically:
//------------------------------ Grid Parameters --------------------------------
inline wxColor getColorLabelText() { return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); }
inline wxColor getColorGridLine() { return { 192, 192, 192 }; } //light grey
@@ -42,15 +42,16 @@ inline wxColor getColorLabelGradientFrom() { return wxSystemSettings::GetColour(
inline wxColor getColorLabelGradientTo () { return { 200, 200, 200 }; } //light grey
inline wxColor getColorLabelGradientFocusFrom() { return getColorLabelGradientFrom(); }
-inline wxColor getColorLabelGradientFocusTo () { return Grid::getColorSelectionGradientFrom(); }
+inline wxColor getColorLabelGradientFocusTo () { return GridData::getColorSelectionGradientFrom(); }
-const double MOUSE_DRAG_ACCELERATION = 1.5; //unit: [rows / (pixel * sec)] -> same value like Explorer!
-const int DEFAULT_COL_LABEL_BORDER = 6; //top + bottom border in addition to label height
-const int COLUMN_MOVE_DELAY = 5; //unit: [pixel] (from Explorer)
-const int COLUMN_MIN_WIDTH = 40; //only honored when resizing manually!
-const int ROW_LABEL_BORDER = 3;
-const int COLUMN_RESIZE_TOLERANCE = 6; //unit [pixel]
-const int COLUMN_FILL_GAP_TOLERANCE = 10; //enlarge column to fill full width when resizing
+const double MOUSE_DRAG_ACCELERATION_DIP = 1.5; //unit: [rows / (DIP * sec)] -> same value like Explorer!
+const int DEFAULT_COL_LABEL_BORDER_DIP = 6; //top + bottom border in addition to label height
+const int COLUMN_MOVE_DELAY_DIP = 5; //unit: [pixel] (from Explorer)
+const int COLUMN_MIN_WIDTH_DIP = 40; //only honored when resizing manually!
+const int ROW_LABEL_BORDER_DIP = 3;
+const int COLUMN_RESIZE_TOLERANCE_DIP = 6; //unit [pixel]
+const int COLUMN_FILL_GAP_TOLERANCE_DIP = 10; //enlarge column to fill full width when resizing
+const int COLUMN_MOVE_MARKER_WIDTH_DIP = 3;
const bool fillGapAfterColumns = true; //draw rows/column label to fill full window width; may become an instance variable some time?
}
@@ -77,15 +78,15 @@ void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType c
{
wxRect rectTmp = drawCellBorder(dc, rect);
- rectTmp.x += COLUMN_GAP_LEFT;
- rectTmp.width -= COLUMN_GAP_LEFT;
+ rectTmp.x += getColumnGapLeft();
+ rectTmp.width -= getColumnGapLeft();
drawCellText(dc, rectTmp, getValue(row, colType));
}
int GridData::getBestSize(wxDC& dc, size_t row, ColumnType colType)
{
- return dc.GetTextExtent(getValue(row, colType)).GetWidth() + 2 * COLUMN_GAP_LEFT + 1; //gap on left and right side + border
+ return dc.GetTextExtent(getValue(row, colType)).GetWidth() + 2 * getColumnGapLeft() + 1; //gap on left and right side + border
}
@@ -104,7 +105,7 @@ void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bo
if (enabled)
{
if (selected)
- dc.GradientFillLinear(rect, Grid::getColorSelectionGradientFrom(), Grid::getColorSelectionGradientTo(), wxEAST);
+ dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST);
else
clearArea(dc, rect, backgroundColor);
}
@@ -186,8 +187,8 @@ void GridData::renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, Colum
wxRect rectTmp = drawColumnLabelBorder(dc, rect);
drawColumnLabelBackground(dc, rectTmp, highlighted);
- rectTmp.x += COLUMN_GAP_LEFT;
- rectTmp.width -= COLUMN_GAP_LEFT;
+ rectTmp.x += getColumnGapLeft();
+ rectTmp.width -= getColumnGapLeft();
drawColumnLabelText(dc, rectTmp, getColumnLabel(colType));
}
@@ -245,15 +246,14 @@ public:
{
Connect(wxEVT_PAINT, wxPaintEventHandler(SubWindow::onPaintEvent), nullptr, this);
Connect(wxEVT_SIZE, wxSizeEventHandler (SubWindow::onSizeEvent), nullptr, this);
- //http://wiki.wxwidgets.org/Flicker-Free_Drawing
- Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(SubWindow::onEraseBackGround), nullptr, this);
+ Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) {}); //http://wiki.wxwidgets.org/Flicker-Free_Drawing
//SetDoubleBuffered(true); slow as hell!
SetBackgroundStyle(wxBG_STYLE_PAINT);
- Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(SubWindow::onFocus), nullptr, this);
- Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(SubWindow::onFocus), nullptr, this);
+ Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(SubWindow::onFocus), nullptr, this);
+ Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(SubWindow::onFocus), nullptr, this);
Connect(wxEVT_CHILD_FOCUS, wxEventHandler(SubWindow::onChildFocus), nullptr, this);
Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(SubWindow::onMouseLeftDown ), nullptr, this);
@@ -364,8 +364,6 @@ private:
event.Skip();
}
- void onEraseBackGround(wxEraseEvent& event) {}
-
Grid& parent_;
Opt<wxBitmap> doubleBuffer_;
};
@@ -429,7 +427,7 @@ public:
int bestWidth = 0;
for (ptrdiff_t i = rowFrom; i <= rowTo; ++i)
- bestWidth = std::max(bestWidth, dc.GetTextExtent(formatRow(i)).GetWidth() + 2 * ROW_LABEL_BORDER);
+ bestWidth = std::max(bestWidth, dc.GetTextExtent(formatRow(i)).GetWidth() + fastFromDIP(2 * ROW_LABEL_BORDER_DIP));
return bestWidth;
}
@@ -671,10 +669,12 @@ private:
if (refParent().allowColumnMove_)
if (activeClickOrMove_ && activeClickOrMove_->isRealMove())
{
+ const int markerWidth = fastFromDIP(COLUMN_MOVE_MARKER_WIDTH_DIP);
+
if (col + 1 == activeClickOrMove_->refColumnTo()) //handle pos 1, 2, .. up to "at end" position
- dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight() + wxPoint(-2, 0)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
+ dc.GradientFillLinear(wxRect(rect.x + rect.width - markerWidth, rect.y, markerWidth, rect.height), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
else if (col == activeClickOrMove_->refColumnTo() && col == 0) //pos 0
- dc.GradientFillLinear(wxRect(rect.GetTopLeft(), rect.GetBottomLeft() + wxPoint(2, 0)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
+ dc.GradientFillLinear(wxRect(rect.GetTopLeft(), wxSize(markerWidth, rect.height)), getColorLabelGradientFrom(), *wxBLUE, wxSOUTH);
}
}
}
@@ -769,7 +769,7 @@ private:
//check if there's a small gap after last column, if yes, fill it
const int gapWidth = GetClientSize().GetWidth() - refParent().getColWidthsSum(GetClientSize().GetWidth());
- if (std::abs(gapWidth) < COLUMN_FILL_GAP_TOLERANCE)
+ if (std::abs(gapWidth) < fastFromDIP(COLUMN_FILL_GAP_TOLERANCE_DIP))
refParent().setColumnWidth(newWidth + gapWidth, col, ALLOW_GRID_EVENT);
refParent().Refresh(); //refresh columns on main grid as well!
@@ -777,7 +777,7 @@ private:
else if (activeClickOrMove_)
{
const int clientPosX = event.GetPosition().x;
- if (std::abs(clientPosX - activeClickOrMove_->getStartPosX()) > COLUMN_MOVE_DELAY) //real move (not a single click)
+ if (std::abs(clientPosX - activeClickOrMove_->getStartPosX()) > fastFromDIP(COLUMN_MOVE_DELAY_DIP)) //real move (not a single click)
{
activeClickOrMove_->setRealMove();
@@ -1163,13 +1163,13 @@ private:
wnd_.refParent().GetScrollPixelsPerUnit(nullptr, &pixelsPerUnitY);
if (pixelsPerUnitY <= 0) return;
- const double mouseDragSpeedIncScrollU = pixelsPerUnitY > 0 ? MOUSE_DRAG_ACCELERATION * wnd_.rowLabelWin_.getRowHeight() / pixelsPerUnitY : 0; //unit: [scroll units / (pixel * sec)]
+ const double mouseDragSpeedIncScrollU = pixelsPerUnitY > 0 ? MOUSE_DRAG_ACCELERATION_DIP * wnd_.rowLabelWin_.getRowHeight() / pixelsPerUnitY : 0; //unit: [scroll units / (DIP * sec)]
auto autoScroll = [&](int overlapPix, double& toScroll)
{
if (overlapPix != 0)
{
- const double scrollSpeed = overlapPix * mouseDragSpeedIncScrollU; //unit: [scroll units / sec]
+ const double scrollSpeed = wnd_.ToDIP(overlapPix) * mouseDragSpeedIncScrollU; //unit: [scroll units / sec]
toScroll += scrollSpeed * deltaSecs;
}
else
@@ -1291,7 +1291,7 @@ Grid::Grid(wxWindow* parent,
colLabelWin_ = new ColLabelWin(*this); //
mainWin_ = new MainWin (*this, *rowLabelWin_, *colLabelWin_); //
- colLabelHeight_ = 2 * DEFAULT_COL_LABEL_BORDER + [&]() -> int
+ colLabelHeight_ = fastFromDIP(2 * DEFAULT_COL_LABEL_BORDER_DIP) + [&]
{
//coordinate with ColLabelWin::render():
wxFont labelFont = colLabelWin_->GetFont();
@@ -1306,9 +1306,9 @@ Grid::Grid(wxWindow* parent,
assert(GetClientSize() == GetSize()); //borders are NOT allowed for Grid
//reason: updateWindowSizes() wants to use "GetSize()" as a "GetClientSize()" including scrollbars
- Connect(wxEVT_PAINT, wxPaintEventHandler(Grid::onPaintEvent ), nullptr, this);
- Connect(wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(Grid::onEraseBackGround), nullptr, this);
- Connect(wxEVT_SIZE, wxSizeEventHandler (Grid::onSizeEvent ), nullptr, this);
+ Connect(wxEVT_PAINT, wxPaintEventHandler(Grid::onPaintEvent), nullptr, this);
+ Connect(wxEVT_SIZE, wxSizeEventHandler (Grid::onSizeEvent ), nullptr, this);
+ Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) {}); //http://wiki.wxwidgets.org/Flicker-Free_Drawing
Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(Grid::onKeyDown), nullptr, this);
}
@@ -1820,7 +1820,7 @@ Opt<Grid::ColAction> Grid::clientPosToColumnAction(const wxPoint& pos) const
const int absPosX = CalcUnscrolledPosition(pos).x;
if (absPosX >= 0)
{
- const int resizeTolerance = allowColumnResize_ ? COLUMN_RESIZE_TOLERANCE : 0;
+ const int resizeTolerance = allowColumnResize_ ? fastFromDIP(COLUMN_RESIZE_TOLERANCE_DIP) : 0;
std::vector<ColumnWidth> absWidths = getColWidths(); //resolve stretched widths
int accuWidth = 0;
@@ -2104,10 +2104,10 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve
return;
}
//CAVEATS:
- //I. fixed-size columns: normalize offset so that resulting width is at least COLUMN_MIN_WIDTH: this is NOT enforced by getColWidths()!
+ //I. fixed-size columns: normalize offset so that resulting width is at least COLUMN_MIN_WIDTH_DIP: this is NOT enforced by getColWidths()!
//II. stretched columns: do not allow user to set offsets so small that they result in negative (non-normalized) widths: this gives an
//unusual delay when enlarging the column again later
- width = std::max(width, COLUMN_MIN_WIDTH);
+ width = std::max(width, fastFromDIP(COLUMN_MIN_WIDTH_DIP));
vcRs.offset = width - stretchedWidths[col]; //width := stretchedWidth + offset
@@ -2119,7 +2119,7 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve
//4. now verify that the stretched column is resizing immediately if main window is enlarged again
for (size_t col2 = 0; col2 < visibleCols_.size(); ++col2)
if (visibleCols_[col2].stretch > 0) //normalize stretched columns only
- visibleCols_[col2].offset = std::max(visibleCols_[col2].offset, COLUMN_MIN_WIDTH - stretchedWidths[col2]);
+ visibleCols_[col2].offset = std::max(visibleCols_[col2].offset, fastFromDIP(COLUMN_MIN_WIDTH_DIP) - stretchedWidths[col2]);
if (columnResizeEventPolicy == ALLOW_GRID_EVENT)
{
@@ -2213,9 +2213,9 @@ std::vector<Grid::ColumnWidth> Grid::getColWidths(int mainWinWidth) const //eval
int width = stretchedWidths[col2] + vc.offset;
if (vc.stretch > 0)
- width = std::max(width, COLUMN_MIN_WIDTH); //normalization really needed here: e.g. smaller main window would result in negative width
+ width = std::max(width, fastFromDIP(COLUMN_MIN_WIDTH_DIP)); //normalization really needed here: e.g. smaller main window would result in negative width
else
- width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration
+ width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH_DIP if set via configuration
output.push_back({ vc.type, width });
}
diff --git a/wx+/grid.h b/wx+/grid.h
index a5c1a783..c90981b5 100755
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -118,7 +118,9 @@ public:
virtual void renderColumnLabel(Grid& grid, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted); //default implementation
virtual std::wstring getToolTip(ColumnType colType) const { return std::wstring(); }
- static const int COLUMN_GAP_LEFT; //for left-aligned text
+ static int getColumnGapLeft(); //for left-aligned text
+ static wxColor getColorSelectionGradientFrom();
+ static wxColor getColorSelectionGradientTo();
//optional helper routines:
static wxSize drawCellText (wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); //returns text extent
@@ -223,12 +225,8 @@ public:
//############################################################################################################
- static wxColor getColorSelectionGradientFrom();
- static wxColor getColorSelectionGradientTo();
-
private:
void onPaintEvent(wxPaintEvent& event);
- void onEraseBackGround(wxEraseEvent& event) {} //[!]
void onSizeEvent(wxSizeEvent& event) { updateWindowSizes(); event.Skip(); }
void onKeyDown(wxKeyEvent& event);
diff --git a/wx+/image_holder.h b/wx+/image_holder.h
new file mode 100755
index 00000000..6804d5fc
--- /dev/null
+++ b/wx+/image_holder.h
@@ -0,0 +1,52 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef IMAGE_HOLDER_H_284578426342567457
+#define IMAGE_HOLDER_H_284578426342567457
+
+#include <memory>
+
+//used by fs/abstract.h => check carefully before adding dependencies!
+//DO NOT add any wx/wx+ includes!
+
+namespace zen
+{
+struct ImageHolder //prepare conversion to wxImage as much as possible while staying thread-safe (in contrast to wxIcon/wxBitmap)
+{
+ ImageHolder() {}
+
+ ImageHolder(int w, int h, bool withAlpha) : //init with allocated memory
+ width_(w), height_(h),
+ rgb_(static_cast<unsigned char*>(::malloc(w * h * 3))),
+ alpha_(withAlpha ? static_cast<unsigned char*>(::malloc(w * h)) : nullptr) {}
+
+ ImageHolder (ImageHolder&& tmp) = default; //
+ ImageHolder& operator=(ImageHolder&& tmp) = default; //move semantics only!
+ ImageHolder (const ImageHolder&) = delete; //
+ ImageHolder& operator=(const ImageHolder&) = delete; //
+
+ explicit operator bool() const { return rgb_.get() != nullptr; }
+
+ int getWidth () const { return width_; }
+ int getHeight() const { return height_; }
+
+ unsigned char* getRgb () { return rgb_ .get(); }
+ unsigned char* getAlpha() { return alpha_.get(); }
+
+ unsigned char* releaseRgb () { return rgb_ .release(); }
+ unsigned char* releaseAlpha() { return alpha_.release(); }
+
+ struct CLibFree { void operator()(unsigned char* p) const { ::free(p); } }; //use malloc/free to allow direct move into wxImage!
+
+private:
+ int width_ = 0;
+ int height_ = 0;
+ std::unique_ptr<unsigned char, CLibFree> rgb_; //optional
+ std::unique_ptr<unsigned char, CLibFree> alpha_; //
+};
+}
+
+#endif //IMAGE_HOLDER_H_284578426342567457
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index d4547d35..e87b245c 100755
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -9,12 +9,17 @@
#include <map>
#include <zen/utf.h>
#include <zen/globals.h>
+#include <zen/perf.h>
#include <zen/thread.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <wx/image.h>
#include <wx/mstream.h>
+#include <xBRZ/src/xbrz.h>
+#include <xBRZ/src/xbrz_tools.h>
#include "image_tools.h"
+#include "image_holder.h"
+#include "dc.h"
using namespace zen;
@@ -22,6 +27,195 @@ using namespace zen;
namespace
{
+
+ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const unsigned char* imageRgb, const unsigned char* imageAlpha, int hqScale)
+{
+ assert(imageRgb && imageAlpha); //see convertToVanillaImage()
+ if (width <= 0 || height <= 0 || dpiWidth <= 0 || dpiHeight <= 0)
+ return ImageHolder(0, 0, true /*withAlpha*/);
+
+ const int hqWidth = width * hqScale;
+ const int hqHeight = height * hqScale;
+
+ //get rid of allocation and buffer std::vector<> at thread-level? => no discernable perf improvement
+ std::vector<uint32_t> buf(hqWidth * hqHeight + std::max(width * height, dpiWidth * dpiHeight));
+ uint32_t* const argbSrc = &buf[0] + hqWidth * hqHeight;
+ uint32_t* const xbrTrg = &buf[0];
+ uint32_t* const dpiTrg = argbSrc;
+
+ //convert RGB (RGB byte order) to ARGB (BGRA byte order)
+ {
+ const unsigned char* rgb = imageRgb;
+ const unsigned char* rgbEnd = rgb + 3 * width * height;
+ const unsigned char* alpha = imageAlpha;
+ uint32_t* out = argbSrc;
+
+ for (; rgb < rgbEnd; rgb += 3)
+ *out++ = xbrz::makePixel(*alpha++, rgb[0], rgb[1], rgb[2]);
+ }
+ //-----------------------------------------------------
+ xbrz::scale(hqScale, //size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
+ argbSrc, //const uint32_t* src,
+ xbrTrg, //uint32_t* trg,
+ width, height, //int srcWidth, int srcHeight,
+ xbrz::ColorFormat::ARGB_UNBUFFERED); //ColorFormat colFmt,
+ //test: total xBRZ scaling time with ARGB: 300ms, ARGB_UNBUFFERED: 50ms
+ //-----------------------------------------------------
+ xbrz::bilinearScale(xbrTrg, //const uint32_t* src,
+ hqWidth, hqHeight, //int srcWidth, int srcHeight,
+ dpiTrg, //uint32_t* trg,
+ dpiWidth, dpiHeight); //int trgWidth, int trgHeight
+ //-----------------------------------------------------
+ //convert BGRA to RGB + alpha
+ ImageHolder trgImg(dpiWidth, dpiHeight, true /*withAlpha*/);
+ {
+ unsigned char* rgb = trgImg.getRgb();
+ unsigned char* alpha = trgImg.getAlpha();
+
+ std::for_each(dpiTrg, dpiTrg + dpiWidth * dpiHeight, [&](uint32_t col)
+ {
+ *alpha++ = xbrz::getAlpha(col);
+ *rgb++ = xbrz::getRed (col);
+ *rgb++ = xbrz::getGreen(col);
+ *rgb++ = xbrz::getBlue (col);
+ });
+ }
+ return trgImg;
+}
+
+
+struct WorkItem
+{
+ Zbase<wchar_t> name;
+ int width = 0;
+ int height = 0;
+ int dpiWidth = 0;
+ int dpiHeight = 0;
+ const unsigned char* rgb = nullptr;
+ const unsigned char* alpha = nullptr;
+};
+
+
+class WorkLoad
+{
+public:
+ void add(const WorkItem& wi) //context of main thread
+ {
+ assert(std::this_thread::get_id() == mainThreadId);
+ {
+ std::lock_guard<std::mutex> dummy(lockWork_);
+ workLoad_.push_back(wi);
+ }
+ conditionNewWork_.notify_all();
+ }
+
+ void noMoreWork()
+ {
+ assert(std::this_thread::get_id() == mainThreadId);
+ {
+ std::lock_guard<std::mutex> dummy(lockWork_);
+ expectMoreWork_ = false;
+ }
+ conditionNewWork_.notify_all();
+ }
+
+ //context of worker thread, blocking:
+ Opt<WorkItem> extractNext() //throw ThreadInterruption
+ {
+ assert(std::this_thread::get_id() != mainThreadId);
+ std::unique_lock<std::mutex> dummy(lockWork_);
+
+ interruptibleWait(conditionNewWork_, dummy, [this] { return !workLoad_.empty() || !expectMoreWork_; }); //throw ThreadInterruption
+
+ if (workLoad_.empty())
+ return NoValue();
+
+ WorkItem wi = workLoad_.back(); //
+ workLoad_.pop_back(); //yes, no strong exception guarantee (std::bad_alloc)
+ return wi; //
+ }
+
+private:
+ bool expectMoreWork_ = true;
+ std::vector<WorkItem> workLoad_;
+ std::mutex lockWork_;
+ std::condition_variable conditionNewWork_; //signal event: data for processing available
+};
+
+
+class DpiParallelScaler
+{
+public:
+ DpiParallelScaler(int hqScale)
+ {
+ assert(hqScale > 1);
+ const int threadCount = std::max<int>(std::thread::hardware_concurrency(), 1); //hardware_concurrency() == 0 if "not computable or well defined"
+
+ for (int i = 0; i < threadCount; ++i)
+ worker_.push_back([hqScale, &workload = workload_, &result = result_]
+ {
+ setCurrentThreadName("xBRZ Scaler");
+ while (Opt<WorkItem> wi = workload.extractNext()) //throw ThreadInterruption
+ {
+ ImageHolder ih = dpiScale(wi->width, wi->height,
+ wi->dpiWidth, wi->dpiHeight,
+ wi->rgb, wi->alpha, hqScale);
+ result.access([&](std::vector<std::pair<Zbase<wchar_t>, ImageHolder>>& r) { r.emplace_back(wi->name, std::move(ih)); });
+ }
+ });
+ }
+
+ ~DpiParallelScaler()
+ {
+ for (InterruptibleThread& w : worker_)
+ w.interrupt();
+
+ for (InterruptibleThread& w : worker_)
+ if (w.joinable())
+ w.join();
+ }
+
+ void add(const wxString& name, const wxImage& img)
+ {
+ imgKeeper_.push_back(img); //retain (ref-counted) wxImage so that the rgb/alpha pointers remain valid after passed to threads
+ workload_.add({ copyStringTo<Zbase<wchar_t>>(name),
+ img.GetWidth(), img.GetHeight(),
+ fastFromDIP(img.GetWidth()), fastFromDIP(img.GetHeight()), //don't call fastFromDIP() from worker thread (wxWidgets function!)
+ img.GetData(), img.GetAlpha() });
+ }
+
+ std::map<wxString, wxBitmap> waitAndGetResult()
+ {
+ workload_.noMoreWork();
+
+ for (InterruptibleThread& w : worker_)
+ w.join();
+
+ std::map<wxString, wxBitmap> output;
+
+ result_.access([&](std::vector<std::pair<Zbase<wchar_t>, ImageHolder>>& r)
+ {
+ for (auto& item : r)
+ {
+ ImageHolder& ih = item.second;
+
+ wxImage img(ih.getWidth(), ih.getHeight(), ih.releaseRgb(), false /*static_data*/); //pass ownership
+ img.SetAlpha(ih.releaseAlpha(), false /*static_data*/);
+
+ output[utfTo<wxString>(item.first)] = wxBitmap(img);
+ }
+ });
+ return output;
+ }
+
+private:
+ std::vector<InterruptibleThread> worker_;
+ WorkLoad workload_;
+ Protected<std::vector<std::pair<Zbase<wchar_t>, ImageHolder>>> result_;
+ std::vector<wxImage> imgKeeper_;
+};
+
+
void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim)
{
//work around wxWidgets bug:
@@ -39,6 +233,8 @@ void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim)
anim.Load(seekAbleStream, wxANIMATION_TYPE_GIF);
}
+//================================================================================================
+//================================================================================================
class GlobalBitmaps
{
@@ -51,32 +247,35 @@ public:
}
GlobalBitmaps() {}
- ~GlobalBitmaps() { assert(bitmaps.empty() && anims.empty()); } //don't leave wxWidgets objects for static destruction!
+ ~GlobalBitmaps() { assert(bitmaps_.empty() && anims_.empty()); } //don't leave wxWidgets objects for static destruction!
void init(const Zstring& filepath);
void cleanup()
{
- bitmaps.clear();
- anims.clear();
+ bitmaps_.clear();
+ anims_ .clear();
+ dpiScaler_.reset();
}
- const wxBitmap& getImage (const wxString& name) const;
+ const wxBitmap& getImage (const wxString& name);
const wxAnimation& getAnimation(const wxString& name) const;
private:
GlobalBitmaps (const GlobalBitmaps&) = delete;
GlobalBitmaps& operator=(const GlobalBitmaps&) = delete;
- std::map<wxString, wxBitmap> bitmaps;
- std::map<wxString, wxAnimation> anims;
+ std::map<wxString, wxBitmap> bitmaps_;
+ std::map<wxString, wxAnimation> anims_;
+
+ std::unique_ptr<DpiParallelScaler> dpiScaler_;
};
-void GlobalBitmaps::init(const Zstring& filepath)
+void GlobalBitmaps::init(const Zstring& filePath)
{
- assert(bitmaps.empty() && anims.empty());
+ assert(bitmaps_.empty() && anims_.empty());
- wxFFileInputStream input(utfTo<wxString>(filepath));
+ wxFFileInputStream input(utfTo<wxString>(filePath));
if (input.IsOk()) //if not... we don't want to react too harsh here
{
//activate support for .png files
@@ -85,36 +284,49 @@ void GlobalBitmaps::init(const Zstring& filepath)
wxZipInputStream streamIn(input, wxConvUTF8);
//do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!"
- for (;;)
- {
- std::unique_ptr<wxZipEntry> entry(streamIn.GetNextEntry()); //take ownership!
- if (!entry)
- break;
+ //do we need xBRZ scaling for high quality DPI images?
+ const int hqScale = numeric::clampCpy<int>(std::ceil(fastFromDIP(1000) / 1000.0), 1, xbrz::SCALE_FACTOR_MAX);
+ //even for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"!
+ if (hqScale > 1)
+ dpiScaler_ = std::make_unique<DpiParallelScaler>(hqScale);
+ while (const auto& entry = std::unique_ptr<wxZipEntry>(streamIn.GetNextEntry())) //take ownership!)
+ {
const wxString name = entry->GetName();
- //generic image loading
if (endsWith(name, L".png"))
{
wxImage img(streamIn, wxBITMAP_TYPE_PNG);
//end this alpha/no-alpha/mask/wxDC::DrawBitmap/RTL/high-contrast-scheme interoperability nightmare here and now!!!!
- //=> there's only one type of png image: with alpha channel, no mask!!!
+ //=> there's only one type of wxImage: with alpha channel, no mask!!!
convertToVanillaImage(img);
- bitmaps.emplace(name, img);
+ if (dpiScaler_)
+ dpiScaler_->add(name, img); //scale in parallel!
+ else
+ bitmaps_.emplace(name, img);
}
else if (endsWith(name, L".gif"))
- loadAnimFromZip(streamIn, anims[name]);
+ loadAnimFromZip(streamIn, anims_[name]);
}
}
}
-const wxBitmap& GlobalBitmaps::getImage(const wxString& name) const
+const wxBitmap& GlobalBitmaps::getImage(const wxString& name)
{
- auto it = bitmaps.find(contains(name, L'.') ? name : name + L".png"); //assume .png ending if nothing else specified
- if (it != bitmaps.end())
+ //test: this function is first called about 220ms after GlobalBitmaps::init() has ended
+ // => should be enough time to finish xBRZ scaling in parallel (which takes 50ms)
+ //debug perf: extra 800-1000ms during startup
+ if (dpiScaler_)
+ {
+ bitmaps_ = dpiScaler_->waitAndGetResult();
+ dpiScaler_.reset();
+ }
+
+ auto it = bitmaps_.find(contains(name, L'.') ? name : name + L".png"); //assume .png ending if nothing else specified
+ if (it != bitmaps_.end())
return it->second;
assert(false);
return wxNullBitmap;
@@ -123,8 +335,8 @@ const wxBitmap& GlobalBitmaps::getImage(const wxString& name) const
const wxAnimation& GlobalBitmaps::getAnimation(const wxString& name) const
{
- auto it = anims.find(contains(name, L'.') ? name : name + L".gif");
- if (it != anims.end())
+ auto it = anims_.find(contains(name, L'.') ? name : name + L".gif");
+ if (it != anims_.end())
return it->second;
assert(false);
return wxNullAnimation;
diff --git a/wx+/image_resources.h b/wx+/image_resources.h
index cc4ab2cc..5ea56679 100755
--- a/wx+/image_resources.h
+++ b/wx+/image_resources.h
@@ -14,7 +14,7 @@
namespace zen
{
-void initResourceImages(const Zstring& filepath); //pass resources .zip file at application startup
+void initResourceImages(const Zstring& filePath); //pass resources .zip file at application startup
void cleanupResourceImages();
const wxBitmap& getResourceImage (const wxString& name);
diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp
index 235ebe59..26a5b37c 100755
--- a/wx+/image_tools.cpp
+++ b/wx+/image_tools.cpp
@@ -9,9 +9,9 @@
#include <zen/zstring.h>
#include <wx/app.h>
-
using namespace zen;
+
namespace
{
void writeToImage(const wxImage& source, wxImage& target, const wxPoint& pos)
diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp
index 5babaff3..6633d2ab 100755
--- a/wx+/popup_dlg.cpp
+++ b/wx+/popup_dlg.cpp
@@ -15,18 +15,12 @@
using namespace zen;
+
namespace
{
-void setAsStandard(wxButton& btn)
-{
- btn.SetDefault();
- btn.SetFocus();
-}
-
-
void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize)
{
- const int scrollbarWidth = 30;
+ const int scrollbarWidth = fastFromDIP(20);
if (maxSize.x <= scrollbarWidth) //implicitly checks for non-zero, too!
return;
maxSize.x -= scrollbarWidth;
@@ -117,8 +111,9 @@ public:
SetTitle(wxTheApp->GetAppDisplayName() + SPACED_DASH + titleTmp);
}
- int maxWidth = 500;
- int maxHeight = 400; //try to determine better value based on actual display resolution:
+ int maxWidth = fastFromDIP(500);
+ int maxHeight = fastFromDIP(400); //try to determine better value based on actual display resolution:
+ //int [maxWidth, maxHeight] = wxSize(fastFromDIP(500), fastFromDIP(400));
if (parent)
{
@@ -139,7 +134,10 @@ public:
if (!cfg.textDetail.empty())
{
- const wxString& text = L"\n" + trimCpy(cfg.textDetail) + L"\n"; //add empty top/bottom lines *instead* of using border space!
+ wxString text;
+ if (!cfg.textMain.empty())
+ text += L"\n";
+ text += trimCpy(cfg.textDetail) + L"\n"; //add empty top/bottom lines *instead* of using border space!
setBestInitialSize(*m_textCtrlTextDetail, text, wxSize(maxWidth, maxHeight));
m_textCtrlTextDetail->ChangeValue(text);
}
@@ -200,9 +198,10 @@ public:
//set std order after button visibility was set
setStandardButtonLayout(*bSizerStdButtons, stdBtns);
- setAsStandard(*m_buttonAccept);
GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize()
Center(); //needs to be re-applied after a dialog size change!
+
+ m_buttonAccept->SetFocus();
}
private:
diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp
index c8c504ae..25ac00e1 100755
--- a/wx+/popup_dlg_generated.cpp
+++ b/wx+/popup_dlg_generated.cpp
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 17 2015)
+// C++ code generated with wxFormBuilder (version Jan 23 2018)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "popup_dlg_generated.h"
@@ -63,6 +63,7 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, con
bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
m_buttonAccept = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ m_buttonAccept->SetDefault();
bSizerStdButtons->Add( m_buttonAccept, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_buttonAcceptAll = new wxButton( this, wxID_YESTOALL, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h
index 896f8d0c..0d3459e2 100755
--- a/wx+/popup_dlg_generated.h
+++ b/wx+/popup_dlg_generated.h
@@ -1,8 +1,8 @@
///////////////////////////////////////////////////////////////////////////
-// C++ code generated with wxFormBuilder (version Jun 17 2015)
+// C++ code generated with wxFormBuilder (version Jan 23 2018)
// http://www.wxformbuilder.org/
//
-// PLEASE DO "NOT" EDIT THIS FILE!
+// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __POPUP_DLG_GENERATED_H__
diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h
index 8fe5f405..cf0152de 100755
--- a/wx+/std_button_layout.h
+++ b/wx+/std_button_layout.h
@@ -10,6 +10,7 @@
#include <algorithm>
#include <wx/sizer.h>
#include <wx/button.h>
+#include "dc.h"
namespace zen
@@ -71,9 +72,9 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons)
detach(buttonsTmp.btnCancel);
//GNOME Human Interface Guidelines: https://developer.gnome.org/hig-book/3.2/hig-book.html#alert-spacing
- const int spaceH = 6; //OK
- const int spaceRimH = 12; //OK
- const int spaceRimV = 12; //OK
+ const int spaceH = fastFromDIP( 6); //OK
+ const int spaceRimH = fastFromDIP(12); //OK
+ const int spaceRimV = fastFromDIP(12); //OK
bool settingFirstButton = true;
auto attach = [&](wxButton* btn)
@@ -82,7 +83,7 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons)
{
assert(btn->GetMinSize().GetHeight() == -1); //let OS or this routine do the sizing! note: OS X does not allow changing the (visible!) button height!
const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); //buffered by wxWidgets
- btn->SetMinSize(wxSize(-1, std::max(defaultHeight, 30))); //default button height is much too small => increase!
+ btn->SetMinSize(wxSize(-1, std::max(defaultHeight, fastFromDIP(30)))); //default button height is much too small => increase!
if (settingFirstButton)
settingFirstButton = false;
diff --git a/wx+/tooltip.cpp b/wx+/tooltip.cpp
index a8fd1da1..a7bf85fe 100755
--- a/wx+/tooltip.cpp
+++ b/wx+/tooltip.cpp
@@ -11,20 +11,22 @@
#include <wx/statbmp.h>
#include <wx/settings.h>
#include <wx/app.h>
-#include <wx+/image_tools.h>
+#include "image_tools.h"
+#include "dc.h"
using namespace zen;
+namespace
+{
+const int TIP_WINDOW_OFFSET_DIP = 30;
+}
+
+
class Tooltip::TooltipDlgGenerated : public wxDialog
{
public:
- TooltipDlgGenerated(wxWindow* parent,
- wxWindowID id = wxID_ANY,
- const wxString& title = {},
- const wxPoint& pos = wxDefaultPosition,
- const wxSize& size = wxDefaultSize,
- long style = 0) : wxDialog(parent, id, title, pos, size, style)
+ TooltipDlgGenerated(wxWindow* parent) : wxDialog(parent, wxID_ANY, L"" /*title*/, wxDefaultPosition, wxDefaultSize, 0 /*style*/)
{
//Suse Linux/X11: needs parent window, else there are z-order issues
@@ -66,15 +68,15 @@ void Tooltip::show(const wxString& text, wxPoint mousePos, const wxBitmap* bmp)
if (text != tipWindow_->staticTextMain_->GetLabel())
{
tipWindow_->staticTextMain_->SetLabel(text);
- tipWindow_->staticTextMain_->Wrap(600);
+ tipWindow_->staticTextMain_->Wrap(fastFromDIP(600));
}
tipWindow_->GetSizer()->SetSizeHints(tipWindow_); //~=Fit() + SetMinSize()
//Linux: Fit() seems to be broken => this needs to be called EVERY time inside show, not only if text or bmp change
const wxPoint newPos = wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft ?
- mousePos - wxPoint(30 + tipWindow_->GetSize().GetWidth(), 0) :
- mousePos + wxPoint(30, 0);
+ mousePos - wxPoint(fastFromDIP(TIP_WINDOW_OFFSET_DIP) + tipWindow_->GetSize().GetWidth(), 0) :
+ mousePos + wxPoint(fastFromDIP(TIP_WINDOW_OFFSET_DIP), 0);
if (newPos != tipWindow_->GetScreenPosition())
tipWindow_->SetSize(newPos.x, newPos.y, wxDefaultCoord, wxDefaultCoord);
diff --git a/wx+/zlib_wrap.cpp b/wx+/zlib_wrap.cpp
index 540854a1..cb6e3083 100755
--- a/wx+/zlib_wrap.cpp
+++ b/wx+/zlib_wrap.cpp
@@ -5,7 +5,9 @@
// *****************************************************************************
#include "zlib_wrap.h"
- #include <zlib.h> //let's pray this is the same version wxWidgets is linking against!
+//include the SAME zlib version that wxWidgets is linking against!
+ //#include <wx/../../../../../Source/src/zlib/zlib.h> //wxWidgets compiled with: --with-zlib=builtin
+ #include <zlib.h> //use same library as used by Curl (zlib is required for HTTP)
using namespace zen;
bgstack15