Compare commits

...

41 Commits

Author SHA1 Message Date
Mike Griese
0090cf8ecb copy the font size too, why not 2023-10-13 17:00:19 -05:00
Mike Griese
bb28300247 Add an a11y pane, PoC 2023-10-13 16:32:42 -05:00
Mike Griese
fb74fc8c6a dead code 2023-10-13 14:58:19 -05:00
Mike Griese
5f4087ff00 finish exorcising SettingsTab 2023-10-13 14:56:50 -05:00
Mike Griese
e82c627ebe dead code removal 2023-10-13 12:09:08 -05:00
Mike Griese
d726165330 terrible, but it works 2023-10-13 12:06:59 -05:00
Mike Griese
57e1f26d14 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2023-10-13 11:36:27 -05:00
Mike Griese
b49997b4b4 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-10-13 11:12:24 -05:00
Mike Griese
2086e0f3af Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-10-13 10:39:02 -05:00
Mike Griese
6107c3e551 Merge branch 'dev/migrie/fhl/scratchpad-pane' into dev/migrie/f/sui-panes 2023-09-11 05:43:06 -05:00
Mike Griese
46469aa5e3 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-09-11 05:24:30 -05:00
Mike Griese
c869b47e13 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-09-11 05:22:43 -05:00
Mike Griese
9531069538 background brush, done 2023-08-07 15:17:09 -05:00
Mike Griese
521e301541 update settings should work now 2023-08-03 13:50:11 -05:00
Mike Griese
842326daa5 icons for non-terminal pane content 2023-08-03 11:31:57 -05:00
Mike Griese
fb7c80938b derp 2023-08-01 11:37:10 -05:00
Mike Griese
29d0d57656 this works better than it has any right to 2023-07-27 16:29:26 -05:00
Mike Griese
cbd61b0a7d POC: yea, this works 2023-07-27 15:55:05 -05:00
Mike Griese
1cc9835454 feature flags too 2023-07-27 13:58:44 -05:00
Mike Griese
86914bdfc1 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-07-25 13:28:25 -05:00
Mike Griese
e0b003ad4d Merge branch 'main' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-24 14:10:40 -05:00
Mike Griese
f89368c19b [PARENT] try to use GetActiveTerminalControl less in TerminalTab
(cherry picked from commit 262d95aae5)
2023-07-20 10:59:41 -05:00
Mike Griese
5582e1bcc8 [PARENT] You know what, I just went for it.
(cherry picked from commit 63ba8e19fd)
2023-07-20 10:59:28 -05:00
Mike Griese
a23c1a24dc keybindings too 2023-07-20 07:39:02 -05:00
Mike Griese
5f9add4000 Merge branch 'dev/migrie/fhl/non-terminal-panes-2023' into dev/migrie/fhl/scratchpad-pane 2023-07-20 07:04:10 -05:00
Mike Griese
11126f9b37 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-20 07:02:16 -05:00
Mike Griese
e31202b0b8 Merge commit '6a10ea5' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-20 07:02:04 -05:00
Mike Griese
e6dc314c17 Merge commit 'b4042ea' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-19 16:22:03 -05:00
Mike Griese
2d4030683a Let's just make it experimental 2023-07-18 13:47:58 -05:00
Mike Griese
262d95aae5 [PARENT] try to use GetActiveTerminalControl less in TerminalTab 2023-07-18 13:47:38 -05:00
Mike Griese
63ba8e19fd [PARENT] You know what, I just went for it. 2023-07-18 13:21:18 -05:00
Mike Griese
1b39db7ab0 Single commit that adds the whole scratchpad and action 2023-07-18 12:58:55 -05:00
Mike Griese
2dd8f409b2 [TO PARENT] dead code 2023-07-18 11:48:33 -05:00
Mike Griese
049c043279 some last cleanups 2023-07-18 10:26:32 -05:00
Mike Griese
a1da6c117e huge shuffling so that pane content can raise events instead of relying on termcontrol 2023-07-18 10:13:44 -05:00
Mike Griese
7c9ffb0e02 snapping now uses an interface, so that it's not TermControl-specific 2023-07-18 06:06:07 -05:00
Mike Griese
84df8197d4 close event 2023-07-17 14:22:12 -05:00
Mike Griese
5b3aa54b56 move GetNewTerminalArgs into IPaneContent 2023-07-17 12:42:43 -05:00
Mike Griese
ef6bb8a73c hey look, it builds now 2023-07-17 12:35:27 -05:00
Mike Griese
4e144425f0 Merge remote-tracking branch 'origin/main' into dev/migrie/fhl/non-terminal-panes-2023 2023-07-17 10:53:56 -05:00
Mike Griese
f353323a23 I wanted to do this in one shot but _zelda_ 2023-05-12 13:32:12 -05:00
32 changed files with 1500 additions and 875 deletions

