Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Terminal closing with ALT + F4 and warning of multiple open tabs #2526

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/cascadia/TerminalApp/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,12 @@ namespace winrt::TerminalApp::implementation
}

// Method Description:
// - Show a ContentDialog with a single button to dismiss. Uses the
// - Show a ContentDialog with buttons to take further action. Uses the
// FrameworkElements provided as the title and content of this dialog, and
// displays a single button to dismiss.
// displays buttons (or a single button). Two buttons (primary and secondary)
// will be displayed if this is an warning dialog for closing the termimal,
// this allows the users to abondon the closing action. Otherwise, a single
// close button will be displayed.
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens.
// Arguments:
Expand Down
7 changes: 7 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ namespace winrt::TerminalApp::implementation
args.Handled(true);
}

void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_CloseWindow();
args.Handled(true);
}

void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
Expand Down
65 changes: 37 additions & 28 deletions src/cascadia/TerminalApp/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
KaiyuWang16 marked this conversation as resolved.
Show resolved Hide resolved
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>
Expand All @@ -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.
-->
Expand Down Expand Up @@ -193,4 +193,13 @@ Temporarily using the Windows Terminal default settings.
<data name="SettingsMenuItem" xml:space="preserve">
<value>Settings</value>
</data>
</root>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="CloseAll" xml:space="preserve">
<value>Close all</value>
</data>
<data name="CloseWindowWarningTitle" xml:space="preserve">
<value>Do you want to close all tabs?</value>
</data>
</root>
94 changes: 84 additions & 10 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,29 @@ namespace winrt::TerminalApp::implementation
_showDialogHandlers(*this, dialog);
}

// Method Description:
// - Displays a dialog for warnings found while closing the terminal app using
// key binding with multiple tabs opened. Display messages to warn user
// that more than 1 tab is opend, and once the user clicks the OK button, remove
// all the tabs and shut down and app. If cancel is clicked, the dialog will close
// - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. See _ShowDialog for details
void TerminalPage::_ShowCloseWarningDialog()
{
auto title = _resourceLoader->GetLocalizedString(L"CloseWindowWarningTitle");
auto primaryButtonText = _resourceLoader->GetLocalizedString(L"CloseAll");
auto secondaryButtonText = _resourceLoader->GetLocalizedString(L"Cancel");

Controls::ContentDialog dialog;
dialog.Title(winrt::box_value(title));

dialog.PrimaryButtonText(primaryButtonText);
dialog.SecondaryButtonText(secondaryButtonText);
auto token = dialog.PrimaryButtonClick({ this, &TerminalPage::_CloseWarningPrimaryButtonOnClick });

_showDialogHandlers(*this, dialog);
}

// Method Description:
// - Builds the flyout (dropdown) attached to the new tab button, and
// attaches it to the button. Populates the flyout with one entry per
Expand Down Expand Up @@ -500,6 +523,7 @@ namespace winrt::TerminalApp::implementation
bindings.DuplicateTab({ this, &TerminalPage::_HandleDuplicateTab });
bindings.CloseTab({ this, &TerminalPage::_HandleCloseTab });
bindings.ClosePane({ this, &TerminalPage::_HandleClosePane });
bindings.CloseWindow({ this, &TerminalPage::_HandleCloseWindow });
bindings.ScrollUp({ this, &TerminalPage::_HandleScrollUp });
bindings.ScrollDown({ this, &TerminalPage::_HandleScrollDown });
bindings.NextTab({ this, &TerminalPage::_HandleNextTab });
Expand Down Expand Up @@ -590,25 +614,36 @@ namespace winrt::TerminalApp::implementation
}

// Method Description:
// - Removes the tab (both TerminalControl and XAML)
// - Look for the index of the input tabView in the tabs vector,
// and call _RemoveTabViewItemByIndex
// Arguments:
// - tabViewItem: the TabViewItem in the TabView that is being removed.
void TerminalPage::_RemoveTabViewItem(const IInspectable& tabViewItem)
{
uint32_t tabIndexFromControl = 0;
_tabView.Items().IndexOf(tabViewItem, tabIndexFromControl);

_RemoveTabViewItemByIndex(tabIndexFromControl);
}

