Skip to content

Commit

Permalink
Add Profile.BellSound to Settings UI (#17983)
Browse files Browse the repository at this point in the history
## Summary of the Pull Request
Adds the Profile.BellSound setting to the Settings UI under the Profile > Advanced page. 
- View changes:
   - The setting is exposed via an expander placed near the Profile.BellStyle setting.
   - Added a button to be able to preview the added sound
   - Added a browse button that opens a file picker
   - Added a delete button to be able to delete each sound entry
- View model changes:
   - `CurrentBellSounds` keeps track of the bell sounds added and exposed via the UI.
   - `BellSoundViewModel` wraps each sound. This allows us to listen (and propagate) changes to the registered sounds.
   - `BellSoundPreview` provides a written preview of the current bell sound to display in the expander

#10000
  • Loading branch information
carlos-zamora authored Dec 12, 2024
1 parent 8bbf00e commit 8ef77fb
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 11 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/allow/allow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dzhe
Emacspeak
Fitt
FTCS
flac
gantt
gfm
ghe
Expand Down
11 changes: 11 additions & 0 deletions src/cascadia/TerminalSettingsEditor/CommonResources.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@
<Setter Property="TextWrapping" Value="Wrap" />
</Style>

<!-- Text Block -->
<Style x:Key="TextBlockSettingStyle"
BasedOn="{StaticResource BaseTextBlockStyle}"
TargetType="TextBlock">
<Setter Property="MinWidth" Value="{StaticResource StandardBoxMinWidth}" />
<Setter Property="MaxWidth" Value="400" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>

<Style x:Key="TextBlockSubHeaderStyle"
BasedOn="{StaticResource SubtitleTextBlockStyle}"
TargetType="TextBlock">
Expand Down
12 changes: 6 additions & 6 deletions src/cascadia/TerminalSettingsEditor/MainPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ static const std::wstring_view globalAppearanceTag{ L"GlobalAppearance_Nav" };

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
static Editor::ProfileViewModel _viewModelForProfile(const Model::Profile& profile, const Model::CascadiaSettings& appSettings)
static Editor::ProfileViewModel _viewModelForProfile(const Model::Profile& profile, const Model::CascadiaSettings& appSettings, const Windows::UI::Core::CoreDispatcher& dispatcher)
{
return winrt::make<implementation::ProfileViewModel>(profile, appSettings);
return winrt::make<implementation::ProfileViewModel>(profile, appSettings, dispatcher);
}

MainPage::MainPage(const CascadiaSettings& settings) :
Expand Down Expand Up @@ -386,7 +386,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (currentPage == ProfileSubPage::Advanced)
{
contentFrame().Navigate(xaml_typename<Editor::Profiles_Advanced>(), profile);
contentFrame().Navigate(xaml_typename<Editor::Profiles_Advanced>(), winrt::make<implementation::NavigateToProfileArgs>(profile, *this));
const auto crumb = winrt::make<Breadcrumb>(breadcrumbTag, RS_(L"Profile_Advanced/Header"), BreadcrumbSubPage::Profile_Advanced);
_breadcrumbs.Append(crumb);
SettingsMainPage_ScrollViewer().ScrollToVerticalOffset(0);
Expand Down Expand Up @@ -447,7 +447,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
}
else if (clickedItemTag == globalProfileTag)
{
auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone) };
auto profileVM{ _viewModelForProfile(_settingsClone.ProfileDefaults(), _settingsClone, Dispatcher()) };
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
profileVM.IsBaseLayer(true);

