7
7
using osu . Framework . Allocation ;
8
8
using osu . Framework . Bindables ;
9
9
using osu . Framework . Graphics ;
10
+ using osu . Framework . Graphics . Primitives ;
10
11
using osu . Framework . Logging ;
11
12
using osu . Game . Rulesets . Edit ;
12
13
using osu . Game . Rulesets . Karaoke . Edit . ChangeHandlers . Lyrics ;
@@ -45,18 +46,9 @@ protected partial class RubyTagSelectionHandler : LyricPropertySelectionHandler<
45
46
[ Resolved ]
46
47
private IPreviewLyricPositionProvider previewLyricPositionProvider { get ; set ; } = null ! ;
47
48
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 ( ) ;
60
52
61
53
#region User Input Handling
62
54
@@ -96,41 +88,126 @@ void setRubyTagShifting(IEnumerable<RubyTag> rubyTags, int offset)
96
88
=> rubyTagsChangeHandler . ShiftingIndex ( rubyTags , offset ) ;
97
89
}
98
90
99
- public override bool HandleScale ( Vector2 scale , Anchor anchor )
91
+ private int ? calculateNewIndex ( RubyTag rubyTag , float offset , Anchor anchor )
100
92
{
101
- deltaScaleSize += scale . X ;
93
+ // get real left-side and right-side position
94
+ var rect = previewLyricPositionProvider . GetRubyTagByPosition ( rubyTag ) ;
102
95
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.") ;
107
99
108
100
switch ( anchor )
109
101
{
110
102
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 ) ;
117
105
118
106
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 ) ;
125
109
126
110
default :
127
- return false ;
111
+ throw new ArgumentOutOfRangeException ( nameof ( anchor ) ) ;
128
112
}
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 ;
129
199
130
200
void setRubyTagIndex ( RubyTag rubyTag , int ? startPosition , int ? endPosition )
131
201
=> rubyTagsChangeHandler . SetIndex ( rubyTag , startPosition , endPosition ) ;
132
202
}
133
203
204
+ public override void Commit ( )
205
+ {
206
+ base . Commit ( ) ;
207
+
208
+ OriginalSurroundingQuad = null ;
209
+ }
210
+
134
211
private int ? calculateNewIndex ( RubyTag rubyTag , float offset , Anchor anchor )
135
212
{
136
213
// get real left-side and right-side position
@@ -154,10 +231,5 @@ void setRubyTagIndex(RubyTag rubyTag, int? startPosition, int? endPosition)
154
231
throw new ArgumentOutOfRangeException ( nameof ( anchor ) ) ;
155
232
}
156
233
}
157
-
158
- #endregion
159
-
160
- protected override void DeleteItems ( IEnumerable < RubyTag > items )
161
- => rubyTagsChangeHandler . RemoveRange ( items ) ;
162
234
}
163
235
}
0 commit comments