Skip to content

Commit 7a4f57a

Browse files
committed
fix regression: fix word splitting and note splitting with ~ puts the space on the wrong side
and fix mouse area
1 parent 7cf1778 commit 7a4f57a

1 file changed

Lines changed: 150 additions & 31 deletions

File tree

src/screens/UScreenEditSub.pas

Lines changed: 150 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{* UltraStar Deluxe - Karaoke Game
1+
{* UltraStar Deluxe - Karaoke Game
22
*
33
* UltraStar Deluxe is the legal property of its developers, whose names
44
* are too numerous to list here. Please refer to the COPYRIGHT
@@ -1982,7 +1982,7 @@ procedure TScreenEditSub.ToggleTextEditMode(SDL_ModState: word);
19821982
begin
19831983
if Interaction = InteractiveNoteId[NoteIndex] then
19841984
begin
1985-
if (SDL_GetTicks() - LastClickTime < 250) and (SDL_ModState = 0) then
1985+
if (SDL_GetTicks() - LastClickTime < 350) then
19861986
begin
19871987
CopyToUndo;
19881988
GoldenRec.KillAll;
@@ -3710,15 +3710,103 @@ procedure TScreenEditSub.DivideNote(doubleclick: boolean);
37103710
NoteIndex: Integer;
37113711
CutPosition: Integer;
37123712
SpacePosition: Integer;
3713+
FirstSpacePos: Integer;
3714+
LeftWordCount: Integer;
3715+
TotalWordCount: Integer;
37133716
TempR: real;
37143717
NoteDuration: Integer;
3715-
TempStr: UCS4String;
3718+
SourceText: UTF8String;
3719+
SourceTextLen: Integer;
3720+
SourceNoteText: UTF8String;
3721+
ShouldInsertContinuation: Boolean;
3722+
LeftPart: UTF8String;
3723+
RightPart: UTF8String;
3724+
3725+
function CountWords(const S: UTF8String): Integer;
3726+
var
3727+
i: Integer;
3728+
InWord: Boolean;
3729+
begin
3730+
Result := 0;
3731+
InWord := false;
3732+
for i := 1 to Length(S) do
3733+
begin
3734+
if S[i] <> ' ' then
3735+
begin
3736+
if not InWord then
3737+
begin
3738+
Inc(Result);
3739+
InWord := true;
3740+
end;
3741+
end
3742+
else
3743+
InWord := false;
3744+
end;
3745+
end;
3746+
3747+
function IsVowel(const Ch: UTF8String): Boolean;
3748+
begin
3749+
Result :=
3750+
(Ch = 'a') or (Ch = 'e') or (Ch = 'i') or (Ch = 'o') or (Ch = 'u') or (Ch = 'y') or
3751+
(Ch = 'A') or (Ch = 'E') or (Ch = 'I') or (Ch = 'O') or (Ch = 'U') or (Ch = 'Y');
3752+
end;
3753+
3754+
function HasVowel(const S: UTF8String): Boolean;
3755+
var
3756+
Chars: UCS4String;
3757+
i: Integer;
3758+
begin
3759+
Result := false;
3760+
if S = '' then
3761+
Exit;
3762+
3763+
Chars := UTF8ToUCS4String(S);
3764+
for i := 0 to High(Chars) do
3765+
begin
3766+
if IsVowel(UCS4ToUTF8String(Chars[i])) then
3767+
begin
3768+
Result := true;
3769+
Exit;
3770+
end;
3771+
end;
3772+
end;
3773+
3774+
// Returns the first split position at a real word boundary:
3775+
// previous char is non-space and there is at least one non-space char after.
3776+
// Result uses the same 0-based position convention expected by UTF8Copy calls below.
3777+
function FindFirstSplitSpacePos(const S: UTF8String): Integer;
3778+
var
3779+
Chars: UCS4String;
3780+
i, j: Integer;
3781+
begin
3782+
Result := -1;
3783+
if S = '' then
3784+
Exit;
3785+
3786+
Chars := UTF8ToUCS4String(S);
3787+
for i := 0 to High(Chars) do
3788+
begin
3789+
if (UCS4ToUTF8String(Chars[i]) = ' ') and
3790+
(i > 0) and
3791+
(UCS4ToUTF8String(Chars[i - 1]) <> ' ') then
3792+
begin
3793+
for j := i + 1 to High(Chars) do
3794+
begin
3795+
if UCS4ToUTF8String(Chars[j]) <> ' ' then
3796+
begin
3797+
Result := i;
3798+
Exit;
3799+
end;
3800+
end;
3801+
end;
3802+
end;
3803+
end;
37163804
begin
37173805
LineIndex := CurrentSong.Tracks[CurrentTrack].CurrentLine;
37183806
NoteDuration := CurrentSong.Tracks[CurrentTrack].Lines[LineIndex].Notes[CurrentNote[CurrentTrack]].Duration;
37193807
TempR := 720 / (CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].EndBeat - CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].Notes[0].StartBeat);
37203808

