diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/Lyrics/Blueprints/RubyBlueprintContainer.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/Lyrics/Blueprints/RubyBlueprintContainer.cs index 58a38fa0c..ebaf8959a 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/Lyrics/Blueprints/RubyBlueprintContainer.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/Lyrics/Blueprints/RubyBlueprintContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Framework.Logging; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Karaoke.Edit.ChangeHandlers.Lyrics; @@ -45,18 +46,9 @@ protected partial class RubyTagSelectionHandler : LyricPropertySelectionHandler< [Resolved] private IPreviewLyricPositionProvider previewLyricPositionProvider { get; set; } = null!; - private float deltaScaleSize; - - protected override void OnSelectionChanged() - { - base.OnSelectionChanged(); - - // only select one ruby tag can let user drag to change start and end index. - ScaleHandler.CanScaleX.Value = SelectedItems.Count == 1; - - // should clear delta size before change start/end index. - deltaScaleSize = 0; - } + // need to implement this because blueprint container support change the x scale. + public override SelectionScaleHandler CreateScaleHandler() + => new RubyTagSelectionScaleHandler(); #region User Input Handling @@ -96,41 +88,126 @@ void setRubyTagShifting(IEnumerable rubyTags, int offset) => rubyTagsChangeHandler.ShiftingIndex(rubyTags, offset); } - public override bool HandleScale(Vector2 scale, Anchor anchor) + private int? calculateNewIndex(RubyTag rubyTag, float offset, Anchor anchor) { - deltaScaleSize += scale.X; + // get real left-side and right-side position + var rect = previewLyricPositionProvider.GetRubyTagByPosition(rubyTag); - // this feature only works if only select one ruby tag. - var selectedRubyTag = SelectedItems.FirstOrDefault(); - if (selectedRubyTag == null) - return false; + // todo: need to think about how to handle the case if the text-tag already out of the range of the text. + if (rect == null) + throw new InvalidOperationException($"{nameof(rubyTag)} not in the range of the text."); switch (anchor) { case Anchor.CentreLeft: - int? newStartIndex = calculateNewIndex(selectedRubyTag, deltaScaleSize, anchor); - if (newStartIndex == null || !RubyTagUtils.ValidNewStartIndex(selectedRubyTag, newStartIndex.Value)) - return false; - - setRubyTagIndex(selectedRubyTag, newStartIndex, null); - return true; + var leftPosition = rect.Value.BottomLeft + new Vector2(offset, 0); + return previewLyricPositionProvider.GetCharIndexByPosition(leftPosition); case Anchor.CentreRight: - int? newEndIndex = calculateNewIndex(selectedRubyTag, deltaScaleSize, anchor); - if (newEndIndex == null || !RubyTagUtils.ValidNewEndIndex(selectedRubyTag, newEndIndex.Value)) - return false; - - setRubyTagIndex(selectedRubyTag, null, newEndIndex); - return true; + var rightPosition = rect.Value.BottomRight + new Vector2(offset, 0); + return previewLyricPositionProvider.GetCharIndexByPosition(rightPosition); default: - return false; + throw new ArgumentOutOfRangeException(nameof(anchor)); } + } + + #endregion + + protected override void DeleteItems(IEnumerable items) + => rubyTagsChangeHandler.RemoveRange(items); + } + + private partial class RubyTagSelectionScaleHandler : SelectionScaleHandler + { + [Resolved] + private IPreviewLyricPositionProvider previewLyricPositionProvider { get; set; } = null!; + + [Resolved] + private ILyricRubyTagsChangeHandler rubyTagsChangeHandler { get; set; } = null!; + + private BindableList selectedItems { get; } = new(); + + [BackgroundDependencyLoader] + private void load(IEditRubyModeState editRubyModeState) + { + selectedItems.BindTo(editRubyModeState.SelectedItems); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedItems.CollectionChanged += (_, __) => updateState(); + updateState(); + } + + private void updateState() + { + // only select one ruby tag can let user drag to change start and end index. + CanScaleX.Value = selectedItems.Count == 1; + } + + public override void Begin() + { + base.Begin(); + + var selectedItemsRect = selectedItems + .Select(x => previewLyricPositionProvider.GetRubyTagByPosition(x)) + .Where(rect => rect != null) + .OfType(); + + var rect = selectedItemsRect.Aggregate(new RectangleF(), RectangleF.Union); + + // Update the quad + OriginalSurroundingQuad = Quad.FromRectangle(rect); + } + + public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both, float axisRotation = 0) + { + // this feature only works if only select one ruby tag. + var selectedRubyTag = selectedItems.FirstOrDefault(); + if (selectedRubyTag == null) + return; + + if (adjustAxis != Axes.X) + throw new InvalidOperationException("Only can adjust x axes"); + + if (origin == null || OriginalSurroundingQuad == null) + return; + + float offset = OriginalSurroundingQuad.Value.Width * (scale.X - 1); + + if (origin.Value.X > OriginalSurroundingQuad.Value.Centre.X) + { + int? newStartIndex = calculateNewIndex(selectedRubyTag, -offset, Anchor.CentreLeft); + if (newStartIndex == null || !RubyTagUtils.ValidNewStartIndex(selectedRubyTag, newStartIndex.Value)) + return; + + setRubyTagIndex(selectedRubyTag, newStartIndex, null); + } + else + { + int? newEndIndex = calculateNewIndex(selectedRubyTag, offset, Anchor.CentreRight); + if (newEndIndex == null || !RubyTagUtils.ValidNewEndIndex(selectedRubyTag, newEndIndex.Value)) + return; + + setRubyTagIndex(selectedRubyTag, null, newEndIndex); + } + + return; void setRubyTagIndex(RubyTag rubyTag, int? startPosition, int? endPosition) => rubyTagsChangeHandler.SetIndex(rubyTag, startPosition, endPosition); } + public override void Commit() + { + base.Commit(); + + OriginalSurroundingQuad = null; + } + private int? calculateNewIndex(RubyTag rubyTag, float offset, Anchor anchor) { // get real left-side and right-side position @@ -154,10 +231,5 @@ void setRubyTagIndex(RubyTag rubyTag, int? startPosition, int? endPosition) throw new ArgumentOutOfRangeException(nameof(anchor)); } } - - #endregion - - protected override void DeleteItems(IEnumerable items) - => rubyTagsChangeHandler.RemoveRange(items); } }