Merge branch 'master' into editor-wheel-movement

This commit is contained in:
Dean Herbert
2018-03-16 15:56:51 +09:00
committed by GitHub
114 changed files with 866 additions and 588 deletions

View File

@ -9,6 +9,7 @@ using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization;
using Newtonsoft.Json;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO.Serialization.Converters;
namespace osu.Game.Beatmaps
@ -16,14 +17,14 @@ namespace osu.Game.Beatmaps
/// <summary>
/// A Beatmap containing converted HitObjects.
/// </summary>
public class Beatmap<T> : IJsonSerializable
public class Beatmap<T> : IJsonSerializable, IHasComboColours
where T : HitObject
{
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public List<Color4> ComboColors = new List<Color4>
public List<Color4> ComboColours { get; set; } = new List<Color4>
{
new Color4(17, 136, 170, 255),
new Color4(102, 136, 0, 255),
@ -55,7 +56,7 @@ namespace osu.Game.Beatmaps
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
ComboColours = original?.ComboColours ?? ComboColours;
HitObjects = original?.HitObjects ?? HitObjects;
if (original == null && Metadata == null)

View File

@ -50,9 +50,14 @@ namespace osu.Game.Beatmaps
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
{
var beatmap = CreateBeatmap();
// todo: this *must* share logic (or directly use) Beatmap<T>'s constructor.
// right now this isn't easily possible due to generic entanglement.
beatmap.BeatmapInfo = original.BeatmapInfo;
beatmap.ControlPointInfo = original.ControlPointInfo;
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
beatmap.Breaks = original.Breaks;
beatmap.ComboColours = original.ComboColours;
return beatmap;
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
public interface IHasComboColours
{
List<Color4> ComboColours { get; set; }
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
public interface IHasCustomColours
{
Dictionary<string, Color4> CustomColours { get; set; }
}
}

View File

@ -5,7 +5,6 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
@ -19,7 +18,6 @@ namespace osu.Game.Beatmaps.Formats
private Beatmap beatmap;
private bool hasCustomColours;
private ConvertHitObjectParser parser;
private LegacySampleBank defaultSampleBank;
@ -72,29 +70,28 @@ namespace osu.Game.Beatmaps.Formats
{
case Section.General:
handleGeneral(line);
break;
return;
case Section.Editor:
handleEditor(line);
break;
return;
case Section.Metadata:
handleMetadata(line);
break;
return;
case Section.Difficulty:
handleDifficulty(line);
break;
return;
case Section.Events:
handleEvents(line);
break;
return;
case Section.TimingPoints:
handleTimingPoints(line);
break;
case Section.Colours:
handleColours(line);
break;
return;
case Section.HitObjects:
handleHitObjects(line);
break;
return;
}
base.ParseLine(beatmap, section, line);
}
private void handleGeneral(string line)
@ -364,38 +361,6 @@ namespace osu.Game.Beatmaps.Formats
}
}
private void handleColours(string line)
{
var pair = SplitKeyVal(line, ':');
string[] split = pair.Value.Split(',');
if (split.Length != 3)
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
byte r, g, b;
if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
if (!hasCustomColours)
{
beatmap.ComboColors.Clear();
hasCustomColours = true;
}
// Note: the combo index specified in the beatmap is discarded
if (pair.Key.StartsWith(@"Combo"))
{
beatmap.ComboColors.Add(new Color4
{
R = r / 255f,
G = g / 255f,
B = b / 255f,
A = 1f,
});
}
}
private void handleHitObjects(string line)
{
// If the ruleset wasn't specified, assume the osu!standard ruleset.

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats
{
@ -40,7 +41,53 @@ namespace osu.Game.Beatmaps.Formats
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
protected abstract void ParseLine(T output, Section section, string line);
protected virtual void ParseLine(T output, Section section, string line)
{
switch (section)
{
case Section.Colours:
handleColours(output, line);
return;
}
}
private bool hasComboColours;
private void handleColours(T output, string line)
{
var pair = SplitKeyVal(line, ':');
bool isCombo = pair.Key.StartsWith(@"Combo");
string[] split = pair.Value.Split(',');
if (split.Length != 3)
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b))
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
Color4 colour = new Color4(r, g, b, 255);
if (isCombo)
{
if (!(output is IHasComboColours tHasComboColours)) return;
if (!hasComboColours)
{
// remove default colours.
tHasComboColours.ComboColours.Clear();
hasComboColours = true;
}
tHasComboColours.ComboColours.Add(colour);
}
else
{
if (!(output is IHasCustomColours tHasCustomColours)) return;
tHasCustomColours.CustomColours[pair.Key] = colour;
}
}
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
{
@ -65,6 +112,7 @@ namespace osu.Game.Beatmaps.Formats
Colours,
HitObjects,
Variables,
Fonts
}
internal enum LegacySampleBank