Expand Down Expand Up @@ -649,7 +649,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
if (!profile.Deleted())
{
auto profileVM = _viewModelForProfile(profile, _settingsClone);
auto profileVM = _viewModelForProfile(profile, _settingsClone, Dispatcher());
profileVM.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
auto navItem = _CreateProfileNavViewItem(profileVM);
_menuItemSource.Append(navItem);
Expand Down Expand Up @@ -705,7 +705,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void MainPage::_CreateAndNavigateToNewProfile(const uint32_t index, const Model::Profile& profile)
{
const auto newProfile{ profile ? profile : _settingsClone.CreateNewProfile() };
const auto profileViewModel{ _viewModelForProfile(newProfile, _settingsClone) };
const auto profileViewModel{ _viewModelForProfile(newProfile, _settingsClone, Dispatcher()) };
profileViewModel.SetupAppearances(_colorSchemesPageVM.AllColorSchemes());
const auto navItem{ _CreateProfileNavViewItem(profileViewModel) };

Expand Down
183 changes: 180 additions & 3 deletions src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation

static constexpr std::wstring_view HideIconValue{ L"none" };

ProfileViewModel::ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& appSettings) :
ProfileViewModel::ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& appSettings, const Windows::UI::Core::CoreDispatcher& dispatcher) :
_profile{ profile },
_defaultAppearanceViewModel{ winrt::make<implementation::AppearanceViewModel>(profile.DefaultAppearance().try_as<AppearanceConfig>()) },
_originalProfileGuid{ profile.Guid() },
_appSettings{ appSettings },
_unfocusedAppearanceViewModel{ nullptr }
_unfocusedAppearanceViewModel{ nullptr },
_dispatcher{ dispatcher }
{
INITIALIZE_BINDABLE_ENUM_SETTING(AntiAliasingMode, TextAntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode, L"Profile_AntialiasingMode", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING_REVERSE_ORDER(CloseOnExitMode, CloseOnExitMode, winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode, L"Profile_CloseOnExit", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(ScrollState, ScrollbarState, winrt::Microsoft::Terminal::Control::ScrollbarState, L"Profile_ScrollbarVisibility", L"Content");
INITIALIZE_BINDABLE_ENUM_SETTING(PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle, L"Profile_PathTranslationStyle", L"Content");

_InitializeCurrentBellSounds();

// set up IconTypes
std::vector<IInspectable> iconTypes;
iconTypes.reserve(4);
Expand Down Expand Up @@ -112,6 +115,21 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
Icon(CurrentEmojiIcon());
}
else if (viewModelProperty == L"CurrentBellSounds")
{
// we already have infrastructure in place to
// propagate changes from the CurrentBellSounds
// to the model. Refer to...
// - _InitializeCurrentBellSounds() --> _CurrentBellSounds.VectorChanged()
// - RequestAddBellSound()
// - RequestDeleteBellSound()
_MarkDuplicateBellSoundDirectories();
_NotifyChanges(L"BellSoundPreview", L"HasBellSound");
}
else if (viewModelProperty == L"BellSound")
{
_InitializeCurrentBellSounds();
}
else if (viewModelProperty == L"PathTranslationStyle")
{
_NotifyChanges(L"CurrentPathTranslationStyle");
Expand Down Expand Up @@ -696,9 +714,168 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
BellStyle(currentStyle);
}

// Method Description:
// - Construct _CurrentBellSounds by importing the _inherited_ value from the model
// - Adds a PropertyChanged handler to each BellSoundViewModel to propagate changes to the model
void ProfileViewModel::_InitializeCurrentBellSounds()
{
_CurrentBellSounds = winrt::single_threaded_observable_vector<Editor::BellSoundViewModel>();
if (const auto soundList = _profile.BellSound())
{
for (const auto&& bellSound : soundList)
{
_CurrentBellSounds.Append(winrt::make<BellSoundViewModel>(bellSound));
}
}
_MarkDuplicateBellSoundDirectories();
_CheckBellSoundsExistence();
_NotifyChanges(L"CurrentBellSounds");
}

// Method Description:
// - If the current layer is inheriting the bell sound from its parent,
// we need to copy the _inherited_ bell sound list to the current layer
// so that we can then apply modifications to it
void ProfileViewModel::_PrepareModelForBellSoundModification()
{
if (!_profile.HasBellSound())
{
std::vector<hstring> newSounds;
if (const auto inheritedSounds = _profile.BellSound())
{
// copy inherited bell sounds to the current layer
newSounds.reserve(inheritedSounds.Size());
for (const auto sound : inheritedSounds)
{
newSounds.push_back(sound);
}
}
// if we didn't inherit any bell sounds,
// we should still set the bell sound to an empty list (instead of null)
_profile.BellSound(winrt::single_threaded_vector<hstring>(std::move(newSounds)));
}
}

// Method Description:
// - Check if any bell sounds share the same name.
// If they do, mark them so that they show the directory path in the UI
void ProfileViewModel::_MarkDuplicateBellSoundDirectories()
{
for (uint32_t i = 0; i < _CurrentBellSounds.Size(); i++)
{
auto soundA = _CurrentBellSounds.GetAt(i);
for (uint32_t j = i + 1; j < _CurrentBellSounds.Size(); j++)
{
auto soundB = _CurrentBellSounds.GetAt(j);
if (soundA.DisplayPath() == soundB.DisplayPath())
{
get_self<BellSoundViewModel>(soundA)->ShowDirectory(true);
get_self<BellSoundViewModel>(soundB)->ShowDirectory(true);
}
}
}
}

// Method Description:
// - Check if the bell sounds exist on disk. Mark any that don't exist
// so that they show the appropriate UI
safe_void_coroutine ProfileViewModel::_CheckBellSoundsExistence()
{
co_await winrt::resume_background();
std::vector<Editor::BellSoundViewModel> markedSounds;
for (auto&& sound : _CurrentBellSounds)
{
if (!std::filesystem::exists(std::wstring_view{ sound.Path() }))
{
markedSounds.push_back(sound);
}
}

co_await winrt::resume_foreground(_dispatcher);
for (auto&& sound : markedSounds)
{
get_self<BellSoundViewModel>(sound)->FileExists(false);
}
}

BellSoundViewModel::BellSoundViewModel(hstring path) :
_Path{ path }
{
PropertyChanged([this](auto&&, const PropertyChangedEventArgs& args) {
if (args.PropertyName() == L"FileExists")
{
_NotifyChanges(L"DisplayPath", L"SubText");
}
});
}

hstring BellSoundViewModel::DisplayPath() const
{
if (_FileExists)
{
// filename
const std::filesystem::path filePath{ std::wstring_view{ _Path } };
return hstring{ filePath.filename().wstring() };
}
return _Path;
}

hstring BellSoundViewModel::SubText() const
{
if (_FileExists)
{
// Directory
const std::filesystem::path filePath{ std::wstring_view{ _Path } };
return hstring{ filePath.parent_path().wstring() };
}
return RS_(L"Profile_BellSoundNotFound");
}

hstring ProfileViewModel::BellSoundPreview()
{
const auto& currentSound = BellSound();
if (!currentSound || currentSound.Size() == 0)
{
return RS_(L"Profile_BellSoundPreviewDefault");
}
else if (currentSound.Size() == 1)
{
std::filesystem::path filePath{ std::wstring_view{ currentSound.GetAt(0) } };
return hstring{ filePath.filename().wstring() };
}
return RS_(L"Profile_BellSoundPreviewMultiple");
}

void ProfileViewModel::RequestAddBellSound(hstring path)
{
// If we were inheriting our bell sound,
// copy it over to the current layer and apply modifications
_PrepareModelForBellSoundModification();

// No need to check if the file exists. We came from the FilePicker. That's good enough.
_CurrentBellSounds.Append(winrt::make<BellSoundViewModel>(path));
_profile.BellSound().Append(path);
_NotifyChanges(L"CurrentBellSounds");
}

void ProfileViewModel::RequestDeleteBellSound(const Editor::BellSoundViewModel& vm)
{
uint32_t index;
if (_CurrentBellSounds.IndexOf(vm, index))
{
// If we were inheriting our bell sound,
// copy it over to the current layer and apply modifications
_PrepareModelForBellSoundModification();

_CurrentBellSounds.RemoveAt(index);
_profile.BellSound().RemoveAt(index);
_NotifyChanges(L"CurrentBellSounds");
}
}

void ProfileViewModel::DeleteProfile()
{
auto deleteProfileArgs{ winrt::make_self<DeleteProfileEventArgs>(Guid()) };
const auto deleteProfileArgs{ winrt::make_self<DeleteProfileEventArgs>(Guid()) };
DeleteProfileRequested.raise(*this, *deleteProfileArgs);
}

Expand Down
26 changes: 25 additions & 1 deletion src/cascadia/TerminalSettingsEditor/ProfileViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "DeleteProfileEventArgs.g.h"
#include "NavigateToProfileArgs.g.h"
#include "BellSoundViewModel.g.h"
#include "ProfileViewModel.g.h"
#include "Utils.h"
#include "ViewModelHelpers.h"
Expand All @@ -26,6 +27,18 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
Editor::ProfileViewModel _Profile{ nullptr };
};

struct BellSoundViewModel : BellSoundViewModelT<BellSoundViewModel>, ViewModelHelper<BellSoundViewModel>
{
public:
BellSoundViewModel(hstring path);

hstring DisplayPath() const;
hstring SubText() const;
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, FileExists, true);
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, Path);
VIEW_MODEL_OBSERVABLE_PROPERTY(bool, ShowDirectory);
};

