From 4f46129cb40e9d1409ad75281812bc184e345ef6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 17 Dec 2020 21:51:53 -0600 Subject: [PATCH] Add `size` param to `splitPane` action, `split-pane` subcommand (#8543) ## Summary of the Pull Request Adds a `size` parameter to `splitPane`. This takes a `float`, and specifies the portion of the parent pane that should be used to create the new one. This also adds the param to the `split-pane` subcommand. ### Examples | commandline | result | | -- | -- | | `wt ; sp -s .25` | ![image](https://user-images.githubusercontent.com/18356694/101784317-fb595680-3ac0-11eb-8248-782dc61957cf.png) | | `wt ; sp -s .8` | ![image](https://user-images.githubusercontent.com/18356694/101784442-20e66000-3ac1-11eb-8f9b-fb45a73c9334.png) | | `wt ; sp -s .8 ; sp -H -s .3` | ![image](https://user-images.githubusercontent.com/18356694/101784552-470c0000-3ac1-11eb-9deb-df37aaa36f01.png) | ## PR Checklist * [x] Closes #6298 * [x] I work here * [x] Tests added/passed * [x] Docs PR: MicrosoftDocs/terminal#208 ## Detailed Description of the Pull Request / Additional comments I went with `size`, `--size,-s` rather than `percent`, because the arg is the (0,1) version of the size, not the (0%,100%) version. ## Validation Steps Performed Added actions, played with the commandline, ran tests --- doc/cascadia/profiles.schema.json | 7 ++ .../LocalTests_SettingsModel/CommandTests.cpp | 52 ++++++++- .../LocalTests_TerminalApp/TabTests.cpp | 4 +- .../TerminalApp/AppActionHandlers.cpp | 6 +- .../TerminalApp/AppCommandlineArgs.cpp | 6 +- src/cascadia/TerminalApp/AppCommandlineArgs.h | 1 + src/cascadia/TerminalApp/AppLogic.cpp | 3 +- src/cascadia/TerminalApp/Pane.cpp | 101 ++++-------------- src/cascadia/TerminalApp/Pane.h | 8 +- .../Resources/en-US/Resources.resw | 61 ++++++----- src/cascadia/TerminalApp/TerminalPage.cpp | 9 +- src/cascadia/TerminalApp/TerminalPage.h | 7 +- src/cascadia/TerminalApp/TerminalTab.cpp | 24 ++--- src/cascadia/TerminalApp/TerminalTab.h | 10 +- .../TerminalSettingsModel/ActionArgs.cpp | 9 +- .../TerminalSettingsModel/ActionArgs.h | 13 +++ .../TerminalSettingsModel/ActionArgs.idl | 2 + .../TerminalWarnings.idl | 1 + tools/runut.cmd | 1 + 19 files changed, 186 insertions(+), 139 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index a0113974b..ed7136625 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -369,6 +369,13 @@ "splitMode": { "default": "duplicate", "description": "Control how the pane splits. Only accepts \"duplicate\" which will duplicate the focused pane's profile into a new pane." + }, + "size": { + "default": 0.5, + "description": "Specify how large the new pane should be, as a fraction of the current pane's size. 1.0 would be 'all of the current pane', and 0.0 is 'None of the parent'. Accepts floating point values from 0-1 (default 0.5).", + "maximum": 1, + "minimum": 0, + "type": "number" } } } diff --git a/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp b/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp index b3a0951f7..5aea35985 100644 --- a/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/CommandTests.cpp @@ -37,6 +37,7 @@ namespace SettingsModelLocalTests TEST_METHOD(ManyCommandsSameAction); TEST_METHOD(LayerCommand); TEST_METHOD(TestSplitPaneArgs); + TEST_METHOD(TestSplitPaneBadSize); TEST_METHOD(TestResourceKeyName); TEST_METHOD(TestAutogeneratedName); TEST_METHOD(TestLayerOnAutogeneratedName); @@ -147,7 +148,8 @@ namespace SettingsModelLocalTests { "name": "command1", "command": { "action": "splitPane", "split": "vertical" } }, { "name": "command2", "command": { "action": "splitPane", "split": "horizontal" } }, { "name": "command4", "command": { "action": "splitPane" } }, - { "name": "command5", "command": { "action": "splitPane", "split": "auto" } } + { "name": "command5", "command": { "action": "splitPane", "split": "auto" } }, + { "name": "command6", "command": { "action": "splitPane", "size": 0.25 } }, ])" }; const auto commands0Json = VerifyParseSucceeded(commands0String); @@ -156,7 +158,7 @@ namespace SettingsModelLocalTests VERIFY_ARE_EQUAL(0u, commands.Size()); auto warnings = implementation::Command::LayerJson(commands, commands0Json); VERIFY_ARE_EQUAL(0u, warnings.size()); - VERIFY_ARE_EQUAL(4u, commands.Size()); + VERIFY_ARE_EQUAL(5u, commands.Size()); { auto command = commands.Lookup(L"command1"); @@ -167,6 +169,7 @@ namespace SettingsModelLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitState::Vertical, realArgs.SplitStyle()); + VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command2"); @@ -177,6 +180,7 @@ namespace SettingsModelLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitState::Horizontal, realArgs.SplitStyle()); + VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command4"); @@ -187,6 +191,7 @@ namespace SettingsModelLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle()); + VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); } { auto command = commands.Lookup(L"command5"); @@ -197,8 +202,51 @@ namespace SettingsModelLocalTests VERIFY_IS_NOT_NULL(realArgs); // Verify the args have the expected value VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle()); + VERIFY_ARE_EQUAL(0.5, realArgs.SplitSize()); + } + { + auto command = commands.Lookup(L"command6"); + VERIFY_IS_NOT_NULL(command); + VERIFY_IS_NOT_NULL(command.Action()); + VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action()); + const auto& realArgs = command.Action().Args().try_as(); + VERIFY_IS_NOT_NULL(realArgs); + // Verify the args have the expected value + VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle()); + VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize()); } } + + void CommandTests::TestSplitPaneBadSize() + { + const std::string commands0String{ R"([ + { "name": "command1", "command": { "action": "splitPane", "size": 0.25 } }, + { "name": "command2", "command": { "action": "splitPane", "size": 1.0 } }, + { "name": "command3", "command": { "action": "splitPane", "size": 0 } }, + { "name": "command4", "command": { "action": "splitPane", "size": 50 } }, + ])" }; + + const auto commands0Json = VerifyParseSucceeded(commands0String); + + IMap commands = winrt::single_threaded_map(); + VERIFY_ARE_EQUAL(0u, commands.Size()); + auto warnings = implementation::Command::LayerJson(commands, commands0Json); + VERIFY_ARE_EQUAL(3u, warnings.size()); + VERIFY_ARE_EQUAL(1u, commands.Size()); + + { + auto command = commands.Lookup(L"command1"); + VERIFY_IS_NOT_NULL(command); + VERIFY_IS_NOT_NULL(command.Action()); + VERIFY_ARE_EQUAL(ShortcutAction::SplitPane, command.Action().Action()); + const auto& realArgs = command.Action().Args().try_as(); + VERIFY_IS_NOT_NULL(realArgs); + // Verify the args have the expected value + VERIFY_ARE_EQUAL(SplitState::Automatic, realArgs.SplitStyle()); + VERIFY_ARE_EQUAL(0.25, realArgs.SplitSize()); + } + } + void CommandTests::TestResourceKeyName() { // This test checks looking up a name from a resource key. diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index f06e57865..244207924 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -486,7 +486,7 @@ namespace TerminalAppLocalTests Log::Comment(NoThrowString().Format(L"Duplicate the first pane")); result = RunOnUIThread([&page]() { - page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr); + page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); @@ -504,7 +504,7 @@ namespace TerminalAppLocalTests Log::Comment(NoThrowString().Format(L"Duplicate the pane, and don't crash")); result = RunOnUIThread([&page]() { - page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr); + page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, 0.5f, nullptr); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index f51ea8c2a..4aab54c92 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -122,7 +122,11 @@ namespace winrt::TerminalApp::implementation } else if (const auto& realArgs = args.ActionArgs().try_as()) { - _SplitPane(realArgs.SplitStyle(), realArgs.SplitMode(), realArgs.TerminalArgs()); + _SplitPane(realArgs.SplitStyle(), + realArgs.SplitMode(), + // This is safe, we're already filtering so the value is (0, 1) + ::base::saturated_cast(realArgs.SplitSize()), + realArgs.TerminalArgs()); args.Handled(true); } } diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp index 4fcf9026c..54635e5da 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.cpp +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.cpp @@ -247,6 +247,10 @@ void AppCommandlineArgs::_buildSplitPaneParser() _splitVertical, RS_A(L"CmdSplitPaneVerticalArgDesc")); subcommand._verticalOption->excludes(subcommand._horizontalOption); + auto* sizeOpt = subcommand.subcommand->add_option("-s,--size", + _splitPaneSize, + RS_A(L"CmdSplitPaneSizeArgDesc")); + sizeOpt->check(CLI::Range(0.01f, 0.99f)); // When ParseCommand is called, if this subcommand was provided, this // callback function will be triggered on the same thread. We can be sure @@ -274,7 +278,7 @@ void AppCommandlineArgs::_buildSplitPaneParser() style = SplitState::Vertical; } } - SplitPaneArgs args{ style, terminalArgs }; + SplitPaneArgs args{ style, _splitPaneSize, terminalArgs }; splitPaneActionAndArgs.Args(args); _startupActions.push_back(splitPaneActionAndArgs); }); diff --git a/src/cascadia/TerminalApp/AppCommandlineArgs.h b/src/cascadia/TerminalApp/AppCommandlineArgs.h index 984a2b123..33c2f6218 100644 --- a/src/cascadia/TerminalApp/AppCommandlineArgs.h +++ b/src/cascadia/TerminalApp/AppCommandlineArgs.h @@ -86,6 +86,7 @@ private: bool _splitVertical{ false }; bool _splitHorizontal{ false }; + float _splitPaneSize{ 0.5f }; int _focusTabIndex{ -1 }; bool _focusNextTab{ false }; diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index f052341b5..c1eb72c2f 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -42,7 +42,8 @@ static const std::array(SettingsLoadWar USES_RESOURCE(L"LegacyGlobalsProperty"), USES_RESOURCE(L"FailedToParseCommandJson"), USES_RESOURCE(L"FailedToWriteToSettings"), - USES_RESOURCE(L"InvalidColorSchemeInCmd") + USES_RESOURCE(L"InvalidColorSchemeInCmd"), + USES_RESOURCE(L"InvalidSplitSize") }; static const std::array(SettingsLoadErrors::ERRORS_SIZE)> settingsLoadErrorsLabels { USES_RESOURCE(L"NoProfilesText"), diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 3a80e53dd..a9f1767e1 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -21,7 +21,6 @@ using namespace TerminalApp; static const int PaneBorderSize = 2; static const int CombinedPaneBorderSize = 2 * PaneBorderSize; -static const float Half = 0.50f; // WARNING: Don't do this! This won't work // Duration duration{ std::chrono::milliseconds{ 200 } }; @@ -1216,32 +1215,6 @@ void Pane::_SetupEntranceAnimation() setupAnimation(secondSize, false); } -// Method Description: -// - Determines whether the pane can be split -// Arguments: -// - splitType: what type of split we want to create. -// Return Value: -// - True if the pane can be split. False otherwise. -bool Pane::CanSplit(SplitState splitType) -{ - if (_IsLeaf()) - { - return _CanSplit(splitType); - } - - if (_firstChild->_HasFocusedChild()) - { - return _firstChild->CanSplit(splitType); - } - - if (_secondChild->_HasFocusedChild()) - { - return _secondChild->CanSplit(splitType); - } - - return false; -} - // Method Description: // - This is a helper to determine if a given Pane can be split, but without // using the ActualWidth() and ActualHeight() methods. This is used during @@ -1272,12 +1245,15 @@ bool Pane::CanSplit(SplitState splitType) // - This method is highly similar to Pane::PreCalculateAutoSplit std::optional Pane::PreCalculateCanSplit(const std::shared_ptr target, SplitState splitType, + const float splitSize, const winrt::Windows::Foundation::Size availableSpace) const { if (_IsLeaf()) { if (target.get() == this) { + const auto firstPrecent = 1.0f - splitSize; + const auto secondPercent = splitSize; // If this pane is a leaf, and it's the pane we're looking for, use // the available space to calculate which direction to split in. const Size minSize = _GetMinSize(); @@ -1290,17 +1266,19 @@ std::optional Pane::PreCalculateCanSplit(const std::shared_ptr targe else if (splitType == SplitState::Vertical) { const auto widthMinusSeparator = availableSpace.Width - CombinedPaneBorderSize; - const auto newWidth = widthMinusSeparator * Half; + const auto newFirstWidth = widthMinusSeparator * firstPrecent; + const auto newSecondWidth = widthMinusSeparator * secondPercent; - return { newWidth > minSize.Width }; + return { newFirstWidth > minSize.Width && newSecondWidth > minSize.Width }; } else if (splitType == SplitState::Horizontal) { const auto heightMinusSeparator = availableSpace.Height - CombinedPaneBorderSize; - const auto newHeight = heightMinusSeparator * Half; + const auto newFirstHeight = heightMinusSeparator * firstPrecent; + const auto newSecondHeight = heightMinusSeparator * secondPercent; - return { newHeight > minSize.Height }; + return { newFirstHeight > minSize.Height && newSecondHeight > minSize.Height }; } } else @@ -1329,8 +1307,8 @@ std::optional Pane::PreCalculateCanSplit(const std::shared_ptr targe (availableSpace.Height - firstHeight) - PaneBorderSize : availableSpace.Height; - const auto firstResult = _firstChild->PreCalculateCanSplit(target, splitType, { firstWidth, firstHeight }); - return firstResult.has_value() ? firstResult : _secondChild->PreCalculateCanSplit(target, splitType, { secondWidth, secondHeight }); + const auto firstResult = _firstChild->PreCalculateCanSplit(target, splitType, splitSize, { firstWidth, firstHeight }); + return firstResult.has_value() ? firstResult : _secondChild->PreCalculateCanSplit(target, splitType, splitSize, { secondWidth, secondHeight }); } // We should not possibly be getting here - both the above branches should @@ -1348,23 +1326,26 @@ std::optional Pane::PreCalculateCanSplit(const std::shared_ptr targe // - control: A TermControl to use in the new pane. // Return Value: // - The two newly created Panes -std::pair, std::shared_ptr> Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control) +std::pair, std::shared_ptr> Pane::Split(SplitState splitType, + const float splitSize, + const GUID& profile, + const TermControl& control) { if (!_IsLeaf()) { if (_firstChild->_HasFocusedChild()) { - return _firstChild->Split(splitType, profile, control); + return _firstChild->Split(splitType, splitSize, profile, control); } else if (_secondChild->_HasFocusedChild()) { - return _secondChild->Split(splitType, profile, control); + return _secondChild->Split(splitType, splitSize, profile, control); } return { nullptr, nullptr }; } - return _Split(splitType, profile, control); + return _Split(splitType, splitSize, profile, control); } // Method Description: @@ -1392,45 +1373,6 @@ SplitState Pane::_convertAutomaticSplitState(const SplitState& splitType) const return splitType; } -// Method Description: -// - Determines whether the pane can be split. -// Arguments: -// - splitType: what type of split we want to create. -// Return Value: -// - True if the pane can be split. False otherwise. -bool Pane::_CanSplit(SplitState splitType) -{ - const Size actualSize{ gsl::narrow_cast(_root.ActualWidth()), - gsl::narrow_cast(_root.ActualHeight()) }; - - const Size minSize = _GetMinSize(); - - auto actualSplitType = _convertAutomaticSplitState(splitType); - - if (actualSplitType == SplitState::None) - { - return false; - } - - if (actualSplitType == SplitState::Vertical) - { - const auto widthMinusSeparator = actualSize.Width - CombinedPaneBorderSize; - const auto newWidth = widthMinusSeparator * Half; - - return newWidth > minSize.Width; - } - - if (actualSplitType == SplitState::Horizontal) - { - const auto heightMinusSeparator = actualSize.Height - CombinedPaneBorderSize; - const auto newHeight = heightMinusSeparator * Half; - - return newHeight > minSize.Height; - } - - return false; -} - // Method Description: // - Does the bulk of the work of creating a new split. Initializes our UI, // creates a new Pane to host the control, registers event handlers. @@ -1440,7 +1382,10 @@ bool Pane::_CanSplit(SplitState splitType) // - control: A TermControl to use in the new pane. // Return Value: // - The two newly created Panes -std::pair, std::shared_ptr> Pane::_Split(SplitState splitType, const GUID& profile, const TermControl& control) +std::pair, std::shared_ptr> Pane::_Split(SplitState splitType, + const float splitSize, + const GUID& profile, + const TermControl& control) { if (splitType == SplitState::None) { @@ -1465,7 +1410,7 @@ std::pair, std::shared_ptr> Pane::_Split(SplitState _gotFocusRevoker.revoke(); _splitState = actualSplitType; - _desiredSplitPosition = Half; + _desiredSplitPosition = 1.0f - splitSize; // Remove any children we currently have. We can't add the existing // TermControl to a new grid until we do this. diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 1debee5f7..01e6cc237 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -58,14 +58,16 @@ public: bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction); bool NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction); - bool CanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType); std::pair, std::shared_ptr> Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, + const float splitSize, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; - std::optional PreCalculateAutoSplit(const std::shared_ptr target, const winrt::Windows::Foundation::Size parentSize) const; + std::optional PreCalculateAutoSplit(const std::shared_ptr target, + const winrt::Windows::Foundation::Size parentSize) const; std::optional PreCalculateCanSplit(const std::shared_ptr target, winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, + const float splitSize, const winrt::Windows::Foundation::Size availableSpace) const; void Shutdown(); void Close(); @@ -120,8 +122,8 @@ private: bool _HasFocusedChild() const noexcept; void _SetupChildCloseHandlers(); - bool _CanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType); std::pair, std::shared_ptr> _Split(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, + const float splitSize, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index d2747322c..3f1221140 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -1,17 +1,17 @@  - @@ -249,6 +249,10 @@ Found a command with an invalid "colorScheme". This command will be ignored. Make sure that when setting a "colorScheme", the value matches the "name" of a color scheme in the "schemes" list. {Locked="\"colorScheme\"","\"name\"","\"schemes\""} + + Found a "splitPane" command with an invalid "size". This command will be ignored. Make sure the size is between 0 and 1, exclusive. + {Locked="\"splitPane\"","\"size\""} + An optional command, with arguments, to be spawned in the new tab or pane @@ -268,6 +272,9 @@ Move focus the tab at the given index + + Specify the size as a percentage of the parent pane. Valid values are between (0,1), exclusive. + Create a new tab diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index a21779e26..e8c2ca2f6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -209,6 +209,7 @@ namespace winrt::TerminalApp::implementation { page->_SplitPane(SplitState::Automatic, SplitType::Manual, + 0.5f, nullptr); } else @@ -560,6 +561,7 @@ namespace winrt::TerminalApp::implementation { page->_SplitPane(SplitState::Automatic, SplitType::Manual, + 0.5f, newTerminalArgs); } else @@ -809,7 +811,7 @@ namespace winrt::TerminalApp::implementation TermControl newControl{ settings, debugConnection }; _RegisterTerminalEvents(newControl, *newTabImpl); // Split (auto) with the debug tap. - newTabImpl->SplitPane(SplitState::Automatic, profileGuid, newControl); + newTabImpl->SplitPane(SplitState::Automatic, 0.5f, profileGuid, newControl); } // This kicks off TabView::SelectionChanged, in response to which @@ -1578,6 +1580,7 @@ namespace winrt::TerminalApp::implementation // configurations. See CascadiaSettings::BuildSettings for more details. void TerminalPage::_SplitPane(const SplitState splitType, const SplitType splitMode, + const float splitSize, const NewTerminalArgs& newTerminalArgs) { // Do nothing if we're requesting no split. @@ -1647,7 +1650,7 @@ namespace winrt::TerminalApp::implementation realSplitType = focusedTab->PreCalculateAutoSplit(availableSpace); } - const auto canSplit = focusedTab->PreCalculateCanSplit(realSplitType, availableSpace); + const auto canSplit = focusedTab->PreCalculateCanSplit(realSplitType, splitSize, availableSpace); if (!canSplit) { return; @@ -1660,7 +1663,7 @@ namespace winrt::TerminalApp::implementation _UnZoomIfNeeded(); - focusedTab->SplitPane(realSplitType, realGuid, newControl); + focusedTab->SplitPane(realSplitType, splitSize, realGuid, newControl); } CATCH_LOG(); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 3edc7ac40..1ef708441 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -192,8 +192,13 @@ namespace winrt::TerminalApp::implementation // Todo: add more event implementations here // MSFT:20641986: Add keybindings for New Window void _Scroll(ScrollDirection scrollDirection, const Windows::Foundation::IReference& rowsToScroll); - void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitState splitType, const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual, const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr); + + void _SplitPane(const Microsoft::Terminal::Settings::Model::SplitState splitType, + const Microsoft::Terminal::Settings::Model::SplitType splitMode = Microsoft::Terminal::Settings::Model::SplitType::Manual, + const float splitSize = 0.5f, + const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs = nullptr); void _ResizePane(const Microsoft::Terminal::Settings::Model::ResizeDirection& direction); + void _ScrollPage(ScrollDirection scrollDirection); void _ScrollToBufferEdge(ScrollDirection scrollDirection); void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::TerminalControl::KeyChord& keyChord); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index ff2c62e02..ab668af46 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -293,17 +293,6 @@ namespace winrt::TerminalApp::implementation control.ScrollViewport(currentOffset + delta); } - // Method Description: - // - Determines whether the focused pane has sufficient space to be split. - // Arguments: - // - splitType: The type of split we want to create. - // Return Value: - // - True if the focused pane can be split. False otherwise. - bool TerminalTab::CanSplitPane(SplitState splitType) - { - return _activePane->CanSplit(splitType); - } - // Method Description: // - Split the focused pane in our tree of panes, and place the // given TermControl into the newly created pane. @@ -313,11 +302,14 @@ namespace winrt::TerminalApp::implementation // - control: A TermControl to use in the new pane. // Return Value: // - - void TerminalTab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control) + void TerminalTab::SplitPane(SplitState splitType, + const float splitSize, + const GUID& profile, + TermControl& control) { // Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID const auto activePaneId = _activePane->Id(); - auto [first, second] = _activePane->Split(splitType, profile, control); + auto [first, second] = _activePane->Split(splitType, splitSize, profile, control); first->Id(activePaneId); second->Id(_nextPaneId); ++_nextPaneId; @@ -943,9 +935,11 @@ namespace winrt::TerminalApp::implementation return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical); } - bool TerminalTab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const + bool TerminalTab::PreCalculateCanSplit(SplitState splitType, + const float splitSize, + winrt::Windows::Foundation::Size availableSpace) const { - return _rootPane->PreCalculateCanSplit(_activePane, splitType, availableSpace).value_or(false); + return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(false); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index a2bfe9b2b..1ae7ad2e6 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -30,15 +30,19 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget Scroll(const int delta); - bool CanSplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType); - void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); + void SplitPane(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, + const float splitSize, + const GUID& profile, + winrt::Microsoft::Terminal::TerminalControl::TermControl& control); winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath); winrt::fire_and_forget HideIcon(const bool hide); float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; winrt::Microsoft::Terminal::Settings::Model::SplitState PreCalculateAutoSplit(winrt::Windows::Foundation::Size rootSize) const; - bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const; + bool PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitState splitType, + const float splitSize, + winrt::Windows::Foundation::Size availableSpace) const; void ResizeContent(const winrt::Windows::Foundation::Size& newSize); void ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp index 85a4a1967..04dde967f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp @@ -231,8 +231,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation winrt::hstring SplitPaneArgs::GenerateName() const { // The string will be similar to the following: - // * "Duplicate pane[, split: ][, new terminal arguments...]" - // * "Split pane[, split: ][, new terminal arguments...]" + // * "Duplicate pane[, split: ][, size: %][, new terminal arguments...]" + // * "Split pane[, split: ][, size: %][, new terminal arguments...]" // // Direction will only be added to the string if the split direction is // not "auto". @@ -262,6 +262,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation break; } + if (_SplitSize != .5f) + { + ss << L"size: " << (_SplitSize * 100) << L"%, "; + } + winrt::hstring newTerminalArgsStr; if (_TerminalArgs) { diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 8d4d3d57a..8551288e6 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -379,6 +379,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation struct SplitPaneArgs : public SplitPaneArgsT { SplitPaneArgs() = default; + SplitPaneArgs(SplitState style, double size, const Model::NewTerminalArgs& terminalArgs) : + _SplitStyle{ style }, + _SplitSize{ size }, + _TerminalArgs{ terminalArgs } {}; SplitPaneArgs(SplitState style, const Model::NewTerminalArgs& terminalArgs) : _SplitStyle{ style }, _TerminalArgs{ terminalArgs } {}; @@ -387,9 +391,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation GETSET_PROPERTY(SplitState, SplitStyle, SplitState::Automatic); GETSET_PROPERTY(Model::NewTerminalArgs, TerminalArgs, nullptr); GETSET_PROPERTY(SplitType, SplitMode, SplitType::Manual); + GETSET_PROPERTY(double, SplitSize, .5); static constexpr std::string_view SplitKey{ "split" }; static constexpr std::string_view SplitModeKey{ "splitMode" }; + static constexpr std::string_view SplitSizeKey{ "size" }; public: hstring GenerateName() const; @@ -402,6 +408,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return otherAsUs->_SplitStyle == _SplitStyle && (otherAsUs->_TerminalArgs ? otherAsUs->_TerminalArgs.Equals(_TerminalArgs) : otherAsUs->_TerminalArgs == _TerminalArgs) && + otherAsUs->_SplitSize == _SplitSize && otherAsUs->_SplitMode == _SplitMode; } return false; @@ -413,6 +420,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation args->_TerminalArgs = NewTerminalArgs::FromJson(json); JsonUtils::GetValueForKey(json, SplitKey, args->_SplitStyle); JsonUtils::GetValueForKey(json, SplitModeKey, args->_SplitMode); + JsonUtils::GetValueForKey(json, SplitSizeKey, args->_SplitSize); + if (args->_SplitSize >= 1 || args->_SplitSize <= 0) + { + return { nullptr, { SettingsLoadWarnings::InvalidSplitSize } }; + } return { *args, {} }; } IActionArgs Copy() const @@ -421,6 +433,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_SplitStyle = _SplitStyle; copy->_TerminalArgs = _TerminalArgs.Copy(); copy->_SplitMode = _SplitMode; + copy->_SplitSize = _SplitSize; return *copy; } }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 29aa34730..2c539babe 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -135,12 +135,14 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass SplitPaneArgs : IActionArgs { + SplitPaneArgs(SplitState split, Double size, NewTerminalArgs terminalArgs); SplitPaneArgs(SplitState split, NewTerminalArgs terminalArgs); SplitPaneArgs(SplitType splitMode); SplitState SplitStyle { get; }; NewTerminalArgs TerminalArgs { get; }; SplitType SplitMode { get; }; + Double SplitSize { get; }; }; [default_interface] runtimeclass OpenSettingsArgs : IActionArgs diff --git a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl index 1446ff12e..ff8b96369 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl +++ b/src/cascadia/TerminalSettingsModel/TerminalWarnings.idl @@ -19,6 +19,7 @@ namespace Microsoft.Terminal.Settings.Model FailedToParseCommandJson = 9, FailedToWriteToSettings = 10, InvalidColorSchemeInCmd = 11, + InvalidSplitSize = 12, WARNINGS_SIZE // IMPORTANT: This MUST be the last value in this enum. It's an unused placeholder. }; diff --git a/tools/runut.cmd b/tools/runut.cmd index 9df937cf1..6f28b883f 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -24,5 +24,6 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ %_TestHostAppPath%\TerminalApp.LocalTests.dll ^ + %_TestHostAppPath%\SettingsModel.LocalTests.dll ^ %*