View File

@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "AccessibilityContent.h"
#include "PaneArgs.h"
#include "AccessibilityContent.g.cpp"
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Microsoft::Terminal::Settings::Model;
namespace winrt::TerminalApp::implementation
{
AccessibilityContent::AccessibilityContent()
{
_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);
_box.IsReadOnly(true);
_root.Children().Append(_box);
}
void AccessibilityContent::UpdateSettings(const CascadiaSettings& /*settings*/)
{
// Nothing to do.
}
winrt::Windows::UI::Xaml::FrameworkElement AccessibilityContent::GetRoot()
{
return _root;
}
winrt::Windows::Foundation::Size AccessibilityContent::MinSize()
{
return { 1, 1 };
}
void AccessibilityContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
_box.Focus(reason);
}
void AccessibilityContent::Close()
{
CloseRequested.raise(*this, nullptr);
}
NewTerminalArgs AccessibilityContent::GetNewTerminalArgs(const bool /* asContent */) const
{
return nullptr;
}
winrt::hstring AccessibilityContent::Icon() const
{
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
return winrt::hstring{ glyph };
}
winrt::Windows::UI::Xaml::Media::Brush AccessibilityContent::BackgroundBrush()
{
return _root.Background();
}
void AccessibilityContent::Write(winrt::hstring content)
{
_box.Text(content);
}
void AccessibilityContent::SetFont(Microsoft::Terminal::Settings::Model::FontConfig font)
{
_box.FontFamily(Media::FontFamily{ font.FontFace() });
// Multiply by 1.3, for the font point <--> DIP conversion
_box.FontSize(font.FontSize() * 1.3f);
}
}

View File

@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "AccessibilityContent.g.h"
namespace winrt::TerminalApp::implementation
{
struct AccessibilityContent : AccessibilityContentT<AccessibilityContent>
{
AccessibilityContent();
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;
void Write(winrt::hstring content);
void SetFont(Microsoft::Terminal::Settings::Model::FontConfig font);
winrt::hstring Title() { return L"a11y test TODO!"; }
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<> ConnectionStateChanged;
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 };
};
}

View File

@@ -5,6 +5,8 @@
#include "App.h"
#include "TerminalPage.h"
#include "ScratchpadContent.h"
#include "AccessibilityContent.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,6 +1421,59 @@ 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<ScratchpadContent>() };
// 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);
}
}
void TerminalPage::_HandleOpenAccessibilityPane(const IInspectable& sender,
const ActionEventArgs& args)
{
if (Feature_A11yPane::IsEnabled())
{
if (const auto activeTab{ _senderOrFocusedTab(sender) })
{
if (const auto control{ activeTab->GetActiveTerminalControl() })
{
const auto buffer = control.ReadEntireBuffer();
auto a11yPane{ winrt::make_self<AccessibilityContent>() };
if (const auto& profile{ activeTab->GetFocusedProfile() })
{
const auto& fontInfo{ profile.FontInfo() };
a11yPane->SetFont(fontInfo);
}
a11yPane->Write(buffer);
// 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.
a11yPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
auto resultPane = std::make_shared<Pane>(*a11yPane);
_SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
args.Handled(true);
}
}
}
}
void TerminalPage::_HandleOpenAbout(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{

View File

@@ -0,0 +1,56 @@
// 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> ConnectionStateChanged;
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; };
};
}

