Skip to content

Commit

Permalink
Handle window resize event (CSI t, resize) (#17721)
Browse files Browse the repository at this point in the history
`ResizeWindow` event in `TerminalApi` is handled and bubbled to
`TerminalApi->ControlCore->TermControl->TerminalPage->AppHost`. Resizing
is accepted only if the window is not in fullscreen or quake mode, and
has 1 tab and pane.

Relevant issues: #5094
  • Loading branch information
nukoseer authored Aug 29, 2024
1 parent 93d592b commit 837215b
Show file tree
Hide file tree
Showing 24 changed files with 248 additions and 4 deletions.
27 changes: 27 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,8 @@ namespace winrt::TerminalApp::implementation

term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler });

term.WindowSizeChanged({ get_weak(), &TerminalPage::_WindowSizeChanged });

// Don't even register for the event if the feature is compiled off.
if constexpr (Feature_ShellCompletions::IsEnabled())
{
Expand Down Expand Up @@ -3090,6 +3092,31 @@ namespace winrt::TerminalApp::implementation
term.RefreshQuickFixMenu();
}

void TerminalPage::_WindowSizeChanged(const IInspectable sender, const Microsoft::Terminal::Control::WindowSizeChangedEventArgs args)
{
// Raise if:
// - Not in quake mode
// - Not in fullscreen
// - Only one tab exists
// - Only one pane exists
// else:
// - Reset conpty to its original size back
if (!WindowProperties().IsQuakeWindow() && !Fullscreen() &&
NumberOfTabs() == 1 && _GetFocusedTabImpl()->GetLeafPaneCount() == 1)
{
WindowSizeChanged.raise(*this, args);
}
else if (const auto& control{ sender.try_as<TermControl>() })
{
const auto& connection = control.Connection();

if (const auto& conpty{ connection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ResetSize();
}
}
}

// Method Description:
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ namespace winrt::TerminalApp::implementation
til::typed_event<IInspectable, IInspectable> IdentifyWindowsRequested;
til::typed_event<IInspectable, winrt::TerminalApp::RenameWindowRequestedArgs> RenameWindowRequested;
til::typed_event<IInspectable, IInspectable> SummonWindowRequested;
til::typed_event<IInspectable, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs> WindowSizeChanged;

til::typed_event<IInspectable, IInspectable> CloseRequested;
til::typed_event<IInspectable, IInspectable> OpenSystemMenu;
Expand Down Expand Up @@ -541,6 +542,7 @@ namespace winrt::TerminalApp::implementation
Windows::Foundation::IAsyncAction _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::MatchResult>> _FindPackageAsync(hstring query);

void _WindowSizeChanged(const IInspectable sender, const winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args);
safe_void_coroutine _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);

void _onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView& sender, const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e);
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/TerminalPage.idl
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> IdentifyWindowsRequested;
event Windows.Foundation.TypedEventHandler<Object, RenameWindowRequestedArgs> RenameWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> SummonWindowRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.WindowSizeChangedEventArgs> WindowSizeChanged;

event Windows.Foundation.TypedEventHandler<Object, Object> CloseRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> OpenSystemMenu;
Expand Down
37 changes: 37 additions & 0 deletions src/cascadia/TerminalApp/TerminalWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::ApplicationModel::DataTransfer;
using namespace winrt::Windows::Graphics::Display;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Core;
Expand Down Expand Up @@ -217,6 +218,7 @@ namespace winrt::TerminalApp::implementation
_root->SetSettings(_settings, false); // We're on our UI thread right now, so this is safe
_root->Loaded({ get_weak(), &TerminalWindow::_OnLoaded });
_root->Initialized({ get_weak(), &TerminalWindow::_pageInitialized });
_root->WindowSizeChanged({ get_weak(), &TerminalWindow::_WindowSizeChanged });
_root->Create();

AppLogic::Current()->SettingsChanged({ get_weak(), &TerminalWindow::UpdateSettingsHandler });
Expand Down Expand Up @@ -1331,6 +1333,41 @@ namespace winrt::TerminalApp::implementation
}
}

void TerminalWindow::_WindowSizeChanged(const IInspectable&, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args)
{
winrt::Windows::Foundation::Size pixelSize = { static_cast<float>(args.Width()), static_cast<float>(args.Height()) };
const auto scale = static_cast<float>(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel());

if (!FocusMode())
{
if (!_settings.GlobalSettings().AlwaysShowTabs())
{
// Hide the title bar = off, Always show tabs = off.
static constexpr auto titlebarHeight = 10;
pixelSize.Height += (titlebarHeight)*scale;
}
else if (!_settings.GlobalSettings().ShowTabsInTitlebar())
{
// Hide the title bar = off, Always show tabs = on.
static constexpr auto titlebarAndTabBarHeight = 40;
pixelSize.Height += (titlebarAndTabBarHeight)*scale;
}
// Hide the title bar = on, Always show tabs = on.
// In this case, we don't add any height because
// NonClientIslandWindow::GetTotalNonClientExclusiveSize() gets
// called in AppHost::_resizeWindow and it already takes title bar
// height into account. In other cases above
// IslandWindow::GetTotalNonClientExclusiveSize() is called, and it
// doesn't take the title bar height into account, so we have to do
// the calculation manually.
}

args.Width(static_cast<int32_t>(pixelSize.Width));
args.Height(static_cast<int32_t>(pixelSize.Height));

WindowSizeChanged.raise(*this, args);
}

