Skip to content

Commit 4998779

Browse files
authored
Warn when font isn't found and another is chosen (#8207)
Display a warning message when the DirectX renderer resolves a font that isn't the one you selected to warn that it couldn't be found. Also I wrote the dialog event chain out of `TermControl` to be reusable in the future for other messages the control might want to tell a host about and various levels. ## Validation Steps Performed - Manual validation, setting bad font name, fixing font name with `settings.json`. Closes #1017
1 parent 3a57378 commit 4998779

File tree

10 files changed

+199
-58
lines changed

10 files changed

+199
-58
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
renamer
1+
renamer

.github/actions/spell-check/expect/expect.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2829,4 +2829,4 @@ zy
28292829
AAAAABBBBBBCCC
28302830
AAAAA
28312831
BBBBBCCC
2832-
abcd
2832+
abcd

src/cascadia/TerminalApp/Resources/en-US/Resources.resw

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<root>
3-
<!--
4-
Microsoft ResX Schema
5-
3+
<!--
4+
Microsoft ResX Schema
5+
66
Version 2.0
7-
8-
The primary goals of this format is to allow a simple XML format
9-
that is mostly human readable. The generation and parsing of the
10-
various data types are done through the TypeConverter classes
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
1111
associated with the data types.
12-
12+
1313
Example:
14-
14+
1515
... ado.net/XML headers & schema ...
1616
<resheader name="resmimetype">text/microsoft-resx</resheader>
1717
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
2626
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
2727
<comment>This is a comment</comment>
2828
</data>
29-
30-
There are any number of "resheader" rows that contain simple
29+
30+
There are any number of "resheader" rows that contain simple
3131
name/value pairs.
32-
33-
Each data row contains a name, and value. The row also contains a
34-
type or mimetype. Type corresponds to a .NET class that support
35-
text/value conversion through the TypeConverter architecture.
36-
Classes that don't support this are serialized and stored with the
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
3737
mimetype set.
38-
39-
The mimetype is used for serialized objects, and tells the
40-
ResXResourceReader how to depersist the object. This is currently not
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
4141
extensible. For a given mimetype the value must be set accordingly:
42-
43-
Note - application/x-microsoft.net.object.binary.base64 is the format
44-
that the ResXResourceWriter will generate, however the reader can
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
4545
read any of the formats listed below.
46-
46+
4747
mimetype: application/x-microsoft.net.object.binary.base64
48-
value : The object must be serialized with
48+
value : The object must be serialized with
4949
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
5050
: and then encoded with base64 encoding.
51-
51+
5252
mimetype: application/x-microsoft.net.object.soap.base64
53-
value : The object must be serialized with
53+
value : The object must be serialized with
5454
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
5555
: and then encoded with base64 encoding.
5656
5757
mimetype: application/x-microsoft.net.object.bytearray.base64
58-
value : The object must be serialized into a byte array
58+
value : The object must be serialized into a byte array
5959
: using a System.ComponentModel.TypeConverter
6060
: and then encoded with base64 encoding.
6161
-->
@@ -488,4 +488,19 @@
488488
<data name="ParentCommandBackButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
489489
<value>Back</value>
490490
</data>
491-
</root>
491+
<data name="ControlNoticeDialog.PrimaryButtonText" xml:space="preserve">
492+
<value>OK</value>
493+
</data>
494+
<data name="NoticeDebug" xml:space="preserve">
495+
<value>Debug</value>
496+
</data>
497+
<data name="NoticeError" xml:space="preserve">
498+
<value>Error</value>
499+
</data>
500+
<data name="NoticeInfo" xml:space="preserve">
501+
<value>Information</value>
502+
</data>
503+
<data name="NoticeWarning" xml:space="preserve">
504+
<value>Warning</value>
505+
</data>
506+
</root>

src/cascadia/TerminalApp/TerminalPage.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,8 @@ namespace winrt::TerminalApp::implementation
11211121
// - hostingTab: The Tab that's hosting this TermControl instance
11221122
void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab)
11231123
{
1124+
term.RaiseNotice({ this, &TerminalPage::_ControlNoticeRaisedHandler });
1125+
11241126
// Add an event handler when the terminal's selection wants to be copied.
11251127
// When the text buffer data is retrieved, we'll copy the data into the Clipboard
11261128
term.CopyToClipboard({ this, &TerminalPage::_CopyToClipboardHandler });
@@ -1928,6 +1930,48 @@ namespace winrt::TerminalApp::implementation
19281930
}
19291931
}
19301932