View File

@ -46,11 +46,13 @@ namespace osu.Game.Beatmaps.Formats
{
case Section.Events:
handleEvents(line);
break;
return;
case Section.Variables:
handleVariables(line);
break;
return;
}
base.ParseLine(storyboard, section, line);
}
private void handleEvents(string line)

View File

@ -8,15 +8,17 @@ using System.Diagnostics;
using System.Net;
using System.Threading;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Game.Configuration;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
namespace osu.Game.Online.API
{
public class APIAccess : IAPIProvider
public class APIAccess : Component, IAPIProvider
{
private readonly OsuConfigManager config;
private readonly OAuth authentication;
public string Endpoint = @"https://osu.ppy.sh";
@ -25,13 +27,12 @@ namespace osu.Game.Online.API
private ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
public Scheduler Scheduler = new Scheduler();
/// <summary>
/// The username/email provided by the user when initiating a login.
/// </summary>
public string ProvidedUsername { get; private set; }
public string Username;
//private SecurePassword password;
public string Password;
private string password;
public Bindable<User> LocalUser { get; } = new Bindable<User>(createGuestUser());
@ -41,24 +42,31 @@ namespace osu.Game.Online.API
set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); }
}
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password);
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password);
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference).
private readonly Thread thread;
private readonly Logger log;
public APIAccess()
public APIAccess(OsuConfigManager config)
{
this.config = config;
authentication = new OAuth(client_id, client_secret, Endpoint);
log = Logger.GetLogger(LoggingTarget.Network);
ProvidedUsername = config.Get<string>(OsuSetting.Username);
Token = config.Get<string>(OsuSetting.Token);
thread = new Thread(run) { IsBackground = true };
thread.Start();
}
private readonly List<IOnlineComponent> components = new List<IOnlineComponent>();
internal void Schedule(Action action) => base.Schedule(action);
public void Register(IOnlineComponent component)
{
Scheduler.Add(delegate
@ -111,12 +119,15 @@ namespace osu.Game.Online.API
State = APIState.Connecting;
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password))
// save the username at this point, if the user requested for it to be.
config.Set(OsuSetting.Username, config.Get<bool>(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty);
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(ProvidedUsername, password))
{
//todo: this fails even on network-related issues. we should probably handle those differently.
//NotificationOverlay.ShowMessage("Login failed!");
log.Add(@"Login failed!");
Password = null;
password = null;
authentication.Clear();
continue;
}
@ -173,8 +184,8 @@ namespace osu.Game.Online.API
{
Debug.Assert(State == APIState.Offline);
Username = username;
Password = password;
ProvidedUsername = username;
this.password = password;
}
/// <summary>
@ -283,8 +294,8 @@ namespace osu.Game.Online.API
public void Logout(bool clearUsername = true)
{
flushQueue();
if (clearUsername) Username = null;
Password = null;
if (clearUsername) ProvidedUsername = null;
password = null;
authentication.Clear();
LocalUser.Value = createGuestUser();
}
@ -295,9 +306,12 @@ namespace osu.Game.Online.API
Id = 1,
};
public void Update()
protected override void Dispose(bool isDisposing)
{
Scheduler.Update();
base.Dispose(isDisposing);
config.Set(OsuSetting.Token, config.Get<bool>(OsuSetting.SavePassword) ? Token : string.Empty);
config.Save();
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Online.API
return request;
}
private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); });
private void request_Progress(long current, long total) => API.Schedule(() => Progress?.Invoke(current, total));
protected APIDownloadRequest()
{

View File

@ -85,7 +85,7 @@ namespace osu.Game.Online.API
if (checkAndProcessFailure())
return;
api.Scheduler.Add(delegate { Success?.Invoke(); });
api.Schedule(delegate { Success?.Invoke(); });
}
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));
@ -108,7 +108,7 @@ namespace osu.Game.Online.API
{
if (API == null || pendingFailure == null) return cancelled;
API.Scheduler.Add(pendingFailure);
API.Schedule(pendingFailure);
pendingFailure = null;
return true;
}

View File

