Add support for scroll bar 'always' setting (#14047)

This fixes #3454 by adding support for an "always" mode for the scroll bar.

This change uses a custom VisualStateManager to keep the scroll bar from collapsing if the profile is using the 'always' setting.

## Validation Steps Performed
Tried updating settings.json directly and using the UI and making sure the scroll bar behaves as expected.

Closes #3454
This commit is contained in:
Steve Otteson
2022-09-21 05:24:11 -07:00
committed by GitHub
parent 08096b2343
commit b3c9f01432
10 changed files with 131 additions and 10 deletions

View File

@@ -2350,7 +2350,8 @@
"description": "Defines the visibility of the scrollbar.",
"enum": [
"visible",
"hidden"
"hidden",
"always"
],
"type": "string"
},

View File

@@ -9,7 +9,8 @@ namespace Microsoft.Terminal.Control
enum ScrollbarState
{
Visible = 0,
Hidden
Hidden,
Always
};
enum TextAntialiasingMode

View File

@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "ScrollBarVisualStateManager.h"
#include "ScrollBarVisualStateManager.g.cpp"
namespace winrt::Microsoft::Terminal::Control::implementation
{
ScrollBarVisualStateManager::ScrollBarVisualStateManager() :
_termControl(nullptr)
{
}
bool ScrollBarVisualStateManager::GoToStateCore(
winrt::Windows::UI::Xaml::Controls::Control const& control,
winrt::Windows::UI::Xaml::FrameworkElement const& templateRoot,
winrt::hstring const& stateName,
winrt::Windows::UI::Xaml::VisualStateGroup const& group,
winrt::Windows::UI::Xaml::VisualState const& state,
bool useTransitions)
{
if (!_termControl)
{
for (auto parent = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(control);
parent != nullptr;
parent = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(parent))
{
if (parent.try_as(_termControl))
{
break;
}
}
}
WINRT_ASSERT(_termControl);
auto scrollState = _termControl.Settings().ScrollState();
if (scrollState == ScrollbarState::Always)
{
// If we're in Always mode, and the control is trying to collapse,
// go back to expanded
if (stateName == L"Collapsed" || stateName == L"CollapsedWithoutAnimation")
{
for (auto foundState : group.States())
{
if (foundState.Name() == L"ExpandedWithoutAnimation")
{
return base_type::GoToStateCore(control, templateRoot, foundState.Name(), group, foundState, false);
}
}
}
}
return base_type::GoToStateCore(control, templateRoot, stateName, group, state, useTransitions);
}
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Module Name:
// - ScrollBarVisualStateManager.h
//
// Abstract:
// - This is a custom VisualStateManager that keeps the scroll bar from
// going into the collapsed mode when the user wants the scroll bar
// to always be displayed.
//
#pragma once
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "ScrollBarVisualStateManager.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct ScrollBarVisualStateManager : ScrollBarVisualStateManagerT<ScrollBarVisualStateManager>
{
ScrollBarVisualStateManager();
bool GoToStateCore(winrt::Windows::UI::Xaml::Controls::Control const& control, winrt::Windows::UI::Xaml::FrameworkElement const& templateRoot, hstring const& stateName, winrt::Windows::UI::Xaml::VisualStateGroup const& group, winrt::Windows::UI::Xaml::VisualState const& state, bool useTransitions);
private:
winrt::Microsoft::Terminal::Control::TermControl _termControl;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
struct ScrollBarVisualStateManager : ScrollBarVisualStateManagerT<ScrollBarVisualStateManager, implementation::ScrollBarVisualStateManager>
{
};
}

View File

@@ -0,0 +1,8 @@
namespace Microsoft.Terminal.Control
{
[default_interface]
runtimeclass ScrollBarVisualStateManager : Windows.UI.Xaml.VisualStateManager
{
ScrollBarVisualStateManager();
}
}

View File

@@ -2117,7 +2117,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double width = cols * actualFontSize.X;
// Reserve additional space if scrollbar is intended to be visible
if (scrollState == ScrollbarState::Visible)
if (scrollState != ScrollbarState::Hidden)
{
width += scrollbarSize;
}
@@ -2161,7 +2161,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double width = fontSize.Width;
double height = fontSize.Height;
// Reserve additional space if scrollbar is intended to be visible
if (_core.Settings().ScrollState() == ScrollbarState::Visible)
if (_core.Settings().ScrollState() != ScrollbarState::Hidden)
{
width += ScrollBar().ActualWidth();
}
@@ -2204,7 +2204,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
padding.Left + padding.Right :
padding.Top + padding.Bottom);
if (widthOrHeight && _core.Settings().ScrollState() == ScrollbarState::Visible)
if (widthOrHeight && _core.Settings().ScrollState() != ScrollbarState::Hidden)
{
nonTerminalArea += gsl::narrow_cast<float>(ScrollBar().ActualWidth());
}

