Skip to content

Commit

Permalink
Detect Powerline fonts, and add a powerline preview?
Browse files Browse the repository at this point in the history
  • Loading branch information
DHowett committed May 16, 2023
1 parent 221327a commit 9ce6413
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 32 deletions.
40 changes: 23 additions & 17 deletions src/cascadia/TerminalSettingsEditor/Appearances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
bool Font::HasPowerlineCharacters()

Check failure on line 23 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)

Check failure on line 23 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)
{
if (!_hasPowerlineCharacters.has_value())

Check failure on line 25 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)

Check failure on line 25 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)
{
try
{
winrt::com_ptr<IDWriteFont> font;
THROW_IF_FAILED(_family->GetFont(0, font.put()));
BOOL exists{};
// We're actually checking for the "Extended" PowerLine glyph set.
// They're more fun.
THROW_IF_FAILED(font->HasCharacter(0xE0B6, &exists));
_hasPowerlineCharacters = (exists == TRUE);

Check failure on line 35 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)

Check failure on line 35 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)
}
catch (...)
{
_hasPowerlineCharacters = false;

Check failure on line 39 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)

Check failure on line 39 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)
}
}
return _hasPowerlineCharacters.value_or(false);

Check failure on line 42 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)

Check failure on line 42 in src/cascadia/TerminalSettingsEditor/Appearances.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`Powerline` is not a recognized word. (unrecognized-spelling)
}

AppearanceViewModel::AppearanceViewModel(const Model::AppearanceConfig& appearance) :
_appearance{ appearance }
{
Expand Down Expand Up @@ -288,25 +310,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation

IInspectable Appearances::CurrentFontFace() const
{
// look for the current font in our shown list of fonts
const auto& appearanceVM{ Appearance() };
const auto appearanceFontFace{ appearanceVM.FontFace() };
const auto& currentFontList{ ShowAllFonts() ? ProfileViewModel::CompleteFontList() : ProfileViewModel::MonospaceFontList() };
IInspectable fallbackFont;
for (const auto& font : currentFontList)
{
if (font.LocalizedName() == appearanceFontFace)
{
return box_value(font);
}
else if (font.LocalizedName() == L"Cascadia Mono")
{
fallbackFont = box_value(font);
}
}

// we couldn't find the desired font, set to "Cascadia Mono" since that ships by default
return fallbackFont;
return box_value(ProfileViewModel::FindFontWithLocalizedName(appearanceFontFace));
}

void Appearances::FontFace_SelectionChanged(const IInspectable& /*sender*/, const SelectionChangedEventArgs& e)
Expand Down
12 changes: 10 additions & 2 deletions src/cascadia/TerminalSettingsEditor/Appearances.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,22 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
struct Font : FontT<Font>
{
public:
Font(std::wstring name, std::wstring localizedName) :
Font(std::wstring name, std::wstring localizedName, IDWriteFontFamily* family) :
_Name{ name },
_LocalizedName{ localizedName } {};
_LocalizedName{ localizedName }
{
_family.copy_from(family);
}

hstring ToString() { return _LocalizedName; }
bool HasPowerlineCharacters();

WINRT_PROPERTY(hstring, Name);
WINRT_PROPERTY(hstring, LocalizedName);

private:
winrt::com_ptr<IDWriteFontFamily> _family;
std::optional<bool> _hasPowerlineCharacters;
};

struct AppearanceViewModel : AppearanceViewModelT<AppearanceViewModel>, ViewModelHelper<AppearanceViewModel>
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsEditor/Appearances.idl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace Microsoft.Terminal.Settings.Editor
{
String Name { get; };
String LocalizedName { get; };
Boolean HasPowerlineCharacters { get; };
}

runtimeclass AppearanceViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
Expand Down
21 changes: 18 additions & 3 deletions src/cascadia/TerminalSettingsEditor/PreviewConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
using namespace ::winrt::Microsoft::Terminal::TerminalConnection;
using namespace ::winrt::Windows::Foundation;

static constexpr std::wstring_view PromptTextPlain{ L"C:\\> " };
static constexpr std::wstring_view PromptTextPowerline{ L"\x1b[49;34m\xe0b6\x1b[1;97;44m C:\\ \x1b[m\x1b[46;34m\xe0b8\x1b[49;36m\xe0b8\x1b[m " };

// clang-format off
static constexpr std::wstring_view PreviewText{
L"\x001b"
L"c" // Hard Reset (RIS); on separate lines to avoid becoming 0x01BC
L"Windows Terminal\r\n"
L"C:\\> \x1b[93m" L"git\x1b[m diff \x1b[90m-w\x1b[m\r\n"
L"{0}\x1b[93m" L"git\x1b[m diff \x1b[90m-w\x1b[m\r\n"
L"\x1b[1m" L"diff --git a/win b/win\x1b[m\r\n"
L"\x1b[36m@@ -1 +1 @@\x1b[m\r\n"
L"\x1b[31m- Windows Console\x1b[m\r\n"
L"\x1b[32m- Windows Terminal!\x1b[m\r\n"
L"C:\\> \x1b[93mWrite-Host \x1b[36m\"\xd83e\xde9f!\"\x1b[1D\x1b[m"
L"{0}\x1b[93mWrite-Host \x1b[36m\"\xd83e\xde9f!\"\x1b[1D\x1b[m"
};
// clang-format on

Expand All @@ -27,7 +32,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void PreviewConnection::Start() noexcept
{
// Send the preview text
_TerminalOutputHandlers(PreviewText);
_TerminalOutputHandlers(fmt::format(PreviewText, _displayPowerlineGlyphs ? PromptTextPowerline : PromptTextPlain));
}

void PreviewConnection::Initialize(const Windows::Foundation::Collections::ValueSet& /*settings*/) noexcept
Expand All @@ -45,4 +50,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void PreviewConnection::Close() noexcept
{
}

void PreviewConnection::DisplayPowerlineGlyphs(bool d) noexcept
{
std::swap(d, _displayPowerlineGlyphs);
if (_displayPowerlineGlyphs != d)
{
// If it changed, redraw
Start();
}
}
}
5 changes: 5 additions & 0 deletions src/cascadia/TerminalSettingsEditor/PreviewConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void Resize(uint32_t rows, uint32_t columns) noexcept;
void Close() noexcept;