@ -1,13 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework;
using osu.Framework.Configuration;
using osu.Game.Users;
namespace osu.Game.Online.API
{
public interface IAPIProvider : IUpdateable
public interface IAPIProvider
{
/// <summary>
/// The local user.

View File

@ -34,7 +34,7 @@ using osu.Game.Skinning;
namespace osu.Game
{
public class OsuGameBase : Framework.Game, IOnlineComponent, ICanAcceptFiles
public class OsuGameBase : Framework.Game, ICanAcceptFiles
{
protected OsuConfigManager LocalConfig;
@ -56,8 +56,6 @@ namespace osu.Game
protected override string MainResourceFile => @"osu.Game.Resources.dll";
public APIAccess API;
private Container content;
protected override Container<Drawable> Content => content;
@ -108,16 +106,14 @@ namespace osu.Game
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
dependencies.Cache(API = new APIAccess
{
Username = LocalConfig.Get<string>(OsuSetting.Username),
Token = LocalConfig.Get<string>(OsuSetting.Token)
});
dependencies.CacheAs<IAPIProvider>(API);
var api = new APIAccess(LocalConfig);
dependencies.Cache(api);
dependencies.CacheAs<IAPIProvider>(api);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, API, Host));
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
@ -183,9 +179,9 @@ namespace osu.Game
lastBeatmap = b;
};
API.Register(this);
FileStore.Cleanup();
AddInternal(api);
}
private void runMigrations()
@ -211,16 +207,6 @@ namespace osu.Game
private WorkingBeatmap lastBeatmap;
public void APIStateChanged(APIAccess api, APIState state)
{
switch (state)
{
case APIState.Online:
LocalConfig.Set(OsuSetting.Username, LocalConfig.Get<bool>(OsuSetting.SaveUsername) ? API.Username : string.Empty);
break;
}
}
protected override void LoadComplete()
{
base.LoadComplete();
@ -253,24 +239,6 @@ namespace osu.Game
base.SetHost(host);
}
protected override void Update()
{
base.Update();
API.Update();
}
protected override void Dispose(bool isDisposing)
{
//refresh token may have changed.
if (LocalConfig != null && API != null)
{
LocalConfig.Set(OsuSetting.Token, LocalConfig.Get<bool>(OsuSetting.SavePassword) ? API.Token : string.Empty);
LocalConfig.Save();
}
base.Dispose(isDisposing);
}
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
public void Import(params string[] paths)

View File

@ -186,7 +186,7 @@ namespace osu.Game.Overlays.Direct
progressBar.FadeOut(500);
};
request.DownloadProgressed += progress => progressBar.Current.Value = progress;
request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress);
request.Success += data =>
{

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods
public DifficultyIncreaseSection()
{
Header = @"Gameplay Difficulty Increase";
Header = @"Difficulty Increase";
}
}
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Mods
public DifficultyReductionSection()
{
Header = @"Gameplay Difficulty Reduction";
Header = @"Difficulty Reduction";
}
}
}

View File

@ -287,7 +287,7 @@ namespace osu.Game.Overlays.Mods
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new AssistedSection
new SpecialSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,

View File

@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class AssistedSection : ModSection
public class SpecialSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
public override ModType ModType => ModType.Special;
@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Mods
SelectedColour = colours.BlueLight;
}
public AssistedSection()
public SpecialSection()
{
Header = @"Assisted";
Header = @"Special";
}
}
}

View File

@ -210,7 +210,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
{
PlaceholderText = "Email address",
RelativeSizeAxes = Axes.X,
Text = api?.Username ?? string.Empty,
Text = api?.ProvidedUsername ?? string.Empty,
TabbableContentContainer = this
},
password = new OsuPasswordTextBox

View File

@ -13,10 +13,10 @@ using osu.Framework.Logging;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Layers;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Screens.Compose.Layers;
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
namespace osu.Game.Rulesets.Edit
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Edit
return;
}
HitObjectOverlayLayer hitObjectOverlayLayer = CreateHitObjectOverlayLayer();
HitObjectMaskLayer hitObjectMaskLayer = new HitObjectMaskLayer(this);
SelectionLayer selectionLayer = new SelectionLayer(rulesetContainer.Playfield);
var layerBelowRuleset = new BorderLayer
@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Edit
layerAboveRuleset.Children = new Drawable[]
{
selectionLayer, // Below object overlays for input
hitObjectOverlayLayer,
hitObjectMaskLayer,
selectionLayer.CreateProxy() // Proxy above object overlays for selections
};
@ -121,8 +121,10 @@ namespace osu.Game.Rulesets.Edit
}
};
selectionLayer.ObjectSelected += hitObjectOverlayLayer.AddOverlay;
selectionLayer.ObjectDeselected += hitObjectOverlayLayer.RemoveOverlay;
selectionLayer.ObjectSelected += hitObjectMaskLayer.AddOverlay;
selectionLayer.ObjectDeselected += hitObjectMaskLayer.RemoveOverlay;
selectionLayer.SelectionCleared += hitObjectMaskLayer.RemoveSelectionOverlay;
selectionLayer.SelectionFinished += hitObjectMaskLayer.AddSelectionOverlay;
toolboxCollection.Items =
new[] { new RadioButton("Select", () => setCompositionTool(null)) }
@ -259,14 +261,22 @@ namespace osu.Game.Rulesets.Edit
protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; }
/// <summary>
/// Creates a <see cref="HitObjectMask"/> for a specific <see cref="DrawableHitObject"/>.
/// </summary>
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
public virtual HitObjectMask CreateMaskFor(DrawableHitObject hitObject) => null;
/// <summary>
/// Creates a <see cref="SelectionBox"/> which outlines <see cref="DrawableHitObject"/>s
/// and handles all hitobject movement/pattern adjustments.
/// </summary>
/// <param name="overlays">The <see cref="DrawableHitObject"/> overlays.</param>
public virtual SelectionBox CreateSelectionOverlay(IReadOnlyList<HitObjectMask> overlays) => new SelectionBox(overlays);
/// <summary>
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
/// </summary>
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
/// <summary>
/// Creates the <see cref="HitObjectOverlayLayer"/> which overlays selected <see cref="DrawableHitObject"/>s.
/// </summary>
protected virtual HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new HitObjectOverlayLayer();
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Edit
{
/// <summary>
/// A mask placed above a <see cref="DrawableHitObject"/> adding editing functionality.
/// </summary>
public class HitObjectMask : Container
{
public readonly DrawableHitObject HitObject;
public HitObjectMask(DrawableHitObject hitObject)
{
HitObject = hitObject;
}
}
}

