diff options
Diffstat (limited to 'wx+')
-rw-r--r-- | wx+/graph.cpp | 66 | ||||
-rw-r--r-- | wx+/graph.h | 107 | ||||
-rw-r--r-- | wx+/grid.cpp | 20 | ||||
-rw-r--r-- | wx+/image_tools.h | 2 | ||||
-rw-r--r-- | wx+/no_flicker.h | 81 | ||||
-rw-r--r-- | wx+/popup_dlg.cpp | 35 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.cpp | 7 | ||||
-rw-r--r-- | wx+/popup_dlg_generated.h | 4 | ||||
-rw-r--r-- | wx+/window_tools.h (renamed from wx+/focus.h) | 96 |
9 files changed, 301 insertions, 117 deletions
diff --git a/wx+/graph.cpp b/wx+/graph.cpp index 7bd67504..bb320db6 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -11,7 +11,6 @@ #include <zen/basic_math.h> #include <zen/scope_guard.h> #include <zen/perf.h> -#include "dc.h" using namespace zen; @@ -196,7 +195,7 @@ void drawYLabel(wxDC& dc, double yMin, double yMax, int blockCount, const Conver } -void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, Graph2D::PosCorner pos, const wxColor& colorText, const wxColor& colorBack) +void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, GraphCorner pos, const wxColor& colorText, const wxColor& colorBack) { if (txt.empty()) return; @@ -208,15 +207,15 @@ void drawCornerText(wxDC& dc, const wxRect& graphArea, const wxString& txt, Grap wxPoint drawPos = graphArea.GetTopLeft(); switch (pos) { - case Graph2D::CORNER_TOP_LEFT: + case GraphCorner::topL: break; - case Graph2D::CORNER_TOP_RIGHT: + case GraphCorner::topR: drawPos.x += graphArea.width - boxExtent.GetWidth(); break; - case Graph2D::CORNER_BOTTOM_LEFT: + case GraphCorner::bottomL: drawPos.y += graphArea.height - boxExtent.GetHeight(); break; - case Graph2D::CORNER_BOTTOM_RIGHT: + case GraphCorner::bottomR: drawPos.x += graphArea.width - boxExtent.GetWidth(); drawPos.y += graphArea.height - boxExtent.GetHeight(); break; @@ -512,13 +511,6 @@ void Graph2D::onMouseCaptureLost(wxMouseCaptureLostEvent& event) } -void Graph2D::setCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca) -{ - curves_.clear(); - addCurve(data, ca); -} - - void Graph2D::addCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca) { CurveAttributes newAttr = ca; @@ -553,35 +545,35 @@ void Graph2D::render(wxDC& dc) const int xLabelPosY = clientRect.y; int yLabelPosX = clientRect.x; - switch (attr_.labelposX) + switch (attr_.xLabelpos) { - case LABEL_X_TOP: + case XLabelPos::none: + break; + case XLabelPos::top: graphArea.y += xLabelHeight; graphArea.height -= xLabelHeight; break; - case LABEL_X_BOTTOM: + case XLabelPos::bottom: xLabelPosY += clientRect.height - xLabelHeight; graphArea.height -= xLabelHeight; break; - case LABEL_X_NONE: - break; } - switch (attr_.labelposY) + switch (attr_.yLabelpos) { - case LABEL_Y_LEFT: + case YLabelPos::none: + break; + case YLabelPos::left: graphArea.x += yLabelWidth; graphArea.width -= yLabelWidth; break; - case LABEL_Y_RIGHT: + case YLabelPos::right: yLabelPosX += clientRect.width - yLabelWidth; graphArea.width -= yLabelWidth; break; - case LABEL_Y_NONE: - break; } - assert(attr_.labelposX == LABEL_X_NONE || attr_.labelFmtX); - assert(attr_.labelposY == LABEL_Y_NONE || attr_.labelFmtY); + assert(attr_.xLabelpos == XLabelPos::none || attr_.labelFmtX); + assert(attr_.yLabelpos == YLabelPos::none || attr_.labelFmtY); //paint graph background (excluding label area) drawFilledRectangle(dc, graphArea, fastFromDIP(1), getBorderColor(), attr_.colorBack); @@ -614,7 +606,7 @@ void Graph2D::render(wxDC& dc) const int blockCountX = 0; //enlarge minX, maxX to a multiple of a "useful" block size - if (attr_.labelposX != LABEL_X_NONE && attr_.labelFmtX.get()) + if (attr_.xLabelpos != XLabelPos::none && attr_.labelFmtX.get()) blockCountX = widenRange(minX, maxX, //in/out graphArea.width, minimalBlockSizePx.GetWidth() * 7, @@ -638,7 +630,7 @@ void Graph2D::render(wxDC& dc) const if (!points.empty()) { //cut points outside visible x-range now in order to calculate height of visible line fragments only! - const bool doPolygonCut = curves_[index].second.fillMode == CurveAttributes::FILL_POLYGON; //impacts auto minY/maxY!! + const bool doPolygonCut = curves_[index].second.fillMode == CurveFillMode::polygon; //impacts auto minY/maxY!! cutPointsOutsideX(points, marker, minX, maxX, doPolygonCut); if (!attr_.minY || !attr_.maxY) @@ -656,7 +648,7 @@ void Graph2D::render(wxDC& dc) const { int blockCountY = 0; //enlarge minY, maxY to a multiple of a "useful" block size - if (attr_.labelposY != LABEL_Y_NONE && attr_.labelFmtY.get()) + if (attr_.yLabelpos != YLabelPos::none && attr_.labelFmtY.get()) blockCountY = widenRange(minY, maxY, //in/out graphArea.height, minimalBlockSizePx.GetHeight() * 3, @@ -676,7 +668,7 @@ void Graph2D::render(wxDC& dc) const auto& cp = curvePoints[index]; //add two artificial points to fill the curve area towards x-axis => do this before cutPointsOutsideY() to handle curve leaving upper bound - if (curves_[index].second.fillMode == CurveAttributes::FILL_CURVE) + if (curves_[index].second.fillMode == CurveFillMode::curve) if (!cp.empty()) { cp.emplace_back(CurvePoint{cp.back ().x, minY}); //add lower right and left corners @@ -689,7 +681,7 @@ void Graph2D::render(wxDC& dc) const //cut points outside visible y-range before calculating pixels: //1. realToScreenRound() deforms out-of-range values! //2. pixels that are grossly out of range can be a severe performance problem when drawing on the DC (Windows) - const bool doPolygonCut = curves_[index].second.fillMode != CurveAttributes::FILL_NONE; + const bool doPolygonCut = curves_[index].second.fillMode != CurveFillMode::none; cutPointsOutsideY(cp, oobMarker[index], minY, maxY, doPolygonCut); auto& dp = drawPoints[index]; @@ -731,7 +723,7 @@ void Graph2D::render(wxDC& dc) const //#################### begin drawing #################### //1. draw colored area under curves for (auto it = curves_.begin(); it != curves_.end(); ++it) - if (it->second.fillMode != CurveAttributes::FILL_NONE) + if (it->second.fillMode != CurveFillMode::none) if (const std::vector<wxPoint>& points = drawPoints[it - curves_.begin()]; points.size() >= 3) { @@ -746,6 +738,8 @@ void Graph2D::render(wxDC& dc) const std::vector<SelectionBlock> allSelections = oldSel_; if (activeSel_) allSelections.push_back(activeSel_->refSelection()); + + if (!allSelections.empty()) { //alpha channel not supported on wxMSW, so draw selection before curves wxDCBrushChanger dummy(dc, wxColor(168, 202, 236)); //light blue @@ -782,15 +776,15 @@ void Graph2D::render(wxDC& dc) const numeric::round(screenToY)) + graphAreaOrigin; switch (attr_.mouseSelMode) { - case SELECT_NONE: + case GraphSelMode::none: break; - case SELECT_RECTANGLE: - dc.DrawRectangle(wxRect(pixelFrom, pixelTo)); + case GraphSelMode::rect: + dc.DrawRectangle(wxRect(pixelFrom, pixelTo)); //wxRect considers area *including* both points break; - case SELECT_X_AXIS: + case GraphSelMode::x: dc.DrawRectangle(wxRect(wxPoint(pixelFrom.x, graphArea.y), wxPoint(pixelTo.x, graphArea.y + graphArea.height - 1))); break; - case SELECT_Y_AXIS: + case GraphSelMode::y: dc.DrawRectangle(wxRect(wxPoint(graphArea.x, pixelFrom.y), wxPoint(graphArea.x + graphArea.width - 1, pixelTo.y))); break; } diff --git a/wx+/graph.h b/wx+/graph.h index a6e99200..c843b09b 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -14,6 +14,7 @@ #include <wx/settings.h> #include <wx/bitmap.h> #include <zen/string_tools.h> +#include "dc.h" //elegant 2D graph as wxPanel specialization @@ -143,6 +144,44 @@ struct GraphSelectEvent : public wxEvent SelectionBlock selectBlock_; }; + +//------------------------------------------------------------------------------------------------------------ +enum class XLabelPos +{ + none, + top, + bottom, +}; + +enum class YLabelPos +{ + none, + left, + right, +}; + +enum class CurveFillMode +{ + none, + curve, + polygon +}; + +enum class GraphCorner +{ + topL, + topR, + bottomL, + bottomR, +}; + +enum class GraphSelMode +{ + none, + rect, + x, + y, +}; //------------------------------------------------------------------------------------------------------------ class Graph2D : public wxPanel @@ -160,8 +199,8 @@ public: public: CurveAttributes() {} //required by GCC CurveAttributes& setColor (const wxColor& col) { color = col; autoColor = false; return *this; } - CurveAttributes& fillCurveArea (const wxColor& col) { fillColor = col; fillMode = FILL_CURVE; return *this; } - CurveAttributes& fillPolygonArea(const wxColor& col) { fillColor = col; fillMode = FILL_POLYGON; return *this; } + CurveAttributes& fillCurveArea (const wxColor& col) { fillColor = col; fillMode = CurveFillMode::curve; return *this; } + CurveAttributes& fillPolygonArea(const wxColor& col) { fillColor = col; fillMode = CurveFillMode::polygon; return *this; } CurveAttributes& setLineWidth(size_t width) { lineWidth = static_cast<int>(width); return *this; } private: @@ -170,54 +209,17 @@ public: bool autoColor = true; wxColor color; - enum FillMode - { - FILL_NONE, - FILL_CURVE, - FILL_POLYGON - }; - - FillMode fillMode = FILL_NONE; + CurveFillMode fillMode = CurveFillMode::none; wxColor fillColor; - int lineWidth = 2; + int lineWidth = fastFromDIP(2); }; - void setCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca = CurveAttributes()); void addCurve(const std::shared_ptr<CurveData>& data, const CurveAttributes& ca = CurveAttributes()); + void clearCurves() { curves_.clear(); } static wxColor getBorderColor() { return { 130, 135, 144 }; } //medium grey, the same Win7 uses for other frame borders => not accessible! but no big deal... - enum PosLabelY - { - LABEL_Y_LEFT, - LABEL_Y_RIGHT, - LABEL_Y_NONE - }; - - enum PosLabelX - { - LABEL_X_TOP, - LABEL_X_BOTTOM, - LABEL_X_NONE - }; - - enum PosCorner - { - CORNER_TOP_LEFT, - CORNER_TOP_RIGHT, - CORNER_BOTTOM_LEFT, - CORNER_BOTTOM_RIGHT, - }; - - enum SelMode - { - SELECT_NONE, - SELECT_RECTANGLE, - SELECT_X_AXIS, - SELECT_Y_AXIS, - }; - class MainAttributes { public: @@ -229,27 +231,27 @@ public: MainAttributes& setAutoSize() { minX = maxX = minY = maxY = {}; return *this; } - MainAttributes& setLabelX(PosLabelX posX, int height = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr) + MainAttributes& setLabelX(XLabelPos posX, int height = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr) { - labelposX = posX; + xLabelpos = posX; if (height >= 0) xLabelHeight = height; if (newLabelFmt) labelFmtX = newLabelFmt; return *this; } - MainAttributes& setLabelY(PosLabelY posY, int width = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr) + MainAttributes& setLabelY(YLabelPos posY, int width = -1, std::shared_ptr<LabelFormatter> newLabelFmt = nullptr) { - labelposY = posY; + yLabelpos = posY; if (width >= 0) yLabelWidth = width; if (newLabelFmt) labelFmtY = newLabelFmt; return *this; } - MainAttributes& setCornerText(const wxString& txt, PosCorner pos) { cornerTexts[pos] = txt; return *this; } + MainAttributes& setCornerText(const wxString& txt, GraphCorner pos) { cornerTexts[pos] = txt; return *this; } //accessibility: always set both colors MainAttributes& setBaseColors(const wxColor& text, const wxColor& back) { colorText = text; colorBack = back; return *this; } - MainAttributes& setSelectionMode(SelMode mode) { mouseSelMode = mode; return *this; } + MainAttributes& setSelectionMode(GraphSelMode mode) { mouseSelMode = mode; return *this; } private: friend class Graph2D; @@ -260,25 +262,24 @@ public: std::optional<double> minY; //y-range to visualize std::optional<double> maxY; // - PosLabelX labelposX = LABEL_X_BOTTOM; + XLabelPos xLabelpos = XLabelPos::bottom; std::optional<int> xLabelHeight; std::shared_ptr<LabelFormatter> labelFmtX = std::make_shared<DecimalNumberFormatter>(); - PosLabelY labelposY = LABEL_Y_LEFT; + YLabelPos yLabelpos = YLabelPos::left; std::optional<int> yLabelWidth; std::shared_ptr<LabelFormatter> labelFmtY = std::make_shared<DecimalNumberFormatter>(); - std::map<PosCorner, wxString> cornerTexts; + std::map<GraphCorner, 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); - SelMode mouseSelMode = SELECT_RECTANGLE; + GraphSelMode mouseSelMode = GraphSelMode::rect; }; - void setAttributes(const MainAttributes& newAttr) { attr_ = newAttr; Refresh(); } MainAttributes getAttributes() const { return attr_; } diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 6eb25ac9..f7e9736f 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -374,17 +374,23 @@ private: !event.IsPageScroll()) { mouseRotateRemainder_ += -event.GetWheelRotation(); - const int rotations = mouseRotateRemainder_ / event.GetWheelDelta(); + int rotations = mouseRotateRemainder_ / event.GetWheelDelta(); mouseRotateRemainder_ -= rotations * event.GetWheelDelta(); + if (rotations == 0) //macOS generates tiny GetWheelRotation()! => don't allow! Always scroll a single row at least! + { + rotations = -numeric::sign(event.GetWheelRotation()); + mouseRotateRemainder_ = 0; + } + const int rowsDelta = rotations * event.GetLinesPerAction(); parent_.scrollDelta(0, rowsDelta); } else parent_.HandleOnMouseWheel(event); - onMouseMovement(event); - event.Skip(false); + onMouseMovement(event); + event.Skip(false); //if (!sendEventToParent(event)) // event.Skip(); @@ -1013,7 +1019,7 @@ private: { if (auto prov = refParent().getDataProvider()) { - onMouseMovement(event); //update highlight in obscure cases (e.g. right-click while context menu is open) + onMouseMovement(event); //update highlight in obscure cases (e.g. right-click while context menu is open) const wxPoint mousePos = GetPosition() + event.GetPosition(); const ptrdiff_t rowCount = refParent().getRowCount(); @@ -1070,7 +1076,7 @@ private: //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); + onMouseMovement(event); } event.Skip(); //allow changing focus } @@ -1134,10 +1140,10 @@ private: 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); + onMouseMovement(event); event.Skip(); //allow changing focus } diff --git a/wx+/image_tools.h b/wx+/image_tools.h index cd895c2e..5a799a8b 100644 --- a/wx+/image_tools.h +++ b/wx+/image_tools.h @@ -69,7 +69,7 @@ wxImage getTransparentPixel() inline int getDefaultMenuIconSize() { - return fastFromDIP(24); + return fastFromDIP(20); } diff --git a/wx+/no_flicker.h b/wx+/no_flicker.h index 03969c00..53e47bb5 100644 --- a/wx+/no_flicker.h +++ b/wx+/no_flicker.h @@ -7,13 +7,18 @@ #ifndef NO_FLICKER_H_893421590321532 #define NO_FLICKER_H_893421590321532 +#include <zen/string_tools.h> +#include <zen/scope_guard.h> #include <wx/textctrl.h> #include <wx/stattext.h> +#include <wx/richtext/richtextctrl.h> +#include <wx/wupdlock.h> namespace zen { -inline +namespace +{ void setText(wxTextCtrl& control, const wxString& newText, bool* additionalLayoutChange = nullptr) { const wxString& label = control.GetValue(); //perf: don't call twice! @@ -24,7 +29,7 @@ void setText(wxTextCtrl& control, const wxString& newText, bool* additionalLayou control.ChangeValue(newText); } -inline + void setText(wxStaticText& control, wxString newText, bool* additionalLayoutChange = nullptr) { @@ -35,6 +40,78 @@ void setText(wxStaticText& control, wxString newText, bool* additionalLayoutChan if (label != newText) control.SetLabel(newText); } + + +void setTextWithUrls(wxRichTextCtrl& richCtrl, const wxString& newText) +{ + enum class BlockType + { + text, + url, + }; + std::vector<std::pair<BlockType, wxString>> blocks; + + for (auto it = newText.begin();;) + { + const wchar_t urlPrefix[] = L"https://"; + const auto itUrl = std::search(it, newText.end(), + urlPrefix, urlPrefix + strLength(urlPrefix)); + if (it != itUrl) + blocks.emplace_back(BlockType::text, wxString(it, itUrl)); + + if (itUrl == newText.end()) + break; + + auto itUrlEnd = std::find_if(itUrl, newText.end(), [](wchar_t c) { return isWhiteSpace(c); }); + blocks.emplace_back(BlockType::url, wxString(itUrl, itUrlEnd)); + it = itUrlEnd; + } + richCtrl.BeginSuppressUndo(); + ZEN_ON_SCOPE_EXIT(richCtrl.EndSuppressUndo()); + + //fix mouse scroll speed: why the FUCK is this even necessary! + richCtrl.SetLineHeight(richCtrl.GetCharHeight()); + + //get rid of margins and space between text blocks/"paragraphs" + richCtrl.SetMargins({0, 0}); + richCtrl.BeginParagraphSpacing(0, 0); + ZEN_ON_SCOPE_EXIT(richCtrl.EndParagraphSpacing()); + + richCtrl.Clear(); + + if (std::any_of(blocks.begin(), blocks.end(), [](const auto& item) { return item.first == BlockType::url; })) + { + wxRichTextAttr urlStyle; + urlStyle.SetTextColour(*wxBLUE); + urlStyle.SetFontUnderlined(true); + + for (const auto& [type, text] : blocks) + switch (type) + { + case BlockType::text: + richCtrl.WriteText(text); + break; + + case BlockType::url: + richCtrl.BeginStyle(urlStyle); + ZEN_ON_SCOPE_EXIT(richCtrl.EndStyle()); + richCtrl.BeginURL(text); + ZEN_ON_SCOPE_EXIT(richCtrl.EndURL()); + richCtrl.WriteText(text); + break; + } + + //register only once! => use a global function pointer, so that Unbind() works correctly: + using LaunchUrlFun = void(*)(wxTextUrlEvent& event); + static const LaunchUrlFun launchUrl = [](wxTextUrlEvent& event) { wxLaunchDefaultBrowser(event.GetString()); }; + + [[maybe_unused]] const bool unbindOk = richCtrl.Unbind(wxEVT_TEXT_URL, launchUrl); + richCtrl.Bind(wxEVT_TEXT_URL, launchUrl); + } + else + richCtrl.WriteText(newText); +} +} } #endif //NO_FLICKER_H_893421590321532 diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index 541a787c..0a1ff2a0 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -7,6 +7,7 @@ #include "popup_dlg.h" #include <wx/app.h> #include <wx/display.h> +#include "no_flicker.h" #include "font_size.h" #include "image_resources.h" #include "popup_dlg_generated.h" @@ -19,7 +20,7 @@ using namespace zen; namespace { -void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize) +void setBestInitialSize(wxRichTextCtrl& ctrl, const wxString& text, wxSize maxSize) { const int scrollbarWidth = fastFromDIP(25); /*not only scrollbar, but also left/right padding (on macOS)! better use slightly larger than exact value (Windows: 17, Linux(CentOS): 14, macOS: 25) @@ -27,19 +28,21 @@ void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize) if (maxSize.x <= scrollbarWidth) //implicitly checks for non-zero, too! return; - maxSize.x -= scrollbarWidth; - int bestWidth = 0; + int maxLineWidth = 0; int rowCount = 0; int rowHeight = 0; + bool haveLineWrap = false; auto evalLineExtent = [&](const wxSize& sz) -> bool //return true when done { - if (sz.x > bestWidth) - bestWidth = std::min(maxSize.x, sz.x); + maxLineWidth = std::max(maxLineWidth, sz.x); - rowCount += numeric::integerDivideRoundUp(sz.x, maxSize.x); //integer round up: consider line-wraps! + const int wrappedRows = numeric::integerDivideRoundUp(sz.x, maxSize.x - scrollbarWidth); //integer round up: consider line-wraps! + rowCount += wrappedRows; rowHeight = std::max(rowHeight, sz.y); //all rows *should* have same height + if (wrappedRows > 1) + haveLineWrap = true; return rowCount * rowHeight >= maxSize.y; }; @@ -60,8 +63,19 @@ void setBestInitialSize(wxTextCtrl& ctrl, const wxString& text, wxSize maxSize) it = itEnd + 1; } +#if 1 //wxRichTextCtrl const int rowGap = 0; - const wxSize bestSize(bestWidth + scrollbarWidth, std::min(rowCount * (rowHeight + rowGap), maxSize.y)); + const int extraHeight = 0; +#else //wxTextCtrl + const int rowGap = 0; + const int extraHeight = 0; +#endif + int extraWidth = 0; + if (haveLineWrap) //compensate for trivial integerDivideRoundUp() 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)); ctrl.SetMinSize(bestSize); //alas, SetMinClientSize() is just not working! } } @@ -79,7 +93,6 @@ public: buttonToDisableWhenChecked_(cfg.buttonToDisableWhenChecked) { - if (type != DialogInfoType::info) try { @@ -163,11 +176,11 @@ public: text += L'\n'; text += trimCpy(cfg.textDetail) + L'\n'; //add empty top/bottom lines *instead* of using border space! - setBestInitialSize(*m_textCtrlTextDetail, text, wxSize(maxWidth, maxHeight)); - m_textCtrlTextDetail->ChangeValue(text); + setBestInitialSize(*m_richTextDetail, text, wxSize(maxWidth, maxHeight)); + setTextWithUrls(*m_richTextDetail, text); } else - m_textCtrlTextDetail->Hide(); + m_richTextDetail->Hide(); if (checkBoxValue_) { diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp index 8933b135..22e3d02c 100644 --- a/wx+/popup_dlg_generated.cpp +++ b/wx+/popup_dlg_generated.cpp @@ -36,11 +36,8 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, con m_staticTextMain->Wrap( -1 ); bSizer16->Add( m_staticTextMain, 0, wxRIGHT, 10 ); - - 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_richTextDetail = new wxRichTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY|wxBORDER_NONE|wxVSCROLL|wxWANTS_CHARS ); + bSizer16->Add( m_richTextDetail, 1, wxEXPAND, 5 ); bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h index 87474708..93842d17 100644 --- a/wx+/popup_dlg_generated.h +++ b/wx+/popup_dlg_generated.h @@ -20,7 +20,7 @@ #include <wx/settings.h> #include <wx/string.h> #include <wx/stattext.h> -#include <wx/textctrl.h> +#include <wx/richtext/richtextctrl.h> #include <wx/sizer.h> #include <wx/panel.h> #include <wx/statline.h> @@ -44,7 +44,7 @@ protected: wxPanel* m_panel33; wxStaticBitmap* m_bitmapMsgType; wxStaticText* m_staticTextMain; - wxTextCtrl* m_textCtrlTextDetail; + wxRichTextCtrl* m_richTextDetail; wxStaticLine* m_staticline6; wxCheckBox* m_checkBoxCustom; wxBoxSizer* bSizerStdButtons; diff --git a/wx+/focus.h b/wx+/window_tools.h index 2920828f..73faf272 100644 --- a/wx+/focus.h +++ b/wx+/window_tools.h @@ -8,6 +8,7 @@ #define FOCUS_1084731021985757843 #include <wx/toplevel.h> +#include <wx/display.h> namespace zen @@ -87,6 +88,101 @@ private: //don't store wxWindow* which may be dangling during ~FocusPreserver()! //test: click on delete folder pair and immediately press F5 => focus window (= FP del button) is defer-deleted during sync }; + + +namespace +{ +void setInitialWindowSize(wxTopLevelWindow& topWin, wxSize size, std::optional<wxPoint> pos, bool isMaximized, wxSize defaultSize) +{ + wxSize newSize = defaultSize; + std::optional<wxPoint> newPos; + //set dialog size and position: + // - width/height are invalid if the window is minimized (eg x,y = -32000; width = 160, height = 28) + // - multi-monitor setup: dialog may be placed on second monitor which is currently turned off + if (size.GetWidth () > 0 && + size.GetHeight() > 0) + { + if (pos) + { + //calculate how much of the dialog will be visible on screen + const int dlgArea = size.GetWidth() * size.GetHeight(); + int dlgAreaMaxVisible = 0; + + const int monitorCount = wxDisplay::GetCount(); + for (int i = 0; i < monitorCount; ++i) + { + wxRect overlap = wxDisplay(i).GetClientArea().Intersect(wxRect(*pos, size)); + dlgAreaMaxVisible = std::max(dlgAreaMaxVisible, overlap.GetWidth() * overlap.GetHeight()); + } + + if (dlgAreaMaxVisible > 0.1 * dlgArea //at least 10% of the dialog should be visible! + ) + { + newSize = size; + newPos = pos; + } + } + else + newSize = size; + } + + //old comment: "wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize + // => use wxWindow::SetClientSize instead (for the record: no such issue on Windows/macOS) + //2018-10-15: Weird new problem on CentOS/Ubuntu: SetClientSize() + SetPosition() fail to set correct dialog *position*, but SetSize() + SetPosition() do! + // => old issues with SetSize() seem to be gone... => revert to SetSize() + if (newPos) + topWin.SetSize(wxRect(*newPos, newSize)); + else + { + topWin.SetSize(newSize); + topWin.Center(); + } + + if (isMaximized) //no real need to support both maximize and full screen functions + { + topWin.Maximize(true); + } +} + + +struct WindowLayoutWeak +{ + std::optional<wxSize> size; + std::optional<wxPoint> pos; + bool isMaximized = false; +}; +//destructive! changes window size! +WindowLayoutWeak getWindowSizeBeforeClose(wxTopLevelWindow& topWin) +{ + //we need to portably retrieve non-iconized, non-maximized size and position + // non-portable: Win32 GetWindowPlacement(); wxWidgets take: wxTopLevelWindow::RestoreToGeometry() + if (topWin.IsIconized()) + topWin.Iconize(false); + + WindowLayoutWeak layout; + if (topWin.IsMaximized()) //evaluate AFTER uniconizing! + { + topWin.Maximize(false); + layout.isMaximized = true; + } + + layout.size = topWin.GetSize(); + layout.pos = topWin.GetPosition(); + + if (layout.isMaximized) + if (!topWin.IsShown() //=> Win: can't trust size GetSize()/GetPosition(): still at full screen size! + //wxGTK: returns full screen size and strange position (65/-4) + //OS X 10.9 (but NO issue on 10.11!) returns full screen size and strange position (0/-22) + || layout.pos->y < 0 + ) + { + layout.size = std::nullopt; + layout.pos = std::nullopt; + } + + return layout; +} +} } #endif //FOCUS_1084731021985757843 |