Skip to content

Commit 8d2791f

Browse files
committed
Add Profile.BellSound to Settings UI
1 parent a7e47b7 commit 8d2791f

File tree

8 files changed

+336
-2
lines changed

8 files changed

+336
-2
lines changed

src/cascadia/TerminalSettingsEditor/MainPage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
341341
}
342342
else if (currentPage == ProfileSubPage::Advanced)
343343
{
344-
contentFrame().Navigate(xaml_typename<Editor::Profiles_Advanced>(), profile);
344+
contentFrame().Navigate(xaml_typename<Editor::Profiles_Advanced>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
345345
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, RS_(L"Profile_Advanced/Header"), BreadcrumbSubPage::Profile_Advanced);
346346
_breadcrumbs.Append(crumb);
347347
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);

src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
4040
INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER(CloseOnExitMode, CloseOnExitMode, winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode, L"Profile_CloseOnExit", L"Content");
4141
INITIALIZE_BINDABLE_ENUM_SETTING(ScrollState, ScrollbarState, winrt::Microsoft::Terminal::Control::ScrollbarState, L"Profile_ScrollbarVisibility", L"Content");
4242

43+
_InitializeCurrentBellSounds();
44+
4345
// Add a property changed handler to our own property changed event.
4446
// This propagates changes from the settings model to anybody listening to our
4547
// unique view model members.
@@ -76,6 +78,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
7678
{
7779
_NotifyChanges(L"HideIcon");
7880
}
81+
else if (viewModelProperty == L"CurrentBellSounds")
82+
{
83+
// we already have infrastructure in place to
84+
// propagate changes from the CurrentBellSounds
85+
// to the model. Refer to...
86+
// - _InitializeCurrentBellSounds() --> _CurrentBellSounds.VectorChanged()
87+
// - RequestAddBellSound()
88+
// - RequestDeleteBellSound()
89+
90+
_NotifyChanges(L"BellSoundPreview", L"HasBellSound");
91+
}
92+
else if (viewModelProperty == L"BellSound")
93+
{
94+
_InitializeCurrentBellSounds();
95+
}
7996
});
8097

8198
// Do the same for the starting directory
@@ -374,6 +391,145 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
374391
BellStyle(currentStyle);
375392
}
376393