View File

@ -1,68 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
namespace osu.Game.Rulesets.Edit.Layers.Selection
{
/// <summary>
/// A box which encloses <see cref="DrawableHitObject"/>s.
/// </summary>
public class CaptureBox : VisibilityContainer
{
private readonly IDrawable captureArea;
private readonly IReadOnlyList<DrawableHitObject> capturedObjects;
public CaptureBox(IDrawable captureArea, IReadOnlyList<DrawableHitObject> capturedObjects)
{
this.captureArea = captureArea;
this.capturedObjects = capturedObjects;
Masking = true;
BorderThickness = SelectionBox.BORDER_RADIUS;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
AlwaysPresent = true,
Alpha = 0
};
State = Visibility.Visible;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BorderColour = colours.Yellow;
// Move the rectangle to cover the hitobjects
var topLeft = new Vector2(float.MaxValue, float.MaxValue);
var bottomRight = new Vector2(float.MinValue, float.MinValue);
foreach (var obj in capturedObjects)
{
topLeft = Vector2.ComponentMin(topLeft, captureArea.ToLocalSpace(obj.SelectionQuad.TopLeft));
bottomRight = Vector2.ComponentMax(bottomRight, captureArea.ToLocalSpace(obj.SelectionQuad.BottomRight));
}
topLeft -= new Vector2(5);
bottomRight += new Vector2(5);
Size = bottomRight - topLeft;
Position = topLeft;
}
public override bool DisposeOnDeathRemoval => true;
protected override void PopIn() => this.FadeIn();
protected override void PopOut() => this.FadeOut();
}
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Edit.Layers.Selection
{
public class HitObjectOverlay : OverlayContainer
{
// ReSharper disable once NotAccessedField.Local
// This will be used later to handle drag movement, etc
private readonly DrawableHitObject hitObject;
public HitObjectOverlay(DrawableHitObject hitObject)
{
this.hitObject = hitObject;
State = Visibility.Visible;
}
protected override void PopIn() => Alpha = 1;
protected override void PopOut() => Alpha = 0;
}
}

View File

