diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/bitmap_button.h | 2 | ||||
-rw-r--r-- | wx+/dc.h | 40 | ||||
-rw-r--r-- | wx+/graph.cpp | 2 | ||||
-rw-r--r-- | wx+/grid.cpp | 144 | ||||
-rw-r--r-- | wx+/grid.h | 19 |
5 files changed, 112 insertions, 95 deletions
diff --git a/wx+/bitmap_button.h b/wx+/bitmap_button.h index 8fe8e146..a3e6b0f6 100644 --- a/wx+/bitmap_button.h +++ b/wx+/bitmap_button.h @@ -101,7 +101,7 @@ wxBitmap renderSelectedButton(const wxSize& sz) 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); + drawInsetRectangle(dc, wxRect(bmp.GetSize()), fastFromDIP(1), borderCol, innerCol); } return bmp; } @@ -37,6 +37,7 @@ void clearArea(wxDC& dc, const wxRect& rect, const wxColor& col) if (rect.width > 0 && //clearArea() is surprisingly expensive rect.height > 0) { + assert(col.IsSolid()); //wxDC::DrawRectangle() just widens inner area if wxTRANSPARENT_PEN is used! //bonus: wxTRANSPARENT_PEN is about 2x faster than redundantly drawing with col! wxDCPenChanger areaPen (dc, *wxTRANSPARENT_PEN); @@ -48,16 +49,37 @@ void clearArea(wxDC& dc, const wxRect& rect, const wxColor& col) //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) +void drawInsetRectangle(wxDC& dc, wxRect rect, int borderWidth, const wxColor& borderCol, const wxColor& innerCol) { - assert(borderCol.IsSolid() && innerCol.IsSolid()); - wxDCPenChanger rectPen (dc, *wxTRANSPARENT_PEN); - wxDCBrushChanger rectBrush(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); + if (rect.width > 0 && + rect.height > 0) + { + assert(borderCol.IsSolid() && innerCol.IsSolid()); + wxDCPenChanger rectPen (dc, *wxTRANSPARENT_PEN); + wxDCBrushChanger rectBrush(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); + } +} + + +inline +void drawInsetRectangle(wxDC& dc, const wxRect& rect, int borderWidth, const wxColor& col) +{ + if (rect.width > 0 && + rect.height > 0) + { + assert(col.IsSolid()); + wxDCPenChanger areaPen (dc, *wxTRANSPARENT_PEN); + wxDCBrushChanger areaBrush(dc, col); + dc.DrawRectangle(rect.GetTopLeft(), {borderWidth, rect.height}); + dc.DrawRectangle(rect.GetTopLeft() + wxPoint{rect.width - borderWidth, 0}, {borderWidth, rect.height}); + dc.DrawRectangle(rect.GetTopLeft(), {rect.width, borderWidth}); + dc.DrawRectangle(rect.GetTopLeft() + wxPoint{0, rect.height - borderWidth}, {rect.width, borderWidth}); + } } diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 8dec2074..0f9cffef 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -576,7 +576,7 @@ void Graph2D::render(wxDC& dc) const assert(attr_.yLabelpos == YLabelPos::none || attr_.labelFmtY); //paint graph background (excluding label area) - drawFilledRectangle(dc, graphArea, fastFromDIP(1), getBorderColor(), attr_.colorBack); + drawInsetRectangle(dc, graphArea, fastFromDIP(1), getBorderColor(), attr_.colorBack); graphArea.Deflate(fastFromDIP(1)); //set label areas respecting graph area border! diff --git a/wx+/grid.cpp b/wx+/grid.cpp index fb9aacbc..80e9d1d6 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -409,7 +409,7 @@ private: class Grid::CornerWin : public SubWindow { public: - CornerWin(Grid& parent) : SubWindow(parent) {} + explicit CornerWin(Grid& parent) : SubWindow(parent) {} private: bool AcceptsFocus() const override { return false; } @@ -447,7 +447,7 @@ private: class Grid::RowLabelWin : public SubWindow { public: - RowLabelWin(Grid& parent) : + explicit RowLabelWin(Grid& parent) : SubWindow(parent), rowHeight_(parent.GetCharHeight() + 2 + 1) {} //default height; don't call any functions on "parent" other than those from wxWindow during construction! //2 for some more space, 1 for bottom border (gives 15 + 2 + 1 on Windows, 17 + 2 + 1 on Ubuntu) @@ -468,12 +468,11 @@ public: ptrdiff_t getRowAtPos(ptrdiff_t posY) const //returns < 0 on invalid input, else row number within: [0, rowCount]; rowCount if out of range { - if (posY >= 0 && rowHeight_ > 0) - { - const size_t row = posY / rowHeight_; - return std::min(row, refParent().getRowCount()); - } - return -1; + if (posY < 0) + return -1; + + const size_t row = posY / rowHeight_; + return std::min(row, refParent().getRowCount()); } int getRowHeight() const { return rowHeight_; } //guarantees to return size >= 1 ! @@ -488,15 +487,6 @@ public: return wxRect(); } - std::pair<ptrdiff_t, ptrdiff_t> getRowsOnClient(const wxRect& clientRect) const //returns range [begin, end) - { - const int yFrom = refParent().CalcUnscrolledPosition(clientRect.GetTopLeft ()).y; - const int yTo = refParent().CalcUnscrolledPosition(clientRect.GetBottomRight()).y; - - return {std::max(yFrom / rowHeight_, 0), - std::min<ptrdiff_t>((yTo / rowHeight_) + 1, refParent().getRowCount())}; - } - private: static std::wstring formatRowNum(size_t row) { return formatNumber(row + 1); } //convert number to std::wstring including thousands separator @@ -510,8 +500,8 @@ private: dc.SetFont(GetFont()); //harmonize with RowLabelWin::getBestWidth()! - auto rowRange = getRowsOnClient(rect); //returns range [begin, end) - for (auto row = rowRange.first; row < rowRange.second; ++row) + const auto& [rowFirst, rowLast] = refParent().getVisibleRows(rect); + for (auto row = rowFirst; row < rowLast; ++row) { wxRect rectRowLabel = getRowLabelArea(row); //returns empty rect if row not found if (rectRowLabel.height > 0) @@ -631,7 +621,7 @@ private: class Grid::ColLabelWin : public SubWindow { public: - ColLabelWin(Grid& parent) : SubWindow(parent) + explicit ColLabelWin(Grid& parent) : SubWindow(parent) { //coordinate with ColLabelWin::render(): wxFont labelFont = GetFont(); @@ -915,9 +905,8 @@ private: const std::wstring toolTip = [&] { - const wxPoint absPos = refParent().CalcUnscrolledPosition(clientPos); - const ColumnType colType = refParent().getColumnAtPos(absPos.x).colType; //returns ColumnType::none if no column at x position! - if (colType != ColumnType::none) + if (const ColumnType colType = refParent().getColumnAtWinPos(clientPos.x).colType; //returns ColumnType::none if no column at x position! + colType != ColumnType::none) if (auto prov = refParent().getDataProvider()) return prov->getToolTip(colType); return std::wstring(); @@ -1021,9 +1010,9 @@ private: const wxPoint gridAreaTL(refParent().CalcScrolledPosition(wxPoint(0, 0))); //client coordinates const int rowHeight = rowLabelWin_.getRowHeight(); - const auto rowRange = rowLabelWin_.getRowsOnClient(rect); //returns range [begin, end) + const auto& [rowFirst, rowLast] = refParent().getVisibleRows(rect); - for (auto row = rowRange.first; row < rowRange.second; ++row) + for (auto row = rowFirst; row < rowLast; ++row) { //draw background lines const wxRect rowRect(gridAreaTL + wxPoint(0, row * rowHeight), wxSize(totalRowWidth, rowHeight)); @@ -1089,9 +1078,8 @@ private: { const wxPoint mousePos = GetPosition() + event.GetPosition(); 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 ptrdiff_t row = refParent().getRowAtWinPos (event.GetPosition().y); //return -1 for invalid position; >= rowCount if out of range + const ColumnPosInfo cpi = refParent().getColumnAtWinPos(event.GetPosition().x); //returns ColumnType::none if no column at x position! const HoverArea rowHover = [&] { if (0 <= row && row < rowCount && cpi.colType != ColumnType::none) @@ -1124,9 +1112,8 @@ private: const wxPoint mousePos = GetPosition() + event.GetPosition(); 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 ptrdiff_t row = refParent().getRowAtWinPos (event.GetPosition().y); //return -1 for invalid position; >= rowCount if out of range + const ColumnPosInfo cpi = refParent().getColumnAtWinPos(event.GetPosition().x); //returns ColumnType::none if no column at x position! const HoverArea rowHover = [&] { if (0 <= row && row < rowCount && cpi.colType != ColumnType::none) @@ -1196,7 +1183,7 @@ private: selectionAnchor_ = cursorRow_; } //slight deviation from Explorer: change cursor while dragging mouse! -> unify behavior with shift + direction keys - + const wxPoint mousePos = GetPosition() + event.GetPosition(); const size_t rowFrom = activeSelection_->getStartRow(); const size_t rowTo = activeSelection_->getCurrentRow(); const bool positive = activeSelection_->isPositiveSelect(); @@ -1210,7 +1197,7 @@ private: refParent().selectRange2(rowFirst, rowLast, positive, &mouseClick, GridEventPolicy::allow); if (mouseClick.GetEventType() == EVENT_GRID_MOUSE_RIGHT_DOWN) - sendEventToParent(GridContextMenuEvent(mouseClick.mousePos_)); + sendEventToParent(GridContextMenuEvent(mousePos)); //... *not* mouseClick.mousePos_ } #if 0 if (!event.RightUp()) @@ -1219,9 +1206,8 @@ private: //this one may point to row which is not in visible area! const wxPoint mousePos = GetPosition() + event.GetPosition(); 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 ptrdiff_t row = refParent().getRowAtWinPos (event.GetPosition().y); //return -1 for invalid position; >= rowCount if out of range + const ColumnPosInfo cpi = refParent().getColumnAtWinPos(event.GetPosition().x); //returns ColumnType::none if no column at x position! const HoverArea rowHover = [&] { if (0 <= row && row < rowCount && cpi.colType != ColumnType::none) @@ -1254,9 +1240,8 @@ private: if (auto prov = refParent().getDataProvider()) { const ptrdiff_t rowCount = refParent().getRowCount(); - const wxPoint absPos = refParent().CalcUnscrolledPosition(clientPos); - 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 ptrdiff_t row = refParent().getRowAtWinPos (clientPos.y); //return -1 for invalid position; >= rowCount if out of range + const ColumnPosInfo cpi = refParent().getColumnAtWinPos(clientPos.x); //returns ColumnType::none if no column at x position! const HoverArea rowHover = [&] { if (0 <= row && row < rowCount && cpi.colType != ColumnType::none) @@ -1378,8 +1363,7 @@ private: wxPoint clientPosTrimmed = clientPos; clientPosTrimmed.y = std::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window! - 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 + const ptrdiff_t newRow = wnd_.refParent().getRowAtWinPos(clientPosTrimmed.y); //return -1 for invalid position; >= rowCount if out of range assert(newRow >= 0); if (newRow >= 0) if (rowCurrent_ != newRow) @@ -1435,7 +1419,7 @@ private: void refreshRow(size_t row) { const wxRect& rowArea = rowLabelWin_.getRowLabelArea(row); //returns empty rect if row not found - const wxPoint topLeft = refParent().CalcScrolledPosition(wxPoint(0, rowArea.y)); //absolute -> client coordinates + const wxPoint topLeft = refParent().CalcScrolledPosition(wxPoint(0, rowArea.y)); //logical -> window coordinates wxRect cellArea(topLeft, wxSize(refParent().getColWidthsSum(GetClientSize().GetWidth()), rowArea.height)); RefreshRect(cellArea); } @@ -1470,7 +1454,7 @@ private: MouseHighlight highlight_; //current mouse highlight bool freezeMouseHighlight_ = false; - ptrdiff_t cursorRow_ = 0; + size_t cursorRow_ = 0; size_t selectionAnchor_ = 0; bool gridUpdatePending_ = false; }; @@ -1535,7 +1519,7 @@ void Grid::updateWindowSizes(bool updateScrollbar) //1. calculate row label width independent from scrollbars const int mainWinHeightGross = std::max(0, GetSize().GetHeight() - getColumnLabelHeight()); //independent from client sizes and scrollbars! - const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); // + const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); // const int rowLabelWidth = [&] { @@ -1641,10 +1625,10 @@ void Grid::updateWindowSizes(bool updateScrollbar) wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size) { - //1. "size == GetSize() == (0, 0)" happens temporarily during initialization - //2. often it's even (0, 20) - //3. fuck knows why, but we *temporarily* get "size == GetSize() == (1, 1)" when wxAUI panel containing Grid is dropped - if (size.x <= 1 || size.y <= 1) + //1. "size == GetSize() == (0, 0)" happens temporarily during initialization + //2. often it's even (0, 20) + //3. fuck knows why, but we *temporarily* get "size == GetSize() == (1, 1)" when wxAUI panel containing Grid is dropped + if (size.x <= 1 || size.y <= 1) return {}; //probably best considering calling code in generic/scrlwing.cpp: wxScrollHelper::AdjustScrollbars() //1. calculate row label width independent from scrollbars @@ -1771,12 +1755,14 @@ void Grid::onKeyDown(wxKeyEvent& event) switch (keyCode) { - case WXK_MENU: // - case WXK_WINDOWS_MENU: //simulate right mouse click at cursor row (+1) position + case WXK_MENU: //simulate right mouse click at cursor row position (on lower edge) + case WXK_WINDOWS_MENU: //(but truncate to window if cursor is out of view) { - 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 size_t row = std::min(mainWin_->getCursor(), getRowCount()); + + const int clientPosMainWinY = std::clamp(CalcScrolledPosition(wxPoint(0, rowLabelWin_->getRowHeight() * (row + 1))).y - 1, //logical -> window coordinates + 0, mainWin_->GetClientSize().GetHeight() - 1); + const wxPoint mousePos = mainWin_->GetPosition() + wxPoint(0, clientPosMainWinY); //mainWin_-relative to Grid-relative GridContextMenuEvent contextEvent(mousePos); @@ -2096,25 +2082,44 @@ ColumnType Grid::colToType(size_t col) const } -ptrdiff_t Grid::getRowAtPos(int posY) const { return rowLabelWin_->getRowAtPos(posY); } - - -Grid::ColumnPosInfo Grid::getColumnAtPos(int posX) const +Grid::ColumnPosInfo Grid::getColumnAtWinPos(int posX) const { - if (posX >= 0) + if (const int absX = CalcUnscrolledPosition(wxPoint(posX, 0)).x; + absX >= 0) { int accWidth = 0; for (const ColumnWidth& cw : getColWidths()) { accWidth += cw.width; - if (posX < accWidth) - return {cw.type, posX + cw.width - accWidth, cw.width}; + if (absX < accWidth) + return {cw.type, absX + cw.width - accWidth, cw.width}; } } return {ColumnType::none, 0, 0}; } +ptrdiff_t Grid::getRowAtWinPos(int posY) const +{ + const int absY = CalcUnscrolledPosition(wxPoint(0, posY)).y; + return rowLabelWin_->getRowAtPos(absY); //return -1 for invalid position, rowCount if past the end +} + + +std::pair<ptrdiff_t, ptrdiff_t> Grid::getVisibleRows(const wxRect& clientRect) const //returns range [begin, end) +{ + if (clientRect.height > 0) + { + const int rowFrom = getRowAtWinPos(clientRect.y); + const int rowTo = getRowAtWinPos(clientRect.GetBottom()); + + return {std::max(rowFrom, 0), + std::min<ptrdiff_t>((rowTo) + 1, getRowCount())}; + } + return {}; +} + + wxRect Grid::getColumnLabelArea(ColumnType colType) const { const std::vector<ColumnWidth>& absWidths = getColWidths(); //resolve negative/stretched widths @@ -2139,9 +2144,9 @@ void Grid::refreshCell(size_t row, ColumnType colType) { const wxRect& colArea = getColumnLabelArea(colType); //returns empty rect if column not found const wxRect& rowArea = rowLabelWin_->getRowLabelArea(row); //returns empty rect if row not found - if (colArea.height > 0 && rowArea.height > 0) + if (colArea.width > 0 && rowArea.height > 0) { - const wxPoint topLeft = CalcScrolledPosition(wxPoint(colArea.x, rowArea.y)); //absolute -> client coordinates + const wxPoint topLeft = CalcScrolledPosition(wxPoint(colArea.x, rowArea.y)); //logical -> window coordinates const wxRect cellArea(topLeft, wxSize(colArea.width, rowArea.height)); getMainWin().RefreshRect(cellArea); @@ -2224,15 +2229,6 @@ void Grid::scrollTo(size_t row) } -size_t Grid::getTopRow() const -{ - const wxPoint absPos = CalcUnscrolledPosition(wxPoint(0, 0)); - const ptrdiff_t row = rowLabelWin_->getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range - assert((getRowCount() == 0 && row == 0) || (0 <= row && row < static_cast<ptrdiff_t>(getRowCount()))); - return row; -} - - bool Grid::Enable(bool enable) { Refresh(); @@ -2255,10 +2251,10 @@ int Grid::getBestColumnSize(size_t col) const wxClientDC dc(mainWin_); dc.SetFont(mainWin_->GetFont()); //harmonize with MainWin::render() - int maxSize = 0; + const auto& [rowFirst, rowLast] = getVisibleRows(mainWin_->GetClientRect()); - auto rowRange = rowLabelWin_->getRowsOnClient(mainWin_->GetClientRect()); //returns range [begin, end) - for (auto row = rowRange.first; row < rowRange.second; ++row) + int maxSize = 0; + for (auto row = rowFirst; row < rowLast; ++row) maxSize = std::max(maxSize, dataView_->getBestSize(dc, row, type)); return maxSize; @@ -13,7 +13,6 @@ #include <set> #include <vector> #include <zen/stl_tools.h> -//#include <zen/basic_math.h> #include <wx/scrolwin.h> @@ -56,7 +55,7 @@ struct GridClickEvent : public wxEvent const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range const HoverArea hoverArea_; //may be HoverArea::none - const wxPoint mousePos_; //client coordinates + const wxPoint mousePos_; //Grid-relative coordinates }; struct GridSelectEvent : public wxEvent @@ -78,7 +77,7 @@ struct GridLabelClickEvent : public wxEvent GridLabelClickEvent* Clone() const override { return new GridLabelClickEvent(*this); } const ColumnType colType_; //may be ColumnType::none - const wxPoint mousePos_; //client coordinates + const wxPoint mousePos_; //Grid-relative coordinates }; struct GridColumnResizeEvent : public wxEvent @@ -92,10 +91,10 @@ struct GridColumnResizeEvent : public wxEvent struct GridContextMenuEvent : public wxEvent { - explicit GridContextMenuEvent(const wxPoint& mousePos) : wxEvent(0 /*winid*/, EVENT_GRID_CONTEXT_MENU), mousePos_(mousePos) {} + 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 + const wxPoint mousePos_; //Grid-relative coordinates }; //------------------------------------------------------------------------------------------------------------ @@ -203,15 +202,16 @@ public: wxWindow& getMainWin (); const wxWindow& getMainWin() const; - ptrdiff_t getRowAtPos(int posY) const; //return -1 for invalid position, >= rowCount if out of range; absolute coordinates! - struct ColumnPosInfo { - ColumnType colType = ColumnType::none; //ColumnType::none no column at x position! + ColumnType colType = ColumnType::none; //ColumnType::none => no column at posX! int cellRelativePosX = 0; int colWidth = 0; }; - ColumnPosInfo getColumnAtPos(int posX) const; //absolute position! + ColumnPosInfo getColumnAtWinPos(int posX) const; + ptrdiff_t getRowAtWinPos(int posY) const; //return -1 for invalid position, >= rowCount if out of range + + std::pair<ptrdiff_t, ptrdiff_t> getVisibleRows(const wxRect& clientRect) const; //returns range [begin, end) void refreshCell(size_t row, ColumnType colType); @@ -222,7 +222,6 @@ public: size_t getGridCursor() const; //returns row void scrollTo(size_t row); - size_t getTopRow() const; void makeRowVisible(size_t row); |