From f99c6bd2da01bd61471d7b258c8aac9737b291a5 Mon Sep 17 00:00:00 2001 From: AleGrz Date: Thu, 14 Dec 2023 18:01:51 +0100 Subject: [PATCH 1/5] feat: added music status component --- GlazeWM.Bar/BarViewModel.cs | 1 + GlazeWM.Bar/Components/ComponentPortal.xaml | 5 + GlazeWM.Bar/Components/MusicComponent.xaml | 10 ++ GlazeWM.Bar/Components/MusicComponent.xaml.cs | 15 +++ .../Components/MusicComponentViewModel.cs | 114 ++++++++++++++++++ GlazeWM.Bar/GlazeWM.Bar.csproj | 6 + .../BarComponentConfigConverter.cs | 4 + .../UserConfigs/MusicComponentConfig.cs | 18 +++ 8 files changed, 173 insertions(+) create mode 100644 GlazeWM.Bar/Components/MusicComponent.xaml create mode 100644 GlazeWM.Bar/Components/MusicComponent.xaml.cs create mode 100644 GlazeWM.Bar/Components/MusicComponentViewModel.cs create mode 100644 GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs diff --git a/GlazeWM.Bar/BarViewModel.cs b/GlazeWM.Bar/BarViewModel.cs index 84ccd8e5a..d3bf00df4 100644 --- a/GlazeWM.Bar/BarViewModel.cs +++ b/GlazeWM.Bar/BarViewModel.cs @@ -111,6 +111,7 @@ private List CreateComponentViewModels( GpuComponentConfig gpupc => new GpuComponentViewModel(this, gpupc), MemoryComponentConfig rampc => new MemoryComponentViewModel(this, rampc), TextFileComponentConfig stc => new TextFileComponentViewModel(this, stc), + MusicComponentConfig mcc => new MusicComponentViewModel(this, mcc), _ => throw new ArgumentOutOfRangeException(nameof(config)), }); } diff --git a/GlazeWM.Bar/Components/ComponentPortal.xaml b/GlazeWM.Bar/Components/ComponentPortal.xaml index 11cc2d7f6..13d3e463a 100644 --- a/GlazeWM.Bar/Components/ComponentPortal.xaml +++ b/GlazeWM.Bar/Components/ComponentPortal.xaml @@ -96,6 +96,11 @@ Padding="{Binding Padding}" Background="{Binding Background}" /> + + + diff --git a/GlazeWM.Bar/Components/MusicComponent.xaml b/GlazeWM.Bar/Components/MusicComponent.xaml new file mode 100644 index 000000000..f46fdc891 --- /dev/null +++ b/GlazeWM.Bar/Components/MusicComponent.xaml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/GlazeWM.Bar/Components/MusicComponent.xaml.cs b/GlazeWM.Bar/Components/MusicComponent.xaml.cs new file mode 100644 index 000000000..eb0b03e0d --- /dev/null +++ b/GlazeWM.Bar/Components/MusicComponent.xaml.cs @@ -0,0 +1,15 @@ +using System.Windows.Controls; + +namespace GlazeWM.Bar.Components +{ + /// + /// Interaction logic for MusicComponent.xaml + /// + public partial class MusicComponent : UserControl + { + public MusicComponent() + { + InitializeComponent(); + } + } +} diff --git a/GlazeWM.Bar/Components/MusicComponentViewModel.cs b/GlazeWM.Bar/Components/MusicComponentViewModel.cs new file mode 100644 index 000000000..39bc76c76 --- /dev/null +++ b/GlazeWM.Bar/Components/MusicComponentViewModel.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using GlazeWM.Domain.UserConfigs; +using Windows.Media.Control; +using WindowsMediaController; + +namespace GlazeWM.Bar.Components +{ + public class MusicComponentViewModel : ComponentViewModel + { + private readonly MusicComponentConfig _config; + private static MediaManager mediaManager; + private LabelViewModel _label; + public LabelViewModel Label + { + get => _label; + protected set => SetField(ref _label, value); + } + public struct SongSession + { + public SongSession(GlobalSystemMediaTransportControlsSessionPlaybackStatus musicStatus, string songTitle, string artistName) + { + SongTitle = songTitle; + ArtistName = artistName; + MusicStatus = musicStatus; + } + public string SongTitle { get; set; } + public string ArtistName { get; set; } + public GlobalSystemMediaTransportControlsSessionPlaybackStatus MusicStatus { get; set; } + } + private readonly Dictionary songSessionDict; + public MusicComponentViewModel( + BarViewModel parentViewModel, + MusicComponentConfig config) : base(parentViewModel, config) + { + songSessionDict = new Dictionary(); + _config = config; + mediaManager = new MediaManager(); + mediaManager.OnAnyMediaPropertyChanged += (session, args) => MusicTitleChanged(session.Id, args.Artist, args.Title); + mediaManager.OnAnyPlaybackStateChanged += (session, args) => PlaybackStateChanged(session.Id, args.PlaybackStatus); + mediaManager.OnAnySessionOpened += (session) => OpenedSession(session.Id); + mediaManager.OnAnySessionClosed += (session) => ClosedSession(session.Id); + mediaManager.Start(); + } + private string GetLabel(GlobalSystemMediaTransportControlsSessionPlaybackStatus musicStatus) + { + return musicStatus switch + { + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => _config.LabelPlaying, + GlobalSystemMediaTransportControlsSessionPlaybackStatus.Paused => _config.LabelPaused, + _ => _config.LabelNotPlaying, + }; + } + private void MusicTitleChanged(string sessionId, string artistName, string songTitle) + { + var session = songSessionDict[sessionId]; + session.ArtistName = artistName; + session.SongTitle = songTitle; + songSessionDict[sessionId] = session; + Label = CreateLabel(); + } + private void PlaybackStateChanged(string sessionId, GlobalSystemMediaTransportControlsSessionPlaybackStatus status) + { + var session = songSessionDict[sessionId]; + session.MusicStatus = status; + songSessionDict[sessionId] = session; + Label = CreateLabel(); + } + private void OpenedSession(string sessionId) + { + var session = new SongSession(GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing, "", ""); + songSessionDict.Add(sessionId, session); + } + private void ClosedSession(string sessionId) + { + songSessionDict.Remove(sessionId); + Label = CreateLabel(); + } + private LabelViewModel CreateLabel() + { + var label = _config.LabelNotPlaying; + var variableDictionary = new Dictionary>() + { + { + "song_title", () => "" + }, + { + "artist_name", () => "" + } + }; + + foreach (var session in songSessionDict) + { + if(GetLabel(session.Value.MusicStatus) == _config.LabelPaused && label != _config.LabelPlaying) + { + label = _config.LabelPaused; + variableDictionary["song_title"] = () => session.Value.SongTitle; + variableDictionary["artist_name"] = () => session.Value.ArtistName; + } + else if (GetLabel(session.Value.MusicStatus) == _config.LabelPlaying) + { + label = _config.LabelPlaying; + variableDictionary["song_title"] = () => session.Value.SongTitle; + variableDictionary["artist_name"] = () => session.Value.ArtistName; + } + } + return XamlHelper.ParseLabel( + label, + variableDictionary, + this + ); + } + } +} diff --git a/GlazeWM.Bar/GlazeWM.Bar.csproj b/GlazeWM.Bar/GlazeWM.Bar.csproj index 88c1dfe8b..379245223 100644 --- a/GlazeWM.Bar/GlazeWM.Bar.csproj +++ b/GlazeWM.Bar/GlazeWM.Bar.csproj @@ -18,6 +18,12 @@ Version="1.1.39" /> + + + 2.5.2 + + + MSBuild:Compile diff --git a/GlazeWM.Domain/UserConfigs/BarComponentConfigConverter.cs b/GlazeWM.Domain/UserConfigs/BarComponentConfigConverter.cs index 7d6e3bf97..c0e18f4f5 100644 --- a/GlazeWM.Domain/UserConfigs/BarComponentConfigConverter.cs +++ b/GlazeWM.Domain/UserConfigs/BarComponentConfigConverter.cs @@ -97,6 +97,10 @@ public override BarComponentConfig Read( jsonObject.RootElement.ToString(), options ), + "music" => JsonSerializer.Deserialize( + jsonObject.RootElement.ToString(), + options + ), _ => throw new ArgumentException($"Invalid component type '{typeDiscriminator}'."), }; } diff --git a/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs b/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs new file mode 100644 index 000000000..962b2db24 --- /dev/null +++ b/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs @@ -0,0 +1,18 @@ +namespace GlazeWM.Domain.UserConfigs +{ + public class MusicComponentConfig : BarComponentConfig + { + /// + /// Formatted text to display when music is not playing. + /// + public string LabelNotPlaying { get; set; } = ""; + /// + /// Formatted text to display when music is not paused. + /// + public string LabelPaused { get; set; } = ""; + /// + /// Formatted text to display when music is playing. + /// + public string LabelPlaying { get; set; } = "{song_title} - {artist_name}"; + } +} From 73379059b0759c42479bd6fc263319f37829d00a Mon Sep 17 00:00:00 2001 From: AleGrz Date: Thu, 14 Dec 2023 18:38:16 +0100 Subject: [PATCH 2/5] docs: added music component to README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index d50f7e45f..df8c69670 100644 --- a/README.md +++ b/README.md @@ -444,6 +444,17 @@ Use `Alt+Click` to pin and un-pin an icon. label_collapse_text: ">" ``` +### Bar Component: Music + +Displays currently playing music. + +```yaml +- type: "musc" + label_not_playing: "" + label_paused: "{song_title} - {artist name}" + label_playing: "{song_title} - {artist name} ▶" +``` + ## Mixing font properties within a label Font family, font weight, font size, and foreground color can be changed within parts of a label. This means that icons and text fonts can be used together in a label. To customize a part of the label, wrap it in an tag: From e6b09eec1bae6fbc91c69d8cbc0bedb9db7f4b88 Mon Sep 17 00:00:00 2001 From: Aleksander Grzybek <114233371+AleGrz@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:54:41 +0100 Subject: [PATCH 3/5] Fixed typos in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index df8c69670..3ee8b2131 100644 --- a/README.md +++ b/README.md @@ -449,10 +449,10 @@ Use `Alt+Click` to pin and un-pin an icon. Displays currently playing music. ```yaml -- type: "musc" +- type: "music" label_not_playing: "" - label_paused: "{song_title} - {artist name}" - label_playing: "{song_title} - {artist name} ▶" + label_paused: "{song_title} - {artist_name}" + label_playing: "{song_title} - {artist_name} ▶" ``` ## Mixing font properties within a label From ee4cebafd0c00a31cf4c8a0a2bc90ca83fb309b3 Mon Sep 17 00:00:00 2001 From: AleGrz Date: Mon, 18 Dec 2023 20:33:33 +0100 Subject: [PATCH 4/5] feat: added trimming to song title and artist name --- .../Components/MusicComponentViewModel.cs | 18 +++++++++++++----- .../UserConfigs/MusicComponentConfig.cs | 8 ++++++++ README.md | 2 ++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/GlazeWM.Bar/Components/MusicComponentViewModel.cs b/GlazeWM.Bar/Components/MusicComponentViewModel.cs index 39bc76c76..77b0ea1f3 100644 --- a/GlazeWM.Bar/Components/MusicComponentViewModel.cs +++ b/GlazeWM.Bar/Components/MusicComponentViewModel.cs @@ -91,17 +91,19 @@ private LabelViewModel CreateLabel() foreach (var session in songSessionDict) { - if(GetLabel(session.Value.MusicStatus) == _config.LabelPaused && label != _config.LabelPlaying) + var title = Truncate(session.Value.SongTitle, _config.MaxTitleLength); + var artist = Truncate(session.Value.ArtistName, _config.MaxArtistLength); + if (GetLabel(session.Value.MusicStatus) == _config.LabelPaused && label != _config.LabelPlaying) { label = _config.LabelPaused; - variableDictionary["song_title"] = () => session.Value.SongTitle; - variableDictionary["artist_name"] = () => session.Value.ArtistName; + variableDictionary["song_title"] = () => title; + variableDictionary["artist_name"] = () => artist; } else if (GetLabel(session.Value.MusicStatus) == _config.LabelPlaying) { label = _config.LabelPlaying; - variableDictionary["song_title"] = () => session.Value.SongTitle; - variableDictionary["artist_name"] = () => session.Value.ArtistName; + variableDictionary["song_title"] = () => title; + variableDictionary["artist_name"] = () => artist; } } return XamlHelper.ParseLabel( @@ -110,5 +112,11 @@ private LabelViewModel CreateLabel() this ); } + public static string Truncate(string value, int maxLength, string truncationSuffix = "…") + { + return value?.Length > maxLength && maxLength >= 0 + ? string.Concat(value.AsSpan(0, maxLength), truncationSuffix) + : value; + } } } diff --git a/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs b/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs index 962b2db24..c7164ebe4 100644 --- a/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs +++ b/GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs @@ -14,5 +14,13 @@ public class MusicComponentConfig : BarComponentConfig /// Formatted text to display when music is playing. /// public string LabelPlaying { get; set; } = "{song_title} - {artist_name}"; + /// + /// The maximum length after which the song title will be truncated; if set to -1, the title will not be truncated. + /// + public int MaxTitleLength { get; set; } = -1; + /// + /// The maximum length after which the artist name will be truncated; if set to -1, the name will not be truncated. + /// + public int MaxArtistLength { get; set; } = -1; } } diff --git a/README.md b/README.md index 3ee8b2131..394bc7bac 100644 --- a/README.md +++ b/README.md @@ -453,6 +453,8 @@ Displays currently playing music. label_not_playing: "" label_paused: "{song_title} - {artist_name}" label_playing: "{song_title} - {artist_name} ▶" + max_title_length: 20 + max_artist_length: 20 ``` ## Mixing font properties within a label From 1554404277d4d70fc79e6b33e82c59047136209f Mon Sep 17 00:00:00 2001 From: AleGrz Date: Tue, 19 Dec 2023 19:39:41 +0100 Subject: [PATCH 5/5] fix: added newline to the end of MusicComponent.xaml --- GlazeWM.Bar/Components/MusicComponent.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GlazeWM.Bar/Components/MusicComponent.xaml b/GlazeWM.Bar/Components/MusicComponent.xaml index f46fdc891..51710d601 100644 --- a/GlazeWM.Bar/Components/MusicComponent.xaml +++ b/GlazeWM.Bar/Components/MusicComponent.xaml @@ -7,4 +7,4 @@ xmlns:components="clr-namespace:GlazeWM.Bar.Components" mc:Ignorable="d"> - \ No newline at end of file +