@ -1,49 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Edit.Layers.Selection
{
/// <summary>
/// A box that represents a drag selection.
/// </summary>
public class SelectionBox : VisibilityContainer
{
public const float BORDER_RADIUS = 2;
/// <summary>
/// Creates a new <see cref="SelectionBox"/>.
/// </summary>
public SelectionBox()
{
Masking = true;
BorderColour = Color4.White;
BorderThickness = BORDER_RADIUS;
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.1f
};
}
public void SetDragRectangle(RectangleF rectangle)
{
var topLeft = Parent.ToLocalSpace(rectangle.TopLeft);
var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight);
Position = topLeft;
Size = bottomRight - topLeft;
}
public override bool DisposeOnDeathRemoval => true;
protected override void PopIn() => this.FadeIn(250, Easing.OutQuint);
protected override void PopOut() => this.FadeOut(250, Easing.OutQuint);
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Edit.Types
{
public interface IHasEditablePosition : IHasPosition
{
void OffsetPosition(Vector2 offset);
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Autoplay";
public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => "Watch a perfect automated play through the song";
public override string Description => "Watch a perfect automated play through the song.";
public override double ScoreMultiplier => 0;
public bool AllowFail => false;
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };

View File

@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mods
public override string ShortenedName => "CN";
public override bool HasImplementation => false;
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
public override string Description => "Watch the video without visual distractions.";
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Daycore";
public override string ShortenedName => "DC";
public override FontAwesome Icon => FontAwesome.fa_question;
public override string Description => "whoaaaaa";
public override string Description => "Whoaaaaa...";
public override void ApplyToClock(IAdjustableClock clock)
{

View File

@ -7,18 +7,16 @@ using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mods
{
public class ModDoubleTime : Mod, IApplicableToClock
public abstract class ModDoubleTime : Mod, IApplicableToClock
{
public override string Name => "Double Time";
public override string ShortenedName => "DT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Zoooooooooom";
public override string Description => "Zoooooooooom...";
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) };
public override double ScoreMultiplier => 1.12;
public virtual void ApplyToClock(IAdjustableClock clock)
{
clock.Rate = 1.5;

View File

@ -13,7 +13,6 @@ namespace osu.Game.Rulesets.Mods
public override string ShortenedName => "EZ";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required.";
public override double ScoreMultiplier => 0.5;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };

View File

@ -13,12 +13,10 @@ namespace osu.Game.Rulesets.Mods
public override string ShortenedName => "HT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => "Less zoom";
public override string Description => "Less zoom...";
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) };
public override double ScoreMultiplier => 1.12;
public virtual void ApplyToClock(IAdjustableClock clock)
{
clock.Rate = 0.75;

View File

@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mods
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Everything just got a bit harder...";
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModEasy) };
public void ApplyToDifficulty(BeatmapDifficulty difficulty)

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => "Nightcore";
public override string ShortenedName => "NC";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore;
public override string Description => "uguuuuuuuu";
public override string Description => "Uguuuuuuuu...";
public override void ApplyToClock(IAdjustableClock clock)
{

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods
{
public abstract class ModNoFail : Mod, IApplicableFailOverride
{
public override string Name => "NoFail";
public override string Name => "No Fail";
public override string ShortenedName => "NF";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override ModType Type => ModType.DifficultyReduction;

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mods
@ -9,6 +10,7 @@ namespace osu.Game.Rulesets.Mods
{
public override string Name => "Perfect";
public override string ShortenedName => "PF";
public override FontAwesome Icon => FontAwesome.fa_question;
public override string Description => "SS or quit.";
protected override bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Accuracy.Value != 1;

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods
public override string ShortenedName => "SD";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Miss a note and fail.";
public override string Description => "Miss and fail.";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };

View File

@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mods
public override string Name => string.Empty;
public override string ShortenedName => string.Empty;
public override string Description => string.Empty;
public override double ScoreMultiplier => 0.0;
public override double ScoreMultiplier => 0;
public Mod[] Mods;
}

View File

@ -6,7 +6,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Edit.Layers
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class BorderLayer : Container
{

View File

@ -1,20 +1,25 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Edit.Layers.Selection
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class HitObjectOverlayLayer : CompositeDrawable
public class HitObjectMaskLayer : CompositeDrawable
{
private readonly Dictionary<DrawableHitObject, HitObjectOverlay> existingOverlays = new Dictionary<DrawableHitObject, HitObjectOverlay>();
private readonly HitObjectComposer composer;
private readonly Container<HitObjectMask> overlayContainer;
public HitObjectOverlayLayer()
public HitObjectMaskLayer(HitObjectComposer composer)
{
this.composer = composer;
RelativeSizeAxes = Axes.Both;
InternalChild = overlayContainer = new Container<HitObjectMask> { RelativeSizeAxes = Axes.Both };
}
/// <summary>
@ -23,12 +28,11 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create an overlay for.</param>
public void AddOverlay(DrawableHitObject hitObject)
{
var overlay = CreateOverlayFor(hitObject);
var overlay = composer.CreateMaskFor(hitObject);
if (overlay == null)
return;
existingOverlays[hitObject] = overlay;
AddInternal(overlay);
overlayContainer.Add(overlay);
}
/// <summary>
@ -37,17 +41,22 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to remove the overlay for.</param>
public void RemoveOverlay(DrawableHitObject hitObject)
{
if (!existingOverlays.TryGetValue(hitObject, out var existing))
var existing = overlayContainer.FirstOrDefault(h => h.HitObject == hitObject);
if (existing == null)
return;
existing.Hide();
existing.Expire();
}
/// <summary>
/// Creates a <see cref="HitObjectOverlay"/> for a specific <see cref="DrawableHitObject"/>.
/// </summary>
/// <param name="hitObject">The <see cref="DrawableHitObject"/> to create the overlay for.</param>
protected virtual HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject) => null;
private SelectionBox currentSelectionBox;
public void AddSelectionOverlay() => AddInternal(currentSelectionBox = composer.CreateSelectionOverlay(overlayContainer));
public void RemoveSelectionOverlay()
{
currentSelectionBox?.Hide();
currentSelectionBox?.Expire();
}
}
}

View File

@ -0,0 +1,102 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Types;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
/// <summary>
/// A box which surrounds <see cref="DrawableHitObject"/>s and provides interactive handles, context menus etc.
/// </summary>
public class SelectionBox : VisibilityContainer
{
private readonly IReadOnlyList<HitObjectMask> overlays;
public const float BORDER_RADIUS = 2;
public SelectionBox(IReadOnlyList<HitObjectMask> overlays)
{
this.overlays = overlays;
Masking = true;
BorderThickness = BORDER_RADIUS;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
AlwaysPresent = true,
Alpha = 0
};
State = Visibility.Visible;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BorderColour = colours.Yellow;
}
protected override void Update()
{
base.Update();
// Todo: We might need to optimise this
// Move the rectangle to cover the hitobjects
var topLeft = new Vector2(float.MaxValue, float.MaxValue);
var bottomRight = new Vector2(float.MinValue, float.MinValue);
foreach (var obj in overlays)
{
topLeft = Vector2.ComponentMin(topLeft, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.TopLeft));
bottomRight = Vector2.ComponentMax(bottomRight, Parent.ToLocalSpace(obj.HitObject.SelectionQuad.BottomRight));
}
topLeft -= new Vector2(5);
bottomRight += new Vector2(5);
Size = bottomRight - topLeft;
Position = topLeft;
}
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => overlays.Any(o => o.ReceiveMouseInputAt(screenSpacePos));
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
protected override bool OnDragStart(InputState state) => true;
protected override bool OnDrag(InputState state)
{
// Todo: Various forms of snapping
foreach (var hitObject in overlays.Select(o => o.HitObject.HitObject))
{
switch (hitObject)
{
case IHasEditablePosition editablePosition:
editablePosition.OffsetPosition(state.Mouse.Delta);
break;
}
}
return true;
}
protected override bool OnDragEnd(InputState state) => true;
public override bool DisposeOnDeathRemoval => true;
protected override void PopIn() => this.FadeIn();
protected override void PopOut() => this.FadeOut();
}
}