struct ProfileViewModel : ProfileViewModelT<ProfileViewModel>, ViewModelHelper<ProfileViewModel>
{
public:
Expand All @@ -35,7 +48,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
static Windows::Foundation::Collections::IObservableVector<Editor::Font> MonospaceFontList() noexcept { return _MonospaceFontList; };
static Windows::Foundation::Collections::IVector<IInspectable> BuiltInIcons() noexcept { return _BuiltInIcons; };

ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings);
ProfileViewModel(const Model::Profile& profile, const Model::CascadiaSettings& settings, const Windows::UI::Core::CoreDispatcher& dispatcher);
Model::TerminalSettings TermSettings() const;
void DeleteProfile();

Expand All @@ -47,6 +60,10 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
void SetBellStyleWindow(winrt::Windows::Foundation::IReference<bool> on);
void SetBellStyleTaskbar(winrt::Windows::Foundation::IReference<bool> on);

hstring BellSoundPreview();
void RequestAddBellSound(hstring path);
void RequestDeleteBellSound(const Editor::BellSoundViewModel& vm);

void SetAcrylicOpacityPercentageValue(double value)
{
Opacity(static_cast<float>(value) / 100.0f);
Expand Down Expand Up @@ -102,6 +119,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
til::typed_event<Editor::ProfileViewModel, Editor::DeleteProfileEventArgs> DeleteProfileRequested;

VIEW_MODEL_OBSERVABLE_PROPERTY(ProfileSubPage, CurrentPage);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::Collections::IObservableVector<Editor::BellSoundViewModel>, CurrentBellSounds);
VIEW_MODEL_OBSERVABLE_PROPERTY(Windows::Foundation::IInspectable, CurrentBuiltInIcon);
VIEW_MODEL_OBSERVABLE_PROPERTY(hstring, CurrentEmojiIcon);