View File

@@ -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:
@@ -1022,181 +963,18 @@ Pane::PaneNeighborSearch Pane::_FindPaneAndNeighbor(const std::shared_ptr<Pane>
}
// 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).
// - Returns true if the connection state of this pane is closed.
// Arguments:
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget Pane::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& /*args*/)
// - true if the connection state of this Pane is closed.
bool Pane::IsConnectionClosed() const
{
auto newConnectionState = ConnectionState::Closed;
if (const auto coreState = sender.try_as<ICoreState>())
if (const auto& control{ GetTerminalControl() })
{
newConnectionState = coreState.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.
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;
}
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));
}
return control.ConnectionState() >= ConnectionState::Closed;
}
return false;
}
// Event Description:
@@ -1207,7 +985,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 +1000,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 +1023,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 +1080,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 +1095,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:
@@ -1332,9 +1113,16 @@ TermControl Pane::GetLastFocusedTerminalControl()
// - <none>
// Return Value:
// - nullptr if this Pane is a parent, otherwise the TermControl of this Pane.
TermControl Pane::GetTerminalControl()
TermControl Pane::GetTerminalControl() const
{
return _IsLeaf() ? _control : nullptr;
if (const auto& terminalPane{ _getTerminalContent() })
{
return terminalPane.GetTerminal();
}
else
{
return nullptr;
}
}
// Method Description:
@@ -1381,19 +1169,11 @@ void Pane::SetActive()
Profile Pane::GetFocusedProfile()
{
auto lastFocused = GetActivePane();
return lastFocused ? lastFocused->_profile : nullptr;
}
// Method Description:
// - Returns true if the connection state of this pane is closed. If this Pane is not a leaf this will
// return false.
// Arguments:
// - <none>
// Return Value:
// - true if the connection state of this Pane is closed.
bool Pane::IsConnectionClosed() const
{
return _control && _control.ConnectionState() >= ConnectionState::Closed;
if (const auto& terminalPane{ lastFocused->_getTerminalContent() })
{
return terminalPane.GetProfile();
}
return nullptr;
}
// Method Description:
@@ -1466,10 +1246,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:
@@ -1508,6 +1285,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:
@@ -1515,13 +1300,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:
@@ -1607,7 +1393,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
@@ -1643,35 +1429,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.
@@ -1691,7 +1458,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
@@ -1700,14 +1468,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
@@ -1746,15 +1517,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();
@@ -2173,7 +1935,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
@@ -2494,9 +2256,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.
@@ -2525,11 +2284,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;
_firstChild->_broadcastEnabled = _broadcastEnabled;
}
@@ -2852,8 +2608,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();
@@ -2864,8 +2628,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;
@@ -2884,8 +2650,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 };
}
}
@@ -2931,21 +2699,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
@@ -3050,7 +2833,7 @@ Size Pane::_GetMinSize() const
{
if (_IsLeaf())
{
auto controlSize = _control.MinimumSize();
auto controlSize = _content.MinSize();
auto newWidth = controlSize.Width;
auto newHeight = controlSize.Height;
@@ -3148,14 +2931,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:
@@ -3170,8 +2956,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
@@ -3186,9 +2972,12 @@ void Pane::EnableBroadcast(bool enabled)
if (_IsLeaf())
{
_broadcastEnabled = enabled;
_control.CursorVisibility(enabled ?
CursorDisplayState::Shown :
CursorDisplayState::Default);
if (const auto& termControl{ GetTerminalControl() })
{
termControl.CursorVisibility(enabled ?
CursorDisplayState::Shown :
CursorDisplayState::Default);
}
UpdateVisuals();
}
else
@@ -3205,9 +2994,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);
}
}
});
}
@@ -3218,9 +3010,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);
}
}
});
}
@@ -3229,19 +3024,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)
@@ -3249,7 +3041,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;
}