View File

@ -8,12 +8,14 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Edit.Layers.Selection
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class SelectionLayer : CompositeDrawable
{
@ -27,6 +29,16 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
/// </summary>
public event Action<DrawableHitObject> ObjectDeselected;
/// <summary>
/// Invoked when the selection has been cleared.
/// </summary>
public event Action SelectionCleared;
/// <summary>
/// Invoked when the user has finished selecting all <see cref="DrawableHitObject"/>s.
/// </summary>
public event Action SelectionFinished;
private readonly Playfield playfield;
public SelectionLayer(Playfield playfield)
@ -36,8 +48,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
RelativeSizeAxes = Axes.Both;
}
private SelectionBox selectionBox;
private CaptureBox captureBox;
private DragBox dragBox;
private readonly HashSet<DrawableHitObject> selectedHitObjects = new HashSet<DrawableHitObject>();
@ -49,20 +60,20 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
protected override bool OnDragStart(InputState state)
{
AddInternal(selectionBox = new SelectionBox());
AddInternal(dragBox = new DragBox());
return true;
}
protected override bool OnDrag(InputState state)
{
selectionBox.Show();
dragBox.Show();
var dragPosition = state.Mouse.NativeState.Position;
var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition;
var screenSpaceDragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y);
selectionBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat);
dragBox.SetDragRectangle(screenSpaceDragQuad.AABBFloat);
selectQuad(screenSpaceDragQuad);
return true;
@ -70,8 +81,8 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
protected override bool OnDragEnd(InputState state)
{
selectionBox.Hide();
selectionBox.Expire();
dragBox.Hide();
dragBox.Expire();
finishSelection();
@ -95,7 +106,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
if (!select(hitObject))
return;
clearCapture();
clearSelection();
finishSelection();
}
@ -122,7 +133,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
if (!deselect(hitObject))
return;
clearCapture();
clearSelection();
finishSelection();
}
@ -148,7 +159,7 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
selectedHitObjects.ForEach(h => ObjectDeselected?.Invoke(h));
selectedHitObjects.Clear();
clearCapture();
clearSelection();
}
/// <summary>
@ -180,18 +191,49 @@ namespace osu.Game.Rulesets.Edit.Layers.Selection
select(target);
}
private void clearCapture()
{
captureBox?.Hide();
captureBox?.Expire();
}
private void clearSelection() => SelectionCleared?.Invoke();
private void finishSelection()
{
if (selectedHitObjects.Count == 0)
return;
SelectionFinished?.Invoke();
}
AddInternal(captureBox = new CaptureBox(this, selectedHitObjects.ToList()));
/// <summary>
/// A box that represents a drag selection.
/// </summary>
private class DragBox : VisibilityContainer
{
/// <summary>
/// Creates a new <see cref="DragBox"/>.
/// </summary>
public DragBox()
{
Masking = true;
BorderColour = Color4.White;
BorderThickness = SelectionBox.BORDER_RADIUS;
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.1f
};
}
public void SetDragRectangle(RectangleF rectangle)
{
var topLeft = Parent.ToLocalSpace(rectangle.TopLeft);
var bottomRight = Parent.ToLocalSpace(rectangle.BottomRight);
Position = topLeft;
Size = bottomRight - topLeft;
}
public override bool DisposeOnDeathRemoval => true;
protected override void PopIn() => this.FadeIn(250, Easing.OutQuint);
protected override void PopOut() => this.FadeOut(250, Easing.OutQuint);
}
}
}

