mirror of
https://github.com/osukey/osukey.git
synced 2025-08-07 00:23:59 +09:00
Merge pull request #10234 from peppy/editor-load-audio
Add audio track selection to editor setup screen
This commit is contained in:
@ -0,0 +1,69 @@
|
|||||||
|
// 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 System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Screens.Edit.Setup;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
using SharpCompress.Archives;
|
||||||
|
using SharpCompress.Archives.Zip;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneEditorBeatmapCreation : EditorTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null));
|
||||||
|
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
// if we save a beatmap with a hash collision, things fall over.
|
||||||
|
// probably needs a more solid resolution in the future but this will do for now.
|
||||||
|
AddStep("make new beatmap unique", () => EditorBeatmap.Metadata.Title = Guid.NewGuid().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCreateNewBeatmap()
|
||||||
|
{
|
||||||
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
|
AddAssert("new beatmap persisted", () => EditorBeatmap.BeatmapInfo.ID > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddAudioTrack()
|
||||||
|
{
|
||||||
|
AddAssert("switch track to real track", () =>
|
||||||
|
{
|
||||||
|
var setup = Editor.ChildrenOfType<SetupScreen>().First();
|
||||||
|
|
||||||
|
var temp = TestResources.GetTestBeatmapForImport();
|
||||||
|
|
||||||
|
string extractedFolder = $"{temp}_extracted";
|
||||||
|
Directory.CreateDirectory(extractedFolder);
|
||||||
|
|
||||||
|
using (var zip = ZipArchive.Open(temp))
|
||||||
|
zip.WriteToDirectory(extractedFolder);
|
||||||
|
|
||||||
|
bool success = setup.ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
|
||||||
|
|
||||||
|
File.Delete(temp);
|
||||||
|
Directory.Delete(extractedFolder, true);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("track length changed", () => Beatmap.Value.Track.Length > 60000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -260,7 +260,7 @@ namespace osu.Game.Beatmaps
|
|||||||
fileInfo.Filename = beatmapInfo.Path;
|
fileInfo.Filename = beatmapInfo.Path;
|
||||||
|
|
||||||
stream.Seek(0, SeekOrigin.Begin);
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
UpdateFile(setInfo, fileInfo, stream);
|
ReplaceFile(setInfo, fileInfo, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,12 +401,27 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update an existing file, or create a new entry if not already part of the <paramref name="model"/>'s files.
|
/// Replace an existing file with a new version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The item to operate on.</param>
|
/// <param name="model">The item to operate on.</param>
|
||||||
/// <param name="file">The file model to be updated or added.</param>
|
/// <param name="file">The existing file to be replaced.</param>
|
||||||
/// <param name="contents">The new file contents.</param>
|
/// <param name="contents">The new file contents.</param>
|
||||||
public void UpdateFile(TModel model, TFileModel file, Stream contents)
|
/// <param name="filename">An optional filename for the new file. Will use the previous filename if not specified.</param>
|
||||||
|
public void ReplaceFile(TModel model, TFileModel file, Stream contents, string filename = null)
|
||||||
|
{
|
||||||
|
using (ContextFactory.GetForWrite())
|
||||||
|
{
|
||||||
|
DeleteFile(model, file);
|
||||||
|
AddFile(model, contents, filename ?? file.Filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete new file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The item to operate on.</param>
|
||||||
|
/// <param name="file">The existing file to be deleted.</param>
|
||||||
|
public void DeleteFile(TModel model, TFileModel file)
|
||||||
{
|
{
|
||||||
using (var usage = ContextFactory.GetForWrite())
|
using (var usage = ContextFactory.GetForWrite())
|
||||||
{
|
{
|
||||||
@ -415,15 +430,28 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
Files.Dereference(file.FileInfo);
|
Files.Dereference(file.FileInfo);
|
||||||
|
|
||||||
// Remove the file model.
|
// This shouldn't be required, but here for safety in case the provided TModel is not being change tracked
|
||||||
|
// Definitely can be removed once we rework the database backend.
|
||||||
usage.Context.Set<TFileModel>().Remove(file);
|
usage.Context.Set<TFileModel>().Remove(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new file info and containing file model.
|
|
||||||
model.Files.Remove(file);
|
model.Files.Remove(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a new file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">The item to operate on.</param>
|
||||||
|
/// <param name="contents">The new file contents.</param>
|
||||||
|
/// <param name="filename">The filename for the new file.</param>
|
||||||
|
public void AddFile(TModel model, Stream contents, string filename)
|
||||||
|
{
|
||||||
|
using (ContextFactory.GetForWrite())
|
||||||
|
{
|
||||||
model.Files.Add(new TFileModel
|
model.Files.Add(new TFileModel
|
||||||
{
|
{
|
||||||
Filename = file.Filename,
|
Filename = filename,
|
||||||
FileInfo = Files.Add(contents)
|
FileInfo = Files.Add(contents)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,13 +44,18 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
Component.BorderColour = colours.Blue;
|
Component.BorderColour = colours.Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override OsuTextBox CreateComponent() => new OsuTextBox
|
protected virtual OsuTextBox CreateTextBox() => new OsuTextBox
|
||||||
{
|
{
|
||||||
CommitOnFocusLost = true,
|
CommitOnFocusLost = true,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
CornerRadius = CORNER_RADIUS,
|
CornerRadius = CORNER_RADIUS,
|
||||||
}.With(t => t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText));
|
};
|
||||||
|
|
||||||
|
protected override OsuTextBox CreateComponent() => CreateTextBox().With(t =>
|
||||||
|
{
|
||||||
|
t.OnCommit += (sender, newText) => OnCommit?.Invoke(sender, newText);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,11 @@ namespace osu.Game.Overlays
|
|||||||
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
|
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forcefully reload the current <see cref="WorkingBeatmap"/>'s track from disk.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadCurrentTrack() => changeTrack();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change the position of a <see cref="BeatmapSetInfo"/> in the current playlist.
|
/// Change the position of a <see cref="BeatmapSetInfo"/> in the current playlist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -18,7 +18,8 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
private const float contents_padding = 15;
|
private const float contents_padding = 15;
|
||||||
|
|
||||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
protected Track Track => Beatmap.Value.Track;
|
|
||||||
|
protected readonly IBindable<Track> Track = new Bindable<Track>();
|
||||||
|
|
||||||
private readonly Drawable background;
|
private readonly Drawable background;
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
@ -42,9 +43,11 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours)
|
private void load(IBindable<WorkingBeatmap> beatmap, OsuColour colours, EditorClock clock)
|
||||||
{
|
{
|
||||||
Beatmap.BindTo(beatmap);
|
Beatmap.BindTo(beatmap);
|
||||||
|
Track.BindTo(clock.Track);
|
||||||
|
|
||||||
background.Colour = colours.Gray1;
|
background.Colour = colours.Gray1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,12 +62,12 @@ namespace osu.Game.Screens.Edit.Components
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Track?.AddAdjustment(AdjustableProperty.Tempo, tempo);
|
Track.BindValueChanged(tr => tr.NewValue?.AddAdjustment(AdjustableProperty.Tempo, tempo), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
Track?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
|
Track.Value?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
|
||||||
|
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -22,6 +23,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
{
|
{
|
||||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
|
||||||
|
protected readonly IBindable<Track> Track = new Bindable<Track>();
|
||||||
|
|
||||||
private readonly Container<T> content;
|
private readonly Container<T> content;
|
||||||
|
|
||||||
protected override Container<T> Content => content;
|
protected override Container<T> Content => content;
|
||||||
@ -35,12 +38,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
updateRelativeChildSize();
|
updateRelativeChildSize();
|
||||||
LoadBeatmap(b.NewValue);
|
LoadBeatmap(b.NewValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Track.ValueChanged += _ => updateRelativeChildSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(IBindable<WorkingBeatmap> beatmap)
|
private void load(IBindable<WorkingBeatmap> beatmap, EditorClock clock)
|
||||||
{
|
{
|
||||||
Beatmap.BindTo(beatmap);
|
Beatmap.BindTo(beatmap);
|
||||||
|
Track.BindTo(clock.Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRelativeChildSize()
|
private void updateRelativeChildSize()
|
||||||
|
@ -43,6 +43,7 @@ using osuTK.Input;
|
|||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
[Cached(typeof(IBeatSnapProvider))]
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
|
[Cached]
|
||||||
public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>, IKeyBindingHandler<PlatformAction>, IBeatSnapProvider
|
public class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>, IKeyBindingHandler<PlatformAction>, IBeatSnapProvider
|
||||||
{
|
{
|
||||||
public override float BackgroundParallaxAmount => 0.1f;
|
public override float BackgroundParallaxAmount => 0.1f;
|
||||||
@ -91,6 +92,9 @@ namespace osu.Game.Screens.Edit
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, GameHost host)
|
private void load(OsuColour colours, GameHost host)
|
||||||
{
|
{
|
||||||
@ -98,9 +102,9 @@ namespace osu.Game.Screens.Edit
|
|||||||
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
||||||
|
|
||||||
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
|
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
|
||||||
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
|
||||||
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
||||||
clock.ChangeSource(sourceClock);
|
|
||||||
|
UpdateClockSource();
|
||||||
|
|
||||||
dependencies.CacheAs(clock);
|
dependencies.CacheAs(clock);
|
||||||
AddInternal(clock);
|
AddInternal(clock);
|
||||||
@ -271,6 +275,15 @@ namespace osu.Game.Screens.Edit
|
|||||||
bottomBackground.Colour = colours.Gray2;
|
bottomBackground.Colour = colours.Gray2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the beatmap's track has changed, this method must be called to keep the editor in a valid state.
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateClockSource()
|
||||||
|
{
|
||||||
|
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
||||||
|
clock.ChangeSource(sourceClock);
|
||||||
|
}
|
||||||
|
|
||||||
protected void Save()
|
protected void Save()
|
||||||
{
|
{
|
||||||
// apply any set-level metadata changes.
|
// apply any set-level metadata changes.
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Transforms;
|
using osu.Framework.Graphics.Transforms;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -17,7 +19,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
||||||
{
|
{
|
||||||
public readonly double TrackLength;
|
public IBindable<Track> Track => track;
|
||||||
|
|
||||||
|
private readonly Bindable<Track> track = new Bindable<Track>();
|
||||||
|
|
||||||
|
public double TrackLength => track.Value?.Length ?? 60000;
|
||||||
|
|
||||||
public ControlPointInfo ControlPointInfo;
|
public ControlPointInfo ControlPointInfo;
|
||||||
|
|
||||||
@ -35,7 +41,6 @@ namespace osu.Game.Screens.Edit
|
|||||||
this.beatDivisor = beatDivisor;
|
this.beatDivisor = beatDivisor;
|
||||||
|
|
||||||
ControlPointInfo = controlPointInfo;
|
ControlPointInfo = controlPointInfo;
|
||||||
TrackLength = trackLength;
|
|
||||||
|
|
||||||
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
||||||
}
|
}
|
||||||
@ -190,7 +195,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||||
|
|
||||||
public void ChangeSource(IClock source) => underlyingClock.ChangeSource(source);
|
public void ChangeSource(IClock source)
|
||||||
|
{
|
||||||
|
track.Value = source as Track;
|
||||||
|
underlyingClock.ChangeSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
public IClock Source => underlyingClock.Source;
|
public IClock Source => underlyingClock.Source;
|
||||||
|
|
||||||
|
@ -1,17 +1,24 @@
|
|||||||
// 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.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
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.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
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.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Setup
|
namespace osu.Game.Screens.Edit.Setup
|
||||||
@ -23,6 +30,16 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
private LabelledTextBox titleTextBox;
|
private LabelledTextBox titleTextBox;
|
||||||
private LabelledTextBox creatorTextBox;
|
private LabelledTextBox creatorTextBox;
|
||||||
private LabelledTextBox difficultyTextBox;
|
private LabelledTextBox difficultyTextBox;
|
||||||
|
private LabelledTextBox audioTrackTextBox;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private MusicController music { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapManager beatmaps { get; set; }
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private Editor editor { get; set; }
|
||||||
|
|
||||||
public SetupScreen()
|
public SetupScreen()
|
||||||
: base(EditorScreenMode.SongSetup)
|
: base(EditorScreenMode.SongSetup)
|
||||||
@ -32,6 +49,12 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
|
Container audioTrackFileChooserContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
};
|
||||||
|
|
||||||
Child = new Container
|
Child = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -75,6 +98,18 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
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"
|
Text = "Beatmap metadata"
|
||||||
},
|
},
|
||||||
@ -109,10 +144,47 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
audioTrackTextBox.Current.BindValueChanged(audioTrackChanged);
|
||||||
|
|
||||||
foreach (var item in flow.OfType<LabelledTextBox>())
|
foreach (var item in flow.OfType<LabelledTextBox>())
|
||||||
item.OnCommit += onCommit;
|
item.OnCommit += onCommit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private void onCommit(TextBox sender, bool newText)
|
||||||
{
|
{
|
||||||
if (!newText) return;
|
if (!newText) return;
|
||||||
@ -125,4 +197,60 @@ namespace osu.Game.Screens.Edit.Setup
|
|||||||
Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value;
|
Beatmap.Value.BeatmapInfo.Version = difficultyTextBox.Current.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: new[] { ".mp3", ".ogg" })
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,15 @@ namespace osu.Game.Tests.Visual
|
|||||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual bool EditorComponentsReady => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
||||||
|
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true;
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
|
||||||
AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
AddUntilStep("wait for editor to load", () => EditorComponentsReady);
|
||||||
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
|
|
||||||
AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
AddStep("get beatmap", () => EditorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
||||||
AddStep("get clock", () => EditorClock = Editor.ChildrenOfType<EditorClock>().Single());
|
AddStep("get clock", () => EditorClock = Editor.ChildrenOfType<EditorClock>().Single());
|
||||||
}
|
}
|
||||||
|
@ -909,6 +909,7 @@ private void load()
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmaps/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmaps/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmap_0027s/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmap_0027s/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindable/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindable/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindables/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
Reference in New Issue
Block a user