View File

@@ -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,
@@ -73,7 +73,7 @@ public:
std::shared_ptr<Pane> GetActivePane();
winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl();
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl() const;
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
bool IsConnectionClosed() const;
@@ -82,10 +82,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();
@@ -102,8 +107,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,
@@ -219,7 +225,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;
@@ -239,10 +244,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;
@@ -252,17 +255,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;
@@ -271,13 +263,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,
@@ -308,19 +301,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;
@@ -331,8 +316,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.

View File

@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "PaneArgs.h"
#include "BellEventArgs.g.cpp"

View 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;
};
};

View 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();
}
}

View File

@@ -0,0 +1,43 @@
// 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<> ConnectionStateChanged;
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 };
};
}

View File

@@ -0,0 +1,86 @@
// 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
{
// For now, we're doing a terrible thing in TerminalTab itself to
// generate an OpenSettings action manually, without asking for the pane
// structure.
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>();
}
}

View File

@@ -0,0 +1,50 @@
// 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<> ConnectionStateChanged;
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);
}

View File

@@ -1,131 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <LibraryResources.h>
#include "SettingsTab.h"
#include "SettingsTab.g.cpp"
#include "Utils.h"
using namespace winrt;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Core;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::Settings::Editor;
using namespace winrt::Windows::System;
namespace winrt
{
namespace MUX = Microsoft::UI::Xaml;
namespace WUX = Windows::UI::Xaml;
}
#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
namespace winrt::TerminalApp::implementation
{
SettingsTab::SettingsTab(MainPage settingsUI,
winrt::Windows::UI::Xaml::ElementTheme requestedTheme)
{
Content(settingsUI);
_requestedTheme = requestedTheme;
_MakeTabViewItem();
_CreateContextMenu();
_CreateIcon();
}
void SettingsTab::UpdateSettings(CascadiaSettings settings)
{
ASSERT_UI_THREAD();
auto settingsUI{ Content().as<MainPage>() };
settingsUI.UpdateSettings(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();
}
// Method Description:
// - Creates a list of actions that can be run to recreate the state of this tab
// Arguments:
// - asContent: unused. There's nothing different we need to do when
// serializing the settings tab for moving to another window. If we ever
// really want to support opening the SUI to a specific page, we can
// re-evaluate including that arg in this action then.
// Return Value:
// - The list of actions.
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
{
ASSERT_UI_THREAD();
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);
return std::vector{ std::move(action) };
}
// Method Description:
// - Focus the settings UI
// Arguments:
// - focusState: The FocusState mode by which focus is to be obtained.
// Return Value:
// - <none>
void SettingsTab::Focus(WUX::FocusState focusState)
{
ASSERT_UI_THREAD();
_focusState = focusState;
if (_focusState != FocusState::Unfocused)
{
Content().as<WUX::Controls::Page>().Focus(focusState);
}
}
// Method Description:
// - Initializes a TabViewItem for this Tab instance.
// Arguments:
// - <none>
// Return Value:
// - <none>
void SettingsTab::_MakeTabViewItem()
{
TabBase::_MakeTabViewItem();
Title(RS_(L"SettingsTab"));
TabViewItem().Header(winrt::box_value(Title()));
}
// Method Description:
// - Set the icon on the TabViewItem for this tab.
// Arguments:
// - <none>
// Return Value:
// - <none>
void SettingsTab::_CreateIcon()
{
// This is the Setting icon (looks like a gear)
static constexpr std::wstring_view glyph{ L"\xE713" };
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
Icon(winrt::hstring{ glyph });
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph, false));
}
winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_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>();
}
}

View File

