77using osu . Framework . Allocation ;
88using osu . Framework . Bindables ;
99using osu . Framework . Graphics ;
10+ using osu . Framework . Graphics . Primitives ;
1011using osu . Framework . Logging ;
1112using osu . Game . Rulesets . Edit ;
1213using osu . Game . Rulesets . Karaoke . Edit . ChangeHandlers . Lyrics ;
@@ -45,18 +46,9 @@ protected partial class RubyTagSelectionHandler : LyricPropertySelectionHandler<
4546 [ Resolved ]
4647 private IPreviewLyricPositionProvider previewLyricPositionProvider { get ; set ; } = null ! ;
4748
48- private float deltaScaleSize ;
49-
50- protected override void OnSelectionChanged ( )
51- {
52- base . OnSelectionChanged ( ) ;
53-
54- // only select one ruby tag can let user drag to change start and end index.
55- ScaleHandler . CanScaleX . Value = SelectedItems . Count == 1 ;
56-
57- // should clear delta size before change start/end index.
58- deltaScaleSize = 0 ;
59- }
49+ // need to implement this because blueprint container support change the x scale.
50+ public override SelectionScaleHandler CreateScaleHandler ( )
51+ => new RubyTagSelectionScaleHandler ( ) ;
6052
6153 #region User Input Handling
6254
@@ -96,41 +88,126 @@ void setRubyTagShifting(IEnumerable<RubyTag> rubyTags, int offset)
9688 => rubyTagsChangeHandler . ShiftingIndex ( rubyTags , offset ) ;
9789 }
9890
99- public override bool HandleScale ( Vector2 scale , Anchor anchor )
91+ private int ? calculateNewIndex ( RubyTag rubyTag , float offset , Anchor anchor )
10092 {
101- deltaScaleSize += scale . X ;
93+ // get real left-side and right-side position
94+ var rect = previewLyricPositionProvider . GetRubyTagByPosition ( rubyTag ) ;
10295
103- // this feature only works if only select one ruby tag.
104- var selectedRubyTag = SelectedItems . FirstOrDefault ( ) ;
105- if ( selectedRubyTag == null )
106- return false ;
96+ // todo: need to think about how to handle the case if the text-tag already out of the range of the text.
97+ if ( rect == null )
98+ throw new InvalidOperationException ( $ "{ nameof ( rubyTag ) } not in the range of the text.") ;
10799
108100 switch ( anchor )
109101 {
110102 case Anchor . CentreLeft :
111- int ? newStartIndex = calculateNewIndex ( selectedRubyTag , deltaScaleSize , anchor ) ;
112- if ( newStartIndex == null || ! RubyTagUtils . ValidNewStartIndex ( selectedRubyTag , newStartIndex . Value ) )
113- return false ;
114-
115- setRubyTagIndex ( selectedRubyTag , newStartIndex , null ) ;
116- return true ;
103+ var leftPosition = rect . Value . BottomLeft + new Vector2 ( offset , 0 ) ;
104+ return previewLyricPositionProvider . GetCharIndexByPosition ( leftPosition ) ;
117105
118106 case Anchor . CentreRight :
119- int ? newEndIndex = calculateNewIndex ( selectedRubyTag , deltaScaleSize , anchor ) ;
120- if ( newEndIndex == null || ! RubyTagUtils . ValidNewEndIndex ( selectedRubyTag , newEndIndex . Value ) )
121- return false ;
122-
123- setRubyTagIndex ( selectedRubyTag , null , newEndIndex ) ;
124- return true ;
107+ var rightPosition = rect . Value . BottomRight + new Vector2 ( offset , 0 ) ;
108+ return previewLyricPositionProvider . GetCharIndexByPosition ( rightPosition ) ;
125109
126110 default :
127- return false ;
111+ throw new ArgumentOutOfRangeException ( nameof ( anchor ) ) ;
128112 }
113+ }
114+
115+ #endregion
116+
117+ protected override void DeleteItems ( IEnumerable < RubyTag > items )
118+ => rubyTagsChangeHandler . RemoveRange ( items ) ;
119+ }
120+
121+ private partial class RubyTagSelectionScaleHandler : SelectionScaleHandler
122+ {
123+ [ Resolved ]
124+ private IPreviewLyricPositionProvider previewLyricPositionProvider { get ; set ; } = null ! ;
125+
126+ [ Resolved ]
127+ private ILyricRubyTagsChangeHandler rubyTagsChangeHandler { get ; set ; } = null ! ;
128+
129+ private BindableList < RubyTag > selectedItems { get ; } = new ( ) ;
130+
131+ [ BackgroundDependencyLoader ]
132+ private void load ( IEditRubyModeState editRubyModeState )
133+ {
134+ selectedItems . BindTo ( editRubyModeState . SelectedItems ) ;
135+ }
136+
137+ protected override void LoadComplete ( )
138+ {
139+ base . LoadComplete ( ) ;
140+
141+ selectedItems . CollectionChanged += ( _ , __ ) => updateState ( ) ;
142+ updateState ( ) ;
143+ }
144+
145+ private void updateState ( )
146+ {
147+ // only select one ruby tag can let user drag to change start and end index.
148+ CanScaleX . Value = selectedItems . Count == 1 ;
149+ }
150+
151+ public override void Begin ( )
152+ {
153+ base . Begin ( ) ;
154+
155+ var selectedItemsRect = selectedItems
156+ . Select ( x => previewLyricPositionProvider . GetRubyTagByPosition ( x ) )
157+ . Where ( rect => rect != null )
158+ . OfType < RectangleF > ( ) ;
159+
160+ var rect = selectedItemsRect . Aggregate ( new RectangleF ( ) , RectangleF . Union ) ;
161+
162+ // Update the quad
163+ OriginalSurroundingQuad = Quad . FromRectangle ( rect ) ;
164+ }
165+
166+ public override void Update ( Vector2 scale , Vector2 ? origin = null , Axes adjustAxis = Axes . Both , float axisRotation = 0 )
167+ {
168+ // this feature only works if only select one ruby tag.
169+ var selectedRubyTag = selectedItems . FirstOrDefault ( ) ;
170+ if ( selectedRubyTag == null )
171+ return ;
172+
173+ if ( adjustAxis != Axes . X )
174+ throw new InvalidOperationException ( "Only can adjust x axes" ) ;
175+
176+ if ( origin == null || OriginalSurroundingQuad == null )
177+ return ;
178+
179+ float offset = OriginalSurroundingQuad . Value . Width * ( scale . X - 1 ) ;
180+
181+ if ( origin . Value . X > OriginalSurroundingQuad . Value . Centre . X )
182+ {
183+ int ? newStartIndex = calculateNewIndex ( selectedRubyTag , - offset , Anchor . CentreLeft ) ;
184+ if ( newStartIndex == null || ! RubyTagUtils . ValidNewStartIndex ( selectedRubyTag , newStartIndex . Value ) )
185+ return ;
186+
187+ setRubyTagIndex ( selectedRubyTag , newStartIndex , null ) ;
188+ }
189+ else
190+ {
191+ int ? newEndIndex = calculateNewIndex ( selectedRubyTag , offset , Anchor . CentreRight ) ;
192+ if ( newEndIndex == null || ! RubyTagUtils . ValidNewEndIndex ( selectedRubyTag , newEndIndex . Value ) )
193+ return ;
194+
195+ setRubyTagIndex ( selectedRubyTag , null , newEndIndex ) ;
196+ }
197+
198+ return ;
129199
130200 void setRubyTagIndex ( RubyTag rubyTag , int ? startPosition , int ? endPosition )
131201 => rubyTagsChangeHandler . SetIndex ( rubyTag , startPosition , endPosition ) ;
132202 }
133203
204+ public override void Commit ( )
205+ {
206+ base . Commit ( ) ;
207+
208+ OriginalSurroundingQuad = null ;
209+ }
210+
134211 private int ? calculateNewIndex ( RubyTag rubyTag , float offset , Anchor anchor )
135212 {
136213 // get real left-side and right-side position
@@ -154,10 +231,5 @@ void setRubyTagIndex(RubyTag rubyTag, int? startPosition, int? endPosition)
154231 throw new ArgumentOutOfRangeException ( nameof ( anchor ) ) ;
155232 }
156233 }
157-
158- #endregion
159-
160- protected override void DeleteItems ( IEnumerable < RubyTag > items )
161- => rubyTagsChangeHandler . RemoveRange ( items ) ;
162234 }
163235}
0 commit comments