3721-
if (doubleclick) and (InteractAt(currentX, CurrentY) > 0) then
3809+
if (doubleclick) and (InteractAt(currentX, CurrentY) >= 0) then
37223810
CutPosition := Round((currentX - button[Interactions[InteractAt(currentX, CurrentY)].Num].X) / TempR)
37233811
else
37243812
CutPosition := NoteDuration div 2;
@@ -3728,6 +3816,20 @@ procedure TScreenEditSub.DivideNote(doubleclick: boolean);
37283816
if CutPosition >= NoteDuration then
37293817
CutPosition := NoteDuration - 1;
37303818

3819+
// Auto-splitting text at spaces should also split timing proportionally by word count.
3820+
if TextPosition < 0 then
3821+
begin
3822+
SourceNoteText := CurrentSong.Tracks[CurrentTrack].Lines[LineIndex].Notes[CurrentNote[CurrentTrack]].Text;
3823+
FirstSpacePos := FindFirstSplitSpacePos(SourceNoteText);
3824+
if (FirstSpacePos >= 0) then
3825+
begin
3826+
LeftWordCount := CountWords(UTF8Copy(SourceNoteText, 1, FirstSpacePos));
3827+
TotalWordCount := CountWords(SourceNoteText);
3828+
if (LeftWordCount > 0) and (TotalWordCount > LeftWordCount) then
3829+
CutPosition := EnsureRange(Round(NoteDuration * LeftWordCount / TotalWordCount), 1, NoteDuration - 1);
3830+
end;
3831+
end;
3832+
37313833
with CurrentSong.Tracks[CurrentTrack].Lines[LineIndex] do
37323834
begin
37333835
Inc(HighNote);
@@ -3747,38 +3849,51 @@ procedure TScreenEditSub.DivideNote(doubleclick: boolean);
37473849
Notes[CurrentNote[CurrentTrack]+1].StartBeat := Notes[CurrentNote[CurrentTrack]].StartBeat + Notes[CurrentNote[CurrentTrack]].Duration;
37483850
Notes[CurrentNote[CurrentTrack]+1].Duration := Notes[CurrentNote[CurrentTrack]+1].Duration - Notes[CurrentNote[CurrentTrack]].Duration;
37493851

