Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
34 changes: 34 additions & 0 deletions src/cascadia/QueryExtension/ExtensionPalette.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Controls::Primitives;
using namespace winrt::Windows::System;
namespace WWH = ::winrt::Windows::Web::Http;
namespace WSS = ::winrt::Windows::Storage::Streams;
Expand Down Expand Up @@ -340,6 +341,14 @@ namespace winrt::Microsoft::Terminal::Query::Extension::implementation
void ExtensionPalette::_lostFocusHandler(const Windows::Foundation::IInspectable& /*sender*/,
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
{
const auto focusedElement = Input::FocusManager::GetFocusedElement(this->XamlRoot());
if (focusedElement && (focusedElement.try_as<RichTextBlock>() || focusedElement.try_as<MenuFlyoutPresenter>() || focusedElement.try_as<Popup>()))
{
// The context menu for the message don't seem to be found when the VisualTreeHelper walks the visual tree. So we check here
// if one of the focused elements is a message or a context menu of one of those messages and return early to support
// copy and select all using a mouse
return;
}
const auto flyout = _queryBox().ContextFlyout();
if (flyout && flyout.IsOpen())
{
Expand Down Expand Up @@ -404,6 +413,13 @@ namespace winrt::Microsoft::Terminal::Query::Extension::implementation
}
else if (key == VirtualKey::C && ctrlDown)
{
// Get the focused element. If it is a chat message copy its selection (if any) to the clipboard.
const auto focusedElement = Input::FocusManager::GetFocusedElement(this->XamlRoot());
if (focusedElement && focusedElement.try_as<RichTextBlock>())
{
const auto textBlock = focusedElement.as<RichTextBlock>();
textBlock.CopySelectionToClipboard();
}
_queryBox().CopySelectionToClipboard();
Copy link
Member

@lhecker lhecker Jun 23, 2025

Choose a reason for hiding this comment

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

Questions:

  • This copies to the clipboard twice in a row. Is that intentional?
  • Do we need to handle Ctrl+C manually here? Why is it not handled as part of the _richBlock context menu?
  • Most importantly, why don't we use our custom TextMenuFlyout class here, similar to other parts of the code base? It should support RichTextBlock.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. No, Its not intentional. :|
  2. I think it's useful. I'm sure other people would find it useful too. There was other code here that did something similar. "Get in where you fit in?".
  3. Dang, it! My gut told me something was in the project for that. Didnt look hard enough. Ill swap it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So ill basically be at the previous commit only using the TextMenuFlyout class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Update pushed. Thanks for your feedback!

e.Handled(true);
}
Expand Down Expand Up @@ -440,6 +456,10 @@ namespace winrt::Microsoft::Terminal::Query::Extension::implementation
_richBlock{ nullptr }
{
_richBlock = Microsoft::Terminal::UI::Markdown::Builder::Convert(_messageContent, L"");
_richBlock.IsTextSelectionEnabled(true);
_richBlock.ContextRequested({ this, &ChatMessage::_chatMessageCopyRequested });
_richBlock.AllowFocusWhenDisabled(true);
_richBlock.AllowFocusOnInteraction(true);
const auto resources = Application::Current().Resources();
const auto textBrushObj = _isQuery ? resources.Lookup(box_value(L"TextOnAccentFillColorPrimaryBrush")) : resources.Lookup(box_value(L"TextFillColorPrimaryBrush"));
if (const auto textBrush = textBrushObj.try_as<Windows::UI::Xaml::Media::SolidColorBrush>())
Expand Down Expand Up @@ -478,4 +498,18 @@ namespace winrt::Microsoft::Terminal::Query::Extension::implementation
}
}
}

// Method Description:
// - Handle the ContextFlyoutRequested Event
// * Should be handled according to the documentation
// Arguments:
// - Standard Event Args
// Return Value:
// - <none>
void ChatMessage::_chatMessageCopyRequested(const Windows::Foundation::IInspectable& /*sender*/,
const Windows::UI::Xaml::Input::ContextRequestedEventArgs& e)
{
e.Handled(true);
}

}
3 changes: 2 additions & 1 deletion src/cascadia/QueryExtension/ExtensionPalette.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ namespace winrt::Microsoft::Terminal::Query::Extension::implementation
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender,
const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
void _setUpAIProviderInSettings(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);

void _close();
};

Expand All @@ -67,6 +66,8 @@ namespace winrt::Microsoft::Terminal::Query::Extension::implementation
bool IsQuery() const { return _isQuery; };
winrt::hstring MessageContent() const { return _messageContent; };
winrt::Windows::UI::Xaml::Controls::RichTextBlock RichBlock() const { return _richBlock; };
void _chatMessageCopyRequested(const Windows::Foundation::IInspectable& /*sender*/,
const Windows::UI::Xaml::Input::ContextRequestedEventArgs& e);

TYPED_EVENT(RunCommandClicked, winrt::Microsoft::Terminal::Query::Extension::ChatMessage, winrt::hstring);

Expand Down
Loading