1933+
void TerminalPage::_ControlNoticeRaisedHandler(const IInspectable /*sender*/, const Microsoft::Terminal::TerminalControl::NoticeEventArgs eventArgs)
1934+
{
1935+
winrt::hstring message = eventArgs.Message();
1936+
1937+
winrt::hstring title;
1938+
1939+
switch (eventArgs.Level())
1940+
{
1941+
case TerminalControl::NoticeLevel::Debug:
1942+
title = RS_(L"NoticeDebug"); //\xebe8
1943+
break;
1944+
case TerminalControl::NoticeLevel::Info:
1945+
title = RS_(L"NoticeInfo"); // \xe946
1946+
break;
1947+
case TerminalControl::NoticeLevel::Warning:
1948+
title = RS_(L"NoticeWarning"); //\xe7ba
1949+
break;
1950+
case TerminalControl::NoticeLevel::Error:
1951+
title = RS_(L"NoticeError"); //\xe783
1952+
break;
1953+
}
1954+
1955+
_ShowControlNoticeDialog(title, message);
1956+
}
1957+
1958+
void TerminalPage::_ShowControlNoticeDialog(const winrt::hstring& title, const winrt::hstring& message)
1959+
{
1960+
if (auto presenter{ _dialogPresenter.get() })
1961+
{
1962+
// FindName needs to be called first to actually load the xaml object
1963+
auto controlNoticeDialog = FindName(L"ControlNoticeDialog").try_as<WUX::Controls::ContentDialog>();
1964+
1965+
ControlNoticeDialog().Title(winrt::box_value(title));
1966+
1967+
// Insert the message
1968+
NoticeMessage().Text(message);
1969+
1970+
// Show the dialog
1971+
presenter.ShowDialog(controlNoticeDialog);
1972+
}
1973+
}
1974+
19311975
// Method Description:
19321976
// - Copy text from the focused terminal to the Windows Clipboard
19331977
// Arguments:

src/cascadia/TerminalApp/TerminalPage.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ namespace winrt::TerminalApp::implementation
197197

198198
void _PasteText();
199199

200+
void _ControlNoticeRaisedHandler(const IInspectable sender, const Microsoft::Terminal::TerminalControl::NoticeEventArgs eventArgs);
201+
void _ShowControlNoticeDialog(const winrt::hstring& title, const winrt::hstring& message);
202+
200203
fire_and_forget _LaunchSettings(const Microsoft::Terminal::Settings::Model::SettingsTarget target);
201204

202205
void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs);

src/cascadia/TerminalApp/TerminalPage.xaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ the MIT License. See LICENSE in the project root for license information. -->
6969
DefaultButton="Primary">
7070
</ContentDialog>
7171

72+
<ContentDialog
73+
x:Load="False"
74+
x:Name="ControlNoticeDialog"
75+
x:Uid="ControlNoticeDialog"
76+
DefaultButton="Primary">
77+
<TextBlock IsTextSelectionEnabled="True" TextWrapping="WrapWholeWords">
78+
<Run x:Name="NoticeMessage"/>
79+
</TextBlock>
80+
</ContentDialog>
81+
7282
<ContentDialog
7383
x:Load="False"
7484
x:Name="CouldNotOpenUriDialog"

src/cascadia/TerminalControl/Resources/en-US/Resources.resw

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<root>
3-
<!--
4-
Microsoft ResX Schema
5-
3+
<!--
4+
Microsoft ResX Schema
5+
66
Version 2.0
7-
8-
The primary goals of this format is to allow a simple XML format
9-
that is mostly human readable. The generation and parsing of the
10-
various data types are done through the TypeConverter classes
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
1111
associated with the data types.
12-
12+
1313
Example:
14-
14+
1515
... ado.net/XML headers & schema ...
1616
<resheader name="resmimetype">text/microsoft-resx</resheader>
1717
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
2626
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
2727
<comment>This is a comment</comment>
2828
</data>
29-
30-
There are any number of "resheader" rows that contain simple
29+
30+
There are any number of "resheader" rows that contain simple
3131
name/value pairs.
32-
33-
Each data row contains a name, and value. The row also contains a
34-
type or mimetype. Type corresponds to a .NET class that support
35-
text/value conversion through the TypeConverter architecture.
36-
Classes that don't support this are serialized and stored with the
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
3737
mimetype set.
38-
39-
The mimetype is used for serialized objects, and tells the
40-
ResXResourceReader how to depersist the object. This is currently not
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
4141
extensible. For a given mimetype the value must be set accordingly:
42-
43-
Note - application/x-microsoft.net.object.binary.base64 is the format
44-
that the ResXResourceWriter will generate, however the reader can
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
4545
read any of the formats listed below.
46-
46+
4747
mimetype: application/x-microsoft.net.object.binary.base64
48-
value : The object must be serialized with
48+
value : The object must be serialized with
4949
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
5050
: and then encoded with base64 encoding.
51-
51+
5252
mimetype: application/x-microsoft.net.object.soap.base64
53-
value : The object must be serialized with
53+
value : The object must be serialized with
5454
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
5555
: and then encoded with base64 encoding.
5656
5757
mimetype: application/x-microsoft.net.object.bytearray.base64
58-
value : The object must be serialized into a byte array
58+
value : The object must be serialized into a byte array
5959
: using a System.ComponentModel.TypeConverter
6060
: and then encoded with base64 encoding.
6161
-->
@@ -178,4 +178,12 @@
178178
<data name="HowToOpenRun.Text" xml:space="preserve">
179179
<value>Ctrl+Click to follow link</value>
180180
</data>
181-
</root>
181+
<data name="NoticeFontNotFound" xml:space="preserve">
182+
<value>Unable to find the selected font "{0}".
183+
184+
"{1}" has been selected instead.
185+
186+
Please either install the missing font or choose another one.</value>
187+
<comment>0 and 1 are names of fonts provided by the user and system respectively.</comment>
188+
</data>
189+
</root>