void DisplayPowerlineGlyphs(bool d) noexcept;

winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; }

WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler);
TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, IInspectable);

private:
bool _displayPowerlineGlyphs{ false };
};
}
41 changes: 34 additions & 7 deletions src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ using namespace winrt::Microsoft::Terminal::Settings::Model;

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
static Editor::Font _FontObjectForDWriteFont(IDWriteFontFamily* family);

Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_FontList{ nullptr };

Expand Down Expand Up @@ -118,12 +120,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
com_ptr<IDWriteFontFamily> fontFamily;
THROW_IF_FAILED(fontCollection->GetFontFamily(i, fontFamily.put()));

// get the font's localized names
com_ptr<IDWriteLocalizedStrings> localizedFamilyNames;
THROW_IF_FAILED(fontFamily->GetFamilyNames(localizedFamilyNames.put()));

// construct a font entry for tracking
if (const auto fontEntry{ _GetFont(localizedFamilyNames) })
if (const auto fontEntry{ _FontObjectForDWriteFont(fontFamily.get()) })
{
// check if the font is monospaced
try
Expand Down Expand Up @@ -159,7 +157,32 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
CATCH_LOG();

Editor::Font ProfileViewModel::_GetFont(com_ptr<IDWriteLocalizedStrings> localizedFamilyNames)
Editor::Font ProfileViewModel::FindFontWithLocalizedName(const winrt::hstring& name) noexcept
{
// look for the current font in our shown list of fonts
Editor::Font fallbackFont{ nullptr };
try
{
const auto& currentFontList{ CompleteFontList() };
for (const auto& font : currentFontList)
{
if (font.LocalizedName() == name)
{
return font;
}
else if (font.LocalizedName() == L"Cascadia Mono")
{
fallbackFont = font;
}
}
}
CATCH_LOG();

// we couldn't find the desired font, set to "Cascadia Mono" if we found that since it ships by default
return fallbackFont;
}

static Editor::Font _FontObjectForDWriteFont(IDWriteFontFamily* family)
{
// used for the font's name as an identifier (i.e. text block's font family property)
std::wstring nameID;
Expand All @@ -169,6 +192,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
std::wstring localizedName;
UINT32 localizedNameIndex;

// get the font's localized names
winrt::com_ptr<IDWriteLocalizedStrings> localizedFamilyNames;
THROW_IF_FAILED(family->GetFamilyNames(localizedFamilyNames.put()));

// use our current locale to find the localized name
auto exists{ FALSE };
HRESULT hr;
Expand Down Expand Up @@ -211,7 +238,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation

if (!nameID.empty() && !localizedName.empty())
{
return make<Font>(nameID, localizedName);
return make<Font>(nameID, localizedName, family);
}
return nullptr;
}
Expand Down
3 changes: 1 addition & 2 deletions src/cascadia/TerminalSettingsEditor/ProfileViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static void UpdateFontList() noexcept;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> CompleteFontList() noexcept { return _FontList; };
static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; };
static Editor::Font FindFontWithLocalizedName(winrt::hstring const& name) noexcept;

ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings);
Model::TerminalSettings TermSettings() const;
Expand Down Expand Up @@ -123,8 +124,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;

