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

feat: added music status component #473

Merged
merged 5 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
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