View File

@ -8,6 +8,9 @@ using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Screens
{
/// <summary>
/// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor.
/// </summary>
public class EditorScreen : Container
{
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();

View File

@ -18,7 +18,7 @@ using System.Collections.Generic;
namespace osu.Game.Screens.Play
{
public abstract class GameplayMenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition
public abstract class GameplayMenuOverlay : OverlayContainer
{
private const int transition_duration = 200;
private const int button_height = 70;

View File

@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play
Alpha = 0,
},
RulesetContainer,
new SkipButton(firstObjectTime)
new SkipOverlay(firstObjectTime)
{
Clock = Clock, // skip button doesn't want to use the audio clock directly
ProcessCustomClock = false,

View File

@ -5,12 +5,14 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings;
@ -21,7 +23,6 @@ namespace osu.Game.Screens.Play
private Player player;
private BeatmapMetadataDisplay info;
private VisualSettings visualSettings;
private bool showOverlays = true;
public override bool ShowOverlaysOnEnter => showOverlays;
@ -46,7 +47,8 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
Add(visualSettings = new VisualSettings
Add(new VisualSettings
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@ -93,7 +95,7 @@ namespace osu.Game.Screens.Play
contentIn();
info.Delay(750).FadeIn(500);
this.Delay(2150).Schedule(pushWhenLoaded);
this.Delay(1800).Schedule(pushWhenLoaded);
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
@ -109,29 +111,65 @@ namespace osu.Game.Screens.Play
logo.Delay(resuming ? 0 : 500).MoveToOffset(new Vector2(0, -0.24f), 500, Easing.InOutExpo);
}
private bool weHandledMouseDown;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
weHandledMouseDown = true;
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
weHandledMouseDown = false;
return base.OnMouseUp(state, args);
}
private ScheduledDelegate pushDebounce;
private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && (!GetContainingInputManager().CurrentState.Mouse.HasAnyButtonPressed || weHandledMouseDown);
private void pushWhenLoaded()
{
if (player.LoadState != LoadState.Ready || visualSettings.IsHovered)
if (!IsCurrentScreen) return;
try
{
if (!readyForPush)
{
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
// if we become unready for push during the delay.
pushDebounce?.Cancel();
pushDebounce = null;
return;
}
if (pushDebounce != null)
return;
pushDebounce = Scheduler.AddDelayed(() =>
{
contentOut();
this.Delay(250).Schedule(() =>
{
if (!IsCurrentScreen) return;
if (!Push(player))
Exit();
else
{
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
ValidForResume = false;
}
});
}, 500);
}
finally
{
Schedule(pushWhenLoaded);
return;
}
contentOut();
this.Delay(250).Schedule(() =>
{
if (!IsCurrentScreen) return;
if (!Push(player))
Exit();
else
{
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
ValidForResume = false;
}
});
}
protected override bool OnExiting(Screen next)

View File

@ -5,6 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@ -133,5 +134,8 @@ namespace osu.Game.Screens.Play.PlayerSettings
}
protected override Container<Drawable> Content => content;
protected override bool OnHover(InputState state) => true;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
}
}

View File

@ -21,7 +21,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Screens.Play
{
public class SkipButton : OverlayContainer, IKeyBindingHandler<GlobalAction>
public class SkipOverlay : OverlayContainer, IKeyBindingHandler<GlobalAction>
{
private readonly double startTime;
@ -35,8 +35,9 @@ namespace osu.Game.Screens.Play
private double displayTime;
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
protected override bool BlockPassThroughMouse => false;
public SkipButton(double startTime)
public SkipOverlay(double startTime)
{
this.startTime = startTime;
@ -51,12 +52,6 @@ namespace osu.Game.Screens.Play
Origin = Anchor.Centre;
}
protected override bool OnMouseMove(InputState state)
{
fadeContainer.State = Visibility.Visible;
return base.OnMouseMove(state);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
@ -121,15 +116,9 @@ namespace osu.Game.Screens.Play
Expire();
}
protected override void PopIn()
{
this.FadeIn();
}
protected override void PopIn() => this.FadeIn();
protected override void PopOut()
{
this.FadeOut();
}
protected override void PopOut() => this.FadeOut();
protected override void Update()
{
@ -137,6 +126,13 @@ namespace osu.Game.Screens.Play
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint);
}
protected override bool OnMouseMove(InputState state)
{
if (!state.Mouse.HasAnyButtonPressed)
fadeContainer.State = Visibility.Visible;
return base.OnMouseMove(state);
}
public bool OnPressed(GlobalAction action)
{
switch (action)
@ -176,7 +172,7 @@ namespace osu.Game.Screens.Play
if (stateChanged)
this.FadeIn(500, Easing.OutExpo);
if (!IsHovered)
if (!IsHovered && !IsDragged)
using (BeginDelayedSequence(1000))
scheduledHide = Schedule(() => State = Visibility.Hidden);
break;
@ -194,6 +190,18 @@ namespace osu.Game.Screens.Play
base.LoadComplete();
State = Visibility.Visible;
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
scheduledHide?.Cancel();
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
State = Visibility.Visible;
return base.OnMouseUp(state, args);
}
}
private class Button : OsuClickableContainer
@ -274,7 +282,7 @@ namespace osu.Game.Screens.Play
flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint);
box.FadeColour(colourHover, 500, Easing.OutQuint);
background.FadeTo(0.4f, 500, Easing.OutQuint);
return base.OnHover(state);
return true;
}
protected override void OnHoverLost(InputState state)

