From ad4e3d2c55e75193c41356c23619f80add41db18 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 2 Oct 2015 14:57:46 +0200 Subject: 7.5 --- wx+/async_task.h | 147 ++++++++++++++++++++++++++++++++++++++++++++++++ wx+/choice_enum.h | 4 +- wx+/context_menu.h | 10 ++-- wx+/dc.h | 4 +- wx+/file_drop.h | 4 +- wx+/graph.cpp | 2 +- wx+/graph.h | 74 ++++++++++-------------- wx+/grid.cpp | 122 ++++++++++++++++++---------------------- wx+/grid.h | 14 ++--- wx+/image_resources.cpp | 2 +- wx+/image_tools.h | 4 +- wx+/popup_dlg.cpp | 18 ++++-- wx+/rtl.h | 2 +- 13 files changed, 267 insertions(+), 140 deletions(-) create mode 100644 wx+/async_task.h (limited to 'wx+') diff --git a/wx+/async_task.h b/wx+/async_task.h new file mode 100644 index 00000000..b23f6948 --- /dev/null +++ b/wx+/async_task.h @@ -0,0 +1,147 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef ASYNC_JOB_839147839170432143214321 +#define ASYNC_JOB_839147839170432143214321 + +#include +#include +#include +#include +#include + + +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; + +2. schedule async task and synchronous continuation: + guiQueue.processAsync(evalAsync, evalOnGui); +*/ + +namespace impl +{ +struct Task +{ + virtual ~Task() {} + virtual bool resultReady () const = 0; + virtual void evaluateResult() = 0; +}; + + +template +class ConcreteTask : public Task +{ +public: + template + ConcreteTask(std::future&& asyncResult, Fun2&& evalOnGui) : asyncResult_(std::move(asyncResult)), evalOnGui_(std::forward(evalOnGui)) {} + + bool resultReady () const override { return isReady(asyncResult_); } + void evaluateResult() override + { +#ifndef NDEBUG + assert(resultReady() && !resultEvaluated_); + resultEvaluated_ = true; +#endif + evalResult(IsSameType()); + } + +private: + void evalResult(FalseType /*void result type*/) { evalOnGui_(asyncResult_.get()); } + void evalResult(TrueType /*void result type*/) { asyncResult_.get(); evalOnGui_(); } + + std::future asyncResult_; + Fun evalOnGui_; //keep "evalOnGui" strictly separated from async thread: in particular do not copy in thread! +#ifndef NDEBUG + bool resultEvaluated_ = false; +#endif +}; + + +class AsyncTasks +{ +public: + AsyncTasks() {} + + template + void add(Fun&& evalAsync, Fun2&& evalOnGui) + { + using ResultType = decltype(evalAsync()); + tasks.push_back(std::make_unique>(zen::runAsync(std::forward(evalAsync)), std::forward(evalOnGui))); + } + //equivalent to "evalOnGui(evalAsync())" + // -> evalAsync: the usual thread-safety requirements apply! + // -> evalOnGui: no thread-safety concerns, but must only reference variables with greater-equal lifetime than the AsyncTask instance! + + void evalResults() //call from gui thread repreatedly + { + if (!inRecursion) //prevent implicit recursion, e.g. if we're called from an idle event and spawn another one via the callback below + { + inRecursion = true; + ZEN_ON_SCOPE_EXIT(inRecursion = false); + + std::vector> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside erase_if + + erase_if(tasks, [&](std::unique_ptr& task) + { + if (task->resultReady()) + { + readyTasks.push_back(std::move(task)); + return true; + } + return false; + }); + + for (auto& task : readyTasks) + task->evaluateResult(); + } + } + + bool empty() const { return tasks.empty(); } + +private: + AsyncTasks (const AsyncTasks&) = delete; + AsyncTasks& operator=(const AsyncTasks&) = delete; + + bool inRecursion = false; + std::vector> tasks; +}; +} + + +class AsyncGuiQueue : private wxEvtHandler +{ +public: + AsyncGuiQueue() { timer.Connect(wxEVT_TIMER, wxEventHandler(AsyncGuiQueue::onTimerEvent), nullptr, this); } + + template + void processAsync(Fun&& evalAsync, Fun2&& evalOnGui) + { + asyncTasks.add(std::forward(evalAsync), + std::forward(evalOnGui)); + if (!timer.IsRunning()) + timer.Start(50 /*unit: [ms]*/); + } + +private: + void onTimerEvent(wxEvent& event) //schedule and run long-running tasks asynchronously + { + asyncTasks.evalResults(); //process results on GUI queue + if (asyncTasks.empty()) + timer.Stop(); + } + + impl::AsyncTasks asyncTasks; + wxTimer timer; //don't use wxWidgets' idle handling => repeated idle requests/consumption hogs 100% cpu! +}; + +} + +#endif //ASYNC_JOB_839147839170432143214321 diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index 6a795d4a..9b1575ee 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -27,10 +27,10 @@ Set enum value: setEnumVal(enumDescrMap, *m_choiceHandleError, value); Get enum value: - value = getEnumVal(enumDescrMap, *m_choiceHandleError) + value = getEnumVal(enumDescrMap, *m_choiceHandleError) Update enum tooltips (after user changed selection): - updateTooltipEnumVal(enumDescrMap, *m_choiceHandleError); + updateTooltipEnumVal(enumDescrMap, *m_choiceHandleError); */ diff --git a/wx+/context_menu.h b/wx+/context_menu.h index 2ae85162..ec62852d 100644 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -17,10 +17,10 @@ A context menu supporting C++11 lambda callbacks! Usage: - ContextMenu menu; - menu.addItem(L"Some Label", [&]{ ...do something... }); -> capture by reference is fine, as long as captured variables have at least scope of ContextMenu::popup()! - ... - menu.popup(wnd); + ContextMenu menu; + menu.addItem(L"Some Label", [&]{ ...do something... }); -> capture by reference is fine, as long as captured variables have at least scope of ContextMenu::popup()! + ... + menu.popup(wnd); */ namespace zen @@ -28,7 +28,7 @@ namespace zen class ContextMenu : private wxEvtHandler { public: - ContextMenu() : menu(zen::make_unique()) {} + ContextMenu() : menu(std::make_unique()) {} void addItem(const wxString& label, const std::function& command, const wxBitmap* bmp = nullptr, bool enabled = true) { diff --git a/wx+/dc.h b/wx+/dc.h index 0d3c1cb8..3fba4aaf 100644 --- a/wx+/dc.h +++ b/wx+/dc.h @@ -48,7 +48,7 @@ public: auto it = refDcToAreaMap().find(&dc); if (it != refDcToAreaMap().end()) { - oldRect = zen::make_unique(it->second); + oldRect = std::make_unique(it->second); wxRect tmp = r; tmp.Intersect(*oldRect); //better safe than sorry @@ -98,7 +98,7 @@ public: { const wxSize clientSize = wnd.GetClientSize(); if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight())) - buffer = zen::make_unique(clientSize.GetWidth(), clientSize.GetHeight()); + buffer = std::make_unique(clientSize.GetWidth(), clientSize.GetHeight()); SelectObject(*buffer); diff --git a/wx+/file_drop.h b/wx+/file_drop.h index c9f2469c..f8943788 100644 --- a/wx+/file_drop.h +++ b/wx+/file_drop.h @@ -28,8 +28,8 @@ namespace zen /* 1. setup a window to emit EVENT_DROP_FILE: - - simple file system paths: setupFileDrop - - any shell paths with validation: setupShellItemDrop + - simple file system paths: setupFileDrop + - any shell paths with validation: setupShellItemDrop 2. register events: wnd.Connect (EVENT_DROP_FILE, FileDropEventHandler(MyDlg::OnFilesDropped), nullptr, this); diff --git a/wx+/graph.cpp b/wx+/graph.cpp index d76e4d41..601c4913 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -453,7 +453,7 @@ void Graph2D::onPaintEvent(wxPaintEvent& event) void Graph2D::OnMouseLeftDown(wxMouseEvent& event) { - activeSel = zen::make_unique(*this, event.GetPosition()); + activeSel = std::make_unique(*this, event.GetPosition()); if (!event.ControlDown()) oldSel.clear(); diff --git a/wx+/graph.h b/wx+/graph.h index 34c7ab3c..ea05709d 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -27,19 +27,20 @@ Example: setLabelY(Graph2D::Y_LABEL_RIGHT, 60, std::make_shared())); //set graph data std::shared_ptr curveDataBytes = ... - m_panelGraph->setCurve(curveDataBytes, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0))); + m_panelGraph->setCurve(curveDataBytes, Graph2D::CurveAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0))); */ struct CurvePoint { - CurvePoint() : x(0), y(0) {} + CurvePoint() {} CurvePoint(double xVal, double yVal) : x(xVal), y(yVal) {} - double x; - double y; + double x = 0; + double y = 0; }; inline bool operator==(const CurvePoint& lhs, const CurvePoint& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } inline bool operator!=(const CurvePoint& lhs, const CurvePoint& rhs) { return !(lhs == rhs); } + struct CurveData { virtual ~CurveData() {} @@ -67,9 +68,10 @@ struct SparseCurveData : public CurveData private: void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override; - bool addSteps_; + const bool addSteps_; }; + struct ArrayCurveData : public SparseCurveData { virtual double getValue(size_t pos) const = 0; @@ -96,12 +98,14 @@ private: } }; + struct VectorCurveData : public ArrayCurveData { std::vector& 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(); } + std::vector data; }; @@ -118,6 +122,7 @@ struct LabelFormatter virtual wxString formatText(double value, double optimalBlockSize) const = 0; }; + double nextNiceNumber(double blockSize); //round to next number which is convenient to read, e.g. 2.13 -> 2; 2.7 -> 2.5 struct DecimalNumberFormatter : public LabelFormatter @@ -172,8 +177,7 @@ public: class CurveAttributes { public: - CurveAttributes() : autoColor(true), drawCurveArea(false), lineWidth(2) {} - + CurveAttributes() {} //required by GCC CurveAttributes& setColor (const wxColour& col) { color = col; autoColor = false; return *this; } CurveAttributes& fillCurveArea(const wxColour& col) { fillColor = col; drawCurveArea = true; return *this; } CurveAttributes& setLineWidth(size_t width) { lineWidth = static_cast(width); return *this; } @@ -181,13 +185,13 @@ public: private: friend class Graph2D; - bool autoColor; + bool autoColor = true; wxColour color; - bool drawCurveArea; + bool drawCurveArea = false; wxColour fillColor; - int lineWidth; + int lineWidth = 2; }; void setCurve(const std::shared_ptr& data, const CurveAttributes& ca = CurveAttributes()); @@ -226,24 +230,6 @@ public: class MainAttributes { public: - MainAttributes() : - minXauto(true), - maxXauto(true), - minX(0), - maxX(0), - minYauto(true), - maxYauto(true), - minY(0), - maxY(0), - labelposX(X_LABEL_BOTTOM), - xLabelHeight(25), - labelFmtX(std::make_shared()), - labelposY(Y_LABEL_LEFT), - yLabelWidth(60), - labelFmtY(std::make_shared()), - backgroundColor(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)), - mouseSelMode(SELECT_RECTANGLE) {} - MainAttributes& setMinX(double newMinX) { minX = newMinX; minXauto = false; return *this; } MainAttributes& setMaxX(double newMaxX) { maxX = newMaxX; maxXauto = false; return *this; } @@ -278,28 +264,28 @@ public: private: friend class Graph2D; - bool minXauto; //autodetect range for X value - bool maxXauto; - double minX; //x-range to visualize - double maxX; + bool minXauto = true; //autodetect range for X value + bool maxXauto = true; + double minX = 0; //x-range to visualize + double maxX = 0; // - bool minYauto; //autodetect range for Y value - bool maxYauto; - double minY; //y-range to visualize - double maxY; + bool minYauto = true; //autodetect range for Y value + bool maxYauto = true; + double minY = 0; //y-range to visualize + double maxY = 0; // - PosLabelX labelposX; - int xLabelHeight; - std::shared_ptr labelFmtX; + PosLabelX labelposX = X_LABEL_BOTTOM; + int xLabelHeight = 25; + std::shared_ptr labelFmtX = std::make_shared(); - PosLabelY labelposY; - int yLabelWidth; - std::shared_ptr labelFmtY; + PosLabelY labelposY = Y_LABEL_LEFT; + int yLabelWidth = 60; + std::shared_ptr labelFmtY = std::make_shared(); std::map cornerTexts; - wxColour backgroundColor; - SelMode mouseSelMode; + wxColour backgroundColor = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + SelMode mouseSelMode = SELECT_RECTANGLE; }; void setAttributes(const MainAttributes& newAttr) { attr = newAttr; Refresh(); } MainAttributes getAttributes() const { return attr; } diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 9eea2e28..c819763f 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -162,9 +162,9 @@ void drawTextLabelFitting(wxDC& dc, const wxString& text, const wxRect& rect, in wxDC::GetMultiLineTextExtent() is implemented in terms of wxDC::GetTextExtent() average total times: - Windows Linux - single wxDC::DrawText() 7µs 50µs - wxDC::DrawLabel() + 10µs 90µs + Windows Linux + single wxDC::DrawText() 7µs 50µs + wxDC::DrawLabel() + 10µs 90µs repeated GetTextExtent() */ @@ -581,8 +581,7 @@ public: wnd_(wnd), colFrom_(colFrom), colTo_(colFrom), - clientPosX_(clientPosX), - singleClick_(true) { wnd_.CaptureMouse(); } + clientPosX_(clientPosX) { wnd_.CaptureMouse(); } ~ColumnMove() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); } size_t getColumnFrom() const { return colFrom_; } @@ -597,7 +596,7 @@ private: const size_t colFrom_; size_t colTo_; const int clientPosX_; - bool singleClick_; + bool singleClick_ = true; }; } @@ -695,10 +694,10 @@ private: { if (!event.LeftDClick()) //double-clicks never seem to arrive here; why is this checked at all??? if (Opt colWidth = refParent().getColWidth(action->col)) - activeResizing = make_unique(*this, action->col, *colWidth, event.GetPosition().x); + activeResizing = std::make_unique(*this, action->col, *colWidth, event.GetPosition().x); } else //a move or single click - activeMove = make_unique(*this, action->col, event.GetPosition().x); + activeMove = std::make_unique(*this, action->col, event.GetPosition().x); } event.Skip(); } @@ -792,7 +791,7 @@ private: { if (const Opt action = refParent().clientPosToColumnAction(event.GetPosition())) { - highlightCol = make_unique(action->col); + highlightCol = std::make_unique(action->col); if (action->wantResize) SetCursor(wxCURSOR_SIZEWE); //set window-local only! :) @@ -863,11 +862,8 @@ public: RowLabelWin& rowLabelWin, ColLabelWin& colLabelWin) : SubWindow(parent), rowLabelWin_(rowLabelWin), - colLabelWin_(colLabelWin), - cursorRow(0), - selectionAnchor(0), - gridUpdatePending(false) - { + colLabelWin_(colLabelWin) + { Connect(EVENT_GRID_HAS_SCROLLED, wxEventHandler(MainWin::onRequestWindowUpdate), nullptr, this); } @@ -994,15 +990,15 @@ private: if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area! { if (event.ControlDown()) - activeSelection = make_unique(*this, row, !refParent().isSelected(row)); + activeSelection = std::make_unique(*this, row, !refParent().isSelected(row)); else if (event.ShiftDown()) { - activeSelection = make_unique(*this, selectionAnchor, true); + activeSelection = std::make_unique(*this, selectionAnchor, true); refParent().clearSelection(ALLOW_GRID_EVENT); } else { - activeSelection = make_unique(*this, row, true); + activeSelection = std::make_unique(*this, row, true); refParent().clearSelection(ALLOW_GRID_EVENT); } } @@ -1096,9 +1092,7 @@ private: { public: MouseSelection(MainWin& wnd, size_t rowStart, bool positiveSelect) : - wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect), toScrollX(0), toScrollY(0), - tickCountLast(getTicks()), - ticksPerSec_(ticksPerSec()) + wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positiveSelect) { wnd_.CaptureMouse(); timer.Connect(wxEVT_TIMER, wxEventHandler(MouseSelection::onTimer), nullptr, this); @@ -1121,8 +1115,7 @@ private: tickCountLast = now; } - wxMouseState mouseState = wxGetMouseState(); - const wxPoint clientPos = wnd_.ScreenToClient(wxPoint(mouseState.GetX(), mouseState.GetY())); + const wxPoint clientPos = wnd_.ScreenToClient(wxGetMousePosition()); const wxSize clientSize = wnd_.GetClientSize(); assert(wnd_.GetClientAreaOrigin() == wxPoint()); @@ -1183,10 +1176,10 @@ private: ptrdiff_t rowCurrent_; const bool positiveSelect_; wxTimer timer; - double toScrollX; //count outstanding scroll units to scroll while dragging mouse - double toScrollY; // - TickVal tickCountLast; - const std::int64_t ticksPerSec_; + double toScrollX = 0; //count outstanding scroll units to scroll while dragging mouse + double toScrollY = 0; // + TickVal tickCountLast = getTicks(); + const std::int64_t ticksPerSec_ = ticksPerSec(); }; void ScrollWindow(int dx, int dy, const wxRect* rect) override @@ -1224,9 +1217,9 @@ private: std::unique_ptr activeSelection; //bound while user is selecting with mouse - ptrdiff_t cursorRow; - size_t selectionAnchor; - bool gridUpdatePending; + ptrdiff_t cursorRow = 0; + size_t selectionAnchor = 0; + bool gridUpdatePending = false; }; //---------------------------------------------------------------------------------------------------------------- @@ -1237,14 +1230,7 @@ Grid::Grid(wxWindow* parent, const wxPoint& pos, const wxSize& size, long style, - const wxString& name) : wxScrolledWindow(parent, id, pos, size, style | wxWANTS_CHARS, name), - showScrollbarX(SB_SHOW_AUTOMATIC), - showScrollbarY(SB_SHOW_AUTOMATIC), - colLabelHeight(0), //dummy init - drawRowLabel(true), - allowColumnMove(true), - allowColumnResize(true), - rowCountOld(0) + const wxString& name) : wxScrolledWindow(parent, id, pos, size, style | wxWANTS_CHARS, name) { cornerWin_ = new CornerWin (*this); // rowLabelWin_ = new RowLabelWin(*this); //owership handled by "this" @@ -1278,24 +1264,24 @@ void Grid::updateWindowSizes(bool updateScrollbar) { /* We have to deal with TWO nasty circular dependencies: 1. - rowLabelWidth - /|\ - mainWin::client width - /|\ - SetScrollbars -> show/hide horizontal scrollbar depending on client width - /|\ - mainWin::client height -> possibly trimmed by horizontal scrollbars - /|\ - rowLabelWidth + rowLabelWidth + /|\ + mainWin::client width + /|\ + SetScrollbars -> show/hide horizontal scrollbar depending on client width + /|\ + mainWin::client height -> possibly trimmed by horizontal scrollbars + /|\ + rowLabelWidth 2. - mainWin_->GetClientSize() - /|\ - SetScrollbars -> show/hide scrollbars depending on whether client size is big enough - /|\ - GetClientSize(); -> possibly trimmed by scrollbars - /|\ - mainWin_->GetClientSize() -> also trimmed, since it's a sub-window! + mainWin_->GetClientSize() + /|\ + SetScrollbars -> show/hide scrollbars depending on whether client size is big enough + /|\ + GetClientSize(); -> possibly trimmed by scrollbars + /|\ + mainWin_->GetClientSize() -> also trimmed, since it's a sub-window! */ //break this vicious circle: @@ -1379,27 +1365,27 @@ void Grid::updateWindowSizes(bool updateScrollbar) ------------------------ \|/ | | | | | ------------------------ \|/ - gw := gross width - nw := net width := gross width - sb size - gh := gross height - nh := net height := gross height - sb size + gw := gross width + nw := net width := gross width - sb size + gh := gross height + nh := net height := gross height - sb size There are 6 cases that can occur: --------------------------------- - lw := logical width - lh := logical height + lw := logical width + lh := logical height 1. lw <= gw && lh <= gh => no scrollbars needed 2. lw > gw && lh > gh => need both scrollbars 3. lh > gh - 4.1 lw <= nw => need vertical scrollbar only - 4.2 nw < lw <= gw => need both scrollbars + 4.1 lw <= nw => need vertical scrollbar only + 4.2 nw < lw <= gw => need both scrollbars 4. lw > gw - 3.1 lh <= nh => need horizontal scrollbar only - 3.2 nh < lh <= gh => need both scrollbars + 3.1 lh <= nh => need horizontal scrollbar only + 3.2 nh < lh <= gh => need both scrollbars */ } } @@ -2051,11 +2037,11 @@ void Grid::scrollTo(size_t row) } - bool Grid::Enable(bool enable) - { - Refresh(); - return wxScrolledWindow::Enable(enable); - } +bool Grid::Enable(bool enable) +{ + Refresh(); + return wxScrolledWindow::Enable(enable); +} size_t Grid::getGridCursor() const diff --git a/wx+/grid.h b/wx+/grid.h index 8523b698..5fef34e2 100644 --- a/wx+/grid.h +++ b/wx+/grid.h @@ -323,21 +323,21 @@ private: ColLabelWin* colLabelWin_; MainWin* mainWin_; - ScrollBarStatus showScrollbarX; - ScrollBarStatus showScrollbarY; + ScrollBarStatus showScrollbarX = SB_SHOW_AUTOMATIC; + ScrollBarStatus showScrollbarY = SB_SHOW_AUTOMATIC; - int colLabelHeight; - bool drawRowLabel; + int colLabelHeight = 0; + bool drawRowLabel = true; std::shared_ptr dataView_; Selection selection; - bool allowColumnMove; - bool allowColumnResize; + bool allowColumnMove = true; + bool allowColumnResize = true; std::vector visibleCols; //individual widths, type and total column count std::vector oldColAttributes; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*! - size_t rowCountOld; //at the time of last Grid::Refresh() + size_t rowCountOld = 0; //at the time of last Grid::Refresh() }; } diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 7383f620..98cdc7a8 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -22,7 +22,7 @@ namespace void loadAnimFromZip(wxZipInputStream& zipInput, wxAnimation& anim) { //work around wxWidgets bug: - //construct seekable input stream (zip-input stream is non-seekable) for wxAnimation::Load() + //construct seekable input stream (zip-input stream is not seekable) for wxAnimation::Load() //luckily this method call is very fast: below measurement precision! std::vector data; data.reserve(10000); diff --git a/wx+/image_tools.h b/wx+/image_tools.h index 5e21bd40..43c1a625 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -33,7 +33,7 @@ wxImage stackImages(const wxImage& img1, const wxImage& img2, ImageStackLayout d wxImage createImageFromText(const wxString& text, const wxFont& font, const wxColor& col); -wxBitmap layOver(const wxBitmap& foreground, const wxBitmap& background); //merge +wxBitmap layOver(const wxBitmap& background, const wxBitmap& foreground); //merge wxImage greyScale(const wxImage& img); //greyscale + brightness adaption wxBitmap greyScale(const wxBitmap& bmp); // @@ -147,7 +147,7 @@ void adjustBrightness(wxImage& img, int targetLevel) inline -wxBitmap layOver(const wxBitmap& foreground, const wxBitmap& background) +wxBitmap layOver(const wxBitmap& background, const wxBitmap& foreground) { assert(foreground.HasAlpha() == background.HasAlpha()); //we don't support mixed-mode brittleness! diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index 480021eb..188ad76d 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -145,7 +145,7 @@ public: else m_checkBoxCustom->Hide(); - Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StandardPopupDialog::OnKeyPressed), nullptr, this); + Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StandardPopupDialog::OnKeyPressed), nullptr, this); //dialog-specific local key events } private: @@ -154,11 +154,19 @@ private: void OnKeyPressed(wxKeyEvent& event) { - const int keyCode = event.GetKeyCode(); - if (keyCode == WXK_ESCAPE) //handle case where cancel button is hidden! + switch (event.GetKeyCode()) { - EndModal(static_cast(ConfirmationButton3::CANCEL)); - return; + case WXK_RETURN: + case WXK_NUMPAD_ENTER: + { + wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); + OnButtonAffirmative(dummy); + return; + } + + case WXK_ESCAPE: //handle case where cancel button is hidden! + EndModal(static_cast(ConfirmationButton3::CANCEL)); + return; } event.Skip(); } diff --git a/wx+/rtl.h b/wx+/rtl.h index 5f1c9321..84182b2c 100644 --- a/wx+/rtl.h +++ b/wx+/rtl.h @@ -55,7 +55,7 @@ void drawRtlImpl(wxDC& dc, const wxRect& rect, std::unique_ptr& buffer if (dc.GetLayoutDirection() == wxLayout_RightToLeft) { if (!buffer || buffer->GetWidth() != rect.width || buffer->GetHeight() < rect.height) //[!] since we do a mirror, width needs to match exactly! - buffer = zen::make_unique(rect.width, rect.height); + buffer = std::make_unique(rect.width, rect.height); wxMemoryDC memDc(*buffer); memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction! -- cgit