mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 14:57:52 +08:00
Merge branch 'master' into fix-first-hitobject-must-be-combo
This commit is contained in:
commit
fbb10f6774
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
BorderThickness = 10;
|
BorderThickness = 9; // roughly matches slider borders and makes stacked circles distinctly visible from each other.
|
||||||
BorderColour = Color4.White;
|
BorderColour = Color4.White;
|
||||||
|
|
||||||
Child = new Box
|
Child = new Box
|
||||||
|
@ -49,6 +49,23 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject;
|
OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject;
|
||||||
|
|
||||||
|
bool allowFallback = false;
|
||||||
|
|
||||||
|
// attempt lookup using priority specification
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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).
|
||||||
|
Texture overlayTexture = getTextureWithFallback("overlay");
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
circleSprites = new Container<Sprite>
|
circleSprites = new Container<Sprite>
|
||||||
@ -60,13 +77,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
hitCircleSprite = new Sprite
|
hitCircleSprite = new Sprite
|
||||||
{
|
{
|
||||||
Texture = getTextureWithFallback(string.Empty),
|
Texture = baseTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
hitCircleOverlay = new Sprite
|
hitCircleOverlay = new Sprite
|
||||||
{
|
{
|
||||||
Texture = getTextureWithFallback("overlay"),
|
Texture = overlayTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}
|
}
|
||||||
@ -101,8 +118,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
Texture tex = null;
|
Texture tex = null;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(priorityLookup))
|
if (!string.IsNullOrEmpty(priorityLookup))
|
||||||
|
{
|
||||||
tex = skin.GetTexture($"{priorityLookup}{name}");
|
tex = skin.GetTexture($"{priorityLookup}{name}");
|
||||||
|
|
||||||
|
if (!allowFallback)
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
return tex ?? skin.GetTexture($"hitcircle{name}");
|
return tex ?? skin.GetTexture($"hitcircle{name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
using (var zip = ZipArchive.Open(temp))
|
using (var zip = ZipArchive.Open(temp))
|
||||||
zip.WriteToDirectory(extractedFolder);
|
zip.WriteToDirectory(extractedFolder);
|
||||||
|
|
||||||
bool success = setup.ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
|
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
|
||||||
|
|
||||||
File.Delete(temp);
|
File.Delete(temp);
|
||||||
Directory.Delete(extractedFolder, true);
|
Directory.Delete(extractedFolder, true);
|
||||||
|
@ -54,6 +54,13 @@ namespace osu.Game.Overlays.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use Current instead")] // Can be removed 20210406
|
||||||
|
public Bindable<T> Bindable
|
||||||
|
{
|
||||||
|
get => Current;
|
||||||
|
set => Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual Bindable<T> Current
|
public virtual Bindable<T> Current
|
||||||
{
|
{
|
||||||
get => controlWithCurrent.Current;
|
get => controlWithCurrent.Current;
|
||||||
|
@ -77,6 +77,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hit windows for this <see cref="HitObject"/>.
|
/// The hit windows for this <see cref="HitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public HitWindows HitWindows { get; set; }
|
public HitWindows HitWindows { get; set; }
|
||||||
|
|
||||||
private readonly List<HitObject> nestedHitObjects = new List<HitObject>();
|
private readonly List<HitObject> nestedHitObjects = new List<HitObject>();
|
||||||
|
@ -199,7 +199,40 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// no bindable so we perform this every 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;
|
protected override bool ShouldBeConsideredForInput(Drawable child) => true;
|
||||||
|
@ -234,25 +234,19 @@ namespace osu.Game.Screens.Edit
|
|||||||
applyFunction(this);
|
applyFunction(this);
|
||||||
|
|
||||||
beatmapProcessor?.PreProcess();
|
beatmapProcessor?.PreProcess();
|
||||||
|
|
||||||
|
foreach (var h in batchPendingDeletes) processHitObject(h);
|
||||||
|
foreach (var h in batchPendingInserts) processHitObject(h);
|
||||||
|
|
||||||
beatmapProcessor?.PostProcess();
|
beatmapProcessor?.PostProcess();
|
||||||
|
|
||||||
isBatchApplying = false;
|
foreach (var h in batchPendingDeletes) HitObjectRemoved?.Invoke(h);
|
||||||
|
foreach (var h in batchPendingInserts) HitObjectAdded?.Invoke(h);
|
||||||
foreach (var h in batchPendingDeletes)
|
|
||||||
{
|
|
||||||
processHitObject(h);
|
|
||||||
HitObjectRemoved?.Invoke(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
batchPendingDeletes.Clear();
|
batchPendingDeletes.Clear();
|
||||||
|
|
||||||
foreach (var h in batchPendingInserts)
|
|
||||||
{
|
|
||||||
processHitObject(h);
|
|
||||||
HitObjectAdded?.Invoke(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
batchPendingInserts.Clear();
|
batchPendingInserts.Clear();
|
||||||
|
|
||||||
|
isBatchApplying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -86,7 +86,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="snapped">Whether to snap to the closest beat after seeking.</param>
|
/// <param name="snapped">Whether to snap to the closest beat after seeking.</param>
|
||||||
/// <param name="amount">The relative amount (magnitude) which should be seeked.</param>
|
/// <param name="amount">The relative amount (magnitude) which should be seeked.</param>
|
||||||
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));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Seeks forwards by one beat length.
|
/// Seeks forwards by one beat length.
|
||||||
|
73
osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs
Normal file
73
osu.Game/Screens/Edit/Setup/FileChooserLabelledTextBox.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// 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 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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A labelled textbox which reveals an inline file chooser when clicked.
|
||||||
|
/// </summary>
|
||||||
|
internal class FileChooserLabelledTextBox : LabelledTextBox
|
||||||
|
{
|
||||||
|
public Container Target;
|
||||||
|
|
||||||
|
private readonly IBindable<FileInfo> currentFile = new Bindable<FileInfo>();
|
||||||
|
|
||||||
|
public FileChooserLabelledTextBox()
|
||||||
|
{
|
||||||
|
currentFile.BindValueChanged(onFileSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFileSelected(ValueChangedEvent<FileInfo> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
osu.Game/Screens/Edit/Setup/MetadataSection.cs
Normal file
71
osu.Game/Screens/Edit/Setup/MetadataSection.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// 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 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()
|
||||||
|
{
|
||||||
|
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 Children.OfType<LabelledTextBox>())
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
211
osu.Game/Screens/Edit/Setup/ResourcesSection.cs
Normal file
211
osu.Game/Screens/Edit/Setup/ResourcesSection.cs
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
// 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 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<string> 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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<string> 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,76 +1,33 @@
|
|||||||
// 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.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
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;
|
||||||
using osu.Game.Graphics.Containers;
|
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 osu.Game.Overlays;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Setup
|
namespace osu.Game.Screens.Edit.Setup
|
||||||
{
|
{
|
||||||
public class SetupScreen : EditorScreen, ICanAcceptFiles
|
public class SetupScreen : EditorScreen
|
||||||
{
|
{
|
||||||
public IEnumerable<string> 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]
|
[Resolved]
|
||||||
private OsuGameBase game { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
[Resolved]
|
[Cached]
|
||||||
private MusicController music { get; set; }
|
protected readonly OverlayColourProvider ColourProvider;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private BeatmapManager beatmaps { get; set; }
|
|
||||||
|
|
||||||
[Resolved(canBeNull: true)]
|
|
||||||
private Editor editor { get; set; }
|
|
||||||
|
|
||||||
public SetupScreen()
|
public SetupScreen()
|
||||||
: base(EditorScreenMode.SongSetup)
|
: base(EditorScreenMode.SongSetup)
|
||||||
{
|
{
|
||||||
|
ColourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private OsuColour colours { get; set; }
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Container audioTrackFileChooserContainer = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
};
|
|
||||||
|
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -87,273 +44,33 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
Colour = colours.GreySeafoamDark,
|
Colour = colours.GreySeafoamDark,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
new OsuScrollContainer
|
new SectionsContainer<SetupSection>
|
||||||
{
|
{
|
||||||
|
FixedHeader = new SetupScreenHeader(),
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding(10),
|
Children = new SetupSection[]
|
||||||
Child = flow = new FillFlowContainer
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
new ResourcesSection(),
|
||||||
AutoSizeAxes = Axes.Y,
|
new MetadataSection(),
|
||||||
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
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateBackgroundSprite();
|
|
||||||
|
|
||||||
audioTrackTextBox.Current.BindValueChanged(audioTrackChanged);
|
|
||||||
|
|
||||||
foreach (var item in flow.OfType<LabelledTextBox>())
|
|
||||||
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<string> 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<FileInfo> currentFile = new Bindable<FileInfo>();
|
private class SetupScreenTitle : OverlayTitle
|
||||||
|
|
||||||
public FileChooserLabelledTextBox()
|
|
||||||
{
|
{
|
||||||
currentFile.BindValueChanged(onFileSelected);
|
public SetupScreenTitle()
|
||||||
}
|
|
||||||
|
|
||||||
private void onFileSelected(ValueChangedEvent<FileInfo> file)
|
|
||||||
{
|
|
||||||
if (file.NewValue == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Target.Clear();
|
|
||||||
Current.Value = file.NewValue.FullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override OsuTextBox CreateTextBox() =>
|
|
||||||
new FileChooserOsuTextBox
|
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Title = "beatmap setup";
|
||||||
Origin = Anchor.Centre,
|
Description = "change general settings of your beatmap";
|
||||||
RelativeSizeAxes = Axes.X,
|
IconTexture = "Icons/Hexacons/social";
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
42
osu.Game/Screens/Edit/Setup/SetupSection.cs
Normal file
42
osu.Game/Screens/Edit/Setup/SetupSection.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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.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
|
||||||
|
{
|
||||||
|
private readonly FillFlowContainer flow;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
protected OsuColour Colours { get; private set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
protected IBindable<WorkingBeatmap> Beatmap { get; private set; }
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => flow;
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user