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

* ALT+F4 to close the terminal app window and warn user of multiple tabs

* Fix indent issue

* Indentation issue fix 2

* add some comments

* move close window warning texts to resources

* CR feedback changes - 8/28

* fix resource file space issue

* Fix resource file space issue 2

* Fix resource file space issue

* minor CR changes

* update comments

* Sync to the latest master branch

* CR changes round 2

* Format fix

* fix type conversion warning

* CR changes on 9-12

* CR feedback changes on 9-12

* add comments why we remove tabs in reverse order

* Fix warnings
This commit is contained in:
Kaiyu Wang 2019-09-16 22:43:27 -07:00 committed by GitHub
parent 2d0608d8c0
commit 5806cdab52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 139 additions and 40 deletions

View file

@ -167,9 +167,12 @@ namespace winrt::TerminalApp::implementation
} }
// Method Description: // 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 // 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 // - Only one dialog can be visible at a time. If another dialog is visible
// when this is called, nothing happens. // when this is called, nothing happens.
// Arguments: // Arguments:

View file

@ -60,6 +60,13 @@ namespace winrt::TerminalApp::implementation
args.Handled(true); args.Handled(true);
} }
void TerminalPage::_HandleCloseWindow(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
_CloseWindow();
args.Handled(true);
}
void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/, void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args) const TerminalApp::ActionEventArgs& args)
{ {

View file

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
Version 2.0 Version 2.0
The primary goals of this format is to allow a simple XML format The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes various data types are done through the TypeConverter classes
associated with the data types. associated with the data types.
Example: Example:
... ado.net/XML headers & schema ... ... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</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> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment> <comment>This is a comment</comment>
</data> </data>
There are any number of "resheader" rows that contain simple There are any number of "resheader" rows that contain simple
name/value pairs. name/value pairs.
Each data row contains a name, and value. The row also contains a Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture. text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the Classes that don't support this are serialized and stored with the
mimetype set. mimetype set.
The mimetype is used for serialized objects, and tells the The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly: extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below. read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64 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 : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64 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 : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64 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 : using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding. : and then encoded with base64 encoding.
--> -->
@ -193,4 +193,13 @@ Temporarily using the Windows Terminal default settings.
<data name="SettingsMenuItem" xml:space="preserve"> <data name="SettingsMenuItem" xml:space="preserve">
<value>Settings</value> <value>Settings</value>
</data> </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>

View file

@ -184,6 +184,29 @@ namespace winrt::TerminalApp::implementation
_showDialogHandlers(*this, dialog); _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: // Method Description:
// - Builds the flyout (dropdown) attached to the new tab button, and // - Builds the flyout (dropdown) attached to the new tab button, and
// attaches it to the button. Populates the flyout with one entry per // attaches it to the button. Populates the flyout with one entry per
@ -513,6 +536,7 @@ namespace winrt::TerminalApp::implementation
bindings.DuplicateTab({ this, &TerminalPage::_HandleDuplicateTab }); bindings.DuplicateTab({ this, &TerminalPage::_HandleDuplicateTab });
bindings.CloseTab({ this, &TerminalPage::_HandleCloseTab }); bindings.CloseTab({ this, &TerminalPage::_HandleCloseTab });
bindings.ClosePane({ this, &TerminalPage::_HandleClosePane }); bindings.ClosePane({ this, &TerminalPage::_HandleClosePane });
bindings.CloseWindow({ this, &TerminalPage::_HandleCloseWindow });
bindings.ScrollUp({ this, &TerminalPage::_HandleScrollUp }); bindings.ScrollUp({ this, &TerminalPage::_HandleScrollUp });
bindings.ScrollDown({ this, &TerminalPage::_HandleScrollDown }); bindings.ScrollDown({ this, &TerminalPage::_HandleScrollDown });
bindings.NextTab({ this, &TerminalPage::_HandleNextTab }); bindings.NextTab({ this, &TerminalPage::_HandleNextTab });
@ -603,25 +627,36 @@ namespace winrt::TerminalApp::implementation
} }
// Method Description: // 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: // Arguments:
// - tabViewItem: the TabViewItem in the TabView that is being removed. // - tabViewItem: the TabViewItem in the TabView that is being removed.
void TerminalPage::_RemoveTabViewItem(const IInspectable& tabViewItem) 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. // To close the window here, we need to close the hosting window.
if (_tabs.size() == 1) if (_tabs.size() == 1)
{ {
_lastTabClosedHandlers(*this, nullptr); _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. // Removing the tab from the collection will destroy its control and disconnect its connection.
_tabs.erase(_tabs.begin() + tabIndexFromControl); _tabs.erase(_tabs.begin() + tabIndex);
_tabView.Items().RemoveAt(tabIndexFromControl); _tabView.Items().RemoveAt(tabIndex);
if (tabIndexFromControl == focusedTabIndex) auto focusedTabIndex = _GetFocusedTabIndex();
if (tabIndex == focusedTabIndex)
{ {
auto const tabCount = gsl::narrow_cast<decltype(focusedTabIndex)>(_tabs.size()); auto const tabCount = gsl::narrow_cast<decltype(focusedTabIndex)>(_tabs.size());
if (focusedTabIndex >= tabCount) if (focusedTabIndex >= tabCount)
@ -771,9 +806,8 @@ namespace winrt::TerminalApp::implementation
// - Close the currently focused tab. Focus will move to the left, if possible. // - Close the currently focused tab. Focus will move to the left, if possible.
void TerminalPage::_CloseFocusedTab() void TerminalPage::_CloseFocusedTab()
{ {
int focusedTabIndex = _GetFocusedTabIndex(); uint32_t focusedTabIndex = _GetFocusedTabIndex();
std::shared_ptr<Tab> focusedTab{ _tabs[focusedTabIndex] }; _RemoveTabViewItemByIndex(focusedTabIndex);
_RemoveTabViewItem(focusedTab->GetTabViewItem());
} }
// Method Description: // Method Description:
@ -787,6 +821,32 @@ namespace winrt::TerminalApp::implementation
focusedTab->ClosePane(); 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: // Method Description:
// - Move the viewport of the terminal of the currently focused tab up or // - 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 // down a number of lines. Negative values of `delta` will move the
@ -1205,6 +1265,20 @@ namespace winrt::TerminalApp::implementation
eventArgs.Cancel(true); 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: // Method Description:
// - Hook up keybindings, and refresh the UI of the terminal. // - Hook up keybindings, and refresh the UI of the terminal.
// This includes update the settings of all the tabs according // This includes update the settings of all the tabs according

View file

@ -61,6 +61,7 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr }; std::shared_ptr<ScopedResourceLoader> _resourceLoader{ nullptr };
void _ShowAboutDialog(); void _ShowAboutDialog();
void _ShowCloseWarningDialog();
void _CreateNewTabFlyout(); void _CreateNewTabFlyout();
void _OpenNewTabDropdown(); void _OpenNewTabDropdown();
@ -71,6 +72,7 @@ namespace winrt::TerminalApp::implementation
void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
void _FeedbackButtonOnClick(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 _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; void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept;
@ -79,6 +81,7 @@ namespace winrt::TerminalApp::implementation
void _UpdateTabView(); void _UpdateTabView();
void _DuplicateTabViewItem(); void _DuplicateTabViewItem();
void _RemoveTabViewItem(const IInspectable& tabViewItem); void _RemoveTabViewItem(const IInspectable& tabViewItem);
void _RemoveTabViewItemByIndex(uint32_t tabIndex);
void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab); void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr<Tab> hostingTab);
@ -91,6 +94,8 @@ namespace winrt::TerminalApp::implementation
void _SetFocusedTabIndex(int tabIndex); void _SetFocusedTabIndex(int tabIndex);
void _CloseFocusedTab(); void _CloseFocusedTab();
void _CloseFocusedPane(); void _CloseFocusedPane();
void _CloseWindow();
void _CloseAllTabs();
// Todo: add more event implementations here // Todo: add more event implementations here
// MSFT:20641986: Add keybindings for New Window // MSFT:20641986: Add keybindings for New Window
@ -142,6 +147,7 @@ namespace winrt::TerminalApp::implementation
void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleMoveFocus(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 _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
void _HandleCloseWindow(const IInspectable&, const TerminalApp::ActionEventArgs& args);
#pragma endregion #pragma endregion
}; };
} }