From 583d22efb5901296d1e3dcbc091be5dee9d8a14f Mon Sep 17 00:00:00 2001 From: B Stack Date: Fri, 14 Jun 2019 22:33:49 -0400 Subject: 10.13 --- wx+/dc.h | 26 +++++----- wx+/graph.cpp | 4 +- wx+/grid.cpp | 158 +++++++++++++++++++++++++++++++++------------------------- wx+/grid.h | 7 ++- wx+/rtl.h | 53 ++++++++++++-------- 5 files changed, 142 insertions(+), 106 deletions(-) (limited to 'wx+') diff --git a/wx+/dc.h b/wx+/dc.h index 311080dd..bcf62a14 100644 --- a/wx+/dc.h +++ b/wx+/dc.h @@ -17,20 +17,19 @@ namespace zen { /* -1. wxDCClipper does *not* stack: another fix for yet another poor wxWidgets implementation + 1. wxDCClipper does *not* stack: another fix for yet another poor wxWidgets implementation -class RecursiveDcClipper -{ - RecursiveDcClipper(wxDC& dc, const wxRect& r) : dc_(dc) -}; + class RecursiveDcClipper + { + RecursiveDcClipper(wxDC& dc, const wxRect& r) + }; ------------------------------------------------------------------------------------------------- + 2. wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active: a fix for a poor wxWidgets implementation -2. wxAutoBufferedPaintDC skips one pixel on left side when RTL layout is active: a fix for a poor wxWidgets implementation -class BufferedPaintDC -{ - BufferedPaintDC(wxWindow& wnd, std::unique_ptr& buffer); -}; + class BufferedPaintDC + { + BufferedPaintDC(wxWindow& wnd, std::unique_ptr& buffer) + }; */ @@ -77,7 +76,7 @@ public: oldRect_ = it->second; wxRect tmp = r; - tmp.Intersect(*oldRect_); //better safe than sorry + tmp.Intersect(*oldRect_); //better safe than sorry dc_.SetClippingRegion(tmp); // it->second = tmp; } @@ -113,6 +112,7 @@ private: #error we need this one! #endif +//CAVEAT: wxPaintDC on wxGTK/wxMAC does not implement SetLayoutDirection()!!! => GetLayoutDirection() == wxLayout_Default #if wxALWAYS_NATIVE_DOUBLE_BUFFER struct BufferedPaintDC : public wxPaintDC { BufferedPaintDC(wxWindow& wnd, std::optional& buffer) : wxPaintDC(&wnd) {} }; @@ -144,7 +144,7 @@ public: if (GetLayoutDirection() == wxLayout_RightToLeft) { paintDc_.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit() - SetLayoutDirection(wxLayout_LeftToRight); // + SetLayoutDirection(wxLayout_LeftToRight); // } const wxPoint origin = GetDeviceOrigin(); diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 3df8888a..41750a48 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -623,7 +623,7 @@ void Graph2D::render(wxDC& dc) const if (minX <= maxX && maxX - minX < std::numeric_limits::infinity()) //valid x-range { - const wxSize minimalBlockSizePx = dc.GetTextExtent(L"00"); + const wxSize minimalBlockSizePx = dc.GetTextExtent(L"00"); int blockCountX = 0; //enlarge minX, maxX to a multiple of a "useful" block size @@ -676,7 +676,7 @@ void Graph2D::render(wxDC& dc) const *attr_.labelFmtY); if (graphArea.width <= 1 || graphArea.height <= 1) - return; + return; const ConvertCoord cvrtX(minX, maxX, graphArea.width - 1); //map [minX, maxX] to [0, pixelWidth - 1] const ConvertCoord cvrtY(maxY, minY, graphArea.height - 1); //map [minY, maxY] to [pixelHeight - 1, 0] diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 2838f575..804c8fba 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -688,8 +688,8 @@ private: void onMouseLeftDown(wxMouseEvent& event) override { - if (FindFocus() != &refParent().getMainWin()) - refParent().getMainWin().SetFocus(); + //if (FindFocus() != &refParent().getMainWin()) -> clicking column label shouldn't change input focus, right!? e.g. resizing column, sorting...(other grid) + // refParent().getMainWin().SetFocus(); activeResizing_.reset(); activeClickOrMove_.reset(); @@ -1178,7 +1178,7 @@ private: int pixelsPerUnitY = 0; wnd_.refParent().GetScrollPixelsPerUnit(nullptr, &pixelsPerUnitY); if (pixelsPerUnitY <= 0) - return; + return; const double mouseDragSpeedIncScrollU = MOUSE_DRAG_ACCELERATION_DIP * wnd_.rowLabelWin_.getRowHeight() / pixelsPerUnitY; //unit: [scroll units / (DIP * sec)] @@ -1321,7 +1321,7 @@ Grid::Grid(wxWindow* parent, SetInitialSize(size); //"Most controls will use this to set their initial size" -> why not - assert(GetClientSize() == GetSize()); //borders are NOT allowed for Grid + 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); @@ -1362,45 +1362,30 @@ void Grid::updateWindowSizes(bool updateScrollbar) //harmonize with Grid::GetSizeAvailableForScrollTarget()! //1. calculate row label width independent from scrollbars - const int mainWinHeightGross = std::max(GetSize().GetHeight() - colLabelHeight_, 0); //independent from client sizes and scrollbars! - const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); // + const int mainWinHeightGross = std::max(0, GetSize().GetHeight() - colLabelHeight_); //independent from client sizes and scrollbars! + const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); // - int rowLabelWidth = 0; - if (drawRowLabel_ && logicalHeight > 0) + const int rowLabelWidth = [&] { - ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y; - ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ; - yFrom = std::clamp(yFrom, 0, logicalHeight - 1); - yTo = std::clamp(yTo, 0, logicalHeight - 1); - - const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom); - const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo); - if (rowFrom >= 0 && rowTo >= 0) - rowLabelWidth = rowLabelWin_->getBestWidth(rowFrom, rowTo); - } - - auto getMainWinSize = [&](const wxSize& clientSize) { return wxSize(std::max(0, clientSize.GetWidth() - rowLabelWidth), std::max(0, clientSize.GetHeight() - colLabelHeight_)); }; - - auto setScrollbars2 = [&](int logWidth, int logHeight) //replace SetScrollbars, which loses precision of pixelsPerUnitX for some brain-dead reason - { - mainWin_->SetVirtualSize(logWidth, logHeight); //set before calling SetScrollRate(): - //else SetScrollRate() would fail to preserve scroll position when "new virtual pixel-pos > old virtual height" - - int ppsuX = 0; //pixel per scroll unit - int ppsuY = 0; - GetScrollPixelsPerUnit(&ppsuX, &ppsuY); - - const int ppsuNew = rowLabelWin_->getRowHeight(); - if (ppsuX != ppsuNew || ppsuY != ppsuNew) //support polling! - SetScrollRate(ppsuNew, ppsuNew); //internally calls AdjustScrollbars() and GetVirtualSize()! - - AdjustScrollbars(); //lousy wxWidgets design decision: internally calls mainWin_->GetClientSize() without considering impact of scrollbars! - //Attention: setting scrollbars triggers *synchronous* resize event if scrollbars are shown or hidden! => updateWindowSizes() recursion! (Windows) - }; + if (drawRowLabel_ && logicalHeight > 0) + { + ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y; + ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ; + yFrom = std::clamp(yFrom, 0, logicalHeight - 1); + yTo = std::clamp(yTo, 0, logicalHeight - 1); + + const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom); + const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo); + if (rowFrom >= 0 && rowTo >= 0) + return rowLabelWin_->getBestWidth(rowFrom, rowTo); + } + return 0; + }(); //2. update managed windows' sizes: just assume scrollbars are already set correctly, even if they may not be (yet)! //this ensures mainWin_->SetVirtualSize() and AdjustScrollbars() are working with the correct main window size, unless sb change later, which triggers a recalculation anyway! - const wxSize mainWinSize = getMainWinSize(GetClientSize()); + const wxSize mainWinSize(std::max(0, GetClientSize().GetWidth () - rowLabelWidth), + std::max(0, GetClientSize().GetHeight() - colLabelHeight_)); cornerWin_ ->SetSize(0, 0, rowLabelWidth, colLabelHeight_); rowLabelWin_->SetSize(0, colLabelHeight_, rowLabelWidth, mainWinSize.GetHeight()); @@ -1414,13 +1399,30 @@ void Grid::updateWindowSizes(bool updateScrollbar) //3. update scrollbars: "guide wxScrolledHelper to not screw up too much" if (updateScrollbar) { - const int mainWinWidthGross = getMainWinSize(GetSize()).GetWidth(); + auto setScrollbars2 = [&](int logWidth, int logHeight) //replace SetScrollbars, which loses precision of pixelsPerUnitX for some brain-dead reason + { + mainWin_->SetVirtualSize(logWidth, logHeight); //set before calling SetScrollRate(): + //else SetScrollRate() would fail to preserve scroll position when "new virtual pixel-pos > old virtual height" - if (logicalHeight <= mainWinHeightGross && + int ppsuX = 0; //pixel per scroll unit + int ppsuY = 0; + GetScrollPixelsPerUnit(&ppsuX, &ppsuY); + + const int ppsuNew = rowLabelWin_->getRowHeight(); + if (ppsuX != ppsuNew || ppsuY != ppsuNew) //support polling! + SetScrollRate(ppsuNew, ppsuNew); //internally calls AdjustScrollbars() and GetVirtualSize()! + + AdjustScrollbars(); //lousy wxWidgets design decision: internally calls mainWin_->GetClientSize() without considering impact of scrollbars! + //Attention: setting scrollbars triggers *synchronous* resize event if scrollbars are shown or hidden! => updateWindowSizes() recursion! (Windows) + }; + + const int mainWinWidthGross = std::max(0, GetSize().GetWidth() - rowLabelWidth); + + if (logicalHeight <= mainWinHeightGross && getColWidthsSum(mainWinWidthGross) <= mainWinWidthGross && //this special case needs to be considered *only* when both scrollbars are flexible: - showScrollbarX_ == SB_SHOW_AUTOMATIC && - showScrollbarY_ == SB_SHOW_AUTOMATIC) + showScrollbarH_ == SB_SHOW_AUTOMATIC && + showScrollbarV_ == SB_SHOW_AUTOMATIC) setScrollbars2(0, 0); //no scrollbars required at all! -> wxScrolledWindow requires active help to detect this special case! else { @@ -1452,13 +1454,13 @@ void Grid::updateWindowSizes(bool updateScrollbar) 2. lw > gw && lh > gh => need both scrollbars - 3. lh > gh - 4.1 lw <= nw => need vertical scrollbar only - 4.2 nw < lw <= gw => need both scrollbars + lh > gh + 3. lw <= nw => need vertical scrollbar only + 4. nw < lw <= gw => need both scrollbars - 4. lw > gw - 3.1 lh <= nh => need horizontal scrollbar only - 3.2 nh < lh <= gh => need both scrollbars + lw > gw + 5. lh <= nh => need horizontal scrollbar only + 6. nh < lh <= gh => need both scrollbars */ } } @@ -1467,27 +1469,47 @@ void Grid::updateWindowSizes(bool updateScrollbar) wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size) { - //harmonize with Grid::updateWindowSizes()! - //1. calculate row label width independent from scrollbars - const int mainWinHeightGross = std::max(size.GetHeight() - colLabelHeight_, 0); //independent from client sizes and scrollbars! - const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); // + const int mainWinHeightGross = std::max(0, size.GetHeight() - colLabelHeight_); //independent from client sizes and scrollbars! + const ptrdiff_t logicalHeight = rowLabelWin_->getLogicalHeight(); // - int rowLabelWidth = 0; - if (drawRowLabel_ && logicalHeight > 0) + const int rowLabelWidth = [&] { - ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y; - ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ; - yFrom = std::clamp(yFrom, 0, logicalHeight - 1); - yTo = std::clamp(yTo, 0, logicalHeight - 1); + if (drawRowLabel_ && logicalHeight > 0) + { + ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y; + ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ; + yFrom = std::clamp(yFrom, 0, logicalHeight - 1); + yTo = std::clamp(yTo, 0, logicalHeight - 1); + + const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom); + const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo); + if (rowFrom >= 0 && rowTo >= 0) + return rowLabelWin_->getBestWidth(rowFrom, rowTo); + } + return 0; + }(); - const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom); - const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo); - if (rowFrom >= 0 && rowTo >= 0) - rowLabelWidth = rowLabelWin_->getBestWidth(rowFrom, rowTo); - } + //2. try(!) to determine scrollbar sizes: + const wxSize scrollBarSizeTmp = GetSize() - GetClientSize(); + assert(scrollBarHeightH_ == 0 || scrollBarSizeTmp.y == 0 || scrollBarHeightH_ == scrollBarSizeTmp.y); + assert(scrollBarWidthV_ == 0 || scrollBarSizeTmp.x == 0 || scrollBarWidthV_ == scrollBarSizeTmp.x); + scrollBarHeightH_ = std::max(scrollBarHeightH_, scrollBarSizeTmp.y); + scrollBarWidthV_ = std::max(scrollBarWidthV_, scrollBarSizeTmp.x); + //this function is called again by wxScrollHelper::AdjustScrollbars() if SB_SHOW_ALWAYS-scrollbars are not yet shown => scrollbar size > 0 eventually! + + //----------------------------------------------------------------------------- + //harmonize with Grid::updateWindowSizes()! + wxSize sizeAvail = size - wxSize(rowLabelWidth, colLabelHeight_); + + //EXCEPTION: space consumed by SB_SHOW_ALWAYS-scrollbars is *never* available for "scroll target"; see wxScrollHelper::AdjustScrollbars() + if (showScrollbarH_ == SB_SHOW_ALWAYS) + sizeAvail.y -= scrollBarHeightH_; + if (showScrollbarV_ == SB_SHOW_ALWAYS) + sizeAvail.x -= scrollBarWidthV_; - return size - wxSize(rowLabelWidth, colLabelHeight_); + return wxSize(std::max(0, sizeAvail.x), + std::max(0, sizeAvail.y)); } @@ -1835,11 +1857,11 @@ std::vector Grid::getColumnConfig() const void Grid::showScrollBars(Grid::ScrollBarStatus horizontal, Grid::ScrollBarStatus vertical) { - if (showScrollbarX_ == horizontal && - showScrollbarY_ == vertical) return; //support polling! + if (showScrollbarH_ == horizontal && + showScrollbarV_ == vertical) return; //support polling! - showScrollbarX_ = horizontal; - showScrollbarY_ = vertical; + showScrollbarH_ = horizontal; + showScrollbarV_ = vertical; //the following wxGTK approach is pretty much identical to wxWidgets 2.9 ShowScrollbars() code! diff --git a/wx+/grid.h b/wx+/grid.h index dbd33c7a..cecdfafe 100644 --- a/wx+/grid.h +++ b/wx+/grid.h @@ -347,8 +347,8 @@ private: ColLabelWin* colLabelWin_; MainWin* mainWin_; - ScrollBarStatus showScrollbarX_ = SB_SHOW_AUTOMATIC; - ScrollBarStatus showScrollbarY_ = SB_SHOW_AUTOMATIC; + ScrollBarStatus showScrollbarH_ = SB_SHOW_AUTOMATIC; + ScrollBarStatus showScrollbarV_ = SB_SHOW_AUTOMATIC; int colLabelHeight_ = 0; bool drawRowLabel_ = true; @@ -362,6 +362,9 @@ private: std::vector oldColAttributes_; //visible + nonvisible columns; use for conversion in setColumnConfig()/getColumnConfig() *only*! size_t rowCountOld_ = 0; //at the time of last Grid::Refresh() + + int scrollBarHeightH_ = 0; //optional: may not be known (yet) + int scrollBarWidthV_ = 0; // }; //------------------------------------------------------------------------------------------------------------ diff --git a/wx+/rtl.h b/wx+/rtl.h index 26380f9d..16fde699 100644 --- a/wx+/rtl.h +++ b/wx+/rtl.h @@ -15,8 +15,8 @@ namespace zen { //functions supporting right-to-left GUI layout -void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional& buffer); -void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment); +void drawBitmapRtlMirror (wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment, std::optional& buffer); +void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment); //wxDC::DrawIcon DOES mirror by default -> implement RTL support when needed wxBitmap mirrorIfRtl(const wxBitmap& bmp); @@ -36,49 +36,60 @@ namespace impl //don't use wxDC::DrawLabel: it results in expensive GetTextExtent() call even when passing an empty string!!! //also avoid wxDC::DrawLabel 1-off alignment bugs inline -void drawBitmapAligned(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment) +void drawBitmapAligned(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment) { wxPoint pt = rect.GetTopLeft(); if (alignment & wxALIGN_RIGHT) //note: wxALIGN_LEFT == 0! - pt.x += rect.width - image.GetWidth(); + pt.x += rect.width - bmp.GetWidth(); else if (alignment & wxALIGN_CENTER_HORIZONTAL) - pt.x += (rect.width - image.GetWidth()) / 2; + pt.x += (rect.width - bmp.GetWidth()) / 2; if (alignment & wxALIGN_BOTTOM) //note: wxALIGN_TOP == 0! - pt.y += rect.height - image.GetHeight(); + pt.y += rect.height - bmp.GetHeight(); else if (alignment & wxALIGN_CENTER_VERTICAL) - pt.y += (rect.height - image.GetHeight()) / 2; + pt.y += (rect.height - bmp.GetHeight()) / 2; - dc.DrawBitmap(image, pt); + dc.DrawBitmap(bmp, pt); } } inline -void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment, std::optional& buffer) +void drawBitmapRtlMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment, std::optional& buffer) { - if (dc.GetLayoutDirection() == wxLayout_RightToLeft) + switch (dc.GetLayoutDirection()) { - if (!buffer || buffer->GetWidth() != rect.width || buffer->GetHeight() < rect.height) //[!] since we do a mirror, width needs to match exactly! - buffer = wxBitmap(rect.width, rect.height); + case wxLayout_LeftToRight: + return impl::drawBitmapAligned(dc, bmp, rect, alignment); - wxMemoryDC memDc(*buffer); - memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction! + case wxLayout_RightToLeft: + { + if (!buffer || buffer->GetWidth() != rect.width || buffer->GetHeight() < rect.height) //[!] since we do a mirror, width needs to match exactly! + buffer = wxBitmap(rect.width, rect.height); - impl::drawBitmapAligned(memDc, image, wxRect(0, 0, rect.width, rect.height), alignment); - //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug! + wxMemoryDC memDc(*buffer); + memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction! - dc.Blit(rect.GetTopLeft(), rect.GetSize(), &memDc, wxPoint(0, 0)); //blit out: mirror once again + impl::drawBitmapAligned(memDc, bmp, wxRect(0, 0, rect.width, rect.height), alignment); + //note: we cannot simply use memDc.SetLayoutDirection(wxLayout_RightToLeft) due to some strange 1 pixel bug! + + dc.Blit(rect.GetTopLeft(), rect.GetSize(), &memDc, wxPoint(0, 0)); //blit out: mirror once again + } + break; + + case wxLayout_Default: //CAVEAT: wxPaintDC/wxMemoryDC on wxGTK/wxMAC does not implement SetLayoutDirection()!!! => GetLayoutDirection() == wxLayout_Default + if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) + return impl::drawBitmapAligned(dc, bmp.ConvertToImage().Mirror(), rect, alignment); + else + return impl::drawBitmapAligned(dc, bmp, rect, alignment); } - else - impl::drawBitmapAligned(dc, image, rect, alignment); } inline -void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& image, const wxRect& rect, int alignment) +void drawBitmapRtlNoMirror(wxDC& dc, const wxBitmap& bmp, const wxRect& rect, int alignment) { - return impl::drawBitmapAligned(dc, image, rect, alignment); //wxDC::DrawBitmap does NOT mirror by default + return impl::drawBitmapAligned(dc, bmp, rect, alignment); //wxDC::DrawBitmap does NOT mirror by default } -- cgit