Merge branch 'master' into fix-skin-duplication

This commit is contained in:
Dan Balasescu
2018-11-30 16:20:03 +09:00
committed by GitHub
46 changed files with 853 additions and 149 deletions

View File

@ -43,6 +43,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(InputKey.Space, GlobalAction.Select),
new KeyBinding(InputKey.Enter, GlobalAction.Select),
new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select),
};
public IEnumerable<KeyBinding> InGameKeyBindings => new[]

View File

@ -178,6 +178,7 @@ namespace osu.Game.Online.API
AddParameter("grant_type", GrantType);
AddParameter("client_id", ClientId);
AddParameter("client_secret", ClientSecret);
AddParameter("scope", "*");
base.PrePerform();
}

View File

@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Edit
// Process object
var processor = ruleset.CreateBeatmapProcessor(beatmap);
processor.PreProcess();
processor?.PreProcess();
tObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
processor.PostProcess();
processor?.PostProcess();
// Add visual representation
var drawableObject = rulesetContainer.GetVisualRepresentation(tObject);

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Edit
{
public abstract class HitObjectComposer : CompositeDrawable
{
public IEnumerable<DrawableHitObject> HitObjects => rulesetContainer.Playfield.AllHitObjects;
public IEnumerable<DrawableHitObject> HitObjects => RulesetContainer.Playfield.AllHitObjects;
protected readonly Ruleset Ruleset;
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Edit
private readonly List<Container> layerContainers = new List<Container>();
private EditRulesetContainer rulesetContainer;
protected EditRulesetContainer RulesetContainer { get; private set; }
private BlueprintContainer blueprintContainer;
@ -54,8 +54,8 @@ namespace osu.Game.Rulesets.Edit
try
{
rulesetContainer = CreateRulesetContainer();
rulesetContainer.Clock = framedClock;
RulesetContainer = CreateRulesetContainer();
RulesetContainer.Clock = framedClock;
}
catch (Exception e)
{
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Edit
Children = new Drawable[]
{
layerBelowRuleset,
rulesetContainer,
RulesetContainer,
layerAboveRuleset
}
}
@ -140,25 +140,25 @@ namespace osu.Game.Rulesets.Edit
layerContainers.ForEach(l =>
{
l.Anchor = rulesetContainer.Playfield.Anchor;
l.Origin = rulesetContainer.Playfield.Origin;
l.Position = rulesetContainer.Playfield.Position;
l.Size = rulesetContainer.Playfield.Size;
l.Anchor = RulesetContainer.Playfield.Anchor;
l.Origin = RulesetContainer.Playfield.Origin;
l.Position = RulesetContainer.Playfield.Position;
l.Size = RulesetContainer.Playfield.Size;
});
}
/// <summary>
/// Whether the user's cursor is currently in an area of the <see cref="HitObjectComposer"/> that is valid for placement.
/// </summary>
public virtual bool CursorInPlacementArea => rulesetContainer.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
public virtual bool CursorInPlacementArea => RulesetContainer.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
/// <summary>
/// Adds a <see cref="HitObject"/> to the <see cref="Beatmaps.Beatmap"/> and visualises it.
/// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to add.</param>
public void Add(HitObject hitObject) => blueprintContainer.AddBlueprintFor(rulesetContainer.Add(hitObject));
public void Add(HitObject hitObject) => blueprintContainer.AddBlueprintFor(RulesetContainer.Add(hitObject));
public void Remove(HitObject hitObject) => blueprintContainer.RemoveBlueprintFor(rulesetContainer.Remove(hitObject));
public void Remove(HitObject hitObject) => blueprintContainer.RemoveBlueprintFor(RulesetContainer.Remove(hitObject));
internal abstract EditRulesetContainer CreateRulesetContainer();
@ -171,10 +171,9 @@ namespace osu.Game.Rulesets.Edit
public virtual SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject) => null;
/// <summary>
/// Creates a <see cref="SelectionBox"/> which outlines <see cref="DrawableHitObject"/>s
/// and handles hitobject pattern adjustments.
/// Creates a <see cref="SelectionHandler"/> which outlines <see cref="DrawableHitObject"/>s and handles movement of selections.
/// </summary>
public virtual SelectionBox CreateSelectionBox() => new SelectionBox();
public virtual SelectionHandler CreateSelectionHandler() => new SelectionHandler();
/// <summary>
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.

View File

