summaryrefslogtreecommitdiff
path: root/wx+
diff options
context:
space:
mode:
Diffstat (limited to 'wx+')
-rw-r--r--wx+/graph.cpp66
-rw-r--r--wx+/graph.h107
-rw-r--r--wx+/grid.cpp20
-rw-r--r--wx+/image_tools.h2
-rw-r--r--wx+/no_flicker.h81
-rw-r--r--wx+/popup_dlg.cpp35
-rw-r--r--wx+/popup_dlg_generated.cpp7
-rw-r--r--wx+/popup_dlg_generated.h4
-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
bgstack15