winrt::hstring WindowProperties::WindowName() const noexcept
{
return _WindowName;
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalApp/TerminalWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ namespace winrt::TerminalApp::implementation
til::typed_event<Windows::Foundation::IInspectable, Windows::Foundation::IInspectable> IsQuakeWindowChanged;
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SystemMenuChangeArgs> SystemMenuChangeRequested;
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::SettingsLoadEventArgs> SettingsChanged;
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs> WindowSizeChanged;

private:
// If you add controls here, but forget to null them either here or in
Expand Down Expand Up @@ -202,6 +203,7 @@ namespace winrt::TerminalApp::implementation
void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _pageInitialized(const IInspectable& sender, const IInspectable& eventArgs);
void _OpenSettingsUI();
void _WindowSizeChanged(const IInspectable& sender, winrt::Microsoft::Terminal::Control::WindowSizeChangedEventArgs args);

winrt::Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::ActionAndArgs> _contentStringToActions(const winrt::hstring& content,
const bool replaceFirstWithNewTab);
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/TerminalWindow.idl
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ namespace TerminalApp
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
event Windows.Foundation.TypedEventHandler<Object, TerminalApp.SystemMenuChangeArgs> SystemMenuChangeRequested;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Control.WindowSizeChangedEventArgs> WindowSizeChanged;

event Windows.Foundation.TypedEventHandler<Object, SettingsLoadEventArgs> SettingsChanged;

Expand Down
8 changes: 8 additions & 0 deletions src/cascadia/TerminalConnection/ConptyConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
}
}

void ConptyConnection::ResetSize()
{
if (_isConnected())
{
THROW_IF_FAILED(ConptyResizePseudoConsole(_hPC.get(), { Utils::ClampToShortMax(_cols, 1), Utils::ClampToShortMax(_rows, 1) }));
}
}

void ConptyConnection::ClearBuffer()
{
// If we haven't connected yet, then we really don't need to do
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalConnection/ConptyConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void Start();
void WriteInput(const winrt::array_view<const char16_t> buffer);
void Resize(uint32_t rows, uint32_t columns);
void ResetSize();
void Close() noexcept;
void ClearBuffer();

Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalConnection/ConptyConnection.idl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Microsoft.Terminal.TerminalConnection
String StartingTitle { get; };
UInt16 ShowWindow { get; };

void ResetSize();
void ClearBuffer();

void ShowHide(Boolean show);
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnClearQuickFix = [this] { ClearQuickFix(); };
_terminal->SetClearQuickFixCallback(pfnClearQuickFix);

auto pfnWindowSizeChanged = [this](auto&& PH1, auto&& PH2) { _terminalWindowSizeChanged(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2)); };
_terminal->SetWindowSizeChangedCallback(pfnWindowSizeChanged);

// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
Expand Down Expand Up @@ -1629,6 +1632,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration));
}

void ControlCore::_terminalWindowSizeChanged(int32_t width, int32_t height)
{
auto size = winrt::make<implementation::WindowSizeChangedEventArgs>(width, height);
WindowSizeChanged.raise(*this, size);
}

void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow)
{
SearchMissingCommand.raise(*this, make<implementation::SearchMissingCommandEventArgs>(hstring{ missingCommand }, bufferRow));
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::typed_event<IInspectable, Control::CompletionsChangedEventArgs> CompletionsChanged;
til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand;
til::typed_event<> RefreshQuickFixUI;
til::typed_event<IInspectable, Control::WindowSizeChangedEventArgs> WindowSizeChanged;

til::typed_event<> CloseTerminalRequested;
til::typed_event<> RestartTerminalRequested;
Expand Down Expand Up @@ -391,6 +392,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const int velocity,
const std::chrono::microseconds duration);
void _terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow);
void _terminalWindowSizeChanged(int32_t width, int32_t height);

safe_void_coroutine _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);

Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/ControlCore.idl
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, ShowWindowArgs> ShowWindowChanged;
event Windows.Foundation.TypedEventHandler<Object, SearchMissingCommandEventArgs> SearchMissingCommand;
event Windows.Foundation.TypedEventHandler<Object, Object> RefreshQuickFixUI;
event Windows.Foundation.TypedEventHandler<Object, WindowSizeChangedEventArgs> WindowSizeChanged;