@@ -1,43 +0,0 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- SettingsTab.h
Abstract:
- The SettingsTab is a tab whose content is a Settings UI control. They can
coexist in a TabView with all other types of tabs, like the TerminalTab.
There should only be at most one SettingsTab open at any given time.
Author(s):
- Leon Liang - October 2020
--*/
#pragma once
#include "TabBase.h"
#include "SettingsTab.g.h"
namespace winrt::TerminalApp::implementation
{
struct SettingsTab : SettingsTabT<SettingsTab, TabBase>
{
public:
SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI,
winrt::Windows::UI::Xaml::ElementTheme requestedTheme);
void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(const bool asContent = false) const override;
private:
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
void _MakeTabViewItem() override;
void _CreateIcon();
virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
};
}

View File

@@ -1,12 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TabBase.idl";
namespace TerminalApp
{
[default_interface] runtimeclass SettingsTab : TabBase
{
void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
}
}

View File

@@ -19,7 +19,6 @@
#include "TabRowControl.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
#include "SettingsTab.h"
#include "..\TerminalSettingsModel\FileUtils.h"
#include <shlobj.h>
@@ -63,7 +62,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 +85,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,16 +170,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())
{
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
newTabImpl->UpdateIcon(profile.Icon(), iconStyle);
}
}
// Set this tab's icon to the icon from the content
_UpdateTabIcon(*newTabImpl);
tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
@@ -225,13 +216,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:
@@ -241,11 +234,12 @@ 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() })
{
const auto& icon{ content.Icon() };
const auto theme = _settings.GlobalSettings().CurrentTheme();
const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
tab.UpdateIcon(profile.Icon(), iconStyle);
tab.UpdateIcon(icon, iconStyle);
}
}
@@ -799,14 +793,6 @@ namespace winrt::TerminalApp::implementation
}
}
}
else if (auto index{ _GetFocusedTabIndex() })
{
const auto tab{ _tabs.GetAt(*index) };
if (tab.try_as<TerminalApp::SettingsTab>())
{
_HandleCloseTabRequested(tab);
}
}
}
// Method Description:

View File

@@ -88,9 +88,6 @@
<DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
<SubType>Code</SubType>
</ClInclude>
<ClInclude Include="SettingsTab.h">
<DependentUpon>SettingsTab.idl</DependentUpon>
</ClInclude>
<ClInclude Include="PaletteItem.h" />
<ClInclude Include="TabBase.h">
<DependentUpon>TabBase.idl</DependentUpon>
@@ -134,6 +131,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 +161,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="AccessibilityContent.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>
@@ -182,9 +194,6 @@
<DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
<SubType>Code</SubType>
</ClCompile>
<ClCompile Include="SettingsTab.cpp">
<DependentUpon>SettingsTab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="PaletteItem.cpp" />
<ClCompile Include="TabBase.cpp">
<DependentUpon>TabBase.idl</DependentUpon>
@@ -236,6 +245,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 +278,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="AccessibilityContent.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">
@@ -288,7 +312,6 @@
<Midl Include="PaletteItemTemplateSelector.idl">
<SubType>Designer</SubType>
</Midl>
<Midl Include="SettingsTab.idl" />
<Midl Include="PaletteItem.idl" />
<Midl Include="ShortcutActionDispatch.idl" />
<Midl Include="AppKeyBindings.idl" />
@@ -340,6 +363,8 @@
</Midl>
<Midl Include="FilteredCommand.idl" />
<Midl Include="EmptyStringVisibilityConverter.idl" />
<Midl Include="IPaneContent.idl" />
<Midl Include="TerminalPaneContent.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->
<ItemGroup>

View File

