mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-06 06:09:50 +00:00
Compare commits
40 Commits
v1.21.2701
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6254cf94bc | ||
|
|
7492a35a94 | ||
|
|
869472a60b | ||
|
|
d0170dcfe6 | ||
|
|
9628ef7056 | ||
|
|
d973f1de53 | ||
|
|
018a918f75 | ||
|
|
cb7a045aad | ||
|
|
6107c3e551 | ||
|
|
46469aa5e3 | ||
|
|
c869b47e13 | ||
|
|
9531069538 | ||
|
|
521e301541 | ||
|
|
842326daa5 | ||
|
|
fb7c80938b | ||
|
|
29d0d57656 | ||
|
|
cbd61b0a7d | ||
|
|
1cc9835454 | ||
|
|
86914bdfc1 | ||
|
|
e0b003ad4d | ||
|
|
f89368c19b | ||
|
|
5582e1bcc8 | ||
|
|
a23c1a24dc | ||
|
|
5f9add4000 | ||
|
|
11126f9b37 | ||
|
|
e31202b0b8 | ||
|
|
e6dc314c17 | ||
|
|
2d4030683a | ||
|
|
262d95aae5 | ||
|
|
63ba8e19fd | ||
|
|
1b39db7ab0 | ||
|
|
2dd8f409b2 | ||
|
|
049c043279 | ||
|
|
a1da6c117e | ||
|
|
7c9ffb0e02 | ||
|
|
84df8197d4 | ||
|
|
5b3aa54b56 | ||
|
|
ef6bb8a73c | ||
|
|
4e144425f0 | ||
|
|
f353323a23 |
@@ -5,6 +5,8 @@
|
||||
#include "App.h"
|
||||
|
||||
#include "TerminalPage.h"
|
||||
#include "ScratchpadContent.h"
|
||||
#include "OceanContent.h"
|
||||
#include "../WinRTUtils/inc/WtExeUtils.h"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
#include "Utils.h"
|
||||
@@ -1404,7 +1406,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto activePane{ activeTab->GetActivePane() })
|
||||
{
|
||||
_restartPaneConnection(activePane);
|
||||
_restartPaneConnection(activePane->GetContent().try_as<TerminalApp::TerminalPaneContent>(), nullptr);
|
||||
}
|
||||
}
|
||||
args.Handled(true);
|
||||
@@ -1419,4 +1421,24 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
args.Handled(true);
|
||||
}
|
||||
|
||||
void TerminalPage::_HandleOpenScratchpad(const IInspectable& sender,
|
||||
const ActionEventArgs& args)
|
||||
{
|
||||
if (Feature_ScratchpadPane::IsEnabled())
|
||||
{
|
||||
auto scratchPane{ winrt::make_self<OceanContent>() };
|
||||
scratchPane->SetHostingWindow(reinterpret_cast<uint64_t>(*_hostingHwnd));
|
||||
// auto scratchPane{ winrt::make_self<SettingsPaneContent>(_settings) };
|
||||
|
||||
// This is maybe a little wacky - add our key event handler to the pane
|
||||
// we made. So that we can get actions for keys that the content didn't
|
||||
// handle.
|
||||
scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
|
||||
|
||||
auto resultPane = std::make_shared<Pane>(*scratchPane);
|
||||
_SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
|
||||
args.Handled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
src/cascadia/TerminalApp/IPaneContent.idl
Normal file
55
src/cascadia/TerminalApp/IPaneContent.idl
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
|
||||
runtimeclass BellEventArgs
|
||||
{
|
||||
Boolean FlashTaskbar { get; };
|
||||
};
|
||||
|
||||
interface IPaneContent
|
||||
{
|
||||
Windows.UI.Xaml.FrameworkElement GetRoot();
|
||||
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
|
||||
Windows.Foundation.Size MinSize { get; };
|
||||
|
||||
String Title { get; };
|
||||
UInt64 TaskbarState { get; };
|
||||
UInt64 TaskbarProgress { get; };
|
||||
Boolean ReadOnly { get; };
|
||||
String Icon { get; };
|
||||
Windows.Foundation.IReference<Windows.UI.Color> TabColor { get; };
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
|
||||
Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(Boolean asContent);
|
||||
|
||||
void Focus(Windows.UI.Xaml.FocusState reason);
|
||||
|
||||
void Close();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, BellEventArgs> BellRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> TitleChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> TaskbarProgressChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusRequested;
|
||||
};
|
||||
|
||||
|
||||
enum PaneSnapDirection
|
||||
{
|
||||
Width,
|
||||
Height
|
||||
};
|
||||
|
||||
interface ISnappable
|
||||
{
|
||||
Single SnapDownToGrid(PaneSnapDirection direction, Single sizeToSnap);
|
||||
Windows.Foundation.Size GridSize { get; };
|
||||
};
|
||||
}
|
||||
202
src/cascadia/TerminalApp/OceanContent.cpp
Normal file
202
src/cascadia/TerminalApp/OceanContent.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "OceanContent.h"
|
||||
#include "PaneArgs.h"
|
||||
#include "OceanContent.g.cpp"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
OceanContent::OceanContent()
|
||||
{
|
||||
_root = winrt::Windows::UI::Xaml::Controls::Grid{};
|
||||
_root.VerticalAlignment(VerticalAlignment::Stretch);
|
||||
_root.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
|
||||
// auto res = Windows::UI::Xaml::Application::Current().Resources();
|
||||
// auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush"));
|
||||
// _root.Background(bg.try_as<Media::Brush>());
|
||||
|
||||
// transparent
|
||||
// return winrt::Windows::UI::Xaml::Media::SolidColorBrush{ winrt::Windows::UI::Colors::Transparent() };
|
||||
|
||||
// _box = winrt::Windows::UI::Xaml::Controls::TextBox{};
|
||||
// _box.Margin({ 10, 10, 10, 10 });
|
||||
// _box.AcceptsReturn(true);
|
||||
// _box.TextWrapping(TextWrapping::Wrap);
|
||||
// _root.Children().Append(_box);
|
||||
|
||||
_createOcean();
|
||||
|
||||
// Add a size change handler to the root grid.
|
||||
_sizeChangedRevoker = _root.SizeChanged(winrt::auto_revoke, [this](auto&&, auto&&) {
|
||||
_resizeToMatch();
|
||||
});
|
||||
}
|
||||
|
||||
void OceanContent::_resizeToMatch()
|
||||
{
|
||||
const auto widthInDips = _root.ActualWidth();
|
||||
const auto heightInDips = _root.ActualHeight();
|
||||
// wprintf(L"resized to: %f, %f\n", width, height);
|
||||
|
||||
// adjust for DPI
|
||||
const auto dpi = Windows::Graphics::Display::DisplayInformation::GetForCurrentView().LogicalDpi();
|
||||
const auto dpiScale = dpi / 96.0f;
|
||||
const auto widthInPixels = widthInDips * dpiScale;
|
||||
const auto heightInPixels = heightInDips * dpiScale;
|
||||
|
||||
// Get the actual location of our _root, relative to the screen
|
||||
const auto transform = _root.TransformToVisual(nullptr);
|
||||
const auto point = transform.TransformPoint({ 0, 0 });
|
||||
|
||||
// scale from DIPs to pixels:
|
||||
|
||||
// Resize our ocean HWND
|
||||
SetWindowPos(_window.get(),
|
||||
_hostingHwnd, // set it behind the parent
|
||||
gsl::narrow_cast<int>(point.X * dpiScale) + 48,
|
||||
gsl::narrow_cast<int>(point.Y * dpiScale) + 48,
|
||||
gsl::narrow_cast<int>(widthInPixels) - 96,
|
||||
gsl::narrow_cast<int>(heightInPixels) - 96,
|
||||
/* SWP_NOZORDER | SWP_NOMOVE */ SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
static OceanContent* GetThisFromHandle(HWND const window) noexcept
|
||||
{
|
||||
return reinterpret_cast<OceanContent*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
||||
}
|
||||
LRESULT __stdcall OceanContent::s_WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||||
{
|
||||
WINRT_ASSERT(window);
|
||||
|
||||
if (WM_NCCREATE == message)
|
||||
{
|
||||
auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||
OceanContent* that = static_cast<OceanContent*>(cs->lpCreateParams);
|
||||
that->_window = wil::unique_hwnd(window);
|
||||
|
||||
// return that->OnNcCreate(wparam, lparam);
|
||||
|
||||
SetWindowLongPtr(that->_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(that));
|
||||
|
||||
return DefWindowProc(that->_window.get(), WM_NCCREATE, wparam, lparam);
|
||||
}
|
||||
else if (OceanContent* that = GetThisFromHandle(window))
|
||||
{
|
||||
return that->_messageHandler(message, wparam, lparam);
|
||||
}
|
||||
|
||||
return DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
LRESULT OceanContent::_messageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(_window.get(), &ps);
|
||||
|
||||
// All painting occurs here, between BeginPaint and EndPaint.
|
||||
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_HIGHLIGHT + 1));
|
||||
|
||||
EndPaint(_window.get(), &ps);
|
||||
break;
|
||||
}
|
||||
case WM_CLOSE:
|
||||
case WM_DESTROY:
|
||||
{
|
||||
// PostQuitMessage(0);
|
||||
CloseRequested.raise(*this, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DefWindowProc(_window.get(), message, wparam, lparam);
|
||||
}
|
||||
|
||||
void OceanContent::_createOcean()
|
||||
{
|
||||
static const auto oceanClassAtom = []() {
|
||||
static const auto SCRATCH_WINDOW_CLASS = L"ocean_window_class";
|
||||
WNDCLASSEXW scratchClass{ 0 };
|
||||
scratchClass.cbSize = sizeof(WNDCLASSEXW);
|
||||
scratchClass.style = CS_HREDRAW | CS_VREDRAW | /*CS_PARENTDC |*/ CS_DBLCLKS;
|
||||
scratchClass.lpszClassName = SCRATCH_WINDOW_CLASS;
|
||||
scratchClass.lpfnWndProc = s_WndProc;
|
||||
scratchClass.cbWndExtra = 0; // GWL_CONSOLE_WNDALLOC; // this is required to store the owning thread/process override in NTUSER
|
||||
auto windowClassAtom{ RegisterClassExW(&scratchClass) };
|
||||
return windowClassAtom;
|
||||
}();
|
||||
const auto style = 0; // no resize border, no caption, etc.
|
||||
const auto exStyle = 0;
|
||||
|
||||
CreateWindowExW(exStyle, // WS_EX_LAYERED,
|
||||
reinterpret_cast<LPCWSTR>(oceanClassAtom),
|
||||
L"Hello World",
|
||||
style,
|
||||
200, // CW_USEDEFAULT,
|
||||
200, // CW_USEDEFAULT,
|
||||
200, // CW_USEDEFAULT,
|
||||
200, // CW_USEDEFAULT,
|
||||
0, // owner
|
||||
nullptr,
|
||||
nullptr,
|
||||
this);
|
||||
|
||||
SetWindowLong(_window.get(), GWL_STYLE, 0); //remove all window styles, cause it's created with WS_CAPTION even if we didn't ask for it
|
||||
ShowWindow(_window.get(), SW_SHOW); //display window
|
||||
_resizeToMatch();
|
||||
}
|
||||
|
||||
void OceanContent::SetHostingWindow(uint64_t hostingWindow) noexcept
|
||||
{
|
||||
_hostingHwnd = reinterpret_cast<HWND>(hostingWindow);
|
||||
// Set our HWND as owned by the parent's. We're always on top of them, but not explicitly a _child_.
|
||||
::SetWindowLongPtrW(_window.get(), GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(_hostingHwnd));
|
||||
}
|
||||
|
||||
void OceanContent::UpdateSettings(const CascadiaSettings& /*settings*/)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement OceanContent::GetRoot()
|
||||
{
|
||||
return _root;
|
||||
}
|
||||
winrt::Windows::Foundation::Size OceanContent::MinSize()
|
||||
{
|
||||
return { 1, 1 };
|
||||
}
|
||||
void OceanContent::Focus(winrt::Windows::UI::Xaml::FocusState /*reason*/)
|
||||
{
|
||||
// _box.Focus(reason);
|
||||
}
|
||||
void OceanContent::Close()
|
||||
{
|
||||
CloseRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
NewTerminalArgs OceanContent::GetNewTerminalArgs(const bool /* asContent */) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::hstring OceanContent::Icon() const
|
||||
{
|
||||
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
|
||||
return winrt::hstring{ glyph };
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush OceanContent::BackgroundBrush()
|
||||
{
|
||||
return _root.Background();
|
||||
}
|
||||
}
|
||||
53
src/cascadia/TerminalApp/OceanContent.h
Normal file
53
src/cascadia/TerminalApp/OceanContent.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include "OceanContent.g.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct OceanContent : OceanContentT<OceanContent>
|
||||
{
|
||||
OceanContent();
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
|
||||
|
||||
winrt::Windows::Foundation::Size MinSize();
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
|
||||
void Close();
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
|
||||
|
||||
winrt::hstring Title() { return L"Xaml Ocean"; }
|
||||
uint64_t TaskbarState() { return 0; }
|
||||
uint64_t TaskbarProgress() { return 0; }
|
||||
bool ReadOnly() { return false; }
|
||||
winrt::hstring Icon() const;
|
||||
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
|
||||
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
|
||||
|
||||
void SetHostingWindow(uint64_t hostingWindow) noexcept;
|
||||
|
||||
til::typed_event<> CloseRequested;
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::BellEventArgs> BellRequested;
|
||||
til::typed_event<> TitleChanged;
|
||||
til::typed_event<> TabColorChanged;
|
||||
til::typed_event<> TaskbarProgressChanged;
|
||||
til::typed_event<> ReadOnlyChanged;
|
||||
til::typed_event<> FocusRequested;
|
||||
|
||||
private:
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr };
|
||||
|
||||
wil::unique_hwnd _window;
|
||||
HWND _hostingHwnd;
|
||||
winrt::Windows::UI::Xaml::Controls::Grid::SizeChanged_revoker _sizeChangedRevoker;
|
||||
|
||||
void _createOcean();
|
||||
void _resizeToMatch();
|
||||
|
||||
LRESULT _messageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam);
|
||||
static LRESULT __stdcall s_WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept;
|
||||
};
|
||||
}
|
||||
@@ -33,19 +33,21 @@ static const int CombinedPaneBorderSize = 2 * PaneBorderSize;
|
||||
static const int AnimationDurationInMilliseconds = 200;
|
||||
static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Windows::Foundation::TimeSpan(std::chrono::milliseconds(AnimationDurationInMilliseconds)));
|
||||
|
||||
Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) :
|
||||
_control{ control },
|
||||
_lastActive{ lastFocused },
|
||||
_profile{ profile }
|
||||
Pane::Pane(const IPaneContent& content, const bool lastFocused) :
|
||||
_content{ content },
|
||||
_lastActive{ lastFocused }
|
||||
{
|
||||
_root.Children().Append(_borderFirst);
|
||||
_borderFirst.Child(_control);
|
||||
|
||||
_setupControlEvents();
|
||||
const auto& control{ _content.GetRoot() };
|
||||
_borderFirst.Child(control);
|
||||
|
||||
// Register an event with the control to have it inform us when it gains focus.
|
||||
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
|
||||
_lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
|
||||
if (control)
|
||||
{
|
||||
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ContentGotFocusHandler });
|
||||
_lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler });
|
||||
}
|
||||
|
||||
// When our border is tapped, make sure to transfer focus to our control.
|
||||
// LOAD-BEARING: This will NOT work if the border's BorderBrush is set to
|
||||
@@ -102,19 +104,6 @@ Pane::Pane(std::shared_ptr<Pane> first,
|
||||
});
|
||||
}
|
||||
|
||||
void Pane::_setupControlEvents()
|
||||
{
|
||||
_controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &Pane::_ControlConnectionStateChangedHandler });
|
||||
_controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { this, &Pane::_ControlWarningBellHandler });
|
||||
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &Pane::_CloseTerminalRequestedHandler });
|
||||
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &Pane::_RestartTerminalRequestedHandler });
|
||||
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { this, &Pane::_ControlReadOnlyChangedHandler });
|
||||
}
|
||||
void Pane::_removeControlEvents()
|
||||
{
|
||||
_controlEvents = {};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Extract the terminal settings from the current (leaf) pane's control
|
||||
// to be used to create an equivalent control
|
||||
@@ -129,55 +118,7 @@ NewTerminalArgs Pane::GetTerminalArgsForPane(const bool asContent) const
|
||||
// Leaves are the only things that have controls
|
||||
assert(_IsLeaf());
|
||||
|
||||
NewTerminalArgs args{};
|
||||
auto controlSettings = _control.Settings();
|
||||
|
||||
args.Profile(controlSettings.ProfileName());
|
||||
// If we know the user's working directory use it instead of the profile.
|
||||
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
|
||||
{
|
||||
args.StartingDirectory(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.StartingDirectory(controlSettings.StartingDirectory());
|
||||
}
|
||||
args.TabTitle(controlSettings.StartingTitle());
|
||||
args.Commandline(controlSettings.Commandline());
|
||||
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
|
||||
if (controlSettings.TabColor() || controlSettings.StartingTabColor())
|
||||
{
|
||||
til::color c;
|
||||
// StartingTabColor is prioritized over other colors
|
||||
if (const auto color = controlSettings.StartingTabColor())
|
||||
{
|
||||
c = til::color(color.Value());
|
||||
}
|
||||
else
|
||||
{
|
||||
c = til::color(controlSettings.TabColor().Value());
|
||||
}
|
||||
|
||||
args.TabColor(winrt::Windows::Foundation::IReference<winrt::Windows::UI::Color>{ static_cast<winrt::Windows::UI::Color>(c) });
|
||||
}
|
||||
|
||||
// TODO:GH#9800 - we used to be able to persist the color scheme that a
|
||||
// TermControl was initialized with, by name. With the change to having the
|
||||
// control own its own copy of its settings, this isn't possible anymore.
|
||||
//
|
||||
// We may be able to get around this by storing the Name in the Core::Scheme
|
||||
// object. That would work for schemes set by the Terminal, but not ones set
|
||||
// by VT, but that seems good enough.
|
||||
|
||||
// Only fill in the ContentId if absolutely needed. If you fill in a number
|
||||
// here (even 0), we'll serialize that number, AND treat that action as an
|
||||
// "attach existing" rather than a "create"
|
||||
if (asContent)
|
||||
{
|
||||
args.ContentId(_control.ContentId());
|
||||
}
|
||||
|
||||
return args;
|
||||
return _content.GetNewTerminalArgs(asContent);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1021,184 +962,6 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr<Pane>
|
||||
return { nullptr, nullptr, offset };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when our attached control is closed. Triggers listeners to our close
|
||||
// event, if we're a leaf pane.
|
||||
// - If this was called, and we became a parent pane (due to work on another
|
||||
// thread), this function will do nothing (allowing the control's new parent
|
||||
// to handle the event instead).
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
auto newConnectionState = ConnectionState::Closed;
|
||||
if (const auto coreState = sender.try_as<ICoreState>())
|
||||
{
|
||||
newConnectionState = coreState.ConnectionState();
|
||||
}
|
||||
|
||||
if (newConnectionState < ConnectionState::Closed)
|
||||
{
|
||||
// Pane doesn't care if the connection isn't entering a terminal state.
|
||||
co_return;
|
||||
}
|
||||
|
||||
const auto weakThis = weak_from_this();
|
||||
co_await wil::resume_foreground(_root.Dispatcher());
|
||||
const auto strongThis = weakThis.lock();
|
||||
if (!strongThis)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
// It's possible that this event handler started being executed, scheduled
|
||||
// on the UI thread, another child got created. So our control is
|
||||
// actually no longer _our_ control, and instead could be a descendant.
|
||||
//
|
||||
// When the control's new Pane takes ownership of the control, the new
|
||||
// parent will register its own event handler. That event handler will get
|
||||
// fired after this handler returns, and will properly cleanup state.
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
const auto previousConnectionState = std::exchange(_connectionState, newConnectionState);
|
||||
if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed)
|
||||
{
|
||||
// A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit".
|
||||
// This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting
|
||||
// in Terminal flashing open and immediately closed.
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (_profile)
|
||||
{
|
||||
if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic)
|
||||
{
|
||||
// For 'automatic', we only care about the connection state if we were launched by Terminal
|
||||
// Since we were launched via defterm, ignore the connection state (i.e. we treat the
|
||||
// close on exit mode as 'always', see GH #13325 for discussion)
|
||||
Close();
|
||||
}
|
||||
|
||||
const auto mode = _profile.CloseOnExit();
|
||||
if ((mode == CloseOnExitMode::Always) ||
|
||||
((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Pane::_CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
// It's possible that this event handler started being executed, then before
|
||||
// we got the lock, another thread created another child. So our control is
|
||||
// actually no longer _our_ control, and instead could be a descendant.
|
||||
//
|
||||
// When the control's new Pane takes ownership of the control, the new
|
||||
// parent will register its own event handler. That event handler will get
|
||||
// fired after this handler returns, and will properly cleanup state.
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
void Pane::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
return;
|
||||
}
|
||||
_RestartTerminalRequestedHandlers(shared_from_this());
|
||||
}
|
||||
|
||||
winrt::fire_and_forget Pane::_playBellSound(winrt::Windows::Foundation::Uri uri)
|
||||
{
|
||||
auto weakThis{ weak_from_this() };
|
||||
|
||||
co_await wil::resume_foreground(_root.Dispatcher());
|
||||
if (auto pane{ weakThis.lock() })
|
||||
{
|
||||
if (!_bellPlayerCreated)
|
||||
{
|
||||
// The MediaPlayer might not exist on Windows N SKU.
|
||||
try
|
||||
{
|
||||
_bellPlayerCreated = true;
|
||||
_bellPlayer = winrt::Windows::Media::Playback::MediaPlayer();
|
||||
// GH#12258: The media keys (like play/pause) should have no effect on our bell sound.
|
||||
_bellPlayer.CommandManager().IsEnabled(false);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
if (_bellPlayer)
|
||||
{
|
||||
const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) };
|
||||
const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) };
|
||||
_bellPlayer.Source(item);
|
||||
_bellPlayer.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Plays a warning note when triggered by the BEL control character,
|
||||
// using the sound configured for the "Critical Stop" system event.`
|
||||
// This matches the behavior of the Windows Console host.
|
||||
// - Will also flash the taskbar if the bellStyle setting for this profile
|
||||
// has the 'visual' flag set
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
|
||||
{
|
||||
if (!_IsLeaf())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_profile)
|
||||
{
|
||||
// We don't want to do anything if nothing is set, so check for that first
|
||||
if (static_cast<int>(_profile.BellStyle()) != 0)
|
||||
{
|
||||
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
|
||||
{
|
||||
// Audible is set, play the sound
|
||||
auto sounds{ _profile.BellSound() };
|
||||
if (sounds && sounds.Size() > 0)
|
||||
{
|
||||
winrt::hstring soundPath{ wil::ExpandEnvironmentStringsW<std::wstring>(sounds.GetAt(rand() % sounds.Size()).c_str()) };
|
||||
winrt::Windows::Foundation::Uri uri{ soundPath };
|
||||
_playBellSound(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
|
||||
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
|
||||
}
|
||||
}
|
||||
|
||||
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
|
||||
{
|
||||
_control.BellLightOn();
|
||||
}
|
||||
|
||||
// raise the event with the bool value corresponding to the taskbar flag
|
||||
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event Description:
|
||||
// - Called when our control gains focus. We'll use this to trigger our GotFocus
|
||||
// callback. The tab that's hosting us should have registered a callback which
|
||||
@@ -1207,7 +970,7 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
|
||||
// - <unused>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
void Pane::_ContentGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const RoutedEventArgs& /* args */)
|
||||
{
|
||||
auto f = FocusState::Programmatic;
|
||||
@@ -1222,7 +985,7 @@ void Pane::_ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectabl
|
||||
// - Called when our control loses focus. We'll use this to trigger our LostFocus
|
||||
// callback. The tab that's hosting us should have registered a callback which
|
||||
// can be used to update its own internal focus state
|
||||
void Pane::_ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */,
|
||||
void Pane::_ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& /* sender */,
|
||||
const RoutedEventArgs& /* args */)
|
||||
{
|
||||
_LostFocusHandlers(shared_from_this());
|
||||
@@ -1245,21 +1008,9 @@ void Pane::Close()
|
||||
// and connections beneath it.
|
||||
void Pane::Shutdown()
|
||||
{
|
||||
// Clear out our media player callbacks, and stop any playing media. This
|
||||
// will prevent the callback from being triggered after we've closed, and
|
||||
// also make sure that our sound stops when we're closed.
|
||||
if (_bellPlayer)
|
||||
{
|
||||
_bellPlayer.Pause();
|
||||
_bellPlayer.Source(nullptr);
|
||||
_bellPlayer.Close();
|
||||
_bellPlayer = nullptr;
|
||||
_bellPlayerCreated = false;
|
||||
}
|
||||
|
||||
if (_IsLeaf())
|
||||
{
|
||||
_control.Close();
|
||||
_content.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1314,7 +1065,14 @@ TermControl Pane::GetLastFocusedTerminalControl()
|
||||
{
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
return p->_control;
|
||||
if (const auto& terminalPane{ p->_content.try_as<TerminalPaneContent>() })
|
||||
{
|
||||
return terminalPane.GetTerminal();
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
pane = p;
|
||||
}
|
||||
@@ -1322,7 +1080,15 @@ TermControl Pane::GetLastFocusedTerminalControl()
|
||||
}
|
||||
return _firstChild->GetLastFocusedTerminalControl();
|
||||
}
|
||||
return _control;
|
||||
|
||||
if (const auto& terminalPane{ _content.try_as<TerminalPaneContent>() })
|
||||
{
|
||||
return terminalPane.GetTerminal();
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1334,7 +1100,14 @@ TermControl Pane::GetLastFocusedTerminalControl()
|
||||
// - nullptr if this Pane is a parent, otherwise the TermControl of this Pane.
|
||||
TermControl Pane::GetTerminalControl()
|
||||
{
|
||||
return _IsLeaf() ? _control : nullptr;
|
||||
if (const auto& terminalPane{ _getTerminalContent() })
|
||||
{
|
||||
return terminalPane.GetTerminal();
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1381,7 +1154,11 @@ void Pane::SetActive()
|
||||
Profile Pane::GetFocusedProfile()
|
||||
{
|
||||
auto lastFocused = GetActivePane();
|
||||
return lastFocused ? lastFocused->_profile : nullptr;
|
||||
if (const auto& terminalPane{ lastFocused->_getTerminalContent() })
|
||||
{
|
||||
return terminalPane.GetProfile();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1454,10 +1231,7 @@ void Pane::UpdateVisuals()
|
||||
void Pane::_Focus()
|
||||
{
|
||||
_GotFocusHandlers(shared_from_this(), FocusState::Programmatic);
|
||||
if (const auto& control = GetLastFocusedTerminalControl())
|
||||
{
|
||||
control.Focus(FocusState::Programmatic);
|
||||
}
|
||||
_content.Focus(FocusState::Programmatic);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1496,6 +1270,14 @@ void Pane::_FocusFirstChild()
|
||||
}
|
||||
}
|
||||
|
||||
void Pane::UpdateSettings(const CascadiaSettings& settings)
|
||||
{
|
||||
if (_content)
|
||||
{
|
||||
_content.UpdateSettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Updates the settings of this pane, presuming that it is a leaf.
|
||||
// Arguments:
|
||||
@@ -1503,13 +1285,14 @@ void Pane::_FocusFirstChild()
|
||||
// - profile: The profile from which these settings originated.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
|
||||
void Pane::UpdateTerminalSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
|
||||
{
|
||||
assert(_IsLeaf());
|
||||
|
||||
_profile = profile;
|
||||
|
||||
_control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings());
|
||||
if (const auto& terminalPane{ _getTerminalContent() })
|
||||
{
|
||||
return terminalPane.UpdateTerminalSettings(settings, profile);
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1595,7 +1378,7 @@ std::shared_ptr<Pane> Pane::DetachPane(std::shared_ptr<Pane> pane)
|
||||
// reattached to a tree somewhere as the control moves with the pane.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
||||
void Pane::_CloseChild(const bool closeFirst, const bool /*isDetaching*/)
|
||||
{
|
||||
// If we're a leaf, then chances are both our children closed in close
|
||||
// succession. We waited on the lock while the other child was closed, so
|
||||
@@ -1631,35 +1414,16 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
||||
_borders = _GetCommonBorders();
|
||||
|
||||
// take the control, profile, id and isDefTermSession of the pane that _wasn't_ closed.
|
||||
_control = remainingChild->_control;
|
||||
_connectionState = remainingChild->_connectionState;
|
||||
_profile = remainingChild->_profile;
|
||||
_content = remainingChild->_content;
|
||||
_id = remainingChild->Id();
|
||||
_isDefTermSession = remainingChild->_isDefTermSession;
|
||||
|
||||
// Add our new event handler before revoking the old one.
|
||||
_setupControlEvents();
|
||||
|
||||
// Revoke the old event handlers. Remove both the handlers for the panes
|
||||
// themselves closing, and remove their handlers for their controls
|
||||
// closing. At this point, if the remaining child's control is closed,
|
||||
// they'll trigger only our event handler for the control's close.
|
||||
|
||||
// However, if we are detaching the pane we want to keep its control
|
||||
// handlers since it is just getting moved.
|
||||
if (!isDetaching)
|
||||
{
|
||||
closedChild->WalkTree([](auto p) {
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->_removeControlEvents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
closedChild->Closed(closedChildClosedToken);
|
||||
remainingChild->Closed(remainingChildClosedToken);
|
||||
remainingChild->_removeControlEvents();
|
||||
|
||||
// If we or either of our children was focused, we want to take that
|
||||
// focus from them.
|
||||
@@ -1679,7 +1443,8 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
||||
|
||||
// Reattach the TermControl to our grid.
|
||||
_root.Children().Append(_borderFirst);
|
||||
_borderFirst.Child(_control);
|
||||
const auto& control{ _content.GetRoot() };
|
||||
_borderFirst.Child(control);
|
||||
|
||||
// Make sure to set our _splitState before focusing the control. If you
|
||||
// fail to do this, when the tab handles the GotFocus event and asks us
|
||||
@@ -1688,14 +1453,17 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
||||
_splitState = SplitState::None;
|
||||
|
||||
// re-attach our handler for the control's GotFocus event.
|
||||
_gotFocusRevoker = _control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });
|
||||
_lostFocusRevoker = _control.LostFocus(winrt::auto_revoke, { this, &Pane::_ControlLostFocusHandler });
|
||||
if (control)
|
||||
{
|
||||
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ContentGotFocusHandler });
|
||||
_lostFocusRevoker = control.LostFocus(winrt::auto_revoke, { this, &Pane::_ContentLostFocusHandler });
|
||||
}
|
||||
|
||||
// If we're inheriting the "last active" state from one of our children,
|
||||
// focus our control now. This should trigger our own GotFocus event.
|
||||
if (usedToFocusClosedChildsTerminal || _lastActive)
|
||||
{
|
||||
_control.Focus(FocusState::Programmatic);
|
||||
_content.Focus(FocusState::Programmatic);
|
||||
|
||||
// See GH#7252
|
||||
// Manually fire off the GotFocus event. Typically, this is done
|
||||
@@ -1734,15 +1502,6 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
|
||||
// Remove the event handlers on the old children
|
||||
remainingChild->Closed(remainingChildClosedToken);
|
||||
closedChild->Closed(closedChildClosedToken);
|
||||
if (!isDetaching)
|
||||
{
|
||||
closedChild->WalkTree([](auto p) {
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->_removeControlEvents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Reset our UI:
|
||||
_root.Children().Clear();
|
||||
@@ -2161,7 +1920,7 @@ void Pane::_SetupEntranceAnimation()
|
||||
auto child = isFirstChild ? _firstChild : _secondChild;
|
||||
auto childGrid = child->_root;
|
||||
// If we are splitting a parent pane this may be null
|
||||
auto control = child->_control;
|
||||
auto control = child->_content.GetRoot();
|
||||
// Build up our animation:
|
||||
// * it'll take as long as our duration (200ms)
|
||||
// * it'll change the value of our property from 0 to secondSize
|
||||
@@ -2482,9 +2241,6 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
|
||||
|
||||
if (_IsLeaf())
|
||||
{
|
||||
// revoke our handler - the child will take care of the control now.
|
||||
_removeControlEvents();
|
||||
|
||||
// Remove our old GotFocus handler from the control. We don't want the
|
||||
// control telling us that it's now focused, we want it telling its new
|
||||
// parent.
|
||||
@@ -2513,11 +2269,8 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitDirect
|
||||
else
|
||||
{
|
||||
// Move our control, guid, isDefTermSession into the first one.
|
||||
_firstChild = std::make_shared<Pane>(_profile, _control);
|
||||
_firstChild->_connectionState = std::exchange(_connectionState, ConnectionState::NotConnected);
|
||||
_profile = nullptr;
|
||||
_control = { nullptr };
|
||||
_firstChild->_isDefTermSession = _isDefTermSession;
|
||||
_firstChild = std::make_shared<Pane>(_content);
|
||||
_content = nullptr;
|
||||
}
|
||||
|
||||
_splitState = actualSplitType;
|
||||
@@ -2839,8 +2592,16 @@ float Pane::CalcSnappedDimension(const bool widthOrHeight, const float dimension
|
||||
// If requested size is already snapped, then both returned values equal this value.
|
||||
Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
|
||||
{
|
||||
const auto direction{ widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height };
|
||||
|
||||
if (_IsLeaf())
|
||||
{
|
||||
const auto& snappable{ _content.try_as<ISnappable>() };
|
||||
if (!snappable)
|
||||
{
|
||||
return { dimension, dimension };
|
||||
}
|
||||
|
||||
// If we're a leaf pane, align to the grid of controlling terminal
|
||||
|
||||
const auto minSize = _GetMinSize();
|
||||
@@ -2851,8 +2612,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
|
||||
return { minDimension, minDimension };
|
||||
}
|
||||
|
||||
auto lower = _control.SnapDimensionToGrid(widthOrHeight, dimension);
|
||||
if (widthOrHeight)
|
||||
auto lower = snappable.SnapDownToGrid(widthOrHeight ? PaneSnapDirection::Width : PaneSnapDirection::Height,
|
||||
dimension);
|
||||
|
||||
if (direction == PaneSnapDirection::Width)
|
||||
{
|
||||
lower += WI_IsFlagSet(_borders, Borders::Left) ? PaneBorderSize : 0;
|
||||
lower += WI_IsFlagSet(_borders, Borders::Right) ? PaneBorderSize : 0;
|
||||
@@ -2871,8 +2634,10 @@ Pane::SnapSizeResult Pane::_CalcSnappedDimension(const bool widthOrHeight, const
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cellSize = _control.CharacterDimensions();
|
||||
const auto higher = lower + (widthOrHeight ? cellSize.Width : cellSize.Height);
|
||||
const auto cellSize = snappable.GridSize();
|
||||
const auto higher = lower + (direction == PaneSnapDirection::Width ?
|
||||
cellSize.Width :
|
||||
cellSize.Height);
|
||||
return { lower, higher };
|
||||
}
|
||||
}
|
||||
@@ -2918,21 +2683,36 @@ void Pane::_AdvanceSnappedDimension(const bool widthOrHeight, LayoutSizeNode& si
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
// We're a leaf pane, so just add one more row or column (unless isMinimumSize
|
||||
// is true, see below).
|
||||
|
||||
if (sizeNode.isMinimumSize)
|
||||
const auto& snappable{ _content.try_as<ISnappable>() };
|
||||
if (snappable)
|
||||
{
|
||||
// If the node is of its minimum size, this size might not be snapped (it might
|
||||
// be, say, half a character, or fixed 10 pixels), so snap it upward. It might
|
||||
// however be already snapped, so add 1 to make sure it really increases
|
||||
// (not strictly necessary but to avoid surprises).
|
||||
sizeNode.size = _CalcSnappedDimension(widthOrHeight, sizeNode.size + 1).higher;
|
||||
// We're a leaf pane, so just add one more row or column (unless isMinimumSize
|
||||
// is true, see below).
|
||||
|
||||
if (sizeNode.isMinimumSize)
|
||||
{
|
||||
// If the node is of its minimum size, this size might not be snapped (it might
|
||||
// be, say, half a character, or fixed 10 pixels), so snap it upward. It might
|
||||
// however be already snapped, so add 1 to make sure it really increases
|
||||
// (not strictly necessary but to avoid surprises).
|
||||
sizeNode.size = _CalcSnappedDimension(widthOrHeight,
|
||||
sizeNode.size + 1)
|
||||
.higher;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cellSize = snappable.GridSize();
|
||||
sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto cellSize = _control.CharacterDimensions();
|
||||
sizeNode.size += widthOrHeight ? cellSize.Width : cellSize.Height;
|
||||
// If we're a leaf that didn't have a TermControl, then just increment
|
||||
// by one. We have to increment by _some_ value, because this is used in
|
||||
// a while() loop to find the next bigger size we can snap to. But since
|
||||
// a non-terminal control doesn't really care what size it's snapped to,
|
||||
// we can just say "one pixel larger is the next snap point"
|
||||
sizeNode.size += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3037,7 +2817,7 @@ Size Pane::_GetMinSize() const
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
auto controlSize = _control.MinimumSize();
|
||||
auto controlSize = _content.MinSize();
|
||||
auto newWidth = controlSize.Width;
|
||||
auto newHeight = controlSize.Height;
|
||||
|
||||
@@ -3135,14 +2915,17 @@ int Pane::GetLeafPaneCount() const noexcept
|
||||
// created via default handoff
|
||||
void Pane::FinalizeConfigurationGivenDefault()
|
||||
{
|
||||
_isDefTermSession = true;
|
||||
if (const auto& terminalPane{ _content.try_as<TerminalPaneContent>() })
|
||||
{
|
||||
terminalPane.MarkAsDefterm();
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Returns true if the pane or one of its descendants is read-only
|
||||
bool Pane::ContainsReadOnly() const
|
||||
{
|
||||
return _IsLeaf() ? _control.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
|
||||
return _IsLeaf() ? _content.ReadOnly() : (_firstChild->ContainsReadOnly() || _secondChild->ContainsReadOnly());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -3157,8 +2940,8 @@ void Pane::CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& s
|
||||
{
|
||||
if (_IsLeaf())
|
||||
{
|
||||
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(_control.TaskbarState(),
|
||||
_control.TaskbarProgress()) };
|
||||
auto tbState{ winrt::make<winrt::TerminalApp::implementation::TaskbarState>(_content.TaskbarState(),
|
||||
_content.TaskbarProgress()) };
|
||||
states.push_back(tbState);
|
||||
}
|
||||
else
|
||||
@@ -3189,9 +2972,12 @@ void Pane::BroadcastKey(const winrt::Microsoft::Terminal::Control::TermControl&
|
||||
const bool keyDown)
|
||||
{
|
||||
WalkTree([&](const auto& pane) {
|
||||
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
|
||||
if (const auto& termControl{ pane->GetTerminalControl() })
|
||||
{
|
||||
pane->_control.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown);
|
||||
if (termControl != sourceControl && !termControl.ReadOnly())
|
||||
{
|
||||
termControl.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3202,9 +2988,12 @@ void Pane::BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl&
|
||||
const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers)
|
||||
{
|
||||
WalkTree([&](const auto& pane) {
|
||||
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
|
||||
if (const auto& termControl{ pane->GetTerminalControl() })
|
||||
{
|
||||
pane->_control.RawWriteChar(character, scanCode, modifiers);
|
||||
if (termControl != sourceControl && !termControl.ReadOnly())
|
||||
{
|
||||
termControl.RawWriteChar(character, scanCode, modifiers);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3213,19 +3002,16 @@ void Pane::BroadcastString(const winrt::Microsoft::Terminal::Control::TermContro
|
||||
const winrt::hstring& text)
|
||||
{
|
||||
WalkTree([&](const auto& pane) {
|
||||
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
|
||||
if (const auto& termControl{ pane->GetTerminalControl() })
|
||||
{
|
||||
pane->_control.RawWriteString(text);
|
||||
if (termControl != sourceControl && !termControl.ReadOnly())
|
||||
{
|
||||
termControl.RawWriteString(text);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Pane::_ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*e*/)
|
||||
{
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor()
|
||||
{
|
||||
if (_lastActive)
|
||||
@@ -3233,7 +3019,7 @@ winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor()
|
||||
return _themeResources.focusedBorderBrush;
|
||||
}
|
||||
|
||||
if (_broadcastEnabled && (_IsLeaf() && !_control.ReadOnly()))
|
||||
if (_broadcastEnabled && (_IsLeaf() && !_content.ReadOnly()))
|
||||
{
|
||||
return _themeResources.broadcastBorderBrush;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "TaskbarState.h"
|
||||
#include "TerminalPaneContent.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
@@ -61,8 +62,7 @@ struct PaneResources
|
||||
class Pane : public std::enable_shared_from_this<Pane>
|
||||
{
|
||||
public:
|
||||
Pane(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control,
|
||||
Pane(const winrt::TerminalApp::IPaneContent& content,
|
||||
const bool lastFocused = false);
|
||||
|
||||
Pane(std::shared_ptr<Pane> first,
|
||||
@@ -81,10 +81,15 @@ public:
|
||||
// - If this is a branch/root pane, return nullptr.
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
|
||||
{
|
||||
return _profile;
|
||||
if (const auto& c{ _content.try_as<winrt::TerminalApp::TerminalPaneContent>() })
|
||||
{
|
||||
return c.GetProfile();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();
|
||||
winrt::TerminalApp::IPaneContent GetContent() const noexcept { return _IsLeaf() ? _content : nullptr; }
|
||||
|
||||
bool WasLastFocused() const noexcept;
|
||||
void UpdateVisuals();
|
||||
@@ -101,8 +106,9 @@ public:
|
||||
BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, const bool asContent = false, const bool asMovePane = false);
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(const bool asContent = false) const;
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
|
||||
void UpdateTerminalSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
|
||||
std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
|
||||
@@ -218,7 +224,6 @@ public:
|
||||
WINRT_CALLBACK(LostFocus, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
WINRT_CALLBACK(PaneRaiseBell, winrt::Windows::Foundation::EventHandler<bool>);
|
||||
WINRT_CALLBACK(Detached, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
WINRT_CALLBACK(RestartTerminalRequested, winrt::delegate<std::shared_ptr<Pane>>);
|
||||
|
||||
private:
|
||||
struct PanePoint;
|
||||
@@ -238,10 +243,8 @@ private:
|
||||
std::shared_ptr<Pane> _secondChild{ nullptr };
|
||||
SplitState _splitState{ SplitState::None };
|
||||
float _desiredSplitPosition;
|
||||
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
|
||||
bool _isDefTermSession{ false };
|
||||
|
||||
winrt::TerminalApp::IPaneContent _content{ nullptr };
|
||||
#pragma endregion
|
||||
|
||||
std::optional<uint32_t> _id;
|
||||
@@ -251,17 +254,6 @@ private:
|
||||
winrt::event_token _firstClosedToken{ 0 };
|
||||
winrt::event_token _secondClosedToken{ 0 };
|
||||
|
||||
struct ControlEventTokens
|
||||
{
|
||||
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged;
|
||||
} _controlEvents;
|
||||
void _setupControlEvents();
|
||||
void _removeControlEvents();
|
||||
|
||||
winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
|
||||
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
|
||||
|
||||
@@ -270,13 +262,14 @@ private:
|
||||
bool _zoomed{ false };
|
||||
bool _broadcastEnabled{ false };
|
||||
|
||||
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
|
||||
bool _bellPlayerCreated{ false };
|
||||
|
||||
bool _IsLeaf() const noexcept;
|
||||
bool _HasFocusedChild() const noexcept;
|
||||
void _SetupChildCloseHandlers();
|
||||
bool _HasChild(const std::shared_ptr<Pane> child);
|
||||
winrt::TerminalApp::TerminalPaneContent _getTerminalContent() const
|
||||
{
|
||||
return _IsLeaf() ? _content.try_as<winrt::TerminalApp::TerminalPaneContent>() : nullptr;
|
||||
}
|
||||
|
||||
std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
|
||||
const float splitSize,
|
||||
@@ -307,19 +300,11 @@ private:
|
||||
|
||||
void _Focus();
|
||||
void _FocusFirstChild();
|
||||
winrt::fire_and_forget _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
void _ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& e);
|
||||
void _ControlGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
void _ContentGotFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
void _ContentLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
|
||||
void _ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);
|
||||
|
||||
void _CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
void _RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
|
||||
std::pair<float, float> _CalcChildrenSizes(const float fullSize) const;
|
||||
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
|
||||
SnapSizeResult _CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
|
||||
@@ -330,8 +315,6 @@ private:
|
||||
|
||||
SplitState _convertAutomaticOrDirectionalSplitState(const winrt::Microsoft::Terminal::Settings::Model::SplitDirection& splitType) const;
|
||||
|
||||
winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri);
|
||||
|
||||
// Function Description:
|
||||
// - Returns true if the given direction can be used with the given split
|
||||
// type.
|
||||
|
||||
6
src/cascadia/TerminalApp/PaneArgs.cpp
Normal file
6
src/cascadia/TerminalApp/PaneArgs.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "PaneArgs.h"
|
||||
#include "BellEventArgs.g.cpp"
|
||||
18
src/cascadia/TerminalApp/PaneArgs.h
Normal file
18
src/cascadia/TerminalApp/PaneArgs.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BellEventArgs.g.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct BellEventArgs : public BellEventArgsT<BellEventArgs>
|
||||
{
|
||||
public:
|
||||
BellEventArgs(bool flashTaskbar) :
|
||||
FlashTaskbar(flashTaskbar) {}
|
||||
|
||||
til::property<bool> FlashTaskbar;
|
||||
};
|
||||
};
|
||||
69
src/cascadia/TerminalApp/ScratchpadContent.cpp
Normal file
69
src/cascadia/TerminalApp/ScratchpadContent.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ScratchpadContent.h"
|
||||
#include "PaneArgs.h"
|
||||
#include "ScratchpadContent.g.cpp"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ScratchpadContent::ScratchpadContent()
|
||||
{
|
||||
_root = winrt::Windows::UI::Xaml::Controls::Grid{};
|
||||
_root.VerticalAlignment(VerticalAlignment::Stretch);
|
||||
_root.HorizontalAlignment(HorizontalAlignment::Stretch);
|
||||
|
||||
auto res = Windows::UI::Xaml::Application::Current().Resources();
|
||||
auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush"));
|
||||
_root.Background(bg.try_as<Media::Brush>());
|
||||
|
||||
_box = winrt::Windows::UI::Xaml::Controls::TextBox{};
|
||||
_box.Margin({ 10, 10, 10, 10 });
|
||||
_box.AcceptsReturn(true);
|
||||
_box.TextWrapping(TextWrapping::Wrap);
|
||||
_root.Children().Append(_box);
|
||||
}
|
||||
|
||||
void ScratchpadContent::UpdateSettings(const CascadiaSettings& /*settings*/)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement ScratchpadContent::GetRoot()
|
||||
{
|
||||
return _root;
|
||||
}
|
||||
winrt::Windows::Foundation::Size ScratchpadContent::MinSize()
|
||||
{
|
||||
return { 1, 1 };
|
||||
}
|
||||
void ScratchpadContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
|
||||
{
|
||||
_box.Focus(reason);
|
||||
}
|
||||
void ScratchpadContent::Close()
|
||||
{
|
||||
CloseRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
NewTerminalArgs ScratchpadContent::GetNewTerminalArgs(const bool /* asContent */) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::hstring ScratchpadContent::Icon() const
|
||||
{
|
||||
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
|
||||
return winrt::hstring{ glyph };
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush ScratchpadContent::BackgroundBrush()
|
||||
{
|
||||
return _root.Background();
|
||||
}
|
||||
}
|
||||
42
src/cascadia/TerminalApp/ScratchpadContent.h
Normal file
42
src/cascadia/TerminalApp/ScratchpadContent.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include "ScratchpadContent.g.h"
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct ScratchpadContent : ScratchpadContentT<ScratchpadContent>
|
||||
{
|
||||
ScratchpadContent();
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
|
||||
|
||||
winrt::Windows::Foundation::Size MinSize();
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
|
||||
void Close();
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
|
||||
|
||||
winrt::hstring Title() { return L"Scratchpad"; }
|
||||
uint64_t TaskbarState() { return 0; }
|
||||
uint64_t TaskbarProgress() { return 0; }
|
||||
bool ReadOnly() { return false; }
|
||||
winrt::hstring Icon() const;
|
||||
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
|
||||
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
|
||||
|
||||
til::typed_event<> CloseRequested;
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::BellEventArgs> BellRequested;
|
||||
til::typed_event<> TitleChanged;
|
||||
til::typed_event<> TabColorChanged;
|
||||
til::typed_event<> TaskbarProgressChanged;
|
||||
til::typed_event<> ReadOnlyChanged;
|
||||
til::typed_event<> FocusRequested;
|
||||
|
||||
private:
|
||||
winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::TextBox _box{ nullptr };
|
||||
};
|
||||
}
|
||||
84
src/cascadia/TerminalApp/SettingsPaneContent.cpp
Normal file
84
src/cascadia/TerminalApp/SettingsPaneContent.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "SettingsPaneContent.h"
|
||||
#include "PaneArgs.h"
|
||||
#include "SettingsPaneContent.g.cpp"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
#define ASSERT_UI_THREAD() assert(_sui.Dispatcher().HasThreadAccess())
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
SettingsPaneContent::SettingsPaneContent(CascadiaSettings settings)
|
||||
{
|
||||
_sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings };
|
||||
|
||||
// Stash away the current requested theme of the app. We'll need that in
|
||||
// _BackgroundBrush() to do a theme-aware resource lookup
|
||||
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
|
||||
}
|
||||
|
||||
void SettingsPaneContent::UpdateSettings(const CascadiaSettings& settings)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
_sui.UpdateSettings(settings);
|
||||
|
||||
_requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot()
|
||||
{
|
||||
return _sui;
|
||||
}
|
||||
winrt::Windows::Foundation::Size SettingsPaneContent::MinSize()
|
||||
{
|
||||
return { 1, 1 };
|
||||
}
|
||||
void SettingsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
|
||||
{
|
||||
if (reason != FocusState::Unfocused)
|
||||
{
|
||||
_sui.as<Controls::Page>().Focus(reason);
|
||||
}
|
||||
}
|
||||
void SettingsPaneContent::Close()
|
||||
{
|
||||
CloseRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const bool /* asContent */) const
|
||||
{
|
||||
// TODO! hey, can we somehow replicate std::vector<ActionAndArgs> SettingsTab::BuildStartupActions?
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::hstring SettingsPaneContent::Icon() const
|
||||
{
|
||||
// This is the Setting icon (looks like a gear)
|
||||
static constexpr std::wstring_view glyph{ L"\xE713" };
|
||||
return winrt::hstring{ glyph };
|
||||
}
|
||||
|
||||
Windows::Foundation::IReference<winrt::Windows::UI::Color> SettingsPaneContent::TabColor() const noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush SettingsPaneContent::BackgroundBrush()
|
||||
{
|
||||
// Look up the color we should use for the settings tab item from our
|
||||
// resources. This should only be used for when "terminalBackground" is
|
||||
// requested.
|
||||
static const auto key = winrt::box_value(L"SettingsUiTabBrush");
|
||||
// You can't just do a Application::Current().Resources().TryLookup
|
||||
// lookup, cause the app theme never changes! Do the hacky version
|
||||
// instead.
|
||||
return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
|
||||
}
|
||||
}
|
||||
49
src/cascadia/TerminalApp/SettingsPaneContent.h
Normal file
49
src/cascadia/TerminalApp/SettingsPaneContent.h
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include "SettingsPaneContent.g.h"
|
||||
#include <LibraryResources.h>
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct SettingsPaneContent : SettingsPaneContentT<SettingsPaneContent>
|
||||
{
|
||||
SettingsPaneContent(winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
|
||||
winrt::Microsoft::Terminal::Settings::Editor::MainPage SettingsUI() { return _sui; }
|
||||
|
||||
winrt::Windows::Foundation::Size MinSize();
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
|
||||
void Close();
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
|
||||
|
||||
winrt::hstring Title() { return RS_(L"SettingsTab"); }
|
||||
uint64_t TaskbarState() { return 0; }
|
||||
uint64_t TaskbarProgress() { return 0; }
|
||||
bool ReadOnly() { return false; }
|
||||
winrt::hstring Icon() const;
|
||||
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
|
||||
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
|
||||
|
||||
til::typed_event<> CloseRequested;
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::BellEventArgs> BellRequested;
|
||||
til::typed_event<> TitleChanged;
|
||||
til::typed_event<> TabColorChanged;
|
||||
til::typed_event<> TaskbarProgressChanged;
|
||||
til::typed_event<> ReadOnlyChanged;
|
||||
til::typed_event<> FocusRequested;
|
||||
|
||||
private:
|
||||
winrt::Microsoft::Terminal::Settings::Editor::MainPage _sui{ nullptr };
|
||||
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(SettingsPaneContent);
|
||||
}
|
||||
@@ -63,7 +63,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - existingConnection: An optional connection that is already established to a PTY
|
||||
// for this tab to host instead of creating one.
|
||||
// If not defined, the tab will create the connection.
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection)
|
||||
HRESULT TerminalPage::_OpenNewTab(const NewTerminalArgs& newTerminalArgs)
|
||||
try
|
||||
{
|
||||
const auto profile{ _settings.GetProfileForArgs(newTerminalArgs) };
|
||||
@@ -86,7 +86,7 @@ namespace winrt::TerminalApp::implementation
|
||||
//
|
||||
// This call to _MakePane won't return nullptr, we already checked that
|
||||
// case above with the _maybeElevate call.
|
||||
_CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr, existingConnection));
|
||||
_CreateNewTabFromPane(_MakePane(newTerminalArgs, nullptr));
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
@@ -171,14 +171,8 @@ namespace winrt::TerminalApp::implementation
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().InsertAt(insertPosition, tabViewItem);
|
||||
|
||||
// Set this tab's icon to the icon from the user's profile
|
||||
if (const auto profile{ newTabImpl->GetFocusedProfile() })
|
||||
{
|
||||
if (!profile.Icon().empty())
|
||||
{
|
||||
newTabImpl->UpdateIcon(profile.Icon());
|
||||
}
|
||||
}
|
||||
// Set this tab's icon to the icon from the content
|
||||
_UpdateTabIcon(*newTabImpl);
|
||||
|
||||
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
|
||||
|
||||
@@ -223,13 +217,15 @@ namespace winrt::TerminalApp::implementation
|
||||
// Arguments:
|
||||
// - pane: The pane to use as the root.
|
||||
// - insertPosition: Optional parameter to indicate the position of tab.
|
||||
void TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition)
|
||||
TerminalApp::TerminalTab TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition)
|
||||
{
|
||||
if (pane)
|
||||
{
|
||||
auto newTabImpl = winrt::make_self<TerminalTab>(pane);
|
||||
_InitializeTab(newTabImpl, insertPosition);
|
||||
return *newTabImpl;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -239,9 +235,10 @@ namespace winrt::TerminalApp::implementation
|
||||
// - tab: the Tab to update the title for.
|
||||
void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
|
||||
{
|
||||
if (const auto profile = tab.GetFocusedProfile())
|
||||
if (const auto content{ tab.GetActiveContent() })
|
||||
{
|
||||
tab.UpdateIcon(profile.Icon());
|
||||
const auto& icon{ content.Icon() };
|
||||
tab.UpdateIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,9 @@
|
||||
<DependentUpon>EmptyStringVisibilityConverter.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Pane.h" />
|
||||
<ClInclude Include="PaneArgs.h">
|
||||
<DependentUpon>IPaneContent.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ColorHelper.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="ShortcutActionDispatch.h">
|
||||
@@ -161,6 +164,18 @@
|
||||
<ClInclude Include="SettingsLoadEventArgs.h">
|
||||
<DependentUpon>TerminalWindow.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TerminalPaneContent.h">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScratchpadContent.h">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="OceanContent.h">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SettingsPaneContent.h">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h" />
|
||||
<ClInclude Include="SuggestionsControl.h">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
@@ -236,6 +251,9 @@
|
||||
<DependentUpon>EmptyStringVisibilityConverter.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pane.cpp" />
|
||||
<ClCompile Include="PaneArgs.cpp">
|
||||
<DependentUpon>IPaneContent.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
|
||||
<ClCompile Include="ColorHelper.cpp" />
|
||||
<ClCompile Include="DebugTapConnection.cpp" />
|
||||
@@ -266,6 +284,18 @@
|
||||
<ClCompile Include="TerminalWindow.cpp">
|
||||
<DependentUpon>TerminalWindow.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TerminalPaneContent.cpp">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ScratchpadContent.cpp">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="OceanContent.cpp">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SettingsPaneContent.cpp">
|
||||
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="Toast.cpp" />
|
||||
<ClCompile Include="SuggestionsControl.cpp">
|
||||
@@ -340,6 +370,8 @@
|
||||
</Midl>
|
||||
<Midl Include="FilteredCommand.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
<Midl Include="IPaneContent.idl" />
|
||||
<Midl Include="TerminalPaneContent.idl" />
|
||||
</ItemGroup>
|
||||
<!-- ========================= Misc Files ======================== -->
|
||||
<ItemGroup>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ColorHelper.h"
|
||||
#include "DebugTapConnection.h"
|
||||
#include "SettingsTab.h"
|
||||
#include "SettingsPaneContent.h"
|
||||
#include "TabRowControl.h"
|
||||
#include "Utils.h"
|
||||
|
||||
@@ -1287,15 +1288,20 @@ namespace winrt::TerminalApp::implementation
|
||||
return connection;
|
||||
}
|
||||
|
||||
TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(std::shared_ptr<Pane> pane)
|
||||
TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent)
|
||||
{
|
||||
const auto& control{ pane->GetTerminalControl() };
|
||||
if (paneContent == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& control{ paneContent.GetTerminal() };
|
||||
if (control == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const auto& connection = control.Connection();
|
||||
auto profile{ pane->GetProfile() };
|
||||
auto profile{ paneContent.GetProfile() };
|
||||
|
||||
TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
|
||||
@@ -2329,6 +2335,12 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
// For now, prevent splitting the _settingsTab. We can always revisit this later.
|
||||
if (*activeTab == _settingsTab)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the caller is calling us with the return value of _MakePane
|
||||
// directly, it's possible that nullptr was returned, if the connections
|
||||
// was supposed to be launched in an elevated window. In that case, do
|
||||
@@ -3019,10 +3031,9 @@ namespace winrt::TerminalApp::implementation
|
||||
// Don't need to worry about duplicating or anything - we'll
|
||||
// serialize the actual profile's GUID along with the content guid.
|
||||
const auto& profile = _settings.GetProfileForArgs(newTerminalArgs);
|
||||
|
||||
const auto control = _AttachControlToContent(newTerminalArgs.ContentId());
|
||||
|
||||
return std::make_shared<Pane>(profile, control);
|
||||
auto terminalPane{ winrt::make<TerminalPaneContent>(profile, control) };
|
||||
return std::make_shared<Pane>(terminalPane);
|
||||
}
|
||||
|
||||
TerminalSettingsCreateResult controlSettings{ nullptr };
|
||||
@@ -3078,13 +3089,15 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
const auto control = _CreateNewControlAndContent(controlSettings, connection);
|
||||
|
||||
auto resultPane = std::make_shared<Pane>(profile, control);
|
||||
auto terminalPane{ winrt::make<TerminalPaneContent>(profile, control) };
|
||||
auto resultPane = std::make_shared<Pane>(terminalPane);
|
||||
|
||||
if (debugConnection) // this will only be set if global debugging is on and tap is active
|
||||
{
|
||||
auto newControl = _CreateNewControlAndContent(controlSettings, debugConnection);
|
||||
// Split (auto) with the debug tap.
|
||||
auto debugPane = std::make_shared<Pane>(profile, newControl);
|
||||
auto debugTerminalPane{ winrt::make<TerminalPaneContent>(profile, newControl) };
|
||||
auto debugPane = std::make_shared<Pane>(debugTerminalPane);
|
||||
|
||||
// Since we're doing this split directly on the pane (instead of going through TerminalTab,
|
||||
// we need to handle the panes 'active' states
|
||||
@@ -3098,16 +3111,18 @@ namespace winrt::TerminalApp::implementation
|
||||
original->SetActive();
|
||||
}
|
||||
|
||||
resultPane->RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection });
|
||||
terminalPane.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection });
|
||||
|
||||
return resultPane;
|
||||
}
|
||||
|
||||
void TerminalPage::_restartPaneConnection(const std::shared_ptr<Pane>& pane)
|
||||
void TerminalPage::_restartPaneConnection(
|
||||
const TerminalApp::TerminalPaneContent& paneContent,
|
||||
const winrt::Windows::Foundation::IInspectable&)
|
||||
{
|
||||
if (const auto& connection{ _duplicateConnectionForRestart(pane) })
|
||||
if (const auto& connection{ _duplicateConnectionForRestart(paneContent) })
|
||||
{
|
||||
pane->GetTerminalControl().Connection(connection);
|
||||
paneContent.GetTerminal().Connection(connection);
|
||||
connection.Start();
|
||||
}
|
||||
}
|
||||
@@ -3209,11 +3224,17 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
|
||||
{
|
||||
terminalTab->UpdateSettings();
|
||||
// Let the tab know that there are new settings. It's up to each content to decide what to do with them.
|
||||
terminalTab->UpdateSettings(_settings);
|
||||
|
||||
// FURTHERMORE We need to do a bit more work here for terminal
|
||||
// panes. They need to know about the profile that was used for
|
||||
// them, and about the focused/unfocused settings.
|
||||
|
||||
// Manually enumerate the panes in each tab; this will let us recycle TerminalSettings
|
||||
// objects but only have to iterate one time.
|
||||
terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
|
||||
// If the pane isn't a terminal pane, it won't have a profile.
|
||||
if (const auto profile{ pane->GetProfile() })
|
||||
{
|
||||
const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
|
||||
@@ -3228,7 +3249,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
|
||||
}
|
||||
pane->UpdateSettings(pair.second, pair.first);
|
||||
pane->UpdateTerminalSettings(pair.second, pair.first);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -3809,7 +3830,10 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
}
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Editor::MainPage sui{ _settings };
|
||||
// Create the SUI pane content
|
||||
auto settingsContent{ winrt::make_self<SettingsPaneContent>(_settings) };
|
||||
auto sui = settingsContent->SettingsUI();
|
||||
|
||||
if (_hostingHwnd)
|
||||
{
|
||||
sui.SetHostingWindow(reinterpret_cast<uint64_t>(*_hostingHwnd));
|
||||
@@ -3825,54 +3849,68 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
auto newTabImpl = winrt::make_self<SettingsTab>(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme());
|
||||
// Create the tab
|
||||
auto resultPane = std::make_shared<Pane>(*settingsContent);
|
||||
_settingsTab = _CreateNewTabFromPane(resultPane);
|
||||
|
||||
// Add the new tab to the list of our tabs.
|
||||
_tabs.Append(*newTabImpl);
|
||||
_mruTabs.Append(*newTabImpl);
|
||||
// auto newTabImpl = winrt::make_self<SettingsTab>(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme());
|
||||
|
||||
newTabImpl->SetDispatch(*_actionDispatch);
|
||||
newTabImpl->SetActionMap(_settings.ActionMap());
|
||||
// // Add the new tab to the list of our tabs.
|
||||
// _tabs.Append(*newTabImpl);
|
||||
// _mruTabs.Append(*newTabImpl);
|
||||
|
||||
// Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
|
||||
_UpdateTabIndices();
|
||||
// newTabImpl->SetDispatch(*_actionDispatch);
|
||||
// newTabImpl->SetActionMap(_settings.ActionMap());
|
||||
|
||||
// Don't capture a strong ref to the tab. If the tab is removed as this
|
||||
// is called, we don't really care anymore about handling the event.
|
||||
auto weakTab = make_weak(newTabImpl);
|
||||
// // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
|
||||
// _UpdateTabIndices();
|
||||
|
||||
auto tabViewItem = newTabImpl->TabViewItem();
|
||||
_tabView.TabItems().Append(tabViewItem);
|
||||
// // Don't capture a strong ref to the tab. If the tab is removed as this
|
||||
// // is called, we don't really care anymore about handling the event.
|
||||
// auto weakTab = make_weak(newTabImpl);
|
||||
|
||||
tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick });
|
||||
// auto tabViewItem = newTabImpl->TabViewItem();
|
||||
// _tabView.TabItems().Append(tabViewItem);
|
||||
|
||||
// When the tab requests close, try to close it (prompt for approval, if required)
|
||||
newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
|
||||
auto page{ weakThis.get() };
|
||||
auto tab{ weakTab.get() };
|
||||
// tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick });
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_HandleCloseTabRequested(*tab);
|
||||
}
|
||||
});
|
||||
// // When the tab requests close, try to close it (prompt for approval, if required)
|
||||
// newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
|
||||
// auto page{ weakThis.get() };
|
||||
// auto tab{ weakTab.get() };
|
||||
// if (page && tab)
|
||||
// {
|
||||
// page->_HandleCloseTabRequested(*tab);
|
||||
// }
|
||||
// });
|
||||
|
||||
// When the tab is closed, remove it from our list of tabs.
|
||||
newTabImpl->Closed([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
|
||||
const auto page = weakThis.get();
|
||||
const auto tab = weakTab.get();
|
||||
// TODO! Make sure we remove the _settingsTab if it is closed!
|
||||
// ---------------------- main
|
||||
// newTabImpl->Closed([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
|
||||
// const auto page = weakThis.get();
|
||||
// const auto tab = weakTab.get();
|
||||
|
||||
if (page && tab)
|
||||
{
|
||||
page->_RemoveTab(*tab);
|
||||
}
|
||||
});
|
||||
// if (page && tab)
|
||||
// {
|
||||
// page->_RemoveTab(*tab);
|
||||
// }
|
||||
// });
|
||||
// =======
|
||||
// // When the tab is closed, remove it from our list of tabs.
|
||||
// newTabImpl->Closed([tabViewItem, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
|
||||
// if (auto page{ weakThis.get() })
|
||||
// {
|
||||
// page->_settingsTab = nullptr;
|
||||
// page->_RemoveOnCloseRoutine(tabViewItem, page);
|
||||
// }
|
||||
// });
|
||||
// -------------------------- dev/migrie/fhl/scratchpad-pane
|
||||
|
||||
_settingsTab = *newTabImpl;
|
||||
// _settingsTab = *newTabImpl;
|
||||
|
||||
// This kicks off TabView::SelectionChanged, in response to which
|
||||
// we'll attach the terminal's Xaml control to the Xaml root.
|
||||
_tabView.SelectedItem(tabViewItem);
|
||||
//// This kicks off TabView::SelectionChanged, in response to which
|
||||
//// we'll attach the terminal's Xaml control to the Xaml root.
|
||||
//_tabView.SelectedItem(tabViewItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _UpdateTabIndices();
|
||||
|
||||
TerminalApp::SettingsTab _settingsTab{ nullptr };
|
||||
TerminalApp::TerminalTab _settingsTab{ nullptr };
|
||||
|
||||
bool _isInFocusMode{ false };
|
||||
bool _isFullscreen{ false };
|
||||
@@ -301,14 +301,14 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _CreateNewTabFlyoutProfile(const Microsoft::Terminal::Settings::Model::Profile profile, int profileIndex);
|
||||
|
||||
void _OpenNewTabDropdown();
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
|
||||
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
|
||||
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs);
|
||||
TerminalApp::TerminalTab _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
|
||||
|
||||
std::wstring _evaluatePathForCwd(std::wstring_view path);
|
||||
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings, const bool inheritCursor);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(std::shared_ptr<Pane> pane);
|
||||
void _restartPaneConnection(const std::shared_ptr<Pane>& pane);
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(const TerminalApp::TerminalPaneContent& paneContent);
|
||||
void _restartPaneConnection(const TerminalApp::TerminalPaneContent&, const winrt::Windows::Foundation::IInspectable&);
|
||||
|
||||
winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);
|
||||
|
||||
|
||||
332
src/cascadia/TerminalApp/TerminalPaneContent.cpp
Normal file
332
src/cascadia/TerminalApp/TerminalPaneContent.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "TerminalPaneContent.h"
|
||||
#include "PaneArgs.h"
|
||||
#include "TerminalPaneContent.g.cpp"
|
||||
|
||||
#include <Mmsystem.h>
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
using namespace winrt::Microsoft::Terminal::Control;
|
||||
using namespace winrt::Microsoft::Terminal::TerminalConnection;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
TerminalPaneContent::TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control) :
|
||||
_control{ control },
|
||||
_profile{ profile }
|
||||
{
|
||||
_setupControlEvents();
|
||||
}
|
||||
|
||||
void TerminalPaneContent::_setupControlEvents()
|
||||
{
|
||||
_controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &TerminalPaneContent::_ControlConnectionStateChangedHandler });
|
||||
_controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_ControlWarningBellHandler });
|
||||
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_CloseTerminalRequestedHandler });
|
||||
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_RestartTerminalRequestedHandler });
|
||||
|
||||
_controlEvents._TitleChanged = _control.TitleChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTitleChanged });
|
||||
_controlEvents._TabColorChanged = _control.TabColorChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlTabColorChanged });
|
||||
_controlEvents._SetTaskbarProgress = _control.SetTaskbarProgress(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlSetTaskbarProgress });
|
||||
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlReadOnlyChanged });
|
||||
_controlEvents._FocusFollowMouseRequested = _control.FocusFollowMouseRequested(winrt::auto_revoke, { get_weak(), &TerminalPaneContent::_controlFocusFollowMouseRequested });
|
||||
}
|
||||
void TerminalPaneContent::_removeControlEvents()
|
||||
{
|
||||
_controlEvents = {};
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement TerminalPaneContent::GetRoot()
|
||||
{
|
||||
return _control;
|
||||
}
|
||||
winrt::Microsoft::Terminal::Control::TermControl TerminalPaneContent::GetTerminal()
|
||||
{
|
||||
return _control;
|
||||
}
|
||||
winrt::Windows::Foundation::Size TerminalPaneContent::MinSize()
|
||||
{
|
||||
return _control.MinimumSize();
|
||||
}
|
||||
void TerminalPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
|
||||
{
|
||||
_control.Focus(reason);
|
||||
}
|
||||
void TerminalPaneContent::Close()
|
||||
{
|
||||
_removeControlEvents();
|
||||
|
||||
// Clear out our media player callbacks, and stop any playing media. This
|
||||
// will prevent the callback from being triggered after we've closed, and
|
||||
// also make sure that our sound stops when we're closed.
|
||||
if (_bellPlayer)
|
||||
{
|
||||
_bellPlayer.Pause();
|
||||
_bellPlayer.Source(nullptr);
|
||||
_bellPlayer.Close();
|
||||
_bellPlayer = nullptr;
|
||||
_bellPlayerCreated = false;
|
||||
}
|
||||
|
||||
CloseRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
winrt::hstring TerminalPaneContent::Icon() const
|
||||
{
|
||||
return _profile.Icon();
|
||||
}
|
||||
|
||||
Windows::Foundation::IReference<winrt::Windows::UI::Color> TerminalPaneContent::TabColor() const noexcept
|
||||
{
|
||||
return _control.TabColor();
|
||||
}
|
||||
|
||||
NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const bool asContent) const
|
||||
{
|
||||
NewTerminalArgs args{};
|
||||
auto controlSettings = _control.Settings();
|
||||
|
||||
args.Profile(controlSettings.ProfileName());
|
||||
// If we know the user's working directory use it instead of the profile.
|
||||
if (const auto dir = _control.WorkingDirectory(); !dir.empty())
|
||||
{
|
||||
args.StartingDirectory(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.StartingDirectory(controlSettings.StartingDirectory());
|
||||
}
|
||||
args.TabTitle(controlSettings.StartingTitle());
|
||||
args.Commandline(controlSettings.Commandline());
|
||||
args.SuppressApplicationTitle(controlSettings.SuppressApplicationTitle());
|
||||
if (controlSettings.TabColor() || controlSettings.StartingTabColor())
|
||||
{
|
||||
til::color c;
|
||||
// StartingTabColor is prioritized over other colors
|
||||
if (const auto color = controlSettings.StartingTabColor())
|
||||
{
|
||||
c = til::color(color.Value());
|
||||
}
|
||||
else
|
||||
{
|
||||
c = til::color(controlSettings.TabColor().Value());
|
||||
}
|
||||
|
||||
args.TabColor(winrt::Windows::Foundation::IReference<winrt::Windows::UI::Color>{ static_cast<winrt::Windows::UI::Color>(c) });
|
||||
}
|
||||
|
||||
// TODO:GH#9800 - we used to be able to persist the color scheme that a
|
||||
// TermControl was initialized with, by name. With the change to having the
|
||||
// control own its own copy of its settings, this isn't possible anymore.
|
||||
//
|
||||
// We may be able to get around this by storing the Name in the Core::Scheme
|
||||
// object. That would work for schemes set by the Terminal, but not ones set
|
||||
// by VT, but that seems good enough.
|
||||
|
||||
// Only fill in the ContentId if absolutely needed. If you fill in a number
|
||||
// here (even 0), we'll serialize that number, AND treat that action as an
|
||||
// "attach existing" rather than a "create"
|
||||
if (asContent)
|
||||
{
|
||||
args.ContentId(_control.ContentId());
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
void TerminalPaneContent::_controlTitleChanged(const IInspectable&, const IInspectable&)
|
||||
{
|
||||
TitleChanged.raise(*this, nullptr);
|
||||
}
|
||||
void TerminalPaneContent::_controlTabColorChanged(const IInspectable&, const IInspectable&)
|
||||
{
|
||||
TabColorChanged.raise(*this, nullptr);
|
||||
}
|
||||
void TerminalPaneContent::_controlSetTaskbarProgress(const IInspectable&, const IInspectable&)
|
||||
{
|
||||
TaskbarProgressChanged.raise(*this, nullptr);
|
||||
}
|
||||
void TerminalPaneContent::_controlReadOnlyChanged(const IInspectable&, const IInspectable&)
|
||||
{
|
||||
ReadOnlyChanged.raise(*this, nullptr);
|
||||
}
|
||||
void TerminalPaneContent::_controlFocusFollowMouseRequested(const IInspectable&, const IInspectable&)
|
||||
{
|
||||
FocusRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called when our attached control is closed. Triggers listeners to our close
|
||||
// event, if we're a leaf pane.
|
||||
// - If this was called, and we became a parent pane (due to work on another
|
||||
// thread), this function will do nothing (allowing the control's new parent
|
||||
// to handle the event instead).
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalPaneContent::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
const auto newConnectionState = _control.ConnectionState();
|
||||
const auto previousConnectionState = std::exchange(_connectionState, newConnectionState);
|
||||
|
||||
if (newConnectionState < ConnectionState::Closed)
|
||||
{
|
||||
// Pane doesn't care if the connection isn't entering a terminal state.
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousConnectionState < ConnectionState::Connected && newConnectionState >= ConnectionState::Failed)
|
||||
{
|
||||
// A failure to complete the connection (before it has _connected_) is not covered by "closeOnExit".
|
||||
// This is to prevent a misconfiguration (closeOnExit: always, startingDirectory: garbage) resulting
|
||||
// in Terminal flashing open and immediately closed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_profile)
|
||||
{
|
||||
if (_isDefTermSession && _profile.CloseOnExit() == CloseOnExitMode::Automatic)
|
||||
{
|
||||
// For 'automatic', we only care about the connection state if we were launched by Terminal
|
||||
// Since we were launched via defterm, ignore the connection state (i.e. we treat the
|
||||
// close on exit mode as 'always', see GH #13325 for discussion)
|
||||
Close();
|
||||
}
|
||||
|
||||
const auto mode = _profile.CloseOnExit();
|
||||
if ((mode == CloseOnExitMode::Always) ||
|
||||
((mode == CloseOnExitMode::Graceful || mode == CloseOnExitMode::Automatic) && newConnectionState == ConnectionState::Closed))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Plays a warning note when triggered by the BEL control character,
|
||||
// using the sound configured for the "Critical Stop" system event.`
|
||||
// This matches the behavior of the Windows Console host.
|
||||
// - Will also flash the taskbar if the bellStyle setting for this profile
|
||||
// has the 'visual' flag set
|
||||
// Arguments:
|
||||
// - <unused>
|
||||
void TerminalPaneContent::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*eventArgs*/)
|
||||
{
|
||||
if (_profile)
|
||||
{
|
||||
// We don't want to do anything if nothing is set, so check for that first
|
||||
if (static_cast<int>(_profile.BellStyle()) != 0)
|
||||
{
|
||||
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Audible))
|
||||
{
|
||||
// Audible is set, play the sound
|
||||
auto sounds{ _profile.BellSound() };
|
||||
if (sounds && sounds.Size() > 0)
|
||||
{
|
||||
winrt::hstring soundPath{ wil::ExpandEnvironmentStringsW<std::wstring>(sounds.GetAt(rand() % sounds.Size()).c_str()) };
|
||||
winrt::Windows::Foundation::Uri uri{ soundPath };
|
||||
_playBellSound(uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto soundAlias = reinterpret_cast<LPCTSTR>(SND_ALIAS_SYSTEMHAND);
|
||||
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
|
||||
}
|
||||
}
|
||||
|
||||
if (WI_IsFlagSet(_profile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
|
||||
{
|
||||
_control.BellLightOn();
|
||||
}
|
||||
|
||||
// raise the event with the bool value corresponding to the taskbar flag
|
||||
BellRequested.raise(*this,
|
||||
*winrt::make_self<TerminalApp::implementation::BellEventArgs>(WI_IsFlagSet(_profile.BellStyle(), BellStyle::Taskbar)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPaneContent::_playBellSound(winrt::Windows::Foundation::Uri uri)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
co_await wil::resume_foreground(_control.Dispatcher());
|
||||
if (auto pane{ weakThis.get() })
|
||||
{
|
||||
if (!_bellPlayerCreated)
|
||||
{
|
||||
// The MediaPlayer might not exist on Windows N SKU.
|
||||
try
|
||||
{
|
||||
_bellPlayerCreated = true;
|
||||
_bellPlayer = winrt::Windows::Media::Playback::MediaPlayer();
|
||||
// GH#12258: The media keys (like play/pause) should have no effect on our bell sound.
|
||||
_bellPlayer.CommandManager().IsEnabled(false);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
if (_bellPlayer)
|
||||
{
|
||||
const auto source{ winrt::Windows::Media::Core::MediaSource::CreateFromUri(uri) };
|
||||
const auto item{ winrt::Windows::Media::Playback::MediaPlaybackItem(source) };
|
||||
_bellPlayer.Source(item);
|
||||
_bellPlayer.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
void TerminalPaneContent::_CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void TerminalPaneContent::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
|
||||
const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
{
|
||||
RestartTerminalRequested.raise(*this, nullptr);
|
||||
}
|
||||
|
||||
void TerminalPaneContent::UpdateSettings(const CascadiaSettings& /*settings*/)
|
||||
{
|
||||
// Do nothing. We'll later be updated manually by
|
||||
// UpdateTerminalSettings, which we need for profile and
|
||||
// focused/unfocused settings.
|
||||
}
|
||||
|
||||
void TerminalPaneContent::UpdateTerminalSettings(const TerminalSettingsCreateResult& settings,
|
||||
const Profile& profile)
|
||||
{
|
||||
_profile = profile;
|
||||
_control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings());
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Should be called when this pane is created via a default terminal handoff
|
||||
// - Finalizes our configuration given the information that we have been
|
||||
// created via default handoff
|
||||
void TerminalPaneContent::MarkAsDefterm()
|
||||
{
|
||||
_isDefTermSession = true;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Media::Brush TerminalPaneContent::BackgroundBrush()
|
||||
{
|
||||
return _control.BackgroundBrush();
|
||||
}
|
||||
|
||||
float TerminalPaneContent::SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap)
|
||||
{
|
||||
return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap);
|
||||
}
|
||||
Windows::Foundation::Size TerminalPaneContent::GridSize()
|
||||
{
|
||||
return _control.CharacterDimensions();
|
||||
}
|
||||
}
|
||||
97
src/cascadia/TerminalApp/TerminalPaneContent.h
Normal file
97
src/cascadia/TerminalApp/TerminalPaneContent.h
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
#include "TerminalPaneContent.g.h"
|
||||
#include "../../cascadia/inc/cppwinrt_utils.h"
|
||||
#include <til/winrt.h>
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct TerminalPaneContent : TerminalPaneContentT<TerminalPaneContent>
|
||||
{
|
||||
TerminalPaneContent(const winrt::Microsoft::Terminal::Settings::Model::Profile& profile,
|
||||
const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
|
||||
winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetTerminal();
|
||||
winrt::Windows::Foundation::Size MinSize();
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
|
||||
void Close();
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const bool asContent) const;
|
||||
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
|
||||
void UpdateTerminalSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
|
||||
const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
|
||||
|
||||
void MarkAsDefterm();
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
|
||||
{
|
||||
return _profile;
|
||||
};
|
||||
|
||||
winrt::hstring Title() { return _control.Title(); }
|
||||
uint64_t TaskbarState() { return _control.TaskbarState(); }
|
||||
uint64_t TaskbarProgress() { return _control.TaskbarProgress(); }
|
||||
bool ReadOnly() { return _control.ReadOnly(); }
|
||||
winrt::hstring Icon() const;
|
||||
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
|
||||
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
|
||||
|
||||
float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap);
|
||||
Windows::Foundation::Size GridSize();
|
||||
|
||||
til::typed_event<TerminalApp::TerminalPaneContent, winrt::Windows::Foundation::IInspectable> RestartTerminalRequested;
|
||||
til::typed_event<> CloseRequested;
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::BellEventArgs> BellRequested;
|
||||
til::typed_event<> TitleChanged;
|
||||
til::typed_event<> TabColorChanged;
|
||||
til::typed_event<> TaskbarProgressChanged;
|
||||
til::typed_event<> ReadOnlyChanged;
|
||||
til::typed_event<> FocusRequested;
|
||||
|
||||
private:
|
||||
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
|
||||
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState _connectionState{ winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::NotConnected };
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile _profile{ nullptr };
|
||||
bool _isDefTermSession{ false };
|
||||
|
||||
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
|
||||
bool _bellPlayerCreated{ false };
|
||||
|
||||
struct ControlEventTokens
|
||||
{
|
||||
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl::TitleChanged_revoker _TitleChanged;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::TabColorChanged_revoker _TabColorChanged;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::SetTaskbarProgress_revoker _SetTaskbarProgress;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::FocusFollowMouseRequested_revoker _FocusFollowMouseRequested;
|
||||
|
||||
} _controlEvents;
|
||||
void _setupControlEvents();
|
||||
void _removeControlEvents();
|
||||
|
||||
winrt::fire_and_forget _playBellSound(winrt::Windows::Foundation::Uri uri);
|
||||
|
||||
void _ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
void _ControlWarningBellHandler(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& e);
|
||||
void _ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);
|
||||
|
||||
void _controlTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _controlTabColorChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _controlSetTaskbarProgress(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _controlReadOnlyChanged(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _controlFocusFollowMouseRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
void _CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
void _RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
|
||||
};
|
||||
}
|
||||
36
src/cascadia/TerminalApp/TerminalPaneContent.idl
Normal file
36
src/cascadia/TerminalApp/TerminalPaneContent.idl
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "IPaneContent.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
[default_interface] runtimeclass TerminalPaneContent : IPaneContent, ISnappable
|
||||
{
|
||||
Microsoft.Terminal.Control.TermControl GetTerminal();
|
||||
|
||||
void UpdateTerminalSettings(Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings,
|
||||
Microsoft.Terminal.Settings.Model.Profile profile);
|
||||
|
||||
void MarkAsDefterm();
|
||||
|
||||
Microsoft.Terminal.Settings.Model.Profile GetProfile();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<TerminalPaneContent, Object> RestartTerminalRequested;
|
||||
}
|
||||
|
||||
|
||||
[default_interface] runtimeclass ScratchpadContent : IPaneContent
|
||||
{
|
||||
}
|
||||
[default_interface] runtimeclass SettingsPaneContent : IPaneContent
|
||||
{
|
||||
SettingsPaneContent(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
}
|
||||
|
||||
[default_interface] runtimeclass OceanContent : IPaneContent
|
||||
{
|
||||
// Let's just smuggle the HWND in as a UInt64 :|
|
||||
void SetHostingWindow(UInt64 window);
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,11 @@ namespace winrt::TerminalApp::implementation
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IPaneContent TerminalTab::GetActiveContent() const
|
||||
{
|
||||
return _activePane ? _activePane->GetContent() : nullptr;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Called after construction of a Tab object to bind event handlers to its
|
||||
// associated Pane and TermControl objects
|
||||
@@ -203,9 +208,9 @@ namespace winrt::TerminalApp::implementation
|
||||
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
|
||||
// Attach event handlers to each new pane
|
||||
_AttachEventHandlersToPane(pane);
|
||||
if (auto control = pane->GetTerminalControl())
|
||||
if (auto content = pane->GetContent())
|
||||
{
|
||||
_AttachEventHandlersToControl(pane->Id().value(), control);
|
||||
_AttachEventHandlersToContent(pane->Id().value(), content);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -264,12 +269,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// of the settings that apply to all tabs.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::UpdateSettings()
|
||||
void TerminalTab::UpdateSettings(const CascadiaSettings& settings)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
// The tabWidthMode may have changed, update the header control accordingly
|
||||
_UpdateHeaderControlMaxWidth();
|
||||
|
||||
// Update the settings on all our panes.
|
||||
_rootPane->WalkTree([&](auto pane) {
|
||||
pane->UpdateSettings(settings);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -278,7 +289,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// - iconPath: The new path string to use as the IconPath for our TabViewItem
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::UpdateIcon(const winrt::hstring iconPath)
|
||||
void TerminalTab::UpdateIcon(const winrt::hstring& iconPath)
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
@@ -372,8 +383,8 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
return RS_(L"MultiplePanes");
|
||||
}
|
||||
const auto lastFocusedControl = GetActiveTerminalControl();
|
||||
return lastFocusedControl ? lastFocusedControl.Title() : L"";
|
||||
const auto activeContent = GetActiveContent();
|
||||
return activeContent ? activeContent.Title() : winrt::hstring{ L"" };
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -508,7 +519,10 @@ namespace winrt::TerminalApp::implementation
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->Id(_nextPaneId);
|
||||
_AttachEventHandlersToControl(p->Id().value(), p->_control);
|
||||
if (const auto& content{ p->GetContent() })
|
||||
{
|
||||
_AttachEventHandlersToContent(p->Id().value(), content);
|
||||
}
|
||||
_nextPaneId++;
|
||||
}
|
||||
return false;
|
||||
@@ -619,7 +633,11 @@ namespace winrt::TerminalApp::implementation
|
||||
if (p->_IsLeaf())
|
||||
{
|
||||
p->Id(_nextPaneId);
|
||||
_AttachEventHandlersToControl(p->Id().value(), p->_control);
|
||||
|
||||
if (const auto& content{ p->GetContent() })
|
||||
{
|
||||
_AttachEventHandlersToContent(p->Id().value(), content);
|
||||
}
|
||||
_nextPaneId++;
|
||||
}
|
||||
});
|
||||
@@ -876,28 +894,18 @@ namespace winrt::TerminalApp::implementation
|
||||
// the control itself doesn't have a particular ID and its pointer is
|
||||
// unstable since it is moved when panes split.
|
||||
// Arguments:
|
||||
// - paneId: The ID of the pane that contains the given control.
|
||||
// - control: the control to remove events from.
|
||||
// - paneId: The ID of the pane that contains the given content.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_DetachEventHandlersFromControl(const uint32_t paneId, const TermControl& control)
|
||||
void TerminalTab::_DetachEventHandlersFromContent(const uint32_t paneId)
|
||||
{
|
||||
auto it = _controlEvents.find(paneId);
|
||||
if (it != _controlEvents.end())
|
||||
auto it = _contentEvents.find(paneId);
|
||||
if (it != _contentEvents.end())
|
||||
{
|
||||
auto& events = it->second;
|
||||
events = {};
|
||||
|
||||
control.TitleChanged(events.titleToken);
|
||||
control.TabColorChanged(events.colorToken);
|
||||
control.SetTaskbarProgress(events.taskbarToken);
|
||||
control.ReadOnlyChanged(events.readOnlyToken);
|
||||
control.FocusFollowMouseRequested(events.focusToken);
|
||||
|
||||
events.KeySent.revoke();
|
||||
events.CharSent.revoke();
|
||||
events.StringSent.revoke();
|
||||
|
||||
_controlEvents.erase(paneId);
|
||||
_contentEvents.erase(paneId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,71 +920,84 @@ namespace winrt::TerminalApp::implementation
|
||||
// - control: the TermControl to add events to.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void TerminalTab::_AttachEventHandlersToControl(const uint32_t paneId, const TermControl& control)
|
||||
void TerminalTab::_AttachEventHandlersToContent(const uint32_t paneId, const TerminalApp::IPaneContent& content)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
auto dispatcher = TabViewItem().Dispatcher();
|
||||
ControlEventTokens events{};
|
||||
ContentEventTokens events{};
|
||||
|
||||
events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The title of the control changed, but not necessarily the title of the tab.
|
||||
// Set the tab's text to the active panes' text.
|
||||
tab->UpdateTitle();
|
||||
}
|
||||
});
|
||||
|
||||
events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The control's tabColor changed, but it is not necessarily the
|
||||
// active control in this tab. We'll just recalculate the
|
||||
// current color anyways.
|
||||
tab->_RecalculateAndApplyTabColor();
|
||||
}
|
||||
});
|
||||
|
||||
events.taskbarToken = control.SetTaskbarProgress([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_UpdateProgressState();
|
||||
}
|
||||
});
|
||||
|
||||
events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_RecalculateAndApplyReadOnly();
|
||||
}
|
||||
});
|
||||
|
||||
events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_focused())
|
||||
events.TitleChanged = content.TitleChanged(
|
||||
winrt::auto_revoke,
|
||||
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (const auto termControl{ sender.try_as<winrt::Microsoft::Terminal::Control::TermControl>() })
|
||||
// The title of the control changed, but not necessarily the title of the tab.
|
||||
// Set the tab's text to the active panes' text.
|
||||
tab->UpdateTitle();
|
||||
}
|
||||
});
|
||||
|
||||
events.TabColorChanged = content.TabColorChanged(
|
||||
winrt::auto_revoke,
|
||||
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
// The control's tabColor changed, but it is not necessarily the
|
||||
// active control in this tab. We'll just recalculate the
|
||||
// current color anyways.
|
||||
tab->_RecalculateAndApplyTabColor();
|
||||
}
|
||||
});
|
||||
|
||||
events.TaskbarProgressChanged = content.TaskbarProgressChanged(
|
||||
winrt::auto_revoke,
|
||||
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
// Check if Tab's lifetime has expired
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_UpdateProgressState();
|
||||
}
|
||||
});
|
||||
|
||||
events.ReadOnlyChanged = content.ReadOnlyChanged(
|
||||
winrt::auto_revoke,
|
||||
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
tab->_RecalculateAndApplyReadOnly();
|
||||
}
|
||||
});
|
||||
|
||||
events.FocusRequested = content.FocusRequested(
|
||||
winrt::auto_revoke,
|
||||
[dispatcher, weakThis](auto sender, auto) -> winrt::fire_and_forget {
|
||||
co_await wil::resume_foreground(dispatcher);
|
||||
if (const auto tab{ weakThis.get() })
|
||||
{
|
||||
if (tab->_focused())
|
||||
{
|
||||
termControl.Focus(FocusState::Pointer);
|
||||
if (const auto content{ sender.try_as<TerminalApp::IPaneContent>() })
|
||||
{
|
||||
content.Focus(FocusState::Pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (_tabStatus.IsInputBroadcastActive())
|
||||
{
|
||||
_addBroadcastHandlers(control, events);
|
||||
if (const auto& termContent{ content.try_as<TerminalApp::TerminalPaneContent>() })
|
||||
{
|
||||
_addBroadcastHandlers(termContent.GetTerminal(), events);
|
||||
}
|
||||
}
|
||||
|
||||
_controlEvents[paneId] = std::move(events);
|
||||
_contentEvents[paneId] = std::move(events);
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -1191,36 +1212,12 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
});
|
||||
|
||||
// Add a PaneRaiseBell event handler to the Pane
|
||||
auto bellToken = pane->PaneRaiseBell([weakThis](auto&& /*s*/, auto&& visual) {
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (visual)
|
||||
{
|
||||
// If visual is set, we need to bubble this event all the way to app host to flash the taskbar
|
||||
// In this part of the chain we bubble it from the hosting tab to the page
|
||||
tab->_TabRaiseVisualBellHandlers();
|
||||
}
|
||||
|
||||
// Show the bell indicator in the tab header
|
||||
tab->ShowBellIndicator(true);
|
||||
|
||||
// If this tab is focused, activate the bell indicator timer, which will
|
||||
// remove the bell indicator once it fires
|
||||
// (otherwise, the indicator is removed when the tab gets focus)
|
||||
if (tab->_focusState != WUX::FocusState::Unfocused)
|
||||
{
|
||||
tab->ActivateBellIndicatorTimer();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// box the event token so that we can give a reference to it in the
|
||||
// event handler.
|
||||
auto detachedToken = std::make_shared<winrt::event_token>();
|
||||
// Add a Detached event handler to the Pane to clean up tab state
|
||||
// and other event handlers when a pane is removed from this tab.
|
||||
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, bellToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
|
||||
*detachedToken = pane->Detached([weakThis, weakPane, gotFocusToken, lostFocusToken, closedToken, detachedToken](std::shared_ptr<Pane> /*sender*/) {
|
||||
// Make sure we do this at most once
|
||||
if (auto pane{ weakPane.lock() })
|
||||
{
|
||||
@@ -1228,14 +1225,10 @@ namespace winrt::TerminalApp::implementation
|
||||
pane->GotFocus(gotFocusToken);
|
||||
pane->LostFocus(lostFocusToken);
|
||||
pane->Closed(closedToken);
|
||||
pane->PaneRaiseBell(bellToken);
|
||||
|
||||
if (auto tab{ weakThis.get() })
|
||||
{
|
||||
if (auto control = pane->GetTerminalControl())
|
||||
{
|
||||
tab->_DetachEventHandlersFromControl(pane->Id().value(), control);
|
||||
}
|
||||
tab->_DetachEventHandlersFromContent(pane->Id().value());
|
||||
|
||||
for (auto i = tab->_mruPanes.begin(); i != tab->_mruPanes.end(); ++i)
|
||||
{
|
||||
@@ -1413,7 +1406,12 @@ namespace winrt::TerminalApp::implementation
|
||||
// GH#10112 - if we're opening the tab renamer, don't
|
||||
// immediately toss focus to the control. We don't want to steal
|
||||
// focus from the tab renamer.
|
||||
if (!tab->_headerControl.InRename() && !tab->GetActiveTerminalControl().SearchBoxEditInFocus())
|
||||
const auto& terminalControl{ tab->GetActiveTerminalControl() }; // maybe null
|
||||
// If we're
|
||||
// * NOT in a rename
|
||||
// * AND (the content isn't a TermControl, OR the term control doesn't have focus in the search box)
|
||||
if (!tab->_headerControl.InRename() &&
|
||||
(terminalControl == nullptr || !terminalControl.SearchBoxEditInFocus()))
|
||||
{
|
||||
tab->_RequestFocusActiveControlHandlers();
|
||||
}
|
||||
@@ -1435,12 +1433,12 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
ASSERT_UI_THREAD();
|
||||
|
||||
std::optional<winrt::Windows::UI::Color> controlTabColor;
|
||||
if (const auto& control = GetActiveTerminalControl())
|
||||
std::optional<winrt::Windows::UI::Color> contentTabColor;
|
||||
if (const auto& content{ GetActiveContent() })
|
||||
{
|
||||
if (const auto color = control.TabColor())
|
||||
if (const auto color = content.TabColor())
|
||||
{
|
||||
controlTabColor = color.Value();
|
||||
contentTabColor = color.Value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1450,7 +1448,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// Color | | Set by
|
||||
// -------------------- | -- | --
|
||||
// Runtime Color | _optional_ | Color Picker / `setTabColor` action
|
||||
// Control Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT
|
||||
// Content Tab Color | _optional_ | Profile's `tabColor`, or a color set by VT (whatever the tab's content wants)
|
||||
// Theme Tab Background | _optional_ | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor)
|
||||
// Tab Default Color | **default** | TabView in XAML
|
||||
//
|
||||
@@ -1459,7 +1457,7 @@ namespace winrt::TerminalApp::implementation
|
||||
// tabview color" (and clear out any colors we've set).
|
||||
|
||||
return til::coalesce(_runtimeTabColor,
|
||||
controlTabColor,
|
||||
contentTabColor,
|
||||
std::optional<Windows::UI::Color>(std::nullopt));
|
||||
}
|
||||
|
||||
@@ -1498,7 +1496,7 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::Windows::UI::Xaml::Media::Brush TerminalTab::_BackgroundBrush()
|
||||
{
|
||||
Media::Brush terminalBrush{ nullptr };
|
||||
if (const auto& c{ GetActiveTerminalControl() })
|
||||
if (const auto& c{ GetActiveContent() })
|
||||
{
|
||||
terminalBrush = c.BackgroundBrush();
|
||||
}
|
||||
@@ -1694,6 +1692,10 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
ReadOnly(_rootPane->ContainsReadOnly());
|
||||
_updateIsClosable();
|
||||
|
||||
// Update all the visuals on all our panes, so they can update their
|
||||
// border colors accordingly.
|
||||
_rootPane->WalkTree([](const auto& p) { p->UpdateVisuals(); });
|
||||
}
|
||||
|
||||
std::shared_ptr<Pane> TerminalTab::GetActivePane() const
|
||||
@@ -1746,8 +1748,8 @@ namespace winrt::TerminalApp::implementation
|
||||
}
|
||||
if (const auto& control{ p->GetTerminalControl() })
|
||||
{
|
||||
auto it = _controlEvents.find(*paneId);
|
||||
if (it != _controlEvents.end())
|
||||
auto it = _contentEvents.find(*paneId);
|
||||
if (it != _contentEvents.end())
|
||||
{
|
||||
auto& events = it->second;
|
||||
|
||||
@@ -1765,7 +1767,7 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
}
|
||||
|
||||
void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ControlEventTokens& events)
|
||||
void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ContentEventTokens& events)
|
||||
{
|
||||
auto weakThis{ get_weak() };
|
||||
// ADD EVENT HANDLERS HERE
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
winrt::Microsoft::Terminal::Control::TermControl GetActiveTerminalControl() const;
|
||||
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile() const noexcept;
|
||||
winrt::TerminalApp::IPaneContent GetActiveContent() const;
|
||||
|
||||
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace winrt::TerminalApp::implementation
|
||||
std::shared_ptr<Pane> newPane);
|
||||
|
||||
void ToggleSplitOrientation();
|
||||
void UpdateIcon(const winrt::hstring iconPath);
|
||||
void UpdateIcon(const winrt::hstring& iconPath);
|
||||
void HideIcon(const bool hide);
|
||||
|
||||
void ShowBellIndicator(const bool show);
|
||||
@@ -57,7 +58,7 @@ namespace winrt::TerminalApp::implementation
|
||||
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
|
||||
bool FocusPane(const uint32_t id);
|
||||
|
||||
void UpdateSettings();
|
||||
void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
|
||||
void UpdateTitle();
|
||||
|
||||
void Shutdown() override;
|
||||
@@ -120,19 +121,21 @@ namespace winrt::TerminalApp::implementation
|
||||
winrt::event_token _colorClearedToken;
|
||||
winrt::event_token _pickerClosedToken;
|
||||
|
||||
struct ControlEventTokens
|
||||
struct ContentEventTokens
|
||||
{
|
||||
winrt::event_token titleToken;
|
||||
winrt::event_token colorToken;
|
||||
winrt::event_token taskbarToken;
|
||||
winrt::event_token readOnlyToken;
|
||||
winrt::event_token focusToken;
|
||||
winrt::TerminalApp::IPaneContent::BellRequested_revoker BellRequested;
|
||||
winrt::TerminalApp::IPaneContent::TitleChanged_revoker TitleChanged;
|
||||
winrt::TerminalApp::IPaneContent::TabColorChanged_revoker TabColorChanged;
|
||||
winrt::TerminalApp::IPaneContent::TaskbarProgressChanged_revoker TaskbarProgressChanged;
|
||||
winrt::TerminalApp::IPaneContent::ReadOnlyChanged_revoker ReadOnlyChanged;
|
||||
winrt::TerminalApp::IPaneContent::FocusRequested_revoker FocusRequested;
|
||||
|
||||
// These events literally only apply if the content is a TermControl.
|
||||
winrt::Microsoft::Terminal::Control::TermControl::KeySent_revoker KeySent;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::CharSent_revoker CharSent;
|
||||
winrt::Microsoft::Terminal::Control::TermControl::StringSent_revoker StringSent;
|
||||
};
|
||||
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;
|
||||
std::unordered_map<uint32_t, ContentEventTokens> _contentEvents;
|
||||
|
||||
winrt::event_token _rootClosedToken{};
|
||||
|
||||
@@ -159,8 +162,8 @@ namespace winrt::TerminalApp::implementation
|
||||
void _CreateContextMenu() override;
|
||||
virtual winrt::hstring _CreateToolTipTitle() override;
|
||||
|
||||
void _DetachEventHandlersFromControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _AttachEventHandlersToControl(const uint32_t paneId, const winrt::Microsoft::Terminal::Control::TermControl& control);
|
||||
void _DetachEventHandlersFromContent(const uint32_t paneId);
|
||||
void _AttachEventHandlersToContent(const uint32_t paneId, const winrt::TerminalApp::IPaneContent& content);
|
||||
void _AttachEventHandlersToPane(std::shared_ptr<Pane> pane);
|
||||
|
||||
void _UpdateActivePane(std::shared_ptr<Pane> pane);
|
||||
@@ -175,7 +178,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
|
||||
|
||||
void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ControlEventTokens& events);
|
||||
void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ContentEventTokens& events);
|
||||
|
||||
void _chooseColorClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
void _renameTabClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
|
||||
|
||||
@@ -81,6 +81,7 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider);
|
||||
|
||||
// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||||
#include "til.h"
|
||||
#include <til/winrt.h>
|
||||
|
||||
#include <cppwinrt_utils.h>
|
||||
#include <wil/cppwinrt_helpers.h> // must go after the CoreDispatcher type is defined
|
||||
|
||||
@@ -96,6 +96,7 @@ static constexpr std::string_view ShowContextMenuKey{ "showContextMenu" };
|
||||
static constexpr std::string_view ExpandSelectionToWordKey{ "expandSelectionToWord" };
|
||||
static constexpr std::string_view RestartConnectionKey{ "restartConnection" };
|
||||
static constexpr std::string_view ToggleBroadcastInputKey{ "toggleBroadcastInput" };
|
||||
static constexpr std::string_view OpenScratchpadKey{ "experimental.openScratchpad" };
|
||||
|
||||
static constexpr std::string_view ActionKey{ "action" };
|
||||
|
||||
@@ -430,6 +431,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
{ ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") },
|
||||
{ ShortcutAction::RestartConnection, RS_(L"RestartConnectionKey") },
|
||||
{ ShortcutAction::ToggleBroadcastInput, RS_(L"ToggleBroadcastInputCommandKey") },
|
||||
{ ShortcutAction::OpenScratchpad, RS_(L"OpenScratchpadKey") },
|
||||
};
|
||||
}();
|
||||
|
||||
|
||||
@@ -109,7 +109,8 @@
|
||||
ON_ALL_ACTIONS(ExpandSelectionToWord) \
|
||||
ON_ALL_ACTIONS(CloseOtherPanes) \
|
||||
ON_ALL_ACTIONS(RestartConnection) \
|
||||
ON_ALL_ACTIONS(ToggleBroadcastInput)
|
||||
ON_ALL_ACTIONS(ToggleBroadcastInput) \
|
||||
ON_ALL_ACTIONS(OpenScratchpad)
|
||||
|
||||
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
|
||||
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -700,6 +700,9 @@
|
||||
<data name="RestartConnectionKey" xml:space="preserve">
|
||||
<value>Restart connection</value>
|
||||
</data>
|
||||
<data name="OpenScratchpadKey" xml:space="preserve">
|
||||
<value>Open scratchpad</value>
|
||||
</data>
|
||||
<data name="SelectOutputNextCommandKey" xml:space="preserve">
|
||||
<value>Select next command output</value>
|
||||
</data>
|
||||
|
||||
@@ -122,7 +122,7 @@ void IslandWindow::MakeWindow() noexcept
|
||||
WINRT_VERIFY(CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP | (_alwaysOnTop ? WS_EX_TOPMOST : 0),
|
||||
wc.lpszClassName,
|
||||
L"Windows Terminal",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
|
||||
@@ -187,4 +187,14 @@
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_ScratchpadPane</name>
|
||||
<description>Allow the user to create scratchpad panes. Mostly just exists to validate non-terminal panes.</description>
|
||||
<id>997</id>
|
||||
<stage>AlwaysDisabled</stage>
|
||||
<alwaysEnabledBrandingTokens>
|
||||
<brandingToken>Dev</brandingToken>
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
</featureStaging>
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
winrt::event<ArgsT> _handlers;
|
||||
};
|
||||
|
||||
template<typename SenderT, typename ArgsT>
|
||||
template<typename SenderT = winrt::Windows::Foundation::IInspectable, typename ArgsT = winrt::Windows::Foundation::IInspectable>
|
||||
struct typed_event
|
||||
{
|
||||
typed_event<SenderT, ArgsT>() = default;
|
||||
|
||||
50
src/tools/scratch/ExtensionCode.cpp
Normal file
50
src/tools/scratch/ExtensionCode.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include "ExtensionInterface.h"
|
||||
|
||||
static HBRUSH s_magentaBrush;
|
||||
|
||||
LRESULT CALLBACK ExtensionWindowProc(HWND hWnd,
|
||||
UINT Message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
wParam;
|
||||
lParam;
|
||||
switch (Message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
{
|
||||
wprintf(L"WM_CREATE\n");
|
||||
// Create a magenta brush for later
|
||||
s_magentaBrush = CreateSolidBrush(RGB(255, 0, 255));
|
||||
break;
|
||||
}
|
||||
// case WM_SIZE:
|
||||
// {
|
||||
// const auto width = LOWORD(lParam);
|
||||
// const auto height = HIWORD(lParam);
|
||||
// wprintf(L"resized to: %d, %d\n", width, height);
|
||||
// break;
|
||||
// }
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hWnd, &ps);
|
||||
|
||||
// All painting occurs here, between BeginPaint and EndPaint.
|
||||
FillRect(hdc, &ps.rcPaint, s_magentaBrush);
|
||||
|
||||
EndPaint(hWnd, &ps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO! hax for now
|
||||
void StartExtension()
|
||||
{
|
||||
(void)SetExtensionWindowProc(ExtensionWindowProc);
|
||||
}
|
||||
9
src/tools/scratch/ExtensionInterface.h
Normal file
9
src/tools/scratch/ExtensionInterface.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
// For real code we'd do something like this
|
||||
// HWND CreateExtensionWindow();
|
||||
// But for now wwell just call main()
|
||||
|
||||
// Set the window procedure that the extension host library should call to.
|
||||
// This lets the extension implementer handle window messages too.
|
||||
void SetExtensionWindowProc(WNDPROC proc);
|
||||
@@ -11,6 +11,7 @@
|
||||
<Import Project="..\..\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="ExtensionCode.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
|
||||
@@ -2,9 +2,338 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
#include "ExtensionInterface.h"
|
||||
|
||||
// Used by window structures to place our special frozen-console painting data
|
||||
#define GWL_CONSOLE_WNDALLOC (3 * sizeof(DWORD))
|
||||
|
||||
HANDLE g_stdOut{ nullptr };
|
||||
LRESULT CALLBACK s_ExtensionWindowProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT CALLBACK _ourExtensionWindowProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
// TODO! hax for now
|
||||
void StartExtension();
|
||||
|
||||
HWND g_parent;
|
||||
HWND g_child;
|
||||
HWND g_extension;
|
||||
WNDPROC g_extensionProc{ nullptr };
|
||||
|
||||
LRESULT CALLBACK s_ScratchWindowProc(HWND hWnd,
|
||||
UINT Message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
switch (Message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
{
|
||||
// Show the window
|
||||
ShowWindow(hWnd, SW_SHOWDEFAULT);
|
||||
|
||||
// // Make the window a topmost window cause islands are weird
|
||||
// SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
||||
// Nope, that didn't work
|
||||
break;
|
||||
}
|
||||
case WM_SIZE:
|
||||
{
|
||||
const auto width = LOWORD(lParam);
|
||||
const auto height = HIWORD(lParam);
|
||||
wprintf(L"resized to: %d, %d\n", width, height);
|
||||
|
||||
// Resize the g_extension to match
|
||||
SetWindowPos(g_extension, nullptr, 0, 0, width, height, SWP_NOZORDER);
|
||||
break;
|
||||
}
|
||||
case WM_PAINT:
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(hWnd, &ps);
|
||||
|
||||
// All painting occurs here, between BeginPaint and EndPaint.
|
||||
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_HIGHLIGHT + 1));
|
||||
|
||||
EndPaint(hWnd, &ps);
|
||||
break;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If we get this far, call the default window proc
|
||||
return DefWindowProcW(hWnd, Message, wParam, lParam);
|
||||
}
|
||||
void resizeChildToMatchParent(HWND parent, HWND child)
|
||||
{
|
||||
// get the position of the parent window
|
||||
RECT rect;
|
||||
GetWindowRect(parent, &rect);
|
||||
|
||||
// resize the child window to match
|
||||
// (leave a 48px margin around the child window, for debug)
|
||||
SetWindowPos(child,
|
||||
nullptr,
|
||||
rect.left + 48,
|
||||
rect.top + 48,
|
||||
rect.right - rect.left - 96,
|
||||
rect.bottom - rect.top - 96,
|
||||
/* SWP_NOZORDER | */ SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
void setupHooks(HWND parent, HWND child)
|
||||
{
|
||||
// get pid and tid for parent window
|
||||
DWORD pid = 0;
|
||||
DWORD tid = GetWindowThreadProcessId(parent, &pid);
|
||||
g_parent = parent;
|
||||
g_child = child;
|
||||
|
||||
// Ala
|
||||
// https://devblogs.microsoft.com/oldnewthing/20210104-00/?p=104656
|
||||
// circa 2021
|
||||
|
||||
WINEVENTPROC sizeChange = [](HWINEVENTHOOK /* hWinEventHook */,
|
||||
DWORD /* event */,
|
||||
HWND hwnd,
|
||||
LONG /* idObject */,
|
||||
LONG /* idChild */,
|
||||
DWORD /* dwEventThread */,
|
||||
DWORD /* dwmsEventTime */) {
|
||||
if (hwnd == g_parent)
|
||||
{
|
||||
// wprintf(L"Got a location change\n");
|
||||
resizeChildToMatchParent(g_parent, g_child);
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for changes in size and pos of the ownerhandle
|
||||
SetWinEventHook(
|
||||
EVENT_OBJECT_LOCATIONCHANGE,
|
||||
EVENT_OBJECT_LOCATIONCHANGE,
|
||||
nullptr,
|
||||
reinterpret_cast<WINEVENTPROC>(sizeChange),
|
||||
pid,
|
||||
tid,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
|
||||
// Listen for changes in the ownerhandle's visibility
|
||||
SetWinEventHook(
|
||||
EVENT_OBJECT_SHOW,
|
||||
EVENT_OBJECT_HIDE,
|
||||
nullptr,
|
||||
[](HWINEVENTHOOK /* hWinEventHook */,
|
||||
DWORD /* event */,
|
||||
HWND /* hwnd */,
|
||||
LONG /* idObject */,
|
||||
LONG /* idChild */,
|
||||
DWORD /* dwEventThread */,
|
||||
DWORD /* dwmsEventTime */) {
|
||||
// if (hwnd == g_hWnd)
|
||||
// {
|
||||
// wprintf(L"Got a visibility change\n");
|
||||
// }
|
||||
},
|
||||
pid,
|
||||
tid,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
|
||||
SetWinEventHook(
|
||||
EVENT_OBJECT_REORDER,
|
||||
EVENT_OBJECT_REORDER,
|
||||
nullptr,
|
||||
[](HWINEVENTHOOK /* hWinEventHook */,
|
||||
DWORD /* event */,
|
||||
HWND hwnd,
|
||||
LONG /* idObject */,
|
||||
LONG /* idChild */,
|
||||
DWORD /* dwEventThread */,
|
||||
DWORD /* dwmsEventTime */) {
|
||||
// n.b. not sure this is ever actually called
|
||||
|
||||
if (hwnd == g_parent)
|
||||
{
|
||||
wprintf(L"Got a reorder change\n");
|
||||
// just bring us to the top
|
||||
SetWindowPos(g_child, g_parent /* HWND_TOP */, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
||||
}
|
||||
},
|
||||
pid,
|
||||
tid,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
|
||||
SetWinEventHook(
|
||||
EVENT_OBJECT_DESTROY,
|
||||
EVENT_OBJECT_DESTROY,
|
||||
nullptr,
|
||||
[](HWINEVENTHOOK /* hWinEventHook */,
|
||||
DWORD /* event */,
|
||||
HWND /* hwnd */,
|
||||
LONG /* idObject */,
|
||||
LONG /* idChild */,
|
||||
DWORD /* dwEventThread */,
|
||||
DWORD /* dwmsEventTime */) {
|
||||
// if (hwnd == g_hWnd)
|
||||
// {
|
||||
// wprintf(L"Got a destroy change\n");
|
||||
// }
|
||||
},
|
||||
pid,
|
||||
tid,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
}
|
||||
|
||||
// This wmain exists for help in writing scratch programs while debugging.
|
||||
int __cdecl wmain(int /*argc*/, WCHAR* /*argv[]*/)
|
||||
int __cdecl wmain(int argc, WCHAR* argv[])
|
||||
{
|
||||
g_stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
// Parse out the handle provided on the command line. It's in hexadecimal.
|
||||
const auto ownerHandle = argc < 2 ?
|
||||
0 :
|
||||
std::stoul(argv[1], nullptr, 16);
|
||||
wprintf(L"handle: %d\n", ownerHandle);
|
||||
|
||||
static const auto SCRATCH_WINDOW_CLASS = L"scratch_window_class";
|
||||
WNDCLASSEXW scratchClass{ 0 };
|
||||
scratchClass.cbSize = sizeof(WNDCLASSEXW);
|
||||
scratchClass.style = CS_HREDRAW | CS_VREDRAW | /*CS_PARENTDC |*/ CS_DBLCLKS;
|
||||
scratchClass.lpszClassName = SCRATCH_WINDOW_CLASS;
|
||||
scratchClass.lpfnWndProc = s_ScratchWindowProc;
|
||||
scratchClass.cbWndExtra = GWL_CONSOLE_WNDALLOC; // this is required to store the owning thread/process override in NTUSER
|
||||
auto windowClassAtom{ RegisterClassExW(&scratchClass) };
|
||||
|
||||
// Create a window
|
||||
// const auto style = WS_VISIBLE | WS_OVERLAPPEDWINDOW;
|
||||
const auto style = 0; // WS_VISIBLE | WS_OVERLAPPED; // no resize border, no caption, etc.
|
||||
|
||||
HWND hwnd = CreateWindowExW(0, // WS_EX_LAYERED,
|
||||
reinterpret_cast<LPCWSTR>(windowClassAtom),
|
||||
L"Hello World",
|
||||
style,
|
||||
200, // CW_USEDEFAULT,
|
||||
200, // CW_USEDEFAULT,
|
||||
200, // CW_USEDEFAULT,
|
||||
200, // CW_USEDEFAULT,
|
||||
0, // owner
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr); // gwl
|
||||
if (hwnd == nullptr)
|
||||
{
|
||||
const auto gle = GetLastError();
|
||||
wprintf(L"Failed to create window: %d\n", gle);
|
||||
|
||||
return gle;
|
||||
}
|
||||
|
||||
SetWindowLong(hwnd, GWL_STYLE, 0); //remove all window styles, cause it's created with WS_CAPTION even if we didn't ask for it
|
||||
ShowWindow(hwnd, SW_SHOW); //display window
|
||||
|
||||
// Add hooks so that we can be informed when the child process gets resized.
|
||||
setupHooks(reinterpret_cast<HWND>(ownerHandle), hwnd);
|
||||
// Immediately resize to the right size
|
||||
resizeChildToMatchParent(g_parent, g_child);
|
||||
// Set our HWND as owned by the parent's. We're always on top of them, but not explicitly a _child_.
|
||||
::SetWindowLongPtrW(g_child, GWLP_HWNDPARENT, reinterpret_cast<LONG_PTR>(g_parent));
|
||||
|
||||
// Now, create a child window of this first hwnd.
|
||||
// This is the one that will be the actual extension's HWND.
|
||||
|
||||
static const auto EXTN_WINDOW_CLASS = L"my_extension_window_class";
|
||||
WNDCLASSEXW extClass{ 0 };
|
||||
extClass.cbSize = sizeof(WNDCLASSEXW);
|
||||
extClass.style = CS_HREDRAW | CS_VREDRAW | /*CS_PARENTDC |*/ CS_DBLCLKS;
|
||||
extClass.lpszClassName = EXTN_WINDOW_CLASS;
|
||||
extClass.lpfnWndProc = s_ExtensionWindowProc;
|
||||
extClass.cbWndExtra = GWL_CONSOLE_WNDALLOC; // this is required to store the owning thread/process override in NTUSER
|
||||
auto extnClassAtom{ RegisterClassExW(&extClass) };
|
||||
const auto extensionStyle = WS_VISIBLE | WS_CHILD;
|
||||
const auto extensionStyleEx = 0;
|
||||
|
||||
// HAX set up the extension proc BEFORE we set up the window so it can get a WM_CREATE
|
||||
StartExtension();
|
||||
|
||||
g_extension = CreateWindowExW(extensionStyleEx, // WS_EX_LAYERED,
|
||||
reinterpret_cast<LPCWSTR>(extnClassAtom),
|
||||
L"Hello Extension",
|
||||
extensionStyle,
|
||||
0, // CW_USEDEFAULT,
|
||||
0, // CW_USEDEFAULT,
|
||||
50, // CW_USEDEFAULT,
|
||||
50, // CW_USEDEFAULT,
|
||||
hwnd, // owner
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
// pump messages
|
||||
MSG msg = { 0 };
|
||||
while (GetMessageW(&msg, nullptr, 0, 0))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//TODO! much later:
|
||||
// * The Terminal doesn't think it's active, cause it's HWND... isn't. The extension's is
|
||||
|
||||
LRESULT CALLBACK s_ExtensionWindowProc(HWND hWnd,
|
||||
UINT Message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
const auto ourResult = _ourExtensionWindowProc(hWnd, Message, wParam, lParam);
|
||||
if (ourResult != 0)
|
||||
{
|
||||
return ourResult;
|
||||
}
|
||||
if (g_extensionProc != nullptr)
|
||||
{
|
||||
const auto theirResult = g_extensionProc(hWnd, Message, wParam, lParam);
|
||||
if (theirResult != 0)
|
||||
{
|
||||
return theirResult;
|
||||
}
|
||||
}
|
||||
return DefWindowProcW(hWnd, Message, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK _ourExtensionWindowProc(HWND hWnd,
|
||||
UINT Message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
wParam;
|
||||
lParam;
|
||||
|
||||
switch (Message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
{
|
||||
// Show the window
|
||||
ShowWindow(hWnd, SW_SHOWDEFAULT);
|
||||
break;
|
||||
}
|
||||
case WM_DESTROY:
|
||||
{
|
||||
PostQuitMessage(0);
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetExtensionWindowProc(WNDPROC proc)
|
||||
{
|
||||
g_extensionProc = proc;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user