Expand All @@ -126,6 +144,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
OBSERVABLE_PROJECTED_SETTING(_profile, SnapOnInput);
OBSERVABLE_PROJECTED_SETTING(_profile, AltGrAliasing);
OBSERVABLE_PROJECTED_SETTING(_profile, BellStyle);
OBSERVABLE_PROJECTED_SETTING(_profile, BellSound);
OBSERVABLE_PROJECTED_SETTING(_profile, Elevate);
OBSERVABLE_PROJECTED_SETTING(_profile, ReloadEnvironmentVariables);
OBSERVABLE_PROJECTED_SETTING(_profile, RightClickContextMenu);
Expand Down Expand Up @@ -154,7 +173,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
winrt::hstring _lastIconPath;
Windows::Foundation::IInspectable _currentIconType{};
Editor::AppearanceViewModel _defaultAppearanceViewModel;
Windows::UI::Core::CoreDispatcher _dispatcher;

void _InitializeCurrentBellSounds();
void _PrepareModelForBellSoundModification();
void _MarkDuplicateBellSoundDirectories();
safe_void_coroutine _CheckBellSoundsExistence();
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _MonospaceFontList;
static Windows::Foundation::Collections::IObservableVector<Editor::Font> _FontList;
static Windows::Foundation::Collections::IVector<Windows::Foundation::IInspectable> _BuiltInIcons;
Expand Down
Loading

0 comments on commit 8ef77fb

Please sign in to comment.