1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************
#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
{
namespace
{
void setText(wxTextCtrl& control, const wxString& newText, bool* additionalLayoutChange = nullptr)
{
const wxString& label = control.GetValue(); //perf: don't call twice!
if (additionalLayoutChange && !*additionalLayoutChange) //never revert from true to false!
*additionalLayoutChange = label.length() != newText.length(); //avoid screen flicker: update layout only when necessary
if (label != newText)
control.ChangeValue(newText);
}
void setText(wxStaticText& control, const wxString& newText, bool* additionalLayoutChange = nullptr)
{
//wxControl::EscapeMnemonics() (& -> &&) => wxControl::GetLabelText/SetLabelText
//e.g. "filenames in the sync progress dialog": https://sourceforge.net/p/freefilesync/bugs/279/
const wxString& label = control.GetLabelText(); //perf: don't call twice!
if (additionalLayoutChange && !*additionalLayoutChange)
*additionalLayoutChange = label.length() != newText.length(); //avoid screen flicker: update layout only when necessary
if (label != newText)
control.SetLabelText(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();
wxRichTextAttr urlStyle;
urlStyle.SetTextColour(*wxBLUE);
urlStyle.SetFontUnderlined(true);
for (auto& [type, text] : blocks)
switch (type)
{
case BlockType::text:
if (endsWith(text, L"\n\n")) //bug: multiple newlines before a URL are condensed to only one;
//Why? fuck knows why! no such issue with double newlines *after* URL => hack this shit
text.RemoveLast().Append(ZERO_WIDTH_SPACE).Append(L'\n');
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;
}
if (std::any_of(blocks.begin(), blocks.end(), [](const auto& item) { return item.first == BlockType::url; }))
{
//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);
}
}
}
}
#endif //NO_FLICKER_H_893421590321532
|