src/cascadia/TerminalControl/TermControl.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,6 +1987,33 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
19871987
// actually fail. We need a way to gracefully fallback.
19881988
_renderer->TriggerFontChange(newDpi, _desiredFont, _actualFont);
19891989

1990+
// If the actual font isn't what was requested...
1991+
if (_actualFont.GetFaceName() != _desiredFont.GetFaceName())
1992+
{
1993+
// Then warn the user that we picked something because we couldn't find their font.
1994+
1995+
// Format message with user's choice of font and the font that was chosen instead.
1996+
const winrt::hstring message{ fmt::format(std::wstring_view{ RS_(L"NoticeFontNotFound") }, _desiredFont.GetFaceName(), _actualFont.GetFaceName()) };
1997+
1998+
// Capture what we need to resume later.
1999+
[strongThis = get_strong(), message]() -> winrt::fire_and_forget {
2000+
// Take these out of the lambda and store them locally
2001+
// because the coroutine will lose them into space
2002+
// by the time it resumes.
2003+
const auto msg = message;
2004+
const auto strong = strongThis;
2005+
2006+
// Pop the rest of this function to the tail of the UI thread
2007+
// Just in case someone was holding a lock when they called us and
2008+
// the handlers decide to do something that take another lock
2009+
// (like ShellExecute pumping our messaging thread...GH#7994)
2010+
co_await strong->Dispatcher();
2011+
2012+
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Warning, std::move(msg));
2013+
strong->_raiseNoticeHandlers(*strong, std::move(noticeArgs));
2014+
}();
2015+
}
2016+
19902017
const auto actualNewSize = _actualFont.GetSize();
19912018
_fontSizeChangedHandlers(actualNewSize.X, actualNewSize.Y, initialUpdate);
19922019
}
@@ -3074,5 +3101,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
30743101
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs);
30753102
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs);
30763103
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs);
3104+
DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, RaiseNotice, _raiseNoticeHandlers, TerminalControl::TermControl, TerminalControl::NoticeEventArgs);
30773105
// clang-format on
30783106
}

src/cascadia/TerminalControl/TermControl.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "CopyToClipboardEventArgs.g.h"
88
#include "PasteFromClipboardEventArgs.g.h"
99
#include "OpenHyperlinkEventArgs.g.h"
10+
#include "NoticeEventArgs.g.h"
1011
#include <winrt/Microsoft.Terminal.TerminalConnection.h>
1112
#include "../../renderer/base/Renderer.hpp"
1213
#include "../../renderer/dx/DxRenderer.hpp"
@@ -81,6 +82,22 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
8182
hstring _uri;
8283
};
8384

85+
struct NoticeEventArgs :
86+
public NoticeEventArgsT<NoticeEventArgs>
87+
{
88+
public:
89+
NoticeEventArgs(const NoticeLevel level, const hstring& message) :
90+
_level(level),
91+
_message(message) {}
92+
93+
NoticeLevel Level() { return _level; };
94+
hstring Message() { return _message; };
95+
96+
private:
97+
const NoticeLevel _level;
98+
const hstring _message;
99+
};
100+
84101
struct TermControl : TermControlT<TermControl>
85102
{
86103
TermControl(IControlSettings settings, TerminalConnection::ITerminalConnection connection);
@@ -150,6 +167,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
150167
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs);
151168
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs);
152169
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs);
170+
DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RaiseNotice, _raiseNoticeHandlers, TerminalControl::TermControl, TerminalControl::NoticeEventArgs);
153171

154172
TYPED_EVENT(WarningBell, IInspectable, IInspectable);
155173
TYPED_EVENT(ConnectionStateChanged, TerminalControl::TermControl, IInspectable);

0 commit comments

Comments
 (0)