@ -7,7 +7,6 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
@ -20,7 +19,7 @@ namespace osu.Game.Rulesets.Edit
/// <summary>
/// A blueprint which governs the creation of a new <see cref="HitObject"/> to actualisation.
/// </summary>
public abstract class PlacementBlueprint : CompositeDrawable, IStateful<PlacementState>, IRequireHighFrequencyMousePosition
public abstract class PlacementBlueprint : CompositeDrawable, IStateful<PlacementState>
{
/// <summary>
/// Invoked when <see cref="State"/> has changed.
@ -50,6 +49,10 @@ namespace osu.Game.Rulesets.Edit
RelativeSizeAxes = Axes.Both;
// This is required to allow the blueprint's position to be updated via OnMouseMove/Handle
// on the same frame it is made visible via a PlacementState change.
AlwaysPresent = true;
Alpha = 0;
}

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit
/// <summary>
/// Invoked when this <see cref="SelectionBlueprint"/> has requested drag.
/// </summary>
public event Action<DragEvent> DragRequested;
public event Action<SelectionBlueprint, DragEvent> DragRequested;
/// <summary>
/// The <see cref="DrawableHitObject"/> which this <see cref="SelectionBlueprint"/> applies to.
@ -130,12 +130,10 @@ namespace osu.Game.Rulesets.Edit
protected override bool OnDrag(DragEvent e)
{
DragRequested?.Invoke(e);
DragRequested?.Invoke(this, e);
return true;
}
public abstract void AdjustPosition(DragEvent dragEvent);
/// <summary>
/// The screen-space point that causes this <see cref="SelectionBlueprint"/> to be selected.
/// </summary>

View File

@ -84,6 +84,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
public override bool RemoveCompletedTransforms => false;
protected override bool RequiresChildrenUpdate => true;
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Clock?.CurrentTime >= LifetimeStart;
public readonly Bindable<ArmedState> State = new Bindable<ArmedState>();
protected DrawableHitObject(HitObject hitObject)

View File

@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.UI
/// Remove a DrawableHitObject from this Playfield.
/// </summary>
/// <param name="h">The DrawableHitObject to remove.</param>
public virtual void Remove(DrawableHitObject h) => HitObjectContainer.Remove(h);
public virtual bool Remove(DrawableHitObject h) => HitObjectContainer.Remove(h);
/// <summary>
/// Registers a <see cref="Playfield"/> as a nested <see cref="Playfield"/>.

View File

