diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/FixedInfo/InvalidInfo.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/FixedInfo/InvalidInfo.cs index bf06dcd50..1b87192b0 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/FixedInfo/InvalidInfo.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Components/FixedInfo/InvalidInfo.cs @@ -119,7 +119,7 @@ private partial class IssueTablePopover : OsuPopover { private readonly IReadOnlyDependencyContainer dependencyContainer; - public IssueTablePopover(IReadOnlyDependencyContainer dependencyContainer, IReadOnlyCollection issues) + public IssueTablePopover(IReadOnlyDependencyContainer dependencyContainer, IBindableList issues) { this.dependencyContainer = dependencyContainer; @@ -131,7 +131,7 @@ public IssueTablePopover(IReadOnlyDependencyContainer dependencyContainer, IRead { new SingleLyricIssueTable { - Issues = issues, + Issues = { BindTarget = issues, }, }, new IconButton { @@ -166,30 +166,43 @@ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnl private partial class SingleLyricIssueTable : LyricEditorIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) => - new Drawable[] + protected override IssueTableHeaderText[] CreateHeaders() => new[] + { + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + TruncatingSpriteText issueSpriteText; + + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), ShowTooltip = false, }, - }; + }, issue => + { + issueIcon.Issue = issue; + issueSpriteText.Text = issue.ToString(); + }); + } protected override void OnIssueClicked(Issue issue) { diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Compose/Panels/IssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Compose/Panels/IssueSection.cs index ed06c88ab..fb12685b3 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Compose/Panels/IssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Content/Compose/Panels/IssueSection.cs @@ -64,6 +64,7 @@ public IssueSection() }, }); + issueTable.Issues.BindTo(bindableIssues); bindableIssues.BindCollectionChanged((_, _) => { bool hasIssue = bindableIssues.Any(); @@ -72,7 +73,6 @@ public IssueSection() reloadButton.Alpha = hasIssue ? 1 : 0; issueTable.Alpha = hasIssue ? 1 : 0; - issueTable.Issues = bindableIssues.Take(100); }, true); } @@ -152,28 +152,41 @@ public SingleLyricIssueTable() ShowHeaders = false; } - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) => - new Drawable[] + protected override IssueTableHeaderText[] CreateHeaders() => new[] + { + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + TruncatingSpriteText issueSpriteText; + + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; + }, issue => + { + issueIcon.Issue = issue; + issueSpriteText.Text = issue.ToString(); + }); + } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/LyricEditorIssueTable.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/LyricEditorIssueTable.cs index c46057f37..05110d2ff 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/LyricEditorIssueTable.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/LyricEditorIssueTable.cs @@ -1,11 +1,7 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit.Checks.Components; using osuTK.Graphics; @@ -16,75 +12,23 @@ public abstract partial class LyricEditorIssueTable : IssueTable [Resolved] private IIssueNavigator issueNavigator { get; set; } = null!; - public new bool ShowHeaders + private Color4 colourHover; + private Color4 colourSelected; + + [BackgroundDependencyLoader] + private void load(LyricEditorColourProvider colourProvider, ILyricEditorState state) { - get => base.ShowHeaders; - set - { - base.ShowHeaders = value; - BackgroundFlow.Margin = new MarginPadding { Top = value ? ROW_HEIGHT : 0 }; - } + colourHover = colourProvider.Background1(state.Mode); + colourSelected = colourProvider.Colour3(state.Mode); } - protected override void OnIssueClicked(Issue issue) + protected sealed override Color4 GetBackgroundColour(bool selected) { - issueNavigator.Navigate(issue); + return selected ? colourSelected : colourHover; } - protected override RowBackground CreateRowBackground(Issue issue) - => new IssueTableRowBackground(issue); - - /// - /// Inherit the class just for able to override the colour. - /// - protected partial class IssueTableRowBackground : RowBackground + protected override void OnIssueClicked(Issue issue) { - private const int fade_duration = 100; - - private readonly Box hoveredBackground; - - public IssueTableRowBackground(object item) - : base(item) - { - hoveredBackground = Children.OfType().First(); - } - - private Color4 colourHover; - private Color4 colourSelected; - - [BackgroundDependencyLoader] - private void load(LyricEditorColourProvider colourProvider, ILyricEditorState state) - { - colourHover = colourProvider.Background1(state.Mode); - colourSelected = colourProvider.Colour3(state.Mode); - - Schedule(() => - { - hoveredBackground.Colour = colourHover; - }); - } - - protected override bool OnHover(HoverEvent e) - { - bool hover = base.OnHover(e); - updateState(); - return hover; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - updateState(); - } - - private void updateState() - { - hoveredBackground.FadeColour(Selected ? colourSelected : colourHover, 450, Easing.OutQuint); - - if (Selected || IsHovered) - hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); - else - hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); - } + issueNavigator.Navigate(issue); } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Language/LanguageIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Language/LanguageIssueSection.cs index eb9fd3aed..ec950e1bb 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Language/LanguageIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Language/LanguageIssueSection.cs @@ -22,47 +22,60 @@ public partial class LanguageIssueSection : LyricEditorIssueSection private partial class LanguageIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Lyric", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - var lyric = getInvalidByIssue(issue); + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Lyric", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + TruncatingSpriteText issueSpriteText; - return new Drawable[] + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + orderSpriteText = new OsuSpriteText { - Text = $"#{lyric.Order}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; - } + }, issue => + { + var lyric = getInvalidByIssue(issue); - private Lyric getInvalidByIssue(Issue issue) - { - if (issue is not LyricIssue lyricIssue) - throw new InvalidCastException(); + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{lyric.Order}"; + issueSpriteText.Text = issue.ToString(); + }); + + static Lyric getInvalidByIssue(Issue issue) + { + if (issue is not LyricIssue lyricIssue) + throw new InvalidCastException(); - return lyricIssue.Lyric; + return lyricIssue.Lyric; + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Notes/NoteIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Notes/NoteIssueSection.cs index b0c2a0911..1f8a938d2 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Notes/NoteIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Notes/NoteIssueSection.cs @@ -22,48 +22,61 @@ public partial class NoteIssueSection : LyricEditorIssueSection private partial class NoteIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Note", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - var note = getInvalidByIssue(issue); - string noteIndex = note.ReferenceLyric?.Order.ToString() ?? "??"; + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Note", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + TruncatingSpriteText issueSpriteText; - return new Drawable[] + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + orderSpriteText = new OsuSpriteText { - Text = $"#{noteIndex}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; - } + }, issue => + { + var note = getInvalidByIssue(issue); + string noteIndex = note.ReferenceLyric?.Order.ToString() ?? "??"; - private Note getInvalidByIssue(Issue issue) - { - if (issue is not NoteIssue noteIssue) - throw new InvalidCastException(); + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{noteIndex}"; + issueSpriteText.Text = issue.ToString(); + }); + + static Note getInvalidByIssue(Issue issue) + { + if (issue is not NoteIssue noteIssue) + throw new InvalidCastException(); - return noteIssue.Note; + return noteIssue.Note; + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Reference/ReferenceLyricIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Reference/ReferenceLyricIssueSection.cs index 044c1df8c..43dc63e6e 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Reference/ReferenceLyricIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Reference/ReferenceLyricIssueSection.cs @@ -22,47 +22,60 @@ public partial class ReferenceLyricIssueSection : LyricEditorIssueSection private partial class ReferenceLyricIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Lyric", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - var lyric = getInvalidByIssue(issue); + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Lyric", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + TruncatingSpriteText issueSpriteText; - return new Drawable[] + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + orderSpriteText = new OsuSpriteText { - Text = $"#{lyric.Order}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; - } + }, issue => + { + var lyric = getInvalidByIssue(issue); - private Lyric getInvalidByIssue(Issue issue) - { - if (issue is not LyricIssue lyricIssue) - throw new InvalidCastException(); + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{lyric.Order}"; + issueSpriteText.Text = issue.ToString(); + }); + + static Lyric getInvalidByIssue(Issue issue) + { + if (issue is not LyricIssue lyricIssue) + throw new InvalidCastException(); - return lyricIssue.Lyric; + return lyricIssue.Lyric; + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Romanisation/RomanisationIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Romanisation/RomanisationIssueSection.cs index 836980855..cee40becb 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Romanisation/RomanisationIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Romanisation/RomanisationIssueSection.cs @@ -23,54 +23,73 @@ public partial class RomanisationIssueSection : LyricEditorIssueSection private partial class RomanisationIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Lyric", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Position", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), - new TableColumn("Message", Anchor.CentreLeft), + return new[] + { + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(GridSizeMode.AutoSize, minSize: 60), + new Dimension(), + }; + } + + protected override IssueTableHeaderText[] CreateHeaders() => new[] + { + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Lyric", Anchor.CentreLeft), + new IssueTableHeaderText("Position", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), }; - protected override Drawable[] CreateContent(Issue issue) + protected override Tuple> CreateContent() { - (var lyric, TimeTag timeTag) = getInvalidByIssue(issue); + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + OsuSpriteText timeSpriteText; + TruncatingSpriteText issueSpriteText; - return new Drawable[] - { - new IssueIcon - { - Origin = Anchor.Centre, - Size = new Vector2(10), - Margin = new MarginPadding { Left = 10 }, - Issue = issue, - }, - new OsuSpriteText + return new Tuple>( + new Drawable[] { - Text = $"#{lyric.Order}", - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText + issueIcon = new IssueIcon + { + Origin = Anchor.Centre, + Size = new Vector2(10), + Margin = new MarginPadding { Left = 10 }, + }, + orderSpriteText = new OsuSpriteText + { + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), + Margin = new MarginPadding { Right = 10 }, + }, + timeSpriteText = new OsuSpriteText + { + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), + Margin = new MarginPadding { Right = 10 }, + }, + issueSpriteText = new TruncatingSpriteText + { + RelativeSizeAxes = Axes.X, + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), + }, + }, issue => { - Text = TimeTagUtils.FormattedString(timeTag), - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), - Margin = new MarginPadding { Right = 10 }, - }, - new TruncatingSpriteText - { - Text = issue.ToString(), - RelativeSizeAxes = Axes.X, - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), - }, - }; - } + (var lyric, TimeTag timeTag) = getInvalidByIssue(issue); - private Tuple getInvalidByIssue(Issue issue) - { - if (issue is not LyricTimeTagIssue timeTagIssue) - throw new InvalidCastException(); + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{lyric.Order}"; + timeSpriteText.Text = TimeTagUtils.FormattedString(timeTag); + issueSpriteText.Text = issue.ToString(); + }); + + static Tuple getInvalidByIssue(Issue issue) + { + if (issue is not LyricTimeTagIssue timeTagIssue) + throw new InvalidCastException(); - return new Tuple(timeTagIssue.Lyric, timeTagIssue.TimeTag); + return new Tuple(timeTagIssue.Lyric, timeTagIssue.TimeTag); + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Ruby/RubyTagIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Ruby/RubyTagIssueSection.cs index 012e5ebb0..d33b58c81 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Ruby/RubyTagIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Ruby/RubyTagIssueSection.cs @@ -23,54 +23,69 @@ public partial class RubyTagIssueSection : LyricEditorIssueSection private partial class RubyTagIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Lyric", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Position", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(GridSizeMode.AutoSize, minSize: 60), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - (var lyric, RubyTag rubyTag) = getInvalidByIssue(issue); + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Lyric", Anchor.CentreLeft), + new IssueTableHeaderText("Position", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + OsuSpriteText timeSpriteText; + TruncatingSpriteText issueSpriteText; - return new Drawable[] + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + orderSpriteText = new OsuSpriteText { - Text = $"#{lyric.Order}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new OsuSpriteText + timeSpriteText = new OsuSpriteText { - Text = RubyTagUtils.PositionFormattedString(rubyTag), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; - } + }, issue => + { + var (lyric, rubyTag) = getInvalidByIssue(issue); - private Tuple getInvalidByIssue(Issue issue) - { - if (issue is not LyricRubyTagIssue rubyTagIssue) - throw new InvalidCastException(); + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{lyric.Order}"; + timeSpriteText.Text = RubyTagUtils.PositionFormattedString(rubyTag); + issueSpriteText.Text = issue.ToString(); + }); + + static Tuple getInvalidByIssue(Issue issue) + { + if (issue is not LyricRubyTagIssue rubyTagIssue) + throw new InvalidCastException(); - return new Tuple(rubyTagIssue.Lyric, rubyTagIssue.RubyTag); + return new Tuple(rubyTagIssue.Lyric, rubyTagIssue.RubyTag); + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Text/TextIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Text/TextIssueSection.cs index 1d129645e..feb7418ec 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Text/TextIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/Text/TextIssueSection.cs @@ -22,47 +22,60 @@ public partial class TextIssueSection : LyricEditorIssueSection private partial class TextIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Lyric", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - var lyric = getInvalidByIssue(issue); + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Lyric", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + TruncatingSpriteText issueSpriteText; - return new Drawable[] + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + orderSpriteText = new OsuSpriteText { - Text = $"#{lyric.Order}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; - } + }, issue => + { + var lyric = getInvalidByIssue(issue); - private Lyric getInvalidByIssue(Issue issue) - { - if (issue is not LyricIssue lyricIssue) - throw new InvalidCastException(); + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{lyric.Order}"; + issueSpriteText.Text = issue.ToString(); + }); + + static Lyric getInvalidByIssue(Issue issue) + { + if (issue is not LyricIssue lyricIssue) + throw new InvalidCastException(); - return lyricIssue.Lyric; + return lyricIssue.Lyric; + } } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/TimeTags/TimeTagIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/TimeTags/TimeTagIssueSection.cs index 1570e79b1..a1e4c69ff 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/TimeTags/TimeTagIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Lyrics/Settings/TimeTags/TimeTagIssueSection.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Edit.Checks.Components; @@ -24,88 +23,69 @@ public partial class TimeTagIssueSection : LyricEditorIssueSection private partial class TimeTagIssueTable : LyricsIssueTable { - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Lyric", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Position", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(GridSizeMode.AutoSize, minSize: 60), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - (var lyric, TimeTag? timeTag) = getInvalidByIssue(issue); + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Lyric", Anchor.CentreLeft), + new IssueTableHeaderText("Position", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; - // show the issue with the invalid time-tag. - if (timeTag != null) - { - return new Drawable[] - { - new IssueIcon - { - Origin = Anchor.Centre, - Size = new Vector2(10), - Margin = new MarginPadding { Left = 10 }, - Issue = issue, - }, - 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 TruncatingSpriteText - { - Text = issue.ToString(), - RelativeSizeAxes = Axes.X, - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), - }, - }; - } + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText orderSpriteText; + OsuSpriteText timeSpriteText; + TruncatingSpriteText issueSpriteText; - // show the default issue if not able to get the time-tag. - return new Drawable[] + return new Tuple>(new Drawable[] { - new SpriteIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), - Colour = issue.Template.Colour, Margin = new MarginPadding { Left = 10 }, - Icon = FontAwesome.Solid.AlignLeft, }, - new OsuSpriteText + orderSpriteText = new OsuSpriteText { - Text = $"#{lyric.Order}", Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new OsuSpriteText + timeSpriteText = new OsuSpriteText { Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; - } - - private Tuple getInvalidByIssue(Issue issue) => - issue switch + }, issue => { - LyricTimeTagIssue timeTagIssue => new Tuple(timeTagIssue.Lyric, timeTagIssue.TimeTag), - LyricIssue lyricIssue => new Tuple(lyricIssue.Lyric, null), - _ => throw new InvalidCastException(), - }; + (var lyric, TimeTag? timeTag) = getInvalidByIssue(issue); + + issueIcon.Issue = issue; + orderSpriteText.Text = $"#{lyric.Order}"; + timeSpriteText.Text = timeTag != null ? TextIndexUtils.PositionFormattedString(timeTag.Index) : string.Empty; + issueSpriteText.Text = issue.ToString(); + }); + + static Tuple getInvalidByIssue(Issue issue) => + issue switch + { + LyricTimeTagIssue timeTagIssue => new Tuple(timeTagIssue.Lyric, timeTagIssue.TimeTag), + LyricIssue lyricIssue => new Tuple(lyricIssue.Lyric, null), + _ => throw new InvalidCastException(), + }; + } } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Pages/Settings/PageEditorIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Pages/Settings/PageEditorIssueSection.cs index e853ad96a..4a3f09b05 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Pages/Settings/PageEditorIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Beatmaps/Pages/Settings/PageEditorIssueSection.cs @@ -1,15 +1,18 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Karaoke.Screens.Edit.Components.Issues; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Karaoke.Screens.Edit.Beatmaps.Pages.Settings; @@ -50,42 +53,63 @@ public partial class PageEditorIssueTable : IssueTable [Resolved] private IPageEditorVerifier pageEditorVerifier { get; set; } = null!; - protected override void OnIssueClicked(Issue issue) - => pageEditorVerifier.Navigate(issue); + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; - protected override TableColumn[] CreateHeaders() => new[] + protected override Dimension[] CreateDimensions() => new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Time", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Message", Anchor.CentreLeft), + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(), }; - protected override Drawable[] CreateContent(Issue issue) + protected override IssueTableHeaderText[] CreateHeaders() => new[] { - return new Drawable[] + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Time", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), + }; + + protected override Tuple> CreateContent() + { + IssueIcon issueIcon; + OsuSpriteText timeSpriteText; + TruncatingSpriteText issueSpriteText; + + return new Tuple>(new Drawable[] { - new IssueIcon + issueIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + timeSpriteText = new OsuSpriteText { - Text = getInvalidObjectTimeByIssue(issue), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; + }, issue => + { + issueIcon.Issue = issue; + timeSpriteText.Text = getInvalidObjectTimeByIssue(issue); + issueSpriteText.Text = issue.ToString(); + }); + + static string getInvalidObjectTimeByIssue(Issue issue) => issue.Time?.ToEditorFormattedString() ?? string.Empty; } - private static string getInvalidObjectTimeByIssue(Issue issue) => issue.Time?.ToEditorFormattedString() ?? string.Empty; + protected override Color4 GetBackgroundColour(bool selected) + { + return selected ? colourProvider.Colour3 : colourProvider.Background1; + } + + protected override void OnIssueClicked(Issue issue) + => pageEditorVerifier.Navigate(issue); } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/EditorTable.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/EditorTable.cs deleted file mode 100644 index 28643e787..000000000 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/EditorTable.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) andy840119 . Licensed under the GPL Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Diagnostics; -using osu.Framework.Allocation; -using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Karaoke.Screens.Edit; - -public abstract partial class EditorTable : TableContainer -{ - public event Action? OnRowSelected; - - private const float horizontal_inset = 20; - - protected const float ROW_HEIGHT = 25; - - public const int TEXT_SIZE = 14; - - protected readonly FillFlowContainer BackgroundFlow; - - // We can avoid potentially thousands of objects being added to the input sub-tree since item selection is being handled by the BackgroundFlow - // and no items in the underlying table are clickable. - protected override bool ShouldBeConsideredForInput(Drawable child) => child == BackgroundFlow && base.ShouldBeConsideredForInput(child); - - protected EditorTable() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Horizontal = horizontal_inset }; - RowSize = new Dimension(GridSizeMode.Absolute, ROW_HEIGHT); - - AddInternal(BackgroundFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1f, - Padding = new MarginPadding { Horizontal = -horizontal_inset }, - Margin = new MarginPadding { Top = ROW_HEIGHT } - }); - } - - protected int GetIndexForObject(object? item) - { - for (int i = 0; i < BackgroundFlow.Count; i++) - { - if (BackgroundFlow[i].Item == item) - return i; - } - - return -1; - } - - protected virtual bool SetSelectedRow(object? item) - { - bool foundSelection = false; - - foreach (var b in BackgroundFlow) - { - b.Selected = ReferenceEquals(b.Item, item); - - if (b.Selected) - { - Debug.Assert(!foundSelection); - OnRowSelected?.Invoke(b); - foundSelection = true; - } - } - - return foundSelection; - } - - protected object? GetObjectAtIndex(int index) - { - if (index < 0 || index > BackgroundFlow.Count - 1) - return null; - - return BackgroundFlow[index].Item; - } - - protected override Drawable CreateHeader(int index, TableColumn? column) => new HeaderText(column?.Header ?? default); - - private partial class HeaderText : OsuSpriteText - { - public HeaderText(LocalisableString text) - { - Text = text.ToUpper(); - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); - } - } - - public partial class RowBackground : OsuClickableContainer - { - public readonly object Item; - - private const int fade_duration = 100; - - private readonly Box hoveredBackground; - - public RowBackground(object item) - { - Item = item; - - RelativeSizeAxes = Axes.X; - Height = 25; - - AlwaysPresent = true; - - CornerRadius = 3; - Masking = true; - - Children = new Drawable[] - { - hoveredBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - }; - } - - private Color4 colourHover; - private Color4 colourSelected; - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colours) - { - colourHover = colours.Background1; - colourSelected = colours.Colour3; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - updateState(); - FinishTransforms(true); - } - - private bool selected; - - public bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); - } - - private void updateState() - { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - - if (selected || IsHovered) - hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); - else - hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); - } - } -} diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueSection.cs index 8fed11b7d..ba8138318 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueSection.cs @@ -52,6 +52,7 @@ protected IssueSection() issueTable = CreateIssueTable(), }; + issueTable.Issues.BindTo(Issues); Issues.BindCollectionChanged((_, _) => { bool hasIssue = Issues.Any(); @@ -62,7 +63,6 @@ protected IssueSection() issueNavigator.Issues = Issues; issueTable.Alpha = hasIssue ? 1 : 0; - issueTable.Issues = Issues.Take(100); }, true); } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueTable.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueTable.cs index 4ee4919d4..e92616fa8 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueTable.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/IssueTable.cs @@ -1,54 +1,186 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Extensions; +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Screens.Edit; +using osuTK.Graphics; namespace osu.Game.Rulesets.Karaoke.Screens.Edit; -public abstract partial class IssueTable : EditorTable +[Cached] +public abstract partial class IssueTable : CompositeDrawable { - private const float horizontal_inset = 0; + public IBindableList Issues { get; } = new BindableList(); - protected IssueTable() - { - Padding = new MarginPadding { Horizontal = horizontal_inset }; - BackgroundFlow.Padding = new MarginPadding { Horizontal = -horizontal_inset }; + public const float ROW_HEIGHT = 25; + public const int TEXT_SIZE = 14; + + protected abstract Dimension[] CreateDimensions(); + + protected abstract IssueTableHeaderText[] CreateHeaders(); + + protected abstract Tuple> CreateContent(); - Columns = CreateHeaders(); + protected abstract Color4 GetBackgroundColour(bool selected); + + protected abstract void OnIssueClicked(Issue issue); + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + new GridContainer + { + RelativeSizeAxes = Axes.X, + Height = ROW_HEIGHT, + RowDimensions = CreateDimensions(), + Content = new[] + { + CreateHeaders(), + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = ROW_HEIGHT, }, + Child = new IssueRowList + { + RelativeSizeAxes = Axes.Both, + RowData = { BindTarget = Issues }, + }, + }, + }; } - public IEnumerable Issues + private bool showHeader; + + public bool ShowHeaders { + get => showHeader; set { - Content = null; - BackgroundFlow.Clear(); + showHeader = value; - foreach (var issue in value) + // todo: show/hide the header. + } + } + + protected partial class IssueTableHeaderText : TableHeaderText + { + public IssueTableHeaderText(LocalisableString text, Anchor anchor) + : base(text) + { + Anchor = anchor; + Origin = anchor; + } + } + + private sealed partial class IssueRowList : VirtualisedListContainer + { + public IssueRowList() + : base(ROW_HEIGHT, 50) + { + } + + protected sealed override ScrollContainer CreateScrollContainer() => new OsuScrollContainer(); + } + + private partial class DrawableIssue : PoolableDrawable, IHasCurrentValue + { + private readonly BindableWithCurrent current = new(); + + public Action? Selected; + + private Box background = null!; + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + [Resolved] + private IssueTable issueTable { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + Height = ROW_HEIGHT; + + var (drawables, action) = issueTable.CreateContent(); + Selected = action; + + InternalChildren = new Drawable[] { - BackgroundFlow.Add(CreateRowBackground(issue).With(x => + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new GridContainer { - x.Action = () => + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 20, }, + RowDimensions = issueTable.CreateDimensions(), + Content = new[] { - OnIssueClicked(issue); - }; - })); - } + drawables, + }, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); - Content = value.Select(CreateContent).ToArray().ToRectangular(); + Current.BindValueChanged(_ => updateState(), true); + FinishTransforms(true); } - } - protected abstract void OnIssueClicked(Issue issue); + protected override bool OnHover(HoverEvent e) + { + updateState(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } - protected virtual RowBackground CreateRowBackground(Issue issue) => new(issue); + protected override bool OnClick(ClickEvent e) + { + Selected?.Invoke(current.Value); + updateState(); + + return true; + } - protected abstract TableColumn[] CreateHeaders(); + private void updateState() + { + // todo: implement + bool isSelected = true; - protected abstract Drawable[] CreateContent(Issue issue); + if (IsHovered || isSelected) + background.FadeIn(100, Easing.OutQuint); + else + background.FadeOut(100, Easing.OutQuint); + + background.Colour = issueTable.GetBackgroundColour(isSelected); + } + } } diff --git a/osu.Game.Rulesets.Karaoke/Screens/Edit/Stages/Classic/Stage/Settings/StageEditorIssueSection.cs b/osu.Game.Rulesets.Karaoke/Screens/Edit/Stages/Classic/Stage/Settings/StageEditorIssueSection.cs index 095d572e5..2b2c3a98c 100644 --- a/osu.Game.Rulesets.Karaoke/Screens/Edit/Stages/Classic/Stage/Settings/StageEditorIssueSection.cs +++ b/osu.Game.Rulesets.Karaoke/Screens/Edit/Stages/Classic/Stage/Settings/StageEditorIssueSection.cs @@ -1,15 +1,18 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Karaoke.Screens.Edit.Components.Issues; using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Karaoke.Screens.Edit.Stages.Classic.Stage.Settings; @@ -57,6 +60,9 @@ public partial class StageEditorIssueTable : IssueTable [Resolved] private IStageEditorVerifier stageEditorVerifier { get; set; } = null!; + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + private readonly StageEditorEditCategory category; public StageEditorIssueTable(StageEditorEditCategory category) @@ -64,74 +70,111 @@ public StageEditorIssueTable(StageEditorEditCategory category) this.category = category; } - protected override void OnIssueClicked(Issue issue) - => stageEditorVerifier.Navigate(issue); + protected override Dimension[] CreateDimensions() + { + if (category == StageEditorEditCategory.Timing) + { + return new[] + { + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(GridSizeMode.AutoSize, minSize: 40), + new Dimension(), + }; + } - protected override TableColumn[] CreateHeaders() + return new[] + { + new Dimension(GridSizeMode.AutoSize, minSize: 30), + new Dimension(), + }; + } + + protected override IssueTableHeaderText[] CreateHeaders() { if (category == StageEditorEditCategory.Timing) { return new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Time", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 40)), - new TableColumn("Message", Anchor.CentreLeft), + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Time", Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), }; } return new[] { - new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 30)), - new TableColumn("Message", Anchor.CentreLeft), + new IssueTableHeaderText(string.Empty, Anchor.CentreLeft), + new IssueTableHeaderText("Message", Anchor.CentreLeft), }; } - protected override Drawable[] CreateContent(Issue issue) + protected override Tuple> CreateContent() { if (category == StageEditorEditCategory.Timing) { - return new Drawable[] + IssueIcon issuesIcon; + OsuSpriteText timeSpriteText; + TruncatingSpriteText issueSpriteText; + + return new Tuple>(new Drawable[] { - new IssueIcon + issuesIcon = new IssueIcon { Origin = Anchor.Centre, Size = new Vector2(10), Margin = new MarginPadding { Left = 10 }, - Issue = issue, }, - new OsuSpriteText + timeSpriteText = new OsuSpriteText { - Text = getInvalidObjectTimeByIssue(issue), Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, - new TruncatingSpriteText + issueSpriteText = new TruncatingSpriteText { - Text = issue.ToString(), RelativeSizeAxes = Axes.X, Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), }, - }; + }, issue => + { + issuesIcon.Issue = issue; + timeSpriteText.Text = getInvalidObjectTimeByIssue(issue); + issueSpriteText.Text = issue.ToString(); + }); } - - return new Drawable[] + else { - new IssueIcon + IssueIcon issuesIcon; + TruncatingSpriteText issueSpriteText; + + return new Tuple>(new Drawable[] { - Origin = Anchor.Centre, - Size = new Vector2(10), - Margin = new MarginPadding { Left = 10 }, - Issue = issue, - }, - new TruncatingSpriteText + issuesIcon = new IssueIcon + { + Origin = Anchor.Centre, + Size = new Vector2(10), + Margin = new MarginPadding { Left = 10 }, + }, + issueSpriteText = new TruncatingSpriteText + { + RelativeSizeAxes = Axes.X, + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), + }, + }, issue => { - Text = issue.ToString(), - RelativeSizeAxes = Axes.X, - Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), - }, - }; + issuesIcon.Issue = issue; + issueSpriteText.Text = issue.ToString(); + }); + } + + static string getInvalidObjectTimeByIssue(Issue issue) => issue.Time?.ToEditorFormattedString() ?? string.Empty; } - private static string getInvalidObjectTimeByIssue(Issue issue) => issue.Time?.ToEditorFormattedString() ?? string.Empty; + protected override Color4 GetBackgroundColour(bool selected) + { + return selected ? colourProvider.Colour3 : colourProvider.Background1; + } + + protected override void OnIssueClicked(Issue issue) + => stageEditorVerifier.Navigate(issue); } }