diff --git a/osu.Android.props b/osu.Android.props
index d701aaf199..dc3e14c141 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index 92ae7e0fd3..3a6eaa83db 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -36,35 +36,64 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private bool pressHandledThisFrame;
- private Bindable type;
+ private readonly Bindable type;
public DrawableHit(Hit hit)
: base(hit)
{
+ type = HitObject.TypeBindable.GetBoundCopy();
FillMode = FillMode.Fit;
+
+ updateActionsFromType();
}
[BackgroundDependencyLoader]
private void load()
{
- type = HitObject.TypeBindable.GetBoundCopy();
type.BindValueChanged(_ =>
{
- updateType();
+ updateActionsFromType();
+
+ // will overwrite samples, should only be called on change.
+ updateSamplesFromTypeChange();
+
RecreatePieces();
});
-
- updateType();
}
- private void updateType()
+ private HitSampleInfo[] getRimSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray();
+
+ protected override void LoadSamples()
+ {
+ base.LoadSamples();
+
+ type.Value = getRimSamples().Any() ? HitType.Rim : HitType.Centre;
+ }
+
+ private void updateSamplesFromTypeChange()
+ {
+ var rimSamples = getRimSamples();
+
+ bool isRimType = HitObject.Type == HitType.Rim;
+
+ if (isRimType != rimSamples.Any())
+ {
+ if (isRimType)
+ HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_CLAP });
+ else
+ {
+ foreach (var sample in rimSamples)
+ HitObject.Samples.Remove(sample);
+ }
+ }
+ }
+
+ private void updateActionsFromType()
{
HitActions =
HitObject.Type == HitType.Centre
? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre }
: new[] { TaikoAction.LeftRim, TaikoAction.RightRim };
-
- RecreatePieces();
}
protected override SkinnableDrawable CreateMainPiece() => HitObject.Type == HitType.Centre
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 929cf8a937..9cd23383c4 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -1,19 +1,19 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Framework.Graphics;
-using osu.Framework.Input.Bindings;
-using osu.Game.Rulesets.Objects.Drawables;
-using osuTK;
-using System.Linq;
-using osu.Game.Audio;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
+using osu.Framework.Input.Bindings;
+using osu.Game.Audio;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
+using osuTK;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected Vector2 BaseSize;
protected SkinnableDrawable MainPiece;
- private Bindable isStrong;
+ private readonly Bindable isStrong;
private readonly Container strongHitContainer;
@@ -128,6 +128,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
: base(hitObject)
{
HitObject = hitObject;
+ isStrong = HitObject.IsStrongBindable.GetBoundCopy();
Anchor = Anchor.CentreLeft;
Origin = Anchor.Custom;
@@ -140,8 +141,40 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
- isStrong = HitObject.IsStrongBindable.GetBoundCopy();
- isStrong.BindValueChanged(_ => RecreatePieces(), true);
+ isStrong.BindValueChanged(_ =>
+ {
+ // will overwrite samples, should only be called on change.
+ updateSamplesFromStrong();
+
+ RecreatePieces();
+ });
+
+ RecreatePieces();
+ }
+
+ private HitSampleInfo[] getStrongSamples() => HitObject.Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToArray();
+
+ protected override void LoadSamples()
+ {
+ base.LoadSamples();
+
+ isStrong.Value = getStrongSamples().Any();
+ }
+
+ private void updateSamplesFromStrong()
+ {
+ var strongSamples = getStrongSamples();
+
+ if (isStrong.Value != strongSamples.Any())
+ {
+ if (isStrong.Value)
+ HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH });
+ else
+ {
+ foreach (var sample in strongSamples)
+ HitObject.Samples.Remove(sample);
+ }
+ }
}
protected virtual void RecreatePieces()
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs
index 0cd0f13b5f..082d85603e 100644
--- a/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs
+++ b/osu.Game.Tests/Visual/Settings/TestSceneDirectorySelector.cs
@@ -3,7 +3,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Framework.Platform;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Tests.Visual.Settings
@@ -11,7 +10,7 @@ namespace osu.Game.Tests.Visual.Settings
public class TestSceneDirectorySelector : OsuTestScene
{
[BackgroundDependencyLoader]
- private void load(GameHost host)
+ private void load()
{
Add(new DirectorySelector { RelativeSizeAxes = Axes.Both });
}
diff --git a/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs b/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs
new file mode 100644
index 0000000000..311e4c3362
--- /dev/null
+++ b/osu.Game.Tests/Visual/Settings/TestSceneFileSelector.cs
@@ -0,0 +1,24 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Graphics.UserInterfaceV2;
+
+namespace osu.Game.Tests.Visual.Settings
+{
+ public class TestSceneFileSelector : OsuTestScene
+ {
+ [Test]
+ public void TestAllFiles()
+ {
+ AddStep("create", () => Child = new FileSelector { RelativeSizeAxes = Axes.Both });
+ }
+
+ [Test]
+ public void TestJpgFilesOnly()
+ {
+ AddStep("create", () => Child = new FileSelector(validFileExtensions: new[] { ".jpg" }) { RelativeSizeAxes = Axes.Both });
+ }
+ }
+}
diff --git a/osu.Game.Tournament/Screens/StablePathSelectScreen.cs b/osu.Game.Tournament/Screens/StablePathSelectScreen.cs
index b4d56f60c7..717b43f704 100644
--- a/osu.Game.Tournament/Screens/StablePathSelectScreen.cs
+++ b/osu.Game.Tournament/Screens/StablePathSelectScreen.cs
@@ -129,7 +129,7 @@ namespace osu.Game.Tournament.Screens
protected virtual void ChangePath()
{
- var target = directorySelector.CurrentDirectory.Value.FullName;
+ var target = directorySelector.CurrentPath.Value.FullName;
var fileBasedIpc = ipc as FileBasedIPC;
Logger.Log($"Changing Stable CE location to {target}");
diff --git a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs
index ae34281bfb..a1cd074619 100644
--- a/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/DirectorySelector.cs
@@ -28,11 +28,11 @@ namespace osu.Game.Graphics.UserInterfaceV2
private GameHost host { get; set; }
[Cached]
- public readonly Bindable CurrentDirectory = new Bindable();
+ public readonly Bindable CurrentPath = new Bindable();
public DirectorySelector(string initialPath = null)
{
- CurrentDirectory.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
+ CurrentPath.Value = new DirectoryInfo(initialPath ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
}
[BackgroundDependencyLoader]
@@ -74,7 +74,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
}
};
- CurrentDirectory.BindValueChanged(updateDisplay, true);
+ CurrentPath.BindValueChanged(updateDisplay, true);
}
private void updateDisplay(ValueChangedEvent directory)
@@ -92,22 +92,27 @@ namespace osu.Game.Graphics.UserInterfaceV2
}
else
{
- directoryFlow.Add(new ParentDirectoryPiece(CurrentDirectory.Value.Parent));
+ directoryFlow.Add(new ParentDirectoryPiece(CurrentPath.Value.Parent));
- foreach (var dir in CurrentDirectory.Value.GetDirectories().OrderBy(d => d.Name))
- {
- if ((dir.Attributes & FileAttributes.Hidden) == 0)
- directoryFlow.Add(new DirectoryPiece(dir));
- }
+ directoryFlow.AddRange(GetEntriesForPath(CurrentPath.Value));
}
}
catch (Exception)
{
- CurrentDirectory.Value = directory.OldValue;
+ CurrentPath.Value = directory.OldValue;
this.FlashColour(Color4.Red, 300);
}
}
+ protected virtual IEnumerable GetEntriesForPath(DirectoryInfo path)
+ {
+ foreach (var dir in path.GetDirectories().OrderBy(d => d.Name))
+ {
+ if ((dir.Attributes & FileAttributes.Hidden) == 0)
+ yield return new DirectoryPiece(dir);
+ }
+ }
+
private class CurrentDirectoryDisplay : CompositeDrawable
{
[Resolved]
@@ -126,7 +131,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Spacing = new Vector2(5),
- Height = DirectoryPiece.HEIGHT,
+ Height = DisplayPiece.HEIGHT,
Direction = FillDirection.Horizontal,
},
};
@@ -150,7 +155,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
flow.ChildrenEnumerable = new Drawable[]
{
- new OsuSpriteText { Text = "Current Directory: ", Font = OsuFont.Default.With(size: DirectoryPiece.HEIGHT), },
+ new OsuSpriteText { Text = "Current Directory: ", Font = OsuFont.Default.With(size: DisplayPiece.HEIGHT), },
new ComputerPiece(),
}.Concat(pathPieces);
}
@@ -198,24 +203,44 @@ namespace osu.Game.Graphics.UserInterfaceV2
}
}
- private class DirectoryPiece : CompositeDrawable
+ protected class DirectoryPiece : DisplayPiece
{
- public const float HEIGHT = 20;
-
- protected const float FONT_SIZE = 16;
-
protected readonly DirectoryInfo Directory;
- private readonly string displayName;
-
- protected FillFlowContainer Flow;
-
[Resolved]
private Bindable currentDirectory { get; set; }
public DirectoryPiece(DirectoryInfo directory, string displayName = null)
+ : base(displayName)
{
Directory = directory;
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ currentDirectory.Value = Directory;
+ return true;
+ }
+
+ protected override string FallbackName => Directory.Name;
+
+ protected override IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar)
+ ? FontAwesome.Solid.Database
+ : FontAwesome.Regular.Folder;
+ }
+
+ protected abstract class DisplayPiece : CompositeDrawable
+ {
+ public const float HEIGHT = 20;
+
+ protected const float FONT_SIZE = 16;
+
+ private readonly string displayName;
+
+ protected FillFlowContainer Flow;
+
+ protected DisplayPiece(string displayName = null)
+ {
this.displayName = displayName;
}
@@ -259,20 +284,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
- Text = displayName ?? Directory.Name,
+ Text = displayName ?? FallbackName,
Font = OsuFont.Default.With(size: FONT_SIZE)
});
}
- protected override bool OnClick(ClickEvent e)
- {
- currentDirectory.Value = Directory;
- return true;
- }
+ protected abstract string FallbackName { get; }
- protected virtual IconUsage? Icon => Directory.Name.Contains(Path.DirectorySeparatorChar)
- ? FontAwesome.Solid.Database
- : FontAwesome.Regular.Folder;
+ protected abstract IconUsage? Icon { get; }
}
}
}
diff --git a/osu.Game/Graphics/UserInterfaceV2/FileSelector.cs b/osu.Game/Graphics/UserInterfaceV2/FileSelector.cs
new file mode 100644
index 0000000000..e10b8f7033
--- /dev/null
+++ b/osu.Game/Graphics/UserInterfaceV2/FileSelector.cs
@@ -0,0 +1,94 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Input.Events;
+
+namespace osu.Game.Graphics.UserInterfaceV2
+{
+ public class FileSelector : DirectorySelector
+ {
+ private readonly string[] validFileExtensions;
+
+ [Cached]
+ public readonly Bindable CurrentFile = new Bindable();
+
+ public FileSelector(string initialPath = null, string[] validFileExtensions = null)
+ : base(initialPath)
+ {
+ this.validFileExtensions = validFileExtensions ?? Array.Empty();
+ }
+
+ protected override IEnumerable GetEntriesForPath(DirectoryInfo path)
+ {
+ foreach (var dir in base.GetEntriesForPath(path))
+ yield return dir;
+
+ IEnumerable files = path.GetFiles();
+
+ if (validFileExtensions.Length > 0)
+ files = files.Where(f => validFileExtensions.Contains(f.Extension));
+
+ foreach (var file in files.OrderBy(d => d.Name))
+ {
+ if ((file.Attributes & FileAttributes.Hidden) == 0)
+ yield return new FilePiece(file);
+ }
+ }
+
+ protected class FilePiece : DisplayPiece
+ {
+ private readonly FileInfo file;
+
+ [Resolved]
+ private Bindable currentFile { get; set; }
+
+ public FilePiece(FileInfo file)
+ {
+ this.file = file;
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ currentFile.Value = file;
+ return true;
+ }
+
+ protected override string FallbackName => file.Name;
+
+ protected override IconUsage? Icon
+ {
+ get
+ {
+ switch (file.Extension)
+ {
+ case ".ogg":
+ case ".mp3":
+ case ".wav":
+ return FontAwesome.Regular.FileAudio;
+
+ case ".jpg":
+ case ".jpeg":
+ case ".png":
+ return FontAwesome.Regular.FileImage;
+
+ case ".mp4":
+ case ".avi":
+ case ".mov":
+ case ".flv":
+ return FontAwesome.Regular.FileVideo;
+
+ default:
+ return FontAwesome.Regular.File;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
index 290aba3468..902c23c3c6 100644
--- a/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
+++ b/osu.Game/Graphics/UserInterfaceV2/LabelledTextBox.cs
@@ -46,6 +46,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
protected override OsuTextBox CreateComponent() => new OsuTextBox
{
+ CommitOnFocusLost = true,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs
index 79d842a617..ad540e3691 100644
--- a/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs
+++ b/osu.Game/Overlays/Settings/Sections/Maintenance/MigrationSelectScreen.cs
@@ -106,7 +106,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
private void start()
{
- var target = directorySelector.CurrentDirectory.Value;
+ var target = directorySelector.CurrentPath.Value;
try
{
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index 3ad2394420..b9b7c1ef54 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -208,6 +208,9 @@ namespace osu.Game.Rulesets.Edit
protected override bool OnKeyDown(KeyDownEvent e)
{
+ if (e.ControlPressed || e.AltPressed || e.SuperPressed)
+ return false;
+
if (checkLeftToggleFromKey(e.Key, out var leftIndex))
{
var item = toolboxCollection.Items.ElementAtOrDefault(leftIndex);
@@ -221,7 +224,7 @@ namespace osu.Game.Rulesets.Edit
if (checkRightToggleFromKey(e.Key, out var rightIndex))
{
- var item = togglesCollection.Children[rightIndex];
+ var item = togglesCollection.ElementAtOrDefault(rightIndex);
if (item is DrawableTernaryButton button)
{
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 581617b567..7c05bc9aa7 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -157,6 +157,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
updateState(ArmedState.Idle, true);
}
+ ///
+ /// Invoked by the base to populate samples, once on initial load and potentially again on any change to the samples collection.
+ ///
protected virtual void LoadSamples()
{
if (Samples != null)
@@ -183,6 +186,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
private void onDefaultsApplied(HitObject hitObject)
{
apply(hitObject);
+ updateState(state.Value, true);
DefaultsApplied?.Invoke(this);
}
diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
index 8934a1b6d3..8908520cd7 100644
--- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
+++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
public Container SelectionBlueprints { get; private set; }
- protected SelectionHandler SelectionHandler;
+ protected SelectionHandler SelectionHandler { get; private set; }
[Resolved(CanBeNull = true)]
private IEditorChangeHandler changeHandler { get; set; }
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 71826e161c..6412f707d0 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -24,7 +24,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 90aa903318..f1e13169a5 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -80,7 +80,7 @@
-
+