diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/async_task.h | 3 | ||||
-rw-r--r-- | wx+/bitmap_button.h | 6 | ||||
-rw-r--r-- | wx+/dc.h | 34 | ||||
-rw-r--r-- | wx+/image_resources.cpp | 3 | ||||
-rw-r--r-- | wx+/image_tools.cpp | 4 | ||||
-rw-r--r-- | wx+/image_tools.h | 7 | ||||
-rw-r--r-- | wx+/popup_dlg.cpp | 60 | ||||
-rw-r--r-- | wx+/popup_dlg.h | 11 | ||||
-rw-r--r-- | wx+/rtl.h | 10 | ||||
-rw-r--r-- | wx+/std_button_layout.h | 17 |
10 files changed, 106 insertions, 49 deletions
diff --git a/wx+/async_task.h b/wx+/async_task.h index a7ce2eb5..5a3e7caa 100644 --- a/wx+/async_task.h +++ b/wx+/async_task.h @@ -114,7 +114,8 @@ private: class AsyncGuiQueue : private wxEvtHandler { public: - AsyncGuiQueue(int pollingMs = 50) : pollingMs_(pollingMs) { timer_.Bind(wxEVT_TIMER, [this](wxTimerEvent& event) { onTimerEvent(event); }); } + explicit 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 32bc68c1..d701e64c 100644 --- a/wx+/bitmap_button.h +++ b/wx+/bitmap_button.h @@ -11,6 +11,7 @@ #include <wx/settings.h> #include <wx/statbmp.h> #include "image_tools.h" +#include "std_button_layout.h" #include "dc.h" @@ -66,9 +67,8 @@ void setBitmapTextLabel(wxBitmapButton& btn, const wxImage& img, const wxString& stackImages(imgTxt, img, ImageStackLayout::horizontal, ImageStackAlignment::center, gap); //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work correctly - const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); btn.SetMinSize({imgTxt.GetWidth () + 2 * border, - std::max(imgTxt.GetHeight() + 2 * border, defaultHeight)}); + std::max(imgTxt.GetHeight() + 2 * border, getDefaultButtonHeight())}); setImage(btn, imgTxt); } @@ -104,6 +104,7 @@ inline wxBitmap renderSelectedButton(const wxSize& sz) { wxBitmap bmp(sz); //seems we don't need to pass 24-bit depth here even for high-contrast color schemes + bmp.SetScaleFactor(getDisplayScaleFactor()); { wxMemoryDC dc(bmp); @@ -119,6 +120,7 @@ inline wxBitmap renderPressedButton(const wxSize& sz) { wxBitmap bmp(sz); //seems we don't need to pass 24-bit depth here even for high-contrast color schemes + bmp.SetScaleFactor(getDisplayScaleFactor()); { //draw rectangle border with gradient const wxColor colFrom = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE); @@ -87,7 +87,7 @@ void drawInsetRectangle(wxDC& dc, const wxRect& rect, int borderWidth, const wxC /* 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) */ -constexpr int defaultDpi = 96; +constexpr int defaultDpi = 96; //on Windows same as wxDisplay::GetStdPPIValue() (however returns 72 on macOS!) inline int getDPI() @@ -104,6 +104,13 @@ int getDPI() inline +double getDisplayScaleFactor() +{ + return static_cast<double>(getDPI()) / defaultDpi; +} + + +inline int fastFromDIP(int d) //like wxWindow::FromDIP (but tied to primary monitor and buffered) { return numeric::intDivRound(d * getDPI() - 10 /*round values like 1.5 down => 1 pixel on 150% scale*/, defaultDpi); @@ -123,11 +130,23 @@ wxBitmapBundle toBitmapBundle(const wxImage& img /*expected to be DPI-scaled!*/) { //return wxBitmap(img, -1 /*depth*/, static_cast<double>(getDPI()) / defaultDpi); implementation just ignores scale parameter! WTF! wxBitmap bmpScaled(img); - bmpScaled.SetScaleFactor(static_cast<double>(getDPI()) / defaultDpi); + bmpScaled.SetScaleFactor(getDisplayScaleFactor()); return bmpScaled; } +//all this shit just because wxDC::SetScaleFactor() is missing: +inline +void setScaleFactor(wxDC& dc, double scale) +{ + struct wxDcSurgeon : public wxDCImpl + { + void setScaleFactor(double scale) { m_contentScaleFactor = scale; } + }; + static_cast<wxDcSurgeon*>(dc.GetImpl())->setScaleFactor(scale); +} + + //---------------------- implementation ------------------------ class RecursiveDcClipper { @@ -195,10 +214,13 @@ public: const wxSize clientSize = wnd.GetClientSize(); if (clientSize.GetWidth() > 0 && clientSize.GetHeight() > 0) //wxBitmap asserts this!! width may be 0; test case "Grid::CornerWin": compare both sides, then change config { - if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight())) - buffer = wxBitmap(clientSize.GetWidth(), clientSize.GetHeight()); + if (!buffer_ || buffer->GetSize() != clientSize) + buffer.emplace(clientSize); + + if (buffer->GetScaleFactor() != wnd.GetDPIScaleFactor()) + buffer->SetScaleFactor(wnd.GetDPIScaleFactor()); - SelectObject(*buffer); + SelectObject(*buffer); //copies scale factor from wxBitmap if (paintDc_.IsOk() && paintDc_.GetLayoutDirection() == wxLayout_RightToLeft) SetLayoutDirection(wxLayout_RightToLeft); @@ -213,7 +235,7 @@ public: { if (GetLayoutDirection() == wxLayout_RightToLeft) { - paintDc_.SetLayoutDirection(wxLayout_LeftToRight); //workaround bug in wxDC::Blit() + paintDc_.SetLayoutDirection(wxLayout_LeftToRight); //work around bug in wxDC::Blit() SetLayoutDirection(wxLayout_LeftToRight); // } diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 0301a082..36055f3f 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -209,8 +209,7 @@ ImageBuffer::ImageBuffer(const Zstring& zipPath) //throw FileError } //-------------------------------------------------------------------- - //activate support for .png files - wxImage::AddHandler(new wxPNGHandler); //ownership passed + wxImage::AddHandler(new wxPNGHandler/*ownership passed*/); //activate support for .png files //do we need xBRZ scaling for high quality DPI images? const int hqScale = std::clamp(numeric::intDivCeil(fastFromDIP(1000), 1000), 1, xbrz::SCALE_FACTOR_MAX); diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index ccbd05e2..2df24b6e 100644 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -169,6 +169,7 @@ wxImage zen::stackImages(const wxImage& img1, const wxImage& img2, ImageStackLay wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const wxColor& col, ImageStackAlignment textAlign) { wxMemoryDC dc; //the context used for bitmaps + setScaleFactor(dc, getDisplayScaleFactor()); 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 @@ -187,8 +188,9 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const return wxNullImage; wxBitmap newBitmap(maxWidth, lineHeight * lineInfo.size()); //seems we don't need to pass 24-bit depth here even for high-contrast color schemes + newBitmap.SetScaleFactor(getDisplayScaleFactor()); { - dc.SelectObject(newBitmap); + dc.SelectObject(newBitmap); //copies scale factor from wxBitmap ZEN_ON_SCOPE_EXIT(dc.SelectObject(wxNullBitmap)); if (wxTheApp->GetLayoutDirection() == wxLayout_RightToLeft) diff --git a/wx+/image_tools.h b/wx+/image_tools.h index c2fed4c1..bfd107d0 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -61,13 +61,6 @@ wxImage resizeCanvas(const wxImage& img, wxSize newSize, int alignment); -inline -int getDefaultMenuIconSize() -{ - return fastFromDIP(20); -} - - diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index c163b037..0a4c75c0 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -34,18 +34,20 @@ void setBestInitialSize(wxRichTextCtrl& ctrl, const wxString& text, wxSize maxSi if (maxSize.x <= scrollbarWidth) //implicitly checks for non-zero, too! return; + const int rowGap = 0; int maxLineWidth = 0; - int rowCount = 0; int rowHeight = 0; + int rowCount = 0; bool haveLineWrap = false; auto evalLineExtent = [&](const wxSize& sz) -> bool //return true when done { + assert(rowHeight == 0 || rowHeight == sz.y + rowGap); //all rows *should* have same height + rowHeight = std::max(rowHeight, sz.y + rowGap); maxLineWidth = std::max(maxLineWidth, sz.x); const int wrappedRows = numeric::intDivCeil(sz.x, maxSize.x - scrollbarWidth); //round up: consider line-wraps! rowCount += wrappedRows; - rowHeight = std::max(rowHeight, sz.y); //all rows *should* have same height if (wrappedRows > 1) haveLineWrap = true; @@ -68,20 +70,23 @@ void setBestInitialSize(wxRichTextCtrl& ctrl, const wxString& text, wxSize maxSi it = itEnd + 1; } -#if 1 //wxRichTextCtrl - const int rowGap = 0; - const int extraHeight = 0; -#else //wxTextCtrl - const int rowGap = 0; - const int extraHeight = 0; -#endif int extraWidth = 0; if (haveLineWrap) //compensate for trivial intDivCeil() not... extraWidth += ctrl.GetTextExtent(L"FreeFileSync").x / 2; //...understanding line wrap algorithm - const wxSize bestSize(std::min(maxLineWidth, maxSize.x) + extraWidth, - std::min(rowCount * (rowHeight + rowGap) + extraHeight, maxSize.y)); + const wxSize bestSize(std::min(maxLineWidth + scrollbarWidth /*1*/+ extraWidth, maxSize.x), + std::min(rowHeight * (rowCount + 1 /*2*/), maxSize.y)); + //1: wxWidgets' layout algorithm sucks: e.g. shows scrollbar *nedlessly* => extra line wrap increases height => scrollbar suddenly *needed*: catch 22! + //2: add some vertical space just for looks (*instead* of using border gap)! Extra space needed anyway to avoid scrollbars on Windows (2 px) and macOS (11 px) + ctrl.SetMinSize(bestSize); //alas, SetMinClientSize() is just not working! +#if 0 + std::cout << "rowCount " << rowCount << "\n" << + "maxLineWidth " << maxLineWidth << "\n" << + "rowHeight " << rowHeight << "\n" << + "haveLineWrap " << haveLineWrap << "\n" << + "scrollbarWidth " << scrollbarWidth << "\n\n"; +#endif } } @@ -173,8 +178,7 @@ public: if (!cfg.textDetail.empty()) { - const wxString& text = trimCpy(cfg.textDetail) + L'\n'; //add empty line *instead* of using border space! - + const wxString& text = trimCpy(cfg.textDetail); setBestInitialSize(*m_richTextDetail, text, wxSize(maxWidth, maxHeight)); setTextWithUrls(*m_richTextDetail, text); } @@ -214,6 +218,26 @@ public: } //------------------------------------------------------------------------------ + + auto setButtonImage = [&](wxButton& button, ConfirmationButton3 btnType) + { + auto it = cfg.buttonImages.find(btnType); + if (it != cfg.buttonImages.end()) + setImage(button, it->second); //caveat: image + text at the same time not working on GTK < 2.6 + }; + setButtonImage(*m_buttonAccept, ConfirmationButton3::accept); + setButtonImage(*m_buttonAccept2, ConfirmationButton3::accept2); + setButtonImage(*m_buttonDecline, ConfirmationButton3::decline); + setButtonImage(*m_buttonCancel, ConfirmationButton3::cancel); + + + if (cfg.disabledButtons.contains(ConfirmationButton3::accept )) m_buttonAccept ->Disable(); + if (cfg.disabledButtons.contains(ConfirmationButton3::accept2)) m_buttonAccept2->Disable(); + if (cfg.disabledButtons.contains(ConfirmationButton3::decline)) m_buttonDecline->Disable(); + assert(!cfg.disabledButtons.contains(ConfirmationButton3::cancel)); + assert(!cfg.disabledButtons.contains(cfg.buttonToDisableWhenChecked)); + + StdButtons stdBtns; stdBtns.setAffirmative(m_buttonAccept); if (labelAccept.empty()) //notification dialog @@ -251,18 +275,12 @@ public: stdBtns.setAffirmativeAll(m_buttonAccept2); } } + //set std order after button visibility was set + setStandardButtonLayout(*bSizerStdButtons, stdBtns); - if (cfg.disabledButtons.contains(ConfirmationButton3::accept )) m_buttonAccept ->Disable(); - if (cfg.disabledButtons.contains(ConfirmationButton3::accept2)) m_buttonAccept2->Disable(); - if (cfg.disabledButtons.contains(ConfirmationButton3::decline)) m_buttonDecline->Disable(); - assert(!cfg.disabledButtons.contains(ConfirmationButton3::cancel)); - assert(!cfg.disabledButtons.contains(cfg.buttonToDisableWhenChecked)); updateGui(); - //set std order after button visibility was set - setStandardButtonLayout(*bSizerStdButtons, stdBtns); - GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() Center(); //needs to be re-applied after a dialog size change! diff --git a/wx+/popup_dlg.h b/wx+/popup_dlg.h index 12c19c14..ca4a7591 100644 --- a/wx+/popup_dlg.h +++ b/wx+/popup_dlg.h @@ -7,7 +7,8 @@ #ifndef POPUP_DLG_H_820780154723456 #define POPUP_DLG_H_820780154723456 -#include <set> +#include <unordered_set> +#include <unordered_map> #include <zen/zstring.h> #include <wx/window.h> #include <wx/bitmap.h> @@ -19,8 +20,6 @@ namespace zen //parent window, optional: support correct dialog placement above parent on multiple monitor systems //this module requires error, warning and info image files in Icons.zip, see <wx+/image_resources.h> -struct PopupDialogCfg; - enum class DialogInfoType { info, @@ -53,6 +52,8 @@ enum class QuestionButton2 no = static_cast<int>(ConfirmationButton3::decline), }; +struct PopupDialogCfg; + void showNotificationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg); ConfirmationButton showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept); ConfirmationButton2 showConfirmationDialog(wxWindow* parent, DialogInfoType type, const PopupDialogCfg& cfg, const wxString& labelAccept, const wxString& labelAccept2); @@ -69,6 +70,7 @@ struct PopupDialogCfg PopupDialogCfg& setMainInstructions (const wxString& label) { textMain = label; return *this; } //set at least one of these! PopupDialogCfg& setDetailInstructions(const wxString& label) { textDetail = label; return *this; } // PopupDialogCfg& disableButton(ConfirmationButton3 button) { disabledButtons.insert(button); return *this; } + PopupDialogCfg& setButtonImage(ConfirmationButton3 button, const wxImage& img) { buttonImages.emplace(button, img); return *this; } PopupDialogCfg& alertWhenPending(const Zstring& soundFilePath) { soundFileAlertPending = soundFilePath; return *this; } PopupDialogCfg& setCheckBox(bool& value, const wxString& label, ConfirmationButton3 disableWhenChecked = ConfirmationButton3::cancel) { @@ -85,7 +87,8 @@ private: wxString title; wxString textMain; wxString textDetail; - std::set<ConfirmationButton3> disabledButtons; + std::unordered_set<ConfirmationButton3> disabledButtons; + std::unordered_map<ConfirmationButton3, wxImage> buttonImages; Zstring soundFileAlertPending; bool* checkBoxValue = nullptr; //in/out wxString checkBoxLabel; @@ -64,11 +64,15 @@ void drawBitmapRtlMirror(wxDC& dc, const wxImage& img, const wxRect& rect, int a return impl::drawBitmapAligned(dc, img, rect, alignment); case wxLayout_RightToLeft: + if (rect.GetWidth() > 0 && rect.GetHeight() > 0) { - 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); + if (!buffer || buffer->GetSize() != rect.GetSize()) //[!] since we do a mirror, width needs to match exactly! + buffer.emplace(rect.GetSize()); - wxMemoryDC memDc(*buffer); + if (buffer->GetScaleFactor() != dc.GetContentScaleFactor()) //needed here? + buffer->SetScaleFactor(dc.GetContentScaleFactor()); // + + wxMemoryDC memDc(*buffer); //copies scale factor from wxBitmap memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction! impl::drawBitmapAligned(memDc, img, wxRect(0, 0, rect.width, rect.height), alignment); diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h index 72756041..fbbee1b7 100644 --- a/wx+/std_button_layout.h +++ b/wx+/std_button_layout.h @@ -32,6 +32,20 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons = StdB //sizer width will change! => call wxWindow::Fit and wxWindow::Layout +inline +int getDefaultMenuIconSize() +{ + return fastFromDIP(20); +} + + +inline +int getDefaultButtonHeight() +{ + const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); //buffered by wxWidgets + return std::max(defaultHeight, fastFromDIP(31)); //default button height is much too small => increase! +} + @@ -103,8 +117,7 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) if (btn) { assert(btn->GetMinSize().GetHeight() == -1); //let OS or this routine do the sizing! note: OS X does not allow changing the (visible!) button height! - const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); //buffered by wxWidgets - btn->SetMinSize({-1, std::max(defaultHeight, fastFromDIP(31))}); //default button height is much too small => increase! + btn->SetMinSize({-1, getDefaultButtonHeight()}); if (settingFirstButton) settingFirstButton = false; |