mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-13 11:20:28 +00:00
[同步官方更新]
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
]
|
||||
},
|
||||
"ppy.localisationanalyser.tools": {
|
||||
"version": "2024.802.0",
|
||||
"version": "2025.1208.0",
|
||||
"commands": [
|
||||
"localisation"
|
||||
]
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Humanizer;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
@@ -196,11 +195,6 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
clickSamplePiece(1);
|
||||
samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT);
|
||||
|
||||
setBankViaPopover(string.Empty);
|
||||
hitObjectHasSampleBank(0, HitSampleInfo.BANK_SOFT);
|
||||
hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT);
|
||||
samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT);
|
||||
|
||||
setBankViaPopover(HitSampleInfo.BANK_DRUM);
|
||||
hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM);
|
||||
hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
|
||||
@@ -219,11 +213,6 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
clickSamplePiece(1);
|
||||
samplePopoverHasIndeterminateBank();
|
||||
|
||||
setBankViaPopover(string.Empty);
|
||||
hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
|
||||
hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT);
|
||||
samplePopoverHasIndeterminateBank();
|
||||
|
||||
setBankViaPopover(HitSampleInfo.BANK_NORMAL);
|
||||
hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
|
||||
hitObjectHasSampleBank(1, HitSampleInfo.BANK_NORMAL);
|
||||
@@ -878,17 +867,17 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private void samplePopoverHasSingleBank(string bank) => AddUntilStep($"sample popover has bank {bank}", () =>
|
||||
{
|
||||
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().SingleOrDefault();
|
||||
var textBox = popover?.ChildrenOfType<OsuTextBox>().First();
|
||||
var dropdown = popover?.ChildrenOfType<LabelledDropdown<string>>().First();
|
||||
|
||||
return textBox?.Current.Value == bank && string.IsNullOrEmpty(textBox.PlaceholderText.ToString());
|
||||
return dropdown?.Current.Value == bank;
|
||||
});
|
||||
|
||||
private void samplePopoverHasIndeterminateBank() => AddUntilStep("sample popover has indeterminate bank", () =>
|
||||
{
|
||||
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().SingleOrDefault();
|
||||
var textBox = popover?.ChildrenOfType<OsuTextBox>().First();
|
||||
var dropdown = popover?.ChildrenOfType<LabelledDropdown<string>>().First();
|
||||
|
||||
return textBox != null && string.IsNullOrEmpty(textBox.Current.Value) && !string.IsNullOrEmpty(textBox.PlaceholderText.ToString());
|
||||
return dropdown?.Current.Value == "(multiple)";
|
||||
});
|
||||
|
||||
private void dismissPopover()
|
||||
@@ -920,23 +909,15 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private void setBankViaPopover(string bank) => AddStep($"set bank {bank} via popover", () =>
|
||||
{
|
||||
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().Single();
|
||||
var textBox = popover.ChildrenOfType<LabelledTextBox>().First();
|
||||
var textBox = popover.ChildrenOfType<LabelledDropdown<string>>().First();
|
||||
textBox.Current.Value = bank;
|
||||
// force a commit via keyboard.
|
||||
// this is needed when testing attempting to set empty bank - which should revert to the previous value, but only on commit.
|
||||
((IFocusManager)InputManager).ChangeFocus(textBox);
|
||||
InputManager.Key(Key.Enter);
|
||||
});
|
||||
|
||||
private void setAdditionBankViaPopover(string bank) => AddStep($"set addition bank {bank} via popover", () =>
|
||||
{
|
||||
var popover = this.ChildrenOfType<SamplePointPiece.SampleEditPopover>().Single();
|
||||
var textBox = popover.ChildrenOfType<LabelledTextBox>().ToArray()[1];
|
||||
var textBox = popover.ChildrenOfType<LabelledDropdown<string>>().ToArray()[1];
|
||||
textBox.Current.Value = bank;
|
||||
// force a commit via keyboard.
|
||||
// this is needed when testing attempting to set empty bank - which should revert to the previous value, but only on commit.
|
||||
((IFocusManager)InputManager).ChangeFocus(textBox);
|
||||
InputManager.Key(Key.Enter);
|
||||
});
|
||||
|
||||
private void toggleAdditionViaPopover(int index) => AddStep($"toggle addition {index} via popover", () =>
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
[Test]
|
||||
public void TestLabelledDropdown()
|
||||
=> AddStep(@"create dropdown", () => Child = new LabelledDropdown<string>
|
||||
=> AddStep(@"create dropdown", () => Child = new LabelledDropdown<string>(true)
|
||||
{
|
||||
Label = @"Countdown speed",
|
||||
Items = new[]
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
[Test]
|
||||
public void TestLabelledEnumDropdown()
|
||||
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapOnlineStatus>
|
||||
=> AddStep(@"create dropdown", () => Child = new LabelledEnumDropdown<BeatmapOnlineStatus>(true)
|
||||
{
|
||||
Label = @"Beatmap status",
|
||||
Description = @"This is a description"
|
||||
|
||||
@@ -116,12 +116,13 @@ namespace osu.Game.Tournament.Screens.Setup
|
||||
Failing = api.IsLoggedIn != true,
|
||||
Description = "In order to access the API and display metadata, signing in is required."
|
||||
},
|
||||
new LabelledDropdown<RulesetInfo?>
|
||||
new LabelledDropdown<RulesetInfo?>(padded: true)
|
||||
{
|
||||
Label = "Ruleset",
|
||||
Description = "Decides what stats are displayed and which ranks are retrieved for players. This requires a restart to reload data for an existing bracket.",
|
||||
Items = rulesets.AvailableRulesets,
|
||||
Current = LadderInfo.Ruleset,
|
||||
DropdownWidth = 0.5f,
|
||||
},
|
||||
new TournamentSwitcher
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@@ -72,7 +71,9 @@ namespace osu.Game.Collections
|
||||
var createdItem = flow.Children.SingleOrDefault(item => item.Model.Value.ID == lastCreated);
|
||||
|
||||
if (createdItem != null)
|
||||
scroll.ScrollTo(createdItem);
|
||||
{
|
||||
ScheduleAfterChildren(() => scroll.ScrollIntoView(createdItem));
|
||||
}
|
||||
|
||||
lastCreated = null;
|
||||
}
|
||||
@@ -104,13 +105,8 @@ namespace osu.Game.Collections
|
||||
}
|
||||
}
|
||||
|
||||
protected override OsuRearrangeableListItem<Live<BeatmapCollection>> CreateOsuDrawable(Live<BeatmapCollection> item)
|
||||
{
|
||||
if (item.ID == scroll.PlaceholderItem.Model.ID)
|
||||
return scroll.ReplacePlaceholder();
|
||||
|
||||
return new DrawableCollectionListItem(item, true);
|
||||
}
|
||||
protected override OsuRearrangeableListItem<Live<BeatmapCollection>> CreateOsuDrawable(Live<BeatmapCollection> item) =>
|
||||
new DrawableCollectionListItem(item, true);
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
@@ -122,91 +118,22 @@ namespace osu.Game.Collections
|
||||
/// The scroll container for this <see cref="DrawableCollectionList"/>.
|
||||
/// Contains the main flow of <see cref="DrawableCollectionListItem"/> and attaches a placeholder item to the end of the list.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use <see cref="ReplacePlaceholder"/> to transfer the placeholder into the main list.
|
||||
/// </remarks>
|
||||
private partial class Scroll : OsuScrollContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The currently-displayed placeholder item.
|
||||
/// </summary>
|
||||
public DrawableCollectionListItem PlaceholderItem { get; private set; } = null!;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
private readonly Container<DrawableCollectionListItem> placeholderContainer;
|
||||
private readonly FillFlowContainer content;
|
||||
|
||||
public Scroll()
|
||||
{
|
||||
ScrollbarOverlapsContent = false;
|
||||
|
||||
base.Content.Add(new FillFlowContainer
|
||||
base.Content.Add(content = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
LayoutDuration = 200,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
content = new Container { RelativeSizeAxes = Axes.X },
|
||||
placeholderContainer = new Container<DrawableCollectionListItem>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ReplacePlaceholder();
|
||||
Debug.Assert(PlaceholderItem != null);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// AutoSizeAxes cannot be used as the height should represent the post-layout-transform height at all times, so that the placeholder doesn't bounce around.
|
||||
content.Height = ((Flow)Child).Children.Sum(c => c.IsPresent ? c.DrawHeight + 5 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the current <see cref="PlaceholderItem"/> with a new one, and returns the previous.
|
||||
/// </summary>
|
||||
/// <returns>The current <see cref="PlaceholderItem"/>.</returns>
|
||||
public DrawableCollectionListItem ReplacePlaceholder()
|
||||
{
|
||||
var previous = PlaceholderItem;
|
||||
|
||||
placeholderContainer.Clear(false);
|
||||
placeholderContainer.Add(PlaceholderItem = new NewCollectionEntryItem());
|
||||
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
private partial class NewCollectionEntryItem : DrawableCollectionListItem
|
||||
{
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
public NewCollectionEntryItem()
|
||||
: base(new BeatmapCollection().ToLiveUnmanaged(), false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
TextBox.OnCommit += (sender, newText) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(TextBox.Text))
|
||||
return;
|
||||
|
||||
realm.Write(r => r.Add(new BeatmapCollection(TextBox.Text)));
|
||||
TextBox.Text = string.Empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,11 @@ namespace osu.Game.Collections
|
||||
ShowDragHandle.Value = false;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = item_height / 2;
|
||||
|
||||
// This doesn't match the latest design spec (should be 5) but is an in-between that feels right to the eye
|
||||
// until we move everything over to Form controls.
|
||||
CornerRadius = 10;
|
||||
CornerExponent = 2.5f;
|
||||
}
|
||||
|
||||
protected override Drawable CreateContent() => content = new ItemContent(Model);
|
||||
@@ -135,7 +139,8 @@ namespace osu.Game.Collections
|
||||
{
|
||||
this.collection = collection;
|
||||
|
||||
CornerRadius = item_height / 2;
|
||||
CornerRadius = 10;
|
||||
CornerExponent = 2.5f;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -102,12 +103,13 @@ namespace osu.Game.Collections
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.GreySeaFoamDarker
|
||||
Colour = colours.GreySeaFoamDarker,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
@@ -115,23 +117,30 @@ namespace osu.Game.Collections
|
||||
Padding = new MarginPadding(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
list = new DrawableCollectionList
|
||||
{
|
||||
Padding = new MarginPadding { Vertical = 50 },
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
searchTextBox = new BasicSearchTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Y = 10,
|
||||
Height = 40,
|
||||
ReleaseFocusOnCommit = false,
|
||||
HoldFocus = true,
|
||||
PlaceholderText = HomeStrings.SearchPlaceholder,
|
||||
},
|
||||
list = new DrawableCollectionList
|
||||
new Container
|
||||
{
|
||||
Padding = new MarginPadding
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Top = 60,
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
new NewCollectionEntryItem()
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -184,5 +193,30 @@ namespace osu.Game.Collections
|
||||
// Ensure that textboxes commit
|
||||
GetContainingFocusManager()?.TriggerFocusContention(this);
|
||||
}
|
||||
|
||||
private partial class NewCollectionEntryItem : DrawableCollectionListItem
|
||||
{
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
public NewCollectionEntryItem()
|
||||
: base(new BeatmapCollection().ToLiveUnmanaged(), false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
TextBox.OnCommit += (_, _) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(TextBox.Text))
|
||||
return;
|
||||
|
||||
realm.Write(r => r.Add(new BeatmapCollection(TextBox.Text)));
|
||||
TextBox.Text = string.Empty;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public partial class LabelledDropdown<TItem> : LabelledComponent<OsuDropdown<TItem>, TItem>
|
||||
{
|
||||
public LabelledDropdown()
|
||||
: base(true)
|
||||
public LabelledDropdown(bool padded)
|
||||
: base(padded)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -20,12 +20,22 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
set => Component.Items = value;
|
||||
}
|
||||
|
||||
public float DropdownWidth
|
||||
{
|
||||
get => Component.Width;
|
||||
set => Component.Width = value;
|
||||
}
|
||||
|
||||
protected sealed override OsuDropdown<TItem> CreateComponent() => CreateDropdown().With(d =>
|
||||
{
|
||||
d.RelativeSizeAxes = Axes.X;
|
||||
d.Width = 0.5f;
|
||||
});
|
||||
|
||||
protected virtual OsuDropdown<TItem> CreateDropdown() => new OsuDropdown<TItem>();
|
||||
protected virtual OsuDropdown<TItem> CreateDropdown() => new Dropdown();
|
||||
|
||||
private partial class Dropdown : OsuDropdown<TItem>
|
||||
{
|
||||
protected override DropdownMenu CreateMenu() => base.CreateMenu().With(menu => menu.MaxHeight = 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
public partial class LabelledEnumDropdown<TEnum> : LabelledDropdown<TEnum>
|
||||
where TEnum : struct, Enum
|
||||
{
|
||||
public LabelledEnumDropdown(bool padded)
|
||||
: base(padded)
|
||||
{
|
||||
}
|
||||
|
||||
protected override OsuDropdown<TEnum> CreateDropdown() => new OsuEnumDropdown<TEnum>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ using osu.Game.Resources;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Utils;
|
||||
using RuntimeInfo = osu.Framework.RuntimeInfo;
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace osu.Game.Overlays
|
||||
Origin = Anchor.BottomCentre,
|
||||
Font = OsuFont.Numeric.With(weight: FontWeight.Bold, size: 12),
|
||||
Colour = colours.YellowDark,
|
||||
Text = @"DEVELOPER BUILD For Ez2Lazer",
|
||||
Text = @"DEVELOPER BUILD",
|
||||
},
|
||||
new Sprite
|
||||
{
|
||||
|
||||
@@ -35,7 +35,6 @@ using osu.Game.Screens.Edit.Components.Menus;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.LAsEzExtensions.Configuration;
|
||||
using osu.Game.LAsEzExtensions.Screens;
|
||||
using osu.Game.Utils;
|
||||
|
||||
|
||||
@@ -129,8 +129,11 @@ namespace osu.Game.Rulesets.Edit
|
||||
// Inherit the bank from the previous hit object
|
||||
HitObject.Samples = HitObject.Samples.Select(s => s.Name == HitSampleInfo.HIT_NORMAL ? s.With(newBank: lastHitNormal.Bank) : s).ToList();
|
||||
|
||||
// Inherit the volume from the previous hit object
|
||||
HitObject.Samples = HitObject.Samples.Select(s => s.With(newVolume: lastHitNormal.Volume)).ToList();
|
||||
// Inherit the volume and sample set info from the previous hit object
|
||||
HitObject.Samples = HitObject.Samples.Select(s => s.With(
|
||||
newVolume: lastHitNormal.Volume,
|
||||
newSuffix: lastHitNormal.Suffix,
|
||||
newUseBeatmapSamples: lastHitNormal.UseBeatmapSamples)).ToList();
|
||||
}
|
||||
|
||||
if (HitObject is IHasRepeats hasRepeats)
|
||||
|
||||
@@ -49,7 +49,8 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
|
||||
public Drawable Icon { get; private set; } = null!;
|
||||
|
||||
public DrawableTernaryButton()
|
||||
public DrawableTernaryButton(HoverSampleSet? hoverSampleSet = HoverSampleSet.Button)
|
||||
: base(hoverSampleSet)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
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.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components.TernaryButtons
|
||||
{
|
||||
public partial class SampleSetTernaryButton : DrawableTernaryButton
|
||||
{
|
||||
public EditorBeatmapSkin.SampleSet SampleSet { get; }
|
||||
|
||||
public SampleSetTernaryButton(EditorBeatmapSkin.SampleSet sampleSet)
|
||||
: base(null)
|
||||
{
|
||||
SampleSet = sampleSet;
|
||||
CreateIcon = () => sampleSet.SampleSetIndex == 0
|
||||
? new SpriteIcon { Icon = OsuIcon.SkinA }
|
||||
: new Container
|
||||
{
|
||||
Child = new OsuSpriteText
|
||||
{
|
||||
Text = sampleSet.SampleSetIndex.ToString(),
|
||||
Font = OsuFont.Style.Body.With(weight: FontWeight.Bold),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
|
||||
switch (sampleSet.SampleSetIndex)
|
||||
{
|
||||
case 0:
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Width = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
RelativeSizeAxes = Axes.None;
|
||||
Width = Height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
new HoverSounds(HoverSampleSet.Button),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,13 +36,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
|
||||
private double? lastSeekTime;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
var localPos = ToLocalSpace(e.ScreenSpaceMousePosition);
|
||||
if (localPos.Y <= DrawHeight / 2) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
protected override bool OnDragStart(DragStartEvent e) => true;
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
@@ -58,9 +52,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
var localPos = ToLocalSpace(e.ScreenSpaceMousePosition);
|
||||
if (localPos.Y <= DrawHeight / 2) return false; // only lower half
|
||||
|
||||
seekToPosition(e.ScreenSpaceMousePosition, instant: true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,11 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Screens.Edit.Components.TernaryButtons;
|
||||
using osu.Game.Screens.Edit.Timing;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
@@ -127,7 +128,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
private void updateText()
|
||||
{
|
||||
Label.Text = $"{abbreviateBank(GetBankValue(GetSamples()))} {GetVolumeValue(GetSamples())}";
|
||||
Label.Text = $"{abbreviateBank(GetBankValue(GetSamples()))}{GetSuffix(GetSamples())} {GetVolumeValue(GetSamples())}";
|
||||
|
||||
if (!contracted.Value)
|
||||
LabelContainer.ResizeWidthTo(Label.Width, 200, Easing.OutQuint);
|
||||
@@ -149,6 +150,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
return samples.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL)?.Bank;
|
||||
}
|
||||
|
||||
public static string GetSuffix(IEnumerable<HitSampleInfo> samples)
|
||||
{
|
||||
var suffixes = samples.Select(o => o.Suffix).Distinct().ToList();
|
||||
|
||||
// having multiple values should never happen, but just for safety...
|
||||
if (suffixes.Count != 1 || suffixes.Single() is not string commonSuffix)
|
||||
return string.Empty;
|
||||
|
||||
return $@":{commonSuffix}";
|
||||
}
|
||||
|
||||
public static string? GetAdditionBankValue(IEnumerable<HitSampleInfo> samples)
|
||||
{
|
||||
var firstAddition = samples.FirstOrDefault(o => o.Name != HitSampleInfo.HIT_NORMAL);
|
||||
@@ -176,9 +188,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
private readonly HitObject hitObject;
|
||||
|
||||
private LabelledTextBox bank = null!;
|
||||
private LabelledTextBox additionBank = null!;
|
||||
private LabelledDropdown<string> bank = null!;
|
||||
private LabelledDropdown<string> additionBank = null!;
|
||||
private FillFlowContainer<SampleSetTernaryButton>? sampleSetsFlow;
|
||||
private LabelledDropdown<EditorBeatmapSkin.SampleSet>? sampleSetDropdown;
|
||||
private IndeterminateSliderWithTextBoxInput<int> volume = null!;
|
||||
private SkinnableSound demoSample = null!;
|
||||
|
||||
private FillFlowContainer togglesCollection = null!;
|
||||
|
||||
@@ -229,11 +244,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
flow = new FillFlowContainer
|
||||
{
|
||||
Width = 200,
|
||||
Width = 220,
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
Children = new[]
|
||||
{
|
||||
togglesCollection = new FillFlowContainer
|
||||
{
|
||||
@@ -242,27 +257,30 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 5),
|
||||
},
|
||||
bank = new LabelledTextBox
|
||||
bank = new LabelledDropdown<string>(padded: false)
|
||||
{
|
||||
Label = "Bank Name",
|
||||
SelectAllOnFocus = true,
|
||||
Label = "Normal Bank",
|
||||
Items = HitSampleInfo.ALL_BANKS,
|
||||
},
|
||||
additionBank = new LabelledTextBox
|
||||
additionBank = new LabelledDropdown<string>(padded: false)
|
||||
{
|
||||
Label = "Addition Bank",
|
||||
SelectAllOnFocus = true,
|
||||
Items = HitSampleInfo.ALL_BANKS,
|
||||
},
|
||||
createSampleSetContent(),
|
||||
volume = new IndeterminateSliderWithTextBoxInput<int>("Volume", new BindableInt(100)
|
||||
{
|
||||
MinValue = DrawableHitObject.MINIMUM_SAMPLE_VOLUME,
|
||||
MaxValue = 100,
|
||||
})
|
||||
}
|
||||
},
|
||||
new EditorSkinProvidingContainer(beatmap)
|
||||
{
|
||||
Child = demoSample = new SkinnableSound()
|
||||
}
|
||||
};
|
||||
|
||||
bank.TabbableContentContainer = flow;
|
||||
additionBank.TabbableContentContainer = flow;
|
||||
volume.TabbableContentContainer = flow;
|
||||
|
||||
// if the piece belongs to a currently selected object, assume that the user wants to change all selected objects.
|
||||
@@ -283,10 +301,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
setBank(val.NewValue);
|
||||
updatePrimaryBankState();
|
||||
playDemoSample();
|
||||
});
|
||||
// on commit, ensure that the value is correct by sourcing it from the objects' samples again.
|
||||
// this ensures that committing empty text causes a revert to the previous value.
|
||||
bank.OnCommit += (_, _) => updatePrimaryBankState();
|
||||
|
||||
updateAdditionBankState();
|
||||
additionBank.Current.BindValueChanged(val =>
|
||||
@@ -296,8 +312,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
setAdditionBank(val.NewValue);
|
||||
updateAdditionBankState();
|
||||
playDemoSample();
|
||||
});
|
||||
additionBank.OnCommit += (_, _) => updateAdditionBankState();
|
||||
|
||||
updateSampleSetState();
|
||||
|
||||
volume.Current.BindValueChanged(val =>
|
||||
{
|
||||
@@ -310,6 +328,58 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
togglesCollection.AddRange(createTernaryButtons());
|
||||
}
|
||||
|
||||
private Drawable createSampleSetContent()
|
||||
{
|
||||
if (beatmap.BeatmapSkin == null)
|
||||
return Empty();
|
||||
|
||||
var sampleSets = beatmap.BeatmapSkin.GetAvailableSampleSets().ToList();
|
||||
|
||||
if (sampleSets.Count == 0)
|
||||
return Empty();
|
||||
|
||||
sampleSets.Insert(0, new EditorBeatmapSkin.SampleSet(0, "User skin"));
|
||||
|
||||
if (sampleSets.Count < 20)
|
||||
{
|
||||
sampleSetsFlow = new FillFlowContainer<SampleSetTernaryButton>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(5),
|
||||
ChildrenEnumerable = sampleSets.Select(set => new SampleSetTernaryButton(set) { Description = set.Name }),
|
||||
};
|
||||
|
||||
foreach (var ternary in sampleSetsFlow)
|
||||
{
|
||||
ternary.Current.BindValueChanged(val =>
|
||||
{
|
||||
if (val.NewValue == TernaryState.True)
|
||||
setSampleSet(ternary.SampleSet);
|
||||
|
||||
updateSampleSetState();
|
||||
playDemoSample();
|
||||
});
|
||||
}
|
||||
|
||||
return sampleSetsFlow;
|
||||
}
|
||||
|
||||
sampleSetDropdown = new LabelledDropdown<EditorBeatmapSkin.SampleSet>(padded: false)
|
||||
{
|
||||
Label = "Sample Set",
|
||||
Items = sampleSets,
|
||||
};
|
||||
sampleSetDropdown.Current.BindValueChanged(val =>
|
||||
{
|
||||
setSampleSet(val.NewValue);
|
||||
updateSampleSetState();
|
||||
playDemoSample();
|
||||
});
|
||||
|
||||
return sampleSetDropdown;
|
||||
}
|
||||
|
||||
private string? getCommonBank() => allRelevantSamples.Select(h => GetBankValue(h.samples)).Distinct().Count() == 1
|
||||
? GetBankValue(allRelevantSamples.First().samples)
|
||||
: null;
|
||||
@@ -327,15 +397,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
private void updatePrimaryBankState()
|
||||
{
|
||||
string? commonBank = getCommonBank();
|
||||
bank.Current.Value = commonBank;
|
||||
bank.PlaceholderText = string.IsNullOrEmpty(commonBank) ? "(multiple)" : string.Empty;
|
||||
bank.Current.Value = !string.IsNullOrEmpty(commonBank) ? commonBank : "(multiple)";
|
||||
}
|
||||
|
||||
private void updateAdditionBankState()
|
||||
{
|
||||
string? commonAdditionBank = getCommonAdditionBank();
|
||||
additionBank.PlaceholderText = string.IsNullOrEmpty(commonAdditionBank) ? "(multiple)" : string.Empty;
|
||||
additionBank.Current.Value = commonAdditionBank;
|
||||
additionBank.Current.Value = !string.IsNullOrEmpty(commonAdditionBank) ? commonAdditionBank : "(multiple)";
|
||||
|
||||
bool anyAdditions = allRelevantSamples.Any(o => o.samples.Any(s => s.Name != HitSampleInfo.HIT_NORMAL));
|
||||
if (anyAdditions)
|
||||
@@ -344,6 +412,40 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
additionBank.Hide();
|
||||
}
|
||||
|
||||
private void updateSampleSetState()
|
||||
{
|
||||
HashSet<int> activeSets = new HashSet<int>();
|
||||
|
||||
foreach (var sample in allRelevantSamples.SelectMany(h => h.samples))
|
||||
{
|
||||
if (sample.Suffix == null)
|
||||
activeSets.Add(sample.UseBeatmapSamples ? 1 : 0);
|
||||
else if (int.TryParse(sample.Suffix, out int suffix))
|
||||
activeSets.Add(suffix);
|
||||
}
|
||||
|
||||
if (sampleSetsFlow != null)
|
||||
{
|
||||
var onState = activeSets.Count > 1 ? TernaryState.Indeterminate : TernaryState.True;
|
||||
|
||||
foreach (var ternary in sampleSetsFlow)
|
||||
ternary.Current.Value = activeSets.Contains(ternary.SampleSet.SampleSetIndex) ? onState : TernaryState.False;
|
||||
}
|
||||
|
||||
if (sampleSetDropdown != null)
|
||||
{
|
||||
sampleSetDropdown.Current.Value = activeSets.Count == 1
|
||||
? sampleSetDropdown.Items.Single(i => i.SampleSetIndex == activeSets.Single())
|
||||
: new EditorBeatmapSkin.SampleSet(-1, "(multiple)");
|
||||
}
|
||||
}
|
||||
|
||||
private void playDemoSample() => Scheduler.AddOnce(() =>
|
||||
{
|
||||
demoSample.Samples = allRelevantSamples.First().samples.Cast<ISampleInfo>().ToArray();
|
||||
demoSample.Play();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Applies the given update action on all samples of <see cref="allRelevantSamples"/>
|
||||
/// and invokes the necessary update notifiers for the beatmap and hit objects.
|
||||
@@ -397,6 +499,19 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
});
|
||||
}
|
||||
|
||||
private void setSampleSet(EditorBeatmapSkin.SampleSet newSampleSet)
|
||||
{
|
||||
updateAllRelevantSamples((_, relevantSamples) =>
|
||||
{
|
||||
for (int i = 0; i < relevantSamples.Count; i++)
|
||||
{
|
||||
relevantSamples[i] = relevantSamples[i].With(
|
||||
newSuffix: newSampleSet.SampleSetIndex >= 2 ? newSampleSet.SampleSetIndex.ToString() : null,
|
||||
newUseBeatmapSamples: newSampleSet.SampleSetIndex >= 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setVolume(int newVolume)
|
||||
{
|
||||
updateAllRelevantSamples((_, relevantSamples) =>
|
||||
@@ -435,6 +550,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
addHitSample(sampleName);
|
||||
break;
|
||||
}
|
||||
|
||||
playDemoSample();
|
||||
};
|
||||
|
||||
selectionSampleStates[sampleName] = bindable;
|
||||
@@ -455,7 +572,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
foreach ((string sampleName, var bindable) in selectionSampleStates)
|
||||
{
|
||||
yield return new DrawableTernaryButton
|
||||
yield return new DrawableTernaryButton(null)
|
||||
{
|
||||
Current = bindable,
|
||||
Description = string.Empty,
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -61,6 +63,46 @@ namespace osu.Game.Screens.Edit
|
||||
invokeSkinChanged();
|
||||
}
|
||||
|
||||
public record SampleSet(int SampleSetIndex, string Name)
|
||||
{
|
||||
public SampleSet(int sampleSetIndex)
|
||||
: this(sampleSetIndex, $@"Custom #{sampleSetIndex}")
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
|
||||
public IEnumerable<SampleSet> GetAvailableSampleSets()
|
||||
{
|
||||
string[] possibleSounds = HitSampleInfo.ALL_ADDITIONS.Prepend(HitSampleInfo.HIT_NORMAL).ToArray();
|
||||
string[] possibleBanks = HitSampleInfo.ALL_BANKS;
|
||||
|
||||
string[] possiblePrefixes = possibleSounds.SelectMany(sound => possibleBanks.Select(bank => $@"{bank}-{sound}")).ToArray();
|
||||
|
||||
HashSet<int> indices = new HashSet<int>();
|
||||
|
||||
if (Skin.Samples != null)
|
||||
{
|
||||
foreach (string sample in Skin.Samples.GetAvailableResources())
|
||||
{
|
||||
foreach (string possiblePrefix in possiblePrefixes)
|
||||
{
|
||||
if (!sample.StartsWith(possiblePrefix, StringComparison.InvariantCultureIgnoreCase))
|
||||
continue;
|
||||
|
||||
string indexString = Path.GetFileNameWithoutExtension(sample)[possiblePrefix.Length..];
|
||||
if (string.IsNullOrEmpty(indexString))
|
||||
indices.Add(1);
|
||||
if (int.TryParse(indexString, out int index))
|
||||
indices.Add(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return indices.OrderBy(i => i).Select(i => new SampleSet(i));
|
||||
}
|
||||
|
||||
#region Delegated ISkin implementation
|
||||
|
||||
public Drawable? GetDrawableComponent(ISkinComponentLookup lookup) => Skin.GetDrawableComponent(lookup);
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// A sample store which can be used to perform user file lookups for this skin.
|
||||
/// </summary>
|
||||
protected ISampleStore? Samples { get; }
|
||||
protected internal ISampleStore? Samples { get; }
|
||||
|
||||
public readonly Live<SkinInfo> SkinInfo;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user