@ -13,6 +13,7 @@ using osu.Framework.Timing;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK.Input;
namespace osu.Game.Screens.Edit.Components
{
@ -63,6 +64,18 @@ namespace osu.Game.Screens.Edit.Components
tabs.Current.ValueChanged += newValue => Beatmap.Value.Track.Tempo.Value = newValue;
}
protected override bool OnKeyDown(KeyDownEvent e)
{
switch (e.Key)
{
case Key.Space:
togglePause();
return true;
}
return base.OnKeyDown(e);
}
private void togglePause()
{
if (adjustableClock.IsRunning)

View File

@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
private Container<PlacementBlueprint> placementBlueprintContainer;
private PlacementBlueprint currentPlacement;
private SelectionBox selectionBox;
private SelectionHandler selectionHandler;
private IEnumerable<SelectionBlueprint> selections => selectionBlueprints.Children.Where(c => c.IsAlive);
@ -37,16 +37,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
[BackgroundDependencyLoader]
private void load()
{
selectionBox = composer.CreateSelectionBox();
selectionBox.DeselectAll = deselectAll;
selectionHandler = composer.CreateSelectionHandler();
selectionHandler.DeselectAll = deselectAll;
var dragBox = new DragBox(select);
dragBox.DragEnd += () => selectionBox.UpdateVisibility();
dragBox.DragEnd += () => selectionHandler.UpdateVisibility();
InternalChildren = new[]
{
dragBox,
selectionBox,
selectionHandler,
selectionBlueprints = new SelectionBlueprintContainer { RelativeSizeAxes = Axes.Both },
placementBlueprintContainer = new Container<PlacementBlueprint> { RelativeSizeAxes = Axes.Both },
dragBox.CreateProxy()
@ -168,19 +168,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void onBlueprintSelected(SelectionBlueprint blueprint)
{
selectionBox.HandleSelected(blueprint);
selectionHandler.HandleSelected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 1);
}
private void onBlueprintDeselected(SelectionBlueprint blueprint)
{
selectionBox.HandleDeselected(blueprint);
selectionHandler.HandleDeselected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 0);
}
private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionBox.HandleSelectionRequested(blueprint, state);
private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state);
private void onDragRequested(DragEvent dragEvent) => selectionBox.HandleDrag(dragEvent);
private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) => selectionHandler.HandleDrag(blueprint, dragEvent);
private class SelectionBlueprintContainer : Container<SelectionBlueprint>
{

View File

@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
Masking = true,
BorderColour = Color4.White,
BorderThickness = SelectionBox.BORDER_RADIUS,
BorderThickness = SelectionHandler.BORDER_RADIUS,
Child = new Box
{
RelativeSizeAxes = Axes.Both,

View File

@ -12,26 +12,31 @@ using osu.Framework.Input.Events;
using osu.Framework.Input.States;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
using osuTK.Input;
namespace osu.Game.Screens.Edit.Compose.Components
{
/// <summary>
/// A box which surrounds <see cref="SelectionBlueprint"/>s and provides interactive handles, context menus etc.
/// A component which outlines <see cref="DrawableHitObject"/>s and handles movement of selections.
/// </summary>
public class SelectionBox : CompositeDrawable
public class SelectionHandler : CompositeDrawable
{
public const float BORDER_RADIUS = 2;
protected IEnumerable<SelectionBlueprint> SelectedBlueprints => selectedBlueprints;
private readonly List<SelectionBlueprint> selectedBlueprints;
protected IEnumerable<HitObject> SelectedHitObjects => selectedBlueprints.Select(b => b.HitObject.HitObject);
private Drawable outline;
[Resolved]
private IPlacementHandler placementHandler { get; set; }
public SelectionBox()
public SelectionHandler()
{
selectedBlueprints = new List<SelectionBlueprint>();
@ -59,12 +64,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region User Input Handling
public void HandleDrag(DragEvent dragEvent)
/// <summary>
/// Handles the selected <see cref="DrawableHitObject"/>s being dragged.
/// </summary>
/// <param name="blueprint">The <see cref="SelectionBlueprint"/> that received the drag event.</param>
/// <param name="dragEvent">The drag event.</param>
public virtual void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
{
// Todo: Various forms of snapping
foreach (var blueprint in selectedBlueprints)
blueprint.AdjustPosition(dragEvent);
}
protected override bool OnKeyDown(KeyDownEvent e)
@ -90,19 +96,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary>
/// Bind an action to deselect all selected blueprints.
/// </summary>
public Action DeselectAll { private get; set; }
internal Action DeselectAll { private get; set; }
/// <summary>
/// Handle a blueprint becoming selected.
/// </summary>
/// <param name="blueprint">The blueprint.</param>
public void HandleSelected(SelectionBlueprint blueprint) => selectedBlueprints.Add(blueprint);
internal void HandleSelected(SelectionBlueprint blueprint) => selectedBlueprints.Add(blueprint);
/// <summary>
/// Handle a blueprint becoming deselected.
/// </summary>
/// <param name="blueprint">The blueprint.</param>
public void HandleDeselected(SelectionBlueprint blueprint)
internal void HandleDeselected(SelectionBlueprint blueprint)
{
selectedBlueprints.Remove(blueprint);
@ -115,7 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// Handle a blueprint requesting selection.
/// </summary>
/// <param name="blueprint">The blueprint.</param>
public void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state)
internal void HandleSelectionRequested(SelectionBlueprint blueprint, InputState state)
{
if (state.Keyboard.ControlPressed)
{
@ -139,7 +145,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
#endregion
/// <summary>
/// Updates whether this <see cref="SelectionBox"/> is visible.
/// Updates whether this <see cref="SelectionHandler"/> is visible.
/// </summary>
internal void UpdateVisibility()
{

View File

@ -20,6 +20,7 @@ using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Edit.Components.Menus;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Design;
using osuTK.Input;
namespace osu.Game.Screens.Edit
{
@ -157,29 +158,19 @@ namespace osu.Game.Screens.Edit
bottomBackground.Colour = colours.Gray2;
}
private void exportBeatmap()
protected override bool OnKeyDown(KeyDownEvent e)
{
host.OpenFileExternally(Beatmap.Value.Save());
}
private void onModeChanged(EditorScreenMode mode)
{
currentScreen?.Exit();
switch (mode)
switch (e.Key)
{
case EditorScreenMode.Compose:
currentScreen = new ComposeScreen();
break;
case EditorScreenMode.Design:
currentScreen = new DesignScreen();
break;
default:
currentScreen = new EditorScreen();
break;
case Key.Left:
seek(e, -1);
return true;
case Key.Right:
seek(e, 1);
return true;
}
LoadComponentAsync(currentScreen, screenContainer.Add);
return base.OnKeyDown(e);
}
private double scrollAccumulation;
@ -193,9 +184,9 @@ namespace osu.Game.Screens.Edit
while (Math.Abs(scrollAccumulation) > precision)
{
if (scrollAccumulation > 0)
clock.SeekBackward(!clock.IsRunning);
seek(e, -1);
else
clock.SeekForward(!clock.IsRunning);
seek(e, 1);
scrollAccumulation = scrollAccumulation < 0 ? Math.Min(0, scrollAccumulation + precision) : Math.Max(0, scrollAccumulation - precision);
}
@ -224,7 +215,40 @@ namespace osu.Game.Screens.Edit
Beatmap.Value.Track.Tempo.Value = 1;
Beatmap.Value.Track.Start();
}
return base.OnExiting(next);
}
private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save());
private void onModeChanged(EditorScreenMode mode)
{
currentScreen?.Exit();
switch (mode)
{
case EditorScreenMode.Compose:
currentScreen = new ComposeScreen();
break;
case EditorScreenMode.Design:
currentScreen = new DesignScreen();
break;
default:
currentScreen = new EditorScreen();
break;
}
LoadComponentAsync(currentScreen, screenContainer.Add);
}
private void seek(UIEvent e, int direction)
{
double amount = e.ShiftPressed ? 2 : 1;
if (direction < 1)
clock.SeekBackward(!clock.IsRunning, amount);
else
clock.SeekForward(!clock.IsRunning, amount);
}
}
}

View File

@ -68,16 +68,20 @@ namespace osu.Game.Screens.Edit
/// Seeks backwards by one beat length.
/// </summary>
/// <param name="snapped">Whether to snap to the closest beat after seeking.</param>
public void SeekBackward(bool snapped = false) => seek(-1, snapped);
/// <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);
/// <summary>
/// Seeks forwards by one beat length.
/// </summary>
/// <param name="snapped">Whether to snap to the closest beat after seeking.</param>
public void SeekForward(bool snapped = false) => seek(1, snapped);
/// <param name="amount">The relative amount (magnitude) which should be seeked.</param>
public void SeekForward(bool snapped = false, double amount = 1) => seek(1, snapped, amount);
private void seek(int direction, bool snapped)
private void seek(int direction, bool snapped, double amount = 1)
{
if (amount <= 0) throw new ArgumentException("Value should be greater than zero", nameof(amount));
var timingPoint = ControlPointInfo.TimingPointAt(CurrentTime);
if (direction < 0 && timingPoint.Time == CurrentTime)
{
@ -87,7 +91,7 @@ namespace osu.Game.Screens.Edit
timingPoint = ControlPointInfo.TimingPoints[--activeIndex];
}
double seekAmount = timingPoint.BeatLength / beatDivisor;
double seekAmount = timingPoint.BeatLength / beatDivisor * amount;
double seekTime = CurrentTime + seekAmount * direction;
if (!snapped || ControlPointInfo.TimingPoints.Count == 0)

View File

@ -15,18 +15,14 @@ namespace osu.Game.Tests.Visual
[Cached(Type = typeof(IPlacementHandler))]
public abstract class PlacementBlueprintTestCase : OsuTestCase, IPlacementHandler
{
private readonly Container hitObjectContainer;
protected readonly Container HitObjectContainer;
private PlacementBlueprint currentBlueprint;
protected PlacementBlueprintTestCase()
{
Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = 2;
Add(hitObjectContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedClock(new StopwatchClock())
});
Add(HitObjectContainer = CreateHitObjectContainer());
}
[BackgroundDependencyLoader]
@ -49,7 +45,7 @@ namespace osu.Game.Tests.Visual
public void EndPlacement(HitObject hitObject)
{
hitObjectContainer.Add(CreateHitObject(hitObject));
AddHitObject(CreateHitObject(hitObject));
Remove(currentBlueprint);
Add(currentBlueprint = CreateBlueprint());
@ -59,6 +55,10 @@ namespace osu.Game.Tests.Visual
{
}
protected virtual Container CreateHitObjectContainer() => new Container { RelativeSizeAxes = Axes.Both };
protected virtual void AddHitObject(DrawableHitObject hitObject) => HitObjectContainer.Add(hitObject);
protected abstract DrawableHitObject CreateHitObject(HitObject hitObject);
protected abstract PlacementBlueprint CreateBlueprint();
}

View File

@ -24,6 +24,8 @@ namespace osu.Game.Tests.Visual
public double TimeRange { set => scrollingInfo.TimeRange.Value = value; }
public IScrollingInfo ScrollingInfo => scrollingInfo;
[Cached(Type = typeof(IScrollingInfo))]
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();

View File

@ -24,4 +24,4 @@
<PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
</ItemGroup>
</Project>
</Project>