mirror of
https://github.com/microsoft/terminal.git
synced 2026-05-19 21:41:15 +00:00
Compare commits
2 Commits
dev/migrie
...
user/lheck
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa81e700ff | ||
|
|
f30cbef34d |
@@ -8,7 +8,6 @@
|
||||
#include <LibraryResources.h>
|
||||
|
||||
#include "SuggestionsControl.g.cpp"
|
||||
#include "../../types/inc/utils.hpp"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace winrt::TerminalApp;
|
||||
@@ -20,8 +19,6 @@ using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
SuggestionsControl::SuggestionsControl()
|
||||
@@ -265,8 +262,8 @@ namespace winrt::TerminalApp::implementation
|
||||
// - <unused>
|
||||
// Return Value:
|
||||
// - <none>
|
||||
winrt::fire_and_forget SuggestionsControl::_selectedCommandChanged(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
|
||||
void SuggestionsControl::_selectedCommandChanged(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto selectedCommand = _filteredActionsView().SelectedItem();
|
||||
const auto filteredCommand{ selectedCommand.try_as<winrt::TerminalApp::FilteredCommand>() };
|
||||
@@ -284,106 +281,11 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
|
||||
{
|
||||
const auto& cmd = actionPaletteItem.Command();
|
||||
PreviewAction.raise(*this, cmd);
|
||||
|
||||
const auto description{ cmd.Description() };
|
||||
|
||||
if (SelectedItem())
|
||||
SelectedItem().SetValue(Automation::AutomationProperties::FullDescriptionProperty(), winrt::box_value(description));
|
||||
|
||||
if (!description.empty())
|
||||
{
|
||||
// If it's already open, then just re-target it and update the content immediately.
|
||||
if (_descriptionsView().Visibility() == Visibility::Visible)
|
||||
{
|
||||
_openTooltip(cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, wait a bit before opening it.
|
||||
co_await winrt::resume_after(200ms);
|
||||
co_await wil::resume_foreground(Dispatcher());
|
||||
_openTooltip(cmd);
|
||||
// DescriptionTip().IsOpen(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there's no description, then just close the tooltip.
|
||||
// DescriptionTip().IsOpen(false);
|
||||
_descriptionsView().Visibility(Visibility::Collapsed);
|
||||
_descriptionsBackdrop().Visibility(Visibility::Collapsed);
|
||||
}
|
||||
PreviewAction.raise(*this, actionPaletteItem.Command());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget SuggestionsControl::_openTooltip(Command cmd)
|
||||
{
|
||||
const auto description{ cmd.Description() };
|
||||
|
||||
if (!description.empty())
|
||||
{
|
||||
// DescriptionTip().Target(SelectedItem());
|
||||
// DescriptionTip().Title(cmd.Name());
|
||||
{
|
||||
// The Title
|
||||
_descriptionTitle().Inlines().Clear();
|
||||
Documents::Run titleRun;
|
||||
titleRun.Text(cmd.Name());
|
||||
_descriptionTitle().Inlines().Append(titleRun);
|
||||
}
|
||||
|
||||
// TODO! NOT REALLY TRUE ANYMORE
|
||||
// If you try to put a newline in the Subtitle, it'll _immediately
|
||||
// close the tooltip_. Instead, we'll need to build up the text as a
|
||||
// series of runs, and put them in the content.
|
||||
|
||||
_descriptionComment().Inlines().Clear();
|
||||
|
||||
// First, replace all "\r\n" with "\n"
|
||||
std::wstring filtered = description.c_str();
|
||||
|
||||
// replace all "\r\n" with "\n" in `filtered`
|
||||
std::wstring::size_type pos = 0;
|
||||
while ((pos = filtered.find(L"\r\n", pos)) != std::wstring::npos)
|
||||
{
|
||||
filtered.erase(pos, 1);
|
||||
}
|
||||
|
||||
// Split the filtered description on '\n`
|
||||
const auto lines = ::Microsoft::Console::Utils::SplitString(filtered.c_str(), L'\n');
|
||||
// For each line, build a Run + LineBreak, and add them to the text
|
||||
// block
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
if (line.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Documents::Run textRun;
|
||||
textRun.Text(winrt::hstring{ line });
|
||||
_descriptionComment().Inlines().Append(textRun);
|
||||
_descriptionComment().Inlines().Append(Documents::LineBreak{});
|
||||
}
|
||||
|
||||
// // TODO! These were all feigned attempts to allow us to focus the content of the teachingtip.
|
||||
|
||||
// // We may want to keep IsTextSelectionEnabled in the XAML.
|
||||
// //
|
||||
// // I also have no idea if the FullDescriptionProperty thing worked at all.
|
||||
// _toolTipContent().AllowFocusOnInteraction(true);
|
||||
// _toolTipContent().IsTextSelectionEnabled(true);
|
||||
// DescriptionTip().SetValue(Automation::AutomationProperties::FullDescriptionProperty(), winrt::box_value(description));
|
||||
|
||||
_descriptionsView().Visibility(Visibility::Visible);
|
||||
_descriptionsBackdrop().Visibility(Visibility::Visible);
|
||||
_recalculateTopMargin();
|
||||
}
|
||||
co_return;
|
||||
}
|
||||
|
||||
void SuggestionsControl::_previewKeyDownHandler(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e)
|
||||
{
|
||||
@@ -1125,43 +1027,13 @@ namespace winrt::TerminalApp::implementation
|
||||
if (_direction == TerminalApp::SuggestionsDirection::TopDown)
|
||||
{
|
||||
Controls::Grid::SetRow(_searchBox(), 0);
|
||||
Controls::Grid::SetRow(_descriptionsBackdrop(), 2);
|
||||
}
|
||||
else // BottomUp
|
||||
{
|
||||
Controls::Grid::SetRow(_searchBox(), 4);
|
||||
Controls::Grid::SetRow(_descriptionsBackdrop(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SuggestionsControl::_recalculateTopMargin()
|
||||
{
|
||||
auto currentMargin = Margin();
|
||||
|
||||
const til::size actualSize{ til::math::rounding, ActualWidth(), ActualHeight() };
|
||||
const til::size descriptionSize{ til::math::rounding, _descriptionsBackdrop().ActualWidth(), _descriptionsBackdrop().ActualHeight() };
|
||||
|
||||
// Now, position vertically.
|
||||
if (_direction == TerminalApp::SuggestionsDirection::TopDown)
|
||||
{
|
||||
// The control should open right below the cursor, with the list
|
||||
// extending below. This is easy, we can just use the cursor as the
|
||||
// origin (more or less)
|
||||
currentMargin.Top = (_anchor.Y /* - descriptionSize.height*/);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bottom Up.
|
||||
|
||||
// TODO! This is all wrong. It just jumps around randomly.
|
||||
|
||||
// Position at the cursor. The suggestions UI itself will maintain
|
||||
// its own offset such that it's always above its origin
|
||||
currentMargin.Top = (_anchor.Y - actualSize.height /*+ descriptionSize.height*/);
|
||||
}
|
||||
Margin(currentMargin);
|
||||
}
|
||||
|
||||
void SuggestionsControl::Open(TerminalApp::SuggestionsMode mode,
|
||||
const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& commands,
|
||||
winrt::hstring filter,
|
||||
@@ -1180,7 +1052,6 @@ namespace winrt::TerminalApp::implementation
|
||||
_space = space;
|
||||
|
||||
const til::size actualSize{ til::math::rounding, ActualWidth(), ActualHeight() };
|
||||
const til::size descriptionSize{ til::math::rounding, _descriptionsBackdrop().ActualWidth(), _descriptionsBackdrop().ActualHeight() };
|
||||
// Is there space in the window below the cursor to open the menu downwards?
|
||||
const bool canOpenDownwards = (_anchor.Y + characterHeight + actualSize.height) < space.Height;
|
||||
_setDirection(canOpenDownwards ? TerminalApp::SuggestionsDirection::TopDown :
|
||||
@@ -1207,13 +1078,13 @@ namespace winrt::TerminalApp::implementation
|
||||
// The control should open right below the cursor, with the list
|
||||
// extending below. This is easy, we can just use the cursor as the
|
||||
// origin (more or less)
|
||||
newMargin.Top = (_anchor.Y /* - descriptionSize.height*/);
|
||||
newMargin.Top = (_anchor.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position at the cursor. The suggestions UI itself will maintain
|
||||
// its own offset such that it's always above its origin
|
||||
newMargin.Top = (_anchor.Y - actualSize.height /* - descriptionSize.height*/);
|
||||
newMargin.Top = (_anchor.Y - actualSize.height);
|
||||
}
|
||||
Margin(newMargin);
|
||||
|
||||
|
||||
@@ -99,8 +99,6 @@ namespace winrt::TerminalApp::implementation
|
||||
void _close();
|
||||
void _dismissPalette();
|
||||
|
||||
void _recalculateTopMargin();
|
||||
|
||||
void _filterTextChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
void _keyUpHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
@@ -112,8 +110,7 @@ namespace winrt::TerminalApp::implementation
|
||||
|
||||
void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
|
||||
void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e);
|
||||
winrt::fire_and_forget _selectedCommandChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
winrt::fire_and_forget _openTooltip(Microsoft::Terminal::Settings::Model::Command cmd);
|
||||
void _selectedCommandChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
|
||||
void _updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand);
|
||||
@@ -125,6 +122,7 @@ namespace winrt::TerminalApp::implementation
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter();
|
||||
std::wstring _getTrimmedInput();
|
||||
uint32_t _getNumVisibleItems();
|
||||
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -112,17 +112,15 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" />
|
||||
<RowDefinition Height="7*" />
|
||||
<RowDefinition Height="8*" />
|
||||
<RowDefinition Height="2*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid x:Name="_backdrop"
|
||||
Grid.Row="1"
|
||||
Grid.RowSpan="1"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
MinWidth="300"
|
||||
MaxWidth="300"
|
||||
MaxHeight="300"
|
||||
Margin="0"
|
||||
@@ -138,16 +136,16 @@
|
||||
Translation="0,0,32">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<!-- 0: Top-down _searchBox -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 1: Top-down ParentCommandName -->
|
||||
<!-- Top-down _searchBox -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 2: Top-down UNUSED???????? -->
|
||||
<!-- Top-down ParentCommandName -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 3: _filteredActionsView -->
|
||||
<!-- Top-down UNUSED???????? -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- 4: bottom-up _searchBox -->
|
||||
<!-- _filteredActionsView -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- bottom-up _searchBox -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBox x:Name="_searchBox"
|
||||
@@ -209,55 +207,8 @@
|
||||
SelectionChanged="_listItemSelectionChanged"
|
||||
SelectionMode="Single" />
|
||||
|
||||
|
||||
</Grid>
|
||||
<!-- <Border Height="1" Grid.Row="1" /> -->
|
||||
<Grid x:Name="_descriptionsBackdrop"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
MaxWidth="300"
|
||||
Margin="0,6,0,0"
|
||||
Padding="0,4,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource FlyoutPresenterBackground}"
|
||||
BorderBrush="{ThemeResource FlyoutBorderThemeBrush}"
|
||||
BorderThickness="{ThemeResource FlyoutBorderThemeThickness}"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}"
|
||||
PointerPressed="_backdropPointerPressed"
|
||||
Shadow="{StaticResource SharedShadow}"
|
||||
Translation="0,0,32">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<!-- 0: descriptions view -->
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel x:Name="_descriptionsView"
|
||||
Grid.Row="0"
|
||||
Margin="8,0,8,8"
|
||||
Orientation="Vertical"
|
||||
Visibility="Collapsed">
|
||||
<TextBlock x:Name="_descriptionTitle"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<ScrollViewer MaxHeight="64"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
VerticalScrollMode="Enabled"
|
||||
Visibility="Visible">
|
||||
<!-- <TextBlock Text="Foo bar bax"
|
||||
TextWrapping="WrapWholeWords" />-->
|
||||
<TextBlock x:Name="_descriptionComment"
|
||||
IsTextSelectionEnabled="True"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -4,108 +4,14 @@
|
||||
#include "pch.h"
|
||||
#include "Actions.h"
|
||||
#include "Actions.g.cpp"
|
||||
#include "KeyBindingViewModel.g.cpp"
|
||||
#include "ActionsPageNavigationState.g.cpp"
|
||||
#include "LibraryResources.h"
|
||||
#include "../TerminalSettingsModel/AllShortcutActions.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Data;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
KeyBindingViewModel::KeyBindingViewModel(const Windows::Foundation::Collections::IObservableVector<hstring>& availableActions) :
|
||||
KeyBindingViewModel(nullptr, availableActions.First().Current(), availableActions) {}
|
||||
|
||||
KeyBindingViewModel::KeyBindingViewModel(const Control::KeyChord& keys, const hstring& actionName, const IObservableVector<hstring>& availableActions) :
|
||||
_CurrentKeys{ keys },
|
||||
_KeyChordText{ KeyChordSerialization::ToString(keys) },
|
||||
_CurrentAction{ actionName },
|
||||
_ProposedAction{ box_value(actionName) },
|
||||
_AvailableActions{ availableActions }
|
||||
{
|
||||
// Add a property changed handler to our own property changed event.
|
||||
// This propagates changes from the settings model to anybody listening to our
|
||||
// unique view model members.
|
||||
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto viewModelProperty{ args.PropertyName() };
|
||||
if (viewModelProperty == L"CurrentKeys")
|
||||
{
|
||||
_KeyChordText = KeyChordSerialization::ToString(_CurrentKeys);
|
||||
_NotifyChanges(L"KeyChordText");
|
||||
}
|
||||
else if (viewModelProperty == L"IsContainerFocused" ||
|
||||
viewModelProperty == L"IsEditButtonFocused" ||
|
||||
viewModelProperty == L"IsHovered" ||
|
||||
viewModelProperty == L"IsAutomationPeerAttached" ||
|
||||
viewModelProperty == L"IsInEditMode")
|
||||
{
|
||||
_NotifyChanges(L"ShowEditButton");
|
||||
}
|
||||
else if (viewModelProperty == L"CurrentAction")
|
||||
{
|
||||
_NotifyChanges(L"Name");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hstring KeyBindingViewModel::EditButtonName() const noexcept { return RS_(L"Actions_EditButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
hstring KeyBindingViewModel::CancelButtonName() const noexcept { return RS_(L"Actions_CancelButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
hstring KeyBindingViewModel::AcceptButtonName() const noexcept { return RS_(L"Actions_AcceptButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
hstring KeyBindingViewModel::DeleteButtonName() const noexcept { return RS_(L"Actions_DeleteButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
|
||||
bool KeyBindingViewModel::ShowEditButton() const noexcept
|
||||
{
|
||||
return (IsContainerFocused() || IsEditButtonFocused() || IsHovered() || IsAutomationPeerAttached()) && !IsInEditMode();
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::ToggleEditMode()
|
||||
{
|
||||
// toggle edit mode
|
||||
IsInEditMode(!_IsInEditMode);
|
||||
if (_IsInEditMode)
|
||||
{
|
||||
// if we're in edit mode,
|
||||
// - pre-populate the text box with the current keys
|
||||
// - reset the combo box with the current action
|
||||
ProposedKeys(_CurrentKeys);
|
||||
ProposedAction(box_value(_CurrentAction));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::AttemptAcceptChanges()
|
||||
{
|
||||
AttemptAcceptChanges(_ProposedKeys);
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::AttemptAcceptChanges(const Control::KeyChord newKeys)
|
||||
{
|
||||
const auto args{ make_self<ModifyKeyBindingEventArgs>(_CurrentKeys, // OldKeys
|
||||
newKeys, // NewKeys
|
||||
_IsNewlyAdded ? hstring{} : _CurrentAction, // OldAction
|
||||
unbox_value<hstring>(_ProposedAction)) }; // NewAction
|
||||
_ModifyKeyBindingRequestedHandlers(*this, *args);
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::CancelChanges()
|
||||
{
|
||||
if (_IsNewlyAdded)
|
||||
{
|
||||
_DeleteNewlyAddedKeyBindingHandlers(*this, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
Actions::Actions()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -115,277 +21,55 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
Automation::Peers::AutomationPeer Actions::OnCreateAutomationPeer()
|
||||
{
|
||||
_AutomationPeerAttached = true;
|
||||
for (const auto& kbdVM : _KeyBindingList)
|
||||
{
|
||||
// To create a more accessible experience, we want the "edit" buttons to _always_
|
||||
// appear when a screen reader is attached. This ensures that the edit buttons are
|
||||
// accessible via the UIA tree.
|
||||
get_self<KeyBindingViewModel>(kbdVM)->IsAutomationPeerAttached(_AutomationPeerAttached);
|
||||
}
|
||||
_ViewModel.OnAutomationPeerAttached();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Actions::OnNavigatedTo(const NavigationEventArgs& e)
|
||||
{
|
||||
_State = e.Parameter().as<Editor::ActionsPageNavigationState>();
|
||||
_ViewModel = e.Parameter().as<Editor::ActionsViewModel>();
|
||||
|
||||
// Populate AvailableActionAndArgs
|
||||
_AvailableActionMap = single_threaded_map<hstring, Model::ActionAndArgs>();
|
||||
std::vector<hstring> availableActionAndArgs;
|
||||
for (const auto& [name, actionAndArgs] : _State.Settings().ActionMap().AvailableActions())
|
||||
{
|
||||
availableActionAndArgs.push_back(name);
|
||||
_AvailableActionMap.Insert(name, actionAndArgs);
|
||||
}
|
||||
std::sort(begin(availableActionAndArgs), end(availableActionAndArgs));
|
||||
_AvailableActionAndArgs = single_threaded_observable_vector(std::move(availableActionAndArgs));
|
||||
// Subscribe to the view model's FocusContainer event.
|
||||
// Use the KeyBindingViewModel or index provided in the event to focus the corresponding container
|
||||
_ViewModel.FocusContainer([this](const auto& /*sender*/, const auto& args) {
|
||||
if (auto kbdVM{ args.try_as<KeyBindingViewModel>() })
|
||||
{
|
||||
if (const auto& container = KeyBindingsListView().ContainerFromItem(*kbdVM))
|
||||
{
|
||||
container.as<Controls::ListViewItem>().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
else if (const auto& index = args.try_as<uint32_t>())
|
||||
{
|
||||
if (const auto& container = KeyBindingsListView().ContainerFromIndex(*index))
|
||||
{
|
||||
container.as<Controls::ListViewItem>().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Convert the key bindings from our settings into a view model representation
|
||||
const auto& keyBindingMap{ _State.Settings().ActionMap().KeyBindings() };
|
||||
std::vector<Editor::KeyBindingViewModel> keyBindingList;
|
||||
keyBindingList.reserve(keyBindingMap.Size());
|
||||
for (const auto& [keys, cmd] : keyBindingMap)
|
||||
{
|
||||
// convert the cmd into a KeyBindingViewModel
|
||||
auto container{ make_self<KeyBindingViewModel>(keys, cmd.Name(), _AvailableActionAndArgs) };
|
||||
_RegisterEvents(container);
|
||||
keyBindingList.push_back(*container);
|
||||
}
|
||||
|
||||
std::sort(begin(keyBindingList), end(keyBindingList), KeyBindingViewModelComparator{});
|
||||
_KeyBindingList = single_threaded_observable_vector(std::move(keyBindingList));
|
||||
// Subscribe to the view model's UpdateBackground event.
|
||||
// The view model does not have access to the page resources, so it asks us
|
||||
// to update the key binding's container background
|
||||
_ViewModel.UpdateBackground([this](const auto& /*sender*/, const auto& args) {
|
||||
if (auto kbdVM{ args.try_as<KeyBindingViewModel>() })
|
||||
{
|
||||
if (kbdVM->IsInEditMode())
|
||||
{
|
||||
const auto& containerBackground{ Resources().Lookup(box_value(L"ActionContainerBackgroundEditing")).as<Windows::UI::Xaml::Media::Brush>() };
|
||||
kbdVM->ContainerBackground(containerBackground);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto& containerBackground{ Resources().Lookup(box_value(L"ActionContainerBackground")).as<Windows::UI::Xaml::Media::Brush>() };
|
||||
kbdVM->ContainerBackground(containerBackground);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Actions::AddNew_Click(const IInspectable& /*sender*/, const RoutedEventArgs& /*eventArgs*/)
|
||||
{
|
||||
// Create the new key binding and register all of the event handlers.
|
||||
auto kbdVM{ make_self<KeyBindingViewModel>(_AvailableActionAndArgs) };
|
||||
_RegisterEvents(kbdVM);
|
||||
kbdVM->DeleteNewlyAddedKeyBinding({ this, &Actions::_ViewModelDeleteNewlyAddedKeyBindingHandler });
|
||||
|
||||
// Manually add the editing background. This needs to be done in Actions not the view model.
|
||||
// We also have to do this manually because it hasn't been added to the list yet.
|
||||
kbdVM->IsInEditMode(true);
|
||||
const auto& containerBackground{ Resources().Lookup(box_value(L"ActionContainerBackgroundEditing")).as<Windows::UI::Xaml::Media::Brush>() };
|
||||
kbdVM->ContainerBackground(containerBackground);
|
||||
|
||||
// IMPORTANT: do this _after_ setting IsInEditMode. Otherwise, it'll get deleted immediately
|
||||
// by the PropertyChangedHandler below (where we delete any IsNewlyAdded items)
|
||||
kbdVM->IsNewlyAdded(true);
|
||||
_KeyBindingList.InsertAt(0, *kbdVM);
|
||||
}
|
||||
|
||||
void Actions::_ViewModelPropertyChangedHandler(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args)
|
||||
{
|
||||
const auto senderVM{ sender.as<Editor::KeyBindingViewModel>() };
|
||||
const auto propertyName{ args.PropertyName() };
|
||||
if (propertyName == L"IsInEditMode")
|
||||
{
|
||||
if (senderVM.IsInEditMode())
|
||||
{
|
||||
// Ensure that...
|
||||
// 1. we move focus to the edit mode controls
|
||||
// 2. any actions that were newly added are removed
|
||||
// 3. this is the only entry that is in edit mode
|
||||
for (int32_t i = _KeyBindingList.Size() - 1; i >= 0; --i)
|
||||
{
|
||||
const auto& kbdVM{ _KeyBindingList.GetAt(i) };
|
||||
if (senderVM == kbdVM)
|
||||
{
|
||||
// This is the view model entry that went into edit mode.
|
||||
// Move focus to the edit mode controls by
|
||||
// extracting the list view item container.
|
||||
const auto& container{ KeyBindingsListView().ContainerFromIndex(i).try_as<ListViewItem>() };
|
||||
container.Focus(FocusState::Programmatic);
|
||||
}
|
||||
else if (kbdVM.IsNewlyAdded())
|
||||
{
|
||||
// Remove any actions that were newly added
|
||||
_KeyBindingList.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exit edit mode for all other containers
|
||||
get_self<KeyBindingViewModel>(kbdVM)->DisableEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
const auto& containerBackground{ Resources().Lookup(box_value(L"ActionContainerBackgroundEditing")).as<Windows::UI::Xaml::Media::Brush>() };
|
||||
get_self<KeyBindingViewModel>(senderVM)->ContainerBackground(containerBackground);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Focus on the list view item
|
||||
KeyBindingsListView().ContainerFromItem(senderVM).as<Controls::Control>().Focus(FocusState::Programmatic);
|
||||
|
||||
const auto& containerBackground{ Resources().Lookup(box_value(L"ActionContainerBackground")).as<Windows::UI::Xaml::Media::Brush>() };
|
||||
get_self<KeyBindingViewModel>(senderVM)->ContainerBackground(containerBackground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actions::_ViewModelDeleteKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Control::KeyChord& keys)
|
||||
{
|
||||
// Update the settings model
|
||||
_State.Settings().ActionMap().DeleteKeyBinding(keys);
|
||||
|
||||
// Find the current container in our list and remove it.
|
||||
// This is much faster than rebuilding the entire ActionMap.
|
||||
uint32_t index;
|
||||
if (_KeyBindingList.IndexOf(senderVM, index))
|
||||
{
|
||||
_KeyBindingList.RemoveAt(index);
|
||||
|
||||
// Focus the new item at this index
|
||||
if (_KeyBindingList.Size() != 0)
|
||||
{
|
||||
const auto newFocusedIndex{ std::clamp(index, 0u, _KeyBindingList.Size() - 1) };
|
||||
KeyBindingsListView().ContainerFromIndex(newFocusedIndex).as<Controls::Control>().Focus(FocusState::Programmatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Actions::_ViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args)
|
||||
{
|
||||
const auto isNewAction{ !args.OldKeys() && args.OldActionName().empty() };
|
||||
|
||||
auto applyChangesToSettingsModel = [=]() {
|
||||
// If the key chord was changed,
|
||||
// update the settings model and view model appropriately
|
||||
// NOTE: we still need to update the view model if we're working with a newly added action
|
||||
if (isNewAction || args.OldKeys().Modifiers() != args.NewKeys().Modifiers() || args.OldKeys().Vkey() != args.NewKeys().Vkey())
|
||||
{
|
||||
if (!isNewAction)
|
||||
{
|
||||
// update settings model
|
||||
_State.Settings().ActionMap().RebindKeys(args.OldKeys(), args.NewKeys());
|
||||
}
|
||||
|
||||
// update view model
|
||||
auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
|
||||
senderVMImpl->CurrentKeys(args.NewKeys());
|
||||
}
|
||||
|
||||
// If the action was changed,
|
||||
// update the settings model and view model appropriately
|
||||
// NOTE: no need to check for "isNewAction" here. <empty_string> != <action name> already.
|
||||
if (args.OldActionName() != args.NewActionName())
|
||||
{
|
||||
// convert the action's name into a view model.
|
||||
const auto& newAction{ _AvailableActionMap.Lookup(args.NewActionName()) };
|
||||
|
||||
// update settings model
|
||||
_State.Settings().ActionMap().RegisterKeyBinding(args.NewKeys(), newAction);
|
||||
|
||||
// update view model
|
||||
auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
|
||||
senderVMImpl->CurrentAction(args.NewActionName());
|
||||
senderVMImpl->IsNewlyAdded(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Check for this special case:
|
||||
// we're changing the key chord,
|
||||
// but the new key chord is already in use
|
||||
if (isNewAction || args.OldKeys().Modifiers() != args.NewKeys().Modifiers() || args.OldKeys().Vkey() != args.NewKeys().Vkey())
|
||||
{
|
||||
const auto& conflictingCmd{ _State.Settings().ActionMap().GetActionByKeyChord(args.NewKeys()) };
|
||||
if (conflictingCmd)
|
||||
{
|
||||
// We're about to overwrite another key chord.
|
||||
// Display a confirmation dialog.
|
||||
TextBlock errorMessageTB{};
|
||||
errorMessageTB.Text(RS_(L"Actions_RenameConflictConfirmationMessage"));
|
||||
|
||||
const auto conflictingCmdName{ conflictingCmd.Name() };
|
||||
TextBlock conflictingCommandNameTB{};
|
||||
conflictingCommandNameTB.Text(fmt::format(L"\"{}\"", conflictingCmdName.empty() ? RS_(L"Actions_UnnamedCommandName") : conflictingCmdName));
|
||||
conflictingCommandNameTB.FontStyle(Windows::UI::Text::FontStyle::Italic);
|
||||
|
||||
TextBlock confirmationQuestionTB{};
|
||||
confirmationQuestionTB.Text(RS_(L"Actions_RenameConflictConfirmationQuestion"));
|
||||
|
||||
Button acceptBTN{};
|
||||
acceptBTN.Content(box_value(RS_(L"Actions_RenameConflictConfirmationAcceptButton")));
|
||||
acceptBTN.Click([=](auto&, auto&) {
|
||||
// remove conflicting key binding from list view
|
||||
const auto containerIndex{ _GetContainerIndexByKeyChord(args.NewKeys()) };
|
||||
_KeyBindingList.RemoveAt(*containerIndex);
|
||||
|
||||
// remove flyout
|
||||
senderVM.AcceptChangesFlyout().Hide();
|
||||
senderVM.AcceptChangesFlyout(nullptr);
|
||||
|
||||
// update settings model and view model
|
||||
applyChangesToSettingsModel();
|
||||
senderVM.ToggleEditMode();
|
||||
});
|
||||
|
||||
StackPanel flyoutStack{};
|
||||
flyoutStack.Children().Append(errorMessageTB);
|
||||
flyoutStack.Children().Append(conflictingCommandNameTB);
|
||||
flyoutStack.Children().Append(confirmationQuestionTB);
|
||||
flyoutStack.Children().Append(acceptBTN);
|
||||
|
||||
Flyout acceptChangesFlyout{};
|
||||
acceptChangesFlyout.Content(flyoutStack);
|
||||
senderVM.AcceptChangesFlyout(acceptChangesFlyout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// update settings model and view model
|
||||
applyChangesToSettingsModel();
|
||||
|
||||
// We NEED to toggle the edit mode here,
|
||||
// so that if nothing changed, we still exit
|
||||
// edit mode.
|
||||
senderVM.ToggleEditMode();
|
||||
}
|
||||
|
||||
void Actions::_ViewModelDeleteNewlyAddedKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const IInspectable& /*args*/)
|
||||
{
|
||||
for (uint32_t i = 0; i < _KeyBindingList.Size(); ++i)
|
||||
{
|
||||
const auto& kbdVM{ _KeyBindingList.GetAt(i) };
|
||||
if (kbdVM == senderVM)
|
||||
{
|
||||
_KeyBindingList.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - performs a search on KeyBindingList by key chord.
|
||||
// Arguments:
|
||||
// - keys - the associated key chord of the command we're looking for
|
||||
// Return Value:
|
||||
// - the index of the view model referencing the command. If the command doesn't exist, nullopt
|
||||
std::optional<uint32_t> Actions::_GetContainerIndexByKeyChord(const Control::KeyChord& keys)
|
||||
{
|
||||
for (uint32_t i = 0; i < _KeyBindingList.Size(); ++i)
|
||||
{
|
||||
const auto kbdVM{ get_self<KeyBindingViewModel>(_KeyBindingList.GetAt(i)) };
|
||||
const auto& otherKeys{ kbdVM->CurrentKeys() };
|
||||
if (otherKeys && keys.Modifiers() == otherKeys.Modifiers() && keys.Vkey() == otherKeys.Vkey())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO GH #6900:
|
||||
// an expedited search can be done if we use cmd.Name()
|
||||
// to quickly search through the sorted list.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Actions::_RegisterEvents(com_ptr<KeyBindingViewModel>& kbdVM)
|
||||
{
|
||||
kbdVM->PropertyChanged({ this, &Actions::_ViewModelPropertyChangedHandler });
|
||||
kbdVM->DeleteKeyBindingRequested({ this, &Actions::_ViewModelDeleteKeyBindingHandler });
|
||||
kbdVM->ModifyKeyBindingRequested({ this, &Actions::_ViewModelModifyKeyBindingHandler });
|
||||
kbdVM->IsAutomationPeerAttached(_AutomationPeerAttached);
|
||||
_ViewModel.AddNewKeybinding();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,109 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Actions.g.h"
|
||||
#include "KeyBindingViewModel.g.h"
|
||||
#include "ActionsPageNavigationState.g.h"
|
||||
#include "ModifyKeyBindingEventArgs.g.h"
|
||||
#include "ActionsViewModel.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct KeyBindingViewModelComparator
|
||||
{
|
||||
bool operator()(const Editor::KeyBindingViewModel& lhs, const Editor::KeyBindingViewModel& rhs) const
|
||||
{
|
||||
return lhs.Name() < rhs.Name();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModifyKeyBindingEventArgs : ModifyKeyBindingEventArgsT<ModifyKeyBindingEventArgs>
|
||||
{
|
||||
public:
|
||||
ModifyKeyBindingEventArgs(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys, const hstring oldActionName, const hstring newActionName) :
|
||||
_OldKeys{ oldKeys },
|
||||
_NewKeys{ newKeys },
|
||||
_OldActionName{ std::move(oldActionName) },
|
||||
_NewActionName{ std::move(newActionName) } {}
|
||||
|
||||
WINRT_PROPERTY(Control::KeyChord, OldKeys, nullptr);
|
||||
WINRT_PROPERTY(Control::KeyChord, NewKeys, nullptr);
|
||||
WINRT_PROPERTY(hstring, OldActionName);
|
||||
WINRT_PROPERTY(hstring, NewActionName);
|
||||
};
|
||||
|
||||
struct KeyBindingViewModel : KeyBindingViewModelT<KeyBindingViewModel>, ViewModelHelper<KeyBindingViewModel>
|
||||
{
|
||||
public:
|
||||
KeyBindingViewModel(const Windows::Foundation::Collections::IObservableVector<hstring>& availableActions);
|
||||
KeyBindingViewModel(const Control::KeyChord& keys, const hstring& name, const Windows::Foundation::Collections::IObservableVector<hstring>& availableActions);
|
||||
|
||||
hstring Name() const { return _CurrentAction; }
|
||||
hstring KeyChordText() const { return _KeyChordText; }
|
||||
|
||||
// UIA Text
|
||||
hstring EditButtonName() const noexcept;
|
||||
hstring CancelButtonName() const noexcept;
|
||||
hstring AcceptButtonName() const noexcept;
|
||||
hstring DeleteButtonName() const noexcept;
|
||||
|
||||
void EnterHoverMode() { IsHovered(true); };
|
||||
void ExitHoverMode() { IsHovered(false); };
|
||||
void ActionGotFocus() { IsContainerFocused(true); };
|
||||
void ActionLostFocus() { IsContainerFocused(false); };
|
||||
void EditButtonGettingFocus() { IsEditButtonFocused(true); };
|
||||
void EditButtonLosingFocus() { IsEditButtonFocused(false); };
|
||||
bool ShowEditButton() const noexcept;
|
||||
void ToggleEditMode();
|
||||
void DisableEditMode() { IsInEditMode(false); }
|
||||
void AttemptAcceptChanges();
|
||||
void AttemptAcceptChanges(const Control::KeyChord newKeys);
|
||||
void CancelChanges();
|
||||
void DeleteKeyBinding() { _DeleteKeyBindingRequestedHandlers(*this, _CurrentKeys); }
|
||||
|
||||
// ProposedAction: the entry selected by the combo box; may disagree with the settings model.
|
||||
// CurrentAction: the combo box item that maps to the settings model value.
|
||||
// AvailableActions: the list of options in the combo box; both actions above must be in this list.
|
||||
// NOTE: ProposedAction and CurrentAction may disagree mainly due to the "edit mode" system in place.
|
||||
// Current Action serves as...
|
||||
// 1 - a record of what to set ProposedAction to on a cancellation
|
||||
// 2 - a form of translation between ProposedAction and the settings model
|
||||
// We would also need an ActionMap reference to remove this, but this is a better separation
|
||||
// of responsibilities.
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(IInspectable, ProposedAction);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentAction);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<hstring>, AvailableActions, nullptr);
|
||||
|
||||
// ProposedKeys: the keys proposed by the control; may disagree with the settings model.
|
||||
// CurrentKeys: the key chord bound in the settings model.
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Control::KeyChord, ProposedKeys);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Control::KeyChord, CurrentKeys, nullptr);
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsInEditMode, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsNewlyAdded, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Controls::Flyout, AcceptChangesFlyout, nullptr);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsAutomationPeerAttached, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsHovered, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsContainerFocused, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsEditButtonFocused, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Media::Brush, ContainerBackground, nullptr);
|
||||
TYPED_EVENT(ModifyKeyBindingRequested, Editor::KeyBindingViewModel, Editor::ModifyKeyBindingEventArgs);
|
||||
TYPED_EVENT(DeleteKeyBindingRequested, Editor::KeyBindingViewModel, Terminal::Control::KeyChord);
|
||||
TYPED_EVENT(DeleteNewlyAddedKeyBinding, Editor::KeyBindingViewModel, IInspectable);
|
||||
|
||||
private:
|
||||
hstring _KeyChordText{};
|
||||
};
|
||||
|
||||
struct ActionsPageNavigationState : ActionsPageNavigationStateT<ActionsPageNavigationState>
|
||||
{
|
||||
public:
|
||||
ActionsPageNavigationState(const Model::CascadiaSettings& settings) :
|
||||
_Settings{ settings } {}
|
||||
|
||||
WINRT_PROPERTY(Model::CascadiaSettings, Settings, nullptr)
|
||||
};
|
||||
|
||||
struct Actions : public HasScrollViewer<Actions>, ActionsT<Actions>
|
||||
{
|
||||
public:
|
||||
@@ -114,24 +17,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
|
||||
void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
|
||||
Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer();
|
||||
|
||||
void AddNew_Click(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_PROPERTY(Editor::ActionsPageNavigationState, State, nullptr);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::KeyBindingViewModel>, KeyBindingList);
|
||||
|
||||
private:
|
||||
void _ViewModelPropertyChangedHandler(const Windows::Foundation::IInspectable& senderVM, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
|
||||
void _ViewModelDeleteKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Control::KeyChord& args);
|
||||
void _ViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args);
|
||||
void _ViewModelDeleteNewlyAddedKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const IInspectable& args);
|
||||
|
||||
std::optional<uint32_t> _GetContainerIndexByKeyChord(const Control::KeyChord& keys);
|
||||
void _RegisterEvents(com_ptr<implementation::KeyBindingViewModel>& kbdVM);
|
||||
|
||||
bool _AutomationPeerAttached{ false };
|
||||
Windows::Foundation::Collections::IObservableVector<hstring> _AvailableActionAndArgs;
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionMap;
|
||||
WINRT_OBSERVABLE_PROPERTY(Editor::ActionsViewModel, ViewModel, _PropertyChangedHandlers, nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,63 +1,13 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "EnumEntry.idl";
|
||||
import "ActionsViewModel.idl";
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass ModifyKeyBindingEventArgs
|
||||
{
|
||||
Microsoft.Terminal.Control.KeyChord OldKeys { get; };
|
||||
Microsoft.Terminal.Control.KeyChord NewKeys { get; };
|
||||
String OldActionName { get; };
|
||||
String NewActionName { get; };
|
||||
}
|
||||
|
||||
runtimeclass KeyBindingViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
// Settings Model side
|
||||
String Name { get; };
|
||||
String KeyChordText { get; };
|
||||
|
||||
// UI side
|
||||
Boolean ShowEditButton { get; };
|
||||
Boolean IsInEditMode { get; };
|
||||
Boolean IsNewlyAdded { get; };
|
||||
Microsoft.Terminal.Control.KeyChord ProposedKeys;
|
||||
Object ProposedAction;
|
||||
Windows.UI.Xaml.Controls.Flyout AcceptChangesFlyout;
|
||||
String EditButtonName { get; };
|
||||
String CancelButtonName { get; };
|
||||
String AcceptButtonName { get; };
|
||||
String DeleteButtonName { get; };
|
||||
Windows.UI.Xaml.Media.Brush ContainerBackground { get; };
|
||||
|
||||
void EnterHoverMode();
|
||||
void ExitHoverMode();
|
||||
void ActionGotFocus();
|
||||
void ActionLostFocus();
|
||||
void EditButtonGettingFocus();
|
||||
void EditButtonLosingFocus();
|
||||
IObservableVector<String> AvailableActions { get; };
|
||||
void ToggleEditMode();
|
||||
void AttemptAcceptChanges();
|
||||
void CancelChanges();
|
||||
void DeleteKeyBinding();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, ModifyKeyBindingEventArgs> ModifyKeyBindingRequested;
|
||||
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, Microsoft.Terminal.Control.KeyChord> DeleteKeyBindingRequested;
|
||||
}
|
||||
|
||||
runtimeclass ActionsPageNavigationState
|
||||
{
|
||||
Microsoft.Terminal.Settings.Model.CascadiaSettings Settings;
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass Actions : Windows.UI.Xaml.Controls.Page
|
||||
{
|
||||
Actions();
|
||||
ActionsPageNavigationState State { get; };
|
||||
|
||||
IObservableVector<KeyBindingViewModel> KeyBindingList { get; };
|
||||
ActionsViewModel ViewModel { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
<!-- Keybindings -->
|
||||
<ListView x:Name="KeyBindingsListView"
|
||||
ItemTemplate="{StaticResource KeyBindingTemplate}"
|
||||
ItemsSource="{x:Bind KeyBindingList, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind ViewModel.KeyBindingList, Mode=OneWay}"
|
||||
SelectionMode="None" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
380
src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp
Normal file
380
src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ActionsViewModel.h"
|
||||
#include "ActionsViewModel.g.cpp"
|
||||
#include "KeyBindingViewModel.g.cpp"
|
||||
#include "LibraryResources.h"
|
||||
#include "../TerminalSettingsModel/AllShortcutActions.h"
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::System;
|
||||
using namespace winrt::Windows::UI::Core;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::UI::Xaml::Data;
|
||||
using namespace winrt::Windows::UI::Xaml::Navigation;
|
||||
using namespace winrt::Microsoft::Terminal::Settings::Model;
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
KeyBindingViewModel::KeyBindingViewModel(const IObservableVector<hstring>& availableActions) :
|
||||
KeyBindingViewModel(nullptr, availableActions.First().Current(), availableActions) {}
|
||||
|
||||
KeyBindingViewModel::KeyBindingViewModel(const Control::KeyChord& keys, const hstring& actionName, const IObservableVector<hstring>& availableActions) :
|
||||
_CurrentKeys{ keys },
|
||||
_KeyChordText{ KeyChordSerialization::ToString(keys) },
|
||||
_CurrentAction{ actionName },
|
||||
_ProposedAction{ box_value(actionName) },
|
||||
_AvailableActions{ availableActions }
|
||||
{
|
||||
// Add a property changed handler to our own property changed event.
|
||||
// This propagates changes from the settings model to anybody listening to our
|
||||
// unique view model members.
|
||||
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
|
||||
const auto viewModelProperty{ args.PropertyName() };
|
||||
if (viewModelProperty == L"CurrentKeys")
|
||||
{
|
||||
_KeyChordText = KeyChordSerialization::ToString(_CurrentKeys);
|
||||
_NotifyChanges(L"KeyChordText");
|
||||
}
|
||||
else if (viewModelProperty == L"IsContainerFocused" ||
|
||||
viewModelProperty == L"IsEditButtonFocused" ||
|
||||
viewModelProperty == L"IsHovered" ||
|
||||
viewModelProperty == L"IsAutomationPeerAttached" ||
|
||||
viewModelProperty == L"IsInEditMode")
|
||||
{
|
||||
_NotifyChanges(L"ShowEditButton");
|
||||
}
|
||||
else if (viewModelProperty == L"CurrentAction")
|
||||
{
|
||||
_NotifyChanges(L"Name");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hstring KeyBindingViewModel::EditButtonName() const noexcept { return RS_(L"Actions_EditButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
hstring KeyBindingViewModel::CancelButtonName() const noexcept { return RS_(L"Actions_CancelButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
hstring KeyBindingViewModel::AcceptButtonName() const noexcept { return RS_(L"Actions_AcceptButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
hstring KeyBindingViewModel::DeleteButtonName() const noexcept { return RS_(L"Actions_DeleteButton/[using:Windows.UI.Xaml.Controls]ToolTipService/ToolTip"); }
|
||||
|
||||
bool KeyBindingViewModel::ShowEditButton() const noexcept
|
||||
{
|
||||
return (IsContainerFocused() || IsEditButtonFocused() || IsHovered() || IsAutomationPeerAttached()) && !IsInEditMode();
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::ToggleEditMode()
|
||||
{
|
||||
// toggle edit mode
|
||||
IsInEditMode(!_IsInEditMode);
|
||||
if (_IsInEditMode)
|
||||
{
|
||||
// if we're in edit mode,
|
||||
// - pre-populate the text box with the current keys
|
||||
// - reset the combo box with the current action
|
||||
ProposedKeys(_CurrentKeys);
|
||||
ProposedAction(box_value(_CurrentAction));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::AttemptAcceptChanges()
|
||||
{
|
||||
AttemptAcceptChanges(_ProposedKeys);
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::AttemptAcceptChanges(const Control::KeyChord newKeys)
|
||||
{
|
||||
const auto args{ make_self<ModifyKeyBindingEventArgs>(_CurrentKeys, // OldKeys
|
||||
newKeys, // NewKeys
|
||||
_IsNewlyAdded ? hstring{} : _CurrentAction, // OldAction
|
||||
unbox_value<hstring>(_ProposedAction)) }; // NewAction
|
||||
_ModifyKeyBindingRequestedHandlers(*this, *args);
|
||||
}
|
||||
|
||||
void KeyBindingViewModel::CancelChanges()
|
||||
{
|
||||
if (_IsNewlyAdded)
|
||||
{
|
||||
_DeleteNewlyAddedKeyBindingHandlers(*this, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleEditMode();
|
||||
}
|
||||
}
|
||||
|
||||
ActionsViewModel::ActionsViewModel(Model::CascadiaSettings settings) :
|
||||
_Settings{ settings }
|
||||
{
|
||||
// Populate AvailableActionAndArgs
|
||||
_AvailableActionMap = single_threaded_map<hstring, Model::ActionAndArgs>();
|
||||
std::vector<hstring> availableActionAndArgs;
|
||||
for (const auto& [name, actionAndArgs] : _Settings.ActionMap().AvailableActions())
|
||||
{
|
||||
availableActionAndArgs.push_back(name);
|
||||
_AvailableActionMap.Insert(name, actionAndArgs);
|
||||
}
|
||||
std::sort(begin(availableActionAndArgs), end(availableActionAndArgs));
|
||||
_AvailableActionAndArgs = single_threaded_observable_vector(std::move(availableActionAndArgs));
|
||||
|
||||
// Convert the key bindings from our settings into a view model representation
|
||||
const auto& keyBindingMap{ _Settings.ActionMap().KeyBindings() };
|
||||
std::vector<Editor::KeyBindingViewModel> keyBindingList;
|
||||
keyBindingList.reserve(keyBindingMap.Size());
|
||||
for (const auto& [keys, cmd] : keyBindingMap)
|
||||
{
|
||||
// convert the cmd into a KeyBindingViewModel
|
||||
auto container{ make_self<KeyBindingViewModel>(keys, cmd.Name(), _AvailableActionAndArgs) };
|
||||
_RegisterEvents(container);
|
||||
keyBindingList.push_back(*container);
|
||||
}
|
||||
|
||||
std::sort(begin(keyBindingList), end(keyBindingList), KeyBindingViewModelComparator{});
|
||||
_KeyBindingList = single_threaded_observable_vector(std::move(keyBindingList));
|
||||
}
|
||||
|
||||
void ActionsViewModel::OnAutomationPeerAttached()
|
||||
{
|
||||
_AutomationPeerAttached = true;
|
||||
for (const auto& kbdVM : _KeyBindingList)
|
||||
{
|
||||
// To create a more accessible experience, we want the "edit" buttons to _always_
|
||||
// appear when a screen reader is attached. This ensures that the edit buttons are
|
||||
// accessible via the UIA tree.
|
||||
get_self<KeyBindingViewModel>(kbdVM)->IsAutomationPeerAttached(_AutomationPeerAttached);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsViewModel::AddNewKeybinding()
|
||||
{
|
||||
// Create the new key binding and register all of the event handlers.
|
||||
auto kbdVM{ make_self<KeyBindingViewModel>(_AvailableActionAndArgs) };
|
||||
_RegisterEvents(kbdVM);
|
||||
kbdVM->DeleteNewlyAddedKeyBinding({ this, &ActionsViewModel::_KeyBindingViewModelDeleteNewlyAddedKeyBindingHandler });
|
||||
|
||||
// Manually add the editing background. This needs to be done in Actions not the view model.
|
||||
// We also have to do this manually because it hasn't been added to the list yet.
|
||||
kbdVM->IsInEditMode(true);
|
||||
// Emit an event to let the page know to update the background of this key binding VM
|
||||
_UpdateBackgroundHandlers(*this, *kbdVM);
|
||||
|
||||
// IMPORTANT: do this _after_ setting IsInEditMode. Otherwise, it'll get deleted immediately
|
||||
// by the PropertyChangedHandler below (where we delete any IsNewlyAdded items)
|
||||
kbdVM->IsNewlyAdded(true);
|
||||
_KeyBindingList.InsertAt(0, *kbdVM);
|
||||
_FocusContainerHandlers(*this, *kbdVM);
|
||||
}
|
||||
|
||||
void ActionsViewModel::_KeyBindingViewModelPropertyChangedHandler(const IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args)
|
||||
{
|
||||
const auto senderVM{ sender.as<Editor::KeyBindingViewModel>() };
|
||||
const auto propertyName{ args.PropertyName() };
|
||||
if (propertyName == L"IsInEditMode")
|
||||
{
|
||||
if (senderVM.IsInEditMode())
|
||||
{
|
||||
// Ensure that...
|
||||
// 1. we move focus to the edit mode controls
|
||||
// 2. any actions that were newly added are removed
|
||||
// 3. this is the only entry that is in edit mode
|
||||
for (int32_t i = _KeyBindingList.Size() - 1; i >= 0; --i)
|
||||
{
|
||||
const auto& kbdVM{ _KeyBindingList.GetAt(i) };
|
||||
if (senderVM == kbdVM)
|
||||
{
|
||||
// This is the view model entry that went into edit mode.
|
||||
// Emit an event to let the page know to move focus to
|
||||
// this VM's container.
|
||||
_FocusContainerHandlers(*this, senderVM);
|
||||
}
|
||||
else if (kbdVM.IsNewlyAdded())
|
||||
{
|
||||
// Remove any actions that were newly added
|
||||
_KeyBindingList.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exit edit mode for all other containers
|
||||
get_self<KeyBindingViewModel>(kbdVM)->DisableEditMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emit an event to let the page know to move focus to
|
||||
// this VM's container.
|
||||
_FocusContainerHandlers(*this, senderVM);
|
||||
}
|
||||
|
||||
// Emit an event to let the page know to update the background of this key binding VM
|
||||
_UpdateBackgroundHandlers(*this, senderVM);
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsViewModel::_KeyBindingViewModelDeleteKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Control::KeyChord& keys)
|
||||
{
|
||||
// Update the settings model
|
||||
_Settings.ActionMap().DeleteKeyBinding(keys);
|
||||
|
||||
// Find the current container in our list and remove it.
|
||||
// This is much faster than rebuilding the entire ActionMap.
|
||||
uint32_t index;
|
||||
if (_KeyBindingList.IndexOf(senderVM, index))
|
||||
{
|
||||
_KeyBindingList.RemoveAt(index);
|
||||
|
||||
// Focus the new item at this index
|
||||
if (_KeyBindingList.Size() != 0)
|
||||
{
|
||||
const auto newFocusedIndex{ std::clamp(index, 0u, _KeyBindingList.Size() - 1) };
|
||||
// Emit an event to let the page know to move focus to
|
||||
// this VM's container.
|
||||
_FocusContainerHandlers(*this, winrt::box_value(newFocusedIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActionsViewModel::_KeyBindingViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args)
|
||||
{
|
||||
const auto isNewAction{ !args.OldKeys() && args.OldActionName().empty() };
|
||||
|
||||
auto applyChangesToSettingsModel = [=]() {
|
||||
// If the key chord was changed,
|
||||
// update the settings model and view model appropriately
|
||||
// NOTE: we still need to update the view model if we're working with a newly added action
|
||||
if (isNewAction || args.OldKeys().Modifiers() != args.NewKeys().Modifiers() || args.OldKeys().Vkey() != args.NewKeys().Vkey())
|
||||
{
|
||||
if (!isNewAction)
|
||||
{
|
||||
// update settings model
|
||||
_Settings.ActionMap().RebindKeys(args.OldKeys(), args.NewKeys());
|
||||
}
|
||||
|
||||
// update view model
|
||||
auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
|
||||
senderVMImpl->CurrentKeys(args.NewKeys());
|
||||
}
|
||||
|
||||
// If the action was changed,
|
||||
// update the settings model and view model appropriately
|
||||
// NOTE: no need to check for "isNewAction" here. <empty_string> != <action name> already.
|
||||
if (args.OldActionName() != args.NewActionName())
|
||||
{
|
||||
// convert the action's name into a view model.
|
||||
const auto& newAction{ _AvailableActionMap.Lookup(args.NewActionName()) };
|
||||
|
||||
// update settings model
|
||||
_Settings.ActionMap().RegisterKeyBinding(args.NewKeys(), newAction);
|
||||
|
||||
// update view model
|
||||
auto senderVMImpl{ get_self<KeyBindingViewModel>(senderVM) };
|
||||
senderVMImpl->CurrentAction(args.NewActionName());
|
||||
senderVMImpl->IsNewlyAdded(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Check for this special case:
|
||||
// we're changing the key chord,
|
||||
// but the new key chord is already in use
|
||||
if (isNewAction || args.OldKeys().Modifiers() != args.NewKeys().Modifiers() || args.OldKeys().Vkey() != args.NewKeys().Vkey())
|
||||
{
|
||||
const auto& conflictingCmd{ _Settings.ActionMap().GetActionByKeyChord(args.NewKeys()) };
|
||||
if (conflictingCmd)
|
||||
{
|
||||
// We're about to overwrite another key chord.
|
||||
// Display a confirmation dialog.
|
||||
TextBlock errorMessageTB{};
|
||||
errorMessageTB.Text(RS_(L"Actions_RenameConflictConfirmationMessage"));
|
||||
|
||||
const auto conflictingCmdName{ conflictingCmd.Name() };
|
||||
TextBlock conflictingCommandNameTB{};
|
||||
conflictingCommandNameTB.Text(fmt::format(L"\"{}\"", conflictingCmdName.empty() ? RS_(L"Actions_UnnamedCommandName") : conflictingCmdName));
|
||||
conflictingCommandNameTB.FontStyle(Windows::UI::Text::FontStyle::Italic);
|
||||
|
||||
TextBlock confirmationQuestionTB{};
|
||||
confirmationQuestionTB.Text(RS_(L"Actions_RenameConflictConfirmationQuestion"));
|
||||
|
||||
Button acceptBTN{};
|
||||
acceptBTN.Content(box_value(RS_(L"Actions_RenameConflictConfirmationAcceptButton")));
|
||||
acceptBTN.Click([=](auto&, auto&) {
|
||||
// remove conflicting key binding from list view
|
||||
const auto containerIndex{ _GetContainerIndexByKeyChord(args.NewKeys()) };
|
||||
_KeyBindingList.RemoveAt(*containerIndex);
|
||||
|
||||
// remove flyout
|
||||
senderVM.AcceptChangesFlyout().Hide();
|
||||
senderVM.AcceptChangesFlyout(nullptr);
|
||||
|
||||
// update settings model and view model
|
||||
applyChangesToSettingsModel();
|
||||
senderVM.ToggleEditMode();
|
||||
});
|
||||
|
||||
StackPanel flyoutStack{};
|
||||
flyoutStack.Children().Append(errorMessageTB);
|
||||
flyoutStack.Children().Append(conflictingCommandNameTB);
|
||||
flyoutStack.Children().Append(confirmationQuestionTB);
|
||||
flyoutStack.Children().Append(acceptBTN);
|
||||
|
||||
Flyout acceptChangesFlyout{};
|
||||
acceptChangesFlyout.Content(flyoutStack);
|
||||
senderVM.AcceptChangesFlyout(acceptChangesFlyout);
|
||||
}
|
||||
}
|
||||
|
||||
// update settings model and view model
|
||||
applyChangesToSettingsModel();
|
||||
|
||||
// We NEED to toggle the edit mode here,
|
||||
// so that if nothing changed, we still exit
|
||||
// edit mode.
|
||||
senderVM.ToggleEditMode();
|
||||
}
|
||||
|
||||
void ActionsViewModel::_KeyBindingViewModelDeleteNewlyAddedKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const IInspectable& /*args*/)
|
||||
{
|
||||
for (uint32_t i = 0; i < _KeyBindingList.Size(); ++i)
|
||||
{
|
||||
const auto& kbdVM{ _KeyBindingList.GetAt(i) };
|
||||
if (kbdVM == senderVM)
|
||||
{
|
||||
_KeyBindingList.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - performs a search on KeyBindingList by key chord.
|
||||
// Arguments:
|
||||
// - keys - the associated key chord of the command we're looking for
|
||||
// Return Value:
|
||||
// - the index of the view model referencing the command. If the command doesn't exist, nullopt
|
||||
std::optional<uint32_t> ActionsViewModel::_GetContainerIndexByKeyChord(const Control::KeyChord& keys)
|
||||
{
|
||||
for (uint32_t i = 0; i < _KeyBindingList.Size(); ++i)
|
||||
{
|
||||
const auto kbdVM{ get_self<KeyBindingViewModel>(_KeyBindingList.GetAt(i)) };
|
||||
const auto& otherKeys{ kbdVM->CurrentKeys() };
|
||||
if (otherKeys && keys.Modifiers() == otherKeys.Modifiers() && keys.Vkey() == otherKeys.Vkey())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO GH #6900:
|
||||
// an expedited search can be done if we use cmd.Name()
|
||||
// to quickly search through the sorted list.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ActionsViewModel::_RegisterEvents(com_ptr<KeyBindingViewModel>& kbdVM)
|
||||
{
|
||||
kbdVM->PropertyChanged({ this, &ActionsViewModel::_KeyBindingViewModelPropertyChangedHandler });
|
||||
kbdVM->DeleteKeyBindingRequested({ this, &ActionsViewModel::_KeyBindingViewModelDeleteKeyBindingHandler });
|
||||
kbdVM->ModifyKeyBindingRequested({ this, &ActionsViewModel::_KeyBindingViewModelModifyKeyBindingHandler });
|
||||
kbdVM->IsAutomationPeerAttached(_AutomationPeerAttached);
|
||||
}
|
||||
}
|
||||
131
src/cascadia/TerminalSettingsEditor/ActionsViewModel.h
Normal file
131
src/cascadia/TerminalSettingsEditor/ActionsViewModel.h
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ActionsViewModel.g.h"
|
||||
#include "KeyBindingViewModel.g.h"
|
||||
#include "ModifyKeyBindingEventArgs.g.h"
|
||||
#include "Utils.h"
|
||||
#include "ViewModelHelpers.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
{
|
||||
struct KeyBindingViewModelComparator
|
||||
{
|
||||
bool operator()(const Editor::KeyBindingViewModel& lhs, const Editor::KeyBindingViewModel& rhs) const
|
||||
{
|
||||
return lhs.Name() < rhs.Name();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModifyKeyBindingEventArgs : ModifyKeyBindingEventArgsT<ModifyKeyBindingEventArgs>
|
||||
{
|
||||
public:
|
||||
ModifyKeyBindingEventArgs(const Control::KeyChord& oldKeys, const Control::KeyChord& newKeys, const hstring oldActionName, const hstring newActionName) :
|
||||
_OldKeys{ oldKeys },
|
||||
_NewKeys{ newKeys },
|
||||
_OldActionName{ std::move(oldActionName) },
|
||||
_NewActionName{ std::move(newActionName) } {}
|
||||
|
||||
WINRT_PROPERTY(Control::KeyChord, OldKeys, nullptr);
|
||||
WINRT_PROPERTY(Control::KeyChord, NewKeys, nullptr);
|
||||
WINRT_PROPERTY(hstring, OldActionName);
|
||||
WINRT_PROPERTY(hstring, NewActionName);
|
||||
};
|
||||
|
||||
struct KeyBindingViewModel : KeyBindingViewModelT<KeyBindingViewModel>, ViewModelHelper<KeyBindingViewModel>
|
||||
{
|
||||
public:
|
||||
KeyBindingViewModel(const Windows::Foundation::Collections::IObservableVector<hstring>& availableActions);
|
||||
KeyBindingViewModel(const Control::KeyChord& keys, const hstring& name, const Windows::Foundation::Collections::IObservableVector<hstring>& availableActions);
|
||||
|
||||
hstring Name() const { return _CurrentAction; }
|
||||
hstring KeyChordText() const { return _KeyChordText; }
|
||||
|
||||
// UIA Text
|
||||
hstring EditButtonName() const noexcept;
|
||||
hstring CancelButtonName() const noexcept;
|
||||
hstring AcceptButtonName() const noexcept;
|
||||
hstring DeleteButtonName() const noexcept;
|
||||
|
||||
void EnterHoverMode() { IsHovered(true); };
|
||||
void ExitHoverMode() { IsHovered(false); };
|
||||
void ActionGotFocus() { IsContainerFocused(true); };
|
||||
void ActionLostFocus() { IsContainerFocused(false); };
|
||||
void EditButtonGettingFocus() { IsEditButtonFocused(true); };
|
||||
void EditButtonLosingFocus() { IsEditButtonFocused(false); };
|
||||
bool ShowEditButton() const noexcept;
|
||||
void ToggleEditMode();
|
||||
void DisableEditMode() { IsInEditMode(false); }
|
||||
void AttemptAcceptChanges();
|
||||
void AttemptAcceptChanges(const Control::KeyChord newKeys);
|
||||
void CancelChanges();
|
||||
void DeleteKeyBinding() { _DeleteKeyBindingRequestedHandlers(*this, _CurrentKeys); }
|
||||
|
||||
// ProposedAction: the entry selected by the combo box; may disagree with the settings model.
|
||||
// CurrentAction: the combo box item that maps to the settings model value.
|
||||
// AvailableActions: the list of options in the combo box; both actions above must be in this list.
|
||||
// NOTE: ProposedAction and CurrentAction may disagree mainly due to the "edit mode" system in place.
|
||||
// Current Action serves as...
|
||||
// 1 - a record of what to set ProposedAction to on a cancellation
|
||||
// 2 - a form of translation between ProposedAction and the settings model
|
||||
// We would also need an ActionMap reference to remove this, but this is a better separation
|
||||
// of responsibilities.
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(IInspectable, ProposedAction);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentAction);
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<hstring>, AvailableActions, nullptr);
|
||||
|
||||
// ProposedKeys: the keys proposed by the control; may disagree with the settings model.
|
||||
// CurrentKeys: the key chord bound in the settings model.
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Control::KeyChord, ProposedKeys);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Control::KeyChord, CurrentKeys, nullptr);
|
||||
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsInEditMode, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsNewlyAdded, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Controls::Flyout, AcceptChangesFlyout, nullptr);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsAutomationPeerAttached, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsHovered, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsContainerFocused, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, IsEditButtonFocused, false);
|
||||
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::UI::Xaml::Media::Brush, ContainerBackground, nullptr);
|
||||
TYPED_EVENT(ModifyKeyBindingRequested, Editor::KeyBindingViewModel, Editor::ModifyKeyBindingEventArgs);
|
||||
TYPED_EVENT(DeleteKeyBindingRequested, Editor::KeyBindingViewModel, Terminal::Control::KeyChord);
|
||||
TYPED_EVENT(DeleteNewlyAddedKeyBinding, Editor::KeyBindingViewModel, IInspectable);
|
||||
|
||||
private:
|
||||
hstring _KeyChordText{};
|
||||
};
|
||||
|
||||
struct ActionsViewModel : ActionsViewModelT<ActionsViewModel>, ViewModelHelper<ActionsViewModel>
|
||||
{
|
||||
public:
|
||||
ActionsViewModel(Model::CascadiaSettings settings);
|
||||
|
||||
void OnAutomationPeerAttached();
|
||||
void AddNewKeybinding();
|
||||
|
||||
WINRT_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::KeyBindingViewModel>, KeyBindingList);
|
||||
TYPED_EVENT(FocusContainer, IInspectable, IInspectable);
|
||||
TYPED_EVENT(UpdateBackground, IInspectable, IInspectable);
|
||||
|
||||
private:
|
||||
bool _AutomationPeerAttached{ false };
|
||||
Model::CascadiaSettings _Settings;
|
||||
Windows::Foundation::Collections::IObservableVector<hstring> _AvailableActionAndArgs;
|
||||
Windows::Foundation::Collections::IMap<hstring, Model::ActionAndArgs> _AvailableActionMap;
|
||||
|
||||
std::optional<uint32_t> _GetContainerIndexByKeyChord(const Control::KeyChord& keys);
|
||||
void _RegisterEvents(com_ptr<implementation::KeyBindingViewModel>& kbdVM);
|
||||
|
||||
void _KeyBindingViewModelPropertyChangedHandler(const Windows::Foundation::IInspectable& senderVM, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
|
||||
void _KeyBindingViewModelDeleteKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Control::KeyChord& args);
|
||||
void _KeyBindingViewModelModifyKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const Editor::ModifyKeyBindingEventArgs& args);
|
||||
void _KeyBindingViewModelDeleteNewlyAddedKeyBindingHandler(const Editor::KeyBindingViewModel& senderVM, const IInspectable& args);
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Settings::Editor::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(ActionsViewModel);
|
||||
}
|
||||
60
src/cascadia/TerminalSettingsEditor/ActionsViewModel.idl
Normal file
60
src/cascadia/TerminalSettingsEditor/ActionsViewModel.idl
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Microsoft.Terminal.Settings.Editor
|
||||
{
|
||||
runtimeclass ModifyKeyBindingEventArgs
|
||||
{
|
||||
Microsoft.Terminal.Control.KeyChord OldKeys { get; };
|
||||
Microsoft.Terminal.Control.KeyChord NewKeys { get; };
|
||||
String OldActionName { get; };
|
||||
String NewActionName { get; };
|
||||
}
|
||||
|
||||
runtimeclass KeyBindingViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
// Settings Model side
|
||||
String Name { get; };
|
||||
String KeyChordText { get; };
|
||||
|
||||
// UI side
|
||||
Boolean ShowEditButton { get; };
|
||||
Boolean IsInEditMode { get; };
|
||||
Boolean IsNewlyAdded { get; };
|
||||
Microsoft.Terminal.Control.KeyChord ProposedKeys;
|
||||
Object ProposedAction;
|
||||
Windows.UI.Xaml.Controls.Flyout AcceptChangesFlyout;
|
||||
String EditButtonName { get; };
|
||||
String CancelButtonName { get; };
|
||||
String AcceptButtonName { get; };
|
||||
String DeleteButtonName { get; };
|
||||
Windows.UI.Xaml.Media.Brush ContainerBackground { get; };
|
||||
|
||||
void EnterHoverMode();
|
||||
void ExitHoverMode();
|
||||
void ActionGotFocus();
|
||||
void ActionLostFocus();
|
||||
void EditButtonGettingFocus();
|
||||
void EditButtonLosingFocus();
|
||||
IObservableVector<String> AvailableActions { get; };
|
||||
void ToggleEditMode();
|
||||
void AttemptAcceptChanges();
|
||||
void CancelChanges();
|
||||
void DeleteKeyBinding();
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, ModifyKeyBindingEventArgs> ModifyKeyBindingRequested;
|
||||
event Windows.Foundation.TypedEventHandler<KeyBindingViewModel, Microsoft.Terminal.Control.KeyChord> DeleteKeyBindingRequested;
|
||||
}
|
||||
|
||||
runtimeclass ActionsViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
|
||||
{
|
||||
ActionsViewModel(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
|
||||
|
||||
void OnAutomationPeerAttached();
|
||||
void AddNewKeybinding();
|
||||
|
||||
IObservableVector<KeyBindingViewModel> KeyBindingList { get; };
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusContainer;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> UpdateBackground;
|
||||
}
|
||||
}
|
||||
@@ -374,7 +374,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
|
||||
}
|
||||
else if (clickedItemTag == actionsTag)
|
||||
{
|
||||
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<ActionsPageNavigationState>(_settingsClone));
|
||||
contentFrame().Navigate(xaml_typename<Editor::Actions>(), winrt::make<ActionsViewModel>(_settingsClone));
|
||||
const auto crumb = winrt::make<Breadcrumb>(box_value(clickedItemTag), RS_(L"Nav_Actions/Content"), BreadcrumbSubPage::None);
|
||||
_breadcrumbs.Append(crumb);
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@
|
||||
<DependentUpon>ProfileViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ActionsViewModel.h">
|
||||
<DependentUpon>ActionsViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ColorSchemeViewModel.h">
|
||||
<DependentUpon>ColorSchemeViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -225,6 +229,10 @@
|
||||
<DependentUpon>ProfileViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ActionsViewModel.cpp">
|
||||
<DependentUpon>ActionsViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ColorSchemeViewModel.cpp">
|
||||
<DependentUpon>ColorSchemeViewModel.idl</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
@@ -320,6 +328,7 @@
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Midl>
|
||||
<Midl Include="ProfileViewModel.idl" />
|
||||
<Midl Include="ActionsViewModel.idl" />
|
||||
<Midl Include="ColorSchemeViewModel.idl" />
|
||||
<Midl Include="ColorSchemesPageViewModel.idl" />
|
||||
<Midl Include="RenderingViewModel.idl" />
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="ProfileViewModel.idl" />
|
||||
<Midl Include="ActionsViewModel.idl" />
|
||||
<Midl Include="ColorSchemeViewModel.idl" />
|
||||
<Midl Include="ColorSchemesPageViewModel.idl" />
|
||||
<Midl Include="RenderingViewModel.idl" />
|
||||
|
||||
@@ -27,7 +27,6 @@ static constexpr std::string_view ArgsKey{ "args" };
|
||||
static constexpr std::string_view IterateOnKey{ "iterateOn" };
|
||||
static constexpr std::string_view CommandsKey{ "commands" };
|
||||
static constexpr std::string_view KeysKey{ "keys" };
|
||||
static constexpr std::string_view DescriptionKey{ "description" };
|
||||
|
||||
static constexpr std::string_view ProfileNameToken{ "${profile.name}" };
|
||||
static constexpr std::string_view ProfileIconToken{ "${profile.icon}" };
|
||||
@@ -45,7 +44,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
command->_keyMappings = _keyMappings;
|
||||
command->_iconPath = _iconPath;
|
||||
command->_IterateOn = _IterateOn;
|
||||
command->_Description = _Description;
|
||||
|
||||
command->_originalJson = _originalJson;
|
||||
command->_nestedCommand = _nestedCommand;
|
||||
@@ -266,7 +264,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
auto nested = false;
|
||||
JsonUtils::GetValueForKey(json, IterateOnKey, result->_IterateOn);
|
||||
JsonUtils::GetValueForKey(json, DescriptionKey, result->_Description);
|
||||
|
||||
// For iterable commands, we'll make another pass at parsing them once
|
||||
// the json is patched. So ignore parsing sub-commands for now. Commands
|
||||
@@ -419,7 +416,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
Json::Value cmdJson{ Json::ValueType::objectValue };
|
||||
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
|
||||
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
|
||||
JsonUtils::SetValueForKey(cmdJson, DescriptionKey, _Description);
|
||||
|
||||
if (_ActionAndArgs)
|
||||
{
|
||||
@@ -440,7 +436,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
// First iteration also writes icon and name
|
||||
JsonUtils::SetValueForKey(cmdJson, IconKey, _iconPath);
|
||||
JsonUtils::SetValueForKey(cmdJson, NameKey, _name);
|
||||
JsonUtils::SetValueForKey(cmdJson, DescriptionKey, _Description);
|
||||
}
|
||||
|
||||
if (_ActionAndArgs)
|
||||
@@ -669,10 +664,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
const auto parseElement = [&](const auto& element) {
|
||||
winrt::hstring completionText;
|
||||
winrt::hstring listText;
|
||||
winrt::hstring tooltipText;
|
||||
JsonUtils::GetValueForKey(element, "CompletionText", completionText);
|
||||
JsonUtils::GetValueForKey(element, "ListItemText", listText);
|
||||
JsonUtils::GetValueForKey(element, "ToolTip", tooltipText);
|
||||
|
||||
auto args = winrt::make_self<SendInputArgs>(
|
||||
winrt::hstring{ fmt::format(FMT_COMPILE(L"{:\x7f^{}}{}"),
|
||||
@@ -684,8 +677,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
auto c = winrt::make_self<Command>();
|
||||
c->_name = listText;
|
||||
c->_Description = tooltipText;
|
||||
c->_ActionAndArgs = actionAndArgs;
|
||||
|
||||
// Try to assign a sensible icon based on the result type. These are
|
||||
// roughly chosen to align with the icons in
|
||||
// https://github.com/PowerShell/PowerShellEditorServices/pull/1738
|
||||
|
||||
@@ -74,7 +74,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None);
|
||||
WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs);
|
||||
WINRT_PROPERTY(winrt::hstring, Description, L"");
|
||||
|
||||
private:
|
||||
Json::Value _originalJson;
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Command();
|
||||
|
||||
String Name { get; };
|
||||
String Description { get; };
|
||||
ActionAndArgs ActionAndArgs { get; };
|
||||
Microsoft.Terminal.Control.KeyChord Keys { get; };
|
||||
void RegisterKey(Microsoft.Terminal.Control.KeyChord keys);
|
||||
|
||||
@@ -265,7 +265,12 @@ void BackendD3D::_handleSettingsUpdate(const RenderingPayload& p)
|
||||
{
|
||||
wil::com_ptr<ID3D11Texture2D> buffer;
|
||||
THROW_IF_FAILED(p.swapChain.swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(buffer.addressof())));
|
||||
THROW_IF_FAILED(p.device->CreateRenderTargetView(buffer.get(), nullptr, _renderTargetView.put()));
|
||||
|
||||
static constexpr D3D11_RENDER_TARGET_VIEW_DESC desc{
|
||||
.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
|
||||
.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D,
|
||||
};
|
||||
THROW_IF_FAILED(p.device->CreateRenderTargetView(buffer.get(), &desc, _renderTargetView.put()));
|
||||
}
|
||||
|
||||
const auto fontChanged = _fontGeneration != p.s->font.generation();
|
||||
@@ -547,7 +552,7 @@ void BackendD3D::_recreateBackgroundColorBitmap(const RenderingPayload& p)
|
||||
.Height = p.s->viewportCellCount.y,
|
||||
.MipLevels = 1,
|
||||
.ArraySize = 1,
|
||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
|
||||
.SampleDesc = { 1, 0 },
|
||||
.Usage = D3D11_USAGE_DYNAMIC,
|
||||
.BindFlags = D3D11_BIND_SHADER_RESOURCE,
|
||||
|
||||
@@ -35,6 +35,7 @@ float3 DWrite_EnhanceContrast3(float3 alpha, float k)
|
||||
return alpha * (k + 1.0f) / (alpha * k + 1.0f);
|
||||
}
|
||||
|
||||
// dwrite vs. gamma 1.8: https://www.desmos.com/calculator/6wocsr6vcq
|
||||
float DWrite_ApplyAlphaCorrection(float a, float f, float4 g)
|
||||
{
|
||||
return a + a * (1 - a) * ((g.x * f + g.y) * a + (g.z * f + g.w));
|
||||
|
||||
@@ -27,7 +27,7 @@ struct PSData
|
||||
float4 position : SV_Position;
|
||||
float2 texcoord : texcoord;
|
||||
nointerpolation uint shadingType : shadingType;
|
||||
nointerpolation uint2 renditionScale : renditionScale;
|
||||
nointerpolation float2 renditionScale : renditionScale;
|
||||
nointerpolation float4 color : color;
|
||||
};
|
||||
|
||||
|
||||
@@ -44,27 +44,13 @@ Output main(PSData data) : SV_Target
|
||||
}
|
||||
case SHADING_TYPE_TEXT_GRAYSCALE:
|
||||
{
|
||||
// These are independent of the glyph texture and could be moved to the vertex shader or CPU side of things.
|
||||
const float4 foreground = premultiplyColor(data.color);
|
||||
const float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, data.color.rgb);
|
||||
const float intensity = DWrite_CalcColorIntensity(data.color.rgb);
|
||||
// These aren't.
|
||||
const float4 glyph = glyphAtlas[data.texcoord];
|
||||
const float contrasted = DWrite_EnhanceContrast(glyph.a, blendEnhancedContrast);
|
||||
const float alphaCorrected = DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios);
|
||||
color = alphaCorrected * foreground;
|
||||
color = glyphAtlas[data.texcoord].a * data.color;
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
}
|
||||
case SHADING_TYPE_TEXT_CLEARTYPE:
|
||||
{
|
||||
// These are independent of the glyph texture and could be moved to the vertex shader or CPU side of things.
|
||||
const float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, data.color.rgb);
|
||||
// These aren't.
|
||||
const float4 glyph = glyphAtlas[data.texcoord];
|
||||
const float3 contrasted = DWrite_EnhanceContrast3(glyph.rgb, blendEnhancedContrast);
|
||||
const float3 alphaCorrected = DWrite_ApplyAlphaCorrection3(contrasted, data.color.rgb, gammaRatios);
|
||||
weights = float4(alphaCorrected * data.color.a, 1);
|
||||
weights = float4(glyphAtlas[data.texcoord].rgb * data.color.a, 1);
|
||||
color = weights * data.color;
|
||||
break;
|
||||
}
|
||||
@@ -77,14 +63,14 @@ Output main(PSData data) : SV_Target
|
||||
case SHADING_TYPE_DOTTED_LINE:
|
||||
{
|
||||
const bool on = frac(data.position.x / (2.0f * underlineWidth * data.renditionScale.x)) < 0.5f;
|
||||
color = on * premultiplyColor(data.color);
|
||||
color = on * data.color;
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
}
|
||||
case SHADING_TYPE_DASHED_LINE:
|
||||
{
|
||||
const bool on = frac(data.position.x / (backgroundCellSize.x * data.renditionScale.x)) < 0.5f;
|
||||
color = on * premultiplyColor(data.color);
|
||||
color = on * data.color;
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
}
|
||||
@@ -102,13 +88,13 @@ Output main(PSData data) : SV_Target
|
||||
const float s = sin(data.position.x * freq);
|
||||
const float d = abs(centerY - (s * amp) - data.position.y);
|
||||
const float a = 1 - saturate(d - strokeWidthHalf);
|
||||
color = a * premultiplyColor(data.color);
|
||||
color = a * data.color;
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
color = premultiplyColor(data.color);
|
||||
color = data.color;
|
||||
weights = color.aaaa;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "shader_common.hlsl"
|
||||
|
||||
#pragma warning(disable: 3571) // pow(f, e) will not work for negative f, use abs(f) or conditionally handle negative values if you expect them
|
||||
|
||||
cbuffer ConstBuffer : register(b0)
|
||||
{
|
||||
float2 positionScale;
|
||||
@@ -13,7 +15,7 @@ PSData main(VSData data)
|
||||
// clang-format on
|
||||
{
|
||||
PSData output;
|
||||
output.color = data.color;
|
||||
output.color = float4(pow(data.color.rgb * data.color.a, 2.2), data.color.a);
|
||||
output.shadingType = data.shadingType;
|
||||
output.renditionScale = data.renditionScale;
|
||||
// positionScale is expected to be float2(2.0f / sizeInPixel.x, -2.0f / sizeInPixel.y). Together with the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -24,14 +24,12 @@ try
|
||||
|
||||
_pProvider = pProvider;
|
||||
_pData = pData;
|
||||
_pData->LockConsole();
|
||||
_start = pData->GetViewport().Origin();
|
||||
_end = pData->GetViewport().Origin();
|
||||
_blockRange = false;
|
||||
_wordDelimiters = wordDelimiters;
|
||||
|
||||
UiaTracing::TextRange::Constructor(*this);
|
||||
_pData->UnlockConsole();
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
Reference in New Issue
Block a user