| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- using System.IO;
- using System.Numerics;
- using System.Threading.Tasks;
- using Content.Client.Interactable;
- using Content.Shared.ActionBlocker;
- using Robust.Client.AutoGenerated;
- using Robust.Client.Player;
- using Robust.Client.UserInterface;
- using Robust.Client.UserInterface.CustomControls;
- using Robust.Client.UserInterface.XAML;
- using Robust.Shared.Containers;
- using Robust.Shared.Input;
- using Robust.Shared.Timing;
- using static Robust.Client.UserInterface.Controls.BaseButton;
- using Range = Robust.Client.UserInterface.Controls.Range;
- namespace Content.Client.Instruments.UI
- {
- [GenerateTypedNameReferences]
- public sealed partial class InstrumentMenu : DefaultWindow
- {
- [Dependency] private readonly IEntityManager _entManager = default!;
- [Dependency] private readonly IFileDialogManager _dialogs = default!;
- [Dependency] private readonly IPlayerManager _player = default!;
- private bool _isMidiFileDialogueWindowOpen;
- public event Action? OnOpenBand;
- public event Action? OnOpenChannels;
- public event Action? OnCloseBands;
- public event Action? OnCloseChannels;
- public EntityUid Entity;
- public InstrumentMenu()
- {
- RobustXamlLoader.Load(this);
- IoCManager.InjectDependencies(this);
- InputButton.OnToggled += MidiInputButtonOnOnToggled;
- BandButton.OnPressed += BandButtonOnPressed;
- BandButton.OnToggled += BandButtonOnToggled;
- FileButton.OnPressed += MidiFileButtonOnOnPressed;
- LoopButton.OnToggled += MidiLoopButtonOnOnToggled;
- ChannelsButton.OnPressed += ChannelsButtonOnPressed;
- StopButton.OnPressed += MidiStopButtonOnPressed;
- PlaybackSlider.OnValueChanged += PlaybackSliderSeek;
- PlaybackSlider.OnKeyBindUp += PlaybackSliderKeyUp;
- MinSize = SetSize = new Vector2(400, 150);
- }
- public void SetInstrument(Entity<InstrumentComponent> entity)
- {
- Entity = entity;
- var component = entity.Comp;
- component.OnMidiPlaybackEnded += InstrumentOnMidiPlaybackEnded;
- LoopButton.Disabled = !component.IsMidiOpen;
- LoopButton.Pressed = component.LoopMidi;
- ChannelsButton.Disabled = !component.IsRendererAlive;
- StopButton.Disabled = !component.IsMidiOpen;
- PlaybackSlider.MouseFilter = component.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
- }
- public void RemoveInstrument(InstrumentComponent component)
- {
- component.OnMidiPlaybackEnded -= InstrumentOnMidiPlaybackEnded;
- }
- public void SetMIDI(bool available)
- {
- UnavailableOverlay.Visible = !available;
- }
- private void BandButtonOnPressed(ButtonEventArgs obj)
- {
- if (!PlayCheck())
- return;
- OnOpenBand?.Invoke();
- }
- private void BandButtonOnToggled(ButtonToggledEventArgs obj)
- {
- if (obj.Pressed)
- return;
- if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
- {
- _entManager.System<InstrumentSystem>().SetMaster(Entity, instrument.Master);
- }
- }
- private void ChannelsButtonOnPressed(ButtonEventArgs obj)
- {
- OnOpenChannels?.Invoke();
- }
- private void InstrumentOnMidiPlaybackEnded()
- {
- MidiPlaybackSetButtonsDisabled(true);
- }
- public void MidiPlaybackSetButtonsDisabled(bool disabled)
- {
- if (disabled)
- {
- OnCloseChannels?.Invoke();
- }
- LoopButton.Disabled = disabled;
- StopButton.Disabled = disabled;
- // Whether to allow the slider to receive events..
- PlaybackSlider.MouseFilter = !disabled ? MouseFilterMode.Pass : MouseFilterMode.Ignore;
- }
- private async void MidiFileButtonOnOnPressed(ButtonEventArgs obj)
- {
- if (_isMidiFileDialogueWindowOpen)
- return;
- OnCloseBands?.Invoke();
- var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi"));
- // 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;
- await using var file = await _dialogs.OpenFile(filters);
- _isMidiFileDialogueWindowOpen = false;
- // did the instrument menu get closed while waiting for the user to select a file?
- if (Disposed)
- return;
- // The following checks are only in place to prevent players from playing MIDI songs locally.
- // There are equivalents for these checks on the server.
- if (file == null)
- return;
- 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;
- }
- if (!_entManager.System<InstrumentSystem>()
- .OpenMidi(Entity,
- memStream.GetBuffer().AsSpan(0, (int) memStream.Length),
- instrument))
- {
- return;
- }
- MidiPlaybackSetButtonsDisabled(false);
- if (InputButton.Pressed)
- InputButton.Pressed = false;
- }
- private void MidiInputButtonOnOnToggled(ButtonToggledEventArgs obj)
- {
- OnCloseBands?.Invoke();
- if (obj.Pressed)
- {
- if (!PlayCheck())
- return;
- MidiStopButtonOnPressed(null);
- if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
- _entManager.System<InstrumentSystem>().OpenInput(Entity, instrument);
- }
- else
- {
- _entManager.System<InstrumentSystem>().CloseInput(Entity, false);
- OnCloseChannels?.Invoke();
- }
- }
- private bool PlayCheck()
- {
- // TODO all of these checks should also be done server-side.
- if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
- return false;
- var localEntity = _player.LocalEntity;
- // If we don't have a player or controlled entity, we return.
- if (localEntity == null)
- return false;
- // By default, allow an instrument to play itself and skip all other checks
- if (localEntity == Entity)
- return true;
- var container = _entManager.System<SharedContainerSystem>();
- // If we're a handheld instrument, we might be in a container. Get it just in case.
- container.TryGetContainingContainer((Entity, null, null), out var conMan);
- // If the instrument is handheld and we're not holding it, we return.
- if (instrument.Handheld && (conMan == null || conMan.Owner != localEntity))
- return false;
- if (!_entManager.System<ActionBlockerSystem>().CanInteract(localEntity.Value, Entity))
- return false;
- // We check that we're in range unobstructed just in case.
- return _entManager.System<InteractionSystem>().InRangeUnobstructed(localEntity.Value, Entity);
- }
- private void MidiStopButtonOnPressed(ButtonEventArgs? obj)
- {
- MidiPlaybackSetButtonsDisabled(true);
- _entManager.System<InstrumentSystem>().CloseMidi(Entity, false);
- OnCloseChannels?.Invoke();
- }
- private void MidiLoopButtonOnOnToggled(ButtonToggledEventArgs obj)
- {
- var instrument = _entManager.System<InstrumentSystem>();
- if (_entManager.TryGetComponent(Entity, out InstrumentComponent? instrumentComp))
- {
- instrumentComp.LoopMidi = obj.Pressed;
- }
- instrument.UpdateRenderer(Entity);
- }
- private void PlaybackSliderSeek(Range _)
- {
- // Do not seek while still grabbing.
- if (PlaybackSlider.Grabbed)
- return;
- _entManager.System<InstrumentSystem>().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value));
- }
- private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args)
- {
- if (args.Function != EngineKeyFunctions.UIClick)
- return;
- _entManager.System<InstrumentSystem>().SetPlayerTick(Entity, (int)Math.Ceiling(PlaybackSlider.Value));
- }
- protected override void FrameUpdate(FrameEventArgs args)
- {
- base.FrameUpdate(args);
- if (!_entManager.TryGetComponent(Entity, out InstrumentComponent? instrument))
- return;
- var hasMaster = instrument.Master != null;
- BandButton.ToggleMode = hasMaster;
- BandButton.Pressed = hasMaster;
- BandButton.Disabled = instrument.IsMidiOpen || instrument.IsInputOpen;
- ChannelsButton.Disabled = !instrument.IsRendererAlive;
- if (!instrument.IsMidiOpen)
- {
- PlaybackSlider.MaxValue = 1;
- PlaybackSlider.SetValueWithoutEvent(0);
- return;
- }
- if (PlaybackSlider.Grabbed)
- return;
- PlaybackSlider.MaxValue = instrument.PlayerTotalTick;
- PlaybackSlider.SetValueWithoutEvent(instrument.PlayerTick);
- }
- }
- }
|