3750-
// find space in text
3751-
SpacePosition := -1;
3752-
for NoteIndex := 0 to LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text) do
3852+
// find first real word boundary (ignores leading spaces)
3853+
SpacePosition := FindFirstSplitSpacePos(Notes[CurrentNote[CurrentTrack]].Text);
3854+
if (TextPosition < 0) and (SpacePosition >= 0) then
37533855
begin
3754-
3755-
TempStr := UTF8ToUCS4String(Notes[CurrentNote[CurrentTrack]].Text);
3756-
if ((UCS4ToUTF8String(TempStr[NoteIndex]) = ' ') and (SpacePosition < 0)) then
3757-
SpacePosition := NoteIndex;
3758-
3759-
end;
3760-
if ((TextPosition < 0) and (ansipos(' ', Notes[CurrentNote[CurrentTrack]].Text) > 1) and (ansipos(' ', Notes[CurrentNote[CurrentTrack]].Text) < Length(Notes[CurrentNote[CurrentTrack]].Text) )) then
3761-
begin
3762-
Notes[CurrentNote[CurrentTrack]+1].Text := UTF8Copy(Notes[CurrentNote[CurrentTrack]].Text, SpacePosition + 2, LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text));
3763-
Notes[CurrentNote[CurrentTrack]].Text := UTF8Copy(Notes[CurrentNote[CurrentTrack]].Text, 1, SpacePosition + 1)
3856+
LeftPart := UTF8Copy(Notes[CurrentNote[CurrentTrack]].Text, 1, SpacePosition);
3857+
RightPart := ' ' + UTF8Copy(Notes[CurrentNote[CurrentTrack]].Text, SpacePosition + 2, LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text));
3858+
ShouldInsertContinuation := not (HasVowel(LeftPart) and HasVowel(RightPart));
3859+
Notes[CurrentNote[CurrentTrack]].Text := LeftPart;
3860+
if ShouldInsertContinuation then
3861+
Notes[CurrentNote[CurrentTrack]+1].Text := '~' + RightPart
3862+
else
3863+
Notes[CurrentNote[CurrentTrack]+1].Text := RightPart;
37643864
end
37653865
else
3766-
if ((TextPosition >= 0) and (TextPosition < Length(Notes[CurrentNote[CurrentTrack]].Text))) then
3866+
if (TextPosition >= 0) and (TextPosition <= LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text)) then
37673867
begin
3768-
Notes[CurrentNote[CurrentTrack]+1].Text := UTF8Copy(SelectsS[LyricSlideId].TextOpt[0].Text, TextPosition + 2, LengthUTF8(SelectsS[LyricSlideId].TextOpt[0].Text));
3769-
Notes[CurrentNote[CurrentTrack]].Text := UTF8Copy(SelectsS[LyricSlideId].TextOpt[0].Text, 1, TextPosition);
3770-
3771-
if (LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text) > 0) and
3772-
(UTF8Copy(Notes[CurrentNote[CurrentTrack]].Text, LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text), 1) = ' ') then
3868+
SourceText := SelectsS[LyricSlideId].TextOpt[0].Text;
3869+
SourceTextLen := LengthUTF8(SourceText);
3870+
Notes[CurrentNote[CurrentTrack]+1].Text := UTF8Copy(SourceText, TextPosition + 2, SourceTextLen);
3871+
Notes[CurrentNote[CurrentTrack]].Text := UTF8Copy(SourceText, 1, TextPosition);
3872+
ShouldInsertContinuation := not (HasVowel(Notes[CurrentNote[CurrentTrack]].Text) and HasVowel(Notes[CurrentNote[CurrentTrack]+1].Text));
3873+
3874+
// If splitting at/around a space, keep a continuation marker on its own space note.
3875+
if (TextPosition < SourceTextLen) and
3876+
(UTF8Copy(SourceText, TextPosition + 1, 1) = ' ') then
3877+
begin
3878+
if ShouldInsertContinuation then
3879+
Notes[CurrentNote[CurrentTrack]+1].Text := '~ ' + Notes[CurrentNote[CurrentTrack]+1].Text
3880+
else
3881+
Notes[CurrentNote[CurrentTrack]+1].Text := ' ' + Notes[CurrentNote[CurrentTrack]+1].Text;
3882+
end
3883+
else if (TextPosition > 0) and
3884+
(UTF8Copy(SourceText, TextPosition, 1) = ' ') then
37733885
begin
3774-
UTF8Delete(Notes[CurrentNote[CurrentTrack]].Text, LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text), 1);
3775-
Notes[CurrentNote[CurrentTrack]+1].Text := ' ~' + Notes[CurrentNote[CurrentTrack]+1].Text;
3886+
if (LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text) > 0) then
3887+
UTF8Delete(Notes[CurrentNote[CurrentTrack]].Text, LengthUTF8(Notes[CurrentNote[CurrentTrack]].Text), 1);
3888+
if ShouldInsertContinuation then
3889+
Notes[CurrentNote[CurrentTrack]+1].Text := '~ ' + UTF8Copy(SourceText, TextPosition + 1, SourceTextLen)
3890+
else
3891+
Notes[CurrentNote[CurrentTrack]+1].Text := ' ' + UTF8Copy(SourceText, TextPosition + 1, SourceTextLen);
37763892
end
3777-
else if (LengthUTF8(Notes[CurrentNote[CurrentTrack]+1].Text) > 0) and
3778-
(UTF8Copy(Notes[CurrentNote[CurrentTrack]+1].Text, 1, 1) = ' ') then
3893+
else
37793894
begin
3780-
UTF8Delete(Notes[CurrentNote[CurrentTrack]+1].Text, 1, 1);
3781-
Notes[CurrentNote[CurrentTrack]+1].Text := '~ ' + Notes[CurrentNote[CurrentTrack]+1].Text;
3895+
if ShouldInsertContinuation then
3896+
Notes[CurrentNote[CurrentTrack]+1].Text := '~' + Notes[CurrentNote[CurrentTrack]+1].Text;
37823897
end;
37833898

