diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs index 0f520215a1..24d2a786a0 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneManiaComposeScreen.cs @@ -22,6 +22,9 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor [Resolved] private SkinManager skins { get; set; } + [Cached] + private EditorClipboard clipboard = new EditorClipboard(); + [SetUpSteps] public void SetUpSteps() { diff --git a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs index 4813598c9d..9b8567e853 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneComposeScreen.cs @@ -26,6 +26,9 @@ namespace osu.Game.Tests.Visual.Editing } }); + [Cached] + private EditorClipboard clipboard = new EditorClipboard(); + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs index 926a2ad4e0..3b02d42b41 100644 --- a/osu.Game/Screens/Edit/Compose/ComposeScreen.cs +++ b/osu.Game/Screens/Edit/Compose/ComposeScreen.cs @@ -7,29 +7,26 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Extensions; +using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit.Compose.Components.Timeline; namespace osu.Game.Screens.Edit.Compose { - public class ComposeScreen : EditorScreenWithTimeline, IKeyBindingHandler + public class ComposeScreen : EditorScreenWithTimeline { - [Resolved] - private IBindable beatmap { get; set; } - [Resolved] private GameHost host { get; set; } [Resolved] private EditorClock clock { get; set; } + private Bindable clipboard { get; set; } + private HitObjectComposer composer; public ComposeScreen() @@ -76,18 +73,65 @@ namespace osu.Game.Screens.Edit.Compose return new EditorSkinProvidingContainer(EditorBeatmap).WithChild(content); } - #region Input Handling - - public bool OnPressed(KeyBindingPressEvent e) + [BackgroundDependencyLoader] + private void load(EditorClipboard clipboard) { - if (e.Action == PlatformAction.Copy) - host.GetClipboard()?.SetText(formatSelectionAsString()); - - return false; + this.clipboard = clipboard.Content.GetBoundCopy(); } - public void OnReleased(KeyBindingReleaseEvent e) + protected override void LoadComplete() { + base.LoadComplete(); + EditorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) => updateClipboardActionAvailability()); + clipboard.BindValueChanged(_ => updateClipboardActionAvailability(), true); + } + + #region Clipboard operations + + protected override void PerformCut() + { + base.PerformCut(); + + Copy(); + EditorBeatmap.RemoveRange(EditorBeatmap.SelectedHitObjects.ToArray()); + } + + protected override void PerformCopy() + { + base.PerformCopy(); + + clipboard.Value = new ClipboardContent(EditorBeatmap).Serialize(); + + host.GetClipboard()?.SetText(formatSelectionAsString()); + } + + protected override void PerformPaste() + { + base.PerformPaste(); + + var objects = clipboard.Value.Deserialize().HitObjects; + + Debug.Assert(objects.Any()); + + double timeOffset = clock.CurrentTime - objects.Min(o => o.StartTime); + + foreach (var h in objects) + h.StartTime += timeOffset; + + EditorBeatmap.BeginChange(); + + EditorBeatmap.SelectedHitObjects.Clear(); + + EditorBeatmap.AddRange(objects); + EditorBeatmap.SelectedHitObjects.AddRange(objects); + + EditorBeatmap.EndChange(); + } + + private void updateClipboardActionAvailability() + { + CanCut.Value = CanCopy.Value = EditorBeatmap.SelectedHitObjects.Any(); + CanPaste.Value = !string.IsNullOrEmpty(clipboard.Value); } private string formatSelectionAsString() diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 81b2847443..de265ad94b 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework; @@ -24,7 +23,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.IO.Serialization; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Rulesets.Edit; @@ -105,6 +103,9 @@ namespace osu.Game.Screens.Edit [Resolved] private MusicController music { get; set; } + [Cached] + public readonly EditorClipboard Clipboard = new EditorClipboard(); + public Editor(EditorLoader loader = null) { this.loader = loader; @@ -180,10 +181,6 @@ namespace osu.Game.Screens.Edit OsuMenuItem undoMenuItem; OsuMenuItem redoMenuItem; - EditorMenuItem cutMenuItem; - EditorMenuItem copyMenuItem; - EditorMenuItem pasteMenuItem; - AddInternal(new OsuContextMenuContainer { RelativeSizeAxes = Axes.Both, @@ -299,19 +296,15 @@ namespace osu.Game.Screens.Edit changeHandler.CanUndo.BindValueChanged(v => undoMenuItem.Action.Disabled = !v.NewValue, true); changeHandler.CanRedo.BindValueChanged(v => redoMenuItem.Action.Disabled = !v.NewValue, true); - editorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) => - { - bool hasObjects = editorBeatmap.SelectedHitObjects.Count > 0; - - cutMenuItem.Action.Disabled = !hasObjects; - copyMenuItem.Action.Disabled = !hasObjects; - }, true); - - clipboard.BindValueChanged(content => pasteMenuItem.Action.Disabled = string.IsNullOrEmpty(content.NewValue)); - menuBar.Mode.ValueChanged += onModeChanged; } + protected override void LoadComplete() + { + base.LoadComplete(); + setUpClipboardActionAvailability(); + } + /// /// If the beatmap's track has changed, this method must be called to keep the editor in a valid state. /// @@ -324,7 +317,7 @@ namespace osu.Game.Screens.Edit public void RestoreState([NotNull] EditorState state) => Schedule(() => { clock.Seek(state.Time); - clipboard.Value = state.ClipboardContent; + Clipboard.Content.Value = state.ClipboardContent; }); protected void Save() @@ -561,45 +554,37 @@ namespace osu.Game.Screens.Edit this.Exit(); } - private readonly Bindable clipboard = new Bindable(); + #region Clipboard support - protected void Cut() + private EditorMenuItem cutMenuItem; + private EditorMenuItem copyMenuItem; + private EditorMenuItem pasteMenuItem; + + private readonly BindableWithCurrent canCut = new BindableWithCurrent(); + private readonly BindableWithCurrent canCopy = new BindableWithCurrent(); + private readonly BindableWithCurrent canPaste = new BindableWithCurrent(); + + private void setUpClipboardActionAvailability() { - Copy(); - editorBeatmap.RemoveRange(editorBeatmap.SelectedHitObjects.ToArray()); + canCut.Current.BindValueChanged(cut => cutMenuItem.Action.Disabled = !cut.NewValue, true); + canCopy.Current.BindValueChanged(copy => copyMenuItem.Action.Disabled = !copy.NewValue, true); + canPaste.Current.BindValueChanged(paste => pasteMenuItem.Action.Disabled = !paste.NewValue, true); } - protected void Copy() + private void rebindClipboardBindables() { - if (editorBeatmap.SelectedHitObjects.Count == 0) - return; - - clipboard.Value = new ClipboardContent(editorBeatmap).Serialize(); + canCut.Current = currentScreen.CanCut; + canCopy.Current = currentScreen.CanCopy; + canPaste.Current = currentScreen.CanPaste; } - protected void Paste() - { - if (string.IsNullOrEmpty(clipboard.Value)) - return; + protected void Cut() => currentScreen?.Cut(); - var objects = clipboard.Value.Deserialize().HitObjects; + protected void Copy() => currentScreen?.Copy(); - Debug.Assert(objects.Any()); + protected void Paste() => currentScreen?.Paste(); - double timeOffset = clock.CurrentTime - objects.Min(o => o.StartTime); - - foreach (var h in objects) - h.StartTime += timeOffset; - - editorBeatmap.BeginChange(); - - editorBeatmap.SelectedHitObjects.Clear(); - - editorBeatmap.AddRange(objects); - editorBeatmap.SelectedHitObjects.AddRange(objects); - - editorBeatmap.EndChange(); - } + #endregion protected void Undo() => changeHandler.RestoreState(-1); @@ -677,6 +662,7 @@ namespace osu.Game.Screens.Edit finally { updateSampleDisabledState(); + rebindClipboardBindables(); } } @@ -757,7 +743,7 @@ namespace osu.Game.Screens.Edit protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, new EditorState { Time = clock.CurrentTimeAccurate, - ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? clipboard.Value : string.Empty + ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? Clipboard.Content.Value : string.Empty }); private void cancelExit() diff --git a/osu.Game/Screens/Edit/EditorClipboard.cs b/osu.Game/Screens/Edit/EditorClipboard.cs new file mode 100644 index 0000000000..f6f0c09e00 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorClipboard.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Screens.Edit +{ + /// + /// Wraps the contents of the editor clipboard. + /// + public class EditorClipboard + { + public Bindable Content { get; } = new Bindable(); + } +} diff --git a/osu.Game/Screens/Edit/EditorScreen.cs b/osu.Game/Screens/Edit/EditorScreen.cs index 2810f78835..516d7a23e0 100644 --- a/osu.Game/Screens/Edit/EditorScreen.cs +++ b/osu.Game/Screens/Edit/EditorScreen.cs @@ -2,6 +2,7 @@ // 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.Framework.Graphics.Cursor; @@ -49,5 +50,54 @@ namespace osu.Game.Screens.Edit this.ScaleTo(0.98f, 200, Easing.OutQuint) .FadeOut(200, Easing.OutQuint); } + + #region Clipboard operations + + public BindableBool CanCut { get; } = new BindableBool(); + + /// + /// Performs a "cut to clipboard" operation appropriate for the given screen. + /// + protected virtual void PerformCut() + { + } + + public void Cut() + { + if (CanCut.Value) + PerformCut(); + } + + public BindableBool CanCopy { get; } = new BindableBool(); + + /// + /// Performs a "copy to clipboard" operation appropriate for the given screen. + /// + protected virtual void PerformCopy() + { + } + + public virtual void Copy() + { + if (CanCopy.Value) + PerformCopy(); + } + + public BindableBool CanPaste { get; } = new BindableBool(); + + /// + /// Performs a "paste from clipboard" operation appropriate for the given screen. + /// + protected virtual void PerformPaste() + { + } + + public virtual void Paste() + { + if (CanPaste.Value) + PerformPaste(); + } + + #endregion } }