Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement MVVM for the Actions page #14292

Merged
merged 9 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
377 changes: 28 additions & 349 deletions src/cascadia/TerminalSettingsEditor/Actions.cpp

Large diffs are not rendered by default.

116 changes: 2 additions & 114 deletions src/cascadia/TerminalSettingsEditor/Actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,134 +4,22 @@
#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:
Actions();

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);
};
}

Expand Down
54 changes: 2 additions & 52 deletions src/cascadia/TerminalSettingsEditor/Actions.idl
Original file line number Diff line number Diff line change
@@ -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; };
}
}
4 changes: 2 additions & 2 deletions src/cascadia/TerminalSettingsEditor/Actions.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@
Style="{StaticResource SettingsStackStyle}">
<!-- Add New Button -->
<Button x:Name="AddNewButton"
Click="AddNew_Click">
Click="{x:Bind ViewModel.AddNew_Click}">
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved
<Button.Content>
<StackPanel Orientation="Horizontal">
<FontIcon FontSize="{StaticResource StandardIconSize}"
Expand All @@ -348,7 +348,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>
Expand Down
Loading