static Editor::Font _GetFont(com_ptr<IDWriteLocalizedStrings> localizedFamilyNames);

Model::CascadiaSettings _appSettings;
Editor::AppearanceViewModel _unfocusedAppearanceViewModel;
};
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace Microsoft.Terminal.Settings.Editor
{
static Windows.Foundation.Collections.IObservableVector<Font> CompleteFontList { get; };
static Windows.Foundation.Collections.IObservableVector<Font> MonospaceFontList { get; };
static Font FindFontWithLocalizedName(String name);

Microsoft.Terminal.Settings.Model.TerminalSettings TermSettings { get; };

Expand Down
20 changes: 19 additions & 1 deletion src/cascadia/TerminalSettingsEditor/Profiles_Appearance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Profiles_Appearance::Profiles_Appearance()
{
InitializeComponent();
_previewConnection = winrt::make_self<PreviewConnection>();
}

void Profiles_Appearance::OnNavigatedTo(const NavigationEventArgs& e)
Expand All @@ -36,7 +37,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
if (!_previewControl)
{
const auto settings = _Profile.TermSettings();
_previewControl = Control::TermControl(settings, settings, make<PreviewConnection>());
_previewConnection->DisplayPowerlineGlyphs(_looksLikePowerlineFont());
_previewControl = Control::TermControl(settings, settings, *_previewConnection);
_previewControl.IsEnabled(false);
_previewControl.AllowFocusWhenDisabled(false);
_previewControl.DisplayCursorWhileBlurred(true);
Expand Down Expand Up @@ -68,9 +70,25 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
_Profile.DeleteUnfocusedAppearance();
}

bool Profiles_Appearance::_looksLikePowerlineFont() const
{
if (_Profile && _Profile.DefaultAppearance())
{
if (auto fontName = _Profile.DefaultAppearance().FontFace(); !fontName.empty())
{
if (auto font = ProfileViewModel::FindFontWithLocalizedName(fontName))
{
return font.HasPowerlineCharacters();
}
}
}
return false;
}

void Profiles_Appearance::_onProfilePropertyChanged(const IInspectable&, const PropertyChangedEventArgs&) const
{
const auto settings = _Profile.TermSettings();
_previewConnection->DisplayPowerlineGlyphs(_looksLikePowerlineFont());
_previewControl.UpdateControlSettings(settings, settings);
}
}
3 changes: 3 additions & 0 deletions src/cascadia/TerminalSettingsEditor/Profiles_Appearance.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "Profiles_Appearance.g.h"
#include "Utils.h"
#include "PreviewConnection.h"

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Expand All @@ -26,7 +27,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation

private:
void _onProfilePropertyChanged(const IInspectable&, const PropertyChangedEventArgs&) const;
bool _looksLikePowerlineFont() const;

winrt::com_ptr<PreviewConnection> _previewConnection{ nullptr };
Microsoft::Terminal::Control::TermControl _previewControl{ nullptr };
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _AppearanceViewModelChangedRevoker;
Expand Down

1 comment on commit 9ce6413

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log for details.

Unrecognized words (1)

Powerline

Previously acknowledged words that are now absent Hirots NULs spand xwwyzz xxyyzz :arrow_right:
To accept ✔️ these unrecognized words as correct and remove the previously acknowledged and now absent words, run the following commands

... in a clone of the git@github.com:microsoft/terminal.git repository
on the dev/duhowett/preview-text-powerline branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.21/apply.pl' |
perl - 'https://github.com/microsoft/terminal/actions/runs/4996073621/attempts/1'
✏️ Contributor please read this

By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.

⚠️ The command is written for posix shells. If it doesn't work for you, you can manually add (one word per line) / remove items to expect.txt and the excludes.txt files.

If the listed items are:

  • ... misspelled, then please correct them instead of using the command.
  • ... names, please add them to .github/actions/spelling/allow/names.txt.
  • ... APIs, you can add them to a file in .github/actions/spelling/allow/.
  • ... just things you're using, please add them to an appropriate file in .github/actions/spelling/expect/.
  • ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in .github/actions/spelling/patterns/.

See the README.md in each directory for more information.

🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.