mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 03:33:22 +08:00
Merge branch 'master' into fix-wrapped-storage-native-open
This commit is contained in:
commit
f8d9c044df
@ -8,7 +8,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Resources;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
@ -18,8 +18,8 @@ namespace osu.Game.Benchmarks
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
using (var resources = new DllResourceStore(OsuResources.ResourceAssembly))
|
||||
using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz"))
|
||||
using (var resources = new DllResourceStore(typeof(TestResources).Assembly))
|
||||
using (var archive = resources.GetStream("Resources/Archives/241526 Soleily - Renatus.osz"))
|
||||
using (var reader = new ZipArchiveReader(archive))
|
||||
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj" />
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
@ -12,6 +16,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
@ -32,9 +37,80 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
new SpinnerCompositionTool()
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
LayerBelowRuleset.Add(distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both });
|
||||
|
||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += (_, __) => updateDistanceSnapGrid();
|
||||
EditorBeatmap.PlacementObject.ValueChanged += _ => updateDistanceSnapGrid();
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects);
|
||||
|
||||
protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
|
||||
private DistanceSnapGrid distanceSnapGrid;
|
||||
private Container distanceSnapGridContainer;
|
||||
|
||||
private readonly Cached distanceSnapGridCache = new Cached();
|
||||
private double? lastDistanceSnapGridTime;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!(BlueprintContainer.CurrentTool is SelectTool))
|
||||
{
|
||||
if (EditorClock.CurrentTime != lastDistanceSnapGridTime)
|
||||
{
|
||||
distanceSnapGridCache.Invalidate();
|
||||
lastDistanceSnapGridTime = EditorClock.CurrentTime;
|
||||
}
|
||||
|
||||
if (!distanceSnapGridCache.IsValid)
|
||||
updateDistanceSnapGrid();
|
||||
}
|
||||
}
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
if (distanceSnapGrid == null)
|
||||
return base.SnapScreenSpacePositionToValidTime(screenSpacePosition);
|
||||
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time);
|
||||
}
|
||||
|
||||
private void updateDistanceSnapGrid()
|
||||
{
|
||||
distanceSnapGridContainer.Clear();
|
||||
distanceSnapGridCache.Invalidate();
|
||||
|
||||
switch (BlueprintContainer.CurrentTool)
|
||||
{
|
||||
case SelectTool _:
|
||||
if (!EditorBeatmap.SelectedHitObjects.Any())
|
||||
return;
|
||||
|
||||
distanceSnapGrid = createDistanceSnapGrid(EditorBeatmap.SelectedHitObjects);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!CursorInPlacementArea)
|
||||
return;
|
||||
|
||||
distanceSnapGrid = createDistanceSnapGrid(Enumerable.Empty<HitObject>());
|
||||
break;
|
||||
}
|
||||
|
||||
if (distanceSnapGrid != null)
|
||||
{
|
||||
distanceSnapGridContainer.Add(distanceSnapGrid);
|
||||
distanceSnapGridCache.Validate();
|
||||
}
|
||||
}
|
||||
|
||||
private DistanceSnapGrid createDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
|
||||
{
|
||||
if (BlueprintContainer.CurrentTool is SpinnerCompositionTool)
|
||||
return null;
|
||||
@ -42,7 +118,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
var objects = selectedHitObjects.ToList();
|
||||
|
||||
if (objects.Count == 0)
|
||||
return createGrid(h => h.StartTime <= EditorClock.CurrentTime);
|
||||
// use accurate time value to give more instantaneous feedback to the user.
|
||||
return createGrid(h => h.StartTime <= EditorClock.CurrentTimeAccurate);
|
||||
|
||||
double minTime = objects.Min(h => h.StartTime);
|
||||
return createGrid(h => h.StartTime < minTime, objects.Count + 1);
|
||||
|
@ -4,8 +4,8 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osuTK;
|
||||
|
||||
@ -17,9 +17,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
Dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
Dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
var clock = new EditorClock { IsCoupled = false };
|
||||
Dependencies.CacheAs(clock);
|
||||
|
||||
var playback = new PlaybackControl
|
||||
{
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -69,7 +68,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public AudioVisualiser()
|
||||
{
|
||||
@ -96,13 +95,15 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
base.Update();
|
||||
|
||||
if (beatmap.Value.Track.IsLoaded)
|
||||
marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length);
|
||||
marker.X = (float)(editorClock.CurrentTime / beatmap.Value.Track.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private class StartStopButton : OsuButton
|
||||
{
|
||||
private IAdjustableClock adjustableClock;
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
private bool started;
|
||||
|
||||
public StartStopButton()
|
||||
@ -114,22 +115,16 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
Action = onClick;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
}
|
||||
|
||||
private void onClick()
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
adjustableClock.Stop();
|
||||
editorClock.Stop();
|
||||
Text = "Start";
|
||||
}
|
||||
else
|
||||
{
|
||||
adjustableClock.Start();
|
||||
editorClock.Start();
|
||||
Text = "Stop";
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
||||
using ZipArchive = SharpCompress.Archives.Zip.ZipArchive;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -66,7 +65,6 @@ namespace osu.Game.Beatmaps
|
||||
private readonly AudioManager audioManager;
|
||||
private readonly GameHost host;
|
||||
private readonly BeatmapOnlineLookupQueue onlineLookupQueue;
|
||||
private readonly Storage exportStorage;
|
||||
|
||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, AudioManager audioManager, GameHost host = null,
|
||||
WorkingBeatmap defaultBeatmap = null)
|
||||
@ -83,7 +81,6 @@ namespace osu.Game.Beatmaps
|
||||
beatmaps.BeatmapRestored += b => beatmapRestored.Value = new WeakReference<BeatmapInfo>(b);
|
||||
|
||||
onlineLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
|
||||
exportStorage = storage.GetStorageForDirectory("exports");
|
||||
}
|
||||
|
||||
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) =>
|
||||
@ -214,26 +211,6 @@ namespace osu.Game.Beatmaps
|
||||
workingCache.Remove(working);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a <see cref="BeatmapSetInfo"/> to an .osz package.
|
||||
/// </summary>
|
||||
/// <param name="set">The <see cref="BeatmapSetInfo"/> to export.</param>
|
||||
public void Export(BeatmapSetInfo set)
|
||||
{
|
||||
var localSet = QueryBeatmapSet(s => s.ID == set.ID);
|
||||
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
foreach (var file in localSet.Files)
|
||||
archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
|
||||
|
||||
using (var outputStream = exportStorage.GetStream($"{set}.osz", FileAccess.Write, FileMode.Create))
|
||||
archive.SaveTo(outputStream);
|
||||
|
||||
exportStorage.OpenInNativeExplorer();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly WeakList<WorkingBeatmap> workingCache = new WeakList<WorkingBeatmap>();
|
||||
|
||||
/// <summary>
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.IO.Archives;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Utils;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
@ -82,6 +83,8 @@ namespace osu.Game.Database
|
||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||
private ArchiveImportIPCChannel ipc;
|
||||
|
||||
private readonly Storage exportStorage;
|
||||
|
||||
protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes<TModel, TFileModel> modelStore, IIpcHost importHost = null)
|
||||
{
|
||||
ContextFactory = contextFactory;
|
||||
@ -90,6 +93,8 @@ namespace osu.Game.Database
|
||||
ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference<TModel>(item));
|
||||
ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference<TModel>(item));
|
||||
|
||||
exportStorage = storage.GetStorageForDirectory("exports");
|
||||
|
||||
Files = new FileStore(contextFactory, storage);
|
||||
|
||||
if (importHost != null)
|
||||
@ -369,6 +374,29 @@ namespace osu.Game.Database
|
||||
return item;
|
||||
}, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap();
|
||||
|
||||
/// <summary>
|
||||
/// Exports an item to a legacy (.zip based) package.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to export.</param>
|
||||
public void Export(TModel item)
|
||||
{
|
||||
var retrievedItem = ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == item.ID);
|
||||
|
||||
if (retrievedItem == null)
|
||||
throw new ArgumentException("Specified model could not be found", nameof(item));
|
||||
|
||||
using (var archive = ZipArchive.Create())
|
||||
{
|
||||
foreach (var file in retrievedItem.Files)
|
||||
archive.AddEntry(file.Filename, Files.Storage.GetStream(file.FileInfo.StoragePath));
|
||||
|
||||
using (var outputStream = exportStorage.GetStream($"{getValidFilename(item.ToString())}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create))
|
||||
archive.SaveTo(outputStream);
|
||||
|
||||
exportStorage.OpenInNativeExplorer();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateFile(TModel model, TFileModel file, Stream contents)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
@ -710,5 +738,12 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string getValidFilename(string filename)
|
||||
{
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
filename = filename.Replace(c, '_');
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Skinning;
|
||||
@ -38,9 +39,11 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
FlowContent.Spacing = new Vector2(0, 5);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
skinDropdown = new SkinSettingsDropdown(),
|
||||
new ExportSkinButton(),
|
||||
new SettingsSlider<float, SizeSlider>
|
||||
{
|
||||
LabelText = "Menu cursor size",
|
||||
@ -117,5 +120,35 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200);
|
||||
}
|
||||
}
|
||||
|
||||
private class ExportSkinButton : SettingsButton
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
private Bindable<Skin> currentSkin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Text = "Export selected skin";
|
||||
Action = export;
|
||||
|
||||
currentSkin = skins.CurrentSkin.GetBoundCopy();
|
||||
currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.ID > 0, true);
|
||||
}
|
||||
|
||||
private void export()
|
||||
{
|
||||
try
|
||||
{
|
||||
skins.Export(currentSkin.Value.SkinInfo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Log($"Could not export current skin: {e.Message}", level: LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,14 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
@ -38,22 +37,20 @@ namespace osu.Game.Rulesets.Edit
|
||||
protected readonly Ruleset Ruleset;
|
||||
|
||||
[Resolved]
|
||||
protected IFrameBasedClock EditorClock { get; private set; }
|
||||
protected EditorClock EditorClock { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
protected EditorBeatmap EditorBeatmap { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
protected IBeatSnapProvider BeatSnapProvider { get; private set; }
|
||||
|
||||
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
|
||||
|
||||
private DrawableEditRulesetWrapper<TObject> drawableRulesetWrapper;
|
||||
private Container distanceSnapGridContainer;
|
||||
private DistanceSnapGrid distanceSnapGrid;
|
||||
|
||||
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
private readonly List<Container> layerContainers = new List<Container>();
|
||||
|
||||
private InputManager inputManager;
|
||||
@ -67,7 +64,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IFrameBasedClock framedClock)
|
||||
private void load()
|
||||
{
|
||||
Config = Dependencies.Get<RulesetConfigCache>().GetConfigFor(Ruleset);
|
||||
|
||||
@ -75,7 +72,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap))
|
||||
{
|
||||
Clock = framedClock,
|
||||
Clock = EditorClock,
|
||||
ProcessCustomClock = false
|
||||
};
|
||||
}
|
||||
@ -87,7 +84,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
var layerBelowRuleset = drawableRulesetWrapper.CreatePlayfieldAdjustmentContainer().WithChildren(new Drawable[]
|
||||
{
|
||||
distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both },
|
||||
LayerBelowRuleset,
|
||||
new EditorPlayfieldBorder { RelativeSizeAxes = Axes.Both }
|
||||
});
|
||||
|
||||
@ -139,7 +136,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
setSelectTool();
|
||||
|
||||
BlueprintContainer.SelectionChanged += selectionChanged;
|
||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
@ -165,16 +162,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
inputManager = GetContainingInputManager();
|
||||
}
|
||||
|
||||
private double lastGridUpdateTime;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (EditorClock.CurrentTime != lastGridUpdateTime && !(BlueprintContainer.CurrentTool is SelectTool))
|
||||
showGridFor(Enumerable.Empty<HitObject>());
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
@ -188,19 +175,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
});
|
||||
}
|
||||
|
||||
private void selectionChanged(IEnumerable<HitObject> selectedHitObjects)
|
||||
private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs)
|
||||
{
|
||||
var hitObjects = selectedHitObjects.ToArray();
|
||||
|
||||
if (hitObjects.Any())
|
||||
if (EditorBeatmap.SelectedHitObjects.Any())
|
||||
{
|
||||
// ensure in selection mode if a selection is made.
|
||||
setSelectTool();
|
||||
|
||||
showGridFor(hitObjects);
|
||||
}
|
||||
else
|
||||
distanceSnapGridContainer.Hide();
|
||||
}
|
||||
|
||||
private void setSelectTool() => toolboxCollection.Items.First().Select();
|
||||
@ -209,30 +190,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
BlueprintContainer.CurrentTool = tool;
|
||||
|
||||
if (tool is SelectTool)
|
||||
distanceSnapGridContainer.Hide();
|
||||
else
|
||||
{
|
||||
if (!(tool is SelectTool))
|
||||
EditorBeatmap.SelectedHitObjects.Clear();
|
||||
showGridFor(Enumerable.Empty<HitObject>());
|
||||
}
|
||||
}
|
||||
|
||||
private void showGridFor(IEnumerable<HitObject> selectedHitObjects)
|
||||
{
|
||||
distanceSnapGridContainer.Clear();
|
||||
distanceSnapGrid = CreateDistanceSnapGrid(selectedHitObjects);
|
||||
|
||||
if (distanceSnapGrid != null)
|
||||
{
|
||||
distanceSnapGridContainer.Child = distanceSnapGrid;
|
||||
distanceSnapGridContainer.Show();
|
||||
}
|
||||
|
||||
lastGridUpdateTime = EditorClock.CurrentTime;
|
||||
}
|
||||
|
||||
public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
|
||||
|
||||
public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
|
||||
|
||||
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
||||
@ -254,24 +217,14 @@ namespace osu.Game.Rulesets.Edit
|
||||
{
|
||||
EditorBeatmap.Add(hitObject);
|
||||
|
||||
if (adjustableClock.CurrentTime < hitObject.StartTime)
|
||||
adjustableClock.Seek(hitObject.StartTime);
|
||||
if (EditorClock.CurrentTime < hitObject.StartTime)
|
||||
EditorClock.SeekTo(hitObject.StartTime);
|
||||
}
|
||||
|
||||
showGridFor(Enumerable.Empty<HitObject>());
|
||||
}
|
||||
|
||||
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
|
||||
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||
{
|
||||
if (distanceSnapGrid == null) return new SnapResult(screenSpacePosition, null);
|
||||
|
||||
// TODO: move distance snap grid to OsuHitObjectComposer.
|
||||
(Vector2 pos, double time) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(screenSpacePosition));
|
||||
|
||||
return new SnapResult(distanceSnapGrid.ToScreenSpace(pos), time);
|
||||
}
|
||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) => new SnapResult(screenSpacePosition, null);
|
||||
|
||||
public override float GetBeatSnapDistanceAt(double referenceTime)
|
||||
{
|
||||
@ -321,14 +274,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
public abstract bool CursorInPlacementArea { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="DistanceSnapGrid"/> applicable for a <see cref="HitObject"/> selection.
|
||||
/// </summary>
|
||||
/// <param name="selectedHitObjects">The <see cref="HitObject"/> selection.</param>
|
||||
/// <returns>The <see cref="DistanceSnapGrid"/> for <paramref name="selectedHitObjects"/>. If empty, a grid is returned for the current point in time.</returns>
|
||||
[CanBeNull]
|
||||
protected virtual DistanceSnapGrid CreateDistanceSnapGrid([NotNull] IEnumerable<HitObject> selectedHitObjects) => null;
|
||||
|
||||
public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
|
||||
|
||||
public abstract float GetBeatSnapDistanceAt(double referenceTime);
|
||||
|
@ -6,10 +6,10 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
using osuTK;
|
||||
|
||||
@ -30,10 +30,13 @@ namespace osu.Game.Rulesets.Edit
|
||||
/// </summary>
|
||||
protected readonly HitObject HitObject;
|
||||
|
||||
protected IClock EditorClock { get; private set; }
|
||||
[Resolved(canBeNull: true)]
|
||||
protected EditorClock EditorClock { get; private set; }
|
||||
|
||||
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private Bindable<double> startTimeBindable;
|
||||
|
||||
[Resolved]
|
||||
private IPlacementHandler placementHandler { get; set; }
|
||||
|
||||
@ -49,13 +52,12 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindable<WorkingBeatmap> beatmap, IAdjustableClock clock)
|
||||
private void load(IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
this.beatmap.BindTo(beatmap);
|
||||
|
||||
EditorClock = clock;
|
||||
|
||||
ApplyDefaultsToHitObject();
|
||||
startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy();
|
||||
startTimeBindable.BindValueChanged(_ => ApplyDefaultsToHitObject(), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,9 +83,6 @@ namespace osu.Game.Rulesets.Edit
|
||||
PlacementActive = false;
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private IFrameBasedClock editorClock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of this <see cref="PlacementBlueprint"/> to a new screen-space position.
|
||||
/// </summary>
|
||||
@ -91,7 +90,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
public virtual void UpdatePosition(SnapResult snapResult)
|
||||
{
|
||||
if (!PlacementActive)
|
||||
HitObject.StartTime = snapResult.Time ?? editorClock?.CurrentTime ?? Time.Current;
|
||||
HitObject.StartTime = snapResult.Time ?? EditorClock?.CurrentTime ?? Time.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -26,7 +25,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
private IconButton playButton;
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
private readonly BindableNumber<double> tempo = new BindableDouble(1);
|
||||
|
||||
@ -87,17 +86,17 @@ namespace osu.Game.Screens.Edit.Components
|
||||
|
||||
private void togglePause()
|
||||
{
|
||||
if (adjustableClock.IsRunning)
|
||||
adjustableClock.Stop();
|
||||
if (editorClock.IsRunning)
|
||||
editorClock.Stop();
|
||||
else
|
||||
adjustableClock.Start();
|
||||
editorClock.Start();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
playButton.Icon = adjustableClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
|
||||
playButton.Icon = editorClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
|
||||
}
|
||||
|
||||
private class PlaybackTabControl : OsuTabControl<double>
|
||||
|
@ -5,7 +5,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
@ -15,7 +14,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
private readonly OsuSpriteText trackTimer;
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public TimeInfoContainer()
|
||||
{
|
||||
@ -35,7 +34,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
base.Update();
|
||||
|
||||
trackTimer.Text = TimeSpan.FromMilliseconds(adjustableClock.CurrentTime).ToString(@"mm\:ss\:fff");
|
||||
trackTimer.Text = TimeSpan.FromMilliseconds(editorClock.CurrentTime).ToString(@"mm\:ss\:fff");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@ -20,14 +19,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
/// </summary>
|
||||
public class MarkerPart : TimelinePart
|
||||
{
|
||||
private readonly Drawable marker;
|
||||
private Drawable marker;
|
||||
|
||||
private readonly IAdjustableClock adjustableClock;
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public MarkerPart(IAdjustableClock adjustableClock)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
Add(marker = new MarkerVisualisation());
|
||||
}
|
||||
|
||||
@ -59,14 +58,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
return;
|
||||
|
||||
float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
|
||||
adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length);
|
||||
editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
marker.X = (float)adjustableClock.CurrentTime;
|
||||
marker.X = (float)editorClock.CurrentTime;
|
||||
}
|
||||
|
||||
protected override void LoadBeatmap(WorkingBeatmap beatmap)
|
||||
|
@ -6,7 +6,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
|
||||
@ -18,11 +17,11 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
||||
public class SummaryTimeline : BottomBarContainer
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, IAdjustableClock adjustableClock)
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MarkerPart(adjustableClock) { RelativeSizeAxes = Axes.Both },
|
||||
new MarkerPart { RelativeSizeAxes = Axes.Both },
|
||||
new ControlPointPart
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@ -14,7 +13,6 @@ using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -29,8 +27,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// </summary>
|
||||
public abstract class BlueprintContainer : CompositeDrawable, IKeyBindingHandler<PlatformAction>
|
||||
{
|
||||
public event Action<IEnumerable<HitObject>> SelectionChanged;
|
||||
|
||||
protected DragBox DragBox { get; private set; }
|
||||
|
||||
protected Container<SelectionBlueprint> SelectionBlueprints { get; private set; }
|
||||
@ -41,7 +37,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
private IEditorChangeHandler changeHandler { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap beatmap { get; set; }
|
||||
@ -88,8 +84,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
SelectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
|
||||
break;
|
||||
}
|
||||
|
||||
SelectionChanged?.Invoke(selectedHitObjects);
|
||||
};
|
||||
}
|
||||
|
||||
@ -149,7 +143,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (clickedBlueprint == null)
|
||||
return false;
|
||||
|
||||
adjustableClock?.Seek(clickedBlueprint.HitObject.StartTime);
|
||||
editorClock?.SeekTo(clickedBlueprint.HitObject.StartTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -25,7 +24,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
public readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock adjustableClock { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
public Timeline()
|
||||
{
|
||||
@ -101,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 };
|
||||
|
||||
// This needs to happen after transforms are updated, but before the scroll position is updated in base.UpdateAfterChildren
|
||||
if (adjustableClock.IsRunning)
|
||||
if (editorClock.IsRunning)
|
||||
scrollToTrackTime();
|
||||
}
|
||||
|
||||
@ -111,21 +110,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
if (handlingDragInput)
|
||||
seekTrackToCurrent();
|
||||
else if (!adjustableClock.IsRunning)
|
||||
else if (!editorClock.IsRunning)
|
||||
{
|
||||
// The track isn't running. There are two cases we have to be wary of:
|
||||
// 1) The user flick-drags on this timeline: We want the track to follow us
|
||||
// 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time
|
||||
|
||||
// The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally
|
||||
if (Current != lastScrollPosition && adjustableClock.CurrentTime == lastTrackTime)
|
||||
if (Current != lastScrollPosition && editorClock.CurrentTime == lastTrackTime)
|
||||
seekTrackToCurrent();
|
||||
else
|
||||
scrollToTrackTime();
|
||||
}
|
||||
|
||||
lastScrollPosition = Current;
|
||||
lastTrackTime = adjustableClock.CurrentTime;
|
||||
lastTrackTime = editorClock.CurrentTime;
|
||||
}
|
||||
|
||||
private void seekTrackToCurrent()
|
||||
@ -133,7 +132,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
if (!track.IsLoaded)
|
||||
return;
|
||||
|
||||
adjustableClock.Seek(Current / Content.DrawWidth * track.Length);
|
||||
editorClock.Seek(Current / Content.DrawWidth * track.Length);
|
||||
}
|
||||
|
||||
private void scrollToTrackTime()
|
||||
@ -141,7 +140,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
if (!track.IsLoaded || track.Length == 0)
|
||||
return;
|
||||
|
||||
ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||
ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
@ -164,15 +163,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
private void beginUserDrag()
|
||||
{
|
||||
handlingDragInput = true;
|
||||
trackWasPlaying = adjustableClock.IsRunning;
|
||||
adjustableClock.Stop();
|
||||
trackWasPlaying = editorClock.IsRunning;
|
||||
editorClock.Stop();
|
||||
}
|
||||
|
||||
private void endUserDrag()
|
||||
{
|
||||
handlingDragInput = false;
|
||||
if (trackWasPlaying)
|
||||
adjustableClock.Start();
|
||||
editorClock.Start();
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
|
@ -83,8 +83,8 @@ namespace osu.Game.Screens.Edit
|
||||
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
||||
clock.ChangeSource(sourceClock);
|
||||
|
||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||
dependencies.CacheAs(clock);
|
||||
AddInternal(clock);
|
||||
|
||||
// todo: remove caching of this and consume via editorBeatmap?
|
||||
dependencies.Cache(beatDivisor);
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -13,7 +15,7 @@ namespace osu.Game.Screens.Edit
|
||||
/// <summary>
|
||||
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
|
||||
/// </summary>
|
||||
public class EditorClock : DecoupleableInterpolatingFramedClock
|
||||
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
||||
{
|
||||
public readonly double TrackLength;
|
||||
|
||||
@ -21,12 +23,11 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private readonly BindableBeatDivisor beatDivisor;
|
||||
|
||||
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.beatDivisor = beatDivisor;
|
||||
private readonly DecoupleableInterpolatingFramedClock underlyingClock;
|
||||
|
||||
ControlPointInfo = beatmap.Beatmap.ControlPointInfo;
|
||||
TrackLength = beatmap.Track.Length;
|
||||
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
|
||||
{
|
||||
}
|
||||
|
||||
public EditorClock(ControlPointInfo controlPointInfo, double trackLength, BindableBeatDivisor beatDivisor)
|
||||
@ -35,6 +36,13 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
ControlPointInfo = controlPointInfo;
|
||||
TrackLength = trackLength;
|
||||
|
||||
underlyingClock = new DecoupleableInterpolatingFramedClock();
|
||||
}
|
||||
|
||||
public EditorClock()
|
||||
: this(new ControlPointInfo(), 1000, new BindableBeatDivisor())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -79,20 +87,22 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private void seek(int direction, bool snapped, double amount = 1)
|
||||
{
|
||||
double current = CurrentTimeAccurate;
|
||||
|
||||
if (amount <= 0) throw new ArgumentException("Value should be greater than zero", nameof(amount));
|
||||
|
||||
var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime);
|
||||
var timingPoint = ControlPointInfo.TimingPointAt(current);
|
||||
|
||||
if (direction < 0 && timingPoint.Time == CurrentTime)
|
||||
if (direction < 0 && timingPoint.Time == current)
|
||||
// When going backwards and we're at the boundary of two timing points, we compute the seek distance with the timing point which we are seeking into
|
||||
timingPoint = ControlPointInfo.TimingPointAt(CurrentTime - 1);
|
||||
timingPoint = ControlPointInfo.TimingPointAt(current - 1);
|
||||
|
||||
double seekAmount = timingPoint.BeatLength / beatDivisor.Value * amount;
|
||||
double seekTime = CurrentTime + seekAmount * direction;
|
||||
double seekTime = current + seekAmount * direction;
|
||||
|
||||
if (!snapped || ControlPointInfo.TimingPoints.Count == 0)
|
||||
{
|
||||
Seek(seekTime);
|
||||
SeekTo(seekTime);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,7 +120,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
// Due to the rounding above, we may end up on the current beat. This will effectively cause 0 seeking to happen, but we don't want this.
|
||||
// Instead, we'll go to the next beat in the direction when this is the case
|
||||
if (Precision.AlmostEquals(CurrentTime, seekTime))
|
||||
if (Precision.AlmostEquals(current, seekTime))
|
||||
{
|
||||
closestBeat += direction > 0 ? 1 : -1;
|
||||
seekTime = timingPoint.Time + closestBeat * seekAmount;
|
||||
@ -125,7 +135,97 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
// Ensure the sought point is within the boundaries
|
||||
seekTime = Math.Clamp(seekTime, 0, TrackLength);
|
||||
Seek(seekTime);
|
||||
SeekTo(seekTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current time of this clock, include any active transform seeks performed via <see cref="SeekTo"/>.
|
||||
/// </summary>
|
||||
public double CurrentTimeAccurate =>
|
||||
Transforms.OfType<TransformSeek>().FirstOrDefault()?.EndValue ?? CurrentTime;
|
||||
|
||||
public double CurrentTime => underlyingClock.CurrentTime;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ClearTransforms();
|
||||
underlyingClock.Reset();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
ClearTransforms();
|
||||
underlyingClock.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
underlyingClock.Stop();
|
||||
}
|
||||
|
||||
public bool Seek(double position)
|
||||
{
|
||||
ClearTransforms();
|
||||
return underlyingClock.Seek(position);
|
||||
}
|
||||
|
||||
public void ResetSpeedAdjustments() => underlyingClock.ResetSpeedAdjustments();
|
||||
|
||||
double IAdjustableClock.Rate
|
||||
{
|
||||
get => underlyingClock.Rate;
|
||||
set => underlyingClock.Rate = value;
|
||||
}
|
||||
|
||||
double IClock.Rate => underlyingClock.Rate;
|
||||
|
||||
public bool IsRunning => underlyingClock.IsRunning;
|
||||
|
||||
public void ProcessFrame() => underlyingClock.ProcessFrame();
|
||||
|
||||
public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime;
|
||||
|
||||
public double FramesPerSecond => underlyingClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||
|
||||
public void ChangeSource(IClock source) => underlyingClock.ChangeSource(source);
|
||||
|
||||
public IClock Source => underlyingClock.Source;
|
||||
|
||||
public bool IsCoupled
|
||||
{
|
||||
get => underlyingClock.IsCoupled;
|
||||
set => underlyingClock.IsCoupled = value;
|
||||
}
|
||||
|
||||
private const double transform_time = 300;
|
||||
|
||||
public void SeekTo(double seekDestination)
|
||||
{
|
||||
if (IsRunning)
|
||||
Seek(seekDestination);
|
||||
else
|
||||
transformSeekTo(seekDestination, transform_time, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void transformSeekTo(double seek, double duration = 0, Easing easing = Easing.None)
|
||||
=> this.TransformTo(this.PopulateTransform(new TransformSeek(), seek, duration, easing));
|
||||
|
||||
private double currentTime
|
||||
{
|
||||
get => underlyingClock.CurrentTime;
|
||||
set => underlyingClock.Seek(value);
|
||||
}
|
||||
|
||||
private class TransformSeek : Transform<double, EditorClock>
|
||||
{
|
||||
public override string TargetMember => nameof(currentTime);
|
||||
|
||||
protected override void Apply(EditorClock clock, double time) =>
|
||||
clock.currentTime = Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
|
||||
protected override void ReadIntoStartValue(EditorClock clock) => StartValue = clock.currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
@ -23,7 +22,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
private Bindable<ControlPointGroup> selectedGroup = new Bindable<ControlPointGroup>();
|
||||
|
||||
[Resolved]
|
||||
private IAdjustableClock clock { get; set; }
|
||||
private EditorClock clock { get; set; }
|
||||
|
||||
protected override Drawable CreateMainContent() => new GridContainer
|
||||
{
|
||||
@ -50,7 +49,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
selectedGroup.BindValueChanged(selected =>
|
||||
{
|
||||
if (selected.NewValue != null)
|
||||
clock.Seek(selected.NewValue.Time);
|
||||
clock.SeekTo(selected.NewValue.Time);
|
||||
});
|
||||
}
|
||||
|
||||
@ -62,7 +61,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
private IBindableList<ControlPointGroup> controlGroups;
|
||||
|
||||
[Resolved]
|
||||
private IFrameBasedClock clock { get; set; }
|
||||
private EditorClock clock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
protected IBindable<WorkingBeatmap> Beatmap { get; private set; }
|
||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Skinning
|
||||
|
||||
public bool DeletePending { get; set; }
|
||||
|
||||
public string FullName => $"\"{Name}\" by {Creator}";
|
||||
|
||||
public static SkinInfo Default { get; } = new SkinInfo
|
||||
{
|
||||
Name = "osu!lazer",
|
||||
@ -34,6 +32,10 @@ namespace osu.Game.Skinning
|
||||
|
||||
public bool Equals(SkinInfo other) => other != null && ID == other.ID;
|
||||
|
||||
public override string ToString() => FullName;
|
||||
public override string ToString()
|
||||
{
|
||||
string author = Creator == null ? string.Empty : $"({Creator})";
|
||||
return $"{Name} {author}".Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ namespace osu.Game.Tests.Visual
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
dependencies.Cache(BeatDivisor);
|
||||
dependencies.CacheAs<IFrameBasedClock>(Clock);
|
||||
dependencies.CacheAs<IAdjustableClock>(Clock);
|
||||
dependencies.CacheAs(Clock);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
@ -32,7 +33,7 @@ namespace osu.Game.Tests.Visual
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
dependencies.CacheAs<IAdjustableClock>(new StopwatchClock());
|
||||
dependencies.CacheAs(new EditorClock());
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Dapper" Version="2.0.35" />
|
||||
<PackageReference Include="DiffPlex" Version="1.6.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.6.2" />
|
||||
<PackageReference Include="Humanizer" Version="2.8.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
@ -27,7 +27,7 @@
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.519.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
|
||||
<PackageReference Include="Sentry" Version="2.1.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
@ -75,13 +75,13 @@
|
||||
</ItemGroup>
|
||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<PackageReference Include="DiffPlex" Version="1.6.1" />
|
||||
<PackageReference Include="DiffPlex" Version="1.6.2" />
|
||||
<PackageReference Include="Humanizer" Version="2.8.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.519.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.25.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user