Merge pull request #21434 from jai-x/control-point-scroll-into-view

Ensure currently selected control point will scroll into view
This commit is contained in:
Dean Herbert 2022-11-30 15:14:14 +09:00 committed by GitHub
commit b29054d30d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 75 deletions

View File

@ -10,6 +10,8 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit;
@ -26,6 +28,7 @@ namespace osu.Game.Tests.Visual.Editing
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private TimingScreen timingScreen;
private EditorBeatmap editorBeatmap;
protected override bool ScrollUsingMouseWheel => false;
@ -35,8 +38,11 @@ namespace osu.Game.Tests.Visual.Editing
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
Beatmap.Disabled = true;
}
var editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(Ruleset.Value));
private void reloadEditorBeatmap()
{
editorBeatmap = new EditorBeatmap(Beatmap.Value.GetPlayableBeatmap(Ruleset.Value));
Child = new DependencyProvidingContainer
{
@ -58,7 +64,9 @@ namespace osu.Game.Tests.Visual.Editing
{
AddStep("Stop clock", () => EditorClock.Stop());
AddUntilStep("wait for rows to load", () => Child.ChildrenOfType<EffectRowAttribute>().Any());
AddStep("Reload Editor Beatmap", reloadEditorBeatmap);
AddUntilStep("Wait for rows to load", () => Child.ChildrenOfType<EffectRowAttribute>().Any());
}
[Test]
@ -95,6 +103,37 @@ namespace osu.Game.Tests.Visual.Editing
AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 69670);
}
[Test]
public void TestScrollControlGroupIntoView()
{
AddStep("Add many control points", () =>
{
editorBeatmap.ControlPointInfo.Clear();
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint());
for (int i = 0; i < 100; i++)
{
editorBeatmap.ControlPointInfo.Add((i + 1) * 1000, new EffectControlPoint
{
KiaiMode = Convert.ToBoolean(i % 2),
});
}
});
AddStep("Select first effect point", () =>
{
InputManager.MoveMouseTo(Child.ChildrenOfType<EffectRowAttribute>().First());
InputManager.Click(MouseButton.Left);
});
AddStep("Seek to beginning", () => EditorClock.Seek(0));
AddStep("Seek to last point", () => EditorClock.Seek(101 * 1000));
AddUntilStep("Scrolled to end", () => timingScreen.ChildrenOfType<OsuScrollContainer>().First().IsScrolledToEnd());
}
protected override void Dispose(bool isDisposing)
{
Beatmap.Disabled = false;

View File

@ -1,8 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
@ -20,6 +19,8 @@ namespace osu.Game.Screens.Edit
{
public abstract partial class EditorTable : TableContainer
{
public event Action<Drawable>? OnRowSelected;
private const float horizontal_inset = 20;
protected const float ROW_HEIGHT = 25;
@ -45,7 +46,18 @@ namespace osu.Game.Screens.Edit
});
}
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default);
protected void SetSelectedRow(object? item)
{
foreach (var b in BackgroundFlow)
{
b.Selected = ReferenceEquals(b.Item, item);
if (b.Selected)
OnRowSelected?.Invoke(b);
}
}
protected override Drawable CreateHeader(int index, TableColumn? column) => new HeaderText(column?.Header ?? default);
private partial class HeaderText : OsuSpriteText
{
@ -84,11 +96,6 @@ namespace osu.Game.Screens.Edit
Alpha = 0,
},
};
// todo delete
Action = () =>
{
};
}
private Color4 colourHover;

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
@ -23,10 +21,10 @@ namespace osu.Game.Screens.Edit.Timing
public partial class ControlPointTable : EditorTable
{
[Resolved]
private Bindable<ControlPointGroup> selectedGroup { get; set; }
private Bindable<ControlPointGroup> selectedGroup { get; set; } = null!;
[Resolved]
private EditorClock clock { get; set; }
private EditorClock clock { get; set; } = null!;
public const float TIMING_COLUMN_WIDTH = 230;
@ -37,7 +35,7 @@ namespace osu.Game.Screens.Edit.Timing
Content = null;
BackgroundFlow.Clear();
if (value?.Any() != true)
if (!value.Any())
return;
foreach (var group in value)
@ -63,19 +61,10 @@ namespace osu.Game.Screens.Edit.Timing
{
base.LoadComplete();
selectedGroup.BindValueChanged(_ =>
{
// TODO: This should scroll the selected row into view.
updateSelectedGroup();
}, true);
selectedGroup.BindValueChanged(_ => updateSelectedGroup(), true);
}
private void updateSelectedGroup()
{
// TODO: This should scroll the selected row into view.
foreach (var b in BackgroundFlow)
b.Selected = ReferenceEquals(b.Item, selectedGroup?.Value);
}
private void updateSelectedGroup() => SetSelectedRow(selectedGroup.Value);
private TableColumn[] createHeaders()
{
@ -92,32 +81,38 @@ namespace osu.Game.Screens.Edit.Timing
{
return new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.Y,
Width = TIMING_COLUMN_WIDTH,
Spacing = new Vector2(5),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = group.Time.ToEditorFormattedString(),
Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold),
Width = 70,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new ControlGroupAttributes(group, c => c is TimingControlPoint)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
}
}
},
new ControlGroupAttributes(group, c => !(c is TimingControlPoint))
new ControlGroupTiming(group),
new ControlGroupAttributes(group, c => c is not TimingControlPoint)
};
}
private partial class ControlGroupTiming : FillFlowContainer
{
public ControlGroupTiming(ControlPointGroup group)
{
Name = @"ControlGroupTiming";
RelativeSizeAxes = Axes.Y;
Width = TIMING_COLUMN_WIDTH;
Spacing = new Vector2(5);
Children = new Drawable[]
{
new OsuSpriteText
{
Text = group.Time.ToEditorFormattedString(),
Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold),
Width = 70,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new ControlGroupAttributes(group, c => c is TimingControlPoint)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
}
};
}
}
private partial class ControlGroupAttributes : CompositeDrawable
{
private readonly Func<ControlPoint, bool> matchFunction;
@ -132,6 +127,7 @@ namespace osu.Game.Screens.Edit.Timing
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Name = @"ControlGroupAttributes";
InternalChild = fill = new FillFlowContainer
{
@ -161,7 +157,6 @@ namespace osu.Game.Screens.Edit.Timing
fill.ChildrenEnumerable = controlPoints
.Where(matchFunction)
.Select(createAttribute)
.Where(c => c != null)
// arbitrary ordering to make timing points first.
// probably want to explicitly define order in the future.
.OrderByDescending(c => c.GetType().Name);
@ -184,7 +179,7 @@ namespace osu.Game.Screens.Edit.Timing
return new SampleRowAttribute(sample);
}
return null;
throw new ArgumentOutOfRangeException(nameof(controlPoint), $"Control point type {controlPoint.GetType()} is not supported");
}
}
}

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Linq;
using osu.Framework.Allocation;
@ -50,24 +48,24 @@ namespace osu.Game.Screens.Edit.Timing
public partial class ControlPointList : CompositeDrawable
{
private OsuButton deleteButton;
private ControlPointTable table;
private OsuButton deleteButton = null!;
private ControlPointTable table = null!;
private OsuScrollContainer scroll = null!;
private RoundedButton addButton = null!;
private readonly IBindableList<ControlPointGroup> controlPointGroups = new BindableList<ControlPointGroup>();
private RoundedButton addButton;
[Resolved]
private EditorClock clock { get; set; } = null!;
[Resolved]
private EditorClock clock { get; set; }
protected EditorBeatmap Beatmap { get; private set; } = null!;
[Resolved]
protected EditorBeatmap Beatmap { get; private set; }
private Bindable<ControlPointGroup?> selectedGroup { get; set; } = null!;
[Resolved]
private Bindable<ControlPointGroup> selectedGroup { get; set; }
[Resolved(canBeNull: true)]
private IEditorChangeHandler changeHandler { get; set; }
private IEditorChangeHandler? changeHandler { get; set; }
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colours)
@ -88,7 +86,7 @@ namespace osu.Game.Screens.Edit.Timing
RelativeSizeAxes = Axes.Y,
Width = ControlPointTable.TIMING_COLUMN_WIDTH + margins,
},
new OsuScrollContainer
scroll = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = table = new ControlPointTable(),
@ -142,6 +140,8 @@ namespace osu.Game.Screens.Edit.Timing
table.ControlGroups = controlPointGroups;
changeHandler?.SaveState();
}, true);
table.OnRowSelected += drawable => scroll.ScrollIntoView(drawable);
}
protected override bool OnClick(ClickEvent e)
@ -159,7 +159,7 @@ namespace osu.Game.Screens.Edit.Timing
addButton.Enabled.Value = clock.CurrentTimeAccurate != selectedGroup.Value?.Time;
}
private Type trackedType;
private Type? trackedType;
/// <summary>
/// Given the user has selected a control point group, we want to track any group which is

