mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 04:38:24 -06:00
Introduce a ContentManager helper (#14851)
## Summary _Thus we come to the introduction of a new servant, the `ContentManager`, a singular entity that serves at the behest of the `emperor`. It is its charge to keep track of all `TermControl` instances created by the windows, for each window must seek its blessing before calling forth such an instance._ _With the aid of the `ContentManager`, the `TermControl` shall now be traced by the hand of fate through the use of unique identifying marks, known as `GUID`s. Yet, its purpose remains yet unknown, for it is merely a waypoint upon the journey yet to come._ _This act of bridging also brings a change to the handling of events within the `TermControl`. This change shall see the addition of a revoker, similar to the manner in which the `AppHost` hath employed it, to the `TermControl`. Additionally, there is a new layer of indirection between the `ControlCore` and the `App` layer, making ready for the day when the `TermControl` may be repositioned and re-parented with ease._ _Consider this but a trivial act, a mere shadow of things yet to come, for its impact shall be felt but briefly, like the passing of a gentle breeze._ Related to #5000 Related to #1256 # Detailed description This PR is another small bridge PR between the big work in #14843, and the PR that will enable panes to move between windows. This introduces a new class, called `ContentManager`. This is a global singleton object, owned by the emperor. Whenever a window wants to instantiate a new `TermControl`, it must ask the ContentManager to give it one. This allows the ContentManager to track each "content" by GUID. That's it. We don't do anything with them in this PR by itself, we just track them. This also includes a small change to the way TermControl events are handled. It adds an `AppHost`-like revoker struct, and weak_ref's all the handlers. We also add a layer of indirection between the ControlCore's raising of events and the App layer's handling. This will make reparenting content easier in the future. This is a pretty trivial change which shouldn't have any major side effects. Consider it exposition of the things to come. It's intentionally small to try and keep the reviews more managable.
This commit is contained in:
parent
e0046a4cca
commit
f3a722e0e9
@ -10,6 +10,7 @@
|
||||
#include "../TerminalApp/ShortcutActionDispatch.h"
|
||||
#include "../TerminalApp/TerminalTab.h"
|
||||
#include "../TerminalApp/CommandPalette.h"
|
||||
#include "../TerminalApp/ContentManager.h"
|
||||
#include "CppWinrtTailored.h"
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
@ -112,6 +113,7 @@ namespace TerminalAppLocalTests
|
||||
CascadiaSettings initialSettings);
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::TerminalPage> _commonSetup();
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::WindowProperties> _windowProperties;
|
||||
winrt::com_ptr<winrt::TerminalApp::implementation::ContentManager> _contentManager;
|
||||
};
|
||||
|
||||
template<typename TFunction>
|
||||
@ -199,8 +201,11 @@ namespace TerminalAppLocalTests
|
||||
_windowProperties = winrt::make_self<winrt::TerminalApp::implementation::WindowProperties>();
|
||||
winrt::TerminalApp::WindowProperties props = *_windowProperties;
|
||||
|
||||
auto result = RunOnUIThread([&page, props]() {
|
||||
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>(props);
|
||||
_contentManager = winrt::make_self<winrt::TerminalApp::implementation::ContentManager>();
|
||||
winrt::TerminalApp::ContentManager contentManager = *_contentManager;
|
||||
|
||||
auto result = RunOnUIThread([&page, props, contentManager]() {
|
||||
page = winrt::make_self<winrt::TerminalApp::implementation::TerminalPage>(props, contentManager);
|
||||
VERIFY_IS_NOT_NULL(page);
|
||||
});
|
||||
VERIFY_SUCCEEDED(result);
|
||||
@ -246,9 +251,11 @@ namespace TerminalAppLocalTests
|
||||
|
||||
_windowProperties = winrt::make_self<winrt::TerminalApp::implementation::WindowProperties>();
|
||||
winrt::TerminalApp::WindowProperties props = *_windowProperties;
|
||||
_contentManager = winrt::make_self<winrt::TerminalApp::implementation::ContentManager>();
|
||||
winrt::TerminalApp::ContentManager contentManager = *_contentManager;
|
||||
Log::Comment(NoThrowString().Format(L"Construct the TerminalPage"));
|
||||
auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props]() {
|
||||
projectedPage = winrt::TerminalApp::TerminalPage(props);
|
||||
auto result = RunOnUIThread([&projectedPage, &page, initialSettings, props, contentManager]() {
|
||||
projectedPage = winrt::TerminalApp::TerminalPage(props, contentManager);
|
||||
page.copy_from(winrt::get_self<winrt::TerminalApp::implementation::TerminalPage>(projectedPage));
|
||||
page->_settings = initialSettings;
|
||||
});
|
||||
|
||||
@ -721,7 +721,7 @@ namespace winrt::TerminalApp::implementation
|
||||
warnings,
|
||||
_settings);
|
||||
|
||||
auto window = winrt::make_self<implementation::TerminalWindow>(*ev);
|
||||
auto window = winrt::make_self<implementation::TerminalWindow>(*ev, _contentManager);
|
||||
|
||||
this->SettingsChanged({ window->get_weak(), &implementation::TerminalWindow::UpdateSettingsHandler });
|
||||
if (_hasSettingsStartupActions)
|
||||
@ -731,6 +731,11 @@ namespace winrt::TerminalApp::implementation
|
||||
return *window;
|
||||
}
|
||||
|
||||
winrt::TerminalApp::ContentManager AppLogic::ContentManager()
|
||||
{
|
||||
return _contentManager;
|
||||
}
|
||||
|
||||
bool AppLogic::ShouldUsePersistedLayout() const
|
||||
{
|
||||
return _settings.GlobalSettings().ShouldUsePersistedLayout();
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "LanguageProfileNotifier.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
#include "TerminalWindow.h"
|
||||
#include "ContentManager.h"
|
||||
|
||||
#include <inc/cppwinrt_utils.h>
|
||||
#include <ThrottledFunc.h>
|
||||
@ -70,6 +71,8 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
TerminalApp::TerminalWindow CreateNewWindow();
|
||||
|
||||
winrt::TerminalApp::ContentManager ContentManager();
|
||||
|
||||
TerminalApp::ParseCommandlineResult GetParseCommandlineMessage(array_view<const winrt::hstring> args);
|
||||
|
||||
TYPED_EVENT(SettingsChanged, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs);
|
||||
@ -98,6 +101,8 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::com_ptr<LanguageProfileNotifier> _languageProfileNotifier;
|
||||
wil::unique_folder_change_reader_nothrow _reader;
|
||||
|
||||
TerminalApp::ContentManager _contentManager{ winrt::make<implementation::ContentManager>() };
|
||||
|
||||
static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view<const hstring> args,
|
||||
const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior);
|
||||
|
||||
|
||||
@ -34,6 +34,8 @@ namespace TerminalApp
|
||||
Boolean IsRunningElevated();
|
||||
Boolean CanDragDrop();
|
||||
|
||||
ContentManager ContentManager { get; };
|
||||
|
||||
Boolean HasSettingsStartupActions();
|
||||
|
||||
Boolean ShouldUsePersistedLayout();
|
||||
|
||||
51
src/cascadia/TerminalApp/ContentManager.cpp
Normal file
51
src/cascadia/TerminalApp/ContentManager.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ContentManager.h"
|
||||
#include "ContentManager.g.cpp"
|
||||
|
||||
#include <wil/token_helpers.h>
|
||||
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::ApplicationModel::DataTransfer;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Microsoft::Terminal;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ControlInteractivity ContentManager::CreateCore(const Microsoft::Terminal::Control::IControlSettings& settings,
|
||||
const IControlAppearance& unfocusedAppearance,
|
||||
const TerminalConnection::ITerminalConnection& connection)
|
||||
{
|
||||
ControlInteractivity content{ settings, unfocusedAppearance, connection };
|
||||
content.Closed({ get_weak(), &ContentManager::_closedHandler });
|
||||
|
||||
_content.emplace(content.Id(), content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
ControlInteractivity ContentManager::TryLookupCore(uint64_t id)
|
||||
{
|
||||
const auto it = _content.find(id);
|
||||
return it != _content.end() ? it->second : ControlInteractivity{ nullptr };
|
||||
}
|
||||
|
||||
void ContentManager::_closedHandler(winrt::Windows::Foundation::IInspectable sender,
|
||||
winrt::Windows::Foundation::IInspectable e)
|
||||
{
|
||||
if (const auto& content{ sender.try_as<winrt::Microsoft::Terminal::Control::ControlInteractivity>() })
|
||||
{
|
||||
const auto& contentId{ content.Id() };
|
||||
_content.erase(contentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/cascadia/TerminalApp/ContentManager.h
Normal file
44
src/cascadia/TerminalApp/ContentManager.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*++
|
||||
Copyright (c) Microsoft Corporation
|
||||
Licensed under the MIT license.
|
||||
|
||||
Class Name:
|
||||
- ContentManager.h
|
||||
|
||||
Abstract:
|
||||
- This is a helper class for tracking all of the terminal "content" instances of
|
||||
the Terminal. These are all the ControlInteractivity & ControlCore's of each
|
||||
of our TermControls. These are each assigned a GUID on creation, and stored in
|
||||
a map for later lookup.
|
||||
- This is used to enable moving panes between windows. TermControl's are not
|
||||
thread-agile, so they cannot be reused on other threads. However, the content
|
||||
is. This helper, which exists as a singleton across all the threads in the
|
||||
Terminal app, allows each thread to create content, assign it to a
|
||||
TermControl, detach it from that control, and reattach to new controls on
|
||||
other threads.
|
||||
- When you want to create a new TermControl, call CreateCore to instantiate a
|
||||
new content with a GUID for later reparenting.
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "ContentManager.g.h"
|
||||
|
||||
#include <inc/cppwinrt_utils.h>
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct ContentManager : ContentManagerT<ContentManager>
|
||||
{
|
||||
public:
|
||||
ContentManager() = default;
|
||||
Microsoft::Terminal::Control::ControlInteractivity CreateCore(const Microsoft::Terminal::Control::IControlSettings& settings,
|
||||
const Microsoft::Terminal::Control::IControlAppearance& unfocusedAppearance,
|
||||
const Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection);
|
||||
Microsoft::Terminal::Control::ControlInteractivity TryLookupCore(uint64_t id);
|
||||
|
||||
private:
|
||||
std::unordered_map<uint64_t, Microsoft::Terminal::Control::ControlInteractivity> _content;
|
||||
|
||||
void _closedHandler(winrt::Windows::Foundation::IInspectable sender,
|
||||
winrt::Windows::Foundation::IInspectable e);
|
||||
};
|
||||
}
|
||||
@ -138,6 +138,9 @@
|
||||
<ClInclude Include="AppLogic.h">
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ContentManager.h">
|
||||
<DependentUpon>TerminalPage.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TerminalWindow.h">
|
||||
<DependentUpon>TerminalWindow.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
@ -237,6 +240,9 @@
|
||||
<ClCompile Include="AppLogic.cpp">
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ContentManager.cpp">
|
||||
<DependentUpon>TerminalPage.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TerminalWindow.cpp">
|
||||
<DependentUpon>TerminalWindow.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
|
||||
@ -54,10 +54,11 @@ namespace winrt
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalPage::TerminalPage(TerminalApp::WindowProperties properties) :
|
||||
TerminalPage::TerminalPage(TerminalApp::WindowProperties properties, const TerminalApp::ContentManager& manager) :
|
||||
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
|
||||
_mruTabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
|
||||
_startupActions{ winrt::single_threaded_vector<ActionAndArgs>() },
|
||||
_manager{ manager },
|
||||
_hostingHwnd{},
|
||||
_WindowProperties{ std::move(properties) }
|
||||
{
|
||||
@ -2593,7 +2594,10 @@ namespace winrt::TerminalApp::implementation
|
||||
// Do any initialization that needs to apply to _every_ TermControl we
|
||||
// create here.
|
||||
// TermControl will copy the settings out of the settings passed to it.
|
||||
TermControl term{ settings.DefaultSettings(), settings.UnfocusedSettings(), connection };
|
||||
|
||||
const auto content = _manager.CreateCore(settings.DefaultSettings(), settings.UnfocusedSettings(), connection);
|
||||
|
||||
TermControl term{ content };
|
||||
|
||||
// GH#12515: ConPTY assumes it's hidden at the start. If we're not, let it know now.
|
||||
if (_visible)
|
||||
|
||||
@ -63,7 +63,7 @@ namespace winrt::TerminalApp::implementation
|
||||
struct TerminalPage : TerminalPageT<TerminalPage>
|
||||
{
|
||||
public:
|
||||
TerminalPage(TerminalApp::WindowProperties properties);
|
||||
TerminalPage(TerminalApp::WindowProperties properties, const TerminalApp::ContentManager& manager);
|
||||
|
||||
// This implements shobjidl's IInitializeWithWindow, but due to a XAML Compiler bug we cannot
|
||||
// put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331
|
||||
@ -224,9 +224,10 @@ namespace winrt::TerminalApp::implementation
|
||||
bool _renamerPressedEnter{ false };
|
||||
|
||||
TerminalApp::WindowProperties _WindowProperties{ nullptr };
|
||||
|
||||
PaneResources _paneResources;
|
||||
|
||||
TerminalApp::ContentManager _manager{ nullptr };
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConptyConnection::NewConnection_revoker _newConnectionRevoker;
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::UI::Xaml::Controls::ContentDialogResult> _ShowDialogHelper(const std::wstring_view& name);
|
||||
|
||||
@ -5,6 +5,13 @@ import "IDirectKeyListener.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass ContentManager
|
||||
{
|
||||
Microsoft.Terminal.Control.ControlInteractivity CreateCore(Microsoft.Terminal.Control.IControlSettings settings,
|
||||
Microsoft.Terminal.Control.IControlAppearance unfocusedAppearance,
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
Microsoft.Terminal.Control.ControlInteractivity TryLookupCore(UInt64 id);
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass LastTabClosedEventArgs
|
||||
{
|
||||
@ -33,7 +40,7 @@ namespace TerminalApp
|
||||
|
||||
[default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener
|
||||
{
|
||||
TerminalPage(WindowProperties properties);
|
||||
TerminalPage(WindowProperties properties, ContentManager manager);
|
||||
|
||||
// XAML bound properties
|
||||
String ApplicationDisplayName { get; };
|
||||
|
||||
@ -118,8 +118,10 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalWindow::TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult) :
|
||||
TerminalWindow::TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult,
|
||||
const TerminalApp::ContentManager& manager) :
|
||||
_settings{ settingsLoadedResult.NewSettings() },
|
||||
_manager{ manager },
|
||||
_initialLoadResult{ settingsLoadedResult },
|
||||
_WindowProperties{ winrt::make_self<TerminalApp::implementation::WindowProperties>() }
|
||||
{
|
||||
@ -138,7 +140,7 @@ namespace winrt::TerminalApp::implementation
|
||||
HRESULT TerminalWindow::Initialize(HWND hwnd)
|
||||
{
|
||||
// Now that we know we can do XAML, build our page.
|
||||
_root = winrt::make_self<TerminalPage>(*_WindowProperties);
|
||||
_root = winrt::make_self<TerminalPage>(*_WindowProperties, _manager);
|
||||
_dialog = ContentDialog{};
|
||||
|
||||
// Pass commandline args into the TerminalPage. If we were supposed to
|
||||
|
||||
@ -58,7 +58,7 @@ namespace winrt::TerminalApp::implementation
|
||||
struct TerminalWindow : TerminalWindowT<TerminalWindow, IInitializeWithWindow>
|
||||
{
|
||||
public:
|
||||
TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult);
|
||||
TerminalWindow(const TerminalApp::SettingsLoadEventArgs& settingsLoadedResult, const TerminalApp::ContentManager& manager);
|
||||
~TerminalWindow() = default;
|
||||
|
||||
STDMETHODIMP Initialize(HWND hwnd);
|
||||
@ -177,6 +177,8 @@ namespace winrt::TerminalApp::implementation
|
||||
Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
|
||||
TerminalApp::SettingsLoadEventArgs _initialLoadResult{ nullptr };
|
||||
|
||||
TerminalApp::ContentManager _manager{ nullptr };
|
||||
|
||||
void _ShowLoadErrorsDialog(const winrt::hstring& titleKey,
|
||||
const winrt::hstring& contentKey,
|
||||
HRESULT settingsLoadedResult,
|
||||
|
||||
@ -42,7 +42,7 @@ namespace TerminalApp
|
||||
// information.
|
||||
[default_interface] runtimeclass TerminalWindow : IDirectKeyListener, IDialogPresenter, Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
TerminalWindow(SettingsLoadEventArgs result);
|
||||
TerminalWindow(SettingsLoadEventArgs result, ContentManager manager);
|
||||
|
||||
// For your own sanity, it's better to do setup outside the ctor.
|
||||
// If you do any setup in the ctor that ends up throwing an exception,
|
||||
|
||||
@ -126,7 +126,6 @@ namespace Microsoft.Terminal.Control
|
||||
String HoveredUriText { get; };
|
||||
Windows.Foundation.IReference<Microsoft.Terminal.Core.Point> HoveredCell { get; };
|
||||
|
||||
void Close();
|
||||
void BlinkCursor();
|
||||
Boolean IsInReadOnlyMode { get; };
|
||||
Boolean CursorOn;
|
||||
|
||||
@ -27,6 +27,8 @@ static constexpr unsigned int MAX_CLICK_COUNT = 3;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
std::atomic<uint64_t> ControlInteractivity::_nextId{ 1 };
|
||||
|
||||
static constexpr TerminalInput::MouseButtonState toInternalMouseState(const Control::MouseButtonState& state)
|
||||
{
|
||||
return TerminalInput::MouseButtonState{
|
||||
@ -44,9 +46,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
_lastMouseClickPos{},
|
||||
_selectionNeedsToBeCopied{ false }
|
||||
{
|
||||
_id = _nextId.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
_core = winrt::make_self<ControlCore>(settings, unfocusedAppearance, connection);
|
||||
}
|
||||
|
||||
uint64_t ControlInteractivity::Id()
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates our internal settings. These settings should be
|
||||
// interactivity-specific. Right now, we primarily update _rowsToScroll
|
||||
@ -73,6 +82,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return *_core;
|
||||
}
|
||||
|
||||
void ControlInteractivity::Close()
|
||||
{
|
||||
_ClosedHandlers(*this, nullptr);
|
||||
if (_core)
|
||||
{
|
||||
_core->Close();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns the number of clicks that occurred (double and triple click support).
|
||||
// Every call to this function registers a click.
|
||||
|
||||
@ -44,6 +44,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void Initialize();
|
||||
Control::ControlCore Core();
|
||||
|
||||
void Close();
|
||||
|
||||
Control::InteractivityAutomationPeer OnCreateAutomationPeer();
|
||||
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
|
||||
|
||||
@ -85,11 +87,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void SetEndSelectionPoint(const Core::Point pixelPosition);
|
||||
bool ManglePathsForWsl();
|
||||
|
||||
uint64_t Id();
|
||||
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
|
||||
TYPED_EVENT(ScrollPositionChanged, IInspectable, Control::ScrollPositionChangedArgs);
|
||||
TYPED_EVENT(ContextMenuRequested, IInspectable, Control::ContextMenuRequestedEventArgs);
|
||||
|
||||
TYPED_EVENT(Closed, IInspectable, IInspectable);
|
||||
|
||||
private:
|
||||
// NOTE: _uiaEngine must be ordered before _core.
|
||||
//
|
||||
@ -130,6 +136,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval{ std::nullopt };
|
||||
|
||||
uint64_t _id;
|
||||
static std::atomic<uint64_t> _nextId;
|
||||
|
||||
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
|
||||
void _updateSystemParameterSettings() noexcept;
|
||||
|
||||
|
||||
@ -23,6 +23,10 @@ namespace Microsoft.Terminal.Control
|
||||
void GotFocus();
|
||||
void LostFocus();
|
||||
|
||||
UInt64 Id { get; };
|
||||
|
||||
void Close();
|
||||
|
||||
InteractivityAutomationPeer OnCreateAutomationPeer();
|
||||
|
||||
Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
|
||||
@ -65,6 +69,8 @@ namespace Microsoft.Terminal.Control
|
||||
event Windows.Foundation.TypedEventHandler<Object, ScrollPositionChangedArgs> ScrollPositionChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, PasteFromClipboardEventArgs> PasteFromClipboard;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> Closed;
|
||||
|
||||
// Used to communicate to the TermControl, but not necessarily higher up in the stack
|
||||
event Windows.Foundation.TypedEventHandler<Object, ContextMenuRequestedEventArgs> ContextMenuRequested;
|
||||
|
||||
|
||||
@ -50,6 +50,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TermControl::TermControl(IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ITerminalConnection connection) :
|
||||
TermControl{ winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection) }
|
||||
{
|
||||
}
|
||||
|
||||
TermControl::TermControl(Control::ControlInteractivity content) :
|
||||
_interactivity{ content },
|
||||
_isInternalScrollBarUpdate{ false },
|
||||
_autoScrollVelocity{ 0 },
|
||||
_autoScrollingPointerPoint{ std::nullopt },
|
||||
@ -61,32 +67,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_interactivity = winrt::make<implementation::ControlInteractivity>(settings, unfocusedAppearance, connection);
|
||||
_core = _interactivity.Core();
|
||||
|
||||
// These events might all be triggered by the connection, but that
|
||||
// should be drained and closed before we complete destruction. So these
|
||||
// are safe.
|
||||
_core.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
|
||||
_core.WarningBell({ this, &TermControl::_coreWarningBell });
|
||||
_core.CursorPositionChanged({ this, &TermControl::_CursorPositionChanged });
|
||||
_revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged });
|
||||
_revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell });
|
||||
_revokers.CursorPositionChanged = _core.CursorPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_CursorPositionChanged });
|
||||
|
||||
// This event is specifically triggered by the renderer thread, a BG thread. Use a weak ref here.
|
||||
_core.RendererEnteredErrorState({ get_weak(), &TermControl::_RendererEnteredErrorState });
|
||||
_revokers.RendererEnteredErrorState = _core.RendererEnteredErrorState(winrt::auto_revoke, { get_weak(), &TermControl::_RendererEnteredErrorState });
|
||||
|
||||
// These callbacks can only really be triggered by UI interactions. So
|
||||
// they don't need weak refs - they can't be triggered unless we're
|
||||
// alive.
|
||||
_core.BackgroundColorChanged({ this, &TermControl::_coreBackgroundColorChanged });
|
||||
_core.FontSizeChanged({ this, &TermControl::_coreFontSizeChanged });
|
||||
_core.TransparencyChanged({ this, &TermControl::_coreTransparencyChanged });
|
||||
_core.RaiseNotice({ this, &TermControl::_coreRaisedNotice });
|
||||
_core.HoveredHyperlinkChanged({ this, &TermControl::_hoveredHyperlinkChanged });
|
||||
_core.FoundMatch({ this, &TermControl::_coreFoundMatch });
|
||||
_core.UpdateSelectionMarkers({ this, &TermControl::_updateSelectionMarkers });
|
||||
_core.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
|
||||
_interactivity.OpenHyperlink({ this, &TermControl::_HyperlinkHandler });
|
||||
_interactivity.ScrollPositionChanged({ this, &TermControl::_ScrollPositionChanged });
|
||||
_revokers.BackgroundColorChanged = _core.BackgroundColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreBackgroundColorChanged });
|
||||
_revokers.FontSizeChanged = _core.FontSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreFontSizeChanged });
|
||||
_revokers.TransparencyChanged = _core.TransparencyChanged(winrt::auto_revoke, { get_weak(), &TermControl::_coreTransparencyChanged });
|
||||
_revokers.RaiseNotice = _core.RaiseNotice(winrt::auto_revoke, { get_weak(), &TermControl::_coreRaisedNotice });
|
||||
_revokers.HoveredHyperlinkChanged = _core.HoveredHyperlinkChanged(winrt::auto_revoke, { get_weak(), &TermControl::_hoveredHyperlinkChanged });
|
||||
_revokers.FoundMatch = _core.FoundMatch(winrt::auto_revoke, { get_weak(), &TermControl::_coreFoundMatch });
|
||||
_revokers.UpdateSelectionMarkers = _core.UpdateSelectionMarkers(winrt::auto_revoke, { get_weak(), &TermControl::_updateSelectionMarkers });
|
||||
_revokers.coreOpenHyperlink = _core.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler });
|
||||
_revokers.interactivityOpenHyperlink = _interactivity.OpenHyperlink(winrt::auto_revoke, { get_weak(), &TermControl::_HyperlinkHandler });
|
||||
_revokers.interactivityScrollPositionChanged = _interactivity.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged });
|
||||
|
||||
// "Bubbled" events - ones we want to handle, by raising our own event.
|
||||
_revokers.CopyToClipboard = _core.CopyToClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCopyToClipboard });
|
||||
_revokers.TitleChanged = _core.TitleChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTitleChanged });
|
||||
_revokers.TabColorChanged = _core.TabColorChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleTabColorChanged });
|
||||
_revokers.TaskbarProgressChanged = _core.TaskbarProgressChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSetTaskbarProgress });
|
||||
_revokers.ConnectionStateChanged = _core.ConnectionStateChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleConnectionStateChanged });
|
||||
_revokers.ShowWindowChanged = _core.ShowWindowChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleShowWindowChanged });
|
||||
_revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested });
|
||||
|
||||
_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });
|
||||
|
||||
_interactivity.ContextMenuRequested({ this, &TermControl::_contextMenuHandler });
|
||||
|
||||
@ -134,7 +150,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
|
||||
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
|
||||
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });
|
||||
_autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
|
||||
|
||||
_ApplyUISettings();
|
||||
|
||||
@ -888,7 +904,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// after Enable, then it'll be possible to paint the frame once
|
||||
// _before_ the warning handler is set up, and then warnings from
|
||||
// the first paint will be ignored!
|
||||
_core.RendererWarning({ get_weak(), &TermControl::_RendererWarning });
|
||||
_revokers.RendererWarning = _core.RendererWarning(winrt::auto_revoke, { get_weak(), &TermControl::_RendererWarning });
|
||||
|
||||
const auto coreInitialized = _core.Initialize(panelWidth,
|
||||
panelHeight,
|
||||
@ -899,7 +915,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
_interactivity.Initialize();
|
||||
|
||||
_core.SwapChainChanged({ get_weak(), &TermControl::RenderEngineSwapChainChanged });
|
||||
_revokers.SwapChainChanged = _core.SwapChainChanged(winrt::auto_revoke, { get_weak(), &TermControl::RenderEngineSwapChainChanged });
|
||||
_core.EnablePainting();
|
||||
|
||||
auto bufferHeight = _core.BufferHeight();
|
||||
@ -2054,11 +2070,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
_RestorePointerCursorHandlers(*this, nullptr);
|
||||
|
||||
_revokers = {};
|
||||
|
||||
// Disconnect the TSF input control so it doesn't receive EditContext events.
|
||||
TSFInputControl().Close();
|
||||
_autoScrollTimer.Stop();
|
||||
|
||||
_core.Close();
|
||||
_interactivity.Close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2995,9 +3013,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
// Ensure the marker is oriented properly
|
||||
// (i.e. if start is at the beginning of the buffer, it should be flipped)
|
||||
auto transform{ marker.RenderTransform().as<Windows::UI::Xaml::Media::ScaleTransform>() };
|
||||
transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0));
|
||||
marker.RenderTransform(transform);
|
||||
//
|
||||
// Note - This RenderTransform might not be a
|
||||
// ScaleTransform, if we haven't had a _coreFontSizeChanged
|
||||
// handled yet, because that's the first place we set the
|
||||
// RenderTransform
|
||||
if (const auto& transform{ marker.RenderTransform().try_as<Windows::UI::Xaml::Media::ScaleTransform>() })
|
||||
{
|
||||
transform.ScaleX(std::abs(transform.ScaleX()) * (flipMarker ? -1.0 : 1.0));
|
||||
marker.RenderTransform(transform);
|
||||
}
|
||||
|
||||
// Compute the location of the top left corner of the cell in DIPS
|
||||
auto terminalPos{ targetEnd ? markerData.EndPos : markerData.StartPos };
|
||||
|
||||
@ -25,6 +25,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
struct TermControl : TermControlT<TermControl>
|
||||
{
|
||||
TermControl(Control::ControlInteractivity content);
|
||||
|
||||
TermControl(IControlSettings settings,
|
||||
Control::IControlAppearance unfocusedAppearance,
|
||||
TerminalConnection::ITerminalConnection connection);
|
||||
@ -137,20 +139,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void AdjustOpacity(const double opacity, const bool relative);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
// clang-format off
|
||||
WINRT_CALLBACK(FontSizeChanged, Control::FontSizeChangedEventArgs);
|
||||
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs, _core, CopyToClipboard);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs, _core, TitleChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable, _core, TabColorChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable, _core, TaskbarProgressChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable, _core, ConnectionStateChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs, _core, ShowWindowChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable, _core, CloseTerminalRequested);
|
||||
// UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE
|
||||
// Those attach the handler to the core directly, and will explode if
|
||||
// the core ever gets detached & reattached to another window.
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
|
||||
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
|
||||
BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs);
|
||||
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
TYPED_EVENT(RaiseNotice, IInspectable, Control::NoticeEventArgs);
|
||||
@ -327,6 +331,36 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args);
|
||||
void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args);
|
||||
void _SearchCommandHandler(const IInspectable& sender, const IInspectable& args);
|
||||
|
||||
struct Revokers
|
||||
{
|
||||
Control::ControlCore::ScrollPositionChanged_revoker coreScrollPositionChanged;
|
||||
Control::ControlCore::WarningBell_revoker WarningBell;
|
||||
Control::ControlCore::CursorPositionChanged_revoker CursorPositionChanged;
|
||||
Control::ControlCore::RendererEnteredErrorState_revoker RendererEnteredErrorState;
|
||||
Control::ControlCore::BackgroundColorChanged_revoker BackgroundColorChanged;
|
||||
Control::ControlCore::FontSizeChanged_revoker FontSizeChanged;
|
||||
Control::ControlCore::TransparencyChanged_revoker TransparencyChanged;
|
||||
Control::ControlCore::RaiseNotice_revoker RaiseNotice;
|
||||
Control::ControlCore::HoveredHyperlinkChanged_revoker HoveredHyperlinkChanged;
|
||||
Control::ControlCore::FoundMatch_revoker FoundMatch;
|
||||
Control::ControlCore::UpdateSelectionMarkers_revoker UpdateSelectionMarkers;
|
||||
Control::ControlCore::OpenHyperlink_revoker coreOpenHyperlink;
|
||||
Control::ControlCore::CopyToClipboard_revoker CopyToClipboard;
|
||||
Control::ControlCore::TitleChanged_revoker TitleChanged;
|
||||
Control::ControlCore::TabColorChanged_revoker TabColorChanged;
|
||||
Control::ControlCore::TaskbarProgressChanged_revoker TaskbarProgressChanged;
|
||||
Control::ControlCore::ConnectionStateChanged_revoker ConnectionStateChanged;
|
||||
Control::ControlCore::ShowWindowChanged_revoker ShowWindowChanged;
|
||||
Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested;
|
||||
// These are set up in _InitializeTerminal
|
||||
Control::ControlCore::RendererWarning_revoker RendererWarning;
|
||||
Control::ControlCore::SwapChainChanged_revoker SwapChainChanged;
|
||||
|
||||
Control::ControlInteractivity::OpenHyperlink_revoker interactivityOpenHyperlink;
|
||||
Control::ControlInteractivity::ScrollPositionChanged_revoker interactivityScrollPositionChanged;
|
||||
Control::ControlInteractivity::PasteFromClipboard_revoker PasteFromClipboard;
|
||||
} _revokers{};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
import "IMouseWheelListener.idl";
|
||||
import "IControlSettings.idl";
|
||||
import "ControlInteractivity.idl";
|
||||
import "IDirectKeyListener.idl";
|
||||
import "EventArgs.idl";
|
||||
import "ICoreState.idl";
|
||||
@ -17,6 +18,8 @@ namespace Microsoft.Terminal.Control
|
||||
ICoreState,
|
||||
Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
TermControl(ControlInteractivity content);
|
||||
|
||||
TermControl(IControlSettings settings,
|
||||
IControlAppearance unfocusedAppearance,
|
||||
Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);
|
||||
|
||||
@ -102,6 +102,27 @@ public:
|
||||
winrt::event_token name(const Windows::Foundation::TypedEventHandler<sender, args>& h) { return handler.handlerName(h); } \
|
||||
void name(const winrt::event_token& token) noexcept { handler.handlerName(token); }
|
||||
|
||||
// This is a bit like *FORWARDED_TYPED_EVENT. When you use a forwarded event,
|
||||
// the handler gets added to the object that's raising the event. For example,
|
||||
// the TerminalPage might be the handler for the TermControl's
|
||||
// BackgroundColorChanged event, which is actually implemented by the
|
||||
// ControlCore. So when Core raises an event, it immediately calls the handler
|
||||
// on the Page.
|
||||
//
|
||||
// Instead, the BUBBLED event introduces an indirection layer. In the above
|
||||
// example, the Core would raise the event, but now the Control would handle it,
|
||||
// and raise an event with each of its own handlers.
|
||||
//
|
||||
// This allows us to detach the core from the control safely, without needing to
|
||||
// re-wire all the event handlers from page->control again.
|
||||
//
|
||||
// Implement like:
|
||||
//
|
||||
// _core.TitleChanged({ get_weak(), &TermControl::_bubbleTitleChanged });
|
||||
#define BUBBLED_FORWARDED_TYPED_EVENT(name, sender, args) \
|
||||
TYPED_EVENT(name, sender, args) \
|
||||
void _bubble##name(const sender& s, const args& a) { _##name##Handlers(s, a); }
|
||||
|
||||
// Use this macro to quick implement both the getter and setter for a property.
|
||||
// This should only be used for simple types where there's no logic in the
|
||||
// getter/setter beyond just accessing/updating the value.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user