@@ -24,9 +24,6 @@
<ClCompile Include="Tab.cpp">
<Filter>tab</Filter>
</ClCompile>
<ClCompile Include="SettingsTab.cpp">
<Filter>tab</Filter>
</ClCompile>
<ClCompile Include="FilteredCommand.cpp">
<Filter>commandPalette</Filter>
</ClCompile>
@@ -64,9 +61,6 @@
<ClInclude Include="Tab.h">
<Filter>tab</Filter>
</ClInclude>
<ClInclude Include="SettingsTab.h">
<Filter>tab</Filter>
</ClInclude>
<ClInclude Include="FilteredCommand.h">
<Filter>commandPalette</Filter>
</ClInclude>
@@ -99,9 +93,6 @@
<Filter>settings</Filter>
</Midl>
<Midl Include="IDirectKeyListener.idl" />
<Midl Include="SettingsTab.idl">
<Filter>tab</Filter>
</Midl>
<Midl Include="TerminalTab.idl">
<Filter>tab</Filter>
</Midl>
@@ -187,4 +178,4 @@
<Filter>app</Filter>
</ApplicationDefinition>
</ItemGroup>
</Project>
</Project>

View File

@@ -21,7 +21,7 @@
#include "App.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
#include "SettingsTab.h"
#include "SettingsPaneContent.h"
#include "TabRowControl.h"
#include "Utils.h"
@@ -1305,15 +1305,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 };
@@ -1734,9 +1739,9 @@ namespace winrt::TerminalApp::implementation
hostingTab.TaskbarProgressChanged({ get_weak(), &TerminalPage::_SetTaskbarProgressHandler });
}
void TerminalPage::_RegisterPaneEvents(std::shared_ptr<Pane>& pane)
void TerminalPage::_RegisterPaneEvents(const TerminalApp::TerminalPaneContent& paneContent)
{
pane->RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection });
paneContent.RestartTerminalRequested({ get_weak(), &TerminalPage::_restartPaneConnection });
}
// Method Description:
@@ -2364,6 +2369,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
@@ -2390,7 +2401,10 @@ namespace winrt::TerminalApp::implementation
// re-add our event handler to that newly created pane.
//
// _MakePane will already call this for the newly created pane.
_RegisterPaneEvents(original);
if (const auto& paneContent{ original->GetContent().try_as<TerminalPaneContent>() })
{
_RegisterPaneEvents(*paneContent);
}
// After GH#6586, the control will no longer focus itself
// automatically when it's finished being laid out. Manually focus
@@ -3095,10 +3109,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 };
@@ -3154,13 +3167,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
@@ -3174,16 +3189,18 @@ namespace winrt::TerminalApp::implementation
original->SetActive();
}
_RegisterPaneEvents(resultPane);
_RegisterPaneEvents(terminalPane);
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();
}
}
@@ -3285,11 +3302,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()) };
@@ -3304,7 +3327,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);
}
}
});
@@ -3317,10 +3340,6 @@ namespace winrt::TerminalApp::implementation
// Force the TerminalTab to re-grab its currently active control's title.
terminalTab->UpdateTitle();
}
else if (auto settingsTab = tab.try_as<TerminalApp::SettingsTab>())
{
settingsTab.UpdateSettings(_settings);
}
auto tabImpl{ winrt::get_self<TabBase>(tab) };
tabImpl->SetActionMap(_settings.ActionMap());
@@ -3885,7 +3904,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));
@@ -3901,54 +3923,9 @@ namespace winrt::TerminalApp::implementation
}
});
auto newTabImpl = winrt::make_self<SettingsTab>(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme());
// Add the new tab to the list of our tabs.
_tabs.Append(*newTabImpl);
_mruTabs.Append(*newTabImpl);
newTabImpl->SetDispatch(*_actionDispatch);
newTabImpl->SetActionMap(_settings.ActionMap());
// Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
_UpdateTabIndices();
// 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);
auto tabViewItem = newTabImpl->TabViewItem();
_tabView.TabItems().Append(tabViewItem);
tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick });
// 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();
if (page && tab)
{
page->_RemoveTab(*tab);
}
});
_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);
// Create the tab
auto resultPane = std::make_shared<Pane>(*settingsContent);
_settingsTab = _CreateNewTabFromPane(resultPane);
}
else
{
@@ -4630,13 +4607,12 @@ namespace winrt::TerminalApp::implementation
til::color bgColor = backgroundSolidBrush.Color();
Media::Brush terminalBrush{ nullptr };
if (const auto& control{ _GetActiveControl() })
if (const auto tab{ _GetFocusedTabImpl() })
{
terminalBrush = control.BackgroundBrush();
}
else if (const auto& settingsTab{ _GetFocusedTab().try_as<TerminalApp::SettingsTab>() })
{
terminalBrush = settingsTab.Content().try_as<Settings::Editor::MainPage>().BackgroundBrush();
if (const auto& pane{ tab->GetActivePane() })
{
terminalBrush = pane->GetContent().BackgroundBrush();
}
}
if (_settings.GlobalSettings().UseAcrylicInTabRow())

View File

@@ -224,7 +224,7 @@ namespace winrt::TerminalApp::implementation
void _UpdateTabIndices();
TerminalApp::SettingsTab _settingsTab{ nullptr };
TerminalApp::TerminalTab _settingsTab{ nullptr };
bool _isInFocusMode{ false };
bool _isFullscreen{ false };
@@ -302,14 +302,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);
@@ -345,7 +345,7 @@ namespace winrt::TerminalApp::implementation
void _InitializeTab(winrt::com_ptr<TerminalTab> newTabImpl, uint32_t insertPosition = -1);
void _RegisterTerminalEvents(Microsoft::Terminal::Control::TermControl term);
void _RegisterTabEvents(TerminalTab& hostingTab);
void _RegisterPaneEvents(std::shared_ptr<Pane>& pane);
void _RegisterPaneEvents(const TerminalApp::TerminalPaneContent& paneContent);
void _DismissTabContextMenus();
void _FocusCurrentTab(const bool focusAlways);

