summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2020-07-22 16:56:03 +0000
committerB Stack <bgstack15@gmail.com>2020-07-22 16:56:03 +0000
commite5633fb1c0db91f01ab967330b76baf4ecdb0512 (patch)
tree10260e25ae905564f7978b83fc4e316670f987c6 /wx+
parentMerge branch '10.25' into 'master' (diff)
parentadd upstream 11.0 (diff)
downloadFreeFileSync-e5633fb1c0db91f01ab967330b76baf4ecdb0512.tar.gz
FreeFileSync-e5633fb1c0db91f01ab967330b76baf4ecdb0512.tar.bz2
FreeFileSync-e5633fb1c0db91f01ab967330b76baf4ecdb0512.zip
Merge branch '11.0' into 'master'11.0
add upstream 11.0 See merge request opensource-tracking/FreeFileSync!24
Diffstat (limited to 'wx+')
-rw-r--r--wx+/app_main.h12
-rw-r--r--wx+/async_task.h21
-rw-r--r--wx+/bitmap_button.h86
-rw-r--r--wx+/context_menu.h8
-rw-r--r--wx+/dc.h35
-rw-r--r--wx+/graph.cpp10
-rw-r--r--wx+/grid.cpp32
-rw-r--r--wx+/image_holder.h22
-rw-r--r--wx+/image_resources.cpp223
-rw-r--r--wx+/image_resources.h9
-rw-r--r--wx+/image_tools.cpp284
-rw-r--r--wx+/image_tools.h59
-rw-r--r--wx+/popup_dlg.cpp6
-rw-r--r--wx+/popup_dlg_generated.cpp106
-rw-r--r--wx+/popup_dlg_generated.h47
-rw-r--r--wx+/rtl.h54
-rw-r--r--wx+/toggle_button.h37
-rw-r--r--wx+/tooltip.cpp9
-rw-r--r--wx+/tooltip.h4
19 files changed, 616 insertions, 448 deletions
diff --git a/wx+/app_main.h b/wx+/app_main.h
index 8d2a6eeb..570a2a9c 100644
--- a/wx+/app_main.h
+++ b/wx+/app_main.h
@@ -14,8 +14,8 @@
namespace zen
{
//just some wrapper around a global variable representing the (logical) main application window
-void setMainWindow(wxWindow* window); //set main window and enable "exit on frame delete"
-bool mainWindowWasSet();
+void setGlobalWindow(wxWindow* window); //set main window and enable "exit on frame delete"
+bool globalWindowWasSet();
@@ -26,7 +26,7 @@ bool mainWindowWasSet();
namespace impl
{
inline
-bool& refMainWndStatus()
+bool& refGlobalWindowStatus()
{
static bool status = false; //external linkage!
return status;
@@ -35,15 +35,15 @@ bool& refMainWndStatus()
inline
-void setMainWindow(wxWindow* window)
+void setGlobalWindow(wxWindow* window)
{
wxTheApp->SetTopWindow(window);
wxTheApp->SetExitOnFrameDelete(true);
- impl::refMainWndStatus() = true;
+ impl::refGlobalWindowStatus() = true;
}
-inline bool mainWindowWasSet() { return impl::refMainWndStatus(); }
+inline bool globalWindowWasSet() { return impl::refGlobalWindowStatus(); }
}
#endif //APP_MAIN_H_08215601837818347575856
diff --git a/wx+/async_task.h b/wx+/async_task.h
index 1599c4d7..47660a6a 100644
--- a/wx+/async_task.h
+++ b/wx+/async_task.h
@@ -16,18 +16,16 @@
namespace zen
{
-/*
-Run a task in an async thread, but process result in GUI event loop
--------------------------------------------------------------------
-1. put AsyncGuiQueue instance inside a dialog:
- AsyncGuiQueue guiQueue;
+/* Run a task in an async thread, but process result in GUI event loop
+ -------------------------------------------------------------------
+ 1. put AsyncGuiQueue instance inside a dialog:
+ AsyncGuiQueue guiQueue;
-2. schedule async task and synchronous continuation:
- guiQueue.processAsync(evalAsync, evalOnGui);
+ 2. schedule async task and synchronous continuation:
+ guiQueue.processAsync(evalAsync, evalOnGui);
-Alternative: use wxWidgets' inter-thread communication (wxEvtHandler::QueueEvent) https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication
- => don't bother, probably too many MT race conditions lurking around
-*/
+ Alternative: wxWidgets' inter-thread communication (wxEvtHandler::QueueEvent) https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication
+ => don't bother, probably too many MT race conditions lurking around */
namespace impl
{
@@ -44,7 +42,8 @@ class ConcreteTask : public Task
{
public:
template <class Fun2>
- ConcreteTask(std::future<ResultType>&& asyncResult, Fun2&& evalOnGui) : asyncResult_(std::move(asyncResult)), evalOnGui_(std::forward<Fun2>(evalOnGui)) {}
+ ConcreteTask(std::future<ResultType>&& asyncResult, Fun2&& evalOnGui) :
+ asyncResult_(std::move(asyncResult)), evalOnGui_(std::forward<Fun2>(evalOnGui)) {}
bool resultReady () const override { return isReady(asyncResult_); }
void evaluateResult() override
diff --git a/wx+/bitmap_button.h b/wx+/bitmap_button.h
index b40d8dd3..508a72fc 100644
--- a/wx+/bitmap_button.h
+++ b/wx+/bitmap_button.h
@@ -8,6 +8,7 @@
#define BITMAP_BUTTON_H_83415718945878341563415
#include <wx/bmpbuttn.h>
+#include <wx/settings.h>
#include "image_tools.h"
#include "dc.h"
@@ -26,17 +27,20 @@ public:
long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxButtonNameStr) :
- wxBitmapButton(parent, id, wxNullBitmap, pos, size, style | wxBU_AUTODRAW, validator, name) { SetLabel(label); }
+ wxBitmapButton(parent, id, wxNullBitmap, pos, size, style, validator, name)
+ {
+ SetLabel(label);
+ }
};
//wxButton::SetBitmap() also supports "image + text", but screws up proper gap and border handling
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);
-
-
+void setImage(wxBitmapButton& button, const wxImage& bmp);
+wxBitmap renderSelectedButton(const wxSize& sz);
+wxBitmap renderPressedButton(const wxSize& sz);
@@ -53,34 +57,80 @@ void setBitmapTextLabel(wxBitmapButton& btn, const wxImage& img, const wxString&
gap = std::max(0, gap);
border = std::max(0, border);
- wxImage dynImage = createImageFromText(text, btn.GetFont(), btn.GetForegroundColour());
+ wxImage imgTxt = createImageFromText(text, btn.GetFont(), btn.GetForegroundColour());
if (img.IsOk())
- dynImage = btn.GetLayoutDirection() != wxLayout_RightToLeft ?
- stackImages(img, dynImage, ImageStackLayout::horizontal, ImageStackAlignment::center, gap) :
- stackImages(dynImage, img, ImageStackLayout::horizontal, ImageStackAlignment::center, gap);
+ imgTxt = btn.GetLayoutDirection() != wxLayout_RightToLeft ?
+ stackImages(img, imgTxt, ImageStackLayout::horizontal, ImageStackAlignment::center, gap) :
+ stackImages(imgTxt, img, ImageStackLayout::horizontal, ImageStackAlignment::center, gap);
//SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work correctly
const int defaultHeight = wxButton::GetDefaultSize().GetHeight();
- btn.SetMinSize({dynImage.GetWidth () + 2 * border,
- std::max(dynImage.GetHeight() + 2 * border, defaultHeight)});
+ btn.SetMinSize({imgTxt.GetWidth () + 2 * border,
+ std::max(imgTxt.GetHeight() + 2 * border, defaultHeight)});
- btn.SetBitmapLabel(wxBitmap(dynImage));
+ btn.SetBitmapLabel(imgTxt);
//SetLabel() calls confuse wxBitmapButton in the disabled state and it won't show the image! workaround:
- btn.SetBitmapDisabled(wxBitmap(dynImage.ConvertToDisabled()));
+ btn.SetBitmapDisabled(imgTxt.ConvertToDisabled());
}
inline
-void setImage(wxBitmapButton& button, const wxBitmap& bmp)
+void setImage(wxBitmapButton& button, const wxImage& img)
{
- if (!button.GetBitmapLabel().IsSameAs(bmp))
+ if (!img.IsOk())
{
- button.SetBitmapLabel(bmp);
+ button.SetBitmapLabel (wxNullBitmap);
+ button.SetBitmapDisabled(wxNullBitmap);
+ return;
+ }
- //wxWidgets excels at screwing up consistently once again:
- //the first call to SetBitmapLabel() *implicitly* sets the disabled bitmap, too, subsequent calls, DON'T!
- button.SetBitmapDisabled(bmp.ConvertToDisabled()); //inefficiency: wxBitmap::ConvertToDisabled() implicitly converts to wxImage!
+ button.SetBitmapLabel(img);
+
+ //wxWidgets excels at screwing up consistently once again:
+ //the first call to SetBitmapLabel() *implicitly* sets the disabled bitmap, too, subsequent calls, DON'T!
+ button.SetBitmapDisabled(img.ConvertToDisabled()); //inefficiency: wxBitmap::ConvertToDisabled() implicitly converts to wxImage!
+}
+
+
+inline
+wxBitmap renderSelectedButton(const wxSize& sz)
+{
+ wxBitmap bmp(sz); //seems we don't need to pass 24-bit depth here even for high-contrast color schemes
+ {
+ wxMemoryDC dc(bmp);
+ dc.SetBrush(wxColor(0xcc, 0xe4, 0xf8)); //light blue
+ dc.SetPen (wxColor(0x79, 0xbc, 0xed)); //medium blue
+ dc.DrawRectangle(wxRect(bmp.GetSize()));
+ }
+ return bmp;
+}
+
+
+inline
+wxBitmap renderPressedButton(const wxSize& sz)
+{
+ wxBitmap bmp(sz); //seems we don't need to pass 24-bit depth here even for high-contrast color schemes
+ {
+ //draw rectangle border with gradient
+ const wxColor colFrom = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
+ const wxColor colTo(0x11, 0x79, 0xfe); //light blue
+
+ wxMemoryDC dc(bmp);
+ dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+ wxRect rect(bmp.GetSize());
+
+ const int borderSize = fastFromDIP(3);
+ for (int i = 1 ; i <= borderSize; ++i)
+ {
+ const wxColor colGradient((colFrom.Red () * (borderSize - i) + colTo.Red () * i) / borderSize,
+ (colFrom.Green() * (borderSize - i) + colTo.Green() * i) / borderSize,
+ (colFrom.Blue () * (borderSize - i) + colTo.Blue () * i) / borderSize);
+ dc.SetPen(colGradient);
+ dc.DrawRectangle(rect);
+ rect.Deflate(1);
+ }
}
+ return bmp;
}
}
diff --git a/wx+/context_menu.h b/wx+/context_menu.h
index 08a313bf..7096da33 100644
--- a/wx+/context_menu.h
+++ b/wx+/context_menu.h
@@ -29,10 +29,10 @@ class ContextMenu : private wxEvtHandler
public:
ContextMenu() {}
- void addItem(const wxString& label, const std::function<void()>& command, const wxBitmap* bmp = nullptr, bool enabled = true)
+ void addItem(const wxString& label, const std::function<void()>& command, const wxImage& img = wxNullImage, bool enabled = true)
{
wxMenuItem* newItem = new wxMenuItem(menu_.get(), wxID_ANY, label); //menu owns item!
- if (bmp) newItem->SetBitmap(*bmp); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason
+ if (img.IsOk()) newItem->SetBitmap(img); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason
menu_->Append(newItem);
if (!enabled) newItem->Enable(false); //do not enable BEFORE appending item! wxWidgets screws up for yet another crappy reason
commandList_[newItem->GetId()] = command; //defer event connection, this may be a submenu only!
@@ -56,7 +56,7 @@ public:
void addSeparator() { menu_->AppendSeparator(); }
- void addSubmenu(const wxString& label, ContextMenu& submenu, const wxBitmap* bmp = nullptr, bool enabled = true) //invalidates submenu!
+ void addSubmenu(const wxString& label, ContextMenu& submenu, const wxImage& img = wxNullImage, bool enabled = true) //invalidates submenu!
{
//transfer submenu commands:
commandList_.insert(submenu.commandList_.begin(), submenu.commandList_.end());
@@ -65,7 +65,7 @@ public:
submenu.menu_->SetNextHandler(menu_.get()); //on wxGTK submenu events are not propagated to their parent menu by default!
wxMenuItem* newItem = new wxMenuItem(menu_.get(), wxID_ANY, label, L"", wxITEM_NORMAL, submenu.menu_.release()); //menu owns item, item owns submenu!
- if (bmp) newItem->SetBitmap(*bmp); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason
+ if (img.IsOk()) newItem->SetBitmap(img); //do not set AFTER appending item! wxWidgets screws up for yet another crappy reason
menu_->Append(newItem);
if (!enabled) newItem->Enable(false);
}
diff --git a/wx+/dc.h b/wx+/dc.h
index f6f5518b..f1b92ad2 100644
--- a/wx+/dc.h
+++ b/wx+/dc.h
@@ -17,37 +17,38 @@
namespace zen
{
-/*
- 1. wxDCClipper does *not* stack: another fix for yet another poor wxWidgets implementation
+/* 1. wxDCClipper does *not* stack: another fix for yet another poor wxWidgets implementation
- class RecursiveDcClipper
- {
- RecursiveDcClipper(wxDC& dc, const wxRect& r)
- };
+ class RecursiveDcClipper
+ {
+ RecursiveDcClipper(wxDC& dc, const wxRect& r)
+ };
2. wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active: a fix for a poor wxWidgets implementation
- class BufferedPaintDC
- {
- BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer)
- };
-*/
+ class BufferedPaintDC
+ {
+ BufferedPaintDC(wxWindow& wnd, std::unique_ptr<wxBitmap>& buffer)
+ }; */
inline
void clearArea(wxDC& dc, const wxRect& rect, const wxColor& col)
{
+ if (rect.width > 0 && //clearArea() is surprisingly expensive
+ rect.height > 0)
+ {
wxDCPenChanger dummy (dc, col);
wxDCBrushChanger dummy2(dc, col);
dc.DrawRectangle(rect);
+ }
}
-/*
-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)
-*/
+/* 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)
{
@@ -60,7 +61,7 @@ int fastFromDIP(int d) //like wxWindow::FromDIP (but tied to primary monitor and
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);
+ return 1.0 * d * dpiY / defaultDpi + 0.49 /*round values like 1.5 down, e.g. 1 pixel on 150% scale*/;
#endif
}
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index 1fb3775c..f3805fca 100644
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -150,7 +150,7 @@ void drawXLabel(wxDC& dc, double xMin, double xMax, int blockCount, const Conver
if (blockCount <= 0)
return;
- wxDCPenChanger dummy(dc, wxColor(192, 192, 192)); //light grey => not accessible! but no big deal...
+ wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192), fastFromDIP(1))); //light grey => not accessible! but no big deal...
wxDCTextColourChanger textColor(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
const double valRangePerBlock = (xMax - xMin) / blockCount;
@@ -178,7 +178,7 @@ void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const Conver
if (blockCount <= 0)
return;
- wxDCPenChanger dummy(dc, wxColor(192, 192, 192)); //light grey => not accessible! but no big deal...
+ wxDCPenChanger dummy(dc, wxPen(wxColor(192, 192, 192), fastFromDIP(1))); //light grey => not accessible! but no big deal...
wxDCTextColourChanger textColor(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
const double valRangePerBlock = (yMax - yMin) / blockCount;
@@ -591,7 +591,7 @@ void Graph2D::render(wxDC& dc) const
{
//paint graph background (excluding label area)
- wxDCPenChanger dummy (dc, getBorderColor());
+ wxDCPenChanger dummy (dc, wxPen(getBorderColor(), fastFromDIP(1)));
wxDCBrushChanger dummy2(dc, attr_.colorBack);
//accessibility: consider system text and background colors; small drawback: color of graphs is NOT connected to the background! => responsibility of client to use correct colors
@@ -749,7 +749,7 @@ void Graph2D::render(wxDC& dc) const
if (points.size() >= 3)
{
wxDCBrushChanger dummy(dc, it->second.fillColor);
- wxDCPenChanger dummy2(dc, it->second.fillColor);
+ wxDCPenChanger dummy2(dc, wxPen(it->second.fillColor, fastFromDIP(1)));
dc.DrawPolygon(static_cast<int>(points.size()), &points[0]);
}
}
@@ -761,7 +761,7 @@ void Graph2D::render(wxDC& dc) const
{
//alpha channel not supported on wxMSW, so draw selection before curves
wxDCBrushChanger dummy(dc, wxColor(168, 202, 236)); //light blue
- wxDCPenChanger dummy2(dc, wxColor( 51, 153, 255)); //dark blue
+ wxDCPenChanger dummy2(dc, wxPen(wxColor(51, 153, 255), fastFromDIP(1))); //dark blue
auto shrink = [](double* low, double* high)
{
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 0ce5c978..3f1599f3 100644
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -123,7 +123,7 @@ int GridData::getBestSize(wxDC& dc, size_t row, ColumnType colType)
wxRect GridData::drawCellBorder(wxDC& dc, const wxRect& rect) //returns remaining rectangle
{
- wxDCPenChanger dummy2(dc, getColorGridLine());
+ wxDCPenChanger dummy2(dc, wxPen(getColorGridLine(), fastFromDIP(1)));
dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight());
dc.DrawLine(rect.GetBottomRight(), rect.GetTopRight() + wxPoint(0, -1));
@@ -156,7 +156,7 @@ void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& te
- wxDC::DrawLabel results in GetTextExtent() call even for empty strings!!!
=> skip the wxDC::DrawLabel() cruft and directly call wxDC::DrawText()! */
assert(!contains(text, L'\n'));
- if (text.empty())
+ if (rect.width <= 0 || rect.height <= 0 || text.empty())
return;
//truncate large texts and add ellipsis
@@ -166,7 +166,7 @@ void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& te
if (extentTrunc.GetWidth() > rect.width)
{
- //unlike Windows 7 Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfTo<std::wstring>("\xf0\xa4\xbd\x9c");
+ //unlike Windows Explorer, we truncate UTF-16 correctly: e.g. CJK-Ideogramm encodes to TWO wchar_t: utfTo<std::wstring>("\xf0\xa4\xbd\x9c");
size_t low = 0; //number of unicode chars!
size_t high = unicodeLength(text); //
if (high > 1)
@@ -208,7 +208,7 @@ void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& te
else if (alignment & wxALIGN_CENTER_VERTICAL)
pt.y += static_cast<int>(std::floor((rect.height - extentTrunc.GetHeight()) / 2.0)); //round down negative values, too!
- //std::unique_ptr<RecursiveDcClipper> clip; -> redundant!? RecursiveDcClipper already used during Grid cell rendering
+ //std::unique_ptr<RecursiveDcClipper> clip; -> redundant!? RecursiveDcClipper already used during grid cell rendering
//if (extentTrunc.GetWidth() > rect.width)
// clip = std::make_unique<RecursiveDcClipper>(dc, rect);
@@ -230,12 +230,12 @@ wxRect GridData::drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool hi
{
//left border
{
- wxDCPenChanger dummy(dc, *wxWHITE_PEN);
+ wxDCPenChanger dummy(dc, wxPen(*wxWHITE, fastFromDIP(1)));
dc.DrawLine(rect.GetTopLeft(), rect.GetBottomLeft());
}
//bottom, right border
{
- wxDCPenChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
+ wxDCPenChanger dummy(dc, wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), fastFromDIP(1)));
dc.GradientFillLinear(wxRect(rect.GetTopRight(), rect.GetBottomRight()), getColorLabelGradientFrom(), dc.GetPen().GetColour(), wxSOUTH);
dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight() + wxPoint(1, 0));
}
@@ -419,10 +419,10 @@ private:
dc.GradientFillLinear(clientRect, getColorLabelGradientFrom(), getColorLabelGradientTo(), wxSOUTH);
- dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
+ dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), fastFromDIP(1)));
{
- wxDCPenChanger dummy(dc, getColorLabelGradientFrom());
+ wxDCPenChanger dummy(dc, wxPen(getColorLabelGradientFrom(), fastFromDIP(1)));
dc.DrawLine(clientRect.GetTopLeft(), clientRect.GetTopRight());
}
@@ -433,7 +433,7 @@ private:
wxRect rectShrinked = clientRect;
rectShrinked.Deflate(1);
- dc.SetPen(*wxWHITE_PEN);
+ dc.SetPen(wxPen(*wxWHITE, fastFromDIP(1)));
//dc.DrawLine(clientRect.GetTopLeft(), clientRect.GetTopRight() + wxPoint(1, 0));
dc.DrawLine(rectShrinked.GetTopLeft(), rectShrinked.GetBottomLeft() + wxPoint(0, 1));
@@ -534,11 +534,11 @@ private:
//border lines
{
- wxDCPenChanger dummy(dc, *wxWHITE_PEN);
+ wxDCPenChanger dummy(dc, wxPen(*wxWHITE, fastFromDIP(1)));
dc.DrawLine(rect.GetTopLeft(), rect.GetTopRight());
}
{
- wxDCPenChanger dummy(dc, wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
+ wxDCPenChanger dummy(dc, wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW), fastFromDIP(1)));
dc.DrawLine(rect.GetTopLeft(), rect.GetBottomLeft());
dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight());
dc.DrawLine(rect.GetBottomRight(), rect.GetTopRight() + wxPoint(0, -1));
@@ -1520,8 +1520,14 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size)
//luckily "scrollbar-spacing" is stable on GTK3
const wxSize scrollBarSizeTmp = GetSize() - GetClientSize();
- assert(scrollBarSizeTmp.x == 0 || scrollBarSizeTmp.x == 6 || scrollBarSizeTmp.x == 13); //lame hard-coded numbers (from Ubuntu 19.10)
- assert(scrollBarSizeTmp.y == 0 || scrollBarSizeTmp.y == 6 || scrollBarSizeTmp.y == 13); //=> but let's have a *close* eye on scrollbar fluctuation!
+ //lame hard-coded numbers (from Ubuntu 19.10) and openSuse
+ //=> let's have a *close* eye on scrollbar fluctuation!
+ assert(scrollBarSizeTmp.x == 0 ||
+ scrollBarSizeTmp.x == 6 || scrollBarSizeTmp.x == 13 || //Ubuntu 19.10
+ scrollBarSizeTmp.x == 16); //openSuse
+ assert(scrollBarSizeTmp.y == 0 ||
+ scrollBarSizeTmp.y == 6 || scrollBarSizeTmp.y == 13 || //Ubuntu 19.10
+ scrollBarSizeTmp.y == 16); //openSuse
#else
#error unknown GTK version!
#endif
diff --git a/wx+/image_holder.h b/wx+/image_holder.h
index b11ae451..32f8a863 100644
--- a/wx+/image_holder.h
+++ b/wx+/image_holder.h
@@ -8,7 +8,7 @@
#define IMAGE_HOLDER_H_284578426342567457
#include <memory>
-
+ #include <gio/gio.h>
//used by fs/abstract.h => check carefully before adding dependencies!
//DO NOT add any wx/wx+ includes!
@@ -47,6 +47,26 @@ private:
std::unique_ptr<unsigned char, CLibFree> rgb_; //optional
std::unique_ptr<unsigned char, CLibFree> alpha_; //
};
+
+
+struct FileIconHolder
+{
+ //- GTK is NOT thread-safe! The most we can do from worker threads is retrieve a GIcon and later *try*(!) to convert it on the MAIN THREAD! >:( what a waste
+ //- at least g_file_query_info() *always* returns G_IS_THEMED_ICON(gicon) for native file systems => main thread won't block https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glocalfileinfo.c#L1733
+ //- what about G_IS_FILE_ICON(gicon), G_IS_LOADABLE_ICON(gicon)? => may block! => do NOT convert on main thread! (no big deal: doesn't seem to occur in practice)
+ FileIconHolder() {};
+
+ FileIconHolder(GIcon* gi, int maxSz) : //takes ownership!
+ gicon(gi),
+ maxSize(maxSz) {}
+
+ struct GiconFree { void operator()(GIcon* icon) const { ::g_object_unref(icon); } };
+
+ std::unique_ptr<GIcon, GiconFree> gicon;
+ int maxSize = 0;
+
+ explicit operator bool() const { return static_cast<bool>(gicon); }
+};
}
#endif //IMAGE_HOLDER_H_284578426342567457
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index 5328b845..d09e5dfa 100644
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -5,10 +5,8 @@
// *****************************************************************************
#include "image_resources.h"
-#include <memory>
#include <map>
#include <zen/utf.h>
-#include <zen/globals.h>
#include <zen/perf.h>
#include <zen/thread.h>
#include <zen/file_io.h>
@@ -27,20 +25,19 @@ using namespace zen;
namespace
{
-ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const unsigned char* imageRgb, const unsigned char* imageAlpha, int hqScale)
+ImageHolder xbrzScale(int width, int height, const unsigned char* imageRgb, const unsigned char* imageAlpha, int hqScale)
{
- assert(imageRgb && imageAlpha); //see convertToVanillaImage()
- if (width <= 0 || height <= 0 || dpiWidth <= 0 || dpiHeight <= 0)
+ assert(imageRgb && imageAlpha && width > 0 && height > 0); //see convertToVanillaImage()
+ if (width <= 0 || height <= 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));
+ std::vector<uint32_t> buf(hqWidth * hqHeight + width * height);
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)
{
@@ -60,18 +57,13 @@ ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const u
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*/);
+ ImageHolder trgImg(hqWidth, hqHeight, true /*withAlpha*/);
{
unsigned char* rgb = trgImg.getRgb();
unsigned char* alpha = trgImg.getAlpha();
- std::for_each(dpiTrg, dpiTrg + dpiWidth * dpiHeight, [&](uint32_t col)
+ std::for_each(xbrTrg, xbrTrg + hqWidth * hqHeight, [&](uint32_t col)
{
*alpha++ = xbrz::getAlpha(col);
*rgb++ = xbrz::getRed (col);
@@ -86,28 +78,24 @@ ImageHolder dpiScale(int width, int height, int dpiWidth, int dpiHeight, const u
auto getScalerTask(const std::string& imageName, const wxImage& img, int hqScale, Protected<std::vector<std::pair<std::string, ImageHolder>>>& result)
{
return [imageName,
- width = img.GetWidth(),
- height = img.GetHeight(),
- dpiWidth = fastFromDIP(img.GetWidth()),
- dpiHeight = fastFromDIP(img.GetHeight()), //don't call (wxWidgets function!) fastFromDIP() from worker thread
- rgb = img.GetData(),
- alpha = img.GetAlpha(),
+ width = img.GetWidth(), //
+ height = img.GetHeight(), //don't call wxWidgets functions from worker thread
+ rgb = img.GetData(), //
+ alpha = img.GetAlpha(), //
hqScale, &result]
{
- ImageHolder ih = dpiScale(width, height,
- dpiWidth, dpiHeight,
- rgb, alpha, hqScale);
+ ImageHolder ih = xbrzScale(width, height, rgb, alpha, hqScale);
result.access([&](std::vector<std::pair<std::string, ImageHolder>>& r) { r.emplace_back(imageName, std::move(ih)); });
};
}
-class DpiParallelScaler
+class HqParallelScaler
{
public:
- DpiParallelScaler(int hqScale) : hqScale_(hqScale) { assert(hqScale > 1); }
+ HqParallelScaler(int hqScale) : hqScale_(hqScale) { assert(hqScale > 1); }
- ~DpiParallelScaler() { threadGroup_ = {}; } //DpiParallelScaler must out-live threadGroup!!!
+ ~HqParallelScaler() { threadGroup_ = {}; } //imgKeeper_ must out-live threadGroup!!!
void add(const std::string& imageName, const wxImage& img)
{
@@ -115,11 +103,11 @@ public:
threadGroup_->run(getScalerTask(imageName, img, hqScale_, result_));
}
- std::map<std::string, wxBitmap> waitAndGetResult()
+ std::unordered_map<std::string, wxImage> waitAndGetResult()
{
threadGroup_->wait();
- std::map<std::string, wxBitmap> output;
+ std::unordered_map<std::string, wxImage> output;
result_.access([&](std::vector<std::pair<std::string, ImageHolder>>& r)
{
@@ -147,46 +135,48 @@ private:
//================================================================================================
//================================================================================================
-class GlobalBitmaps
+class ImageBuffer
{
public:
- static std::shared_ptr<GlobalBitmaps> instance()
- {
- static FunStatGlobal<GlobalBitmaps> inst;
- inst.initOnce([] { return std::make_unique<GlobalBitmaps>(); });
- assert(runningMainThread()); //wxWidgets is not thread-safe!
- return inst.get();
- }
+ explicit ImageBuffer(const Zstring& filePath); //throw FileError
- GlobalBitmaps() {}
- ~GlobalBitmaps() { assert(bitmaps_.empty() && anims_.empty()); } //don't leave wxWidgets objects for static destruction!
+ const wxImage& getImage(const std::string& name, int maxWidth /*optional*/, int maxHeight /*optional*/);
- void init(const Zstring& filePath);
- void cleanup()
- {
- bitmaps_.clear();
- anims_ .clear();
- dpiScaler_.reset();
- }
+private:
+ ImageBuffer (const ImageBuffer&) = delete;
+ ImageBuffer& operator=(const ImageBuffer&) = delete;
- const wxBitmap& getImage (const std::string& name);
- const wxAnimation& getAnimation(const std::string& name) const;
+ const wxImage& getRawImage (const std::string& name);
+ const wxImage& getScaledImage(const std::string& name);
-private:
- GlobalBitmaps (const GlobalBitmaps&) = delete;
- GlobalBitmaps& operator=(const GlobalBitmaps&) = delete;
+ std::unordered_map<std::string, wxImage> imagesRaw_;
+ std::unordered_map<std::string, wxImage> imagesScaled_;
+
+ std::unique_ptr<HqParallelScaler> hqScaler_;
+
+ using OutImageKey = std::tuple<std::string /*name*/, int /*height*/>;
+
+ struct OutImageKeyHash
+ {
+ size_t operator()(const OutImageKey& imKey) const
+ {
+ const auto& [name, height] = imKey;
+
+ FNV1aHash<size_t> hash;
+ for (const char c : name)
+ hash.add(c);
- std::map<std::string, wxBitmap> bitmaps_;
- std::map<std::string, wxAnimation> anims_;
+ hash.add(height);
- std::unique_ptr<DpiParallelScaler> dpiScaler_;
+ return hash.get();
+ }
+ };
+ std::unordered_map<OutImageKey, wxImage, OutImageKeyHash> imagesOut_;
};
-void GlobalBitmaps::init(const Zstring& zipPath) //throw FileError
+ImageBuffer::ImageBuffer(const Zstring& zipPath) //throw FileError
{
- assert(bitmaps_.empty() && anims_.empty());
-
std::vector<std::pair<Zstring /*file name*/, std::string /*byte stream*/>> streams;
try //to load from ZIP first:
@@ -223,13 +213,12 @@ void GlobalBitmaps::init(const Zstring& zipPath) //throw FileError
const int hqScale = std::clamp<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);
+ hqScaler_ = std::make_unique<HqParallelScaler>(hqScale);
for (const auto& [fileName, stream] : streams)
if (endsWith(fileName, Zstr(".png")))
{
wxMemoryInputStream wxstream(stream.c_str(), stream.size()); //stream does not take ownership of data
- //bonus: work around wxWidgets bug: wxAnimation::Load() requires seekable input stream (zip-input stream is not seekable)
wxImage img(wxstream, wxBITMAP_TYPE_PNG);
assert(img.IsOk());
@@ -239,88 +228,114 @@ void GlobalBitmaps::init(const Zstring& zipPath) //throw FileError
convertToVanillaImage(img);
const std::string imageName = utfTo<std::string>(beforeLast(fileName, Zstr("."), IF_MISSING_RETURN_NONE));
- if (dpiScaler_)
- dpiScaler_->add(imageName, img); //scale in parallel!
+
+ imagesRaw_.emplace(imageName, img);
+ if (hqScaler_)
+ hqScaler_->add(imageName, img); //scale in parallel!
else
- bitmaps_.emplace(imageName, img);
+ imagesScaled_.emplace(imageName, img);
- //alternative: wxBitmap::NewFromPNGData(stream.c_str(), stream.size())?
- // => Windows: just a (slow!) wrapper for wxBitmpat(wxImage())!
+ //wxBitmap::NewFromPNGData(stream.c_str(), stream.size())?
+ // => Windows: just a (slow!) wrapper for wxBitmap(wxImage())!
}
-#if 0
- else if (endsWith(fileName, Zstr(".gif")))
- {
- [[maybe_unused]] const bool loadSuccess = anims_[fileName].Load(wxstream, wxANIMATION_TYPE_GIF);
- assert(loadSuccess);
- }
-#endif
else
assert(false);
}
-const wxBitmap& GlobalBitmaps::getImage(const std::string& name)
+const wxImage& ImageBuffer::getRawImage(const std::string& name)
+{
+ if (auto it = imagesRaw_.find(name);
+ it != imagesRaw_.end())
+ return it->second;
+
+ assert(false);
+ return wxNullImage;
+}
+
+
+const wxImage& ImageBuffer::getScaledImage(const std::string& name)
{
- //test: this function is first called about 220ms after GlobalBitmaps::init() has ended
+ //test: this function is first called about 220ms after ImageBuffer::ImageBuffer() has ended
// => should be enough time to finish xBRZ scaling in parallel (which takes 50ms)
//debug perf: extra 800-1000ms during startup
- if (dpiScaler_)
+ if (hqScaler_)
{
- bitmaps_ = dpiScaler_->waitAndGetResult();
- dpiScaler_.reset();
+ imagesScaled_ = hqScaler_->waitAndGetResult();
+ hqScaler_.reset();
}
- if (auto it = bitmaps_.find(name);
- it != bitmaps_.end())
+ if (auto it = imagesScaled_.find(name);
+ it != imagesScaled_.end())
return it->second;
assert(false);
- return wxNullBitmap;
+ return wxNullImage;
}
-const wxAnimation& GlobalBitmaps::getAnimation(const std::string& name) const
+const wxImage& ImageBuffer::getImage(const std::string& name, int maxWidth /*optional*/, int maxHeight /*optional*/)
{
- if (auto it = anims_.find(name);
- it != anims_.end())
- return it->second;
- assert(false);
- return wxNullAnimation;
+ const wxImage& rawImg = getRawImage(name);
+
+ const wxSize dpiSize(fastFromDIP(rawImg.GetWidth ()),
+ fastFromDIP(rawImg.GetHeight()));
+
+ int outHeight = dpiSize.y;
+ if (maxWidth >= 0 && maxWidth < dpiSize.x)
+ outHeight = rawImg.GetHeight() * maxWidth / rawImg.GetWidth();
+
+ if (maxHeight >= 0 && maxHeight < outHeight)
+ outHeight = maxHeight;
+
+ const OutImageKey imkey{name, outHeight};
+
+ auto it = imagesOut_.find(imkey);
+ if (it == imagesOut_.end())
+ {
+ if (rawImg.GetHeight() >= outHeight) //=> skip needless xBRZ upscaling
+ it = imagesOut_.emplace(imkey, shrinkImage(rawImg, -1 /*maxWidth*/, outHeight)).first;
+ else if (rawImg.GetHeight() >= 0.9 * outHeight) //almost there: also no need for xBRZ-scale
+ //however: for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"!
+ it = imagesOut_.emplace(imkey, rawImg.Scale(rawImg.GetWidth() * outHeight / rawImg.GetHeight(), outHeight, wxIMAGE_QUALITY_BILINEAR)).first;
+ else
+ it = imagesOut_.emplace(imkey, shrinkImage(getScaledImage(name), -1 /*maxWidth*/, outHeight)).first;
+ }
+ return it->second;
}
+
+
+std::unique_ptr<ImageBuffer> globalImageBuffer;
}
-void zen::initResourceImages(const Zstring& zipPath)
+void zen::imageResourcesInit(const Zstring& zipPath) //throw FileError
{
- if (std::shared_ptr<GlobalBitmaps> inst = GlobalBitmaps::instance())
- inst->init(zipPath);
- else
- assert(false);
+ assert(runningMainThread()); //wxWidgets is not thread-safe!
+ assert(!globalImageBuffer);
+ globalImageBuffer = std::make_unique<ImageBuffer>(zipPath); //throw FileError
}
-void zen::cleanupResourceImages()
+void zen::ImageResourcesCleanup()
{
- if (std::shared_ptr<GlobalBitmaps> inst = GlobalBitmaps::instance())
- inst->cleanup();
- else
- assert(false);
+ assert(runningMainThread()); //wxWidgets is not thread-safe!
+ assert(globalImageBuffer);
+ globalImageBuffer.reset();
}
-const wxBitmap& zen::getResourceImage(const std::string& name)
+const wxImage& zen::loadImage(const std::string& name, int maxWidth /*optional*/, int maxHeight /*optional*/)
{
- if (std::shared_ptr<GlobalBitmaps> inst = GlobalBitmaps::instance())
- return inst->getImage(name);
- assert(false);
- return wxNullBitmap;
+ assert(runningMainThread()); //wxWidgets is not thread-safe!
+ assert(globalImageBuffer);
+ if (globalImageBuffer)
+ return globalImageBuffer->getImage(name, maxWidth, maxHeight);
+ return wxNullImage;
}
-const wxAnimation& zen::getResourceAnimation(const std::string& name)
+const wxImage& zen::loadImage(const std::string& name, int maxSize)
{
- if (std::shared_ptr<GlobalBitmaps> inst = GlobalBitmaps::instance())
- return inst->getAnimation(name);
- assert(false);
- return wxNullAnimation;
+ return loadImage(name, maxSize, maxSize);
}
diff --git a/wx+/image_resources.h b/wx+/image_resources.h
index b996d977..2ac6f308 100644
--- a/wx+/image_resources.h
+++ b/wx+/image_resources.h
@@ -7,7 +7,6 @@
#ifndef IMAGE_RESOURCES_H_8740257825342532457
#define IMAGE_RESOURCES_H_8740257825342532457
-#include <wx/bitmap.h>
#include <wx/animate.h>
#include <zen/zstring.h>
@@ -15,11 +14,11 @@
namespace zen
{
//pass resources .zip file at application startup
-void initResourceImages(const Zstring& zipPath); //throw FileError
-void cleanupResourceImages();
+void imageResourcesInit(const Zstring& zipPath); //throw FileError
+void ImageResourcesCleanup();
-const wxBitmap& getResourceImage (const std::string& name);
-const wxAnimation& getResourceAnimation(const std::string& name);
+const wxImage& loadImage(const std::string& name, int maxWidth /*optional*/, int maxHeight /*optional*/);
+const wxImage& loadImage(const std::string& name, int maxSize = -1);
}
#endif //IMAGE_RESOURCES_H_8740257825342532457
diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp
index 9fcc5563..19ba6ba6 100644
--- a/wx+/image_tools.cpp
+++ b/wx+/image_tools.cpp
@@ -14,48 +14,116 @@ using namespace zen;
namespace
{
-void writeToImage(wxImage& output, const wxImage& top, const wxPoint& pos)
+template <int PixBytes>
+void copyImageBlock(const unsigned char* src, int srcWidth,
+ /**/ unsigned char* trg, int trgWidth, int blockWidth, int blockHeight)
{
- const int topWidth = top.GetWidth ();
- const int topHeight = top.GetHeight();
- const int outWidth = output.GetWidth();
+ assert(srcWidth >= blockWidth && trgWidth >= blockWidth);
+ const int srcPitch = srcWidth * PixBytes;
+ const int trgPitch = trgWidth * PixBytes;
+ const int blockPitch = blockWidth * PixBytes;
+ for (int y = 0; y < blockHeight; ++y)
+ std::memcpy(trg + y * trgPitch, src + y * srcPitch, blockPitch);
+}
+
- 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());
+//...what wxImage::Resize() wants to be when it grows up
+void copySubImage(const wxImage& src, wxPoint srcPos,
+ /**/ wxImage& trg, wxPoint trgPos, wxSize blockSize)
+{
+ auto pointClamp = [](const wxPoint& pos, const wxImage& img) -> wxPoint
+ {
+ return {
+ std::clamp(pos.x, 0, img.GetWidth ()),
+ std::clamp(pos.y, 0, img.GetHeight())};
+ };
+ auto pointMinus = [](const wxPoint& lhs, const wxPoint& rhs) { return wxSize{lhs.x - rhs.x, lhs.y - rhs.y}; };
+ //work around yet another wxWidgets screw up: WTF does "operator-(wxPoint, wxPoint)" return wxPoint instead of wxSize!??
+
+ const wxPoint trgPos2 = pointClamp(trgPos, trg);
+ const wxPoint trgPos2End = pointClamp(trgPos + blockSize, trg);
+
+ blockSize = pointMinus(trgPos2End, trgPos2);
+ srcPos += pointMinus(trgPos2, trgPos);
+ trgPos = trgPos2;
+ if (blockSize.x <= 0 || blockSize.y <= 0)
+ return;
+
+ const wxPoint srcPos2 = pointClamp(srcPos, src);
+ const wxPoint srcPos2End = pointClamp(srcPos + blockSize, src);
+
+ blockSize = pointMinus(srcPos2End, srcPos2);
+ trgPos += pointMinus(srcPos2, srcPos);
+ srcPos = srcPos2;
+ if (blockSize.x <= 0 || blockSize.y <= 0)
+ return;
+ //what if target block size is bigger than source block size? should we clear the area that is not copied from source?
+
+ copyImageBlock<3>(src.GetData() + 3 * (srcPos.x + srcPos.y * src.GetWidth()), src.GetWidth(),
+ trg.GetData() + 3 * (trgPos.x + trgPos.y * trg.GetWidth()), trg.GetWidth(),
+ blockSize.x, blockSize.y);
+
+ copyImageBlock<1>(src.GetAlpha() + srcPos.x + srcPos.y * src.GetWidth(), src.GetWidth(),
+ trg.GetAlpha() + trgPos.x + trgPos.y * trg.GetWidth(), trg.GetWidth(),
+ blockSize.x, blockSize.y);
+}
+
+
+void copyImageLayover(const wxImage& src,
+ /**/ wxImage& trg, wxPoint trgPos)
+{
+ const int srcWidth = src.GetWidth ();
+ const int srcHeight = src.GetHeight();
+ const int trgWidth = trg.GetWidth();
+
+ assert(0 <= trgPos.x && trgPos.x + srcWidth <= trgWidth ); //draw area must be a
+ assert(0 <= trgPos.y && trgPos.y + srcHeight <= trg.GetHeight()); //subset of target image!
//https://en.wikipedia.org/wiki/Alpha_compositing
- const unsigned char* topRgb = top.GetData();
- const unsigned char* topAlpha = top.GetAlpha();
+ const unsigned char* srcRgb = src.GetData();
+ const unsigned char* srcAlpha = src.GetAlpha();
- for (int y = 0; y < topHeight; ++y)
+ for (int y = 0; y < srcHeight; ++y)
{
- unsigned char* outRgb = output.GetData () + 3 * (pos.x + (pos.y + y) * outWidth);
- unsigned char* outAlpha = output.GetAlpha() + pos.x + (pos.y + y) * outWidth;
+ unsigned char* trgRgb = trg.GetData () + 3 * (trgPos.x + (trgPos.y + y) * trgWidth);
+ unsigned char* trgAlpha = trg.GetAlpha() + trgPos.x + (trgPos.y + y) * trgWidth;
- for (int x = 0; x < topWidth; ++x)
+ for (int x = 0; x < srcWidth; ++x)
{
- const int w1 = *topAlpha; //alpha-composition interpreted as weighted average
- const int w2 = *outAlpha * (255 - w1) / 255;
+ const int w1 = *srcAlpha; //alpha-composition interpreted as weighted average
+ const int w2 = *trgAlpha * (255 - w1) / 255;
const int wSum = w1 + w2;
- auto calcColor = [w1, w2, wSum](unsigned char colTop, unsigned char colBot)
+ auto calcColor = [w1, w2, wSum](unsigned char colsrc, unsigned char colTrg)
{
- return static_cast<unsigned char>(wSum == 0 ? 0 : (colTop * w1 + colBot * w2) / wSum);
+ return static_cast<unsigned char>(wSum == 0 ? 0 : (colsrc * w1 + colTrg * w2) / wSum);
};
- outRgb[0] = calcColor(topRgb[0], outRgb[0]);
- outRgb[1] = calcColor(topRgb[1], outRgb[1]);
- outRgb[2] = calcColor(topRgb[2], outRgb[2]);
+ trgRgb[0] = calcColor(srcRgb[0], trgRgb[0]);
+ trgRgb[1] = calcColor(srcRgb[1], trgRgb[1]);
+ trgRgb[2] = calcColor(srcRgb[2], trgRgb[2]);
- *outAlpha = static_cast<unsigned char>(wSum);
+ *trgAlpha = static_cast<unsigned char>(wSum);
- topRgb += 3;
- outRgb += 3;
- ++topAlpha;
- ++outAlpha;
+ srcRgb += 3;
+ trgRgb += 3;
+ ++srcAlpha;
+ ++trgAlpha;
}
}
}
+
+
+std::vector<std::pair<wxString, wxSize>> getTextExtentInfo(const wxString& text, const wxFont& font)
+{
+ wxMemoryDC dc; //the context used for bitmaps
+ dc.SetFont(font); //the font parameter of GetMultiLineTextExtent() is not evaluated on OS X, wxWidgets 2.9.5, so apply it to the DC directly!
+
+ std::vector<std::pair<wxString, wxSize>> lineInfo; //text + extent
+ for (const wxString& line : split(text, L'\n', SplitType::ALLOW_EMPTY))
+ lineInfo.emplace_back(line, line.empty() ? wxSize() : dc.GetTextExtent(line));
+
+ return lineInfo;
+}
}
@@ -69,27 +137,23 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
const int img2Width = img2.GetWidth ();
const int img2Height = img2.GetHeight();
- int width = std::max(img1Width, img2Width);
- int height = std::max(img1Height, img2Height);
-
- if (dir == ImageStackLayout::horizontal)
- width = img1Width + gap + img2Width;
- else
- height = img1Height + gap + img2Height;
+ const wxSize newSize = dir == ImageStackLayout::horizontal ?
+ wxSize(img1Width + gap + img2Width, std::max(img1Height, img2Height)) :
+ wxSize(std::max(img1Width, img2Width), img1Height + gap + img2Height);
- wxImage output(width, height);
+ wxImage output(newSize);
output.SetAlpha();
- ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height);
+ std::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, newSize.x * newSize.y);
auto calcPos = [&](int imageExtent, int totalExtent)
{
switch (align)
{
case ImageStackAlignment::center:
- return static_cast<int>(std::floor((totalExtent - imageExtent) / 2.0)); //consistency: round down negative values, too!
- case ImageStackAlignment::left:
+ return (totalExtent - imageExtent) / 2;
+ case ImageStackAlignment::left: //or top
return 0;
- case ImageStackAlignment::right:
+ case ImageStackAlignment::right: //or bottom
return totalExtent - imageExtent;
}
assert(false);
@@ -99,34 +163,19 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay
switch (dir)
{
case ImageStackLayout::horizontal:
- writeToImage(output, img1, wxPoint(0, calcPos(img1Height, height)));
- writeToImage(output, img2, wxPoint(img1Width + gap, calcPos(img2Height, height)));
+ copySubImage(img1, wxPoint(), output, wxPoint(0, calcPos(img1Height, newSize.y)), img1.GetSize());
+ copySubImage(img2, wxPoint(), output, wxPoint(img1Width + gap, calcPos(img2Height, newSize.y)), img2.GetSize());
break;
case ImageStackLayout::vertical:
- writeToImage(output, img1, wxPoint(calcPos(img1Width, width), 0));
- writeToImage(output, img2, wxPoint(calcPos(img2Width, width), img1Height + gap));
+ copySubImage(img1, wxPoint(), output, wxPoint(calcPos(img1Width, newSize.x), 0), img1.GetSize());
+ copySubImage(img2, wxPoint(), output, wxPoint(calcPos(img2Width, newSize.x), img1Height + gap), img2.GetSize());
break;
}
return output;
}
-namespace
-{
-std::vector<std::pair<wxString, wxSize>> getTextExtentInfo(const wxString& text, const wxFont& font)
-{
- wxMemoryDC dc; //the context used for bitmaps
- dc.SetFont(font); //the font parameter of GetMultiLineTextExtent() is not evalated on OS X, wxWidgets 2.9.5, so apply it to the DC directly!
-
- std::vector<std::pair<wxString, wxSize>> lineInfo; //text + extent
- for (const wxString& line : split(text, L'\n', SplitType::ALLOW_EMPTY))
- lineInfo.emplace_back(line, line.empty() ? wxSize() : dc.GetTextExtent(line));
-
- return lineInfo;
-}
-}
-
wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const wxColor& col, ImageStackAlignment textAlign)
{
//assert(!contains(text, L"&")); //accelerator keys not supported here
@@ -147,7 +196,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
lineHeight = std::max(lineHeight, lineSize.GetHeight()); //wxWidgets comment "GetTextExtent will return 0 for empty string"
}
if (maxWidth == 0 || lineHeight == 0)
- return wxImage();
+ return wxNullImage;
wxBitmap newBitmap(maxWidth, lineHeight * lineInfo.size()); //seems we don't need to pass 24-bit depth here even for high-contrast color schemes
{
@@ -155,7 +204,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
- dc.SetTextForeground(*wxBLACK); //for use in calcAlphaForBlackWhiteImage
+ dc.SetTextForeground(*wxBLACK); //for proper alpha-channel calculation
dc.SetTextBackground(*wxWHITE); //
dc.SetFont(font);
@@ -175,7 +224,6 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
dc.DrawText(lineText, wxPoint((maxWidth - lineSize.GetWidth()) / 2, posY));
break;
}
-
posY += lineHeight;
}
}
@@ -193,11 +241,9 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const
//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;
+ *rgb++ = col.Red (); //
+ *rgb++ = col.Green(); //apply actual text color
+ *rgb++ = col.Blue (); //
}
return output;
}
@@ -208,43 +254,91 @@ wxImage zen::layOver(const wxImage& back, const wxImage& front, int alignment)
if (!front.IsOk()) return back;
assert(front.HasAlpha() && back.HasAlpha());
- const int width = std::max(back.GetWidth(), front.GetWidth());
- const int height = std::max(back.GetHeight(), front.GetHeight());
+ const wxSize newSize(std::max(back.GetWidth(), front.GetWidth()),
+ std::max(back.GetHeight(), front.GetHeight()));
- const int offsetX = [&]
+ auto calcNewPos = [&](const wxImage& img)
{
- if (alignment & wxALIGN_RIGHT)
- return back.GetWidth() - front.GetWidth();
- if (alignment & wxALIGN_CENTER_HORIZONTAL)
- return (back.GetWidth() - front.GetWidth()) / 2;
-
- static_assert(wxALIGN_LEFT == 0);
- return 0;
- }();
+ wxPoint newPos;
+ if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
+ newPos.x = newSize.GetWidth() - img.GetWidth();
+ else if (alignment & wxALIGN_CENTER_HORIZONTAL)
+ newPos.x = (newSize.GetWidth() - img.GetWidth()) / 2;
+
+ if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
+ newPos.y = newSize.GetHeight() - img.GetHeight();
+ else if (alignment & wxALIGN_CENTER_VERTICAL)
+ newPos.y = (newSize.GetHeight() - img.GetHeight()) / 2;
+
+ return newPos;
+ };
- const int offsetY = [&]
- {
- if (alignment & wxALIGN_BOTTOM)
- return back.GetHeight() - front.GetHeight();
- if (alignment & wxALIGN_CENTER_VERTICAL)
- return (back.GetHeight() - front.GetHeight()) / 2;
+ wxImage output(newSize);
+ output.SetAlpha();
+ std::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, newSize.x * newSize.y);
- static_assert(wxALIGN_TOP == 0);
- return 0;
- }();
+ copySubImage(back, wxPoint(), output, calcNewPos(back), back.GetSize());
+ //use resizeCanvas()? might return ref-counted copy!
//can't use wxMemoryDC and wxDC::DrawBitmap(): no alpha channel support on wxGTK!
- wxImage output(width, height);
+ copyImageLayover(front, output, calcNewPos(front));
+
+ return output;
+}
+
+
+wxImage zen::resizeCanvas(const wxImage& img, wxSize newSize, int alignment)
+{
+ if (newSize == img.GetSize())
+ return img; //caveat: wxImage is ref-counted *without* copy on write
+
+ wxPoint newPos;
+ if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
+ newPos.x = newSize.GetWidth() - img.GetWidth();
+ else if (alignment & wxALIGN_CENTER_HORIZONTAL)
+ newPos.x = static_cast<int>(std::floor((newSize.GetWidth() - img.GetWidth()) / 2)); //consistency: round down negative values, too!
+
+ if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
+ newPos.y = newSize.GetHeight() - img.GetHeight();
+ else if (alignment & wxALIGN_CENTER_VERTICAL)
+ newPos.y = static_cast<int>(std::floor((newSize.GetHeight() - img.GetHeight()) / 2)); //consistency: round down negative values, too!
+
+ wxImage output(newSize);
output.SetAlpha();
- ::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, width * height);
+ std::memset(output.GetAlpha(), wxIMAGE_ALPHA_TRANSPARENT, newSize.x * newSize.y);
- const wxPoint posBack(std::max(-offsetX, 0), std::max(-offsetY, 0));
- writeToImage(output, back, posBack);
- writeToImage(output, front, posBack + wxPoint(offsetX, offsetY));
+ copySubImage(img, wxPoint(), output, newPos, img.GetSize());
+ //about 50x faster than e.g. wxImage::Resize!!! suprise :>
return output;
}
+wxImage zen::shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxHeight /*optional*/)
+{
+ wxSize newSize = img.GetSize();
+
+ if (maxWidth >= 0)
+ if (maxWidth < newSize.x)
+ {
+ newSize.y = newSize.y * maxWidth / newSize.x;
+ newSize.x = maxWidth;
+ }
+ if (maxHeight >= 0)
+ if (maxHeight < newSize.y)
+ {
+ newSize = img.GetSize(); //avoid loss of precision
+ newSize.x = newSize.x * maxHeight / newSize.y; //
+ newSize.y = maxHeight;
+ }
+
+ if (newSize == img.GetSize())
+ return img;
+
+ return img.Scale(newSize.x, newSize.y, wxIMAGE_QUALITY_BILINEAR); //looks sharper than wxIMAGE_QUALITY_HIGH!
+ //perf: use xbrz::bilinearScale instead? only about 10% shorter runtime
+}
+
+
void zen::convertToVanillaImage(wxImage& img)
{
if (!img.HasAlpha())
@@ -268,20 +362,20 @@ void zen::convertToVanillaImage(wxImage& img)
if (haveMask)
{
img.SetMask(false);
- unsigned char* alphaPtr = img.GetAlpha();
- const unsigned char* dataPtr = img.GetData();
+ unsigned char* alpha = img.GetAlpha();
+ const unsigned char* rgb = img.GetData();
const int pixelCount = width * height;
for (int i = 0; i < pixelCount; ++ i)
{
- const unsigned char r = *dataPtr++;
- const unsigned char g = *dataPtr++;
- const unsigned char b = *dataPtr++;
+ const unsigned char r = *rgb++;
+ const unsigned char g = *rgb++;
+ const unsigned char b = *rgb++;
if (r == mask_r &&
g == mask_g &&
b == mask_b)
- alphaPtr[i] = wxIMAGE_ALPHA_TRANSPARENT;
+ alpha[i] = wxIMAGE_ALPHA_TRANSPARENT;
}
}
}
diff --git a/wx+/image_tools.h b/wx+/image_tools.h
index 3e401f73..cd895c2e 100644
--- a/wx+/image_tools.h
+++ b/wx+/image_tools.h
@@ -8,10 +8,10 @@
#define IMAGE_TOOLS_H_45782456427634254
#include <numeric>
-#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/dcmemory.h>
#include <zen/basic_math.h>
+#include <wx+/dc.h>
namespace zen
@@ -36,10 +36,8 @@ wxImage createImageFromText(const wxString& text, const wxFont& font, const wxCo
wxImage layOver(const wxImage& back, const wxImage& front, int alignment = wxALIGN_CENTER);
-wxImage greyScale(const wxImage& img); //greyscale + brightness adaption
-wxBitmap greyScale(const wxBitmap& bmp); //
-wxBitmap greyScaleIfDisabled(const wxBitmap& bmp, bool enabled);
-
+wxImage greyScale(const wxImage& img); //greyscale + brightness adaption
+wxImage greyScaleIfDisabled(const wxImage& img, bool enabled);
//void moveImage(wxImage& img, int right, int up);
void adjustBrightness(wxImage& img, int targetLevel);
@@ -52,7 +50,10 @@ void convertToVanillaImage(wxImage& img); //add alpha channel if missing + remov
//wxColor hsvColor(double h, double s, double v); //h within [0, 360), s, v within [0, 1]
-wxImage shrinkImage(const wxImage& img, int requestedSize);
+wxImage shrinkImage(const wxImage& img, int maxWidth /*optional*/, int maxHeight /*optional*/);
+inline wxImage shrinkImage(const wxImage& img, int maxSize) { return shrinkImage(img, maxSize, maxSize); }
+
+wxImage resizeCanvas(const wxImage& img, wxSize newSize, int alignment);
inline
@@ -65,48 +66,38 @@ wxImage getTransparentPixel()
}
+inline
+int getDefaultMenuIconSize()
+{
+ return fastFromDIP(24);
+}
-//################################### implementation ###################################
-/*
-inline
-void moveImage(wxImage& img, int right, int up)
-{
- img = img.GetSubImage(wxRect(std::max(0, -right), std::max(0, up), img.GetWidth() - abs(right), img.GetHeight() - abs(up)));
- img.Resize(wxSize(img.GetWidth() + abs(right), img.GetHeight() + abs(up)), wxPoint(std::max(0, right), std::max(0, -up)));
-}
-*/
+
+//################################### implementation ###################################
+
inline
wxImage greyScale(const wxImage& img)
{
wxImage output = img.ConvertToGreyscale(1.0 / 3, 1.0 / 3, 1.0 / 3); //treat all channels equally!
- //wxImage output = bmp.ConvertToImage().ConvertToGreyscale();
adjustBrightness(output, 160);
return output;
}
inline
-wxBitmap greyScale(const wxBitmap& bmp)
-{
- assert(!bmp.GetMask()); //wxWidgets screws up for the gazillionth time applying a mask instead of alpha channel if the .png image has only 0 and 0xff opacity values!!!
- return greyScale(bmp.ConvertToImage());
-}
-
-
-inline
-wxBitmap greyScaleIfDisabled(const wxBitmap& bmp, bool enabled)
+wxImage greyScaleIfDisabled(const wxImage& img, bool enabled)
{
if (enabled) //avoid ternary WTF
- return bmp;
+ return img;
else
- return greyScale(bmp);
+ return greyScale(img);
}
@@ -162,20 +153,6 @@ void adjustBrightness(wxImage& img, int targetLevel)
}
-inline
-wxImage shrinkImage(const wxImage& img, int requestedSize)
-{
- const int maxExtent = std::max(img.GetWidth(), img.GetHeight());
- assert(requestedSize <= maxExtent);
-
- if (requestedSize >= maxExtent)
- return img;
-
- return img.Scale(img.GetWidth () * requestedSize / maxExtent,
- img.GetHeight() * requestedSize / maxExtent, wxIMAGE_QUALITY_BILINEAR); //looks sharper than wxIMAGE_QUALITY_HIGH!
-}
-
-
/*
inline
wxColor gradient(const wxColor& from, const wxColor& to, double fraction)
diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp
index 2011a228..bdb904f2 100644
--- a/wx+/popup_dlg.cpp
+++ b/wx+/popup_dlg.cpp
@@ -103,14 +103,14 @@ public:
case DialogInfoType::info:
//"Information" is meaningless as caption text!
//confirmation doesn't use info icon
- //iconTmp = getResourceImage("msg_info");
+ //iconTmp = loadImage("msg_info");
break;
case DialogInfoType::warning:
- iconTmp = getResourceImage("msg_warning");
+ iconTmp = loadImage("msg_warning");
titleTmp = _("Warning");
break;
case DialogInfoType::error:
- iconTmp = getResourceImage("msg_error");
+ iconTmp = loadImage("msg_error");
titleTmp = _("Error");
break;
}
diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp
index af43c85b..7fc05d53 100644
--- a/wx+/popup_dlg_generated.cpp
+++ b/wx+/popup_dlg_generated.cpp
@@ -11,91 +11,91 @@
PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
- this->SetSizeHints( wxSize( -1, -1 ), wxDefaultSize );
- this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
+ this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize );
+ this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
- wxBoxSizer* bSizer24;
- bSizer24 = new wxBoxSizer( wxVERTICAL );
+ wxBoxSizer* bSizer24;
+ bSizer24 = new wxBoxSizer( wxVERTICAL );
- m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
- m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
+ m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
+ m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
- wxBoxSizer* bSizer165;
- bSizer165 = new wxBoxSizer( wxHORIZONTAL );
+ wxBoxSizer* bSizer165;
+ bSizer165 = new wxBoxSizer( wxHORIZONTAL );
- m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 );
+ m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 );
- wxBoxSizer* bSizer16;
- bSizer16 = new wxBoxSizer( wxVERTICAL );
+ wxBoxSizer* bSizer16;
+ bSizer16 = new wxBoxSizer( wxVERTICAL );
- bSizer16->Add( 0, 10, 0, 0, 5 );
+ bSizer16->Add( 0, 10, 0, 0, 5 );
- m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- m_staticTextMain->Wrap( -1 );
- bSizer16->Add( m_staticTextMain, 0, wxRIGHT, 10 );
+ m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextMain->Wrap( -1 );
+ bSizer16->Add( m_staticTextMain, 0, wxRIGHT, 10 );
- bSizer16->Add( 0, 5, 0, 0, 5 );
+ bSizer16->Add( 0, 5, 0, 0, 5 );
- m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxBORDER_NONE );
- bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 );
+ m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxBORDER_NONE );
+ bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 );
- bSizer165->Add( bSizer16, 1, wxEXPAND, 5 );
+ bSizer165->Add( bSizer16, 1, wxEXPAND, 5 );
- m_panel33->SetSizer( bSizer165 );
- m_panel33->Layout();
- bSizer165->Fit( m_panel33 );
- bSizer24->Add( m_panel33, 1, wxEXPAND, 5 );
+ m_panel33->SetSizer( bSizer165 );
+ m_panel33->Layout();
+ bSizer165->Fit( m_panel33 );
+ bSizer24->Add( m_panel33, 1, wxEXPAND, 5 );
- m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
- bSizer24->Add( m_staticline6, 0, wxEXPAND, 5 );
+ m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bSizer24->Add( m_staticline6, 0, wxEXPAND, 5 );
- wxBoxSizer* bSizer25;
- bSizer25 = new wxBoxSizer( wxVERTICAL );
+ wxBoxSizer* bSizer25;
+ bSizer25 = new wxBoxSizer( wxVERTICAL );
- m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
- bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
+ m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 );
+ bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
- bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
+ bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL );
- m_buttonAccept = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
+ 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_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 );
- bSizerStdButtons->Add( m_buttonAcceptAll, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
+ m_buttonAcceptAll = new wxButton( this, wxID_YESTOALL, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ bSizerStdButtons->Add( m_buttonAcceptAll, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
- m_buttonDecline = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizerStdButtons->Add( m_buttonDecline, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
+ m_buttonDecline = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ bSizerStdButtons->Add( m_buttonDecline, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
- m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1, -1 ), 0 );
- bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
+ m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
+ bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
- bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
+ bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 );
- bSizer24->Add( bSizer25, 0, wxEXPAND, 5 );
+ bSizer24->Add( bSizer25, 0, wxEXPAND, 5 );
- this->SetSizer( bSizer24 );
- this->Layout();
- bSizer24->Fit( this );
+ this->SetSizer( bSizer24 );
+ this->Layout();
+ bSizer24->Fit( this );
- this->Centre( wxBOTH );
+ this->Centre( wxBOTH );
- // Connect Events
- this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) );
- m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this );
- m_buttonAccept->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAccept ), NULL, this );
- m_buttonAcceptAll->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAcceptAll ), NULL, this );
- m_buttonDecline->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonDecline ), NULL, this );
- m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this );
+ // Connect Events
+ this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) );
+ m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this );
+ m_buttonAccept->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAccept ), NULL, this );
+ m_buttonAcceptAll->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAcceptAll ), NULL, this );
+ m_buttonDecline->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonDecline ), NULL, this );
+ m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this );
}
PopupDialogGenerated::~PopupDialogGenerated()
diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h
index 33cc4872..8191ca0f 100644
--- a/wx+/popup_dlg_generated.h
+++ b/wx+/popup_dlg_generated.h
@@ -38,33 +38,34 @@
///////////////////////////////////////////////////////////////////////////////
class PopupDialogGenerated : public wxDialog
{
-private:
+ private:
-protected:
- wxPanel* m_panel33;
- wxStaticBitmap* m_bitmapMsgType;
- wxStaticText* m_staticTextMain;
- wxTextCtrl* m_textCtrlTextDetail;
- wxStaticLine* m_staticline6;
- wxCheckBox* m_checkBoxCustom;
- wxBoxSizer* bSizerStdButtons;
- wxButton* m_buttonAccept;
- wxButton* m_buttonAcceptAll;
- wxButton* m_buttonDecline;
- wxButton* m_buttonCancel;
+ protected:
+ wxPanel* m_panel33;
+ wxStaticBitmap* m_bitmapMsgType;
+ wxStaticText* m_staticTextMain;
+ wxTextCtrl* m_textCtrlTextDetail;
+ wxStaticLine* m_staticline6;
+ wxCheckBox* m_checkBoxCustom;
+ wxBoxSizer* bSizerStdButtons;
+ wxButton* m_buttonAccept;
+ wxButton* m_buttonAcceptAll;
+ wxButton* m_buttonDecline;
+ wxButton* m_buttonCancel;
- // Virtual event handlers, overide them in your derived class
- virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
- virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnButtonAccept( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnButtonAcceptAll( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnButtonDecline( wxCommandEvent& event ) { event.Skip(); }
- virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
+ virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnButtonAccept( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnButtonAcceptAll( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnButtonDecline( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
-public:
+ public:
- PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
- ~PopupDialogGenerated();
+ PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
+ ~PopupDialogGenerated();
};
+
diff --git a/wx+/rtl.h b/wx+/rtl.h
index 3ab701a7..8e386b49 100644
--- a/wx+/rtl.h
+++ b/wx+/rtl.h
@@ -15,12 +15,11 @@
namespace zen
{
//functions supporting right-to-left GUI layout
-void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer);
-void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment);
+void drawBitmapRtlMirror (wxDC& dc, const wxImage& img, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer);
+void drawBitmapRtlNoMirror(wxDC& dc, const wxImage& img, const wxRect& rect, int alignment);
//wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed
-wxBitmap mirrorIfRtl(const wxBitmap& bmp);
-wxImage mirrorIfRtl(const wxImage& bmp);
+wxImage mirrorIfRtl(const wxImage& img);
//manual text flow correction: https://www.w3.org/International/articles/inline-bidi-markup/
@@ -34,34 +33,35 @@ wxImage mirrorIfRtl(const wxImage& bmp);
//---------------------- implementation ------------------------
namespace impl
{
-//don't use wxDC::DrawLabel: it results in expensive GetTextExtent() call even when passing an empty string!!!
-//also avoid wxDC::DrawLabel 1-off alignment bugs
+//don't use wxDC::DrawLabel:
+// - expensive GetTextExtent() call even when passing an empty string!!!
+// - 1-off alignment bugs!
inline
-void drawBitmapAligned(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment)
+void drawBitmapAligned(wxDC& dc, const wxImage& img, const wxRect& rect, int alignment)
{
wxPoint pt = rect.GetTopLeft();
if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0!
- pt.x += rect.width - bmp.GetWidth();
+ pt.x += rect.width - img.GetWidth();
else if (alignment & wxALIGN_CENTER_HORIZONTAL)
- pt.x += (rect.width - bmp.GetWidth()) / 2;
+ pt.x += (rect.width - img.GetWidth()) / 2;
if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0!
- pt.y += rect.height - bmp.GetHeight();
+ pt.y += rect.height - img.GetHeight();
else if (alignment & wxALIGN_CENTER_VERTICAL)
- pt.y += (rect.height - bmp.GetHeight()) / 2;
+ pt.y += (rect.height - img.GetHeight()) / 2;
- dc.DrawBitmap(bmp, pt);
+ dc.DrawBitmap(img, pt);
}
}
inline
-void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer)
+void drawBitmapRtlMirror(wxDC& dc, const wxImage& img, const wxRect& rect, int alignment, std::optional<wxBitmap>& buffer)
{
switch (dc.GetLayoutDirection())
{
case wxLayout_LeftToRight:
- return impl::drawBitmapAligned(dc, bmp, rect, alignment);
+ return impl::drawBitmapAligned(dc, img, rect, alignment);
case wxLayout_RightToLeft:
{
@@ -71,7 +71,7 @@ void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int
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!
- impl::drawBitmapAligned(memDc, bmp, wxRect(0, 0, rect.width, rect.height), alignment);
+ impl::drawBitmapAligned(memDc, img, wxRect(0, 0, rect.width, rect.height), alignment);
//note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug!
dc.Blit(rect.GetTopLeft(), rect.GetSize(), &memDc, wxPoint(0, 0)); //blit out: mirror once again
@@ -80,37 +80,27 @@ void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int
case wxLayout_Default: //CAVEAT: wxPaintDC/wxMemoryDC on wxGTK/wxMAC does not implement SetLayoutDirection()!!! => GetLayoutDirection() == wxLayout_Default
if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
- return impl::drawBitmapAligned(dc, bmp.ConvertToImage().Mirror(), rect, alignment);
+ return impl::drawBitmapAligned(dc, img.Mirror(), rect, alignment);
else
- return impl::drawBitmapAligned(dc, bmp, rect, alignment);
+ return impl::drawBitmapAligned(dc, img, rect, alignment);
}
}
inline
-void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment)
+void drawBitmapRtlNoMirror(wxDC& dc, const wxImage& img, const wxRect& rect, int alignment)
{
- return impl::drawBitmapAligned(dc, bmp, rect, alignment); //wxDC::DrawBitmap does NOT mirror by default
+ return impl::drawBitmapAligned(dc, img, rect, alignment); //wxDC::DrawBitmap does NOT mirror by default
}
inline
-wxImage mirrorIfRtl(const wxImage& bmp)
+wxImage mirrorIfRtl(const wxImage& img)
{
if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
- return bmp.Mirror();
+ return img.Mirror();
else
- return bmp;
-}
-
-
-inline
-wxBitmap mirrorIfRtl(const wxBitmap& bmp)
-{
- if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft)
- return bmp.ConvertToImage().Mirror();
- else
- return bmp;
+ return img;
}
}
diff --git a/wx+/toggle_button.h b/wx+/toggle_button.h
index 98ca32b3..0a359c5c 100644
--- a/wx+/toggle_button.h
+++ b/wx+/toggle_button.h
@@ -16,6 +16,7 @@ namespace zen
class ToggleButton : public wxBitmapButton
{
public:
+ //wxBitmapButton constructor
ToggleButton(wxWindow* parent,
wxWindowID id,
const wxBitmap& bitmap,
@@ -23,13 +24,25 @@ public:
const wxSize& size = wxDefaultSize,
long style = 0,
const wxValidator& validator = wxDefaultValidator,
- const wxString& name = wxButtonNameStr) : wxBitmapButton(parent, id, bitmap, pos, size, style, validator, name)
+ const wxString& name = wxButtonNameStr) :
+ wxBitmapButton(parent, id, bitmap, pos, size, style, validator, name) {}
+
+ //wxButton constructor
+ ToggleButton(wxWindow* parent,
+ wxWindowID id,
+ const wxString& label,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = 0,
+ const wxValidator& validator = wxDefaultValidator,
+ const wxString& name = wxButtonNameStr) :
+ wxBitmapButton(parent, id, wxNullBitmap, pos, size, style, validator, name)
{
- SetLayoutDirection(wxLayout_LeftToRight); //avoid mirroring RTL languages like Hebrew or Arabic
+ SetLabel(label);
}
- void init(const wxBitmap& bmpActive,
- const wxBitmap& bmpInactive);
+ void init(const wxImage& imgActive,
+ const wxImage& imgInactive);
void setActive(bool value);
bool isActive() const { return active_; }
@@ -37,8 +50,8 @@ public:
private:
bool active_ = false;
- wxBitmap bmpActive_;
- wxBitmap bmpInactive_;
+ wxImage imgActive_;
+ wxImage imgInactive_;
};
@@ -49,13 +62,13 @@ private:
//######################## implementation ########################
inline
-void ToggleButton::init(const wxBitmap& bmpActive,
- const wxBitmap& bmpInactive)
+void ToggleButton::init(const wxImage& imgActive,
+ const wxImage& imgInactive)
{
- bmpActive_ = bmpActive;
- bmpInactive_ = bmpInactive;
+ imgActive_ = imgActive;
+ imgInactive_ = imgInactive;
- setImage(*this, active_ ? bmpActive_ : bmpInactive_);
+ setImage(*this, active_ ? imgActive_ : imgInactive_);
}
@@ -65,7 +78,7 @@ void ToggleButton::setActive(bool value)
if (active_ != value)
{
active_ = value;
- setImage(*this, active_ ? bmpActive_ : bmpInactive_);
+ setImage(*this, active_ ? imgActive_ : imgInactive_);
}
}
}
diff --git a/wx+/tooltip.cpp b/wx+/tooltip.cpp
index 0beef0bf..e3c021c6 100644
--- a/wx+/tooltip.cpp
+++ b/wx+/tooltip.cpp
@@ -54,16 +54,17 @@ public:
};
-void Tooltip::show(const wxString& text, wxPoint mousePos, const wxBitmap* bmp)
+void Tooltip::show(const wxString& text, wxPoint mousePos, const wxImage* img)
{
if (!tipWindow_)
tipWindow_ = new TooltipDlgGenerated(&parent_); //ownership passed to parent
- const wxBitmap& newBmp = bmp ? *bmp : wxNullBitmap;
+ const wxImage& newImg = img ? *img : wxNullImage;
- if (!tipWindow_->bitmapLeft_->GetBitmap().IsSameAs(newBmp))
+ if (!lastUsedImg_.IsSameAs(newImg))
{
- tipWindow_->bitmapLeft_->SetBitmap(newBmp);
+ lastUsedImg_ = newImg;
+ tipWindow_->bitmapLeft_->SetBitmap(newImg);
tipWindow_->Refresh(); //needed if bitmap size changed!
}
diff --git a/wx+/tooltip.h b/wx+/tooltip.h
index d74beb9d..b496f36c 100644
--- a/wx+/tooltip.h
+++ b/wx+/tooltip.h
@@ -8,6 +8,7 @@
#define TOOLTIP_H_8912740832170515
#include <wx/window.h>
+#include <wx/image.h>
namespace zen
@@ -19,13 +20,14 @@ public:
void show(const wxString& text,
wxPoint mousePos, //absolute screen coordinates
- const wxBitmap* bmp = nullptr);
+ const wxImage* img = nullptr);
void hide();
private:
class TooltipDlgGenerated;
TooltipDlgGenerated* tipWindow_ = nullptr;
wxWindow& parent_;
+ wxImage lastUsedImg_;
};
}
bgstack15