// Method Description:
// - Removes the tab (both TerminalControl and XAML)
// Arguments:
// - tabIndex: the index of the tab to be removed
void TerminalPage::_RemoveTabViewItemByIndex(uint32_t tabIndex)
{
// To close the window here, we need to close the hosting window.
if (_tabs.size() == 1)
{
_lastTabClosedHandlers(*this, nullptr);
}
uint32_t tabIndexFromControl = 0;
_tabView.Items().IndexOf(tabViewItem, tabIndexFromControl);
auto focusedTabIndex = _GetFocusedTabIndex();

// Removing the tab from the collection will destroy its control and disconnect its connection.
_tabs.erase(_tabs.begin() + tabIndexFromControl);
_tabView.Items().RemoveAt(tabIndexFromControl);
_tabs.erase(_tabs.begin() + tabIndex);
_tabView.Items().RemoveAt(tabIndex);

if (tabIndexFromControl == focusedTabIndex)
auto focusedTabIndex = _GetFocusedTabIndex();
if (tabIndex == focusedTabIndex)
{
auto const tabCount = gsl::narrow_cast<decltype(focusedTabIndex)>(_tabs.size());
if (focusedTabIndex >= tabCount)
Expand Down Expand Up @@ -758,9 +793,8 @@ namespace winrt::TerminalApp::implementation
// - Close the currently focused tab. Focus will move to the left, if possible.
void TerminalPage::_CloseFocusedTab()
{
int focusedTabIndex = _GetFocusedTabIndex();
std::shared_ptr<Tab> focusedTab{ _tabs[focusedTabIndex] };
_RemoveTabViewItem(focusedTab->GetTabViewItem());
uint32_t focusedTabIndex = _GetFocusedTabIndex();
_RemoveTabViewItemByIndex(focusedTabIndex);
}

// Method Description:
Expand All @@ -774,6 +808,32 @@ namespace winrt::TerminalApp::implementation
focusedTab->ClosePane();
}

// Method Description:
// - Close the terminal app with keys. If there is more
// than one tab opened, show a warning dialog.
void TerminalPage::_CloseWindow()
{
if (_tabs.size() > 1)
{
_ShowCloseWarningDialog();
}
else
{
_CloseAllTabs();
}
}

// Method Description:
// - Remove all the tabs opened and the terminal will terminate
// on its own when the last tab is closed.
void TerminalPage::_CloseAllTabs()
{
while (!_tabs.empty())
{
_RemoveTabViewItemByIndex(0);
}
}

// Method Description:
// - Move the viewport of the terminal of the currently focused tab up or
// down a number of lines. Negative values of `delta` will move the
Expand Down Expand Up @@ -1196,6 +1256,20 @@ namespace winrt::TerminalApp::implementation
eventArgs.Cancel(true);
}

// Method Description:
// - Called when the primary button of the content dialog is clicked.
// This calls _CloseAllTabs(), which closes all the tabs currently
// opened and then the Terminal app. This method will be called if
// the user confirms to close all the tabs.
// Arguments:
// - sender: unused
// - ContentDialogButtonClickEventArgs: unused
void TerminalPage::_CloseWarningPrimaryButtonOnClick(Windows::UI::Xaml::Controls::ContentDialog /* sender */,
Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs /* eventArgs*/)
{
_CloseAllTabs();
}

// Method Description:
// - Hook up keybindings, and refresh the UI of the terminal.
// This includes update the settings of all the tabs according
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };

void _ShowAboutDialog();
void _ShowCloseWarningDialog();

void _CreateNewTabFlyout();
void _OpenNewTabDropdown();
Expand All @@ -71,6 +72,7 @@ namespace winrt::TerminalApp::implementation
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _CloseWarningPrimaryButtonOnClick(Windows::UI::Xaml::Controls::ContentDialog sender, Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs eventArgs);

void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;

Expand All @@ -79,6 +81,7 @@ namespace winrt::TerminalApp::implementation
void _UpdateTabView();
void _DuplicateTabViewItem();
void _RemoveTabViewItem(const IInspectable& tabViewItem);
void _RemoveTabViewItemByIndex(uint32_t tabIndex);

void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);

Expand All @@ -91,6 +94,8 @@ namespace winrt::TerminalApp::implementation
void _SetFocusedTabIndex(int tabIndex);
void _CloseFocusedTab();
void _CloseFocusedPane();
void _CloseWindow();
void _CloseAllTabs();

// Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window
Expand Down Expand Up @@ -143,6 +148,7 @@ namespace winrt::TerminalApp::implementation
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseWindow(const IInspectable&, const TerminalApp::ActionEventArgs& args);
#pragma endregion
};
}
Expand Down