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 descriptions to commands (namely, snippets) #17376

Merged
merged 13 commits into from
Jun 12, 2024
160 changes: 135 additions & 25 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;
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 @@ -103,9 +106,7 @@ namespace winrt::TerminalApp::implementation
// stays "attached" to the cursor.
if (Visibility() == Visibility::Visible && _direction == TerminalApp::SuggestionsDirection::BottomUp)
{
auto m = this->Margin();
m.Top = (_anchor.Y - ActualHeight());
this->Margin(m);
this->_recalculateTopMargin();
}
});

Expand Down Expand Up @@ -281,9 +282,78 @@ namespace winrt::TerminalApp::implementation
{
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 (const auto& selected{ SelectedItem() })
{
selected.SetValue(Automation::AutomationProperties::FullDescriptionProperty(), winrt::box_value(description));
}

if (!description.empty())
{
_openTooltip(cmd);
}
else
{
// If there's no description, then just close the tooltip.
_descriptionsView().Visibility(Visibility::Collapsed);
_descriptionsBackdrop().Visibility(Visibility::Collapsed);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
_recalculateTopMargin();
}
}
}
}

void SuggestionsControl::_openTooltip(Command cmd)
{
const auto description{ cmd.Description() };
if (description.empty())
{
return;
}

// Build the contents of the "tooltip" based on the description
//
// First, the title. This is just the name of the command.
_descriptionTitle().Inlines().Clear();
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
Documents::Run titleRun;
titleRun.Text(cmd.Name());
_descriptionTitle().Inlines().Append(titleRun);

// Now fill up the "subtitle" part of the "tooltip" with the actual
// description itself.
const auto& inlines{ _descriptionComment().Inlines() };
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const auto& inlines{ _descriptionComment().Inlines() };
const auto inlines{ _descriptionComment().Inlines() };

this is a bad habit, we should not perpetuate it

inlines.Clear();

// Split the filtered description on '\n`
const auto lines = ::Microsoft::Console::Utils::SplitString(description, L'\n');
// build a Run + LineBreak, and add them to the text block
for (const auto& line : lines)
{
// Trim off any `\r`'s in the string. Pwsh completions will
// frequently have these embedded.
std::wstring trimmed{ line };
trimmed.erase(std::remove(trimmed.begin(), trimmed.end(), L'\r'), trimmed.end());
if (trimmed.empty())
{
continue;
}

Documents::Run textRun;
textRun.Text(trimmed);
inlines.Append(textRun);
inlines.Append(Documents::LineBreak{});
}

// Now, make ourselves visible.
_descriptionsView().Visibility(Visibility::Visible);
_descriptionsBackdrop().Visibility(Visibility::Visible);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
// and update the padding to account for our new contents.
_recalculateTopMargin();
return;
}

void SuggestionsControl::_previewKeyDownHandler(const IInspectable& /*sender*/,
Expand Down Expand Up @@ -1020,16 +1090,71 @@ namespace winrt::TerminalApp::implementation
void SuggestionsControl::_setDirection(TerminalApp::SuggestionsDirection direction)
{
_direction = direction;

// We need to move either the list of suggestions, or the tooltip, to
// the top of the stack panel (depending on the layout).
Grid controlToMoveToTop = nullptr;

if (_direction == TerminalApp::SuggestionsDirection::TopDown)
{
Controls::Grid::SetRow(_searchBox(), 0);
controlToMoveToTop = _backdrop();
}
else // BottomUp
{
Controls::Grid::SetRow(_searchBox(), 4);
controlToMoveToTop = _descriptionsBackdrop();
}

assert(controlToMoveToTop);
const auto& children{ _listAndDescriptionStack().Children() };
uint32_t index;
if (children.IndexOf(controlToMoveToTop, index))
{
children.Move(index, 0);
}
}

void SuggestionsControl::_recalculateTopMargin()
{
auto currentMargin = Margin();
// Call Measure() on the descriptions backdrop, so that it gets it's new
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Call Measure() on the descriptions backdrop, so that it gets it's new
// Call Measure() on the descriptions backdrop, so that it gets its new

// DesiredSize for this new description text.
//
// If you forget this, then we _probably_ weren't laid out since
// updating that text, and the ActualHeight will be the _last_
// description's height.
_descriptionsBackdrop().Measure({
static_cast<float>(ActualWidth()),
static_cast<float>(ActualHeight()),
});

// Now, position vertically.
if (_direction == TerminalApp::SuggestionsDirection::TopDown)
{
// The control should open right below the cursor, with the list
// extending below. This is easy, we can just use the cursor as the
// origin (more or less)
currentMargin.Top = (_anchor.Y);
}
else
{
// Bottom Up.

// This is wackier, because we need to calculate the offset upwards
// from our anchor. So we need to get the size of our elements:
const auto backdropHeight = _backdrop().ActualHeight();
const auto descriptionDesiredHeight = _descriptionsBackdrop().Visibility() == Visibility::Visible ?
_descriptionsBackdrop().DesiredSize().Height :
0;

const auto marginTop = (_anchor.Y - backdropHeight - descriptionDesiredHeight);

currentMargin.Top = marginTop;
}
Margin(currentMargin);
}

void SuggestionsControl::Open(TerminalApp::SuggestionsMode mode,
const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& commands,
winrt::hstring filter,
Expand All @@ -1047,9 +1172,8 @@ namespace winrt::TerminalApp::implementation
_anchor = anchor;
_space = space;

const til::size actualSize{ til::math::rounding, ActualWidth(), ActualHeight() };
// Is there space in the window below the cursor to open the menu downwards?
const bool canOpenDownwards = (_anchor.Y + characterHeight + actualSize.height) < space.Height;
const bool canOpenDownwards = (_anchor.Y + characterHeight + ActualHeight()) < space.Height;
_setDirection(canOpenDownwards ? TerminalApp::SuggestionsDirection::TopDown :
TerminalApp::SuggestionsDirection::BottomUp);
// Set the anchor below by a character height
Expand All @@ -1063,26 +1187,13 @@ namespace winrt::TerminalApp::implementation
const auto proposedX = gsl::narrow_cast<int>(_anchor.X - 40);
// If the control is too wide to fit in the window, clamp it fit inside
// the window.
const auto maxX = gsl::narrow_cast<int>(space.Width - actualSize.width);
const auto maxX = gsl::narrow_cast<int>(space.Width - ActualWidth());
const auto clampedX = std::clamp(proposedX, 0, maxX);

// Create a thickness for the new margins
auto newMargin = Windows::UI::Xaml::ThicknessHelper::FromLengths(clampedX, 0, 0, 0);
// Now, position vertically.
if (_direction == TerminalApp::SuggestionsDirection::TopDown)
{
// The control should open right below the cursor, with the list
// extending below. This is easy, we can just use the cursor as the
// origin (more or less)
newMargin.Top = (_anchor.Y);
}
else
{
// Position at the cursor. The suggestions UI itself will maintain
// its own offset such that it's always above its origin
newMargin.Top = (_anchor.Y - actualSize.height);
}
Margin(newMargin);
// Create a thickness for the new margins. This will set the left, then
// we'll go update the top separately
Margin(Windows::UI::Xaml::ThicknessHelper::FromLengths(clampedX, 0, 0, 0));
_recalculateTopMargin();

_searchBox().Text(filter);

Expand All @@ -1099,5 +1210,4 @@ namespace winrt::TerminalApp::implementation
// selection starting at the end of the string.
_searchBox().Select(filter.size(), 0);
}

}
4 changes: 3 additions & 1 deletion src/cascadia/TerminalApp/SuggestionsControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ namespace winrt::TerminalApp::implementation
void _close();
void _dismissPalette();