View File

@ -267,6 +267,8 @@
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
<Compile Include="Beatmaps\Formats\IHasComboColours.cs" />
<Compile Include="Beatmaps\Formats\IHasCustomColours.cs" />
<Compile Include="Beatmaps\Formats\JsonBeatmapDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyDecoder.cs" />
<Compile Include="Beatmaps\Formats\LegacyStoryboardDecoder.cs" />
@ -362,10 +364,8 @@
<Compile Include="Overlays\Volume\VolumeMeter.cs" />
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
<Compile Include="Rulesets\Edit\Layers\BorderLayer.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\CaptureBox.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlay.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\HitObjectOverlayLayer.cs" />
<Compile Include="Rulesets\Edit\HitObjectMask.cs" />
<Compile Include="Rulesets\Edit\Types\IHasEditablePosition.cs" />
<Compile Include="Rulesets\Mods\IApplicableFailOverride.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToBeatmapConverter.cs" />
@ -380,6 +380,10 @@
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Layers\BorderLayer.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Layers\HitObjectMaskLayer.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionBox.cs" />
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionLayer.cs" />
<Compile Include="Screens\Play\ScreenWithBeatmapBackground.cs" />
<Compile Include="Screens\Play\PlayerSettings\VisualSettings.cs" />
<Compile Include="Rulesets\Objects\CatmullApproximator.cs" />
@ -394,8 +398,6 @@
<Compile Include="Rulesets\UI\Scrolling\ScrollingRulesetContainer.cs" />
<Compile Include="Screens\Select\ImportFromStablePopup.cs" />
<Compile Include="Overlays\Settings\SettingsButton.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\SelectionBox.cs" />
<Compile Include="Rulesets\Edit\Layers\Selection\SelectionLayer.cs" />
<Compile Include="Screens\Edit\Components\BottomBarContainer.cs" />
<Compile Include="Screens\Edit\Components\PlaybackControl.cs" />
<Compile Include="Screens\Edit\Components\TimeInfoContainer.cs" />
@ -553,7 +555,7 @@
<Compile Include="Overlays\MainSettings.cs" />
<Compile Include="Overlays\MedalOverlay.cs" />
<Compile Include="Overlays\MedalSplash\DrawableMedal.cs" />
<Compile Include="Overlays\Mods\AssistedSection.cs" />
<Compile Include="Overlays\Mods\SpecialSection.cs" />
<Compile Include="Overlays\Mods\DifficultyIncreaseSection.cs" />
<Compile Include="Overlays\Mods\DifficultyReductionSection.cs" />
<Compile Include="Overlays\Mods\ModButton.cs" />
@ -809,7 +811,7 @@
<Compile Include="Screens\Play\PlayerSettings\PlayerCheckbox.cs" />
<Compile Include="Screens\Play\PlayerSettings\PlayerSettingsGroup.cs" />
<Compile Include="Screens\Play\PlayerSettings\PlayerSliderBar.cs" />
<Compile Include="Screens\Play\SkipButton.cs" />
<Compile Include="Screens\Play\SkipOverlay.cs" />
<Compile Include="Screens\Play\SongProgress.cs" />
<Compile Include="Screens\Play\SongProgressBar.cs" />
<Compile Include="Screens\Play\SongProgressGraph.cs" />