diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldSwitchButton.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldSwitchButton.cs index 5dd57cef6..6cb5c7014 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldSwitchButton.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldSwitchButton.cs @@ -59,11 +59,6 @@ protected void TriggerSelect() SelectedItems.Add(item); } - protected void TriggerUnselect() - { - SelectedItems.Remove(item); - } - protected abstract bool GetFieldValue(T item); protected abstract void ApplyValue(T item, bool value); @@ -73,13 +68,7 @@ protected void TriggerUnselect() Selected = selected => { if (selected) - { TriggerSelect(); - } - else - { - TriggerUnselect(); - } } }; diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldTextBox.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldTextBox.cs index 215e6c1b5..de829ef12 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldTextBox.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Components/LabelledObjectFieldTextBox.cs @@ -45,6 +45,9 @@ protected LabelledObjectFieldTextBox(T item) { bool highLight = SelectedItems.Contains(item); Component.HighLight = highLight; + + if (SelectedItems.Contains(item) && SelectedItems.Count == 1) + focus(); }); if (InternalChildren[1] is not FillFlowContainer fillFlowContainer) @@ -70,11 +73,6 @@ protected void TriggerSelect() SelectedItems.Add(item); } - protected void TriggerUnselect() - { - SelectedItems.Remove(item); - } - protected abstract string GetFieldValue(T item); protected abstract void ApplyValue(T item, string value); @@ -89,24 +87,25 @@ protected void TriggerUnselect() Selected = selected => { if (selected) - { TriggerSelect(); - } - else - { - TriggerUnselect(); - } } }; - public void Focus() + private void focus() { Schedule(() => { + var focusedDrawable = GetContainingInputManager().FocusedDrawable; + if (focusedDrawable != null && IsFocused(focusedDrawable)) + return; + GetContainingInputManager().ChangeFocus(Component); }); } + protected virtual bool IsFocused(Drawable focusedDrawable) + => focusedDrawable == Component; + protected class ObjectFieldTextBox : OsuTextBox { [Resolved] @@ -124,6 +123,9 @@ protected override void OnFocusLost(FocusLostEvent e) { base.OnFocusLost(e); + // should not change the border size because still need to highlight the textarea without focus. + BorderThickness = 3f; + // note: should trigger commit event first in the base class. Selected?.Invoke(false); } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Notes/NoteEditPropertySection.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Notes/NoteEditPropertySection.cs index 05f451b01..33f75ba3f 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Notes/NoteEditPropertySection.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/Notes/NoteEditPropertySection.cs @@ -74,16 +74,6 @@ void reCreateEditComponents() } } - protected override void UpdateDisabledState(bool disabled) - { - if (disabled) - return; - - // should auto-focus to the first note property if change the lyric. - var firstTextTagTextBox = Children.OfType>().FirstOrDefault(); - firstTextTagTextBox?.Focus(); - } - [BackgroundDependencyLoader] private void load(IEditNoteModeState editNoteModeState) { diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/Components/LabelledTextTagTextBox.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/Components/LabelledTextTagTextBox.cs index 9273abdd4..12e05607b 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/Components/LabelledTextTagTextBox.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/Components/LabelledTextTagTextBox.cs @@ -65,14 +65,6 @@ protected LabelledTextTagTextBox(Lyric lyric, T textTag) // trigger selected if hover on delete button. TriggerSelect(); } - else - { - // do not clear current selected if typing. - if (Component.HasFocus) - return; - - TriggerUnselect(); - } } } }); @@ -95,13 +87,7 @@ protected LabelledTextTagTextBox(Lyric lyric, T textTag) Selected = selected => { if (selected) - { TriggerSelect(); - } - else - { - TriggerUnselect(); - } }, Action = (indexType, action) => { @@ -153,6 +139,9 @@ protected sealed override string GetFieldValue(T item) protected abstract void RemoveTextTag(T item); + protected override bool IsFocused(Drawable focusedDrawable) + => base.IsFocused(focusedDrawable) || focusedDrawable == indexShiftingPart; + public new CompositeDrawable TabbableContentContainer { set diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/TextTagEditSection.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/TextTagEditSection.cs index 7e2cf6241..3937386fa 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/TextTagEditSection.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/Extends/RubyRomaji/TextTagEditSection.cs @@ -57,16 +57,6 @@ protected override void OnLyricChanged(Lyric lyric) TextTags.BindTo(GetBindableTextTags(lyric)); } - protected override void UpdateDisabledState(bool disabled) - { - if (disabled) - return; - - // should auto-focus to the first time-tag if change the lyric. - var firstTextTagTextBox = Children.OfType>().FirstOrDefault(); - firstTextTagTextBox?.Focus(); - } - private void addCreateButton() { var fillFlowContainer = Content as FillFlowContainer; diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditNoteModeState.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditNoteModeState.cs index a22ecc379..927c433e5 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditNoteModeState.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditNoteModeState.cs @@ -1,10 +1,12 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Rulesets.Karaoke.Edit.Lyrics.Extends.Notes; +using osu.Game.Rulesets.Karaoke.Edit.Utils; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Karaoke.Utils; using osu.Game.Rulesets.Objects; @@ -12,27 +14,37 @@ namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics.States.Modes { - public class EditNoteModeState : Component, IEditNoteModeState + public class EditNoteModeState : ModeStateWithBlueprintContainer, IEditNoteModeState { private readonly Bindable bindableEditMode = new(); private readonly BindableList selectedHitObjects = new(); + [Resolved, AllowNull] + private EditorBeatmap editorBeatmap { get; set; } + public IBindable BindableEditMode => bindableEditMode; public void ChangeEditMode(NoteEditMode mode) => bindableEditMode.Value = mode; - public BindableList SelectedItems { get; } = new(); - public Bindable BindableSpecialAction { get; } = new(); public Bindable NoteEditPropertyMode { get; } = new(); [BackgroundDependencyLoader] - private void load(EditorBeatmap editorBeatmap) + private void load() { BindablesUtils.Sync(SelectedItems, selectedHitObjects); selectedHitObjects.BindTo(editorBeatmap.SelectedHitObjects); } + + protected override bool IsWriteLyricPropertyLocked(Lyric lyric) + => HitObjectWritableUtils.IsCreateOrRemoveNoteLocked(lyric); + + protected override bool SelectFirstProperty(Lyric lyric) + => BindableEditMode.Value == NoteEditMode.Edit; + + protected override IEnumerable SelectableProperties(Lyric lyric) + => EditorBeatmapUtils.GetNotesByLyric(editorBeatmap, lyric); } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRomajiModeState.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRomajiModeState.cs index 9edf45583..87a7b209d 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRomajiModeState.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRomajiModeState.cs @@ -1,13 +1,14 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Bindables; -using osu.Framework.Graphics; +using osu.Game.Rulesets.Karaoke.Edit.Utils; using osu.Game.Rulesets.Karaoke.Objects; namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics.States.Modes { - public class EditRomajiModeState : Component, IEditRomajiModeState + public class EditRomajiModeState : ModeStateWithBlueprintContainer, IEditRomajiModeState { private readonly Bindable bindableEditMode = new(); @@ -16,6 +17,13 @@ public class EditRomajiModeState : Component, IEditRomajiModeState public void ChangeEditMode(TextTagEditMode mode) => bindableEditMode.Value = mode; - public BindableList SelectedItems { get; } = new(); + protected override bool IsWriteLyricPropertyLocked(Lyric lyric) + => HitObjectWritableUtils.IsWriteLyricPropertyLocked(lyric, nameof(Lyric.RomajiTags)); + + protected override bool SelectFirstProperty(Lyric lyric) + => BindableEditMode.Value == TextTagEditMode.Edit; + + protected override IEnumerable SelectableProperties(Lyric lyric) + => lyric.RomajiTags; } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRubyModeState.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRubyModeState.cs index 2764e1865..7c4a6a03f 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRubyModeState.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/EditRubyModeState.cs @@ -1,13 +1,14 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using osu.Framework.Bindables; -using osu.Framework.Graphics; +using osu.Game.Rulesets.Karaoke.Edit.Utils; using osu.Game.Rulesets.Karaoke.Objects; namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics.States.Modes { - public class EditRubyModeState : Component, IEditRubyModeState + public class EditRubyModeState : ModeStateWithBlueprintContainer, IEditRubyModeState { private readonly Bindable bindableEditMode = new(); @@ -16,6 +17,13 @@ public class EditRubyModeState : Component, IEditRubyModeState public void ChangeEditMode(TextTagEditMode mode) => bindableEditMode.Value = mode; - public BindableList SelectedItems { get; } = new(); + protected override bool IsWriteLyricPropertyLocked(Lyric lyric) + => HitObjectWritableUtils.IsWriteLyricPropertyLocked(lyric, nameof(Lyric.RubyTags)); + + protected override bool SelectFirstProperty(Lyric lyric) + => BindableEditMode.Value == TextTagEditMode.Edit; + + protected override IEnumerable SelectableProperties(Lyric lyric) + => lyric.RubyTags; } } diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/ModeStateWithBlueprintContainer.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/ModeStateWithBlueprintContainer.cs new file mode 100644 index 000000000..c79316c12 --- /dev/null +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/ModeStateWithBlueprintContainer.cs @@ -0,0 +1,91 @@ +// Copyright (c) andy840119 . Licensed under the GPL Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Karaoke.Edit.Lyrics.CaretPosition; +using osu.Game.Rulesets.Karaoke.Objects; + +namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics.States.Modes +{ + public abstract class ModeStateWithBlueprintContainer : Component, IHasBlueprintSelection where TObject : class + { + private readonly IBindable bindableMode = new Bindable(); + private readonly IBindable bindableCaretPosition = new Bindable(); + private readonly IBindable bindableLyricPropertyWritableVersion = new Bindable(); + + public BindableList SelectedItems { get; } = new(); + + protected ModeStateWithBlueprintContainer() + { + bindableMode.BindValueChanged(e => + { + TriggerDisableStateChanged(); + }); + + bindableCaretPosition.BindValueChanged(e => + { + bindableLyricPropertyWritableVersion.UnbindBindings(); + + var lyric = e.NewValue?.Lyric; + + if (lyric == null) + return; + + bindableLyricPropertyWritableVersion.BindTo(lyric.LyricPropertyWritableVersion); + TriggerDisableStateChanged(); + }); + + bindableLyricPropertyWritableVersion.BindValueChanged(_ => + { + TriggerDisableStateChanged(); + }); + } + + protected virtual void TriggerDisableStateChanged() + { + var caret = bindableCaretPosition.Value; + if (caret == null) + return; + + var lyric = caret.Lyric; + var generateType = caret.GenerateType; + + SelectedItems.Clear(); + bool locked = IsWriteLyricPropertyLocked(lyric); + if (locked) + return; + + switch (generateType) + { + case CaretGenerateType.Action: + if (SelectFirstProperty(lyric)) + SelectedItems.Add(SelectableProperties(lyric).FirstOrDefault()); + break; + + case CaretGenerateType.TargetLyric: + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + protected abstract bool IsWriteLyricPropertyLocked(Lyric lyric); + + protected abstract bool SelectFirstProperty(Lyric lyric); + + protected abstract IEnumerable SelectableProperties(Lyric lyric); + + [BackgroundDependencyLoader] + private void load(ILyricEditorState state, ILyricCaretState lyricCaretState) + { + bindableMode.BindTo(state.BindableMode); + bindableCaretPosition.BindTo(lyricCaretState.BindableCaretPosition); + } + } +} diff --git a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/TimeTagModeState.cs b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/TimeTagModeState.cs index 702daa9a6..f79691a79 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/TimeTagModeState.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/Lyrics/States/Modes/TimeTagModeState.cs @@ -1,23 +1,22 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Rulesets.Karaoke.Edit.Utils; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Screens.Edit; namespace osu.Game.Rulesets.Karaoke.Edit.Lyrics.States.Modes { - public class TimeTagModeState : Component, ITimeTagModeState + public class TimeTagModeState : ModeStateWithBlueprintContainer, ITimeTagModeState { private readonly Bindable bindableEditMode = new(); public IBindable BindableEditMode => bindableEditMode; - public BindableList SelectedItems { get; } = new(); - public BindableFloat BindableRecordZoom { get; } = new(); public BindableFloat BindableAdjustZoom { get; } = new(); @@ -36,5 +35,14 @@ private void load(EditorClock editorClock) public void ChangeEditMode(TimeTagEditMode mode) => bindableEditMode.Value = mode; + + protected override bool IsWriteLyricPropertyLocked(Lyric lyric) + => HitObjectWritableUtils.IsWriteLyricPropertyLocked(lyric, nameof(Lyric.TimeTags)); + + protected override bool SelectFirstProperty(Lyric lyric) + => false; + + protected override IEnumerable SelectableProperties(Lyric lyric) + => Array.Empty(); } }