Skip to content

Commit

Permalink
feat: added music status component (#473)
Browse files Browse the repository at this point in the history
  • Loading branch information
AleGrz authored Dec 21, 2023
1 parent 872c2fa commit 537edda
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 0 deletions.
1 change: 1 addition & 0 deletions GlazeWM.Bar/BarViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ private List<ComponentViewModel> 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)),
});
}
Expand Down
5 changes: 5 additions & 0 deletions GlazeWM.Bar/Components/ComponentPortal.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@
Padding="{Binding Padding}"
Background="{Binding Background}" />
</DataTemplate>
<DataTemplate DataType="{x:Type components:MusicComponentViewModel}">
<components:MusicComponent
Padding="{Binding Padding}"
Background="{Binding Background}" />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
Expand Down
10 changes: 10 additions & 0 deletions GlazeWM.Bar/Components/MusicComponent.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<UserControl
x:Class="GlazeWM.Bar.Components.MusicComponent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:components="clr-namespace:GlazeWM.Bar.Components"
mc:Ignorable="d">
<components:LabelComponent DataContext="{Binding Label}" />
</UserControl>
15 changes: 15 additions & 0 deletions GlazeWM.Bar/Components/MusicComponent.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Windows.Controls;

namespace GlazeWM.Bar.Components
{
/// <summary>
/// Interaction logic for MusicComponent.xaml
/// </summary>
public partial class MusicComponent : UserControl
{
public MusicComponent()
{
InitializeComponent();
}
}
}
122 changes: 122 additions & 0 deletions GlazeWM.Bar/Components/MusicComponentViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
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<string, SongSession> songSessionDict;
public MusicComponentViewModel(
BarViewModel parentViewModel,
MusicComponentConfig config) : base(parentViewModel, config)
{
songSessionDict = new Dictionary<string, SongSession>();
_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<string, Func<string>>()
{
{
"song_title", () => ""
},
{
"artist_name", () => ""
}
};

foreach (var session in songSessionDict)
{
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"] = () => title;
variableDictionary["artist_name"] = () => artist;
}
else if (GetLabel(session.Value.MusicStatus) == _config.LabelPlaying)
{
label = _config.LabelPlaying;
variableDictionary["song_title"] = () => title;
variableDictionary["artist_name"] = () => artist;
}
}
return XamlHelper.ParseLabel(
label,
variableDictionary,
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;
}
}
}
6 changes: 6 additions & 0 deletions GlazeWM.Bar/GlazeWM.Bar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
Version="1.1.39" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Dubya.WindowsMediaController">
<Version>2.5.2</Version>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Page Update="Components\TextFileComponent.xaml">
<Generator>MSBuild:Compile</Generator>
Expand Down
4 changes: 4 additions & 0 deletions GlazeWM.Domain/UserConfigs/BarComponentConfigConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ public override BarComponentConfig Read(
jsonObject.RootElement.ToString(),
options
),
"music" => JsonSerializer.Deserialize<MusicComponentConfig>(
jsonObject.RootElement.ToString(),
options
),
_ => throw new ArgumentException($"Invalid component type '{typeDiscriminator}'."),
};
}
Expand Down
26 changes: 26 additions & 0 deletions GlazeWM.Domain/UserConfigs/MusicComponentConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace GlazeWM.Domain.UserConfigs
{
public class MusicComponentConfig : BarComponentConfig
{
/// <summary>
/// Formatted text to display when music is not playing.
/// </summary>
public string LabelNotPlaying { get; set; } = "";
/// <summary>
/// Formatted text to display when music is not paused.
/// </summary>
public string LabelPaused { get; set; } = "";
/// <summary>
/// Formatted text to display when music is playing.
/// </summary>
public string LabelPlaying { get; set; } = "{song_title} - {artist_name}";
/// <summary>
/// The maximum length after which the song title will be truncated; if set to -1, the title will not be truncated.
/// </summary>
public int MaxTitleLength { get; set; } = -1;
/// <summary>
/// The maximum length after which the artist name will be truncated; if set to -1, the name will not be truncated.
/// </summary>
public int MaxArtistLength { get; set; } = -1;
}
}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,19 @@ Use `Alt+Click` to pin and un-pin an icon.
label_collapse_text: ">"
```

### Bar Component: Music

Displays currently playing music.

```yaml
- type: "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

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 <attr> tag:
Expand Down

0 comments on commit 537edda

Please sign in to comment.