private Drawable[] createContent(Lyric lyric, TimeTag timeTag, TimeTagInvalid invalid) => new Drawable[] { new RightTriangle { Origin = Anchor.Centre, Size = new Vector2(10), Colour = getInvalidColour(invalid), Margin = new MarginPadding { Left = 10 }, }, new OsuSpriteText { Text = $"#{lyric.Order}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, new OsuSpriteText { Text = TextIndexUtils.PositionFormattedString(timeTag.Index), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, new OsuSpriteText { Text = getInvalidReason(invalid), Truncate = true, RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium) }, };
protected override bool OnMouseMove(MouseMoveEvent e) { if (lyricManager == null) { return(false); } if (!isTrigger(state.Mode)) { return(false); } if (state.Mode == Mode.TimeTagEditMode) { var position = ToLocalSpace(e.ScreenSpaceMousePosition).X / 2; var index = drawableLyric.GetHoverIndex(position); state.MoveHoverCaretToTargetPosition(new TimeTagIndexCaretPosition(Lyric, index)); } else { var position = ToLocalSpace(e.ScreenSpaceMousePosition).X / 2; var index = drawableLyric.GetHoverIndex(position); state.MoveHoverCaretToTargetPosition(new TextCaretPosition(Lyric, TextIndexUtils.ToStringIndex(index))); } return(base.OnMouseMove(e)); }
protected void UpdatePositionAndSize() { // wait until lyric update ruby position. ScheduleAfterChildren(() => { var textTagRect = editorLyricPiece.GetTextTagPosition(Item); var startIndexPosition = editorLyricPiece.GetTextIndexPosition(TextIndexUtils.FromStringIndex(Item.StartIndex, false)); var endIndexPosition = editorLyricPiece.GetTextIndexPosition(TextIndexUtils.FromStringIndex(Item.EndIndex, true)); // update select position updateDrawableRect(previewTextArea, textTagRect); // update index range position. var indexRangePosition = new Vector2(startIndexPosition.X, textTagRect.Y); var indexRangeSize = new Vector2(endIndexPosition.X - startIndexPosition.X, textTagRect.Height); updateDrawableRect(indexRangeBackground, new RectangleF(indexRangePosition, indexRangeSize)); }); void updateDrawableRect(Drawable target, RectangleF rect) { target.X = rect.X; target.Y = rect.Y; target.Width = rect.Width; target.Height = rect.Height; } }
protected override bool OnMouseMove(MouseMoveEvent e) { if (lyricManager == null) { return(false); } if (!state.CaretEnabled) { return(false); } var position = ToLocalSpace(e.ScreenSpaceMousePosition).X; switch (state.Mode) { case LyricEditorMode.Manage: var cuttingLyricStringIndex = Math.Clamp(TextIndexUtils.ToStringIndex(lyricPiece.GetHoverIndex(position)), 0, Lyric.Text.Length - 1); state.MoveHoverCaretToTargetPosition(new TextCaretPosition(Lyric, cuttingLyricStringIndex)); break; case LyricEditorMode.Typing: var typingStringIndex = TextIndexUtils.ToStringIndex(lyricPiece.GetHoverIndex(position)); state.MoveHoverCaretToTargetPosition(new TextCaretPosition(Lyric, typingStringIndex)); break; case LyricEditorMode.EditRubyRomaji: state.MoveHoverCaretToTargetPosition(new NavigateCaretPosition(Lyric)); break; case LyricEditorMode.CreateTimeTag: var textIndex = lyricPiece.GetHoverIndex(position); state.MoveHoverCaretToTargetPosition(new TimeTagIndexCaretPosition(Lyric, textIndex)); break; case LyricEditorMode.RecordTimeTag: var timeTag = lyricPiece.GetHoverTimeTag(position); state.MoveHoverCaretToTargetPosition(new TimeTagCaretPosition(Lyric, timeTag)); break; case LyricEditorMode.AdjustTimeTag: case LyricEditorMode.CreateNote: case LyricEditorMode.CreateNotePosition: case LyricEditorMode.AdjustNote: case LyricEditorMode.Layout: case LyricEditorMode.Singer: state.MoveHoverCaretToTargetPosition(new NavigateCaretPosition(Lyric)); break; default: throw new ArgumentOutOfRangeException(nameof(state.Mode)); } return(base.OnMouseMove(e)); }
[TestCase(0, TextIndex.IndexState.Start, 3, 1, null)] // test switch value. public void TestClamp(int index, TextIndex.IndexState state, int minIndex, int maxIndex, int?actualIndex) { var textIndex = new TextIndex(index, state); if (actualIndex != null) { var actualTextIndex = new TextIndex(actualIndex.Value, state); Assert.AreEqual(TextIndexUtils.Clamp(textIndex, minIndex, maxIndex), actualTextIndex); } else { Assert.Throws <ArgumentException>(() => TextIndexUtils.Clamp(textIndex, minIndex, maxIndex)); } }
public override bool HandleScale(Vector2 scale, Anchor anchor) { deltaPosition += scale.X; // this feature only works if only select one ruby / romaji tag. var selectedTextTag = SelectedItems.FirstOrDefault(); if (selectedTextTag == null) { return(false); } // get real left-side and right-side position var rect = editorLyricPiece.GetTextTagPosition(selectedTextTag); switch (anchor) { case Anchor.CentreLeft: var leftPosition = rect.Left + deltaPosition; var startIndex = TextIndexUtils.ToStringIndex(editorLyricPiece.GetHoverIndex(leftPosition)); if (startIndex >= selectedTextTag.EndIndex) { return(false); } selectedTextTag.StartIndex = startIndex; return(true); case Anchor.CentreRight: var rightPosition = rect.Right + deltaPosition; var endIndex = TextIndexUtils.ToStringIndex(editorLyricPiece.GetHoverIndex(rightPosition)); if (endIndex <= selectedTextTag.StartIndex) { return(false); } selectedTextTag.EndIndex = endIndex; return(true); default: return(false); } }
public Note[] CreateNotes(Lyric lyric) { var timeTags = TimeTagsUtils.ToDictionary(lyric.TimeTags); var notes = new List <Note>(); foreach (var timeTag in timeTags) { var(key, endTime) = timeTags.GetNext(timeTag); if (key.Index <= 0) { continue; } var startTime = timeTag.Value; int startIndex = timeTag.Key.Index; int endIndex = TextIndexUtils.ToStringIndex(key); var text = lyric.Text[startIndex..endIndex];
protected override bool ApplySnapResult(SelectionBlueprint <ITextTag>[] blueprints, SnapResult result) { if (!base.ApplySnapResult(blueprints, result)) { return(false); } // handle lots of ruby / romaji drag position changed. var items = blueprints.Select(x => x.Item).ToArray(); if (!items.Any()) { return(false); } var leftPosition = ToLocalSpace(result.ScreenSpacePosition).X; var startIndex = TextIndexUtils.ToStringIndex(editorLyricPiece.GetHoverIndex(leftPosition)); var diff = startIndex - items.First().StartIndex; if (diff == 0) { return(false); } foreach (var item in items) { var newStartIndex = item.StartIndex + diff; var newEndIndex = item.EndIndex + diff; if (!LyricUtils.AbleToInsertTextTagAtIndex(Lyric, newStartIndex) || !LyricUtils.AbleToInsertTextTagAtIndex(Lyric, newEndIndex)) { continue; } item.StartIndex = newStartIndex; item.EndIndex = newEndIndex; } return(true); }
public override bool SetContent(object content) { if (!(content is Issue[] issues)) { return(false); } // clear exist warning. invalidMessage.Clear(); foreach (var issue in issues) { switch (issue) { // Print time invalid message case LyricTimeIssue lyricTimeIssue: lyricTimeIssue.InvalidLyricTime?.ForEach(createTimeInvalidMessage); break; // Print time-tag invalid message case TimeTagIssue timeTagIssue: if (timeTagIssue.MissingStartTimeTag) { invalidMessage.AddAlertParagraph("Missing start time tag at the start of lyric."); } if (timeTagIssue.MissingEndTimeTag) { invalidMessage.AddAlertParagraph("Missing end time tag at the end of lyric."); } timeTagIssue.InvalidTimeTags?.ForEach(x => createTimeTagInvalidMessage(x.Key, x.Value)); break; // Print ruby invalid message case RubyTagIssue rubyTagIssue: rubyTagIssue.InvalidRubyTags?.ForEach(x => createRubyInvalidMessage(x.Key, x.Value)); break; // Print romaji invalid message case RomajiTagIssue romajiTagIssue: romajiTagIssue.InvalidRomajiTags?.ForEach(x => createRomajiInvalidMessage(x.Key, x.Value)); break; // print normal message case Issue _: invalidMessage.AddAlertParagraph(issue.Template.GetMessage()); break; // Should throw exception because every issue message should be printed. default: throw new ArgumentOutOfRangeException(nameof(issue)); } } // show no problem message if (issues.Length == 0) { invalidMessage.AddSuccessParagraph("Seems no issue in this lyric."); } return(true); void createTimeInvalidMessage(TimeInvalid timeInvalid) { switch (timeInvalid) { case TimeInvalid.Overlapping: invalidMessage.AddAlertParagraph("Start time larger then end time in lyric."); break; case TimeInvalid.StartTimeInvalid: invalidMessage.AddAlertParagraph("Start time is larger than minimum time tag's time."); break; case TimeInvalid.EndTimeInvalid: invalidMessage.AddAlertParagraph("End time is smaller than maximum time tag's time."); break; } } void createTimeTagInvalidMessage(TimeTagInvalid invalid, TimeTag[] timeTags) { switch (invalid) { case TimeTagInvalid.OutOfRange: invalidMessage.AddAlertParagraph("Time tag(s) is out of lyric text size at position "); break; case TimeTagInvalid.Overlapping: invalidMessage.AddAlertParagraph("Time tag(s) is invalid at position "); break; case TimeTagInvalid.EmptyTime: invalidMessage.AddAlertParagraph("Time tag(s) is missing time at position "); break; default: throw new ArgumentOutOfRangeException(nameof(invalid)); } displayInvalidTag(timeTags, tag => invalidMessage.AddHighlightText(TextIndexUtils.PositionFormattedString(tag.Index))); } void createRubyInvalidMessage(RubyTagInvalid invalid, RubyTag[] rubyTags) { switch (invalid) { case RubyTagInvalid.OutOfRange: invalidMessage.AddAlertParagraph("Ruby tag(s) is out of lyric text size at position "); break; case RubyTagInvalid.Overlapping: invalidMessage.AddAlertParagraph("Ruby tag(s) is overlapping to others at position "); break; default: throw new ArgumentOutOfRangeException(nameof(invalid)); } displayInvalidTag(rubyTags, tag => invalidMessage.AddHighlightText(TextTagUtils.PositionFormattedString(tag))); } void createRomajiInvalidMessage(RomajiTagInvalid invalid, RomajiTag[] romajiTags) { switch (invalid) { case RomajiTagInvalid.OutOfRange: invalidMessage.AddAlertParagraph("Romaji tag(s) is out of lyric text size at position "); break; case RomajiTagInvalid.Overlapping: invalidMessage.AddAlertParagraph("Romaji tag(s) is overlapping to others at position "); break; default: throw new ArgumentOutOfRangeException(nameof(invalid)); } displayInvalidTag(romajiTags, tag => invalidMessage.AddHighlightText(TextTagUtils.PositionFormattedString(tag))); } void displayInvalidTag <T>(T[] tags, Action <T> action) { if (tags == null) { throw new ArgumentNullException(nameof(tags)); } // e.g: ka(-2~-1), ra(4~6), and ke(6~7) for (int i = 0; i < tags.Length; i++) { action?.Invoke(tags[i]); if (i == tags.Length - 2 && tags.Length > 1) { invalidMessage.AddText(" and "); } else if (i >= 0 && tags.Length > 1) { invalidMessage.AddText(", "); } else { invalidMessage.AddText("."); } } } }