394+
// Method Description:
395+
// - Construct _CurrentBellSounds by importing the _inherited_ value from the model
396+
// - Adds a PropertyChanged handler to each BellSoundViewModel to propagate changes to the model
397+
void ProfileViewModel::_InitializeCurrentBellSounds()
398+
{
399+
_CurrentBellSounds = winrt::single_threaded_observable_vector<Editor::BellSoundViewModel>();
400+
if (const auto soundList = _profile.BellSound())
401+
{
402+
for (const auto&& bellSound : soundList)
403+
{
404+
auto vm = winrt::make<BellSoundViewModel>(bellSound);
405+
vm.PropertyChanged({ this, &ProfileViewModel::_BellSoundVMPropertyChanged });
406+
_CurrentBellSounds.Append(vm);
407+
}
408+
}
409+
_CurrentBellSounds.VectorChanged([this](auto&&, const IVectorChangedEventArgs& args) {
410+
switch (args.CollectionChange())
411+
{
412+
case CollectionChange::ItemInserted:
413+
{
414+
const auto index = args.Index();
415+
const auto& newSound = _CurrentBellSounds.GetAt(index);
416+
417+
if (!_profile.BellSound())
418+
{
419+
_profile.BellSound(winrt::single_threaded_vector<winrt::hstring>());
420+
}
421+
_profile.BellSound().InsertAt(index, newSound.Path());
422+
break;
423+
}
424+
case CollectionChange::ItemRemoved:
425+
{
426+
_profile.BellSound().RemoveAt(args.Index());
427+
break;
428+
}
429+
case CollectionChange::ItemChanged:
430+
{
431+
// I've never been able to get this one to hit,
432+
// but if it ever does, propagate change to model
433+
const auto index = args.Index();
434+
const auto& newSound = _CurrentBellSounds.GetAt(index);
435+
_profile.BellSound().SetAt(index, newSound.Path());
436+
break;
437+
}
438+
case CollectionChange::Reset:
439+
default:
440+
{
441+
// propagate changes to model
442+
auto list = winrt::single_threaded_vector<winrt::hstring>();
443+
for (const auto& sound : _CurrentBellSounds)
444+
{
445+
list.Append(sound.Path());
446+
}
447+
_profile.BellSound(list);
448+
break;
449+
}
450+
}
451+
});
452+
_NotifyChanges(L"CurrentBellSounds");
453+
}
454+
455+
// Method Description:
456+
// - If the current layer is inheriting the bell sound from its parent,
457+
// we need to copy the _inherited_ bell sound list to the current layer
458+
// so that we can then apply modifications to it
459+
void ProfileViewModel::_PrepareModelForBellSoundModification()
460+
{
461+
if (const auto inheritedSounds = _profile.BellSound(); !_profile.HasBellSound() && inheritedSounds)
462+
{
463+
auto newSounds{ winrt::single_threaded_vector<winrt::hstring>() };
464+
for (const auto sound : inheritedSounds)
465+
{
466+
newSounds.Append(sound);
467+
}
468+
_profile.BellSound(newSounds);
469+
}
470+
}
471+
472+
hstring ProfileViewModel::BellSoundPreview()
473+
{
474+
const auto& currentSound = BellSound();
475+
if (!currentSound || currentSound.Size() == 0)
476+
{
477+
return RS_(L"Profile_BellSoundPreviewDefault");
478+
}
479+
else if (currentSound.Size() == 1)
480+
{
481+
std::filesystem::path filePath{ std::wstring_view{ currentSound.GetAt(0) } };
482+
return hstring{ filePath.filename().wstring() };
483+
}
484+
return RS_(L"Profile_BellSoundPreviewMultiple");
485+
}
486+
487+
void ProfileViewModel::_BellSoundVMPropertyChanged(const IInspectable& sender, const PropertyChangedEventArgs& args)
488+
{
489+
if (args.PropertyName() == L"Path")
490+
{
491+
auto senderVM = sender.as<Editor::BellSoundViewModel>();
492+
493+
// propagate changes to model
494+
uint32_t index;
495+
if (_CurrentBellSounds.IndexOf(senderVM, index))
496+
{
497+
// if current layer is inheriting,
498+
// we should copy the bell sound then apply changes
499+
_PrepareModelForBellSoundModification();
500+
501+
_profile.BellSound().SetAt(index, senderVM.Path());
502+
_NotifyChanges(L"CurrentBellSounds");
503+
}
504+
}
505+
}
506+
507+
void ProfileViewModel::RequestAddBellSound()
508+
{
509+
// If we were inheriting our bell sound,
510+
// copy it over to the current layer and apply modifications
511+
_PrepareModelForBellSoundModification();
512+
513+
auto vm = winrt::make<BellSoundViewModel>();
514+
vm.PropertyChanged({ this, &ProfileViewModel::_BellSoundVMPropertyChanged });
515+
_CurrentBellSounds.Append(vm);
516+
_NotifyChanges(L"CurrentBellSounds");
517+
}
518+
519+
void ProfileViewModel::RequestDeleteBellSound(const Editor::BellSoundViewModel& vm)
520+
{
521+
uint32_t index;
522+
if (_CurrentBellSounds.IndexOf(vm, index))
523+
{
524+
// If we were inheriting our bell sound,
525+
// copy it over to the current layer and apply modifications
526+
_PrepareModelForBellSoundModification();
527+
528+
_CurrentBellSounds.RemoveAt(index);
529+
_NotifyChanges(L"CurrentBellSounds");
530+
}
531+
}
532+
377533
void ProfileViewModel::DeleteProfile()
378534
{
379535
auto deleteProfileArgs{ winrt::make_self<DeleteProfileEventArgs>(Guid()) };

src/cascadia/TerminalSettingsEditor/ProfileViewModel.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "DeleteProfileEventArgs.g.h"
77
#include "NavigateToProfileArgs.g.h"
8+
#include "BellSoundViewModel.g.h"
89
#include "ProfileViewModel.g.h"
910
#include "Utils.h"
1011
#include "ViewModelHelpers.h"
@@ -26,6 +27,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
2627
Editor::ProfileViewModel _Profile{ nullptr };
2728
};
2829

