Add the instrument names to the MIDI channel selector (#38083)

* Add the instrument to the MIDI channel selector

* Reviews

Adds support for chained masters
Makes the channel UI update on its own when the midi changes (Works with bands too!)

* add to admin logs and limit track count

* Limit track names by length too

* remove left over comment

* Requested changes

* Reviews
This commit is contained in:
Simon
2025-06-11 20:32:48 +02:00
committed by GitHub
parent 27cb97a17c
commit f5fbef7ccc
14 changed files with 882 additions and 15 deletions

View File

@@ -7,5 +7,7 @@
<Button Name="AllButton" Text="{Loc 'instruments-component-channels-all-button'}" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="1"/>
<Button Name="ClearButton" Text="{Loc 'instruments-component-channels-clear-button'}" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="1"/>
</BoxContainer>
<CheckButton Name="DisplayTrackNames"
Text="{Loc 'instruments-component-channels-track-names-toggle'}" />
</BoxContainer>
</DefaultWindow>

View File

@@ -1,26 +1,56 @@
using Content.Shared.Instruments;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Audio.Midi;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Instruments.UI;
[GenerateTypedNameReferences]
public sealed partial class ChannelsMenu : DefaultWindow
{
[Dependency] private readonly IEntityManager _entityManager = null!;
private readonly InstrumentBoundUserInterface _owner;
public ChannelsMenu(InstrumentBoundUserInterface owner) : base()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_owner = owner;
ChannelList.OnItemSelected += OnItemSelected;
ChannelList.OnItemDeselected += OnItemDeselected;
AllButton.OnPressed += OnAllPressed;
ClearButton.OnPressed += OnClearPressed;
DisplayTrackNames.OnPressed += OnDisplayTrackNamesPressed;
}
protected override void EnteredTree()
{
base.EnteredTree();
_owner.Instruments.OnChannelsUpdated += UpdateChannelList;
}
private void OnDisplayTrackNamesPressed(BaseButton.ButtonEventArgs obj)
{
DisplayTrackNames.SetClickPressed(!DisplayTrackNames.Pressed);
Populate();
}
private void UpdateChannelList()
{
Populate(); // This is kind of in-efficent because we don't filter for which instrument updated its channels, but idc
}
protected override void ExitedTree()
{
base.ExitedTree();
_owner.Instruments.OnChannelsUpdated -= UpdateChannelList;
}
private void OnItemSelected(ItemList.ItemListSelectedEventArgs args)
@@ -51,15 +81,71 @@ public sealed partial class ChannelsMenu : DefaultWindow
}
}
public void Populate(InstrumentComponent? instrument)
/// <summary>
/// Walks up the tree of instrument masters to find the truest master of them all.
/// </summary>
private ActiveInstrumentComponent ResolveActiveInstrument(InstrumentComponent? comp)
{
comp ??= _entityManager.GetComponent<InstrumentComponent>(_owner.Owner);
var instrument = new Entity<InstrumentComponent>(_owner.Owner, comp);
while (true)
{
if (instrument.Comp.Master == null)
break;
instrument = new Entity<InstrumentComponent>((EntityUid)instrument.Comp.Master,
_entityManager.GetComponent<InstrumentComponent>((EntityUid)instrument.Comp.Master));
}
return _entityManager.GetComponent<ActiveInstrumentComponent>(instrument.Owner);
}
public void Populate()
{
ChannelList.Clear();
var instrument = _entityManager.GetComponent<InstrumentComponent>(_owner.Owner);
var activeInstrument = ResolveActiveInstrument(instrument);
for (int i = 0; i < RobustMidiEvent.MaxChannels; i++)
{
var item = ChannelList.AddItem(_owner.Loc.GetString("instrument-component-channel-name",
("number", i)), null, true, i);
var label = _owner.Loc.GetString("instrument-component-channel-name",
("number", i));
if (activeInstrument != null
&& activeInstrument.Tracks.TryGetValue(i, out var resolvedMidiChannel)
&& resolvedMidiChannel != null)
{
if (DisplayTrackNames.Pressed)
{
label = resolvedMidiChannel switch
{
{ TrackName: not null, InstrumentName: not null } =>
Loc.GetString("instruments-component-channels-multi",
("channel", i),
("name", resolvedMidiChannel.TrackName),
("other", resolvedMidiChannel.InstrumentName)),
{ TrackName: not null } =>
Loc.GetString("instruments-component-channels-single",
("channel", i),
("name", resolvedMidiChannel.TrackName)),
_ => label,
};
}
else
{
label = resolvedMidiChannel switch
{
{ ProgramName: not null } =>
Loc.GetString("instruments-component-channels-single",
("channel", i),
("name", resolvedMidiChannel.ProgramName)),
_ => label,
};
}
}
var item = ChannelList.AddItem(label, null, true, i);
item.Selected = !instrument?.FilteredChannels[i] ?? false;
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.ActionBlocker;
using Content.Shared.Instruments;
using Content.Shared.Instruments.UI;
using Content.Shared.Interaction;
using Robust.Client.Audio.Midi;
@@ -101,9 +102,7 @@ namespace Content.Client.Instruments.UI
public void OpenChannelsMenu()
{
_channelsMenu ??= new ChannelsMenu(this);
EntMan.TryGetComponent(Owner, out InstrumentComponent? instrument);
_channelsMenu.Populate(instrument);
_channelsMenu.Populate();
_channelsMenu.OpenCenteredRight();
}

View File

@@ -11,6 +11,7 @@ using Robust.Client.UserInterface.XAML;
using Robust.Shared.Containers;
using Robust.Shared.Input;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BaseButton;
using Range = Robust.Client.UserInterface.Controls.Range;
@@ -145,10 +146,6 @@ namespace Content.Client.Instruments.UI
if (!PlayCheck())
return;
await using var memStream = new MemoryStream((int) file.Length);
await file.CopyToAsync(memStream);
if (!_entManager.TryGetComponent<InstrumentComponent>(Entity, out var instrument))
{
return;
@@ -156,7 +153,7 @@ namespace Content.Client.Instruments.UI
if (!_entManager.System<InstrumentSystem>()
.OpenMidi(Entity,
memStream.GetBuffer().AsSpan(0, (int) memStream.Length),
file.CopyToArray(),
instrument))
{
return;