diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/app_main.h | 14 | ||||
-rw-r--r-- | wx+/async_task.h | 2 | ||||
-rw-r--r-- | wx+/bitmap_button.h | 17 | ||||
-rw-r--r-- | wx+/choice_enum.h | 4 | ||||
-rw-r--r-- | wx+/context_menu.h | 51 | ||||
-rw-r--r-- | wx+/dc.h | 53 | ||||
-rw-r--r-- | wx+/file_drop.cpp | 7 | ||||
-rw-r--r-- | wx+/file_drop.h | 46 | ||||
-rw-r--r-- | wx+/graph.cpp | 130 | ||||
-rw-r--r-- | wx+/graph.h | 53 | ||||
-rw-r--r-- | wx+/grid.cpp | 459 | ||||
-rw-r--r-- | wx+/grid.h | 86 | ||||
-rw-r--r-- | wx+/image_resources.cpp | 16 | ||||
-rw-r--r-- | wx+/image_tools.cpp | 4 | ||||
-rw-r--r-- | wx+/popup_dlg.cpp | 18 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.cpp | 106 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.h | 46 | ||||
-rw-r--r-- | wx+/rtl.h | 2 | ||||
-rw-r--r-- | wx+/toggle_button.h | 4 |
19 files changed, 537 insertions, 581 deletions
diff --git a/wx+/app_main.h b/wx+/app_main.h index 570a2a9c..17a59d7b 100644 --- a/wx+/app_main.h +++ b/wx+/app_main.h @@ -21,16 +21,10 @@ bool globalWindowWasSet(); - //######################## implementation ######################## namespace impl { -inline -bool& refGlobalWindowStatus() -{ - static bool status = false; //external linkage! - return status; -} +inline bool haveGlobalWindow = false; } @@ -40,10 +34,12 @@ void setGlobalWindow(wxWindow* window) wxTheApp->SetTopWindow(window); wxTheApp->SetExitOnFrameDelete(true); - impl::refGlobalWindowStatus() = true; + impl::haveGlobalWindow = true; } -inline bool globalWindowWasSet() { return impl::refGlobalWindowStatus(); } + +inline +bool globalWindowWasSet() { return impl::haveGlobalWindow; } } #endif //APP_MAIN_H_08215601837818347575856 diff --git a/wx+/async_task.h b/wx+/async_task.h index 47660a6a..1571c917 100644 --- a/wx+/async_task.h +++ b/wx+/async_task.h @@ -114,7 +114,7 @@ private: class AsyncGuiQueue : private wxEvtHandler { public: - AsyncGuiQueue(int pollingMs = 50) : pollingMs_(pollingMs) { timer_.Connect(wxEVT_TIMER, wxEventHandler(AsyncGuiQueue::onTimerEvent), nullptr, this); } + AsyncGuiQueue(int pollingMs = 50) : pollingMs_(pollingMs) { timer_.Bind(wxEVT_TIMER, [this](wxTimerEvent& event) { onTimerEvent(event); }); } template <class Fun, class Fun2> void processAsync(Fun&& evalAsync, Fun2&& evalOnGui) diff --git a/wx+/bitmap_button.h b/wx+/bitmap_button.h index 508a72fc..8fe8e146 100644 --- a/wx+/bitmap_button.h +++ b/wx+/bitmap_button.h @@ -26,7 +26,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxButtonNameStr) : + const wxString& name = wxASCII_STR(wxButtonNameStr)) : wxBitmapButton(parent, id, wxNullBitmap, pos, size, style, validator, name) { SetLabel(label); @@ -98,9 +98,10 @@ 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())); + + const wxColor borderCol(0x79, 0xbc, 0xed); //medium blue + const wxColor innerCol (0xcc, 0xe4, 0xf8); //light blue + drawFilledRectangle(dc, wxRect(bmp.GetSize()), fastFromDIP(1), borderCol, innerCol); } return bmp; } @@ -116,7 +117,8 @@ wxBitmap renderPressedButton(const wxSize& sz) const wxColor colTo(0x11, 0x79, 0xfe); //light blue wxMemoryDC dc(bmp); - dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + dc.SetPen(*wxTRANSPARENT_PEN); //wxTRANSPARENT_PEN is about 2x faster than redundantly drawing with col! + wxRect rect(bmp.GetSize()); const int borderSize = fastFromDIP(3); @@ -125,10 +127,13 @@ wxBitmap renderPressedButton(const wxSize& sz) 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.SetBrush(colGradient); dc.DrawRectangle(rect); rect.Deflate(1); } + + dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + dc.DrawRectangle(rect); } return bmp; } diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index e11b9991..626aa39a 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -104,7 +104,7 @@ Enum getEnumVal(const EnumDescrList<Enum>& mapping, const wxChoice& ctrl) { const int selectedPos = ctrl.GetSelection(); - if (0 <= selectedPos && selectedPos < static_cast<int>(mapping.descrList.size())) + if (0 <= selectedPos && selectedPos < std::ssize(mapping.descrList)) return mapping.descrList[selectedPos].first; else { @@ -117,7 +117,7 @@ template <class Enum> void updateTooltipEnumVal(const EnumDescrList<Enum>& mappi { const int selectedPos = ctrl.GetSelection(); - if (0 <= selectedPos && selectedPos < static_cast<int>(mapping.descrList.size())) + if (0 <= selectedPos && selectedPos < std::ssize(mapping.descrList)) { if (const auto& [text, tooltip] = mapping.descrList[selectedPos].second; !tooltip.empty()) diff --git a/wx+/context_menu.h b/wx+/context_menu.h index 7096da33..c53cec39 100644 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -13,15 +13,14 @@ #include <wx/menu.h> #include <wx/app.h> -/* -A context menu supporting 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); -*/ +/* A context menu supporting 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); */ + namespace zen { class ContextMenu : private wxEvtHandler @@ -32,9 +31,11 @@ public: 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 (img.IsOk()) newItem->SetBitmap(img); //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 + 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! } @@ -42,7 +43,8 @@ public: { wxMenuItem* newItem = menu_->AppendCheckItem(wxID_ANY, label); newItem->Check(checked); - if (!enabled) newItem->Enable(false); + if (!enabled) + newItem->Enable(false); commandList_[newItem->GetId()] = command; } @@ -50,7 +52,8 @@ public: { wxMenuItem* newItem = menu_->AppendRadioItem(wxID_ANY, label); newItem->Check(selected); - if (!enabled) newItem->Enable(false); + if (!enabled) + newItem->Enable(false); commandList_[newItem->GetId()] = command; } @@ -65,16 +68,18 @@ 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 (img.IsOk()) newItem->SetBitmap(img); //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); + if (!enabled) + newItem->Enable(false); } void popup(wxWindow& wnd, const wxPoint& pos = wxDefaultPosition) //show popup menu + process lambdas { //eventually all events from submenu items will be received by this menu for (const auto& [itemId, command] : commandList_) - menu_->Connect(itemId, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(ContextMenu::onSelection), new GenericCommand(command) /*pass ownership*/, this); + menu_->Bind(wxEVT_COMMAND_MENU_SELECTED, [command /*clang bug*/= command](wxCommandEvent& event) { command(); }, itemId); wnd.PopupMenu(menu_.get(), pos); wxTheApp->ProcessPendingEvents(); //make sure lambdas are evaluated before going out of scope; @@ -85,20 +90,8 @@ private: ContextMenu (const ContextMenu&) = delete; ContextMenu& operator=(const ContextMenu&) = delete; - void onSelection(wxCommandEvent& event) - { - if (auto cmd = dynamic_cast<GenericCommand*>(event.m_callbackUserData)) - (cmd->fun_)(); - } - - struct GenericCommand : public wxObject - { - GenericCommand(const std::function<void()>& fun) : fun_(fun) {} - std::function<void()> fun_; - }; - std::unique_ptr<wxMenu> menu_ = std::make_unique<wxMenu>(); - std::map<int, std::function<void()>> commandList_; //(item id, command) + std::map<int /*item id*/, std::function<void()> /*command*/> commandList_; }; } @@ -31,20 +31,36 @@ namespace zen 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); + { + //wxDC::DrawRectangle() just widens inner area if wxTRANSPARENT_PEN is used! + //bonus: wxTRANSPARENT_PEN is about 2x faster than redundantly drawing with col! + wxDCPenChanger dummy (dc, *wxTRANSPARENT_PEN); + wxDCBrushChanger dummy2(dc, col); + dc.DrawRectangle(rect); } } +//properly draw rectangle respecting high DPI (and avoiding wxPen position fuzzyness) +inline +void drawFilledRectangle(wxDC& dc, wxRect rect, int borderWidth, const wxColor& borderCol, const wxColor& innerCol) +{ + assert(borderCol.IsSolid() && innerCol.IsSolid()); + wxDCPenChanger graphPen (dc, *wxTRANSPARENT_PEN); + wxDCBrushChanger graphBrush(dc, borderCol); + dc.DrawRectangle(rect); + rect.Deflate(borderWidth); //attention, more wxWidgets design mistakes: behavior of wxRect::Deflate depends on object being const/non-const!!! + + dc.SetBrush(innerCol); + 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) */ @@ -52,17 +68,14 @@ void clearArea(wxDC& dc, const wxRect& rect, const wxColor& col) inline int fastFromDIP(int d) //like wxWindow::FromDIP (but tied to primary monitor and buffered) { -#ifdef wxHAVE_DPI_INDEPENDENT_PIXELS //pulled from wx/window.h: https://github.com/wxWidgets/wxWidgets/blob/master/include/wx/window.h#L2029 - return d; //e.g. macOS, GTK3 -#else //https://github.com/wxWidgets/wxWidgets/blob/master/src/common/wincmn.cpp#L2865 - static_assert(GTK_MAJOR_VERSION == 2); +#ifndef wxHAVE_DPI_INDEPENDENT_PIXELS +#error why is wxHAVE_DPI_INDEPENDENT_PIXELS not defined? +#endif //GTK2 doesn't properly support high DPI: https://freefilesync.org/forum/viewtopic.php?t=6114 //=> requires general fix at wxWidgets-level - 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 1.0 * d * dpiY / defaultDpi + 0.49 /*round values like 1.5 down, e.g. 1 pixel on 150% scale*/; -#endif + + //https://github.com/wxWidgets/wxWidgets/blob/d9d05c2bb201078f5e762c42458ca2f74af5b322/include/wx/window.h#L2060 + return d; //e.g. macOS, GTK3 } @@ -74,8 +87,8 @@ class RecursiveDcClipper public: RecursiveDcClipper(wxDC& dc, const wxRect& r) : dc_(dc) { - if (auto it = clippingAreas.find(&dc); - it != clippingAreas.end()) + if (auto it = clippingAreas_.find(&dc); + it != clippingAreas_.end()) { oldRect_ = it->second; @@ -87,7 +100,7 @@ public: else { dc_.SetClippingRegion(r); - clippingAreas.emplace(&dc_, r); + clippingAreas_.emplace(&dc_, r); } } @@ -97,10 +110,10 @@ public: if (oldRect_) { dc_.SetClippingRegion(*oldRect_); - clippingAreas[&dc_] = *oldRect_; + clippingAreas_[&dc_] = *oldRect_; } else - clippingAreas.erase(&dc_); + clippingAreas_.erase(&dc_); } private: @@ -108,7 +121,7 @@ private: RecursiveDcClipper& operator=(const RecursiveDcClipper&) = delete; //associate "active" clipping area with each DC - inline static std::unordered_map<wxDC*, wxRect> clippingAreas; + inline static std::unordered_map<wxDC*, wxRect> clippingAreas_; std::optional<wxRect> oldRect_; wxDC& dc_; diff --git a/wx+/file_drop.cpp b/wx+/file_drop.cpp index 938f9dbd..42cfbd3d 100644 --- a/wx+/file_drop.cpp +++ b/wx+/file_drop.cpp @@ -13,7 +13,10 @@ using namespace zen; -const wxEventType zen::EVENT_DROP_FILE = wxNewEventType(); +namespace zen +{ +wxDEFINE_EVENT(EVENT_DROP_FILE, FileDropEvent); +} @@ -23,7 +26,7 @@ namespace class WindowDropTarget : public wxFileDropTarget { public: - WindowDropTarget(const wxWindow& dropWindow) : dropWindow_(dropWindow) {} + explicit WindowDropTarget(const wxWindow& dropWindow) : dropWindow_(dropWindow) {} private: wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def) override diff --git a/wx+/file_drop.h b/wx+/file_drop.h index 0a1089fc..e5de8f95 100644 --- a/wx+/file_drop.h +++ b/wx+/file_drop.h @@ -16,47 +16,29 @@ namespace zen { -//register simple file drop event (without issue of freezing dialogs and without wxFileDropTarget overdesign) -//CAVEAT: a drop target window must not be directly or indirectly contained within a wxStaticBoxSizer until the following wxGTK bug -//is fixed. According to wxWidgets release cycles this is expected to be: never http://trac.wxwidgets.org/ticket/2763 +/* register simple file drop event (without issue of freezing dialogs and without wxFileDropTarget overdesign) + CAVEAT: a drop target window must not be directly or indirectly contained within a wxStaticBoxSizer until the following wxGTK bug + is fixed. According to wxWidgets release cycles this is expected to be: never http://trac.wxwidgets.org/ticket/2763 -/* -1. setup a window to emit EVENT_DROP_FILE: - - simple file system paths: setupFileDrop - - any shell paths with validation: setupShellItemDrop + 1. setup a window to emit EVENT_DROP_FILE: + - simple file system paths: setupFileDrop + - any shell paths with validation: setupShellItemDrop -2. register events: -wnd.Connect (EVENT_DROP_FILE, FileDropEventHandler(MyDlg::OnFilesDropped), nullptr, this); -wnd.Disconnect(EVENT_DROP_FILE, FileDropEventHandler(MyDlg::OnFilesDropped), nullptr, this); + 2. register events: + wnd.Bind(EVENT_DROP_FILE, [this](FileDropEvent& event) { onFilesDropped(event); }); */ +struct FileDropEvent; +wxDECLARE_EVENT(EVENT_DROP_FILE, FileDropEvent); -3. do something: -void MyDlg::OnFilesDropped(FileDropEvent& event); -*/ -extern const wxEventType EVENT_DROP_FILE; - - -class FileDropEvent : public wxCommandEvent +struct FileDropEvent : public wxEvent { -public: - FileDropEvent(const std::vector<Zstring>& droppedPaths) : wxCommandEvent(EVENT_DROP_FILE), droppedPaths_(droppedPaths) { StopPropagation(); } - - const std::vector<Zstring>& getPaths() const { return droppedPaths_; } - -private: - wxEvent* Clone() const override { return new FileDropEvent(*this); } + explicit FileDropEvent(const std::vector<Zstring>& droppedPaths) : wxEvent(0 /*winid*/, EVENT_DROP_FILE), itemPaths_(droppedPaths) {} + FileDropEvent* Clone() const override { return new FileDropEvent(*this); } - const std::vector<Zstring> droppedPaths_; + const std::vector<Zstring> itemPaths_; }; -using FileDropEventFunction = void (wxEvtHandler::*)(FileDropEvent&); - -#define FileDropEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(FileDropEventFunction, &func) - - - void setupFileDrop(wxWindow& dropWindow); } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index f3805fca..7bd67504 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -10,6 +10,7 @@ #include <numeric> #include <zen/basic_math.h> #include <zen/scope_guard.h> +#include <zen/perf.h> #include "dc.h" using namespace zen; @@ -17,7 +18,10 @@ using namespace zen; //todo: support zoom via mouse wheel? -const wxEventType zen::wxEVT_GRAPH_SELECTION = wxNewEventType(); +namespace zen +{ +wxDEFINE_EVENT(EVENT_GRAPH_SELECTION, GraphSelectEvent); +} double zen::nextNiceNumber(double blockSize) //round to next number which is a convenient to read block size @@ -44,26 +48,18 @@ wxColor getDefaultColor(size_t pos) { switch (pos % 10) { - case 0: - return { 0, 69, 134 }; //blue - case 1: - return { 255, 66, 14 }; //red - case 2: - return { 255, 211, 32 }; //yellow - case 3: - return { 87, 157, 28 }; //green - case 4: - return { 126, 0, 33 }; //royal - case 5: - return { 131, 202, 255 }; //light blue - case 6: - return { 49, 64, 4 }; //dark green - case 7: - return { 174, 207, 0 }; //light green - case 8: - return { 75, 31, 111 }; //purple - case 9: - return { 255, 149, 14 }; //orange + //*INDENT-OFF* + case 0: return { 0, 69, 134 }; //blue + case 1: return { 255, 66, 14 }; //red + case 2: return { 255, 211, 32 }; //yellow + case 3: return { 87, 157, 28 }; //green + case 4: return { 126, 0, 33 }; //royal + case 5: return { 131, 202, 255 }; //light blue + case 6: return { 49, 64, 4 }; //dark green + case 7: return { 174, 207, 0 }; //light green + case 8: return { 75, 31, 111 }; //purple + case 9: return { 255, 149, 14 }; //orange + //*INDENT-ON* } assert(false); return *wxBLACK; @@ -228,7 +224,7 @@ void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, Grap //add text shadow to improve readability: wxDCTextColourChanger textColor(dc, colorBack); - dc.DrawText(txt, drawPos + border + wxSize(fastFromDIP(1), fastFromDIP(1))); + dc.DrawText(txt, drawPos + border + wxSize(1, 1) /*better without fastFromDIP()?*/); textColor.Set(colorText); dc.DrawText(txt, drawPos + border); @@ -292,7 +288,7 @@ struct GetIntersectionX { const double deltaX = to.x - from.x; const double deltaY = to.y - from.y; - return numeric::isNull(deltaX) ? to : CurvePoint(x_, from.y + (x_ - from.x) / deltaX * deltaY); + return numeric::isNull(deltaX) ? to : CurvePoint{x_, from.y + (x_ - from.x) / deltaX * deltaY}; } private: @@ -306,7 +302,7 @@ struct GetIntersectionY { const double deltaX = to.x - from.x; const double deltaY = to.y - from.y; - return numeric::isNull(deltaY) ? to : CurvePoint(from.x + (y_ - from.y) / deltaY * deltaX, y_); + return numeric::isNull(deltaY) ? to : CurvePoint{from.x + (y_ - from.y) / deltaY * deltaX, y_}; } private: @@ -350,7 +346,7 @@ std::vector<CurvePoint> ContinuousCurveData::getPoints(double minX, double maxX, for (int i = posFrom; i <= posTo; ++i) { const double x = cvrtX.screenToReal(i); - points.emplace_back(x, getValue(x)); + points.emplace_back(CurvePoint{x, getValue(x)}); } } return points; @@ -375,7 +371,7 @@ std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, con if (addSteps_) if (pt.y != points.back().y) - points.emplace_back(CurvePoint(pt.x, points.back().y)); //[!] aliasing parameter not yet supported via emplace_back: VS bug! => make copy + points.emplace_back(CurvePoint{pt.x, points.back().y}); //[!] aliasing parameter not yet supported via emplace_back: VS bug! => make copy } points.push_back(pt); }; @@ -393,9 +389,9 @@ std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, con const int posGe = ptGe ? cvrtX.realToScreenRound(ptGe->x) : i - 1; assert(!ptLe || posLe <= i); //check for invalid return values assert(!ptGe || posGe >= i); // - /* - Breakdown of all combinations of posLe, posGe and expected action (n >= 1) - Note: For every empty x-range of at least one pixel, both next and previous points must be saved to keep the interpolating line stable!!! + + /* Breakdown of all combinations of posLe, posGe and expected action (n >= 1) + Note: For every empty x-range of at least one pixel, both next and previous points must be saved to keep the interpolating line stable!!! posLe | posGe | action +-------+-------+-------- @@ -410,8 +406,7 @@ std::vector<CurvePoint> SparseCurveData::getPoints(double minX, double maxX, con | none | i + n | save ptGe; jump to position posGe + 1 | i | i + n | save ptLe; if n == 1: continue; else: save ptGe; jump to position posGe + 1 | i - n | i + n | save ptLe, ptGe; jump to position posGe + 1 - +-------+-------+-------- - */ + +-------+-------+-------- */ if (posGe < i) { if (posLe == i) @@ -448,18 +443,18 @@ Graph2D::Graph2D(wxWindow* parent, long style, const wxString& name) : wxPanel(parent, winid, pos, size, style, name) { - Connect(wxEVT_PAINT, wxPaintEventHandler(Graph2D::onPaintEvent), nullptr, this); - Connect(wxEVT_SIZE, wxSizeEventHandler (Graph2D::onSizeEvent ), nullptr, this); + Bind(wxEVT_PAINT, [this](wxPaintEvent& event) { onPaintEvent(event); }); + Bind(wxEVT_SIZE, [this](wxSizeEvent& event) { Refresh(); event.Skip(); }); Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) {}); //https://wiki.wxwidgets.org/Flicker-Free_Drawing //SetDoubleBuffered(true); slow as hell! SetBackgroundStyle(wxBG_STYLE_PAINT); - Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(Graph2D::OnMouseLeftDown), nullptr, this); - Connect(wxEVT_MOTION, wxMouseEventHandler(Graph2D::OnMouseMovement), nullptr, this); - Connect(wxEVT_LEFT_UP, wxMouseEventHandler(Graph2D::OnMouseLeftUp), nullptr, this); - Connect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(Graph2D::OnMouseCaptureLost), nullptr, this); + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) { onMouseLeftDown(event); }); + Bind(wxEVT_MOTION, [this](wxMouseEvent& event) { onMouseMovement(event); }); + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& event) { onMouseLeftUp (event); }); + Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent& event) { onMouseCaptureLost(event); }); } @@ -471,7 +466,7 @@ void Graph2D::onPaintEvent(wxPaintEvent& event) } -void Graph2D::OnMouseLeftDown(wxMouseEvent& event) +void Graph2D::onMouseLeftDown(wxMouseEvent& event) { activeSel_ = std::make_unique<MouseSelection>(*this, event.GetPosition()); @@ -481,7 +476,7 @@ void Graph2D::OnMouseLeftDown(wxMouseEvent& event) } -void Graph2D::OnMouseMovement(wxMouseEvent& event) +void Graph2D::onMouseMovement(wxMouseEvent& event) { if (activeSel_.get()) { @@ -491,7 +486,7 @@ void Graph2D::OnMouseMovement(wxMouseEvent& event) } -void Graph2D::OnMouseLeftUp(wxMouseEvent& event) +void Graph2D::onMouseLeftUp(wxMouseEvent& event) { if (activeSel_.get()) { @@ -510,7 +505,7 @@ void Graph2D::OnMouseLeftUp(wxMouseEvent& event) } -void Graph2D::OnMouseCaptureLost(wxMouseCaptureLostEvent& event) +void Graph2D::onMouseCaptureLost(wxMouseCaptureLostEvent& event) { activeSel_.reset(); Refresh(); @@ -546,15 +541,14 @@ void Graph2D::render(wxDC& dc) const //wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); const int xLabelHeight = attr_.xLabelHeight ? *attr_.xLabelHeight : GetCharHeight() + fastFromDIP(2) /*margin*/; - const int yLabelWidth = attr_.yLabelWidth ? *attr_.yLabelWidth : dc.GetTextExtent(L"1,23457e+07").x; - - /* - ----------------------- - | | x-label | - ----------------------- - |y-label | graph area | - |---------------------- - */ + const int yLabelWidth = attr_.yLabelWidth ? *attr_.yLabelWidth : dc.GetTextExtent(L"1.23457e+07").x; + + /* ----------------------- + | | x-label | + ----------------------- + |y-label | graph area | + |---------------------- */ + wxRect graphArea = clientRect; int xLabelPosY = clientRect.y; int yLabelPosX = clientRect.x; @@ -589,15 +583,9 @@ void Graph2D::render(wxDC& dc) const assert(attr_.labelposX == LABEL_X_NONE || attr_.labelFmtX); assert(attr_.labelposY == LABEL_Y_NONE || attr_.labelFmtY); - { - //paint graph background (excluding label area) - wxDCPenChanger dummy (dc, 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 - - dc.DrawRectangle(graphArea); - graphArea.Deflate(1, 1); //attention more wxWidgets design mistakes: behavior of wxRect::Deflate depends on object being const/non-const!!! - } + //paint graph background (excluding label area) + drawFilledRectangle(dc, graphArea, fastFromDIP(1), getBorderColor(), attr_.colorBack); + graphArea.Deflate(fastFromDIP(1)); //set label areas respecting graph area border! const wxRect xLabelArea(graphArea.x, xLabelPosY, graphArea.width, xLabelHeight); @@ -691,8 +679,8 @@ void Graph2D::render(wxDC& dc) const if (curves_[index].second.fillMode == CurveAttributes::FILL_CURVE) if (!cp.empty()) { - cp.emplace_back(CurvePoint(cp.back ().x, minY)); //add lower right and left corners - cp.emplace_back(CurvePoint(cp.front().x, minY)); //[!] aliasing parameter not yet supported via emplace_back: VS bug! => make copy + cp.emplace_back(CurvePoint{cp.back ().x, minY}); //add lower right and left corners + cp.emplace_back(CurvePoint{cp.front().x, minY}); //[!] aliasing parameter not yet supported via emplace_back: VS bug! => make copy oobMarker[index].back() = true; oobMarker[index].push_back(true); oobMarker[index].push_back(true); @@ -733,26 +721,26 @@ void Graph2D::render(wxDC& dc) const widen(&screenFromY, &screenToY); //save current selection as "double" coordinates - activeSel_->refSelection().from = CurvePoint(cvrtX.screenToReal(screenFromX), - cvrtY.screenToReal(screenFromY)); + activeSel_->refSelection().from = CurvePoint{cvrtX.screenToReal(screenFromX), + cvrtY.screenToReal(screenFromY)}; - activeSel_->refSelection().to = CurvePoint(cvrtX.screenToReal(screenToX), - cvrtY.screenToReal(screenToY)); + activeSel_->refSelection().to = CurvePoint{cvrtX.screenToReal(screenToX), + cvrtY.screenToReal(screenToY)}; } //#################### begin drawing #################### //1. draw colored area under curves for (auto it = curves_.begin(); it != curves_.end(); ++it) if (it->second.fillMode != CurveAttributes::FILL_NONE) - { - const std::vector<wxPoint>& points = drawPoints[it - curves_.begin()]; - if (points.size() >= 3) + if (const std::vector<wxPoint>& points = drawPoints[it - curves_.begin()]; + points.size() >= 3) { - wxDCBrushChanger dummy(dc, it->second.fillColor); - wxDCPenChanger dummy2(dc, wxPen(it->second.fillColor, fastFromDIP(1))); + //wxDC::DrawPolygon() draws *transparent* border if wxTRANSPARENT_PEN is used! + //unlike wxDC::DrawRectangle() which just widens inner area! + wxDCPenChanger dummy (dc, wxPen(it->second.fillColor, 1 /*[!] width*/)); + wxDCBrushChanger dummy2(dc, it->second.fillColor); dc.DrawPolygon(static_cast<int>(points.size()), &points[0]); } - } //2. draw all currently set mouse selections (including active selection) std::vector<SelectionBlock> allSelections = oldSel_; diff --git a/wx+/graph.h b/wx+/graph.h index f1f0c76c..a6e99200 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -19,26 +19,19 @@ //elegant 2D graph as wxPanel specialization namespace zen { -/* -Example: - //init graph (optional) +/* //init graph (optional) m_panelGraph->setAttributes(Graph2D::MainAttributes(). setLabelX(Graph2D::LABEL_X_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()). setLabelY(Graph2D::LABEL_Y_RIGHT, 60, std::make_shared<LabelFormatterBytes>())); //set graph data std::shared_ptr<CurveData> 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() {} - CurvePoint(double xVal, double yVal) : x(xVal), y(yVal) {} 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 @@ -84,7 +77,7 @@ private: const size_t sz = getSize(); const size_t pos = std::min<ptrdiff_t>(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty! if (pos < sz) - return CurvePoint(pos, getValue(pos)); + return CurvePoint{1.0 * pos, getValue(pos)}; return {}; } @@ -92,7 +85,7 @@ private: { const size_t pos = std::max<ptrdiff_t>(std::ceil(x), 0); //[!] use std::max with signed type! if (pos < getSize()) - return CurvePoint(pos, getValue(pos)); + return CurvePoint{1.0 * pos, getValue(pos)}; return {}; } }; @@ -131,12 +124,11 @@ struct DecimalNumberFormatter : public LabelFormatter }; //------------------------------------------------------------------------------------------------------------ +//example: wnd.Bind(EVENT_GRAPH_SELECTION, [this](GraphSelectEvent& event) { onGraphSelect(event); }); -//emit data selection event -//Usage: wnd.Connect(wxEVT_GRAPH_SELECTION, GraphSelectEventHandler(MyDlg::OnGraphSelection), nullptr, this); -// void MyDlg::OnGraphSelection(GraphSelectEvent& event); +struct GraphSelectEvent; +wxDECLARE_EVENT(EVENT_GRAPH_SELECTION, GraphSelectEvent); -extern const wxEventType wxEVT_GRAPH_SELECTION; struct SelectionBlock { @@ -144,23 +136,13 @@ struct SelectionBlock CurvePoint to; }; -class GraphSelectEvent : public wxCommandEvent +struct GraphSelectEvent : public wxEvent { -public: - GraphSelectEvent(const SelectionBlock& selBlock) : wxCommandEvent(wxEVT_GRAPH_SELECTION), selBlock_(selBlock) {} - wxEvent* Clone() const override { return new GraphSelectEvent(selBlock_); } - - SelectionBlock getSelection() { return selBlock_; } + explicit GraphSelectEvent(const SelectionBlock& selBlock) : wxEvent(0 /*winid*/, EVENT_GRAPH_SELECTION), selectBlock_(selBlock) {} + GraphSelectEvent* Clone() const override { return new GraphSelectEvent(*this); } -private: - SelectionBlock selBlock_; + SelectionBlock selectBlock_; }; - -using GraphSelectEventFunction = void (wxEvtHandler::*)(GraphSelectEvent&); - -#define GraphSelectEventHandler(func) \ - (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GraphSelectEventFunction, &func) - //------------------------------------------------------------------------------------------------------------ class Graph2D : public wxPanel @@ -171,7 +153,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL | wxNO_BORDER, - const wxString& name = wxPanelNameStr); + const wxString& name = wxASCII_STR(wxPanelNameStr)); class CurveAttributes { @@ -288,6 +270,8 @@ public: std::map<PosCorner, wxString> cornerTexts; + //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 wxColor colorText = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); wxColor colorBack = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); @@ -308,13 +292,12 @@ public: void clearSelection() { oldSel_.clear(); Refresh(); } private: - void OnMouseLeftDown(wxMouseEvent& event); - void OnMouseMovement(wxMouseEvent& event); - void OnMouseLeftUp (wxMouseEvent& event); - void OnMouseCaptureLost(wxMouseCaptureLostEvent& event); + void onMouseLeftDown(wxMouseEvent& event); + void onMouseMovement(wxMouseEvent& event); + void onMouseLeftUp (wxMouseEvent& event); + void onMouseCaptureLost(wxMouseCaptureLostEvent& event); void onPaintEvent(wxPaintEvent& event); - void onSizeEvent(wxSizeEvent& event) { Refresh(); event.Skip(); } void render(wxDC& dc) const; diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 3f1599f3..80c9aaf1 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -55,8 +55,7 @@ const int COLUMN_MOVE_MARKER_WIDTH_DIP = 3; const bool fillGapAfterColumns = true; //draw rows/column label to fill full window width; may become an instance variable some time? -/* -IsEnabled() vs IsThisEnabled() since wxWidgets 2.9.5: +/* IsEnabled() vs IsThisEnabled() since wxWidgets 2.9.5: void wxWindowBase::NotifyWindowOnEnableChange(), called from bool wxWindowBase::Enable(), fails to refresh child elements when disabling a IsTopLevel() dialog, e.g. when showing a modal dialog. @@ -73,8 +72,7 @@ The perfect solution would be a bool renderAsEnabled() { return "IsEnabled() but However "IsThisEnabled()" is good enough (same like the old IsEnabled() on wxWidgets 2.8.12) and it avoids this pathetic behavior on XP. (Similar problem on Win 7: e.g. directly click sync button without comparing first) -=> 2018-07-30: roll our own: -*/ +=> 2018-07-30: roll our own: */ bool renderAsEnabled(wxWindow& win) { if (win.IsTopLevel()) @@ -88,25 +86,39 @@ bool renderAsEnabled(wxWindow& win) } //---------------------------------------------------------------------------------------------------------------- -const wxEventType zen::EVENT_GRID_MOUSE_LEFT_DOUBLE = wxNewEventType(); -const wxEventType zen::EVENT_GRID_MOUSE_LEFT_DOWN = wxNewEventType(); -const wxEventType zen::EVENT_GRID_MOUSE_LEFT_UP = wxNewEventType(); -const wxEventType zen::EVENT_GRID_MOUSE_RIGHT_DOWN = wxNewEventType(); -const wxEventType zen::EVENT_GRID_MOUSE_RIGHT_UP = wxNewEventType(); -const wxEventType zen::EVENT_GRID_SELECT_RANGE = wxNewEventType(); -const wxEventType zen::EVENT_GRID_COL_LABEL_MOUSE_LEFT = wxNewEventType(); -const wxEventType zen::EVENT_GRID_COL_LABEL_MOUSE_RIGHT = wxNewEventType(); -const wxEventType zen::EVENT_GRID_COL_RESIZE = wxNewEventType(); +namespace zen +{ +wxDEFINE_EVENT(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEvent); +wxDEFINE_EVENT(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEvent); +wxDEFINE_EVENT(EVENT_GRID_MOUSE_RIGHT_DOWN, GridClickEvent); +wxDEFINE_EVENT(EVENT_GRID_SELECT_RANGE, GridSelectEvent); +wxDEFINE_EVENT(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEvent); +wxDEFINE_EVENT(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEvent); +wxDEFINE_EVENT(EVENT_GRID_COL_RESIZE, GridColumnResizeEvent); +wxDEFINE_EVENT(EVENT_GRID_CONTEXT_MENU, GridContextMenuEvent); +} //---------------------------------------------------------------------------------------------------------------- void GridData::renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) { - drawCellBackground(dc, rect, enabled, selected, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + if (enabled) + { + if (selected) + dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST); + else + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + } + else + clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); } void GridData::renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected, HoverArea rowHover) { + wxDCTextColourChanger textColor(dc); + if (enabled && selected) //accessibility: always set *both* foreground AND background colors! + textColor.Set(*wxBLACK); + wxRect rectTmp = drawCellBorder(dc, rect); rectTmp.x += getColumnGapLeft(); @@ -131,20 +143,6 @@ wxRect GridData::drawCellBorder(wxDC& dc, const wxRect& rect) //returns remainin } -void GridData::drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor) -{ - if (enabled) - { - if (selected) - dc.GradientFillLinear(rect, getColorSelectionGradientFrom(), getColorSelectionGradientTo(), wxEAST); - else - clearArea(dc, rect, backgroundColor); - } - else - clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); -} - - void GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment, const wxSize* textExtentHint) { /* Performance Notes (Windows): @@ -270,37 +268,37 @@ class Grid::SubWindow : public wxWindow { public: SubWindow(Grid& parent) : - wxWindow(&parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBORDER_NONE, wxPanelNameStr), + wxWindow(&parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBORDER_NONE, wxASCII_STR(wxPanelNameStr)), parent_(parent) { - Connect(wxEVT_PAINT, wxPaintEventHandler(SubWindow::onPaintEvent), nullptr, this); - Connect(wxEVT_SIZE, wxSizeEventHandler (SubWindow::onSizeEvent), nullptr, this); + Bind(wxEVT_PAINT, [this](wxPaintEvent& event) { onPaintEvent(event); }); + Bind(wxEVT_SIZE, [this](wxSizeEvent& event) { Refresh(); event.Skip(); }); Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) {}); //https://wiki.wxwidgets.org/Flicker-Free_Drawing //SetDoubleBuffered(true); slow as hell! SetBackgroundStyle(wxBG_STYLE_PAINT); - Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(SubWindow::onFocus), nullptr, this); - Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(SubWindow::onFocus), nullptr, this); - Connect(wxEVT_CHILD_FOCUS, wxEventHandler(SubWindow::onChildFocus), nullptr, this); + Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) { onFocus(event); }); + Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& event) { onFocus(event); }); + Bind(wxEVT_CHILD_FOCUS, [](wxChildFocusEvent& event) {}); //wxGTK::wxScrolledWindow automatically scrolls to child window when child gets focus -> prevent! - Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(SubWindow::onMouseLeftDown ), nullptr, this); - Connect(wxEVT_LEFT_UP, wxMouseEventHandler(SubWindow::onMouseLeftUp ), nullptr, this); - Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler(SubWindow::onMouseLeftDouble), nullptr, this); - Connect(wxEVT_RIGHT_DOWN, wxMouseEventHandler(SubWindow::onMouseRightDown ), nullptr, this); - Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(SubWindow::onMouseRightUp ), nullptr, this); - Connect(wxEVT_MOTION, wxMouseEventHandler(SubWindow::onMouseMovement ), nullptr, this); - Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(SubWindow::onLeaveWindow ), nullptr, this); - Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler(SubWindow::onMouseWheel ), nullptr, this); - Connect(wxEVT_MOUSE_CAPTURE_LOST, wxMouseCaptureLostEventHandler(SubWindow::onMouseCaptureLost), nullptr, this); + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) { onMouseLeftDown (event); }); + Bind(wxEVT_LEFT_UP, [this](wxMouseEvent& event) { onMouseLeftUp (event); }); + Bind(wxEVT_LEFT_DCLICK, [this](wxMouseEvent& event) { onMouseLeftDouble(event); }); + Bind(wxEVT_RIGHT_DOWN, [this](wxMouseEvent& event) { onMouseRightDown (event); }); + Bind(wxEVT_RIGHT_UP, [this](wxMouseEvent& event) { onMouseRightUp (event); }); + Bind(wxEVT_MOTION, [this](wxMouseEvent& event) { onMouseMovement (event); }); + Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { onLeaveWindow (event); }); + Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent& event) { onMouseWheel (event); }); + Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent& event) { onMouseCaptureLost(event); }); - Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(SubWindow::onKeyDown), nullptr, this); - Connect(wxEVT_KEY_UP, wxKeyEventHandler(SubWindow::onKeyUp ), nullptr, this); + Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) { onKeyDown(event); }); + Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { onKeyUp (event); }); assert(GetClientAreaOrigin() == wxPoint()); //generally assumed when dealing with coordinates below } - Grid& refParent() { return parent_; } + Grid& refParent() { return parent_; } const Grid& refParent() const { return parent_; } template <class T> @@ -337,7 +335,6 @@ private: virtual void render(wxDC& dc, const wxRect& rect) = 0; virtual void onFocus(wxFocusEvent& event) { event.Skip(); } - virtual void onChildFocus(wxEvent& event) {} //wxGTK::wxScrolledWindow automatically scrolls to child window when child gets focus -> prevent! virtual void onMouseLeftDown (wxMouseEvent& event) { event.Skip(); } virtual void onMouseLeftUp (wxMouseEvent& event) { event.Skip(); } @@ -362,19 +359,30 @@ private: void onMouseWheel(wxMouseEvent& event) { - /* - MSDN, WM_MOUSEWHEEL: "Sent to the focus window when the mouse wheel is rotated. - The DefWindowProc function propagates the message to the window's parent. - There should be no internal forwarding of the message, since DefWindowProc propagates - it up the parent chain until it finds a window that processes it." + /* MSDN, WM_MOUSEWHEEL: "Sent to the focus window when the mouse wheel is rotated. + The DefWindowProc function propagates the message to the window's parent. + There should be no internal forwarding of the message, since DefWindowProc propagates + it up the parent chain until it finds a window that processes it." - On OS X there is no such propagation! => we need a redirection (the same wxGrid implements) - */ + On OS X there is no such propagation! => we need a redirection (the same wxGrid implements) - //new wxWidgets 3.0 screw-up for GTK2: wxScrollHelperEvtHandler::ProcessEvent() ignores wxEVT_MOUSEWHEEL events - //thereby breaking the scenario of redirection to parent we need here (but also breaking their very own wxGrid sample) - //=> call wxScrolledWindow mouse wheel handler directly - parent_.HandleOnMouseWheel(event); + new wxWidgets 3.0 screw-up for GTK2: wxScrollHelperEvtHandler::ProcessEvent() ignores wxEVT_MOUSEWHEEL events + thereby breaking the scenario of redirection to parent we need here (but also breaking their very own wxGrid sample) + => call wxScrolledWindow mouse wheel handler directly */ + + //wxWidgets never ceases to amaze: multi-line scrolling is implemented maximally inefficient by repeating wxEVT_SCROLLWIN_LINEUP!! => WTF! + if (event.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL && //=> reimplement wxScrollHelperBase::HandleOnMouseWheel() in a non-retarded way + !event.IsPageScroll()) + { + mouseRotateRemainder_ += -event.GetWheelRotation(); + const int rotations = mouseRotateRemainder_ / event.GetWheelDelta(); + mouseRotateRemainder_ -= rotations * event.GetWheelDelta(); + + const int rowsDelta = rotations * event.GetLinesPerAction(); + parent_.scrollDelta(0, rowsDelta); + } + else + parent_.HandleOnMouseWheel(event); //if (!sendEventToParent(event)) // event.Skip(); @@ -392,14 +400,9 @@ private: render(dc, it.GetRect()); } - void onSizeEvent(wxSizeEvent& event) - { - Refresh(); - event.Skip(); - } - Grid& parent_; std::optional<wxBitmap> doubleBuffer_; + int mouseRotateRemainder_ = 0; }; //---------------------------------------------------------------------------------------------------------------- @@ -432,7 +435,7 @@ private: dc.DrawLine(clientRect.GetBottomLeft(), clientRect.GetBottomRight()); wxRect rectShrinked = clientRect; - rectShrinked.Deflate(1); + rectShrinked.Deflate(fastFromDIP(1)); dc.SetPen(wxPen(*wxWHITE, fastFromDIP(1))); //dc.DrawLine(clientRect.GetTopLeft(), clientRect.GetTopRight() + wxPoint(1, 0)); @@ -528,7 +531,7 @@ private: //label text wxRect textRect = rect; - textRect.Deflate(1); + textRect.Deflate(fastFromDIP(1)); GridData::drawCellText(dc, textRect, formatRow(row), wxALIGN_CENTRE); @@ -657,7 +660,7 @@ private: const int clientWidth = GetClientSize().GetWidth(); //need reliable, stable width in contrast to rect.width if (totalWidth < clientWidth) - drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(clientWidth - totalWidth, colLabelHeight)), absWidths.size(), ColumnType::NONE, enabled); + drawColumnLabel(dc, wxRect(labelAreaTL, wxSize(clientWidth - totalWidth, colLabelHeight)), absWidths.size(), ColumnType::none, enabled); } } @@ -758,7 +761,7 @@ private: const int bestWidth = refParent().getBestColumnSize(action->col); //return -1 on error if (bestWidth >= 0) { - refParent().setColumnWidth(bestWidth, action->col, GridEventPolicy::ALLOW); + refParent().setColumnWidth(bestWidth, action->col, GridEventPolicy::allow); refParent().Refresh(); //refresh main grid as well! } } @@ -773,12 +776,12 @@ private: const int newWidth = activeResizing_->getStartWidth() + event.GetPosition().x - activeResizing_->getStartPosX(); //set width tentatively - refParent().setColumnWidth(newWidth, col, GridEventPolicy::ALLOW); + refParent().setColumnWidth(newWidth, col, GridEventPolicy::allow); //check if there's a small gap after last column, if yes, fill it const int gapWidth = GetClientSize().GetWidth() - refParent().getColWidthsSum(GetClientSize().GetWidth()); if (std::abs(gapWidth) < fastFromDIP(COLUMN_FILL_GAP_TOLERANCE_DIP)) - refParent().setColumnWidth(newWidth + gapWidth, col, GridEventPolicy::ALLOW); + refParent().setColumnWidth(newWidth + gapWidth, col, GridEventPolicy::allow); refParent().Refresh(); //refresh columns on main grid as well! } @@ -815,8 +818,8 @@ private: const std::wstring toolTip = [&] { const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - const ColumnType colType = refParent().getColumnAtPos(absPos.x).colType; //returns ColumnType::NONE if no column at x position! - if (colType != ColumnType::NONE) + const ColumnType colType = refParent().getColumnAtPos(absPos.x).colType; //returns ColumnType::none if no column at x position! + if (colType != ColumnType::none) if (auto prov = refParent().getDataProvider()) return prov->getToolTip(colType); return std::wstring(); @@ -846,7 +849,7 @@ private: else //notify right click (on free space after last column) if (fillGapAfterColumns) - sendEventToParent(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, ColumnType::NONE)); + sendEventToParent(GridLabelClickEvent(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, ColumnType::none)); event.Skip(); } @@ -859,7 +862,7 @@ private: //---------------------------------------------------------------------------------------------------------------- namespace { -const wxEventType EVENT_GRID_HAS_SCROLLED = wxNewEventType(); //internal to Grid::MainWin::ScrollWindow() +wxDEFINE_EVENT(EVENT_GRID_HAS_SCROLLED, wxCommandEvent); } //---------------------------------------------------------------------------------------------------------------- @@ -872,7 +875,7 @@ public: rowLabelWin_(rowLabelWin), colLabelWin_(colLabelWin) { - Connect(EVENT_GRID_HAS_SCROLLED, wxEventHandler(MainWin::onRequestWindowUpdate), nullptr, this); + Bind(EVENT_GRID_HAS_SCROLLED, [this](wxCommandEvent& event) { onRequestWindowUpdate(event); }); } ~MainWin() { assert(!gridUpdatePending_); } @@ -951,7 +954,7 @@ private: } else if (highlight_.row == row) return highlight_.rowHover; - return HoverArea::NONE; + return HoverArea::none; } bool drawAsSelected(size_t row) const @@ -976,10 +979,14 @@ private: { if (auto prov = refParent().getDataProvider()) { + wxClientDC dc(this); + dc.SetFont(GetFont()); + + const ptrdiff_t rowCount = refParent().getRowCount(); const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position! - const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth); + const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::none if no column at x position! + const HoverArea rowHover = 0 <= row && row < rowCount ? prov->getRowMouseHover(dc, row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth) : HoverArea::none; const wxPoint mousePos = GetPosition() + event.GetPosition(); //client is interested in all double-clicks, even those outside of the grid! @@ -992,40 +999,58 @@ private: { if (auto prov = refParent().getDataProvider()) { + onMouseMovement(event); //update highlight in obscure cases (e.g. right-click while context menu is open) + + wxClientDC dc(this); + dc.SetFont(GetFont()); + + const ptrdiff_t rowCount = refParent().getRowCount(); const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position! - const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth); + const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::none if no column at x position! + const HoverArea rowHover = 0 <= row && row < rowCount ? prov->getRowMouseHover(dc, row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth) : HoverArea::none; const wxPoint mousePos = GetPosition() + event.GetPosition(); - //row < 0 possible!!! Pressing "Menu Key" simulates mouse-right-button down + up at position 0xffff/0xffff! + + assert(row >= 0); + //row < 0 was possible in older wxWidgets: https://github.com/wxWidgets/wxWidgets/commit/2c69d27c0d225d3a331c773da466686153185320#diff-9f11c8f2cb1f734f7c0c1071aba491a5 + //=> pressing "Menu Key" simulated mouse-right-button down + up at position 0xffff/0xffff! GridClickEvent mouseEvent(event.RightDown() ? EVENT_GRID_MOUSE_RIGHT_DOWN : EVENT_GRID_MOUSE_LEFT_DOWN, row, rowHover, mousePos); - if (!sendEventToParent(mouseEvent)) //allow client to swallow event! + + freezeMouseHighlight_ = true; //e.g. while showing context menu + const bool processed = sendEventToParent(mouseEvent); //allow client to swallow event! + freezeMouseHighlight_ = false; + + if (!processed) { if (wxWindow::FindFocus() != this) //doesn't seem to happen automatically for right mouse button SetFocus(); - if (row >= 0) - if (!event.RightDown() || !refParent().isSelected(row)) //do NOT start a new selection if user right-clicks on a selected area! + if (event.RightDown() && (row < 0 || refParent().isSelected(row))) //=> open context menu *immediately* and do *not* start a new selection + sendEventToParent(GridContextMenuEvent(mousePos)); + else if (row >= 0) + { + if (event.ControlDown()) + activeSelection_ = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row) /*positive*/, false /*gridWasCleared*/, mouseEvent); + else if (event.ShiftDown()) { - if (event.ControlDown()) - activeSelection_ = std::make_unique<MouseSelection>(*this, row, !refParent().isSelected(row) /*positive*/, false /*gridWasCleared*/, mouseEvent); - else if (event.ShiftDown()) - { - refParent().clearSelection(GridEventPolicy::DENY); - activeSelection_ = std::make_unique<MouseSelection>(*this, selectionAnchor_, true /*positive*/, true /*gridWasCleared*/, mouseEvent); - } - else - { - refParent().clearSelection(GridEventPolicy::DENY); - activeSelection_ = std::make_unique<MouseSelection>(*this, row, true /*positive*/, true /*gridWasCleared*/, mouseEvent); - //DO NOT emit range event for clearing selection! would be inconsistent with keyboard handling (moving cursor neither emits range event) - //and is also harmful when range event is considered a final action - //e.g. cfg grid would prematurely show a modal dialog after changed config - } + refParent().clearSelection(GridEventPolicy::deny); + activeSelection_ = std::make_unique<MouseSelection>(*this, selectionAnchor_, true /*positive*/, true /*gridWasCleared*/, mouseEvent); } - Refresh(); + else + { + refParent().clearSelection(GridEventPolicy::deny); + activeSelection_ = std::make_unique<MouseSelection>(*this, row, true /*positive*/, true /*gridWasCleared*/, mouseEvent); + //DO NOT emit range event for clearing selection! would be inconsistent with keyboard handling (moving cursor neither emits range event) + //and is also harmful when range event is considered a final action + //e.g. cfg grid would prematurely show a modal dialog after changed config + } + } } + + //update mouse highlight (in case it was frozen above) + event.SetPosition(ScreenToClient(wxGetMousePosition())); //mouse position may have changed within above callbacks (e.g. context menu was shown)! + onMouseMovement(event); } event.Skip(); //allow changing focus } @@ -1056,29 +1081,39 @@ private: const ptrdiff_t rowTo = activeSelection_->getCurrentRow(); const bool positive = activeSelection_->isPositiveSelect(); const GridClickEvent mouseClick = activeSelection_->getFirstClick(); + assert((mouseClick.GetEventType() == EVENT_GRID_MOUSE_RIGHT_DOWN) == event.RightUp()); activeSelection_.reset(); //release mouse capture *before* sending the event (which might show a modal popup dialog requiring the mouse!!!) - refParent().selectRange(rowFrom, rowTo, positive, &mouseClick, GridEventPolicy::ALLOW); - } + refParent().selectRange(rowFrom, rowTo, positive, &mouseClick, GridEventPolicy::allow); - if (auto prov = refParent().getDataProvider()) - { - //this one may point to row which is not in visible area! - const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); - const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position! - const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth); - const wxPoint mousePos = GetPosition() + event.GetPosition(); - //notify click event after the range selection! e.g. this makes sure the selection is applied before showing a context menu - sendEventToParent(GridClickEvent(event.RightUp() ? EVENT_GRID_MOUSE_RIGHT_UP : EVENT_GRID_MOUSE_LEFT_UP, row, rowHover, mousePos)); + if (mouseClick.GetEventType() == EVENT_GRID_MOUSE_RIGHT_DOWN) + sendEventToParent(GridContextMenuEvent(mouseClick.mousePos_)); } - //update highlight_ and tooltip: on OS X no mouse movement event is generated after a mouse button click (unlike on Windows) +#if 0 + if (!event.RightUp()) + if (auto prov = refParent().getDataProvider()) + { + wxClientDC dc(this); + dc.SetFont(GetFont()); + + //this one may point to row which is not in visible area! + const ptrdiff_t rowCount = refParent().getRowCount(); + const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); + const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range + const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::none if no column at x position! + const HoverArea rowHover = 0 <= row && row < rowCount ? prov->getRowMouseHover(dc, row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth) : HoverArea::none; + const wxPoint mousePos = GetPosition() + event.GetPosition(); + //notify click event after the range selection! e.g. this makes sure the selection is applied before showing a context menu + sendEventToParent(GridClickEvent(EVENT_GRID_MOUSE_LEFT_UP, row, rowHover, mousePos)); + } +#endif + + //update mouse highlight and tooltip: macOS no mouse movement event is generated after a mouse button click (unlike on Windows) event.SetPosition(ScreenToClient(wxGetMousePosition())); //mouse position may have changed within above callbacks (e.g. context menu was shown)! onMouseMovement(event); - Refresh(); event.Skip(); //allow changing focus } @@ -1087,12 +1122,12 @@ private: if (activeSelection_) { if (activeSelection_->gridWasCleared()) - refParent().clearSelection(GridEventPolicy::ALLOW); //see onMouseDown(); selection is "completed" => emit GridSelectEvent + refParent().clearSelection(GridEventPolicy::allow); //see onMouseDown(); selection is "completed" => emit GridSelectEvent activeSelection_.reset(); + Refresh(); } - highlight_.row = -1; - Refresh(); + updateMouseHover({-1, HoverArea::none}); //event.Skip(); -> we DID handle it! } @@ -1100,15 +1135,17 @@ private: { if (auto prov = refParent().getDataProvider()) { + wxClientDC dc(this); + dc.SetFont(GetFont()); + const ptrdiff_t rowCount = refParent().getRowCount(); const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); const ptrdiff_t row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::NONE if no column at x position! - const HoverArea rowHover = prov->getRowMouseHover(row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth); + const ColumnPosInfo cpi = refParent().getColumnAtPos(absPos.x); //returns ColumnType::none if no column at x position! const std::wstring toolTip = [&] { - if (cpi.colType != ColumnType::NONE && 0 <= row && row < rowCount) + if (cpi.colType != ColumnType::none && 0 <= row && row < rowCount) return prov->getToolTip(row, cpi.colType); return std::wstring(); }(); @@ -1118,22 +1155,17 @@ private: activeSelection_->evalMousePos(); //call on both mouse movement + timer event! else { - refreshHighlight(highlight_); - highlight_.row = row; - highlight_.rowHover = rowHover; - refreshHighlight(highlight_); //multiple Refresh() calls are condensed into single one! + const HoverArea rowHover = 0 <= row && row < rowCount ? prov->getRowMouseHover(dc, row, cpi.colType, cpi.cellRelativePosX, cpi.colWidth) : HoverArea::none; + updateMouseHover({row, rowHover}); } } event.Skip(); } - void onLeaveWindow(wxMouseEvent& event) override //wxEVT_LEAVE_WINDOW does not respect mouse capture! + void onLeaveWindow(wxMouseEvent& event) override { - if (!activeSelection_) - { - refreshHighlight(highlight_); - highlight_.row = -1; - } + if (!activeSelection_) //wxEVT_LEAVE_WINDOW does not respect mouse capture! + updateMouseHover({-1, HoverArea::none}); event.Skip(); } @@ -1148,9 +1180,10 @@ private: wnd_(wnd), rowStart_(rowStart), rowCurrent_(rowStart), positiveSelect_(positive), gridWasCleared_(gridWasCleared), firstClick_(firstClick) { wnd_.CaptureMouse(); - timer_.Connect(wxEVT_TIMER, wxEventHandler(MouseSelection::onTimer), nullptr, this); + timer_.Bind(wxEVT_TIMER, [this](wxTimerEvent& event) { evalMousePos(); }); timer_.Start(100); //timer interval in ms evalMousePos(); + wnd_.Refresh(); } ~MouseSelection() { if (wnd_.HasCapture()) wnd_.ReleaseMouse(); } @@ -1179,6 +1212,7 @@ private: int pixelsPerUnitY = 0; wnd_.refParent().GetScrollPixelsPerUnit(nullptr, &pixelsPerUnitY); + assert(pixelsPerUnitY > 0); if (pixelsPerUnitY <= 0) return; @@ -1211,6 +1245,7 @@ private: const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed); const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range + assert(newRow >= 0); if (newRow >= 0) if (rowCurrent_ != newRow) { @@ -1220,8 +1255,6 @@ private: } private: - void onTimer(wxEvent& event) { evalMousePos(); } - MainWin& wnd_; const size_t rowStart_; ptrdiff_t rowCurrent_; @@ -1237,7 +1270,9 @@ private: struct MouseHighlight { ptrdiff_t row = -1; - HoverArea rowHover = HoverArea::NONE; + HoverArea rowHover = HoverArea::none; + + bool operator==(const MouseHighlight&) const = default; }; void ScrollWindow(int dx, int dy, const wxRect* rect) override @@ -1266,7 +1301,7 @@ private: assert(gridUpdatePending_); ZEN_ON_SCOPE_EXIT(gridUpdatePending_ = false); - refParent().updateWindowSizes(false); //row label width has changed -> do *not* update scrollbars: recursion on wxGTK! -> still a problem, now that we're called async?? + refParent().updateWindowSizes(false); //row label width has changed -> do *not* update scrollbars: recursion on wxGTK! -> still a problem, now that this function is called async?? rowLabelWin_.Update(); //update while dragging scroll thumb } @@ -1278,18 +1313,27 @@ private: RefreshRect(cellArea, false); } - void refreshHighlight(const MouseHighlight& hl) + void updateMouseHover(const MouseHighlight& hl) { - const ptrdiff_t rowCount = refParent().getRowCount(); - if (0 <= hl.row && hl.row < rowCount && hl.rowHover != HoverArea::NONE) //no highlight_? => NOP! - refreshRow(hl.row); + if (highlight_ != hl && !freezeMouseHighlight_) + { + const ptrdiff_t rowCount = refParent().getRowCount(); + if (0 <= highlight_.row && highlight_.row < rowCount && highlight_.rowHover != HoverArea::none) //no highlight_? => NOP! + refreshRow(highlight_.row); + + highlight_ = hl; + + if (0 <= highlight_.row && highlight_.row < rowCount && highlight_.rowHover != HoverArea::none) //no highlight_? => NOP! + refreshRow(highlight_.row); + } } RowLabelWin& rowLabelWin_; ColLabelWin& colLabelWin_; std::unique_ptr<MouseSelection> activeSelection_; //bound while user is selecting with mouse - MouseHighlight highlight_; //current mouse highlight_ (superseded by activeSelection_ if available) + MouseHighlight highlight_; //current mouse highlight + bool freezeMouseHighlight_ = false; ptrdiff_t cursorRow_ = 0; size_t selectionAnchor_ = 0; @@ -1326,12 +1370,11 @@ Grid::Grid(wxWindow* parent, assert(GetClientSize() == GetSize() && GetWindowBorderSize() == wxSize()); //borders are NOT allowed for Grid //reason: updateWindowSizes() wants to use "GetSize()" as a "GetClientSize()" including scrollbars - Connect(wxEVT_PAINT, wxPaintEventHandler(Grid::onPaintEvent), nullptr, this); - Connect(wxEVT_SIZE, wxSizeEventHandler (Grid::onSizeEvent ), nullptr, this); + Bind(wxEVT_PAINT, [this](wxPaintEvent& event) { wxPaintDC dc(this); }); + Bind(wxEVT_SIZE, [this](wxSizeEvent& event) { updateWindowSizes(); event.Skip(); }); Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) {}); //https://wiki.wxwidgets.org/Flicker-Free_Drawing - Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(Grid::onKeyDown), nullptr, this); - Connect(wxEVT_KEY_UP, wxKeyEventHandler(Grid::onKeyUp ), nullptr, this); + Bind(wxEVT_KEY_DOWN, [this](wxKeyEvent& event) { onKeyDown(event); }); } @@ -1522,12 +1565,12 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size) //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 + 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 @@ -1550,35 +1593,6 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size) } -void Grid::onPaintEvent(wxPaintEvent& event) { wxPaintDC dc(this); } - - -void Grid::onKeyUp(wxKeyEvent& event) -{ - int keyCode = event.GetKeyCode(); - if (event.ShiftDown() && keyCode == WXK_F10) //== alias for menu key - keyCode = WXK_WINDOWS_MENU; - - switch (keyCode) - { - case WXK_MENU: // - case WXK_WINDOWS_MENU: //simulate right mouse click at cursor(+1) position - { - const int cursorNextPosY = rowLabelWin_->getRowHeight() * std::min(getRowCount(), mainWin_->getCursor() + 1); - const int clientPosMainWinY = std::clamp(CalcScrolledPosition(wxPoint(0, cursorNextPosY)).y, //absolute -> client coordinates - 0, mainWin_->GetClientSize().GetHeight() - 1); - const wxPoint mousePos = mainWin_->GetPosition() + wxPoint(0, clientPosMainWinY); //mainWin_-relative to Grid-relative - - GridClickEvent clickEvent(EVENT_GRID_MOUSE_RIGHT_UP, -1, HoverArea::NONE, mousePos); - if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(clickEvent); - } - return; - } - event.Skip(); -} - - void Grid::onKeyDown(wxKeyEvent& event) { int keyCode = event.GetKeyCode(); @@ -1600,7 +1614,7 @@ void Grid::onKeyDown(wxKeyEvent& event) if (rowCount > 0) { row = std::clamp<ptrdiff_t>(row, 0, rowCount - 1); - setGridCursor(row, GridEventPolicy::ALLOW); + setGridCursor(row, GridEventPolicy::allow); } }; @@ -1616,16 +1630,16 @@ void Grid::onKeyDown(wxKeyEvent& event) switch (keyCode) { case WXK_MENU: // - case WXK_WINDOWS_MENU: //simulate right mouse click at cursor(+1) position + case WXK_WINDOWS_MENU: //simulate right mouse click at cursor row (+1) position { const int cursorNextPosY = rowLabelWin_->getRowHeight() * std::min(getRowCount(), mainWin_->getCursor() + 1); const int clientPosMainWinY = std::clamp(CalcScrolledPosition(wxPoint(0, cursorNextPosY)).y, //absolute -> client coordinates 0, mainWin_->GetClientSize().GetHeight() - 1); const wxPoint mousePos = mainWin_->GetPosition() + wxPoint(0, clientPosMainWinY); //mainWin_-relative to Grid-relative - GridClickEvent clickEvent(EVENT_GRID_MOUSE_RIGHT_DOWN, -1, HoverArea::NONE, mousePos); + GridContextMenuEvent contextEvent(mousePos); if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(clickEvent); + evtHandler->ProcessEvent(contextEvent); } return; @@ -1716,12 +1730,12 @@ void Grid::onKeyDown(wxKeyEvent& event) case 'A': //Ctrl + A - select all if (event.ControlDown()) - selectRange(0, rowCount, true /*positive*/, nullptr /*mouseInitiated*/, GridEventPolicy::ALLOW); + selectRange(0, rowCount, true /*positive*/, nullptr /*mouseClick*/, GridEventPolicy::allow); break; case WXK_NUMPAD_ADD: //CTRL + '+' - auto-size all if (event.ControlDown()) - autoSizeColumns(GridEventPolicy::ALLOW); + autoSizeColumns(GridEventPolicy::allow); return; } @@ -1743,60 +1757,41 @@ void Grid::showRowLabel(bool show) } -void Grid::selectRow(size_t row, GridEventPolicy rangeEventPolicy) -{ - selection_.selectRow(row); - mainWin_->Refresh(); - - if (rangeEventPolicy == GridEventPolicy::ALLOW) - { - GridSelectEvent selEvent(row, row + 1, true, nullptr /*mouseClick*/); - if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(selEvent); - } -} - - -void Grid::selectAllRows(GridEventPolicy rangeEventPolicy) +void Grid::selectRange(size_t rowFirst, size_t rowLast, bool positive, GridEventPolicy rangeEventPolicy) { - selection_.selectAll(); + selection_.selectRange(rowFirst, rowLast, positive); mainWin_->Refresh(); - if (rangeEventPolicy == GridEventPolicy::ALLOW) + if (rangeEventPolicy == GridEventPolicy::allow) { - GridSelectEvent selEvent(0, getRowCount(), true /*positive*/, nullptr /*mouseClick*/); + GridSelectEvent selEvent(rowFirst, rowLast, positive, nullptr /*mouseClick*/); if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(selEvent); + /*bool processed = */evtHandler->ProcessEvent(selEvent); } } -void Grid::clearSelection(GridEventPolicy rangeEventPolicy) -{ - selection_.clear(); - mainWin_->Refresh(); - - if (rangeEventPolicy == GridEventPolicy::ALLOW) - { - GridSelectEvent unselectionEvent(0, getRowCount(), false /*positive*/, nullptr /*mouseClick*/); - if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(unselectionEvent); - } -} +void Grid::selectRow(size_t row, GridEventPolicy rangeEventPolicy) { selectRange(row, row + 1, true /*positive*/, rangeEventPolicy); } +void Grid::selectAllRows (GridEventPolicy rangeEventPolicy) { selectRange(0, selection_.gridSize(), true /*positive*/, rangeEventPolicy); } +void Grid::clearSelection (GridEventPolicy rangeEventPolicy) { selectRange(0, selection_.gridSize(), false /*positive*/, rangeEventPolicy); } void Grid::scrollDelta(int deltaX, int deltaY) { - wxPoint scrollPos = GetViewStart(); + const wxPoint scrollPosOld = GetViewStart(); - scrollPos.x += deltaX; - scrollPos.y += deltaY; + wxPoint scrollPosNew = scrollPosOld; + scrollPosNew.x += deltaX; + scrollPosNew.y += deltaY; - scrollPos.x = std::max(0, scrollPos.x); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"! - scrollPos.y = std::max(0, scrollPos.y); // + scrollPosNew.x = std::max(0, scrollPosNew.x); //wxScrollHelper::Scroll() will exit prematurely if input happens to be "-1"! + scrollPosNew.y = std::max(0, scrollPosNew.y); // - Scroll(scrollPos); //internally calls wxWindows::Update()! - updateWindowSizes(); //may show horizontal scroll bar if row column gets wider + if (scrollPosNew != scrollPosOld) + { + Scroll(scrollPosNew); //internally calls wxWindows::Update()! + updateWindowSizes(); //may show horizontal scroll bar if row column gets wider + } } @@ -1826,7 +1821,7 @@ void Grid::Refresh(bool eraseBackground, const wxRect* rect) updateWindowSizes(); } - if (selection_.maxSize() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) + if (selection_.gridSize() != rowCountNew) //clear selection only when needed (consider setSelectedRows()) selection_.init(rowCountNew); wxScrolledWindow::Refresh(eraseBackground, rect); @@ -1850,7 +1845,7 @@ void Grid::setColumnConfig(const std::vector<Grid::ColAttributes>& attr) for (const ColAttributes& ca : attr) { assert(ca.stretch >= 0); - assert(ca.type != ColumnType::NONE); + assert(ca.type != ColumnType::none); if (ca.visible) visCols.push_back({ ca.type, ca.offset, std::max(ca.stretch, 0) }); @@ -2003,7 +1998,7 @@ ColumnType Grid::colToType(size_t col) const { if (col < visibleCols_.size()) return visibleCols_[col].type; - return ColumnType::NONE; + return ColumnType::none; } @@ -2022,7 +2017,7 @@ Grid::ColumnPosInfo Grid::getColumnAtPos(int posX) const return { cw.type, posX + cw.width - accWidth, cw.width }; } } - return { ColumnType::NONE, 0, 0 }; + return { ColumnType::none, 0, 0 }; } @@ -2066,7 +2061,7 @@ void Grid::setGridCursor(size_t row, GridEventPolicy rangeEventPolicy) makeRowVisible(row); selection_.clear(); //clear selection, do NOT fire event - selectRange(row, row, true /*positive*/, nullptr /*mouseInitiated*/, rangeEventPolicy); //set new selection + fire event + selectRange(row, row, true /*positive*/, nullptr /*mouseClick*/, rangeEventPolicy); //set new selection + fire event } @@ -2078,7 +2073,7 @@ void Grid::selectWithCursor(ptrdiff_t row) //emits GridSelectEvent makeRowVisible(row); selection_.clear(); //clear selection, do NOT fire event - selectRange(anchorRow, row, true /*positive*/, nullptr /*mouseInitiated*/, GridEventPolicy::ALLOW); //set new selection + fire event + selectRange(anchorRow, row, true /*positive*/, nullptr /*mouseClick*/, GridEventPolicy::allow); //set new selection + fire event } @@ -2138,11 +2133,11 @@ void Grid::selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const selection_.selectRange(rowFirst, rowLast, positive); mainWin_->Refresh(); - if (rangeEventPolicy == GridEventPolicy::ALLOW) + if (rangeEventPolicy == GridEventPolicy::allow) { GridSelectEvent selectionEvent(rowFirst, rowLast, positive, mouseClick); if (wxEvtHandler* evtHandler = GetEventHandler()) - evtHandler->ProcessEvent(selectionEvent); + /*bool processed = */evtHandler->ProcessEvent(selectionEvent); } } @@ -2243,7 +2238,7 @@ void Grid::setColumnWidth(int width, size_t col, GridEventPolicy columnResizeEve if (visibleCols_[col2].stretch > 0) //normalize stretched columns only visibleCols_[col2].offset = std::max(visibleCols_[col2].offset, fastFromDIP(COLUMN_MIN_WIDTH_DIP) - stretchedWidths[col2]); - if (columnResizeEventPolicy == GridEventPolicy::ALLOW) + if (columnResizeEventPolicy == GridEventPolicy::allow) { GridColumnResizeEvent sizeEvent(vcRs.offset, vcRs.type); if (wxEvtHandler* evtHandler = GetEventHandler()) @@ -19,28 +19,33 @@ //a user-friendly, extensible and high-performance grid control namespace zen { -enum class ColumnType { NONE = -1 }; //user-defiend column type -enum class HoverArea { NONE = -1 }; //user-defined area for mouse selections for a given row (may span multiple columns or split a single column into multiple areas) - -//wxContextMenuEvent? => automatically generated by wxWidgets when right mouse down/up is not handled; even OS-dependent in which case event is generated -//=> inappropriate! client decides when to show context! => simulate right mouse click when WXK_WINDOWS_MENU button is pressed -//=> same behavior as earlier wxWidgets: https://github.com/wxWidgets/wxWidgets/commit/2c69d27c0d225d3a331c773da466686153185320#diff-9f11c8f2cb1f734f7c0c1071aba491a5 +enum class ColumnType { none = -1 }; //user-defiend column type +enum class HoverArea { none = -1 }; //user-defined area for mouse selections for a given row (may span multiple columns or split a single column into multiple areas) //------------------------ events ------------------------------------------------ -extern const wxEventType EVENT_GRID_MOUSE_LEFT_DOUBLE; // -extern const wxEventType EVENT_GRID_MOUSE_LEFT_DOWN; // -extern const wxEventType EVENT_GRID_MOUSE_LEFT_UP; //generates: GridClickEvent -extern const wxEventType EVENT_GRID_MOUSE_RIGHT_DOWN; // -extern const wxEventType EVENT_GRID_MOUSE_RIGHT_UP; // +//example: wnd.Bind(EVENT_GRID_COL_LABEL_LEFT_CLICK, [this](GridClickEvent& event) { onGridLeftClick(event); }); + +struct GridClickEvent; +struct GridSelectEvent; +struct GridLabelClickEvent; +struct GridColumnResizeEvent; +struct GridContextMenuEvent; + +wxDECLARE_EVENT(EVENT_GRID_MOUSE_LEFT_DOUBLE, GridClickEvent); +wxDECLARE_EVENT(EVENT_GRID_MOUSE_LEFT_DOWN, GridClickEvent); +wxDECLARE_EVENT(EVENT_GRID_MOUSE_RIGHT_DOWN, GridClickEvent); -extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridSelectEvent +wxDECLARE_EVENT(EVENT_GRID_SELECT_RANGE, GridSelectEvent); //NOTE: neither first nor second row need to match EVENT_GRID_MOUSE_LEFT_DOWN/EVENT_GRID_MOUSE_LEFT_UP: user holding SHIFT; moving out of window... -extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_LEFT; //generates: GridLabelClickEvent -extern const wxEventType EVENT_GRID_COL_LABEL_MOUSE_RIGHT; // -extern const wxEventType EVENT_GRID_COL_RESIZE; //generates: GridColumnResizeEvent +wxDECLARE_EVENT(EVENT_GRID_COL_LABEL_MOUSE_LEFT, GridLabelClickEvent); +wxDECLARE_EVENT(EVENT_GRID_COL_LABEL_MOUSE_RIGHT, GridLabelClickEvent); +wxDECLARE_EVENT(EVENT_GRID_COL_RESIZE, GridColumnResizeEvent); + +//wxContextMenuEvent? => generated by wxWidgets when right mouse down/up is not handled; even OS-dependent in which case event is generated +//=> inappropriate! we know better when to show context! +wxDECLARE_EVENT(EVENT_GRID_CONTEXT_MENU, GridContextMenuEvent); -//example: wnd.Connect(EVENT_GRID_COL_LABEL_LEFT_CLICK, GridClickEventHandler(MyDlg::OnLeftClick), nullptr, this); struct GridClickEvent : public wxEvent { @@ -49,7 +54,7 @@ struct GridClickEvent : public wxEvent GridClickEvent* Clone() const override { return new GridClickEvent(*this); } const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range - const HoverArea hoverArea_; //may be HoverArea::NONE + const HoverArea hoverArea_; //may be HoverArea::none const wxPoint mousePos_; //client coordinates }; @@ -71,7 +76,7 @@ struct GridLabelClickEvent : public wxEvent GridLabelClickEvent(wxEventType et, ColumnType colType) : wxEvent(0 /*winid*/, et), colType_(colType) {} GridLabelClickEvent* Clone() const override { return new GridLabelClickEvent(*this); } - const ColumnType colType_; //may be ColumnType::NONE + const ColumnType colType_; //may be ColumnType::none }; struct GridColumnResizeEvent : public wxEvent @@ -83,16 +88,13 @@ struct GridColumnResizeEvent : public wxEvent const int offset_; }; -using GridClickEventFunction = void (wxEvtHandler::*)(GridClickEvent&); -using GridSelectEventFunction = void (wxEvtHandler::*)(GridSelectEvent&); -using GridLabelClickEventFunction = void (wxEvtHandler::*)(GridLabelClickEvent&); -using GridColumnResizeEventFunction = void (wxEvtHandler::*)(GridColumnResizeEvent&); - -#define GridClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridClickEventFunction, &func) -#define GridSelectEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridSelectEventFunction, &func) -#define GridLabelClickEventHandler(func) (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridLabelClickEventFunction, &func) -#define GridColumnResizeEventHandler(func)(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(GridColumnResizeEventFunction, &func) +struct GridContextMenuEvent : public wxEvent +{ + explicit GridContextMenuEvent(const wxPoint& mousePos) : wxEvent(0 /*winid*/, EVENT_GRID_CONTEXT_MENU), mousePos_(mousePos) {} + GridContextMenuEvent* Clone() const override { return new GridContextMenuEvent(*this); } + const wxPoint mousePos_; //client coordinates +}; //------------------------------------------------------------------------------------------------------------ class Grid; @@ -110,8 +112,8 @@ public: virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected, HoverArea rowHover); virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType); //must correspond to renderCell()! + virtual HoverArea getRowMouseHover (wxDC& dc, size_t row, ColumnType colType, int cellRelativePosX, int cellWidth) { return HoverArea::none; } virtual std::wstring getToolTip (size_t row, ColumnType colType) const { return std::wstring(); } - virtual HoverArea getRowMouseHover (size_t row, ColumnType colType, int cellRelativePosX, int cellWidth) { return HoverArea::NONE; } //label area: virtual std::wstring getColumnLabel(ColumnType colType) const = 0; @@ -126,7 +128,6 @@ public: static void drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& text, int alignment = wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, const wxSize* textExtentHint = nullptr); //returns text extent static wxRect drawCellBorder (wxDC& dc, const wxRect& rect); //returns inner rectangle - static void drawCellBackground(wxDC& dc, const wxRect& rect, bool enabled, bool selected, const wxColor& backgroundColor); static wxRect drawColumnLabelBackground(wxDC& dc, const wxRect& rect, bool highlighted); //returns inner rectangle static void drawColumnLabelText (wxDC& dc, const wxRect& rect, const std::wstring& text, bool enabled); @@ -135,8 +136,8 @@ public: enum class GridEventPolicy { - ALLOW, - DENY + allow, + deny }; @@ -148,7 +149,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxTAB_TRAVERSAL | wxNO_BORDER, - const wxString& name = wxPanelNameStr); + const wxString& name = wxASCII_STR(wxPanelNameStr)); size_t getRowCount() const; @@ -156,7 +157,7 @@ public: struct ColAttributes { - ColumnType type = ColumnType::NONE; + ColumnType type = ColumnType::none; //first, client width is partitioned according to all available stretch factors, then "offset_" is added //universal model: a non-stretched column has stretch factor 0 with the "offset" becoming identical to final width! int offset = 0; @@ -185,9 +186,11 @@ public: void showScrollBars(ScrollBarStatus horizontal, ScrollBarStatus vertical); std::vector<size_t> getSelectedRows() const { return selection_.get(); } + void selectRow(size_t row, GridEventPolicy rangeEventPolicy); void selectAllRows (GridEventPolicy rangeEventPolicy); //turn off range selection event when calling this function in an event handler to avoid recursion! void clearSelection(GridEventPolicy rangeEventPolicy); // + void selectRange(size_t rowFirst, size_t rowLast, bool positive, GridEventPolicy rangeEventPolicy); //select [rowFirst, rowLast) void scrollDelta(int deltaX, int deltaY); //in scroll units @@ -201,7 +204,7 @@ public: struct ColumnPosInfo { - ColumnType colType = ColumnType::NONE; //ColumnType::NONE no column at x position! + ColumnType colType = ColumnType::none; //ColumnType::none no column at x position! int cellRelativePosX = 0; int colWidth = 0; }; @@ -226,10 +229,7 @@ public: //############################################################################################################ private: - void onPaintEvent(wxPaintEvent& event); - void onSizeEvent(wxSizeEvent& event) { updateWindowSizes(); event.Skip(); } void onKeyDown(wxKeyEvent& event); - void onKeyUp (wxKeyEvent& event); void updateWindowSizes(bool updateScrollbar = true); @@ -256,7 +256,7 @@ private: public: void init(size_t rowCount) { selected_.resize(rowCount); clear(); } - size_t maxSize() const { return selected_.size(); } + size_t gridSize() const { return selected_.size(); } std::vector<size_t> get() const { @@ -267,9 +267,7 @@ private: return result; } - void selectRow(size_t row) { selectRange(row, row + 1, true); } - void selectAll () { selectRange(0, selected_.size(), true); } - void clear () { selectRange(0, selected_.size(), false); } + void clear() { selectRange(0, selected_.size(), false); } bool isSelected(size_t row) const { return row < selected_.size() ? selected_[row] != 0 : false; } @@ -291,14 +289,14 @@ private: struct VisibleColumn { - ColumnType type = ColumnType::NONE; + ColumnType type = ColumnType::none; int offset = 0; int stretch = 0; //>= 0 }; struct ColumnWidth { - ColumnType type = ColumnType::NONE; + ColumnType type = ColumnType::none; int width = 0; }; std::vector<ColumnWidth> getColWidths() const; // @@ -332,7 +330,7 @@ private: void moveColumn(size_t colFrom, size_t colTo); ptrdiff_t clientPosToMoveTargetColumn(const wxPoint& pos) const; //return < 0 on error - ColumnType colToType(size_t col) const; //returns ColumnType::NONE on error + ColumnType colToType(size_t col) const; //returns ColumnType::none on error /* Grid window layout: _______________________________ diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index d09e5dfa..b89abe6d 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -128,7 +128,7 @@ private: Protected<std::vector<std::pair<std::string, ImageHolder>>> result_; using TaskType = FunctionReturnTypeT<decltype(&getScalerTask)>; - std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), "xBRZ Scaler") }; + std::optional<ThreadGroup<TaskType>> threadGroup_{ ThreadGroup<TaskType>(std::max<int>(std::thread::hardware_concurrency(), 1), Zstr("xBRZ Scaler")) }; //hardware_concurrency() == 0 if "not computable or well defined" }; @@ -182,7 +182,7 @@ ImageBuffer::ImageBuffer(const Zstring& zipPath) //throw FileError try //to load from ZIP first: { //wxFFileInputStream/wxZipInputStream loads in junks of 512 bytes => WTF!!! => implement sane file loading: - const std::string rawStream = loadBinContainer<std::string>(zipPath, nullptr /*notifyUnbufferedIO*/); //throw FileError + const std::string rawStream = getFileContent(zipPath, nullptr /*notifyUnbufferedIO*/); //throw FileError wxMemoryInputStream memStream(rawStream.c_str(), rawStream.size()); //does not take ownership wxZipInputStream zipStream(memStream, wxConvUTF8); //do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!" @@ -195,11 +195,11 @@ ImageBuffer::ImageBuffer(const Zstring& zipPath) //throw FileError } catch (FileError&) //fall back to folder { - traverseFolder(beforeLast(zipPath, Zstr(".zip"), IF_MISSING_RETURN_NONE), [&](const FileInfo& fi) + traverseFolder(beforeLast(zipPath, Zstr(".zip"), IfNotFoundReturn::none), [&](const FileInfo& fi) { if (endsWith(fi.fullPath, Zstr(".png"))) { - std::string stream = loadBinContainer<std::string>(fi.fullPath, nullptr /*notifyUnbufferedIO*/); //throw FileError + std::string stream = getFileContent(fi.fullPath, nullptr /*notifyUnbufferedIO*/); //throw FileError streams.emplace_back(fi.itemName, std::move(stream)); } }, nullptr, nullptr, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); @@ -227,7 +227,7 @@ ImageBuffer::ImageBuffer(const Zstring& zipPath) //throw FileError //=> there's only one type of wxImage: with alpha channel, no mask!!! convertToVanillaImage(img); - const std::string imageName = utfTo<std::string>(beforeLast(fileName, Zstr("."), IF_MISSING_RETURN_NONE)); + const std::string imageName = utfTo<std::string>(beforeLast(fileName, Zstr("."), IfNotFoundReturn::none)); imagesRaw_.emplace(imageName, img); if (hqScaler_) @@ -311,7 +311,7 @@ std::unique_ptr<ImageBuffer> globalImageBuffer; void zen::imageResourcesInit(const Zstring& zipPath) //throw FileError { - assert(runningMainThread()); //wxWidgets is not thread-safe! + assert(runningOnMainThread()); //wxWidgets is not thread-safe! assert(!globalImageBuffer); globalImageBuffer = std::make_unique<ImageBuffer>(zipPath); //throw FileError } @@ -319,7 +319,7 @@ void zen::imageResourcesInit(const Zstring& zipPath) //throw FileError void zen::ImageResourcesCleanup() { - assert(runningMainThread()); //wxWidgets is not thread-safe! + assert(runningOnMainThread()); //wxWidgets is not thread-safe! assert(globalImageBuffer); globalImageBuffer.reset(); } @@ -327,7 +327,7 @@ void zen::ImageResourcesCleanup() const wxImage& zen::loadImage(const std::string& name, int maxWidth /*optional*/, int maxHeight /*optional*/) { - assert(runningMainThread()); //wxWidgets is not thread-safe! + assert(runningOnMainThread()); //wxWidgets is not thread-safe! assert(globalImageBuffer); if (globalImageBuffer) return globalImageBuffer->getImage(name, maxWidth, maxHeight); diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index 19ba6ba6..4569a2a7 100644 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -70,7 +70,7 @@ void copySubImage(const wxImage& src, wxPoint srcPos, void copyImageLayover(const wxImage& src, - /**/ wxImage& trg, wxPoint trgPos) + /**/ wxImage& trg, wxPoint trgPos) { const int srcWidth = src.GetWidth (); const int srcHeight = src.GetHeight(); @@ -119,7 +119,7 @@ std::vector<std::pair<wxString, wxSize>> getTextExtentInfo(const wxString& text, 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)) + for (const wxString& line : split(text, L'\n', SplitOnEmpty::allow)) lineInfo.emplace_back(line, line.empty() ? wxSize() : dc.GetTextExtent(line)); return lineInfo; diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index bdb904f2..ffde9824 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -173,7 +173,7 @@ public: else m_checkBoxCustom->Hide(); - Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(StandardPopupDialog::OnKeyPressed), nullptr, this); //dialog-specific local key events + Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent& event) { onLocalKeyEvent(event); }); //dialog-specific local key events //------------------------------------------------------------------------------ StdButtons stdBtns; @@ -225,31 +225,31 @@ public: } private: - void OnClose (wxCloseEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::cancel)); } - void OnCancel(wxCommandEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::cancel)); } + void onClose (wxCloseEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::cancel)); } + void onCancel(wxCommandEvent& event) override { EndModal(static_cast<int>(ConfirmationButton3::cancel)); } - void OnButtonAccept(wxCommandEvent& event) override + void onButtonAccept(wxCommandEvent& event) override { if (checkBoxValue_) *checkBoxValue_ = m_checkBoxCustom->GetValue(); EndModal(static_cast<int>(ConfirmationButton3::accept)); } - void OnButtonAcceptAll(wxCommandEvent& event) override + void onButtonAcceptAll(wxCommandEvent& event) override { if (checkBoxValue_) *checkBoxValue_ = m_checkBoxCustom->GetValue(); EndModal(static_cast<int>(ConfirmationButton3::acceptAll)); } - void OnButtonDecline(wxCommandEvent& event) override + void onButtonDecline(wxCommandEvent& event) override { if (checkBoxValue_) *checkBoxValue_ = m_checkBoxCustom->GetValue(); EndModal(static_cast<int>(ConfirmationButton3::decline)); } - void OnKeyPressed(wxKeyEvent& event) + void onLocalKeyEvent(wxKeyEvent& event) { switch (event.GetKeyCode()) { @@ -257,7 +257,7 @@ private: case WXK_NUMPAD_ENTER: { wxCommandEvent dummy(wxEVT_COMMAND_BUTTON_CLICKED); - OnButtonAccept(dummy); + onButtonAccept(dummy); return; } @@ -268,7 +268,7 @@ private: event.Skip(); } - void OnCheckBoxClick(wxCommandEvent& event) override { updateGui(); event.Skip(); } + void onCheckBoxClick(wxCommandEvent& event) override { updateGui(); event.Skip(); } void updateGui() { diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp index 7fc05d53..b9da4c38 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, _("dummy"), 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 8191ca0f..52bd593c 100644 --- a/wx+/popup_dlg_generated.h +++ b/wx+/popup_dlg_generated.h @@ -38,34 +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(); }; @@ -33,7 +33,7 @@ wxImage mirrorIfRtl(const wxImage& img); //---------------------- implementation ------------------------ namespace impl { -//don't use wxDC::DrawLabel: +//don't use wxDC::DrawLabel: // - expensive GetTextExtent() call even when passing an empty string!!! // - 1-off alignment bugs! inline diff --git a/wx+/toggle_button.h b/wx+/toggle_button.h index 0a359c5c..560ad77e 100644 --- a/wx+/toggle_button.h +++ b/wx+/toggle_button.h @@ -24,7 +24,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxButtonNameStr) : + const wxString& name = wxASCII_STR(wxButtonNameStr)) : wxBitmapButton(parent, id, bitmap, pos, size, style, validator, name) {} //wxButton constructor @@ -35,7 +35,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxButtonNameStr) : + const wxString& name = wxASCII_STR(wxButtonNameStr)) : wxBitmapButton(parent, id, wxNullBitmap, pos, size, style, validator, name) { SetLabel(label); |