30+
struct BellSoundViewModel : BellSoundViewModelT<BellSoundViewModel>, ViewModelHelper<BellSoundViewModel>
31+
{
32+
public:
33+
BellSoundViewModel() = default;
34+
BellSoundViewModel(hstring path) :
35+
_Path{ path } {}
36+
37+
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, Path);
38+
};
39+
2940
struct ProfileViewModel : ProfileViewModelT<ProfileViewModel>, ViewModelHelper<ProfileViewModel>
3041
{
3142
public:
@@ -46,6 +57,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
4657
void SetBellStyleWindow(winrt::Windows::Foundation::IReference<bool> on);
4758
void SetBellStyleTaskbar(winrt::Windows::Foundation::IReference<bool> on);
4859

60+
hstring BellSoundPreview();
61+
void RequestAddBellSound();
62+
void RequestDeleteBellSound(const Editor::BellSoundViewModel& vm);
63+
4964
void SetAcrylicOpacityPercentageValue(double value)
5065
{
5166
Opacity(static_cast<float>(value) / 100.0f);
@@ -88,6 +103,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
88103
til::typed_event<Editor::ProfileViewModel, Editor::DeleteProfileEventArgs> DeleteProfileRequested;
89104

90105
VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
106+
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::BellSoundViewModel>, CurrentBellSounds);
91107

92108
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, Guid);
93109
PERMANENT_OBSERVABLE_PROJECTED_SETTING(_profile, ConnectionType);
@@ -114,6 +130,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
114130
OBSERVABLE_PROJECTED_SETTING(_profile, SnapOnInput);
115131
OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing);
116132
OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle);
133+
OBSERVABLE_PROJECTED_SETTING(_profile, BellSound);
117134
OBSERVABLE_PROJECTED_SETTING(_profile, Elevate);
118135
OBSERVABLE_PROJECTED_SETTING(_profile, ReloadEnvironmentVariables);
119136
OBSERVABLE_PROJECTED_SETTING(_profile, RightClickContextMenu);
@@ -135,6 +152,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
135152
winrt::hstring _lastIcon;
136153
Editor::AppearanceViewModel _defaultAppearanceViewModel;
137154

155+
void _InitializeCurrentBellSounds();
156+
void _PrepareModelForBellSoundModification();
157+
void _BellSoundVMPropertyChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
138158
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
139159
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;
140160

src/cascadia/TerminalSettingsEditor/ProfileViewModel.idl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ namespace Microsoft.Terminal.Settings.Editor
2525
Guid ProfileGuid { get; };
2626
}
2727

28+
runtimeclass BellSoundViewModel : Windows.UI.Xaml.Data.INotifyPropertyChanged
29+
{
30+
String Path;
31+
}
32+
2833
enum ProfileSubPage
2934
{
3035
Base = 0,
@@ -48,6 +53,11 @@ namespace Microsoft.Terminal.Settings.Editor
4853
void SetBellStyleWindow(Windows.Foundation.IReference<Boolean> on);
4954
void SetBellStyleTaskbar(Windows.Foundation.IReference<Boolean> on);
5055

56+
String BellSoundPreview { get; };
57+
Windows.Foundation.Collections.IObservableVector<BellSoundViewModel> CurrentBellSounds { get; };
58+
void RequestAddBellSound();
59+
void RequestDeleteBellSound(BellSoundViewModel vm);
60+
5161
IInspectable CurrentAntiAliasingMode;
5262
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> AntiAliasingModeList { get; };
5363

@@ -105,6 +115,7 @@ namespace Microsoft.Terminal.Settings.Editor
105115
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, SnapOnInput);
106116
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, AltGrAliasing);
107117
OBSERVABLE_PROJECTED_PROFILE_SETTING(Microsoft.Terminal.Settings.Model.BellStyle, BellStyle);
118+
OBSERVABLE_PROJECTED_PROFILE_SETTING(Windows.Foundation.Collections.IVector<String>, BellSound);
108119
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, Elevate);
109120
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, ReloadEnvironmentVariables);
110121
OBSERVABLE_PROJECTED_PROFILE_SETTING(Boolean, RightClickContextMenu);