37843899
SelectsS[LyricSlideId].TextOpt[0].Text := Notes[CurrentNote[CurrentTrack]].Text;
@@ -4792,6 +4907,8 @@ procedure TScreenEditSub.ShowInteractiveBackground;
47924907
var
47934908
TempR: real;
47944909
NoteIndex: Integer;
4910+
HitAreaY: real;
4911+
HitAreaH: real;
47954912
begin
47964913

47974914
for NoteIndex := 0 to High(TransparentNoteButtonId) do
@@ -4813,12 +4930,14 @@ procedure TScreenEditSub.ShowInteractiveBackground;
48134930
InteractiveNoteId[Length(InteractiveNoteId) - 1] := Length(Interactions) - 1;
48144931
end;
48154932
TempR := 720 / (CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].EndBeat - CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].Notes[0].StartBeat);
4933+
HitAreaY := Theme.EditSub.NotesBackground.Y + 7 * LineSpacing - NotesH[0];
4934+
HitAreaH := 2 * NotesH[0];
48164935
for NoteIndex := 0 to CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].HighNote do
48174936
begin
48184937
Button[TransparentNoteButtonId[NoteIndex]].SetX(Theme.EditSub.NotesBackground.X + NotesSkipX + (CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].Notes[NoteIndex].StartBeat - CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].Notes[0].StartBeat) * TempR + 0.5);
4819-
Button[TransparentNoteButtonId[NoteIndex]].SetY(Theme.EditSub.NotesBackground.Y + 7 * LineSpacing - (CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].Notes[NoteIndex].Tone - CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].BaseNote) * LineSpacing / 2 - NotesH[0]);
4938+
Button[TransparentNoteButtonId[NoteIndex]].SetY(HitAreaY);
48204939
Button[TransparentNoteButtonId[NoteIndex]].SetW((CurrentSong.Tracks[CurrentTrack].Lines[CurrentSong.Tracks[CurrentTrack].CurrentLine].Notes[NoteIndex].Duration) * TempR - 0.5);
4821-
Button[TransparentNoteButtonId[NoteIndex]].SetH(2 * NotesH[0]);
4940+
Button[TransparentNoteButtonId[NoteIndex]].SetH(HitAreaH);
48224941
end;
48234942
end;
48244943

0 commit comments

Comments
 (0)