View File

@@ -408,6 +408,11 @@
</Rectangle>
</ControlTemplate>
</Grid.Resources>
<VisualStateManager.CustomVisualStateManager>
<local:ScrollBarVisualStateManager />
</VisualStateManager.CustomVisualStateManager>
<Grid x:Name="HorizontalRoot"
contract7Present:CornerRadius="{TemplateBinding CornerRadius}"
Background="{TemplateBinding Background}"

View File

@@ -30,7 +30,7 @@
<Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
<Import Project="$(OpenConsoleDir)src\common.nugetversions.props" />
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -40,6 +40,9 @@
<ClInclude Include="ControlInteractivity.h">
<DependentUpon>ControlInteractivity.idl</DependentUpon>
</ClInclude>
<ClInclude Include="ScrollBarVisualStateManager.h">
<DependentUpon>ScrollBarVisualStateManager.idl</DependentUpon>
</ClInclude>
<ClInclude Include="KeyChord.h">
<DependentUpon>KeyChord.idl</DependentUpon>
</ClInclude>
@@ -77,6 +80,9 @@
<ClCompile Include="ControlInteractivity.cpp">
<DependentUpon>ControlInteractivity.idl</DependentUpon>
</ClCompile>
<ClCompile Include="ScrollBarVisualStateManager.cpp">
<DependentUpon>ScrollBarVisualStateManager.idl</DependentUpon>
</ClCompile>
<ClCompile Include="EventArgs.cpp">
<DependentUpon>EventArgs.idl</DependentUpon>
</ClCompile>
@@ -109,6 +115,7 @@
<ItemGroup>
<Midl Include="ControlCore.idl" />
<Midl Include="ControlInteractivity.idl" />
<Midl Include="ScrollBarVisualStateManager.idl" />
<Midl Include="ICoreState.idl" />
<Midl Include="IDirectKeyListener.idl" />
<Midl Include="KeyChord.idl" />

View File

@@ -936,7 +936,11 @@
</data>
<data name="Profile_ScrollbarVisibilityVisible.Content" xml:space="preserve">
<value>Visible</value>
<comment>An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible.</comment>
<comment>An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is visible but may dynamically shrink.</comment>
</data>
<data name="Profile_ScrollbarVisibilityAlways.Content" xml:space="preserve">
<value>Always</value>
<comment>An option to choose from for the "scrollbar visibility" setting. When selected, the scrollbar is always visible.</comment>
</data>
<data name="Profile_SnapOnInput.Header" xml:space="preserve">
<value>Scroll to input when typing</value>
@@ -1505,4 +1509,4 @@
<value>Set as default</value>
<comment>Text label for a button that, when invoked, sets the selected color scheme as the default scheme to use.</comment>
</data>
</root>
</root>

View File

@@ -70,9 +70,10 @@ JSON_ENUM_MAPPER(::winrt::Windows::UI::Xaml::Media::Stretch)
JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Control::ScrollbarState)
{
static constexpr std::array<pair_type, 2> mappings = {
static constexpr std::array<pair_type, 3> mappings = {
pair_type{ "visible", ValueType::Visible },
pair_type{ "hidden", ValueType::Hidden }
pair_type{ "hidden", ValueType::Hidden },
pair_type{ "always", ValueType::Always }
};
};