src/cascadia/TerminalSettingsEditor/Profiles_Advanced.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include <LibraryResources.h>
1111
#include "..\WinRTUtils\inc\Utils.h"
1212

13+
using namespace winrt::Windows::Foundation;
14+
using namespace winrt::Windows::UI::Xaml;
15+
using namespace winrt::Windows::UI::Xaml::Controls;
1316
using namespace winrt::Windows::UI::Xaml::Navigation;
1417

1518
namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
@@ -21,11 +24,58 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
2124

2225
void Profiles_Advanced::OnNavigatedTo(const NavigationEventArgs& e)
2326
{
24-
_Profile = e.Parameter().as<Editor::ProfileViewModel>();
27+
const auto args = e.Parameter().as<Editor::NavigateToProfileArgs>();
28+
_Profile = args.Profile();
29+
_windowRoot = args.WindowRoot();
30+
31+
//if (const auto& bellSounds = _Profile.CurrentBellSounds())
32+
//{
33+
// auto uiStack = BellSoundStack().Children();
34+
// const auto dataTemplate = Resources().Lookup(box_value(L"BellSoundEntryViewModelTemplate")).as<DataTemplate>();
35+
// for (const auto&& entry : bellSounds)
36+
// {
37+
// ContentControl ctrl;
38+
// ctrl.Content(entry);
39+
// ctrl.ContentTemplate(dataTemplate);
40+
//
41+
// uiStack.Append(ctrl);
42+
// }
43+
//}
2544
}
2645

2746
void Profiles_Advanced::OnNavigatedFrom(const NavigationEventArgs& /*e*/)
2847
{
2948
_ViewModelChangedRevoker.revoke();
3049
}
50+
51+
safe_void_coroutine Profiles_Advanced::BellSoundBrowse_Click(const IInspectable& sender, const RoutedEventArgs& /*e*/)
52+
{
53+
static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
54+
{ L"Sound Files (*.wav;*.mp3;*.flac)", L"*.wav;*.mp3;*.flac" },
55+
{ L"All Files (*.*)", L"*.*" }
56+
};
57+
58+
const auto parentHwnd{ reinterpret_cast<HWND>(WindowRoot().GetHostingWindow()) };
59+
auto file = co_await OpenFilePicker(parentHwnd, [](auto&& dialog) {
60+
try
61+
{
62+
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_Music, KF_FLAG_DEFAULT, nullptr) };
63+
dialog->SetDefaultFolder(folderShellItem.get());
64+
}
65+
CATCH_LOG(); // non-fatal
66+
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
67+
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
68+
THROW_IF_FAILED(dialog->SetDefaultExtension(L"wav;mp3;flac"));
69+
});
70+
if (!file.empty())
71+
{
72+
sender.as<Button>().Tag().as<Editor::BellSoundViewModel>().Path(file);
73+
}
74+
}
75+
76+
void Profiles_Advanced::BellSoundDelete_Click(const IInspectable& sender, const RoutedEventArgs& /*e*/)
77+
{
78+
auto bellSoundEntry = sender.as<Button>().Tag().as<Editor::BellSoundViewModel>();
79+
_Profile.RequestDeleteBellSound(bellSoundEntry);
80+
}
3181
}

src/cascadia/TerminalSettingsEditor/Profiles_Advanced.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
1717
void OnNavigatedTo(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
1818
void OnNavigatedFrom(const Windows::UI::Xaml::Navigation::NavigationEventArgs& e);
1919

20+
safe_void_coroutine BellSoundBrowse_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
21+
void BellSoundDelete_Click(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e);
22+
2023
til::property_changed_event PropertyChanged;
24+
Editor::IHostedInWindow WindowRoot() { return _windowRoot; };
2125
WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr);
2226

2327
private:
2428
Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker;
29+
Editor::IHostedInWindow _windowRoot;
2530
};
2631
};
2732

0 commit comments

Comments
 (0)