Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tooltips to the Suggestions Control #14939

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
06874ee
dirty, but now with tooltips
zadjii-msft Feb 24, 2023
c839bfa
Cleanup for... review?
zadjii-msft Feb 24, 2023
e09379c
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Mar 1, 2023
dcdb145
Dismiss tooltip on close
zadjii-msft Mar 1, 2023
de56d9e
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Mar 1, 2023
110bb7d
surprisingly load bearing
zadjii-msft Mar 1, 2023
059fa07
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Mar 20, 2023
a010e23
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Mar 24, 2023
c49f765
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Apr 5, 2023
56cc44d
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft May 31, 2023
39c5b63
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Jul 5, 2023
faa6665
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Aug 2, 2023
f33a35b
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Aug 2, 2023
6368a6d
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Aug 8, 2023
6ee0d9f
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Aug 9, 2023
53281b8
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Aug 9, 2023
33197c5
Merge branch 'dev/migrie/fhl-2023/pwsh-autocomplete-demo' into dev/mi…
zadjii-msft Aug 9, 2023
a1043ba
a bunch of notes before I abandon this
zadjii-msft Aug 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 98 additions & 4 deletions src/cascadia/TerminalApp/SuggestionsControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <LibraryResources.h>

#include "SuggestionsControl.g.cpp"
#include "../../types/inc/utils.hpp"

using namespace winrt;
using namespace winrt::TerminalApp;
Expand All @@ -19,6 +20,8 @@
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Microsoft::Terminal::Settings::Model;

using namespace std::chrono_literals;