View File

@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
@ -20,19 +18,19 @@ namespace osu.Game.Screens.Edit.Verify
{
public partial class IssueTable : EditorTable
{
[Resolved]
private VerifyScreen verify { get; set; }
private Bindable<Issue> selectedIssue;
private Bindable<Issue> selectedIssue = null!;
[Resolved]
private EditorClock clock { get; set; }
private VerifyScreen verify { get; set; } = null!;
[Resolved]
private EditorBeatmap editorBeatmap { get; set; }
private EditorClock clock { get; set; } = null!;
[Resolved]
private Editor editor { get; set; }
private EditorBeatmap editorBeatmap { get; set; } = null!;
[Resolved]
private Editor editor { get; set; } = null!;
public IEnumerable<Issue> Issues
{
@ -41,7 +39,7 @@ namespace osu.Game.Screens.Edit.Verify
Content = null;
BackgroundFlow.Clear();
if (value == null)
if (!value.Any())
return;
foreach (var issue in value)
@ -79,7 +77,7 @@ namespace osu.Game.Screens.Edit.Verify
selectedIssue = verify.SelectedIssue.GetBoundCopy();
selectedIssue.BindValueChanged(issue =>
{
foreach (var b in BackgroundFlow) b.Selected = b.Item == issue.NewValue;
SetSelectedRow(issue.NewValue);
}, true);
}