mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 03:02:54 +08:00
Merge branch 'master' into tournament-team-editor-improvements
This commit is contained in:
commit
9d71e8f20d
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -82,7 +80,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNudgeSelection()
|
public void TestNudgeSelection()
|
||||||
{
|
{
|
||||||
HitCircle[] addedObjects = null;
|
HitCircle[] addedObjects = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||||
{
|
{
|
||||||
@ -104,7 +102,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestRotateHotkeys()
|
public void TestRotateHotkeys()
|
||||||
{
|
{
|
||||||
HitCircle[] addedObjects = null;
|
HitCircle[] addedObjects = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||||
{
|
{
|
||||||
@ -136,7 +134,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGlobalFlipHotkeys()
|
public void TestGlobalFlipHotkeys()
|
||||||
{
|
{
|
||||||
HitCircle addedObject = null;
|
HitCircle addedObject = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 }));
|
AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 }));
|
||||||
|
|
||||||
@ -286,11 +284,104 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCyclicSelection()
|
||||||
|
{
|
||||||
|
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||||
|
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
||||||
|
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
|
||||||
|
// cycle around
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCyclicSelectionOutwards()
|
||||||
|
{
|
||||||
|
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||||
|
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
||||||
|
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
|
AddStep("seek near second", () => EditorClock.Seek(320));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
|
||||||
|
// cycle around
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCyclicSelectionBackwards()
|
||||||
|
{
|
||||||
|
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||||
|
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
||||||
|
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => firstObject);
|
||||||
|
|
||||||
|
AddStep("seek to third", () => EditorClock.Seek(600));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||||
|
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||||
|
|
||||||
|
// cycle around
|
||||||
|
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDoubleClickToSeek()
|
||||||
|
{
|
||||||
|
var hitCircle = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||||
|
|
||||||
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { hitCircle }));
|
||||||
|
|
||||||
|
moveMouseToObject(() => hitCircle);
|
||||||
|
|
||||||
|
AddRepeatStep("double click", () => InputManager.Click(MouseButton.Left), 2);
|
||||||
|
|
||||||
|
AddUntilStep("seeked to circle", () => EditorClock.CurrentTime, () => Is.EqualTo(600));
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag)
|
public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag)
|
||||||
{
|
{
|
||||||
HitCircle[] addedObjects = null;
|
HitCircle[] addedObjects = null!;
|
||||||
|
|
||||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||||
{
|
{
|
||||||
@ -389,7 +480,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestQuickDeleteRemovesSliderControlPoint()
|
public void TestQuickDeleteRemovesSliderControlPoint()
|
||||||
{
|
{
|
||||||
Slider slider = null;
|
Slider slider = null!;
|
||||||
|
|
||||||
PathControlPoint[] points =
|
PathControlPoint[] points =
|
||||||
{
|
{
|
||||||
|
@ -67,6 +67,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
|
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectFreeMods()
|
||||||
|
{
|
||||||
|
AddStep("set some freemods", () => songSelect.FreeMods.Value = new OsuRuleset().GetModsFor(ModType.Fun).ToArray());
|
||||||
|
AddStep("set all freemods", () => songSelect.FreeMods.Value = new OsuRuleset().CreateAllMods().ToArray());
|
||||||
|
AddStep("set no freemods", () => songSelect.FreeMods.Value = Array.Empty<Mod>());
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBeatmapConfirmed()
|
public void TestBeatmapConfirmed()
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Input.States;
|
using osu.Framework.Input.States;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Tournament.Components;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
using osu.Game.Tournament.Screens.Ladder;
|
using osu.Game.Tournament.Screens.Ladder;
|
||||||
using osu.Game.Tournament.Screens.Ladder.Components;
|
using osu.Game.Tournament.Screens.Ladder.Components;
|
||||||
@ -35,11 +36,9 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Content.Add(new LadderEditorSettings
|
AddInternal(new ControlPanel
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Child = new LadderEditorSettings(),
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
Margin = new MarginPadding(5)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddInternal(rightClickMessage = new WarningBox("Right click to place and link matches"));
|
AddInternal(rightClickMessage = new WarningBox("Right click to place and link matches"));
|
||||||
|
@ -213,7 +213,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
int instantWinAmount = Match.Round.Value.BestOf.Value / 2;
|
int instantWinAmount = Match.Round.Value.BestOf.Value / 2;
|
||||||
|
|
||||||
Match.Completed.Value = Match.Round.Value.BestOf.Value > 0
|
Match.Completed.Value = Match.Round.Value.BestOf.Value > 0
|
||||||
&& (Match.Team1Score.Value + Match.Team2Score.Value >= Match.Round.Value.BestOf.Value || Match.Team1Score.Value > instantWinAmount || Match.Team2Score.Value > instantWinAmount);
|
&& (Match.Team1Score.Value + Match.Team2Score.Value >= Match.Round.Value.BestOf.Value || Match.Team1Score.Value > instantWinAmount
|
||||||
|
|| Match.Team2Score.Value > instantWinAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -265,8 +266,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e) => e.Button == MouseButton.Left && editorInfo != null;
|
protected override bool OnMouseDown(MouseDownEvent e) => e.Button == MouseButton.Left && editorInfo != null;
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e) => editorInfo != null;
|
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
if (Selected && editorInfo != null && e.Key == Key.Delete)
|
if (Selected && editorInfo != null && e.Key == Key.Delete)
|
||||||
@ -287,17 +286,36 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vector2 positionAtStartOfDrag;
|
||||||
|
|
||||||
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
|
{
|
||||||
|
if (editorInfo != null)
|
||||||
|
{
|
||||||
|
positionAtStartOfDrag = Position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
protected override void OnDrag(DragEvent e)
|
||||||
{
|
{
|
||||||
base.OnDrag(e);
|
base.OnDrag(e);
|
||||||
|
|
||||||
Selected = true;
|
Selected = true;
|
||||||
this.MoveToOffset(e.Delta);
|
|
||||||
|
|
||||||
var pos = Position;
|
this.MoveTo(snapToGrid(positionAtStartOfDrag + (e.MousePosition - e.MouseDownPosition)));
|
||||||
Match.Position.Value = new Point((int)pos.X, (int)pos.Y);
|
|
||||||
|
Match.Position.Value = new Point((int)Position.X, (int)Position.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vector2 snapToGrid(Vector2 pos) =>
|
||||||
|
new Vector2(
|
||||||
|
(int)(pos.X / 10) * 10,
|
||||||
|
(int)(pos.Y / 10) * 10
|
||||||
|
);
|
||||||
|
|
||||||
public void Remove()
|
public void Remove()
|
||||||
{
|
{
|
||||||
Selected = false;
|
Selected = false;
|
||||||
|
@ -11,15 +11,17 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Screens.Play.PlayerSettings;
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
using osu.Game.Tournament.Components;
|
using osu.Game.Tournament.Components;
|
||||||
using osu.Game.Tournament.Models;
|
using osu.Game.Tournament.Models;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Screens.Ladder.Components
|
namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||||
{
|
{
|
||||||
public partial class LadderEditorSettings : PlayerSettingsGroup
|
public partial class LadderEditorSettings : CompositeDrawable
|
||||||
{
|
{
|
||||||
private SettingsDropdown<TournamentRound> roundDropdown;
|
private SettingsDropdown<TournamentRound> roundDropdown;
|
||||||
private PlayerCheckbox losersCheckbox;
|
private PlayerCheckbox losersCheckbox;
|
||||||
@ -33,21 +35,26 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private LadderInfo ladderInfo { get; set; }
|
private LadderInfo ladderInfo { get; set; }
|
||||||
|
|
||||||
public LadderEditorSettings()
|
|
||||||
: base("ladder")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
{
|
{
|
||||||
team1Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 1" },
|
RelativeSizeAxes = Axes.X,
|
||||||
team2Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 2" },
|
AutoSizeAxes = Axes.Y,
|
||||||
roundDropdown = new SettingsRoundDropdown(ladderInfo.Rounds) { LabelText = "Round" },
|
Direction = FillDirection.Vertical,
|
||||||
losersCheckbox = new PlayerCheckbox { LabelText = "Losers Bracket" },
|
Spacing = new Vector2(5),
|
||||||
dateTimeBox = new DateTextBox { LabelText = "Match Time" },
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
team1Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 1" },
|
||||||
|
team2Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 2" },
|
||||||
|
roundDropdown = new SettingsRoundDropdown(ladderInfo.Rounds) { LabelText = "Round" },
|
||||||
|
losersCheckbox = new PlayerCheckbox { LabelText = "Losers Bracket" },
|
||||||
|
dateTimeBox = new DateTextBox { LabelText = "Match Time" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
editorInfo.Selected.ValueChanged += selection =>
|
editorInfo.Selected.ValueChanged += selection =>
|
||||||
|
@ -36,8 +36,8 @@ namespace osu.Game.Tournament.Screens.Ladder
|
|||||||
{
|
{
|
||||||
float newScale = Math.Clamp(scale + e.ScrollDelta.Y / 15 * scale, min_scale, max_scale);
|
float newScale = Math.Clamp(scale + e.ScrollDelta.Y / 15 * scale, min_scale, max_scale);
|
||||||
|
|
||||||
this.MoveTo(target -= e.MousePosition * (newScale - scale), 2000, Easing.OutQuint);
|
this.MoveTo(target -= e.MousePosition * (newScale - scale), 1000, Easing.OutQuint);
|
||||||
this.ScaleTo(scale = newScale, 2000, Easing.OutQuint);
|
this.ScaleTo(scale = newScale, 1000, Easing.OutQuint);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ namespace osu.Game.Tournament.Screens.Ladder
|
|||||||
InternalChild = Content = new Container
|
InternalChild = Content = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new TourneyVideo("ladder")
|
new TourneyVideo("ladder")
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Tournament
|
|||||||
private Container screens;
|
private Container screens;
|
||||||
private TourneyVideo video;
|
private TourneyVideo video;
|
||||||
|
|
||||||
public const float CONTROL_AREA_WIDTH = 160;
|
public const float CONTROL_AREA_WIDTH = 200;
|
||||||
|
|
||||||
public const float STREAM_AREA_WIDTH = 1366;
|
public const float STREAM_AREA_WIDTH = 1366;
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> globalAvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> globalAvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||||
|
|
||||||
private IEnumerable<ModState> allAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
public IEnumerable<ModState> AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
||||||
|
|
||||||
private readonly BindableBool customisationVisible = new BindableBool();
|
private readonly BindableBool customisationVisible = new BindableBool();
|
||||||
|
|
||||||
@ -382,7 +382,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void filterMods()
|
private void filterMods()
|
||||||
{
|
{
|
||||||
foreach (var modState in allAvailableMods)
|
foreach (var modState in AllAvailableMods)
|
||||||
modState.ValidForSelection.Value = modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod);
|
modState.ValidForSelection.Value = modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +407,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
bool anyCustomisableModActive = false;
|
bool anyCustomisableModActive = false;
|
||||||
bool anyModPendingConfiguration = false;
|
bool anyModPendingConfiguration = false;
|
||||||
|
|
||||||
foreach (var modState in allAvailableMods)
|
foreach (var modState in AllAvailableMods)
|
||||||
{
|
{
|
||||||
anyCustomisableModActive |= modState.Active.Value && modState.Mod.GetSettingsSourceProperties().Any();
|
anyCustomisableModActive |= modState.Active.Value && modState.Mod.GetSettingsSourceProperties().Any();
|
||||||
anyModPendingConfiguration |= modState.PendingConfiguration;
|
anyModPendingConfiguration |= modState.PendingConfiguration;
|
||||||
@ -464,7 +464,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
var newSelection = new List<Mod>();
|
var newSelection = new List<Mod>();
|
||||||
|
|
||||||
foreach (var modState in allAvailableMods)
|
foreach (var modState in AllAvailableMods)
|
||||||
{
|
{
|
||||||
var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType());
|
var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType());
|
||||||
|
|
||||||
@ -491,7 +491,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (externalSelectionUpdateInProgress)
|
if (externalSelectionUpdateInProgress)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var candidateSelection = allAvailableMods.Where(modState => modState.Active.Value)
|
var candidateSelection = AllAvailableMods.Where(modState => modState.Active.Value)
|
||||||
.Select(modState => modState.Mod)
|
.Select(modState => modState.Mod)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private SkinEditor editor { get; set; } = null!;
|
private SkinEditor editor { get; set; } = null!;
|
||||||
|
|
||||||
protected override bool AllowCyclicSelection => true;
|
|
||||||
|
|
||||||
public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer)
|
public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer)
|
||||||
{
|
{
|
||||||
this.targetContainer = targetContainer;
|
this.targetContainer = targetContainer;
|
||||||
|
@ -46,15 +46,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
protected readonly BindableList<T> SelectedItems = new BindableList<T>();
|
protected readonly BindableList<T> SelectedItems = new BindableList<T>();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether to allow cyclic selection on clicking multiple times.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Disabled by default as it does not work well with editors that support double-clicking or other advanced interactions.
|
|
||||||
/// Can probably be made to work with more thought.
|
|
||||||
/// </remarks>
|
|
||||||
protected virtual bool AllowCyclicSelection => false;
|
|
||||||
|
|
||||||
protected BlueprintContainer()
|
protected BlueprintContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
@ -167,6 +158,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
if (ClickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != ClickedBlueprint)
|
if (ClickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != ClickedBlueprint)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
doubleClickHandled = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +169,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
endClickSelection(e);
|
endClickSelection(e);
|
||||||
clickSelectionHandled = false;
|
clickSelectionHandled = false;
|
||||||
|
doubleClickHandled = false;
|
||||||
isDraggingBlueprint = false;
|
isDraggingBlueprint = false;
|
||||||
wasDragStarted = false;
|
wasDragStarted = false;
|
||||||
});
|
});
|
||||||
@ -376,6 +369,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool clickSelectionHandled;
|
private bool clickSelectionHandled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a blueprint was double-clicked since last mouse down.
|
||||||
|
/// </summary>
|
||||||
|
private bool doubleClickHandled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case.
|
/// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -427,8 +425,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
/// <returns>Whether a click selection was active.</returns>
|
/// <returns>Whether a click selection was active.</returns>
|
||||||
private bool endClickSelection(MouseButtonEvent e)
|
private bool endClickSelection(MouseButtonEvent e)
|
||||||
{
|
{
|
||||||
// If already handled a selection or drag, we don't want to perform a mouse up / click action.
|
// If already handled a selection, double-click, or drag, we don't want to perform a mouse up / click action.
|
||||||
if (clickSelectionHandled || isDraggingBlueprint) return true;
|
if (clickSelectionHandled || doubleClickHandled || isDraggingBlueprint) return true;
|
||||||
|
|
||||||
if (e.Button != MouseButton.Left) return false;
|
if (e.Button != MouseButton.Left) return false;
|
||||||
|
|
||||||
@ -444,7 +442,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1 && AllowCyclicSelection)
|
if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1)
|
||||||
{
|
{
|
||||||
// If a click occurred and was handled by the currently selected blueprint but didn't result in a drag,
|
// If a click occurred and was handled by the currently selected blueprint but didn't result in a drag,
|
||||||
// cycle between other blueprints which are also under the cursor.
|
// cycle between other blueprints which are also under the cursor.
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Play.HUD;
|
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -17,28 +23,60 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
{
|
{
|
||||||
public partial class FooterButtonFreeMods : FooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
|
public partial class FooterButtonFreeMods : FooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
|
||||||
{
|
{
|
||||||
public Bindable<IReadOnlyList<Mod>> Current
|
public Bindable<IReadOnlyList<Mod>> Current { get; set; } = new BindableWithCurrent<IReadOnlyList<Mod>>();
|
||||||
|
|
||||||
|
private OsuSpriteText count = null!;
|
||||||
|
|
||||||
|
private Circle circle = null!;
|
||||||
|
|
||||||
|
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
||||||
|
|
||||||
|
public FooterButtonFreeMods(FreeModSelectOverlay freeModSelectOverlay)
|
||||||
{
|
{
|
||||||
get => modDisplay.Current;
|
this.freeModSelectOverlay = freeModSelectOverlay;
|
||||||
set => modDisplay.Current = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ModDisplay modDisplay;
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
public FooterButtonFreeMods()
|
|
||||||
{
|
|
||||||
ButtonContentContainer.Add(modDisplay = new ModDisplay
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Scale = new Vector2(0.8f),
|
|
||||||
ExpansionMode = ExpansionMode.AlwaysContracted,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
|
ButtonContentContainer.AddRange(new[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
circle = new Circle
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Colour = colours.YellowDark,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
count = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Padding = new MarginPadding(5),
|
||||||
|
UseFullGlyphHeight = false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new IconButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(0.8f),
|
||||||
|
Icon = FontAwesome.Solid.Bars,
|
||||||
|
Action = () => freeModSelectOverlay.ToggleVisibility()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
SelectedColour = colours.Yellow;
|
SelectedColour = colours.Yellow;
|
||||||
DeselectedColour = SelectedColour.Opacity(0.5f);
|
DeselectedColour = SelectedColour.Opacity(0.5f);
|
||||||
Text = @"freemods";
|
Text = @"freemods";
|
||||||
@ -49,14 +87,49 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Current.BindValueChanged(_ => updateModDisplay(), true);
|
Current.BindValueChanged(_ => updateModDisplay(), true);
|
||||||
|
|
||||||
|
// Overwrite any external behaviour as we delegate the main toggle action to a sub-button.
|
||||||
|
Action = toggleAllFreeMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Immediately toggle all free mods on/off.
|
||||||
|
/// </summary>
|
||||||
|
private void toggleAllFreeMods()
|
||||||
|
{
|
||||||
|
var availableMods = allAvailableAndValidMods.ToArray();
|
||||||
|
|
||||||
|
Current.Value = Current.Value.Count == availableMods.Length
|
||||||
|
? Array.Empty<Mod>()
|
||||||
|
: availableMods;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateModDisplay()
|
private void updateModDisplay()
|
||||||
{
|
{
|
||||||
if (Current.Value?.Count > 0)
|
int current = Current.Value.Count;
|
||||||
modDisplay.FadeIn();
|
|
||||||
|
if (current == allAvailableAndValidMods.Count())
|
||||||
|
{
|
||||||
|
count.Text = "all";
|
||||||
|
count.FadeColour(colours.Gray2, 200, Easing.OutQuint);
|
||||||
|
circle.FadeColour(colours.Yellow, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
else if (current > 0)
|
||||||
|
{
|
||||||
|
count.Text = $"{current} mods";
|
||||||
|
count.FadeColour(colours.Gray2, 200, Easing.OutQuint);
|
||||||
|
circle.FadeColour(colours.YellowDark, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
modDisplay.FadeOut();
|
{
|
||||||
|
count.Text = "off";
|
||||||
|
count.FadeColour(colours.GrayF, 200, Easing.OutQuint);
|
||||||
|
circle.FadeColour(colours.Gray4, 200, Easing.OutQuint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Mod> allAvailableAndValidMods => freeModSelectOverlay.AllAvailableMods
|
||||||
|
.Where(state => state.ValidForSelection.Value)
|
||||||
|
.Select(state => state.Mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,9 +175,12 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons()
|
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons()
|
||||||
{
|
{
|
||||||
var buttons = base.CreateFooterButtons().ToList();
|
var baseButtons = base.CreateFooterButtons().ToList();
|
||||||
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = FreeMods }, freeModSelectOverlay));
|
var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods };
|
||||||
return buttons;
|
|
||||||
|
baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay));
|
||||||
|
|
||||||
|
return baseButtons;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user