From 767a2a10bd0f0798eb5b313068c5b43b3f962160 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 13:56:38 +0900 Subject: [PATCH 1/8] Fix incorrect sliderendcircle fallback logic Correctly handle the case where a skin has "sliderendcircle.png" but not "sliderendcircleoverlay.png". --- .../Skinning/LegacyMainCirclePiece.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index f051cbfa3b..d556ecb9bc 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -49,6 +49,25 @@ namespace osu.Game.Rulesets.Osu.Skinning { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; + Texture baseTexture; + Texture overlayTexture; + bool allowFallback = false; + + // attempt lookup using priority specification + baseTexture = getTextureWithFallback(string.Empty); + + // if the base texture was not found without a fallback, switch on fallback mode and re-perform the lookup. + if (baseTexture == null) + { + allowFallback = true; + baseTexture = getTextureWithFallback(string.Empty); + } + + // at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it. + // the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. + // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin). + overlayTexture = getTextureWithFallback("overlay"); + InternalChildren = new Drawable[] { circleSprites = new Container @@ -60,13 +79,13 @@ namespace osu.Game.Rulesets.Osu.Skinning { hitCircleSprite = new Sprite { - Texture = getTextureWithFallback(string.Empty), + Texture = baseTexture, Anchor = Anchor.Centre, Origin = Anchor.Centre, }, hitCircleOverlay = new Sprite { - Texture = getTextureWithFallback("overlay"), + Texture = overlayTexture, Anchor = Anchor.Centre, Origin = Anchor.Centre, } @@ -101,8 +120,13 @@ namespace osu.Game.Rulesets.Osu.Skinning Texture tex = null; if (!string.IsNullOrEmpty(priorityLookup)) + { tex = skin.GetTexture($"{priorityLookup}{name}"); + if (!allowFallback) + return tex; + } + return tex ?? skin.GetTexture($"hitcircle{name}"); } } From ed982e8dd13f9f4657e1463a7bd19a7aac2f41f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 14:08:55 +0900 Subject: [PATCH 2/8] Make stacked hitcircles more visible when using default skin --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index bcf64b81a6..619fea73bc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Origin = Anchor.Centre; Masking = true; - BorderThickness = 10; + BorderThickness = 9; // roughly matches slider borders and makes stacked circles distinctly visible from each other. BorderColour = Color4.White; Child = new Box From 048507478ee199fc155be842a5d008a05dcf1869 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 14:12:46 +0900 Subject: [PATCH 3/8] Join declaration and specification --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index d556ecb9bc..382d6e53cc 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -49,12 +49,10 @@ namespace osu.Game.Rulesets.Osu.Skinning { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; - Texture baseTexture; - Texture overlayTexture; bool allowFallback = false; // attempt lookup using priority specification - baseTexture = getTextureWithFallback(string.Empty); + Texture baseTexture = getTextureWithFallback(string.Empty); // if the base texture was not found without a fallback, switch on fallback mode and re-perform the lookup. if (baseTexture == null) @@ -66,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Skinning // at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it. // the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist. // expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin). - overlayTexture = getTextureWithFallback("overlay"); + Texture overlayTexture = getTextureWithFallback("overlay"); InternalChildren = new Drawable[] { From 98fe5f78ee956d3765324a1d8482fb1945a63673 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 15:17:15 +0900 Subject: [PATCH 4/8] Split setup screen up into sections (and use a SectionContainer) --- .../Editing/TestSceneEditorBeatmapCreation.cs | 2 +- .../Edit/Setup/FileChooserLabelledTextBox.cs | 73 ++++ .../Screens/Edit/Setup/MetadataSection.cs | 71 ++++ .../Screens/Edit/Setup/ResourcesSection.cs | 211 ++++++++++++ osu.Game/Screens/Edit/Setup/SetupScreen.cs | 319 +----------------- osu.Game/Screens/Edit/Setup/SetupSection.cs | 43 +++ 6 files changed, 417 insertions(+), 302 deletions(-) create mode 100644 osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs create mode 100644 osu.Game/Screens/Edit/Setup/MetadataSection.cs create mode 100644 osu.Game/Screens/Edit/Setup/ResourcesSection.cs create mode 100644 osu.Game/Screens/Edit/Setup/SetupSection.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 13a3195824..7584c74c71 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Editing using (var zip = ZipArchive.Open(temp)) zip.WriteToDirectory(extractedFolder); - bool success = setup.ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")); + bool success = setup.ChildrenOfType().First().ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3")); File.Delete(temp); Directory.Delete(extractedFolder, true); diff --git a/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs new file mode 100644 index 0000000000..b802b3405a --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs @@ -0,0 +1,73 @@ +// 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.IO; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Screens.Edit.Setup +{ + /// + /// A labelled textbox which reveals an inline file chooser when clicked. + /// + internal class FileChooserLabelledTextBox : LabelledTextBox + { + public Container Target; + + private readonly IBindable currentFile = new Bindable(); + + public FileChooserLabelledTextBox() + { + currentFile.BindValueChanged(onFileSelected); + } + + private void onFileSelected(ValueChangedEvent file) + { + if (file.NewValue == null) + return; + + Target.Clear(); + Current.Value = file.NewValue.FullName; + } + + protected override OsuTextBox CreateTextBox() => + new FileChooserOsuTextBox + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + CornerRadius = CORNER_RADIUS, + OnFocused = DisplayFileChooser + }; + + public void DisplayFileChooser() + { + Target.Child = new FileSelector(validFileExtensions: ResourcesSection.AudioExtensions) + { + RelativeSizeAxes = Axes.X, + Height = 400, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + CurrentFile = { BindTarget = currentFile } + }; + } + + internal class FileChooserOsuTextBox : OsuTextBox + { + public Action OnFocused; + + protected override void OnFocus(FocusEvent e) + { + OnFocused?.Invoke(); + base.OnFocus(e); + + GetContainingInputManager().TriggerFocusContention(this); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs new file mode 100644 index 0000000000..31a2c2ce1a --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -0,0 +1,71 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; + +namespace osu.Game.Screens.Edit.Setup +{ + internal class MetadataSection : SetupSection + { + private LabelledTextBox artistTextBox; + private LabelledTextBox titleTextBox; + private LabelledTextBox creatorTextBox; + private LabelledTextBox difficultyTextBox; + + [BackgroundDependencyLoader] + private void load() + { + Flow.Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Beatmap metadata" + }, + artistTextBox = new LabelledTextBox + { + Label = "Artist", + Current = { Value = Beatmap.Value.Metadata.Artist }, + TabbableContentContainer = this + }, + titleTextBox = new LabelledTextBox + { + Label = "Title", + Current = { Value = Beatmap.Value.Metadata.Title }, + TabbableContentContainer = this + }, + creatorTextBox = new LabelledTextBox + { + Label = "Creator", + Current = { Value = Beatmap.Value.Metadata.AuthorString }, + TabbableContentContainer = this + }, + difficultyTextBox = new LabelledTextBox + { + Label = "Difficulty Name", + Current = { Value = Beatmap.Value.BeatmapInfo.Version }, + TabbableContentContainer = this + }, + }; + + foreach (var item in Flow.OfType()) + item.OnCommit += onCommit; + } + + private void onCommit(TextBox sender, bool newText) + { + if (!newText) return; + + // for now, update these on commit rather than making BeatmapMetadata bindables. + // after switching database engines we can reconsider if switching to bindables is a good direction. + Beatmap.Value.Metadata.Artist = artistTextBox.Current.Value; + Beatmap.Value.Metadata.Title = titleTextBox.Current.Value; + Beatmap.Value.Metadata.AuthorString = creatorTextBox.Current.Value; + Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value; + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs new file mode 100644 index 0000000000..86d7968856 --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -0,0 +1,211 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; + +namespace osu.Game.Screens.Edit.Setup +{ + internal class ResourcesSection : SetupSection, ICanAcceptFiles + { + private LabelledTextBox audioTrackTextBox; + private Container backgroundSpriteContainer; + + public IEnumerable HandledExtensions => ImageExtensions.Concat(AudioExtensions); + + public static string[] ImageExtensions { get; } = { ".jpg", ".jpeg", ".png" }; + + public static string[] AudioExtensions { get; } = { ".mp3", ".ogg" }; + + [Resolved] + private OsuGameBase game { get; set; } + + [Resolved] + private MusicController music { get; set; } + + [Resolved] + private BeatmapManager beatmaps { get; set; } + + [Resolved(canBeNull: true)] + private Editor editor { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + Container audioTrackFileChooserContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }; + + Flow.Children = new Drawable[] + { + backgroundSpriteContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = 250, + Masking = true, + CornerRadius = 10, + }, + new OsuSpriteText + { + Text = "Resources" + }, + audioTrackTextBox = new FileChooserLabelledTextBox + { + Label = "Audio Track", + Current = { Value = Beatmap.Value.Metadata.AudioFile ?? "Click to select a track" }, + Target = audioTrackFileChooserContainer, + TabbableContentContainer = this + }, + audioTrackFileChooserContainer, + }; + + updateBackgroundSprite(); + + audioTrackTextBox.Current.BindValueChanged(audioTrackChanged); + } + + Task ICanAcceptFiles.Import(params string[] paths) + { + Schedule(() => + { + var firstFile = new FileInfo(paths.First()); + + if (ImageExtensions.Contains(firstFile.Extension)) + { + ChangeBackgroundImage(firstFile.FullName); + } + else if (AudioExtensions.Contains(firstFile.Extension)) + { + audioTrackTextBox.Text = firstFile.FullName; + } + }); + return Task.CompletedTask; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + game.RegisterImportHandler(this); + } + + public bool ChangeBackgroundImage(string path) + { + var info = new FileInfo(path); + + if (!info.Exists) + return false; + + var set = Beatmap.Value.BeatmapSetInfo; + + // remove the previous background for now. + // in the future we probably want to check if this is being used elsewhere (other difficulties?) + var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.BackgroundFile); + + using (var stream = info.OpenRead()) + { + if (oldFile != null) + beatmaps.ReplaceFile(set, oldFile, stream, info.Name); + else + beatmaps.AddFile(set, stream, info.Name); + } + + Beatmap.Value.Metadata.BackgroundFile = info.Name; + updateBackgroundSprite(); + + return true; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + game?.UnregisterImportHandler(this); + } + + public bool ChangeAudioTrack(string path) + { + var info = new FileInfo(path); + + if (!info.Exists) + return false; + + var set = Beatmap.Value.BeatmapSetInfo; + + // remove the previous audio track for now. + // in the future we probably want to check if this is being used elsewhere (other difficulties?) + var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.AudioFile); + + using (var stream = info.OpenRead()) + { + if (oldFile != null) + beatmaps.ReplaceFile(set, oldFile, stream, info.Name); + else + beatmaps.AddFile(set, stream, info.Name); + } + + Beatmap.Value.Metadata.AudioFile = info.Name; + + music.ReloadCurrentTrack(); + + editor?.UpdateClockSource(); + return true; + } + + private void audioTrackChanged(ValueChangedEvent filePath) + { + if (!ChangeAudioTrack(filePath.NewValue)) + audioTrackTextBox.Current.Value = filePath.OldValue; + } + + private void updateBackgroundSprite() + { + LoadComponentAsync(new BeatmapBackgroundSprite(Beatmap.Value) + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + }, background => + { + if (background.Texture != null) + backgroundSpriteContainer.Child = background; + else + { + backgroundSpriteContainer.Children = new Drawable[] + { + new Box + { + Colour = Colours.GreySeafoamDarker, + RelativeSizeAxes = Axes.Both, + }, + new OsuTextFlowContainer(t => t.Font = OsuFont.Default.With(size: 24)) + { + Text = "Drag image here to set beatmap background!", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.X, + } + }; + } + + background.FadeInFromZero(500); + }); + } + } +} diff --git a/osu.Game/Screens/Edit/Setup/SetupScreen.cs b/osu.Game/Screens/Edit/Setup/SetupScreen.cs index 1e9ebec41d..cd4f6733c0 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreen.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreen.cs @@ -1,76 +1,33 @@ // 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 System.Threading.Tasks; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Drawables; -using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Overlays; -using osuTK; namespace osu.Game.Screens.Edit.Setup { - public class SetupScreen : EditorScreen, ICanAcceptFiles + public class SetupScreen : EditorScreen { - public IEnumerable HandledExtensions => ImageExtensions.Concat(AudioExtensions); - - public static string[] ImageExtensions { get; } = { ".jpg", ".jpeg", ".png" }; - - public static string[] AudioExtensions { get; } = { ".mp3", ".ogg" }; - - private FillFlowContainer flow; - private LabelledTextBox artistTextBox; - private LabelledTextBox titleTextBox; - private LabelledTextBox creatorTextBox; - private LabelledTextBox difficultyTextBox; - private LabelledTextBox audioTrackTextBox; - private Container backgroundSpriteContainer; - [Resolved] - private OsuGameBase game { get; set; } + private OsuColour colours { get; set; } - [Resolved] - private MusicController music { get; set; } - - [Resolved] - private BeatmapManager beatmaps { get; set; } - - [Resolved(canBeNull: true)] - private Editor editor { get; set; } + [Cached] + protected readonly OverlayColourProvider ColourProvider; public SetupScreen() : base(EditorScreenMode.SongSetup) { + ColourProvider = new OverlayColourProvider(OverlayColourScheme.Green); } - [Resolved] - private OsuColour colours { get; set; } - [BackgroundDependencyLoader] private void load() { - Container audioTrackFileChooserContainer = new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }; - Child = new Container { RelativeSizeAxes = Axes.Both, @@ -87,273 +44,33 @@ namespace osu.Game.Screens.Edit.Setup Colour = colours.GreySeafoamDark, RelativeSizeAxes = Axes.Both, }, - new OsuScrollContainer + new SectionsContainer { + FixedHeader = new SetupScreenHeader(), RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(10), - Child = flow = new FillFlowContainer + Children = new SetupSection[] { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(20), - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - backgroundSpriteContainer = new Container - { - RelativeSizeAxes = Axes.X, - Height = 250, - Masking = true, - CornerRadius = 10, - }, - new OsuSpriteText - { - Text = "Resources" - }, - audioTrackTextBox = new FileChooserLabelledTextBox - { - Label = "Audio Track", - Current = { Value = Beatmap.Value.Metadata.AudioFile ?? "Click to select a track" }, - Target = audioTrackFileChooserContainer, - TabbableContentContainer = this - }, - audioTrackFileChooserContainer, - new OsuSpriteText - { - Text = "Beatmap metadata" - }, - artistTextBox = new LabelledTextBox - { - Label = "Artist", - Current = { Value = Beatmap.Value.Metadata.Artist }, - TabbableContentContainer = this - }, - titleTextBox = new LabelledTextBox - { - Label = "Title", - Current = { Value = Beatmap.Value.Metadata.Title }, - TabbableContentContainer = this - }, - creatorTextBox = new LabelledTextBox - { - Label = "Creator", - Current = { Value = Beatmap.Value.Metadata.AuthorString }, - TabbableContentContainer = this - }, - difficultyTextBox = new LabelledTextBox - { - Label = "Difficulty Name", - Current = { Value = Beatmap.Value.BeatmapInfo.Version }, - TabbableContentContainer = this - }, - } - }, + new ResourcesSection(), + new MetadataSection(), + } }, } } }; - - updateBackgroundSprite(); - - audioTrackTextBox.Current.BindValueChanged(audioTrackChanged); - - foreach (var item in flow.OfType()) - item.OnCommit += onCommit; - } - - Task ICanAcceptFiles.Import(params string[] paths) - { - Schedule(() => - { - var firstFile = new FileInfo(paths.First()); - - if (ImageExtensions.Contains(firstFile.Extension)) - { - ChangeBackgroundImage(firstFile.FullName); - } - else if (AudioExtensions.Contains(firstFile.Extension)) - { - audioTrackTextBox.Text = firstFile.FullName; - } - }); - - return Task.CompletedTask; - } - - private void updateBackgroundSprite() - { - LoadComponentAsync(new BeatmapBackgroundSprite(Beatmap.Value) - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - FillMode = FillMode.Fill, - }, background => - { - if (background.Texture != null) - backgroundSpriteContainer.Child = background; - else - { - backgroundSpriteContainer.Children = new Drawable[] - { - new Box - { - Colour = colours.GreySeafoamDarker, - RelativeSizeAxes = Axes.Both, - }, - new OsuTextFlowContainer(t => t.Font = OsuFont.Default.With(size: 24)) - { - Text = "Drag image here to set beatmap background!", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.X, - } - }; - } - - background.FadeInFromZero(500); - }); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - game.RegisterImportHandler(this); - } - - public bool ChangeBackgroundImage(string path) - { - var info = new FileInfo(path); - - if (!info.Exists) - return false; - - var set = Beatmap.Value.BeatmapSetInfo; - - // remove the previous background for now. - // in the future we probably want to check if this is being used elsewhere (other difficulties?) - var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.BackgroundFile); - - using (var stream = info.OpenRead()) - { - if (oldFile != null) - beatmaps.ReplaceFile(set, oldFile, stream, info.Name); - else - beatmaps.AddFile(set, stream, info.Name); - } - - Beatmap.Value.Metadata.BackgroundFile = info.Name; - updateBackgroundSprite(); - - return true; - } - - public bool ChangeAudioTrack(string path) - { - var info = new FileInfo(path); - - if (!info.Exists) - return false; - - var set = Beatmap.Value.BeatmapSetInfo; - - // remove the previous audio track for now. - // in the future we probably want to check if this is being used elsewhere (other difficulties?) - var oldFile = set.Files.FirstOrDefault(f => f.Filename == Beatmap.Value.Metadata.AudioFile); - - using (var stream = info.OpenRead()) - { - if (oldFile != null) - beatmaps.ReplaceFile(set, oldFile, stream, info.Name); - else - beatmaps.AddFile(set, stream, info.Name); - } - - Beatmap.Value.Metadata.AudioFile = info.Name; - - music.ReloadCurrentTrack(); - - editor?.UpdateClockSource(); - return true; - } - - private void audioTrackChanged(ValueChangedEvent filePath) - { - if (!ChangeAudioTrack(filePath.NewValue)) - audioTrackTextBox.Current.Value = filePath.OldValue; - } - - private void onCommit(TextBox sender, bool newText) - { - if (!newText) return; - - // for now, update these on commit rather than making BeatmapMetadata bindables. - // after switching database engines we can reconsider if switching to bindables is a good direction. - Beatmap.Value.Metadata.Artist = artistTextBox.Current.Value; - Beatmap.Value.Metadata.Title = titleTextBox.Current.Value; - Beatmap.Value.Metadata.AuthorString = creatorTextBox.Current.Value; - Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - game?.UnregisterImportHandler(this); } } - internal class FileChooserLabelledTextBox : LabelledTextBox + internal class SetupScreenHeader : OverlayHeader { - public Container Target; + protected override OverlayTitle CreateTitle() => new SetupScreenTitle(); - private readonly IBindable currentFile = new Bindable(); - - public FileChooserLabelledTextBox() + private class SetupScreenTitle : OverlayTitle { - currentFile.BindValueChanged(onFileSelected); - } - - private void onFileSelected(ValueChangedEvent file) - { - if (file.NewValue == null) - return; - - Target.Clear(); - Current.Value = file.NewValue.FullName; - } - - protected override OsuTextBox CreateTextBox() => - new FileChooserOsuTextBox + public SetupScreenTitle() { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - CornerRadius = CORNER_RADIUS, - OnFocused = DisplayFileChooser - }; - - public void DisplayFileChooser() - { - Target.Child = new FileSelector(validFileExtensions: SetupScreen.AudioExtensions) - { - RelativeSizeAxes = Axes.X, - Height = 400, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - CurrentFile = { BindTarget = currentFile } - }; - } - - internal class FileChooserOsuTextBox : OsuTextBox - { - public Action OnFocused; - - protected override void OnFocus(FocusEvent e) - { - OnFocused?.Invoke(); - base.OnFocus(e); - - GetContainingInputManager().TriggerFocusContention(this); + Title = "beatmap setup"; + Description = "change general settings of your beatmap"; + IconTexture = "Icons/Hexacons/social"; } } } diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs new file mode 100644 index 0000000000..54e383a4d8 --- /dev/null +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -0,0 +1,43 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osuTK; + +namespace osu.Game.Screens.Edit.Setup +{ + internal class SetupSection : Container + { + protected FillFlowContainer Flow; + + [Resolved] + protected OsuColour Colours { get; private set; } + + [Resolved] + protected IBindable Beatmap { get; private set; } + + public override void Add(Drawable drawable) => throw new InvalidOperationException("Use Flow.Add instead"); + + public SetupSection() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding(10); + + InternalChild = Flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(20), + Direction = FillDirection.Vertical, + }; + } + } +} From b1a64f89d78603ef86157940ec582127698f1707 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 17:49:12 +0900 Subject: [PATCH 5/8] Increase backwards seek magnitude when the track is running This matches osu-stable. When the track is running, seeking backwards (against the flow) is harder than seeking forwards. Adding a mutliplier makes it feel much better. Note that this is additive not multiplicative because for larger seeks the (where `amount` > 1) we don't want to jump an insanely huge amount - just offset the seek slightly to account for playing audio. --- osu.Game/Screens/Edit/EditorClock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/EditorClock.cs b/osu.Game/Screens/Edit/EditorClock.cs index 4b7cd82637..64ed34f5ec 100644 --- a/osu.Game/Screens/Edit/EditorClock.cs +++ b/osu.Game/Screens/Edit/EditorClock.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Edit /// /// Whether to snap to the closest beat after seeking. /// The relative amount (magnitude) which should be seeked. - public void SeekBackward(bool snapped = false, double amount = 1) => seek(-1, snapped, amount); + public void SeekBackward(bool snapped = false, double amount = 1) => seek(-1, snapped, amount + (IsRunning ? 1.5 : 0)); /// /// Seeks forwards by one beat length. From e64cee10b8fa82ca05e2d365c760924a75fd3cfe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 19:07:31 +0900 Subject: [PATCH 6/8] Add obsoleted Bindable property back to SettingsItem for compatibility --- osu.Game/Overlays/Settings/SettingsItem.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index ad6aaafd9d..278479e04f 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -54,6 +54,13 @@ namespace osu.Game.Overlays.Settings } } + [Obsolete("Use Current instead")] // Can be removed 20210406 + public Bindable Bindable + { + get => Current; + set => Current = value; + } + public virtual Bindable Current { get => controlWithCurrent.Current; From a2796d2c017bce185aaca88e3b581d56599888b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 19:20:53 +0900 Subject: [PATCH 7/8] Add repeats display to timeline blueprints --- .../Timeline/TimelineHitObjectBlueprint.cs | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index bc2ccfc605..f0757a3dda 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -199,7 +199,40 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline base.Update(); // no bindable so we perform this every update - Width = (float)(HitObject.GetEndTime() - HitObject.StartTime); + float duration = (float)(HitObject.GetEndTime() - HitObject.StartTime); + + if (Width != duration) + { + Width = duration; + + // kind of haphazard but yeah, no bindables. + if (HitObject is IHasRepeats repeats) + updateRepeats(repeats); + } + } + + private Container repeatsContainer; + + private void updateRepeats(IHasRepeats repeats) + { + repeatsContainer?.Expire(); + + mainComponents.Add(repeatsContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }); + + for (int i = 0; i < repeats.RepeatCount; i++) + { + repeatsContainer.Add(new Circle + { + Size = new Vector2(circle_size / 2), + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + X = (float)(i + 1) / (repeats.RepeatCount + 1), + }); + } } protected override bool ShouldBeConsideredForInput(Drawable child) => true; From 06a51297a3f2b20d40a99e31d9ee024dea020261 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 6 Oct 2020 19:26:57 +0900 Subject: [PATCH 8/8] Use content instead of exposing the flow container --- osu.Game/Screens/Edit/Setup/MetadataSection.cs | 4 ++-- osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 +- osu.Game/Screens/Edit/Setup/SetupSection.cs | 7 +++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 31a2c2ce1a..4ddee2acc6 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -20,7 +20,7 @@ namespace osu.Game.Screens.Edit.Setup [BackgroundDependencyLoader] private void load() { - Flow.Children = new Drawable[] + Children = new Drawable[] { new OsuSpriteText { @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Edit.Setup }, }; - foreach (var item in Flow.OfType()) + foreach (var item in Children.OfType()) item.OnCommit += onCommit; } diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 86d7968856..17ecfdd52e 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -53,7 +53,7 @@ namespace osu.Game.Screens.Edit.Setup AutoSizeAxes = Axes.Y, }; - Flow.Children = new Drawable[] + Children = new Drawable[] { backgroundSpriteContainer = new Container { diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index 54e383a4d8..cdf17d355e 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -1,7 +1,6 @@ // 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 osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -14,7 +13,7 @@ namespace osu.Game.Screens.Edit.Setup { internal class SetupSection : Container { - protected FillFlowContainer Flow; + private readonly FillFlowContainer flow; [Resolved] protected OsuColour Colours { get; private set; } @@ -22,7 +21,7 @@ namespace osu.Game.Screens.Edit.Setup [Resolved] protected IBindable Beatmap { get; private set; } - public override void Add(Drawable drawable) => throw new InvalidOperationException("Use Flow.Add instead"); + protected override Container Content => flow; public SetupSection() { @@ -31,7 +30,7 @@ namespace osu.Game.Screens.Edit.Setup Padding = new MarginPadding(10); - InternalChild = Flow = new FillFlowContainer + InternalChild = flow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y,