namespace winrt::TerminalApp::implementation
{
SuggestionsControl::SuggestionsControl()
Expand Down Expand Up @@ -262,8 +265,8 @@
// - <unused>
// Return Value:
// - <none>
void SuggestionsControl::_selectedCommandChanged(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
winrt::fire_and_forget SuggestionsControl::_selectedCommandChanged(const IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
{
const auto selectedCommand = _filteredActionsView().SelectedItem();
const auto filteredCommand{ selectedCommand.try_as<winrt::TerminalApp::FilteredCommand>() };
Expand All @@ -281,9 +284,83 @@
{
if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
{
PreviewAction.raise(*this, actionPaletteItem.Command());
const auto& cmd = actionPaletteItem.Command();
PreviewAction.raise(*this, cmd);

const auto description{ cmd.Description() };
if (!description.empty())
{
// If it's already open, then just re-target it and update the content immediately.
if (DescriptionTip().IsOpen())
{
_openTooltip(cmd);
}
else
{
// Otherwise, wait a bit before opening it.
co_await winrt::resume_after(200ms);
co_await wil::resume_foreground(Dispatcher());
_openTooltip(cmd);
DescriptionTip().IsOpen(true);
}
}
else
{
// If there's no description, then just close the tooltip.
DescriptionTip().IsOpen(false);
}
}
}
}

winrt::fire_and_forget SuggestionsControl::_openTooltip(Command cmd)
{
const auto description{ cmd.Description() };

if (!cmd.Description().empty())
{
DescriptionTip().Target(SelectedItem());
DescriptionTip().Title(cmd.Name());

// If you try to put a newline in the Subtitle, it'll _immediately
// close the tooltip_. Instead, we'll need to build up the text as a
// series of runs, and put them in the content.

_toolTipContent().Inlines().Clear();

// First, replace all "\r\n" with "\n"
std::wstring filtered = description.c_str();

// replace all "\r\n" with "\n" in `filtered`
std::wstring::size_type pos = 0;
while ((pos = filtered.find(L"\r\n", pos)) != std::wstring::npos)
{
filtered.erase(pos, 1);
}

// Split the filtered description on '\n`
const auto lines = ::Microsoft::Console::Utils::SplitString(filtered.c_str(), L'\n');
// For each line, build a Run + LineBreak, and add them to the text
// block
for (const auto& line : lines)
{
if (line.empty())
continue;
Documents::Run textRun;
textRun.Text(winrt::hstring{ line });
_toolTipContent().Inlines().Append(textRun);
_toolTipContent().Inlines().Append(Documents::LineBreak{});
}

// TODO! These were all feigned attempts to allow us to focus the content of the teachingtip.

Check failure on line 355 in src/cascadia/TerminalApp/SuggestionsControl.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

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

Check failure on line 355 in src/cascadia/TerminalApp/SuggestionsControl.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`teachingtip` is not a recognized word. (unrecognized-spelling)
// We may want to keep IsTextSelectionEnabled in the XAML.
//
// I also have no idea if the FullDescriptionProperty thing worked at all.
_toolTipContent().AllowFocusOnInteraction(true);
_toolTipContent().IsTextSelectionEnabled(true);
DescriptionTip().SetValue(Automation::AutomationProperties::FullDescriptionProperty(), winrt::box_value(description));
}
co_return;
}

void SuggestionsControl::_previewKeyDownHandler(const IInspectable& /*sender*/,
Expand Down Expand Up @@ -377,7 +454,9 @@
// If the user types a character while the menu (not in palette mode)
// is open, then dismiss ourselves. That way, when you type a character,
// we'll instead send it to the TermControl.
if (_mode == SuggestionsMode::Menu && !e.Handled())
if (_mode == SuggestionsMode::Menu &&
!e.Handled() &&
key != VirtualKey::F6)
{
_dismissPalette();
}
Expand Down Expand Up @@ -444,7 +523,21 @@
return;
}

const auto& popups = Media::VisualTreeHelper::GetOpenPopupsForXamlRoot(root);
const auto numPopups = popups.Size();
// TODO! I think _we_ are the first popup.
if (numPopups > 1)
{
// TODO! a test. If theres a popup, don't dismiss.
return;
}

// TODO! if the teaching tip is closed, re-focus the suggestions list

auto focusedElementOrAncestor = Input::FocusManager::GetFocusedElement(root).try_as<DependencyObject>();
// TODO! if there wasn't a focused element in the main root, look
// instead underneath the teachingtip. left as an exersize to figure out

Check failure on line 539 in src/cascadia/TerminalApp/SuggestionsControl.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

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

Check failure on line 539 in src/cascadia/TerminalApp/SuggestionsControl.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

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

Check failure on line 539 in src/cascadia/TerminalApp/SuggestionsControl.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

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

Check failure on line 539 in src/cascadia/TerminalApp/SuggestionsControl.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`teachingtip` is not a recognized word. (unrecognized-spelling)
// _how_ to do that.
while (focusedElementOrAncestor)
{
if (focusedElementOrAncestor == *this)
Expand Down Expand Up @@ -946,6 +1039,7 @@
void SuggestionsControl::_close()
{
Visibility(Visibility::Collapsed);
DescriptionTip().IsOpen(false);

// Clear the text box each time we close the dialog. This is consistent with VsCode.
_searchBox().Text(L"");
Expand Down
6 changes: 5 additions & 1 deletion src/cascadia/TerminalApp/SuggestionsControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ namespace winrt::TerminalApp::implementation

void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e);
void _selectedCommandChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);

winrt::fire_and_forget _selectedCommandChanged(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::RoutedEventArgs& args);

winrt::fire_and_forget _openTooltip(Microsoft::Terminal::Settings::Model::Command cmd);

void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
void _updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand);
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/TerminalApp/SuggestionsControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@
ItemsSource="{x:Bind FilteredActions}"
SelectionChanged="_listItemSelectionChanged"
SelectionMode="Single" />
<mux:TeachingTip x:Name="DescriptionTip"
IsOpen="False"
PreferredPlacement="Right"
ShouldConstrainToRootBounds="False">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
HorizontalScrollMode="Enabled">
<TextBlock x:Name="_toolTipContent" />
</ScrollViewer>
</mux:TeachingTip>

</Grid>

Expand Down
10 changes: 8 additions & 2 deletions src/cascadia/TerminalSettingsModel/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static constexpr std::string_view ArgsKey{ "args" };
static constexpr std::string_view IterateOnKey{ "iterateOn" };
static constexpr std::string_view CommandsKey{ "commands" };
static constexpr std::string_view KeysKey{ "keys" };
static constexpr std::string_view DescriptionKey{ "description" };

static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
Expand All @@ -40,6 +41,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{
auto command{ winrt::make_self<Command>() };
command->_name = _name;
command->_Description = _Description;
command->_ActionAndArgs = *get_self<implementation::ActionAndArgs>(_ActionAndArgs)->Copy();
command->_keyMappings = _keyMappings;
command->_iconPath = _iconPath;
Expand Down Expand Up @@ -254,7 +256,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation

auto nested = false;
JsonUtils::GetValueForKey(json, IterateOnKey, result->_IterateOn);

JsonUtils::GetValueForKey(json, DescriptionKey, result->_Description);
// For iterable commands, we'll make another pass at parsing them once
// the json is patched. So ignore parsing sub-commands for now. Commands
// will only be marked iterable on the first pass.
Expand Down Expand Up @@ -406,7 +408,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Json::Value cmdJson{ Json::ValueType::objectValue };
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);

JsonUtils::SetValueForKey(cmdJson, DescriptionKey, _Description);
if (_ActionAndArgs)
{
cmdJson[JsonKey(ActionKey)] = ActionAndArgs::ToJson(_ActionAndArgs);
Expand All @@ -426,6 +428,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
// First iteration also writes icon and name
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
JsonUtils::SetValueForKey(cmdJson, DescriptionKey, _Description);
}

if (_ActionAndArgs)
Expand Down Expand Up @@ -654,8 +657,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
const auto parseElement = [&](const auto& element) {
winrt::hstring completionText;
winrt::hstring listText;
winrt::hstring tooltipText;
JsonUtils::GetValueForKey(element, "CompletionText", completionText);
JsonUtils::GetValueForKey(element, "ListItemText", listText);
JsonUtils::GetValueForKey(element, "ToolTip", tooltipText);

auto args = winrt::make_self<SendInputArgs>(
winrt::hstring{ fmt::format(FMT_COMPILE(L"{:\x7f^{}}{}"),
Expand All @@ -668,6 +673,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
auto c = winrt::make_self<Command>();
c->_name = listText;
c->_ActionAndArgs = actionAndArgs;
c->_Description = tooltipText;

// Try to assign a sensible icon based on the result type. These are
// roughly chosen to align with the icons in
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/Command.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation

WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
WINRT_PROPERTY(winrt::hstring, Description, L"");

private:
Json::Value _originalJson;
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/Command.idl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ namespace Microsoft.Terminal.Settings.Model
Command();

String Name { get; };
String Description { get; };

ActionAndArgs ActionAndArgs { get; };
Microsoft.Terminal.Control.KeyChord Keys { get; };
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);
Expand Down
Loading