View File

@@ -0,0 +1,353 @@
// 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>
winrt::fire_and_forget TerminalPaneContent::_ControlConnectionStateChangedHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::Foundation::IInspectable& args)
{
ConnectionStateChanged.raise(sender, args);
auto newConnectionState = ConnectionState::Closed;
if (const auto coreState = sender.try_as<ICoreState>())
{
newConnectionState = coreState.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.
co_return;
}
const auto weakThis = get_weak();
co_await wil::resume_foreground(_control.Dispatcher());
const auto strongThis = weakThis.get();
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 (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();
}
}
}
// 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();
}
}

View File

@@ -0,0 +1,98 @@
// 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<> ConnectionStateChanged;
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);
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 _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*/);
};
}

View File

@@ -0,0 +1,33 @@
// 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 AccessibilityContent : IPaneContent
{
}
}

View File

@@ -191,6 +191,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
@@ -205,9 +210,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);
}
});
}
@@ -266,12 +271,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:
@@ -280,7 +291,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, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle)
void TerminalTab::UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle)
{
ASSERT_UI_THREAD();
@@ -383,8 +394,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:
@@ -439,9 +450,32 @@ namespace winrt::TerminalApp::implementation
// Give initial ids (0 for the child created with this tab,
// 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1, asContent);
Pane::BuildStartupState state;
// HORRIBLE
//
// Workaround till we know how we actually want to handle state
// restoring other kinda of panes. If this is a settings tab, just
// restore it as a settings tab. Don't bother recreating terminal args
// for every pane.
//
// In the future, we'll want to definitely get rid of
// Pane::GetTerminalArgsForPane, and somehown instead find a better way
// of re-creating the pane state. Probably through a combo of ResizePane
// actions and SetPaneOrientation actions.
if (const auto& settings{ _rootPane->GetContent().try_as<SettingsPaneContent>() })
{
ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
action.Args(args);
state.args = std::vector{ std::move(action) };
}
else
{
state = _rootPane->BuildStartupActions(0, 1, asContent);
ActionAndArgs newTabAction{};
newTabAction.Action(ShortcutAction::NewTab);
NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(asContent) };
@@ -519,7 +553,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;
@@ -632,7 +669,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++;
}
});
@@ -889,29 +930,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.ConnectionStateChanged(events.stateToken);
control.ReadOnlyChanged(events.readOnlyToken);
control.FocusFollowMouseRequested(events.focusToken);
events.KeySent.revoke();
events.CharSent.revoke();
events.StringSent.revoke();
_controlEvents.erase(paneId);
_contentEvents.erase(paneId);
}
}
@@ -926,79 +956,94 @@ 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.stateToken = control.ConnectionStateChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThis.get() })
{
tab->_UpdateConnectionClosedState();
}
});
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.ConnectionStateChanged = content.ConnectionStateChanged(
winrt::auto_revoke,
[dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThis.get() })
{
tab->_UpdateConnectionClosedState();
}
});
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:
@@ -1248,36 +1293,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() })
{
@@ -1285,14 +1306,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)
{
@@ -1493,7 +1510,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();
}
@@ -1515,12 +1537,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();
}
}
@@ -1530,7 +1552,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
//
@@ -1539,7 +1561,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));
}
@@ -1578,7 +1600,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();
}
@@ -1774,6 +1796,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
@@ -1826,8 +1852,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;
@@ -1845,7 +1871,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