// These events are always called from the UI thread (bugs aside)
event Windows.Foundation.TypedEventHandler<Object, FontSizeChangedArgs> FontSizeChanged;
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/EventArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
#include "CharSentEventArgs.g.cpp"
#include "StringSentEventArgs.g.cpp"
#include "SearchMissingCommandEventArgs.g.cpp"
#include "WindowSizeChangedEventArgs.g.cpp"
15 changes: 15 additions & 0 deletions src/cascadia/TerminalControl/EventArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "CharSentEventArgs.g.h"
#include "StringSentEventArgs.g.h"
#include "SearchMissingCommandEventArgs.g.h"
#include "WindowSizeChangedEventArgs.g.h"

namespace winrt::Microsoft::Terminal::Control::implementation
{
Expand Down Expand Up @@ -223,6 +224,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::property<winrt::hstring> MissingCommand;
til::property<til::CoordType> BufferRow;
};

struct WindowSizeChangedEventArgs : public WindowSizeChangedEventArgsT<WindowSizeChangedEventArgs>
{
public:
WindowSizeChangedEventArgs(int32_t width,
int32_t height) :
_Width(width),
_Height(height)
{
}

WINRT_PROPERTY(int32_t, Width);
WINRT_PROPERTY(int32_t, Height);
};
}

namespace winrt::Microsoft::Terminal::Control::factory_implementation
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalControl/EventArgs.idl
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,10 @@ namespace Microsoft.Terminal.Control
String MissingCommand { get; };
Int32 BufferRow { get; };
}

runtimeclass WindowSizeChangedEventArgs
{
Int32 Width;
Int32 Height;
}
}
51 changes: 51 additions & 0 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged });
_revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested });
_revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand });
_revokers.WindowSizeChanged = _core.WindowSizeChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleWindowSizeChanged });

_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });

Expand Down Expand Up @@ -2779,6 +2780,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return { width, height };
}

// Function Description:
// - Calculates new dimensions (in pixels) from row and column counts.
// Arguments:
// - dpi: The dpi value.
// - sizeInChars: The size to get the new dimensions for.
// Return Value:
// - a size containing the requested dimensions in pixels.
winrt::Windows::Foundation::Size TermControl::GetNewDimensions(const winrt::Windows::Foundation::Size& sizeInChars)
{
const auto cols = ::base::saturated_cast<int32_t>(sizeInChars.Width);
const auto rows = ::base::saturated_cast<int32_t>(sizeInChars.Height);
const auto fontSize = _core.FontSize();
const auto scrollState = _core.Settings().ScrollState();
const auto padding = _core.Settings().Padding();
const auto scale = static_cast<float>(DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel());
float width = cols * static_cast<float>(fontSize.Width);
float height = rows * static_cast<float>(fontSize.Height);

// Reserve additional space if scrollbar is intended to be visible
if (scrollState != ScrollbarState::Hidden)
{
// UWP XAML scrollbars aren't guaranteed to be the same size as the
// ComCtl scrollbars, but it's certainly close enough.
const auto dpi = ::base::saturated_cast<uint32_t>(USER_DEFAULT_SCREEN_DPI * scale);
const auto scrollbarSize = GetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
width += scrollbarSize;
}

const auto thickness = ParseThicknessFromPadding(padding);
// GH#2061 - make sure to account for the size the padding _will be_ scaled to
width += scale * static_cast<float>(thickness.Left + thickness.Right);
height += scale * static_cast<float>(thickness.Top + thickness.Bottom);

return { width, height };
}

// Method Description:
// - Get the size of a single character of this control. The size is in
// _pixels_. If you want it in DIPs, you'll need to DIVIDE by the
Expand Down Expand Up @@ -4088,6 +4125,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
SearchMissingCommand.raise(*this, args);
}

winrt::fire_and_forget TermControl::_bubbleWindowSizeChanged(const IInspectable& /*sender*/, Control::WindowSizeChangedEventArgs args)
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(Dispatcher());

if (auto control{ weakThis.get() })
{
winrt::Windows::Foundation::Size cellCount{ static_cast<float>(args.Width()), static_cast<float>(args.Height()) };
const auto pixelSize = GetNewDimensions(cellCount);

WindowSizeChanged.raise(*this, winrt::make<implementation::WindowSizeChangedEventArgs>(static_cast<int32_t>(pixelSize.Width), static_cast<int32_t>(pixelSize.Height)));
}
}

til::CoordType TermControl::_calculateSearchScrollOffset() const
{
auto result = 0;
Expand Down
Loading

0 comments on commit 837215b

Please sign in to comment.