diff --git a/UltraStar Play/Assets/Common/I18N/I18NManager.cs b/UltraStar Play/Assets/Common/I18N/I18NManager.cs index 85cdab633..f43ad0ce7 100644 --- a/UltraStar Play/Assets/Common/I18N/I18NManager.cs +++ b/UltraStar Play/Assets/Common/I18N/I18NManager.cs @@ -218,4 +218,21 @@ private static string GetPropertiesFilePath(string propertiesFileNameWithCountry path = ApplicationUtils.GetStreamingAssetsPath(path); return path; } + + public List GetTranslatedLanguages() + { + HashSet translatedLanguages = new HashSet { SystemLanguage.English }; + foreach (SystemLanguage systemLanguage in EnumUtils.GetValuesAsList()) + { + string suffix = GetCountryCodeSuffixForPropertiesFile(systemLanguage); + string propertiesFilePath = GetPropertiesFilePath(PropertiesFileName + suffix); + if (File.Exists(propertiesFilePath)) + { + translatedLanguages.Add(systemLanguage); + } + } + return translatedLanguages + .OrderBy(it => it.ToString()) + .ToList(); + } } diff --git a/UltraStar Play/Assets/Common/I18N/LanguageHelper.cs b/UltraStar Play/Assets/Common/I18N/LanguageHelper.cs index 05dd11c49..6c835033c 100644 --- a/UltraStar Play/Assets/Common/I18N/LanguageHelper.cs +++ b/UltraStar Play/Assets/Common/I18N/LanguageHelper.cs @@ -23,7 +23,9 @@ public static string Get2LetterIsoCodeFromSystemLanguage(SystemLanguage lang) case SystemLanguage.Belarusian: return "BY"; case SystemLanguage.Bulgarian: return "BG"; case SystemLanguage.Catalan: return "CA"; - case SystemLanguage.Chinese: return "ZH"; + case SystemLanguage.Chinese: + case SystemLanguage.ChineseSimplified: + case SystemLanguage.ChineseTraditional: return "ZH"; case SystemLanguage.Czech: return "CS"; case SystemLanguage.Danish: return "DA"; case SystemLanguage.Dutch: return "NL"; @@ -63,4 +65,4 @@ public static string Get2LetterIsoCodeFromSystemLanguage(SystemLanguage lang) return "EN"; } } -} \ No newline at end of file +} diff --git a/UltraStar Play/Assets/Common/Model/Song/SongMetaUtils.cs b/UltraStar Play/Assets/Common/Model/Song/SongMetaUtils.cs index 915737de3..343600ff8 100644 --- a/UltraStar Play/Assets/Common/Model/Song/SongMetaUtils.cs +++ b/UltraStar Play/Assets/Common/Model/Song/SongMetaUtils.cs @@ -46,8 +46,9 @@ public static Sentence FindExistingSentenceForNote(IReadOnlyCollection public static Voice GetOrCreateVoice(SongMeta songMeta, string voiceName) { Voice matchingVoice = songMeta.GetVoices() - .Where(it => it.Name == voiceName || (voiceName.IsNullOrEmpty() && it.Name == Voice.soloVoiceName)) - .FirstOrDefault(); + .FirstOrDefault(voice => voice.Name == voiceName + || (voiceName.IsNullOrEmpty() && voice.Name == Voice.firstVoiceName) + || (voiceName == Voice.firstVoiceName && voice.Name.IsNullOrEmpty())); if (matchingVoice != null) { return matchingVoice; diff --git a/UltraStar Play/Assets/Common/UI/ContextMenu/ContextMenuItem.cs b/UltraStar Play/Assets/Common/UI/ContextMenu/ContextMenuItem.cs index bea34f3f0..fd5b75c78 100644 --- a/UltraStar Play/Assets/Common/UI/ContextMenu/ContextMenuItem.cs +++ b/UltraStar Play/Assets/Common/UI/ContextMenu/ContextMenuItem.cs @@ -42,7 +42,6 @@ public void SetAction(Action action) { actionSubscriptionDisposable = button.OnClickAsObservable().Subscribe(_ => { - Debug.Log("ContextMenuItem triggered"); action(); ContextMenu.CloseContextMenu(); }); diff --git a/UltraStar Play/Assets/Common/UI/PlayerProfileUi/AvatarImage/AvatarImage.cs b/UltraStar Play/Assets/Common/UI/PlayerProfileUi/AvatarImage/AvatarImage.cs index 73181f649..c2c9fdf76 100644 --- a/UltraStar Play/Assets/Common/UI/PlayerProfileUi/AvatarImage/AvatarImage.cs +++ b/UltraStar Play/Assets/Common/UI/PlayerProfileUi/AvatarImage/AvatarImage.cs @@ -17,24 +17,28 @@ public class AvatarImage : MonoBehaviour, INeedInjection, IExcludeFromSceneInjec [Inject(optional = true)] private MicProfile micProfile; - public void SetPlayerProfile(PlayerProfile playerProfile) - { - AvatarImageReference imageRef = FindObjectsOfType().Where(it => it.avatar == playerProfile.Avatar).FirstOrDefault(); - if (imageRef != null) - { - image.sprite = imageRef.Sprite; - } - else - { - Debug.LogWarning("Did not find an image for the avatar: " + playerProfile.Avatar); - } - } - + [Inject(optional = true)] + private PlayerProfile playerProfile; + public void OnInjectionFinished() { if (micProfile != null) { image.color = micProfile.Color; } + + if (playerProfile != null) + { + AvatarImageReference imageRef = FindObjectsOfType() + .FirstOrDefault(it => it.avatar == playerProfile.Avatar); + if (imageRef != null) + { + image.sprite = imageRef.Sprite; + } + else + { + Debug.LogWarning("Did not find an image for the avatar: " + playerProfile.Avatar); + } + } } } diff --git a/UltraStar Play/Assets/Scenes/Options/GameOptions/LanguageItemSlider.cs b/UltraStar Play/Assets/Scenes/Options/GameOptions/LanguageItemSlider.cs index 7316e6418..48ea8c76e 100644 --- a/UltraStar Play/Assets/Scenes/Options/GameOptions/LanguageItemSlider.cs +++ b/UltraStar Play/Assets/Scenes/Options/GameOptions/LanguageItemSlider.cs @@ -7,16 +7,19 @@ public class LanguageItemSlider : TextItemSlider, INeedInjection { + [Inject] + private I18NManager i18nManager; + protected override void Start() { base.Start(); - Items = EnumUtils.GetValuesAsList(); + Items = i18nManager.GetTranslatedLanguages(); SystemLanguage currentLanguage = SettingsManager.Instance.Settings.GameSettings.language; - Selection.Value = currentLanguage; + Selection.Value = Items.Contains(currentLanguage) ? currentLanguage : SystemLanguage.English; Selection.Subscribe(newValue => { SettingsManager.Instance.Settings.GameSettings.language = newValue; - I18NManager.Instance.UpdateCurrentLanguageAndTranslations(); + i18nManager.UpdateCurrentLanguageAndTranslations(); }); } diff --git a/UltraStar Play/Assets/Scenes/Sing/PlayerController/PlayerUiController/UiNotes/UiNote.cs b/UltraStar Play/Assets/Scenes/Sing/PlayerController/PlayerUiController/UiNotes/UiNote.cs index 77033a197..85e5f6b0c 100644 --- a/UltraStar Play/Assets/Scenes/Sing/PlayerController/PlayerUiController/UiNotes/UiNote.cs +++ b/UltraStar Play/Assets/Scenes/Sing/PlayerController/PlayerUiController/UiNotes/UiNote.cs @@ -57,6 +57,7 @@ void Update() void OnDestroy() { DestroyStars(); + DestroyLyrics(); } public void SetColorOfMicProfile(MicProfile micProfile) @@ -100,6 +101,20 @@ private void DestroyStars() stars.Clear(); } + private void DestroyLyrics() + { + if (lyricsUiText != null + && lyricsUiText.gameObject != null) + { + Destroy(lyricsUiText.gameObject); + } + if (lyricsUiTextRectTransform != null + && lyricsUiTextRectTransform.gameObject != null) + { + Destroy(lyricsUiTextRectTransform.gameObject); + } + } + private void CreateGoldenNoteEffect() { // Create several particles. Longer notes require more particles because they have more space to fill. diff --git a/UltraStar Play/Assets/Scenes/Sing/SingSceneController.cs b/UltraStar Play/Assets/Scenes/Sing/SingSceneController.cs index 973e87aa4..d1398080c 100644 --- a/UltraStar Play/Assets/Scenes/Sing/SingSceneController.cs +++ b/UltraStar Play/Assets/Scenes/Sing/SingSceneController.cs @@ -393,7 +393,7 @@ private string GetVoiceName(PlayerProfile playerProfile) return Voice.soloVoiceName; } - List voiceNames = new List(SongMeta.VoiceNames.Values); + List voiceNames = new List(SongMeta.VoiceNames.Keys); int voiceNameCount = voiceNames.Count; if (voiceNameCount > 1) { diff --git a/UltraStar Play/Assets/Scenes/Sing/SingSceneNoteDisplayer/ScrollingNoteStreamDisplayer.cs b/UltraStar Play/Assets/Scenes/Sing/SingSceneNoteDisplayer/ScrollingNoteStreamDisplayer.cs index 1cea37d8b..a29a67951 100644 --- a/UltraStar Play/Assets/Scenes/Sing/SingSceneNoteDisplayer/ScrollingNoteStreamDisplayer.cs +++ b/UltraStar Play/Assets/Scenes/Sing/SingSceneNoteDisplayer/ScrollingNoteStreamDisplayer.cs @@ -115,7 +115,7 @@ private void PositionUiNoteLyrics(UiNote uiNote) lyricsRectTransform.anchorMax = new Vector2(lyricsRectTransform.anchorMax.x, 1); lyricsRectTransform.sizeDelta = new Vector2(lyricsRectTransform.sizeDelta.x, 0); lyricsRectTransform.localPosition = new Vector2(lyricsRectTransform.localPosition.x, 0); - uiNote.lyricsUiText.transform.SetParent(uiNote.RectTransform, true); + uiNote.lyricsUiText.transform.SetParent(lyricsBar, true); } private double GetNoteStartBeatOfFollowingNote(Note note) @@ -152,7 +152,10 @@ private void UpdateUiNotePositions() { foreach (UiNote uiNote in noteToUiNoteMap.Values) { + Vector3 lastPosition = uiNote.RectTransform.position; PositionUiNote(uiNote.RectTransform, uiNote.Note.MidiNote, uiNote.Note.StartBeat, uiNote.Note.EndBeat); + Vector3 positionDelta = uiNote.RectTransform.position - lastPosition; + uiNote.lyricsUiTextRectTransform.Translate(positionDelta); } foreach (UiRecordedNote uiRecordedNote in uiRecordedNotes) diff --git a/UltraStar Play/Assets/Scenes/SongEditor/Actions/DeleteNotesAction.cs b/UltraStar Play/Assets/Scenes/SongEditor/Actions/DeleteNotesAction.cs index 9b464614c..782d85a14 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/Actions/DeleteNotesAction.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/Actions/DeleteNotesAction.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using UniInject; // Disable warning about fields that are never assigned, their values are injected. @@ -15,14 +16,27 @@ public class DeleteNotesAction : INeedInjection [Inject] private SongMetaChangeEventStream songMetaChangeEventStream; + [Inject] + private DeleteSentencesAction deleteSentencesAction; + public void Execute(IReadOnlyCollection selectedNotes) { + HashSet affectedSentences = new HashSet(); foreach (Note note in selectedNotes) { + if (note.Sentence != null) + { + affectedSentences.Add(note.Sentence); + } note.SetSentence(null); songEditorLayerManager.RemoveNoteFromAllLayers(note); editorNoteDisplayer.DeleteNote(note); } + + List affectedSentencesWithoutNotes = affectedSentences + .Where(sentence => sentence.Notes.Count == 0) + .ToList(); + deleteSentencesAction.Execute(affectedSentencesWithoutNotes); } public void ExecuteAndNotify(IReadOnlyCollection selectedNotes) @@ -30,4 +44,4 @@ public void ExecuteAndNotify(IReadOnlyCollection selectedNotes) Execute(selectedNotes); songMetaChangeEventStream.OnNext(new NotesDeletedEvent()); } -} \ No newline at end of file +} diff --git a/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNoteToOwnSentenceAction.cs b/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNoteToOwnSentenceAction.cs new file mode 100644 index 000000000..27937a05c --- /dev/null +++ b/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNoteToOwnSentenceAction.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using UniInject; + +// Disable warning about fields that are never assigned, their values are injected. +#pragma warning disable CS0649 + +public class MoveNoteToOwnSentenceAction : INeedInjection +{ + [Inject] + private SongMetaChangeEventStream songMetaChangeEventStream; + + [Inject] + private DeleteSentencesAction deleteSentencesAction; + + public bool CanMoveToOwnSentence(List notes) + { + if (notes.IsNullOrEmpty()) + { + return false; + } + return notes.AnyMatch(note => note.Sentence?.Voice != null); + } + + public void MoveToOwnSentence(List notes) + { + List affectedSentences = notes.Select(note => note.Sentence).ToList(); + + Sentence newSentence = new Sentence(); + Voice voice = notes + .Select(note => note.Sentence?.Voice) + .FirstOrDefault(); + newSentence.SetVoice(voice); + + notes.ForEach(note => note.SetSentence(newSentence)); + newSentence.FitToNotes(); + + // Remove old sentence if not more notes left + List sentencesWithoutNotes = affectedSentences.Where(it => it.Notes.IsNullOrEmpty()).ToList(); + deleteSentencesAction.Execute(sentencesWithoutNotes); + + // Fit sentences to notes + List sentencesWithNotes = affectedSentences.Where(it => !it.Notes.IsNullOrEmpty()).ToList(); + sentencesWithNotes.ForEach(it => it.FitToNotes()); + } + + public void MoveToOwnSentenceAndNotify(List notes) + { + MoveToOwnSentence(notes); + songMetaChangeEventStream.OnNext(new SentencesChangedEvent()); + } +} diff --git a/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNoteToOwnSentenceAction.cs.meta b/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNoteToOwnSentenceAction.cs.meta new file mode 100644 index 000000000..c8330e64b --- /dev/null +++ b/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNoteToOwnSentenceAction.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 02d746a1843a40ea939515c6130d16fb +timeCreated: 1619966630 \ No newline at end of file diff --git a/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNotesToOtherVoiceAction.cs b/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNotesToOtherVoiceAction.cs index a7d2d4453..3a481eec6 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNotesToOtherVoiceAction.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/Actions/MoveNotesToOtherVoiceAction.cs @@ -21,6 +21,7 @@ public bool CanMoveNotesToVoice(List selectedNotes, params string[] voiceN public void MoveNotesToVoice(SongMeta songMeta, List selectedNotes, string voiceName) { Voice voice = SongMetaUtils.GetOrCreateVoice(songMeta, voiceName); + Sentence createdSentence = null; List changedSentences = new List(); foreach (Note note in selectedNotes) { @@ -35,9 +36,14 @@ public void MoveNotesToVoice(SongMeta songMeta, List selectedNotes, string { sentenceForNote = new Sentence(note.Sentence.MinBeat, note.Sentence.MaxBeat); } + else if (createdSentence != null) + { + sentenceForNote = createdSentence; + } else { - sentenceForNote = new Sentence(); + createdSentence = new Sentence(); + sentenceForNote = createdSentence; } sentenceForNote.SetVoice(voice); } @@ -76,4 +82,4 @@ private static bool HasVoice(Note note, string[] voiceNames) return note.Sentence != null && voiceNames.Contains(note.Sentence.Voice.Name); } -} \ No newline at end of file +} diff --git a/UltraStar Play/Assets/Scenes/SongEditor/Actions/SongEditorActionBinder.cs b/UltraStar Play/Assets/Scenes/SongEditor/Actions/SongEditorActionBinder.cs index 4bd5a199f..7215dbfa3 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/Actions/SongEditorActionBinder.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/Actions/SongEditorActionBinder.cs @@ -17,6 +17,7 @@ public List GetBindings() bb.BindTypeToNewInstances(typeof(AddNoteAction)); bb.BindTypeToNewInstances(typeof(MoveNoteToAjacentSentenceAction)); bb.BindTypeToNewInstances(typeof(MoveNotesToOtherVoiceAction)); + bb.BindTypeToNewInstances(typeof(MoveNoteToOwnSentenceAction)); bb.BindTypeToNewInstances(typeof(MoveNotesAction)); bb.BindTypeToNewInstances(typeof(ExtendNotesAction)); diff --git a/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteContextMenuHandler.cs b/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteContextMenuHandler.cs index 75b325993..46a964d31 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteContextMenuHandler.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteContextMenuHandler.cs @@ -41,6 +41,9 @@ public class EditorNoteContextMenuHandler : AbstractContextMenuHandler [Inject] private MoveNotesToOtherVoiceAction moveNotesToOtherVoiceAction; + [Inject] + private MoveNoteToOwnSentenceAction moveNoteToOwnSentenceAction; + [Inject] private SpaceBetweenNotesAction spaceBetweenNotesAction; @@ -183,6 +186,11 @@ private void FillContextMenuToMoveToOtherVoice(ContextMenu contextMenu, List moveNotesToOtherVoiceAction.MoveNotesToVoiceAndNotify(songMeta, selectedNotes, Voice.secondVoiceName)); } + + if (moveNoteToOwnSentenceAction.CanMoveToOwnSentence(selectedNotes)) + { + contextMenu.AddItem("Move to own sentence", () => moveNoteToOwnSentenceAction.MoveToOwnSentenceAndNotify(selectedNotes)); + } } private void FillContextMenuToMoveToOtherSentence(ContextMenu contextMenu, List selectedNotes) diff --git a/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteDisplayer.cs b/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteDisplayer.cs index eed7e7cb8..3fa0e38f7 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteDisplayer.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorNoteDisplayer.cs @@ -78,7 +78,10 @@ void Start() .Subscribe(_ => UpdateNotes()); } - settings.SongEditorSettings.ObserveEveryValueChanged(it => it.HideVoices.Count).Subscribe(_ => OnHideVoicesChanged()); + settings.SongEditorSettings + .ObserveEveryValueChanged(it => it.HideVoices.Count) + .Subscribe(_ => OnHideVoicesChanged()) + .AddTo(this); } private void OnHideVoicesChanged() @@ -182,7 +185,8 @@ private void DrawSentencesInVoice(Voice voice) public void UpdateNotes() { - if (!gameObject.activeInHierarchy) + if (gameObject == null + || !gameObject.activeInHierarchy) { return; } diff --git a/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorUiNote.cs b/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorUiNote.cs index a16c722fc..7bce7353b 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorUiNote.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/EditorNote/EditorUiNote.cs @@ -109,6 +109,7 @@ public void SyncWithNote() Color color = songEditorSceneController.GetColorForVoice(Note.Sentence.Voice); SetColor(color); } + UpdateFontSize(); } void Start() diff --git a/UltraStar Play/Assets/Scenes/SongEditor/EditorSentence/EditorUiSentencePrefab.prefab b/UltraStar Play/Assets/Scenes/SongEditor/EditorSentence/EditorUiSentencePrefab.prefab index ed7f2f0f4..c7579f0dc 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/EditorSentence/EditorUiSentencePrefab.prefab +++ b/UltraStar Play/Assets/Scenes/SongEditor/EditorSentence/EditorUiSentencePrefab.prefab @@ -60,11 +60,11 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 1} m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_Sprite: {fileID: 21300000, guid: 55f3e2c7b25af03468f72991504fb124, type: 3} m_Type: 0 m_PreserveAspect: 0 @@ -139,11 +139,11 @@ MonoBehaviour: m_Material: {fileID: 0} m_Color: {r: 1, g: 1, b: 1, a: 0} m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_Sprite: {fileID: 0} m_Type: 0 m_PreserveAspect: 0 @@ -167,6 +167,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: backgroundImage: {fileID: 300791945676296084} + uiText: {fileID: 0} --- !u!114 &3718104915973145978 MonoBehaviour: m_ObjectHideFlags: 0 @@ -238,12 +239,12 @@ MonoBehaviour: m_EditorClassIdentifier: m_Material: {fileID: 0} m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} - m_RaycastTarget: 1 + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null m_FontData: m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} m_FontSize: 14 diff --git a/UltraStar Play/Assets/Scenes/SongEditor/Layers/SongEditorLayerManager.cs b/UltraStar Play/Assets/Scenes/SongEditor/Layers/SongEditorLayerManager.cs index e90a5d1b8..c894836ab 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/Layers/SongEditorLayerManager.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/Layers/SongEditorLayerManager.cs @@ -17,6 +17,9 @@ public class SongEditorLayerManager : MonoBehaviour, INeedInjection, ISceneInjec [Inject] private SongMetaChangeEventStream songMetaChangeEventStream; + [Inject] + private Settings settings; + public void OnSceneInjectionFinished() { songMetaChangeEventStream.Subscribe(OnSongMetaChanged); @@ -24,21 +27,14 @@ public void OnSceneInjectionFinished() private void OnSongMetaChanged(SongMetaChangeEvent changeEvent) { - if (!(changeEvent is MovedNotesToVoiceEvent)) - { - return; - } - - IReadOnlyCollection notes = (changeEvent as MovedNotesToVoiceEvent).notes; - foreach (Note note in notes) + if (changeEvent is MovedNotesToVoiceEvent mntve) { - if (note.Sentence != null) - { - RemoveNoteFromAllLayers(note); - } + mntve.notes + .Where(note => note.Sentence != null) + .ForEach(note => RemoveNoteFromAllLayers(note)); } } - + public void AddNoteToLayer(ESongEditorLayer layerKey, Note note) { layerKeyToLayerMap[layerKey].AddNote(note); @@ -107,4 +103,10 @@ public void RemoveNoteFromAllLayers(Note note) layer.RemoveNote(note); } } + + public bool IsVisible(Note note) + { + return note.Sentence?.Voice == null + || !settings.SongEditorSettings.HideVoices.Contains(note.Sentence.Voice.Name); + } } diff --git a/UltraStar Play/Assets/Scenes/SongEditor/OverviewBar/NoteOverviewVisualizer.cs b/UltraStar Play/Assets/Scenes/SongEditor/OverviewBar/NoteOverviewVisualizer.cs index b931d37fb..f5fde5873 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/OverviewBar/NoteOverviewVisualizer.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/OverviewBar/NoteOverviewVisualizer.cs @@ -77,6 +77,11 @@ private void DrawVoice(int songDurationInMillis, SongMeta songMeta, Voice voice, private void DrawNotes(int songDurationInMillis, SongMeta songMeta, Voice voice, Color color) { List notes = voice.Sentences.SelectMany(sentence => sentence.Notes).ToList(); + if (notes.IsNullOrEmpty()) + { + return; + } + // constant offset to // (a) ensure that midiNoteRange > 0, // (b) have some space to the border of the texture. diff --git a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorCopyPasteManager.cs b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorCopyPasteManager.cs index 9f54c824e..b886ba079 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorCopyPasteManager.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorCopyPasteManager.cs @@ -34,6 +34,9 @@ public class SongEditorCopyPasteManager : MonoBehaviour, INeedInjection [Inject] private EventSystem eventSystem; + [Inject] + private MoveNotesToOtherVoiceAction moveNotesToOtherVoiceAction; + private Voice copiedVoice; public List CopiedNotes @@ -57,6 +60,16 @@ void Start() InputManager.GetInputAction(R.InputActions.songEditor_paste).PerformedAsObservable() .Where(_ => !GameObjectUtils.InputFieldHasFocus(eventSystem)) .Subscribe(_ => PasteCopiedNotes()); + + // Cancel copy + InputManager.GetInputAction(R.InputActions.usplay_back).PerformedAsObservable(SongEditorSceneInputControl.cancelCopyPriority) + .Where(_ => !GameObjectUtils.InputFieldHasFocus(eventSystem)) + .Where(_ => HasCopiedNotes()) + .Subscribe(_ => + { + ClearCopiedNotes(); + InputManager.GetInputAction(R.InputActions.usplay_back).CancelNotifyForThisFrame(); + }); } private void MoveCopiedNotesToMillisInSong(double newMillis) @@ -84,11 +97,23 @@ private void MoveCopiedNotesToMillisInSong(double newMillis) private void ClearCopiedNotes() { + List notes = layerManager.GetNotes(ESongEditorLayer.CopyPaste); + notes.ForEach(note => editorNoteDisplayer.DeleteNote(note)); layerManager.ClearLayer(ESongEditorLayer.CopyPaste); } + public bool HasCopiedNotes() + { + return !layerManager.GetNotes(ESongEditorLayer.CopyPaste).IsNullOrEmpty(); + } + public void PasteCopiedNotes() { + if (!HasCopiedNotes()) + { + return; + } + int minBeat = CopiedNotes.Select(it => it.StartBeat).Min(); Sentence sentenceAtBeatWithVoice = SongMetaUtils.GetSentencesAtBeat(songMeta, minBeat) .Where(it => it.Voice != null).FirstOrDefault(); @@ -109,32 +134,12 @@ public void PasteCopiedNotes() } // Add the notes to the voice - foreach (Note note in CopiedNotes) - { - InsertNote(note, voice); - } + moveNotesToOtherVoiceAction.MoveNotesToVoiceAndNotify(songMeta, CopiedNotes, voice.Name); ClearCopiedNotes(); songMetaChangeEventStream.OnNext(new NotesAddedEvent()); } - private void InsertNote(Note note, Voice voice) - { - Sentence sentenceAtBeatOfVoice = SongMetaUtils.GetSentencesAtBeat(songMeta, note.StartBeat) - .Where(sentence => sentence.Voice == voice).FirstOrDefault(); - if (sentenceAtBeatOfVoice == null) - { - // Add sentence with note - Sentence newSentence = new Sentence(new List { note }, note.EndBeat); - newSentence.SetVoice(voice); - } - else - { - // Add note to existing sentence - note.SetSentence(sentenceAtBeatOfVoice); - } - } - public void CopySelectedNotes() { // Remove any old copied notes from the Ui. @@ -155,7 +160,7 @@ public void CopySelectedNotes() layerManager.AddNoteToLayer(ESongEditorLayer.CopyPaste, noteCopy); } - selectionController.SetSelection(CopiedNotes); + selectionController.ClearSelection(); editorNoteDisplayer.UpdateNotes(); } diff --git a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorNoteRecorder.cs b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorNoteRecorder.cs index 9b24252bf..69a9e7e23 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorNoteRecorder.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorNoteRecorder.cs @@ -169,7 +169,7 @@ private void RecordNote(int midiNote, double positionInSongInMillis, ESongEditor private void CreateNewRecordedNote(int midiNote, int currentBeat, ESongEditorLayer targetLayer) { - lastRecordedNote = new Note(ENoteType.Normal, currentBeat, 1, midiNote - 60, " "); + lastRecordedNote = new Note(ENoteType.Normal, currentBeat, 1, midiNote - 60, ""); songEditorLayerManager.AddNoteToLayer(targetLayer, lastRecordedNote); // EndBeat of new note is currentBeat + 1. Overwrite notes that start before this beat. diff --git a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneController.cs b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneController.cs index 86a156424..07787f859 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneController.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneController.cs @@ -200,6 +200,18 @@ public List GetAllNotes() result.AddRange(notesInLayers); return result; } + + public List GetAllVisibleNotes() + { + List result = new List(); + List notesInVoices = SongMetaUtils.GetAllNotes(SongMeta) + .Where(note => songEditorLayerManager.IsVisible(note)) + .ToList(); + List notesInLayers = songEditorLayerManager.GetAllNotes(); + result.AddRange(notesInVoices); + result.AddRange(notesInLayers); + return result; + } private void CreateVoiceToColorMap() { diff --git a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneInputControl.cs b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneInputControl.cs index fc0cf8253..637c6d487 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneInputControl.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSceneInputControl.cs @@ -18,6 +18,9 @@ public class SongEditorSceneInputControl : MonoBehaviour, INeedInjection { private const float PinchGestureMagnitudeThresholdInPixels = 100f; + public static readonly int cancelCopyPriority = 20; + public static readonly int cancelNoteSelectionPriority = 10; + [InjectedInInspector] public GameObject inputActionInfoOverlay; @@ -95,10 +98,12 @@ private void Start() // Play / pause InputManager.GetInputAction(R.InputActions.songEditor_togglePlayPause).PerformedAsObservable() .Where(_ => !InputUtils.IsKeyboardControlPressed()) + .Where(_ => !GameObjectUtils.InputFieldHasFocus(eventSystem)) .Subscribe(_ => songEditorSceneController.ToggleAudioPlayPause()); // Play only the selected notes InputManager.GetInputAction(R.InputActions.songEditor_playSelectedNotes).PerformedAsObservable() + .Where(_ => !GameObjectUtils.InputFieldHasFocus(eventSystem)) .Subscribe(_ => PlayAudioInRangeOfNotes(selectionController.GetSelectedNotes())); // Stop playback or return to last scene @@ -107,6 +112,7 @@ private void Start() // Select all notes InputManager.GetInputAction(R.InputActions.songEditor_selectAll).PerformedAsObservable() + .Where(_ => !GameObjectUtils.InputFieldHasFocus(eventSystem)) .Subscribe(_ => selectionController.SelectAll()); // Select next / previous note @@ -116,8 +122,19 @@ private void Start() InputManager.GetInputAction(R.InputActions.songEditor_selectPreviousNote).PerformedAsObservable() .Subscribe(_ => selectionController.SelectPreviousNote()); + // Deselect notes + InputManager.GetInputAction(R.InputActions.usplay_back).PerformedAsObservable(cancelNoteSelectionPriority) + .Where(_ => !InputUtils.AnyKeyboardModifierPressed() && !GameObjectUtils.InputFieldHasFocus(eventSystem)) + .Where(_ => selectionController.HasSelectedNotes()) + .Subscribe(_ => + { + selectionController.ClearSelection(); + InputManager.GetInputAction(R.InputActions.usplay_back).CancelNotifyForThisFrame(); + }); + // Delete notes InputManager.GetInputAction(R.InputActions.songEditor_delete).PerformedAsObservable() + .Where(_ => !GameObjectUtils.InputFieldHasFocus(eventSystem)) .Subscribe(_ => deleteNotesAction.ExecuteAndNotify(selectionController.GetSelectedNotes())); // Undo @@ -196,6 +213,11 @@ private void OnBack(InputAction.CallbackContext context) { songAudioPlayer.PauseAudio(); } + if (GameObjectUtils.InputFieldHasFocus(eventSystem)) + { + // Deselect TextArea + eventSystem.SetSelectedGameObject(null); + } else if (!inputFieldHasFocusOld) { // Special case: the back-Action in an InputField removes its focus. @@ -314,13 +336,17 @@ private void Update() && (Keyboard.current.leftArrowKey.isPressed || Keyboard.current.rightArrowKey.isPressed) && selectionController.GetSelectedNotes().IsNullOrEmpty()) { + int stepInMillis = InputUtils.IsKeyboardShiftPressed() + ? 1 + : 10; + if (Keyboard.current.leftArrowKey.isPressed) { - songAudioPlayer.PositionInSongInMillis -= 1; + songAudioPlayer.PositionInSongInMillis -= stepInMillis; } else if (Keyboard.current.rightArrowKey.isPressed) { - songAudioPlayer.PositionInSongInMillis += 1; + songAudioPlayer.PositionInSongInMillis += stepInMillis; } } diff --git a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSelectionController.cs b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSelectionController.cs index 9ddf9a32e..019ecee7a 100644 --- a/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSelectionController.cs +++ b/UltraStar Play/Assets/Scenes/SongEditor/SongEditorSelectionController.cs @@ -27,6 +27,9 @@ public class SongEditorSelectionController : MonoBehaviour, INeedInjection [Inject] private SongMeta songMeta; + [Inject] + private SongEditorLayerManager layerManager; + private readonly NoteHashSet selectedNotes = new NoteHashSet(); public List GetSelectedNotes() @@ -34,6 +37,12 @@ public List GetSelectedNotes() return new List(selectedNotes); } + public bool HasSelectedNotes() + { + return selectedNotes != null + && selectedNotes.Count > 0; + } + public bool IsSelected(Note note) { return selectedNotes.Contains(note); @@ -51,7 +60,7 @@ public void ClearSelection() public void SelectAll() { - List allNotes = songEditorSceneController.GetAllNotes(); + List allNotes = songEditorSceneController.GetAllVisibleNotes(); SetSelection(allNotes); } @@ -85,6 +94,10 @@ public void SetSelection(List notes) ClearSelection(); foreach (Note note in notes) { + if (!layerManager.IsVisible(note)) + { + continue; + } selectedNotes.Add(note); EditorUiNote uiNote = editorNoteDisplayer.GetUiNoteForNote(note); @@ -135,7 +148,7 @@ public void SelectNextNote(bool updatePositionInSong = true) return; } - List notes = songEditorSceneController.GetAllNotes(); + List notes = songEditorSceneController.GetAllVisibleNotes(); int maxEndBeat = selectedNotes.Select(it => it.EndBeat).Max(); // Find the next note, i.e., the note right of maxEndBeat with the smallest distance to it. @@ -174,7 +187,7 @@ public void SelectPreviousNote(bool updatePositionInSong = true) return; } - List notes = songEditorSceneController.GetAllNotes(); + List notes = songEditorSceneController.GetAllVisibleNotes(); int minStartBeat = selectedNotes.Select(it => it.StartBeat).Min(); // Find the previous note, i.e., the note left of minStartBeat with the smallest distance to it. diff --git a/UltraStar Play/Assets/Scenes/SongSelect/SongSelectSceneInputControl.cs b/UltraStar Play/Assets/Scenes/SongSelect/SongSelectSceneInputControl.cs index c568a7cc0..c8f97af34 100644 --- a/UltraStar Play/Assets/Scenes/SongSelect/SongSelectSceneInputControl.cs +++ b/UltraStar Play/Assets/Scenes/SongSelect/SongSelectSceneInputControl.cs @@ -108,13 +108,11 @@ private void OnScrollWheel(InputAction.CallbackContext context) private void OnNavigate(InputAction.CallbackContext context) { - if (context.ReadValue().x > 0 - && !songSelectSceneController.IsSearchEnabled()) + if (context.ReadValue().x > 0) { songRouletteController.SelectNextSong(); } - if (context.ReadValue().x < 0 - && !songSelectSceneController.IsSearchEnabled()) + if (context.ReadValue().x < 0) { songRouletteController.SelectPreviousSong(); } diff --git a/UltraStar Play/Assets/VERSION.txt b/UltraStar Play/Assets/VERSION.txt index c82f0b0a7..99ae36b7d 100644 --- a/UltraStar Play/Assets/VERSION.txt +++ b/UltraStar Play/Assets/VERSION.txt @@ -1,4 +1,4 @@ -release = 0.3 -build_timestamp = 2104081801 -commit_hash = 5e07588 +release = 0.3.1 +build_timestamp = 2105021747 +commit_hash = f8d1102 website_link = https://usplay.net/ diff --git a/UltraStar Play/ProjectSettings/ProjectSettings.asset b/UltraStar Play/ProjectSettings/ProjectSettings.asset index 65ed1c06b..ca8db7a46 100644 --- a/UltraStar Play/ProjectSettings/ProjectSettings.asset +++ b/UltraStar Play/ProjectSettings/ProjectSettings.asset @@ -127,7 +127,7 @@ PlayerSettings: 16:10: 1 16:9: 1 Others: 1 - bundleVersion: 0.3 + bundleVersion: 0.3.1 preloadedAssets: - {fileID: 11400000, guid: 14252c98bd239904da98289cb5928ea0, type: 2} metroInputSource: 0