View File

@@ -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, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle);
void UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle);
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;
@@ -122,20 +123,22 @@ 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 stateToken;
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::ConnectionStateChanged_revoker ConnectionStateChanged;
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{};
@@ -162,8 +165,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);
@@ -181,7 +184,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);

View File

@@ -36,7 +36,6 @@
<ClInclude Include="../TabRowControl.h" />
<ClInclude Include="../App.h" />
<ClInclude Include="../TerminalTab.h" />
<ClInclude Include="../SettingsTab.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>

View File

@@ -82,6 +82,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

View File

@@ -96,6 +96,8 @@ 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 OpenAccessibilityPaneKey{ "experimental.openAccessibilityPane" };
static constexpr std::string_view OpenAboutKey{ "openAbout" };
static constexpr std::string_view ActionKey{ "action" };
@@ -431,6 +433,8 @@ 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") },
{ ShortcutAction::OpenAccessibilityPane, RS_(L"OpenAccessibilityPaneKey") },
{ ShortcutAction::OpenAbout, RS_(L"OpenAboutCommandKey") },
};
}();

View File

@@ -110,6 +110,8 @@
ON_ALL_ACTIONS(CloseOtherPanes) \
ON_ALL_ACTIONS(RestartConnection) \
ON_ALL_ACTIONS(ToggleBroadcastInput) \
ON_ALL_ACTIONS(OpenScratchpad) \
ON_ALL_ACTIONS(OpenAccessibilityPane) \
ON_ALL_ACTIONS(OpenAbout)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \

View File

@@ -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,12 @@
<data name="RestartConnectionKey" xml:space="preserve">
<value>Restart connection</value>
</data>
<data name="OpenScratchpadKey" xml:space="preserve">
<value>Open scratchpad</value>
</data>
<data name="OpenAccessibilityPaneKey" xml:space="preserve">
<value>Open accessibility pane</value>
</data>
<data name="SelectOutputNextCommandKey" xml:space="preserve">
<value>Select next command output</value>
</data>

View File

@@ -187,4 +187,26 @@
</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>
<brandingToken>Canary</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
<feature>
<name>Feature_A11yPane</name>
<description>Allow the user to create accessibility panes.</description>
<id>16005</id>
<stage>AlwaysDisabled</stage>
<alwaysEnabledBrandingTokens>
<brandingToken>Dev</brandingToken>
<brandingToken>Canary</brandingToken>
</alwaysEnabledBrandingTokens>
</feature>
</featureStaging>

View File

@@ -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;