2020-11-25 18:00:18 +01:00
|
|
|
using System.IO;
|
2023-07-08 14:08:32 +10:00
|
|
|
using System.Numerics;
|
2020-11-25 18:00:18 +01:00
|
|
|
using System.Threading.Tasks;
|
2021-02-01 16:49:32 +01:00
|
|
|
using Robust.Client.AutoGenerated;
|
2019-11-25 00:11:47 +01:00
|
|
|
using Robust.Client.UserInterface;
|
|
|
|
|
using Robust.Client.UserInterface.CustomControls;
|
2021-02-01 16:49:32 +01:00
|
|
|
using Robust.Client.UserInterface.XAML;
|
2020-06-25 15:27:22 +02:00
|
|
|
using Robust.Shared.Containers;
|
2021-02-04 13:37:26 +01:00
|
|
|
using Robust.Shared.Input;
|
|
|
|
|
using Robust.Shared.Timing;
|
2021-03-10 14:48:29 +01:00
|
|
|
using static Robust.Client.UserInterface.Controls.BaseButton;
|
2021-02-04 13:37:26 +01:00
|
|
|
using Range = Robust.Client.UserInterface.Controls.Range;
|
2019-11-25 00:11:47 +01:00
|
|
|
|
2021-06-09 22:19:39 +02:00
|
|
|
namespace Content.Client.Instruments.UI
|
2019-11-25 00:11:47 +01:00
|
|
|
{
|
2021-02-01 16:49:32 +01:00
|
|
|
[GenerateTypedNameReferences]
|
2022-02-16 00:23:23 -07:00
|
|
|
public sealed partial class InstrumentMenu : DefaultWindow
|
2019-11-25 00:11:47 +01:00
|
|
|
{
|
2020-11-21 14:02:00 +01:00
|
|
|
private readonly InstrumentBoundUserInterface _owner;
|
2019-11-25 00:11:47 +01:00
|
|
|
|
2024-01-04 02:19:22 +00:00
|
|
|
private bool _isMidiFileDialogueWindowOpen;
|
|
|
|
|
|
2019-11-25 00:11:47 +01:00
|
|
|
public InstrumentMenu(InstrumentBoundUserInterface owner)
|
|
|
|
|
{
|
2021-02-01 16:49:32 +01:00
|
|
|
RobustXamlLoader.Load(this);
|
2019-11-25 00:11:47 +01:00
|
|
|
|
|
|
|
|
_owner = owner;
|
|
|
|
|
|
2021-03-10 14:48:29 +01:00
|
|
|
if (_owner.Instrument != null)
|
|
|
|
|
{
|
|
|
|
|
_owner.Instrument.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
|
2023-07-16 21:12:53 +02:00
|
|
|
Title = _owner.Entities.GetComponent<MetaDataComponent>(_owner.Owner).EntityName;
|
2021-03-10 14:48:29 +01:00
|
|
|
LoopButton.Disabled = !_owner.Instrument.IsMidiOpen;
|
|
|
|
|
LoopButton.Pressed = _owner.Instrument.LoopMidi;
|
2023-07-16 21:12:53 +02:00
|
|
|
ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive;
|
2021-03-10 14:48:29 +01:00
|
|
|
StopButton.Disabled = !_owner.Instrument.IsMidiOpen;
|
|
|
|
|
PlaybackSlider.MouseFilter = _owner.Instrument.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
|
|
|
|
|
}
|
2019-11-25 00:11:47 +01:00
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
if (!_owner.MidiManager.IsAvailable)
|
2020-03-30 18:36:58 +02:00
|
|
|
{
|
2021-02-21 12:38:56 +01:00
|
|
|
UnavailableOverlay.Visible = true;
|
2021-02-01 16:49:32 +01:00
|
|
|
// We return early as to not give the buttons behavior.
|
|
|
|
|
return;
|
2020-03-30 18:36:58 +02:00
|
|
|
}
|
|
|
|
|
|
2021-02-01 16:49:32 +01:00
|
|
|
InputButton.OnToggled += MidiInputButtonOnOnToggled;
|
2023-07-16 21:12:53 +02:00
|
|
|
BandButton.OnPressed += BandButtonOnPressed;
|
|
|
|
|
BandButton.OnToggled += BandButtonOnToggled;
|
2021-02-01 16:49:32 +01:00
|
|
|
FileButton.OnPressed += MidiFileButtonOnOnPressed;
|
|
|
|
|
LoopButton.OnToggled += MidiLoopButtonOnOnToggled;
|
2023-07-16 21:12:53 +02:00
|
|
|
ChannelsButton.OnPressed += ChannelsButtonOnPressed;
|
2021-02-01 16:49:32 +01:00
|
|
|
StopButton.OnPressed += MidiStopButtonOnPressed;
|
2021-02-04 13:37:26 +01:00
|
|
|
PlaybackSlider.OnValueChanged += PlaybackSliderSeek;
|
|
|
|
|
PlaybackSlider.OnKeyBindUp += PlaybackSliderKeyUp;
|
2021-02-21 12:38:56 +01:00
|
|
|
|
2023-07-08 14:08:32 +10:00
|
|
|
MinSize = SetSize = new Vector2(400, 150);
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
private void BandButtonOnPressed(ButtonEventArgs obj)
|
|
|
|
|
{
|
|
|
|
|
if (!PlayCheck())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_owner.OpenBandMenu();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void BandButtonOnToggled(ButtonToggledEventArgs obj)
|
|
|
|
|
{
|
|
|
|
|
if (obj.Pressed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_owner.Instruments.SetMaster(_owner.Owner, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ChannelsButtonOnPressed(ButtonEventArgs obj)
|
|
|
|
|
{
|
|
|
|
|
_owner.OpenChannelsMenu();
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-25 00:11:47 +01:00
|
|
|
private void InstrumentOnMidiPlaybackEnded()
|
|
|
|
|
{
|
|
|
|
|
MidiPlaybackSetButtonsDisabled(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MidiPlaybackSetButtonsDisabled(bool disabled)
|
|
|
|
|
{
|
2023-07-16 21:12:53 +02:00
|
|
|
if(disabled)
|
|
|
|
|
_owner.CloseChannelsMenu();
|
|
|
|
|
|
2021-02-01 16:49:32 +01:00
|
|
|
LoopButton.Disabled = disabled;
|
|
|
|
|
StopButton.Disabled = disabled;
|
2021-02-04 13:37:26 +01:00
|
|
|
|
|
|
|
|
// Whether to allow the slider to receive events..
|
|
|
|
|
PlaybackSlider.MouseFilter = !disabled ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-10 14:48:29 +01:00
|
|
|
private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
|
2019-11-25 00:11:47 +01:00
|
|
|
{
|
2024-01-04 02:19:22 +00:00
|
|
|
if (_isMidiFileDialogueWindowOpen)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
_owner.CloseBandMenu();
|
|
|
|
|
|
2020-03-30 18:36:58 +02:00
|
|
|
var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi"));
|
2024-01-04 02:19:22 +00:00
|
|
|
|
|
|
|
|
// TODO: Once the file dialogue manager can handle focusing or closing windows, improve this logic to close
|
|
|
|
|
// or focus the previously-opened window.
|
|
|
|
|
_isMidiFileDialogueWindowOpen = true;
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
await using var file = await _owner.FileDialogManager.OpenFile(filters);
|
2019-11-25 00:11:47 +01:00
|
|
|
|
2024-01-04 02:19:22 +00:00
|
|
|
_isMidiFileDialogueWindowOpen = false;
|
|
|
|
|
|
2021-12-02 23:51:30 +13:00
|
|
|
// did the instrument menu get closed while waiting for the user to select a file?
|
|
|
|
|
if (Disposed)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-06-25 15:27:22 +02:00
|
|
|
// The following checks are only in place to prevent players from playing MIDI songs locally.
|
|
|
|
|
// There are equivalents for these checks on the server.
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
if (file == null)
|
|
|
|
|
return;
|
2019-11-25 00:11:47 +01:00
|
|
|
|
2020-10-29 12:22:56 +01:00
|
|
|
if (!PlayCheck())
|
|
|
|
|
return;
|
|
|
|
|
|
2021-02-04 13:37:26 +01:00
|
|
|
await using var memStream = new MemoryStream((int) file.Length);
|
2024-01-04 02:19:22 +00:00
|
|
|
|
|
|
|
|
await file.CopyToAsync(memStream);
|
2020-11-25 18:00:18 +01:00
|
|
|
|
2021-11-28 01:47:36 +01:00
|
|
|
if (_owner.Instrument is not {} instrument
|
2023-07-16 21:12:53 +02:00
|
|
|
|| !_owner.Instruments.OpenMidi(_owner.Owner, memStream.GetBuffer().AsSpan(0, (int) memStream.Length), instrument))
|
2020-11-25 18:00:18 +01:00
|
|
|
return;
|
|
|
|
|
|
2019-11-25 00:11:47 +01:00
|
|
|
MidiPlaybackSetButtonsDisabled(false);
|
2021-02-01 16:49:32 +01:00
|
|
|
if (InputButton.Pressed)
|
|
|
|
|
InputButton.Pressed = false;
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-10 14:48:29 +01:00
|
|
|
private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj)
|
2019-11-25 00:11:47 +01:00
|
|
|
{
|
2023-07-16 21:12:53 +02:00
|
|
|
_owner.CloseBandMenu();
|
2021-11-28 01:47:36 +01:00
|
|
|
|
2019-11-25 00:11:47 +01:00
|
|
|
if (obj.Pressed)
|
|
|
|
|
{
|
2020-10-29 12:22:56 +01:00
|
|
|
if (!PlayCheck())
|
|
|
|
|
return;
|
|
|
|
|
|
2019-11-25 00:11:47 +01:00
|
|
|
MidiStopButtonOnPressed(null);
|
2021-11-28 01:47:36 +01:00
|
|
|
if(_owner.Instrument is {} instrument)
|
2023-07-16 21:12:53 +02:00
|
|
|
_owner.Instruments.OpenInput(_owner.Owner, instrument);
|
|
|
|
|
}
|
|
|
|
|
else if (_owner.Instrument is { } instrument)
|
|
|
|
|
{
|
|
|
|
|
_owner.Instruments.CloseInput(_owner.Owner, false, instrument);
|
|
|
|
|
_owner.CloseChannelsMenu();
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 12:22:56 +01:00
|
|
|
private bool PlayCheck()
|
|
|
|
|
{
|
2022-06-27 20:09:21 +12:00
|
|
|
// TODO all of these checks should also be done server-side.
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
var instrumentEnt = _owner.Owner;
|
2020-10-29 12:22:56 +01:00
|
|
|
var instrument = _owner.Instrument;
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
if (instrument == null)
|
2021-03-10 14:48:29 +01:00
|
|
|
return false;
|
2021-03-13 02:46:32 +01:00
|
|
|
|
2024-02-13 22:48:39 +01:00
|
|
|
var localEntity = _owner.PlayerManager.LocalEntity;
|
2020-10-29 12:22:56 +01:00
|
|
|
|
|
|
|
|
// If we don't have a player or controlled entity, we return.
|
2024-02-13 22:48:39 +01:00
|
|
|
if (localEntity == null)
|
2023-07-16 21:12:53 +02:00
|
|
|
return false;
|
2020-10-29 12:22:56 +01:00
|
|
|
|
2022-07-20 18:08:33 +02:00
|
|
|
// By default, allow an instrument to play itself and skip all other checks
|
2024-02-13 22:48:39 +01:00
|
|
|
if (localEntity == instrumentEnt)
|
2022-07-20 18:08:33 +02:00
|
|
|
return true;
|
|
|
|
|
|
2023-10-11 02:18:49 -07:00
|
|
|
var container = _owner.Entities.System<SharedContainerSystem>();
|
2022-07-20 18:08:33 +02:00
|
|
|
// If we're a handheld instrument, we might be in a container. Get it just in case.
|
2023-10-11 02:18:49 -07:00
|
|
|
container.TryGetContainingContainer(instrumentEnt, out var conMan);
|
2022-07-20 18:08:33 +02:00
|
|
|
|
2020-10-29 12:22:56 +01:00
|
|
|
// If the instrument is handheld and we're not holding it, we return.
|
2024-02-13 22:48:39 +01:00
|
|
|
if ((instrument.Handheld && (conMan == null || conMan.Owner != localEntity)))
|
2023-07-16 21:12:53 +02:00
|
|
|
return false;
|
2020-10-29 12:22:56 +01:00
|
|
|
|
2024-02-13 22:48:39 +01:00
|
|
|
if (!_owner.ActionBlocker.CanInteract(localEntity.Value, instrumentEnt))
|
2022-06-27 20:09:21 +12:00
|
|
|
return false;
|
|
|
|
|
|
2020-10-29 12:22:56 +01:00
|
|
|
// We check that we're in range unobstructed just in case.
|
2024-02-13 22:48:39 +01:00
|
|
|
return _owner.Interactions.InRangeUnobstructed(localEntity.Value, instrumentEnt);
|
2020-10-29 12:22:56 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-10 14:48:29 +01:00
|
|
|
private void MidiStopButtonOnPressed(ButtonEventArgs? obj)
|
2019-11-25 00:11:47 +01:00
|
|
|
{
|
|
|
|
|
MidiPlaybackSetButtonsDisabled(true);
|
2021-11-28 01:47:36 +01:00
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
if (_owner.Instrument is not {} instrument)
|
2021-11-28 01:47:36 +01:00
|
|
|
return;
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
_owner.Instruments.CloseMidi(_owner.Owner, false, instrument);
|
|
|
|
|
_owner.CloseChannelsMenu();
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-10 14:48:29 +01:00
|
|
|
private void MidiLoopButtonOnOnToggled(ButtonToggledEventArgs obj)
|
2019-11-25 00:11:47 +01:00
|
|
|
{
|
2021-11-28 01:47:36 +01:00
|
|
|
if (_owner.Instrument == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_owner.Instrument.LoopMidi = obj.Pressed;
|
2023-07-16 21:12:53 +02:00
|
|
|
_owner.Instruments.UpdateRenderer(_owner.Owner, _owner.Instrument);
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
2021-02-04 13:37:26 +01:00
|
|
|
|
|
|
|
|
private void PlaybackSliderSeek(Range _)
|
|
|
|
|
{
|
|
|
|
|
// Do not seek while still grabbing.
|
2023-07-16 21:12:53 +02:00
|
|
|
if (PlaybackSlider.Grabbed || _owner.Instrument is not {} instrument)
|
|
|
|
|
return;
|
2021-02-04 13:37:26 +01:00
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
_owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
|
2021-02-04 13:37:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args)
|
|
|
|
|
{
|
2023-07-16 21:12:53 +02:00
|
|
|
if (args.Function != EngineKeyFunctions.UIClick || _owner.Instrument is not {} instrument)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
_owner.Instruments.SetPlayerTick(_owner.Owner, (int)Math.Ceiling(PlaybackSlider.Value), instrument);
|
|
|
|
|
}
|
2021-03-10 14:48:29 +01:00
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
public override void Close()
|
|
|
|
|
{
|
|
|
|
|
base.Close();
|
|
|
|
|
_owner.CloseBandMenu();
|
|
|
|
|
_owner.CloseChannelsMenu();
|
2021-02-04 13:37:26 +01:00
|
|
|
}
|
|
|
|
|
|
2021-03-26 17:10:31 -07:00
|
|
|
protected override void FrameUpdate(FrameEventArgs args)
|
2021-02-04 13:37:26 +01:00
|
|
|
{
|
2021-03-26 17:10:31 -07:00
|
|
|
base.FrameUpdate(args);
|
2021-02-04 13:37:26 +01:00
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
if (_owner.Instrument == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var hasMaster = _owner.Instrument.Master != null;
|
|
|
|
|
BandButton.ToggleMode = hasMaster;
|
|
|
|
|
BandButton.Pressed = hasMaster;
|
|
|
|
|
BandButton.Disabled = _owner.Instrument.IsMidiOpen || _owner.Instrument.IsInputOpen;
|
|
|
|
|
ChannelsButton.Disabled = !_owner.Instrument.IsRendererAlive;
|
2021-03-10 14:48:29 +01:00
|
|
|
|
2021-02-04 13:37:26 +01:00
|
|
|
if (!_owner.Instrument.IsMidiOpen)
|
|
|
|
|
{
|
|
|
|
|
PlaybackSlider.MaxValue = 1;
|
|
|
|
|
PlaybackSlider.SetValueWithoutEvent(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-16 21:12:53 +02:00
|
|
|
if (PlaybackSlider.Grabbed)
|
|
|
|
|
return;
|
2021-02-04 13:37:26 +01:00
|
|
|
|
|
|
|
|
PlaybackSlider.MaxValue = _owner.Instrument.PlayerTotalTick;
|
|
|
|
|
PlaybackSlider.SetValueWithoutEvent(_owner.Instrument.PlayerTick);
|
|
|
|
|
}
|
2019-11-25 00:11:47 +01:00
|
|
|
}
|
|
|
|
|
}
|