void _recalculateTopMargin();

void _filterTextChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _keyUpHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
Expand All @@ -110,6 +112,7 @@ 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);
void _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 All @@ -121,7 +124,6 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter();
std::wstring _getTrimmedInput();
uint32_t _getNumVisibleItems();

friend class TerminalAppLocalTests::TabTests;
};
}
Expand Down
72 changes: 50 additions & 22 deletions src/cascadia/TerminalApp/SuggestionsControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,10 @@
</ResourceDictionary>
</UserControl.Resources>

<Grid>
Copy link
Member Author

Choose a reason for hiding this comment

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

guess what? we basically weren't using this Grid for anything. There was only ever _backdrop in it, taking up the whole thing.

This was a side-effect of command palette copypasta

<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>

<Grid.RowDefinitions>
<RowDefinition Height="8*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>

<StackPanel x:Name="_listAndDescriptionStack"
Orientation="Vertical">
<Grid x:Name="_backdrop"
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="3"
MinWidth="300"
MaxWidth="300"
MaxHeight="300"
Margin="0"
Expand All @@ -134,16 +121,16 @@
Translation="0,0,32">

<Grid.RowDefinitions>
<!-- 0: Top-down _searchBox -->
<RowDefinition Height="Auto" />
<!-- Top-down _searchBox -->
<!-- 1: Top-down ParentCommandName -->
<RowDefinition Height="Auto" />
<!-- Top-down ParentCommandName -->
<!-- 2: Top-down UNUSED???????? -->
<RowDefinition Height="Auto" />
<!-- Top-down UNUSED???????? -->
<!-- 3: _filteredActionsView -->
<RowDefinition Height="*" />
<!-- _filteredActionsView -->
<!-- 4: bottom-up _searchBox -->
<RowDefinition Height="Auto" />
<!-- bottom-up _searchBox -->
</Grid.RowDefinitions>

<TextBox x:Name="_searchBox"
Expand Down Expand Up @@ -206,8 +193,49 @@
SelectionMode="Single"
Style="{StaticResource NoAnimationsPlease}" />


</Grid>

<Grid x:Name="_descriptionsBackdrop"
MaxWidth="300"
Margin="0,6,0,6"
Padding="0,4,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{ThemeResource FlyoutPresenterBackground}"
BorderBrush="{ThemeResource FlyoutBorderThemeBrush}"
BorderThickness="{ThemeResource FlyoutBorderThemeThickness}"
CornerRadius="{ThemeResource OverlayCornerRadius}"
PointerPressed="_backdropPointerPressed"
Shadow="{StaticResource SharedShadow}"
Translation="0,0,32">

<Grid.RowDefinitions>
<!-- 0: descriptions view -->
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>

<StackPanel x:Name="_descriptionsView"
Grid.Row="0"
Margin="8,0,8,8"
Orientation="Vertical"
Visibility="Collapsed">
<TextBlock x:Name="_descriptionTitle"
FontSize="14"
FontWeight="Bold"
IsTextSelectionEnabled="True"
TextWrapping="WrapWholeWords" />
<ScrollViewer MaxHeight="64"
VerticalScrollBarVisibility="Visible"
VerticalScrollMode="Enabled"
Visibility="Visible">
<TextBlock x:Name="_descriptionComment"
IsTextSelectionEnabled="True"
TextWrapping="WrapWholeWords" />
</ScrollViewer>
</StackPanel>

</Grid>

</Grid>
</StackPanel>
</UserControl>
Loading
Loading