Merge remote-tracking branch 'upstream/master' into ranks-section

This commit is contained in:
Dean Herbert
2017-10-13 18:46:43 +09:00
163 changed files with 7946 additions and 956 deletions

View File

@ -58,6 +58,23 @@ namespace osu.Game.Beatmaps
ComboColors = original?.ComboColors ?? ComboColors;
HitObjects = original?.HitObjects ?? HitObjects;
Storyboard = original?.Storyboard ?? Storyboard;
if (original == null && Metadata == null)
{
// we may have no metadata in cases we weren't sourced from the database.
// let's fill it (and other related fields) so we don't need to null-check it in future usages.
BeatmapInfo = new BeatmapInfo
{
Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Unknown",
Author = @"Unknown Creator",
},
Version = @"Normal",
Difficulty = new BeatmapDifficulty()
};
}
}
}

View File

@ -80,15 +80,29 @@ namespace osu.Game.Beatmaps
// Editor
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
public string StoredBookmarks { get; set; }
[JsonIgnore]
public string StoredBookmarks
{
get { return string.Join(",", Bookmarks); }
set
{
if (string.IsNullOrEmpty(value))
{
Bookmarks = new int[0];
return;
}
Bookmarks = value.Split(',').Select(v =>
{
int val;
bool result = int.TryParse(v, out val);
return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray();
}
}
[Ignore]
[JsonIgnore]
public int[] Bookmarks
{
get { return StoredBookmarks.Split(',').Select(int.Parse).ToArray(); }
set { StoredBookmarks = string.Join(",", value); }
}
public int[] Bookmarks { get; set; } = new int[0];
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }

View File

@ -550,7 +550,7 @@ namespace osu.Game.Beatmaps
catch { return null; }
}
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).FileInfo.StoragePath;
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath;
protected override Texture GetBackground()
{
@ -573,6 +573,8 @@ namespace osu.Game.Beatmaps
}
catch { return new TrackVirtual(); }
}
protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile)));
}
/// <summary>

View File

@ -10,6 +10,28 @@ namespace osu.Game.Beatmaps
/// </summary>
public class BeatmapOnlineInfo
{
/// <summary>
/// The length in milliseconds of this beatmap's song.
/// </summary>
public double Length { get; set; }
/// <summary>
/// Whether or not this beatmap has a background video.
/// </summary>
public bool HasVideo { get; set; }
/// <summary>
/// The amount of circles in this beatmap.
/// </summary>
[JsonProperty(@"count_circles")]
public int CircleCount { get; set; }
/// <summary>
/// The amount of sliders in this beatmap.
/// </summary>
[JsonProperty(@"count_sliders")]
public int SliderCount { get; set; }
/// <summary>
/// The amount of plays this beatmap has.
/// </summary>

View File

@ -1,7 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using Newtonsoft.Json;
using osu.Game.Users;
namespace osu.Game.Beatmaps
{
@ -10,6 +12,26 @@ namespace osu.Game.Beatmaps
/// </summary>
public class BeatmapSetOnlineInfo
{
/// <summary>
/// The author of the beatmaps in this set.
/// </summary>
public User Author;
/// <summary>
/// The date this beatmap set was submitted to the online listing.
/// </summary>
public DateTimeOffset Submitted { get; set; }
/// <summary>
/// The date this beatmap set was ranked.
/// </summary>
public DateTimeOffset? Ranked { get; set; }
/// <summary>
/// The date this beatmap set was last updated.
/// </summary>
public DateTimeOffset? LastUpdated { get; set; }
/// <summary>
/// The different sizes of cover art for this beatmap set.
/// </summary>
@ -22,6 +44,11 @@ namespace osu.Game.Beatmaps
[JsonProperty(@"previewUrl")]
public string Preview { get; set; }
/// <summary>
/// The beats per minute of this beatmap set's song.
/// </summary>
public double BPM { get; set; }
/// <summary>
/// The amount of plays this beatmap set has.
/// </summary>

View File

@ -31,6 +31,8 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapInfo> HideDifficultyRequested;
public Action<BeatmapInfo> EditRequested;
public BeatmapSetHeader Header;
public List<BeatmapPanel> BeatmapPanels;
@ -87,7 +89,8 @@ namespace osu.Game.Beatmaps.Drawables
Alpha = 0,
GainedSelection = panelGainedSelection,
HideRequested = p => HideDifficultyRequested?.Invoke(p),
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
StartRequested = p => StartRequested?.Invoke(p.Beatmap),
EditRequested = p => EditRequested?.Invoke(p.Beatmap),
RelativeSizeAxes = Axes.X,
}).ToList();

View File

@ -613,7 +613,7 @@ namespace osu.Game.Beatmaps.Formats
string line;
while ((line = stream.ReadLine()) != null)
{
if (string.IsNullOrEmpty(line))
if (string.IsNullOrWhiteSpace(line))
continue;
if (line.StartsWith("//"))
@ -679,10 +679,12 @@ namespace osu.Game.Beatmaps.Formats
private KeyValuePair<string, string> splitKeyVal(string line, char separator)
{
var split = line.Trim().Split(new[] { separator }, 2);
return new KeyValuePair<string, string>
(
line.Remove(line.IndexOf(separator)).Trim(),
line.Substring(line.IndexOf(separator) + 1).Trim()
split[0].Trim(),
split.Length > 1 ? split[1].Trim() : string.Empty
);
}

View File

@ -8,7 +8,7 @@ namespace osu.Game.Beatmaps.Timing
/// <summary>
/// The minimum duration required for a break to have any effect.
/// </summary>
private const double min_break_duration = 650;
public const double MIN_BREAK_DURATION = 650;
/// <summary>
/// The break start time.
@ -28,6 +28,6 @@ namespace osu.Game.Beatmaps.Timing
/// <summary>
/// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap.
/// </summary>
public bool HasEffect => Duration >= min_break_duration;
public bool HasEffect => Duration >= MIN_BREAK_DURATION;
}
}
}

View File

@ -43,6 +43,7 @@ namespace osu.Game.Beatmaps
protected abstract Beatmap GetBeatmap();
protected abstract Texture GetBackground();
protected abstract Track GetTrack();
protected virtual Waveform GetWaveform() => new Waveform();
private Beatmap beatmap;
private readonly object beatmapLock = new object();
@ -96,6 +97,17 @@ namespace osu.Game.Beatmaps
}
}
private Waveform waveform;
private readonly object waveformLock = new object();
public Waveform Waveform
{
get
{
lock (waveformLock)
return waveform ?? (waveform = GetWaveform());
}
}
public bool TrackLoaded => track != null;
public void TransferTo(WorkingBeatmap other)
@ -114,6 +126,8 @@ namespace osu.Game.Beatmaps
{
background?.Dispose();
background = null;
waveform?.Dispose();
}
public void DisposeTrack()

View File

@ -54,6 +54,9 @@ namespace osu.Game.Configuration
// Graphics
Set(OsuSetting.ShowFpsDisplay, false);
Set(OsuSetting.ShowStoryboard, true);
Set(OsuSetting.CursorRotation, true);
Set(OsuSetting.MenuParallax, true);
Set(OsuSetting.SnakingInSliders, true);
@ -87,6 +90,7 @@ namespace osu.Game.Configuration
GameplayCursorSize,
AutoCursorSize,
DimLevel,
ShowStoryboard,
KeyOverlay,
FloatingComments,
PlaybackSpeed,
@ -96,6 +100,7 @@ namespace osu.Game.Configuration
AudioOffset,
MenuMusic,
MenuVoice,
CursorRotation,
MenuParallax,
BeatmapDetailTab,
Username,

View File

@ -20,13 +20,14 @@ namespace osu.Game.Graphics.Cursor
{
protected override Drawable CreateCursor() => new Cursor();
private Bindable<bool> cursorRotate;
private bool dragging;
private bool startRotation;
protected override bool OnMouseMove(InputState state)
{
if (dragging)
if (cursorRotate && dragging)
{
Debug.Assert(state.Mouse.PositionMouseDown != null);
@ -102,6 +103,12 @@ namespace osu.Game.Graphics.Cursor
ActiveCursor.ScaleTo(0, 500, Easing.In);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
}
public class Cursor : Container
{
private Container cursorContainer;

View File

@ -1,27 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Framework.Graphics;
namespace osu.Game.Graphics.Processing
{
internal class RatioAdjust : Container
{
public RatioAdjust()
{
RelativeSizeAxes = Axes.Both;
}
protected override void Update()
{
base.Update();
Vector2 parent = Parent.DrawSize;
Scale = new Vector2(Math.Min(parent.Y / 768f, parent.X / 1024f));
Size = new Vector2(1 / Scale.X);
}
}
}

View File

@ -15,32 +15,91 @@ namespace osu.Game.Graphics.UserInterface
{
public class IconButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
private readonly Box hover;
private readonly Container content;
private const float button_size = 30;
private Color4? flashColour;
/// <summary>
/// The colour that should be flashed when the <see cref="IconButton"/> is clicked.
/// </summary>
public Color4 FlashColour
{
get { return flashColour ?? Color4.White; }
set { flashColour = value; }
}
private Color4? iconColour;
/// <summary>
/// The icon colour. This does not affect <see cref="IconButton.Colour"/>.
/// </summary>
public Color4 IconColour
{
get { return iconColour ?? Color4.White; }
set
{
iconColour = value;
icon.Colour = value;
}
}
private Color4? iconHoverColour;
/// <summary>
/// The icon colour while the <see cref="IconButton"/> is hovered.
/// </summary>
public Color4 IconHoverColour
{
get { return iconHoverColour ?? IconColour; }
set { iconHoverColour = value; }
}
private Color4? hoverColour;
/// <summary>
/// The background colour of the <see cref="IconButton"/> while it is hovered.
/// </summary>
public Color4 HoverColour
{
get { return hoverColour ?? Color4.White; }
set
{
hoverColour = value;
hover.Colour = value;
}
}
/// <summary>
/// The icon.
/// </summary>
public FontAwesome Icon
{
get { return icon.Icon; }
set { icon.Icon = value; }
}
private const float button_size = 30;
private Color4 flashColour;
/// <summary>
/// The icon scale. This does not affect <see cref="IconButton.Scale"/>.
/// </summary>
public Vector2 IconScale
{
get { return icon.Scale; }
set { icon.Scale = value; }
}
/// <summary>
/// The size of the <see cref="IconButton"/> while it is not being pressed.
/// </summary>
public Vector2 ButtonSize
{
get { return content.Size; }
set { content.Size = value; }
}
private readonly Container content;
private readonly SpriteIcon icon;
private readonly Box hover;
public IconButton()
{
AutoSizeAxes = Axes.Both;
Origin = Anchor.Centre;
Anchor = Anchor.Centre;
Children = new Drawable[]
{
content = new Container
@ -48,7 +107,6 @@ namespace osu.Game.Graphics.UserInterface
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(button_size),
CornerRadius = 5,
Masking = true,
EdgeEffect = new EdgeEffectParameters
@ -78,8 +136,11 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hover.Colour = colours.Yellow.Opacity(0.6f);
flashColour = colours.Yellow;
if (hoverColour == null)
HoverColour = colours.Yellow.Opacity(0.6f);
if (flashColour == null)
FlashColour = colours.Yellow;
Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint);
}
@ -87,18 +148,20 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnHover(InputState state)
{
hover.FadeIn(500, Easing.OutQuint);
icon.FadeColour(IconHoverColour, 500, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
hover.FadeOut(500, Easing.OutQuint);
icon.FadeColour(IconColour, 500, Easing.OutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
hover.FlashColour(flashColour, 800, Easing.OutQuint);
hover.FlashColour(FlashColour, 800, Easing.OutQuint);
return base.OnClick(state);
}

View File

@ -59,7 +59,7 @@ namespace osu.Game.Graphics.UserInterface
public class OsuTabItem : TabItem<T>, IHasAccentColour
{
protected readonly SpriteText Text;
private readonly Box box;
protected readonly Box Bar;
private Color4 accentColour;
public Color4 AccentColour
@ -77,13 +77,13 @@ namespace osu.Game.Graphics.UserInterface
private void fadeActive()
{
box.FadeIn(transition_length, Easing.OutQuint);
Bar.FadeIn(transition_length, Easing.OutQuint);
Text.FadeColour(Color4.White, transition_length, Easing.OutQuint);
}
private void fadeInactive()
{
box.FadeOut(transition_length, Easing.OutQuint);
Bar.FadeOut(transition_length, Easing.OutQuint);
Text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
}
@ -123,7 +123,7 @@ namespace osu.Game.Graphics.UserInterface
TextSize = 14,
Font = @"Exo2.0-Bold", // Font should only turn bold when active?
},
box = new Box
Bar = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,

View File

@ -49,7 +49,7 @@ namespace osu.Game.IO.Legacy
int len = ReadInt32();
if (len > 0) return ReadBytes(len);
if (len < 0) return null;
return new byte[0];
return Array.Empty<byte>();
}
/// <summary> Reads a char array from the buffer, handling nulls and the array length. </summary>
@ -58,7 +58,7 @@ namespace osu.Game.IO.Legacy
int len = ReadInt32();
if (len > 0) return ReadChars(len);
if (len < 0) return null;
return new char[0];
return Array.Empty<char>();
}
/// <summary> Reads a DateTime from the buffer. </summary>

View File

@ -101,18 +101,16 @@ namespace osu.Game.Online.API
}
break;
case APIState.Offline:
case APIState.Connecting:
//work to restore a connection...
if (!HasLogin)
{
//OsuGame.Scheduler.Add(() => { OsuGame.ShowLogin(); });
State = APIState.Offline;
Thread.Sleep(500);
Thread.Sleep(50);
continue;
}
if (State < APIState.Connecting)
State = APIState.Connecting;
State = APIState.Connecting;
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password))
{
@ -125,7 +123,8 @@ namespace osu.Game.Online.API
var userReq = new GetUserRequest();
userReq.Success += u => {
userReq.Success += u =>
{
LocalUser.Value = u;
//we're connected!
State = APIState.Online;
@ -133,16 +132,14 @@ namespace osu.Game.Online.API
};
if (!handleRequest(userReq))
{
State = APIState.Failing;
continue;
}
break;
}
//hard bail if we can't get a valid access token.
if (authentication.RequestAccessToken() == null)
{
Logout(false);
State = APIState.Offline;
continue;
}
@ -162,20 +159,12 @@ namespace osu.Game.Online.API
}
}
private void clearCredentials()
{
Username = null;
Password = null;
}
public void Login(string username, string password)
{
Debug.Assert(State == APIState.Offline);
Username = username;
Password = password;
State = APIState.Connecting;
}
/// <summary>
@ -204,7 +193,7 @@ namespace osu.Game.Online.API
switch (statusCode)
{
case HttpStatusCode.Unauthorized:
State = APIState.Offline;
Logout(false);
return true;
case HttpStatusCode.RequestTimeout:
failureCount++;
@ -215,6 +204,7 @@ namespace osu.Game.Online.API
return false;
State = APIState.Failing;
flushQueue();
return true;
}
@ -235,33 +225,21 @@ namespace osu.Game.Online.API
public APIState State
{
get { return state; }
set
private set
{
APIState oldState = state;
APIState newState = value;
state = value;
switch (state)
{
case APIState.Failing:
case APIState.Offline:
flushQueue();
break;
}
if (oldState != newState)
{
//OsuGame.Scheduler.Add(delegate
log.Add($@"We just went {newState}!");
Scheduler.Add(delegate
{
//NotificationOverlay.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000);
log.Add($@"We just went {newState}!");
Scheduler.Add(delegate
{
components.ForEach(c => c.APIStateChanged(this, newState));
OnStateChange?.Invoke(oldState, newState);
});
}
components.ForEach(c => c.APIStateChanged(this, newState));
OnStateChange?.Invoke(oldState, newState);
});
}
}
}
@ -292,11 +270,12 @@ namespace osu.Game.Online.API
}
}
public void Logout()
public void Logout(bool clearUsername = true)
{
clearCredentials();
flushQueue();
if (clearUsername) Username = null;
Password = null;
authentication.Clear();
State = APIState.Offline;
LocalUser.Value = createGuestUser();
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API
@ -70,13 +69,11 @@ namespace osu.Game.Online.API
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds);
public bool ExceededTimeout => remainingTime == 0;
private double? startTime;
public double StartTime => startTime ?? -1;
private DateTimeOffset? startTime;
protected APIAccess API;
protected WebRequest WebRequest;
@ -96,7 +93,7 @@ namespace osu.Game.Online.API
return;
if (startTime == null)
startTime = DateTime.Now.TotalMilliseconds();
startTime = DateTimeOffset.UtcNow;
if (remainingTime <= 0)
throw new TimeoutException(@"API request timeout hit");

View File

@ -4,7 +4,6 @@
using System;
using System.Globalization;
using Newtonsoft.Json;
using osu.Framework.Extensions;
namespace osu.Game.Online.API
{
@ -22,12 +21,12 @@ namespace osu.Game.Online.API
{
get
{
return AccessTokenExpiry - DateTime.Now.ToUnixTimestamp();
return AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
set
{
AccessTokenExpiry = DateTime.Now.AddSeconds(value).ToUnixTimestamp();
AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds();
}
}

View File

@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests
{
private readonly BeatmapInfo beatmap;
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={beatmap.Path}";
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}";
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
{

View File

@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
@ -49,6 +50,12 @@ namespace osu.Game.Online.API.Requests
[JsonProperty(@"id")]
private int onlineId { get; set; }
[JsonProperty(@"creator")]
private string creatorUsername { get; set; }
[JsonProperty(@"user_id")]
private long creatorId = 1;
[JsonProperty(@"beatmaps")]
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
@ -60,6 +67,11 @@ namespace osu.Game.Online.API.Requests
Metadata = this,
OnlineInfo = new BeatmapSetOnlineInfo
{
Author = new User
{
Id = creatorId,
Username = creatorUsername,
},
Covers = covers,
Preview = preview,
PlayCount = playCount,

View File

@ -96,7 +96,7 @@ namespace osu.Game.Online.API.Requests
}
[JsonProperty(@"statistics")]
private Dictionary<string, dynamic> jsonStats
private Dictionary<string, object> jsonStats
{
set
{

View File

@ -23,6 +23,7 @@ namespace osu.Game.Online.API.Requests
req.Method = HttpMethod.POST;
req.AddParameter(@"target_type", message.TargetType.GetDescription());
req.AddParameter(@"target_id", message.TargetId.ToString());
req.AddParameter(@"is_action", message.IsAction.ToString().ToLower());
req.AddParameter(@"message", message.Content);
return req;
@ -30,4 +31,4 @@ namespace osu.Game.Online.API.Requests
protected override string Target => @"chat/messages";
}
}
}

View File

@ -1,25 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Users;
namespace osu.Game.Online.Chat
{
public class ErrorMessage : Message
public class ErrorMessage : InfoMessage
{
private static int errorId = -1;
public ErrorMessage(string message) : base(errorId--)
public ErrorMessage(string message) : base(message)
{
Timestamp = DateTimeOffset.Now;
Content = message;
Sender = new User
{
Username = @"system",
Colour = @"ff0000",
};
Sender.Colour = @"ff0000";
}
}
}
}

View File

@ -0,0 +1,25 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Users;
namespace osu.Game.Online.Chat
{
public class InfoMessage : Message
{
private static int infoID = -1;
public InfoMessage(string message) : base(infoID--)
{
Timestamp = DateTimeOffset.Now;
Content = message;
Sender = new User
{
Username = @"system",
Colour = @"0000ff",
};
}
}
}

View File

@ -23,6 +23,9 @@ namespace osu.Game.Online.Chat
[JsonProperty(@"target_id")]
public int TargetId;
[JsonProperty(@"is_action")]
public bool IsAction;
[JsonProperty(@"timestamp")]
public DateTimeOffset Timestamp;

View File

@ -47,6 +47,8 @@ namespace osu.Game
private UserProfileOverlay userProfile;
private BeatmapSetOverlay beatmapSetOverlay;
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
@ -186,10 +188,11 @@ namespace osu.Game
GetToolbarHeight = () => ToolbarOffset,
Depth = -1
}, overlayContent.Add);
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add);
LoadComponentAsync(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -2 }, mainContent.Add);
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -3 }, mainContent.Add);
LoadComponentAsync(musicController = new MusicController
{
Depth = -3,
Depth = -4,
Position = new Vector2(0, Toolbar.HEIGHT),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
@ -197,7 +200,7 @@ namespace osu.Game
LoadComponentAsync(notificationOverlay = new NotificationOverlay
{
Depth = -3,
Depth = -4,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
}, overlayContent.Add);
@ -223,6 +226,7 @@ namespace osu.Game
dependencies.Cache(chat);
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
dependencies.Cache(beatmapSetOverlay);
dependencies.Cache(notificationOverlay);
dependencies.Cache(dialogOverlay);

View File

@ -15,7 +15,6 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Processing;
using osu.Game.Online.API;
using SQLite.Net;
using osu.Framework.Graphics.Performance;
@ -82,6 +81,13 @@ namespace osu.Game
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
private SQLiteConnection createConnection()
{
var conn = Host.Storage.GetDatabase(@"client");
conn.BusyTimeout = new TimeSpan(TimeSpan.TicksPerSecond * 10);
return conn;
}
private SQLiteConnection connection;
[BackgroundDependencyLoader]
@ -90,8 +96,7 @@ namespace osu.Game
dependencies.Cache(this);
dependencies.Cache(LocalConfig);
connection = Host.Storage.GetDatabase(@"client");
connection = createConnection();
connection.CreateTable<StoreVersion>();
dependencies.Cache(API = new APIAccess
@ -180,7 +185,7 @@ namespace osu.Game
GlobalKeyBindingInputManager globalBinding;
base.Content.Add(new RatioAdjust
base.Content.Add(new DrawSizePreservingFillContainer
{
Children = new Drawable[]
{

View File

@ -0,0 +1,131 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Overlays.BeatmapSet
{
public class AuthorInfo : Container
{
private const float height = 50;
private readonly UpdateableAvatar avatar;
private readonly ClickableArea clickableArea;
private readonly FillFlowContainer fields;
private UserProfileOverlay profile;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
var i = BeatmapSet.OnlineInfo;
avatar.User = i.Author;
clickableArea.Action = () => profile?.ShowUser(avatar.User);
fields.Children = new Drawable[]
{
new Field("made by", i.Author.Username, @"Exo2.0-RegularItalic"),
new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{
Margin = new MarginPadding { Top = 5 },
},
};
if (i.Ranked.HasValue)
{
fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
else if (i.LastUpdated.HasValue)
{
fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
}
}
public AuthorInfo()
{
RelativeSizeAxes = Axes.X;
Height = height;
Children = new Drawable[]
{
clickableArea = new ClickableArea
{
AutoSizeAxes = Axes.Both,
CornerRadius = 3,
Masking = true,
Child = avatar = new UpdateableAvatar
{
Size = new Vector2(height),
},
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
},
},
fields = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Left = height + 5 },
},
};
}
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
this.profile = profile;
clickableArea.Action = () => profile?.ShowUser(avatar.User);
}
private class Field : FillFlowContainer
{
public Field(string first, string second, string secondFont)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Children = new[]
{
new OsuSpriteText
{
Text = $"{first} ",
TextSize = 13,
},
new OsuSpriteText
{
Text = second,
TextSize = 13,
Font = secondFont,
},
};
}
}
private class ClickableArea : OsuClickableContainer, IHasTooltip
{
public string TooltipText => @"View Profile";
}
}
}

View File

@ -0,0 +1,130 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class BasicStats : Container
{
private readonly Statistic length, bpm, circleCount, sliderCount;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##");
}
}
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
length.Value = TimeSpan.FromMilliseconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString("N0");
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString("N0");
}
}
public BasicStats()
{
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new[]
{
length = new Statistic(FontAwesome.fa_clock_o, "Length") { Width = 0.25f },
bpm = new Statistic(FontAwesome.fa_circle, "BPM") { Width = 0.25f },
circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = 0.25f },
sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = 0.25f },
},
};
}
private class Statistic : Container, IHasTooltip
{
private readonly string name;
private readonly OsuSpriteText value;
public string TooltipText => name;
public string Value
{
get { return value.Text; }
set { this.value.Text = value; }
}
public Statistic(FontAwesome icon, string name)
{
this.name = name;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_square,
Size = new Vector2(13),
Rotation = 45,
Colour = OsuColour.FromHex(@"441288"),
},
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(13),
Colour = OsuColour.FromHex(@"f7dd55"),
Scale = new Vector2(0.8f),
},
value = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TextSize = 13,
Font = @"Exo2.0-Bold",
Margin = new MarginPadding { Left = 10 },
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
value.Colour = colour.Yellow;
}
}
}
}

View File

@ -0,0 +1,312 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class BeatmapPicker : Container
{
private const float tile_icon_padding = 7;
private const float tile_spacing = 2;
private readonly DifficultiesContainer difficulties;
private readonly OsuSpriteText version, starRating;
private readonly Statistic plays, favourites;
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Beatmap.Value = BeatmapSet.Beatmaps.First();
plays.Value = BeatmapSet.OnlineInfo.PlayCount;
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount;
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b)
{
State = DifficultySelectorState.NotSelected,
OnHovered = beatmap =>
{
showBeatmap(beatmap);
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
starRating.FadeIn(100);
},
OnClicked = beatmap =>
{
Beatmap.Value = beatmap;
},
});
updateDifficultyButtons();
}
}
public BeatmapPicker()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
difficulties = new DifficultiesContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) },
OnLostHover = () =>
{
showBeatmap(Beatmap.Value);
starRating.FadeOut(100);
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 10 },
Spacing = new Vector2(5f),
Children = new[]
{
version = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
TextSize = 20,
Font = @"Exo2.0-Bold",
},
starRating = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
TextSize = 13,
Font = @"Exo2.0-Bold",
Text = "Star Difficulty",
Alpha = 0,
Margin = new MarginPadding { Bottom = 1 },
},
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(10f),
Margin = new MarginPadding { Top = 5 },
Children = new[]
{
plays = new Statistic(FontAwesome.fa_play_circle),
favourites = new Statistic(FontAwesome.fa_heart),
},
},
},
},
};
Beatmap.ValueChanged += b =>
{
showBeatmap(b);
updateDifficultyButtons();
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
starRating.Colour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
// done here so everything can bind in intialization and get the first trigger
Beatmap.TriggerChange();
}
private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version;
private void updateDifficultyButtons()
{
difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected);
}
private class DifficultiesContainer : FillFlowContainer<DifficultySelectorButton>
{
public Action OnLostHover;
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
OnLostHover?.Invoke();
}
}
private class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
{
private const float transition_duration = 100;
private const float size = 52;
private readonly Container bg;
private readonly DifficultyIcon icon;
public readonly BeatmapInfo Beatmap;
public Action<BeatmapInfo> OnHovered;
public Action<BeatmapInfo> OnClicked;
public event Action<DifficultySelectorState> StateChanged;
private DifficultySelectorState state;
public DifficultySelectorState State
{
get { return state; }
set
{
if (value == state) return;
state = value;
StateChanged?.Invoke(State);
if (value == DifficultySelectorState.Selected)
fadeIn();
else
fadeOut();
}
}
public DifficultySelectorButton(BeatmapInfo beatmap)
{
Beatmap = beatmap;
Size = new Vector2(size);
Margin = new MarginPadding { Horizontal = tile_spacing / 2 };
Children = new Drawable[]
{
bg = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
},
icon = new DifficultyIcon(beatmap)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(size - tile_icon_padding * 2),
Margin = new MarginPadding { Bottom = 1 },
},
};
}
protected override bool OnHover(InputState state)
{
fadeIn();
OnHovered?.Invoke(Beatmap);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (State == DifficultySelectorState.NotSelected)
fadeOut();
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
OnClicked?.Invoke(Beatmap);
return base.OnClick(state);
}
private void fadeIn()
{
bg.FadeIn(transition_duration);
icon.FadeIn(transition_duration);
}
private void fadeOut()
{
bg.FadeOut();
icon.FadeTo(0.7f, transition_duration);
}
}
private class Statistic : FillFlowContainer
{
private readonly OsuSpriteText text;
private int value;
public int Value
{
get { return value; }
set
{
this.value = value;
text.Text = Value.ToString(@"N0");
}
}
public Statistic(FontAwesome icon)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(2f);
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = icon,
Shadow = true,
Size = new Vector2(13),
},
text = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBoldItalic",
TextSize = 14,
},
};
}
}
private enum DifficultySelectorState
{
Selected,
NotSelected,
}
}
}

View File

@ -0,0 +1,118 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select.Details;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class Details : FillFlowContainer
{
private readonly PreviewButton preview;
private readonly BasicStats basic;
private readonly AdvancedStats advanced;
private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
}
}
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
basic.Beatmap = advanced.Beatmap = Beatmap;
ratings.Metrics = Beatmap.Metrics;
}
}
public Details()
{
Width = BeatmapSetOverlay.RIGHT_WIDTH;
AutoSizeAxes = Axes.Y;
Spacing = new Vector2(1f);
Children = new Drawable[]
{
preview = new PreviewButton
{
RelativeSizeAxes = Axes.X,
},
new DetailBox
{
Child = basic = new BasicStats
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 10 },
},
},
new DetailBox
{
Child = advanced = new AdvancedStats
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 7.5f },
},
},
new DetailBox
{
Child = ratings = new UserRatings
{
RelativeSizeAxes = Axes.X,
Height = 95,
Margin = new MarginPadding { Top = 10 },
},
},
};
}
private class DetailBox : Container
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public DetailBox()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
content = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 15 },
},
};
}
}
}
}

View File

@ -0,0 +1,59 @@
// Copyright (c) 2007-2017 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.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class DownloadButton : HeaderButton
{
public DownloadButton(string title, string subtitle)
{
Width = 120;
RelativeSizeAxes = Axes.Y;
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = title,
TextSize = 13,
Font = @"Exo2.0-Bold",
},
new OsuSpriteText
{
Text = subtitle,
TextSize = 11,
Font = @"Exo2.0-Bold",
},
},
},
new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
},
};
}
}
}

View File

@ -0,0 +1,80 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class FavouriteButton : HeaderButton
{
public readonly Bindable<bool> Favourited = new Bindable<bool>();
public FavouriteButton()
{
RelativeSizeAxes = Axes.Y;
Container pink;
SpriteIcon icon;
Children = new Drawable[]
{
pink = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"9f015f"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourLight = OsuColour.FromHex(@"cb2187"),
ColourDark = OsuColour.FromHex(@"9f015f"),
TriangleScale = 1.5f,
},
},
},
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_heart_o,
Size = new Vector2(18),
Shadow = false,
},
};
Favourited.ValueChanged += value =>
{
if (value)
{
pink.FadeIn(200);
icon.Icon = FontAwesome.fa_heart;
}
else
{
pink.FadeOut(200);
icon.Icon = FontAwesome.fa_heart_o;
}
};
Action = () => Favourited.Value = !Favourited.Value;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Width = DrawHeight;
}
}
}

View File

@ -0,0 +1,228 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class Header : Container
{
private const float transition_duration = 250;
private const float tabs_height = 50;
private const float buttons_height = 45;
private const float buttons_spacing = 5;
private readonly Box tabsBg;
private readonly Container coverContainer;
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
private readonly Details details;
private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet;
title.Text = BeatmapSet.Metadata.Title;
artist.Text = BeatmapSet.Metadata.Artist;
cover?.FadeOut(400, Easing.Out);
coverContainer.Add(cover = new DelayedLoadWrapper(new BeatmapSetCover(BeatmapSet)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
OnLoadComplete = d =>
{
d.FadeInFromZero(400, Easing.Out);
},
})
{
RelativeSizeAxes = Axes.Both,
TimeBeforeLoad = 300
});
}
}
public Header()
{
RelativeSizeAxes = Axes.X;
Height = 400;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Container noVideoButtons;
FillFlowContainer videoButtons;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = tabs_height,
Children = new[]
{
tabsBg = new Box
{
RelativeSizeAxes = Axes.Both,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = tabs_height },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
coverContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING },
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = 113,
Child = Picker = new BeatmapPicker(),
},
title = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
TextSize = 37,
},
artist = new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
TextSize = 25,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 20 },
Child = author = new AuthorInfo(),
},
new Container
{
RelativeSizeAxes = Axes.X,
Height = buttons_height,
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
new FavouriteButton(),
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Children = new Drawable[]
{
noVideoButtons = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
Child = new DownloadButton("Download", @""),
},
videoButtons = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Spacing = new Vector2(buttons_spacing),
Alpha = 0f,
Children = new[]
{
new DownloadButton("Download", "with Video"),
new DownloadButton("Download", "without Video"),
},
},
},
},
},
},
},
},
},
details = new Details
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING },
},
},
},
};
Picker.Beatmap.ValueChanged += b =>
{
details.Beatmap = b;
if (b.OnlineInfo.HasVideo)
{
noVideoButtons.FadeOut(transition_duration);
videoButtons.FadeIn(transition_duration);
}
else
{
noVideoButtons.FadeIn(transition_duration);
videoButtons.FadeOut(transition_duration);
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabsBg.Colour = colours.Gray3;
}
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) 2007-2017 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.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.BeatmapSet
{
public class HeaderButton : OsuClickableContainer
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public HeaderButton()
{
CornerRadius = 3;
Masking = true;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"094c5f"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourLight = OsuColour.FromHex(@"0f7c9b"),
ColourDark = OsuColour.FromHex(@"094c5f"),
TriangleScale = 1.5f,
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
},
};
}
}
}

View File

@ -0,0 +1,196 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class Info : Container
{
private const float transition_duration = 250;
private const float metadata_width = 225;
private const float spacing = 20;
private readonly MetadataSection description, source, tags;
private readonly Box successRateBackground;
private readonly SuccessRate successRate;
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
source.Text = BeatmapSet.Metadata.Source;
tags.Text = BeatmapSet.Metadata.Tags;
}
}
public BeatmapInfo Beatmap
{
get { return successRate.Beatmap; }
set { successRate.Beatmap = value; }
}
public Info()
{
RelativeSizeAxes = Axes.X;
Height = 220;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = metadata_width + BeatmapSetOverlay.RIGHT_WIDTH + spacing * 2 },
Child = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = description = new MetadataSection("Description"),
},
},
new ScrollContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = metadata_width,
ScrollbarVisible = false,
Padding = new MarginPadding { Horizontal = 10 },
Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing },
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = transition_duration,
Children = new[]
{
source = new MetadataSection("Source"),
tags = new MetadataSection("Tags"),
},
},
},
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = BeatmapSetOverlay.RIGHT_WIDTH,
Children = new Drawable[]
{
successRateBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
successRate = new SuccessRate
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 20, Horizontal = 15 },
},
},
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
successRateBackground.Colour = colours.GrayE;
source.TextColour = description.TextColour = colours.Gray5;
tags.TextColour = colours.BlueDark;
}
private class MetadataSection : FillFlowContainer
{
private readonly OsuSpriteText header;
private readonly TextFlowContainer textFlow;
public string Text
{
set
{
if (string.IsNullOrEmpty(value))
{
this.FadeOut(transition_duration);
return;
}
this.FadeIn(transition_duration);
textFlow.Clear();
textFlow.AddText(value, s => s.TextSize = 14);
}
}
public Color4 TextColour
{
get { return textFlow.Colour; }
set { textFlow.Colour = value; }
}
public MetadataSection(string title)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Spacing = new Vector2(5f);
InternalChildren = new Drawable[]
{
header = new OsuSpriteText
{
Text = title,
Font = @"Exo2.0-Bold",
TextSize = 14,
Shadow = false,
Margin = new MarginPadding { Top = 20 },
},
textFlow = new TextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
header.Colour = colours.Gray5;
}
}
}
}

View File

@ -0,0 +1,107 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Overlays.Direct;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.BeatmapSet
{
public class PreviewButton : OsuClickableContainer
{
private const float transition_duration = 500;
private readonly Box bg, progress;
private readonly PlayButton playButton;
private Track preview => playButton.Preview;
private Bindable<bool> playing => playButton.Playing;
public BeatmapSetInfo BeatmapSet
{
get { return playButton.BeatmapSet; }
set { playButton.BeatmapSet = value; }
}
public PreviewButton()
{
Height = 42;
Children = new Drawable[]
{
bg = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.25f),
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 3,
Child = progress = new Box
{
RelativeSizeAxes = Axes.Both,
Width = 0f,
Alpha = 0f,
},
},
playButton = new PlayButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(18),
},
};
Action = () => playing.Value = !playing.Value;
playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
progress.Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
if (playing.Value && preview != null)
{
progress.Width = (float)(preview.CurrentTime / preview.Length);
}
}
protected override void Dispose(bool isDisposing)
{
playing.Value = false;
base.Dispose(isDisposing);
}
protected override bool OnHover(InputState state)
{
bg.FadeColour(Color4.Black.Opacity(0.5f), 100);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
bg.FadeColour(Color4.Black.Opacity(0.25f), 100);
base.OnHoverLost(state);
}
}
}

View File

@ -0,0 +1,113 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Details;
namespace osu.Game.Overlays.BeatmapSet
{
public class SuccessRate : Container
{
private readonly FillFlowContainer header;
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
private readonly Bar successRate;
private readonly Container percentContainer;
private readonly FailRetryGraph graph;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
beatmap = value;
var rate = (float)beatmap.OnlineInfo.PassCount / beatmap.OnlineInfo.PlayCount;
successPercent.Text = $"{Math.Round(rate * 100)}%";
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
graph.Metrics = Beatmap.Metrics;
}
}
public SuccessRate()
{
Children = new Drawable[]
{
header = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
successRateLabel = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Success Rate",
TextSize = 13,
},
successRate = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
Margin = new MarginPadding { Top = 5 },
},
percentContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0f,
Child = successPercent = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre,
Text = @"0%",
TextSize = 13,
},
},
graphLabel = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Points of Failure",
TextSize = 13,
Margin = new MarginPadding { Vertical = 20 },
},
},
},
graph = new FailRetryGraph
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
graph.Padding = new MarginPadding { Top = header.DrawHeight };
}
}
}

View File

@ -0,0 +1,102 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.BeatmapSet;
namespace osu.Game.Overlays
{
public class BeatmapSetOverlay : WaveOverlayContainer
{
public const float X_PADDING = 40;
public const float RIGHT_WIDTH = 275;
private readonly Header header;
private readonly Info info;
// receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Both;
Width = 0.85f;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
header = new Header(),
info = new Info(),
},
},
},
};
header.Picker.Beatmap.ValueChanged += b => info.Beatmap = b;
}
protected override void PopIn()
{
base.PopIn();
FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
protected override bool OnClick(InputState state)
{
State = Visibility.Hidden;
return true;
}
public void ShowBeatmapSet(BeatmapSetInfo set)
{
header.BeatmapSet = info.BeatmapSet = set;
Show();
}
}
}

View File

@ -63,6 +63,7 @@ namespace osu.Game.Overlays.Chat
private const float padding = 15;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
private Color4 customUsernameColour;
@ -194,6 +195,8 @@ namespace osu.Game.Overlays.Chat
}
}
};
if (message.IsAction && senderHasBackground)
contentFlow.Colour = OsuColour.FromHex(message.Sender.Colour);
updateMessageContent();
FinishTransforms(true);
@ -206,7 +209,17 @@ namespace osu.Game.Overlays.Chat
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
username.Text = $@"{message.Sender.Username}" + (senderHasBackground ? "" : ":");
contentFlow.Text = message.Content;
if (message.IsAction)
{
contentFlow.Clear();
contentFlow.AddText("[", sprite => sprite.Padding = new MarginPadding { Right = action_padding });
contentFlow.AddText(message.Content, sprite => sprite.Font = @"Exo2.0-MediumItalic");
contentFlow.AddText("]", sprite => sprite.Padding = new MarginPadding { Left = action_padding });
}
else
contentFlow.Text = message.Content;
}
private class MessageSender : ClickableContainer, IHasContextMenu

View File

@ -465,7 +465,7 @@ namespace osu.Game.Overlays
textbox.Text = string.Empty;
if (string.IsNullOrEmpty(postText))
if (string.IsNullOrWhiteSpace(postText))
return;
var target = currentChannel;
@ -478,11 +478,36 @@ namespace osu.Game.Overlays
return;
}
bool isAction = false;
if (postText[0] == '/')
{
// TODO: handle commands
target.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
return;
string[] parameters = postText.Substring(1).Split(new[] { ' ' }, 2);
string command = parameters[0];
string content = parameters.Length == 2 ? parameters[1] : string.Empty;
switch (command)
{
case "me":
if (string.IsNullOrWhiteSpace(content))
{
currentChannel.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
return;
}
isAction = true;
postText = content;
break;
case "help":
currentChannel.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
return;
default:
currentChannel.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
return;
}
}
var message = new LocalEchoMessage
@ -491,6 +516,7 @@ namespace osu.Game.Overlays
Timestamp = DateTimeOffset.Now,
TargetType = TargetType.Channel, //TODO: read this from channel
TargetId = target.Id,
IsAction = isAction,
Content = postText
};

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Framework.Input;
namespace osu.Game.Overlays.Direct
{
@ -22,6 +21,11 @@ namespace osu.Game.Overlays.Direct
private const float vertical_padding = 5;
private FillFlowContainer bottomPanel;
private PlayButton playButton;
private Box progressBar;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
@ -88,6 +92,15 @@ namespace osu.Game.Overlays.Direct
{
RelativeSizeAxes = Axes.Both,
},
progressBar = new Box
{
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
BypassAutoSizeAxes = Axes.Both,
Size = new Vector2(0, 3),
Alpha = 0,
Colour = colours.Yellow,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
@ -150,6 +163,15 @@ namespace osu.Game.Overlays.Direct
},
},
},
new DownloadButton
{
Size = new Vector2(30),
Margin = new MarginPadding(horizontal_padding),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Colour = colours.Gray5,
Action = StartDownload
},
},
},
},
@ -170,13 +192,13 @@ namespace osu.Game.Overlays.Direct
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
},
},
playButton = new PlayButton(SetInfo)
{
Margin = new MarginPadding { Top = 5, Left = 10 },
Size = new Vector2(30),
Alpha = 0,
},
});
}
protected override bool OnClick(InputState state)
{
StartDownload();
return true;
}
}
}

View File

@ -11,10 +11,8 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Input;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Direct
{
@ -30,8 +28,14 @@ namespace osu.Game.Overlays.Direct
Height = height;
}
private PlayButton playButton;
private Box progressBar;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation)
private void load(LocalisationEngine localisation, OsuColour colours)
{
Content.CornerRadius = 5;
@ -50,29 +54,50 @@ namespace osu.Game.Overlays.Direct
{
new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Direction = FillDirection.Horizontal,
LayoutEasing = Easing.OutQuint,
LayoutDuration = 120,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new OsuSpriteText
playButton = new PlayButton(SetInfo)
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Size = new Vector2(height / 2),
FillMode = FillMode.Fit,
Alpha = 0,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
},
}
},
new FillFlowContainer
{
@ -128,49 +153,17 @@ namespace osu.Game.Overlays.Direct
},
},
},
});
}
private class DownloadButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public DownloadButton()
{
Children = new Drawable[]
progressBar = new Box
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(30),
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
}
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
BypassAutoSizeAxes = Axes.Y,
Size = new Vector2(0, 3),
Alpha = 0,
Colour = colours.Yellow,
},
});
}
}
}

View File

@ -20,6 +20,8 @@ using osu.Game.Online.API;
using osu.Framework.Logging;
using osu.Game.Overlays.Notifications;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
namespace osu.Game.Overlays.Direct
{
@ -37,6 +39,12 @@ namespace osu.Game.Overlays.Direct
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private NotificationOverlay notifications;
private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview;
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }
protected override Container<Drawable> Content => content;
@ -63,11 +71,12 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay)
{
this.api = api;
this.beatmaps = beatmaps;
this.notifications = notifications;
this.beatmapSetOverlay = beatmapSetOverlay;
AddInternal(content = new Container
{
@ -102,10 +111,21 @@ namespace osu.Game.Overlays.Direct
attachDownload(downloadRequest);
}
protected override void Update()
{
base.Update();
if (PreviewPlaying && Preview != null)
{
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
}
}
protected override bool OnHover(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
PlayButton.FadeIn(120, Easing.InOutQuint);
return base.OnHover(state);
}
@ -114,10 +134,21 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
if (!PreviewPlaying)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
ShowInformation();
PreviewPlaying.Value = false;
return true;
}
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
protected void StartDownload()
{
if (!api.LocalUser.Value.IsSupporter)
@ -173,6 +204,9 @@ namespace osu.Game.Overlays.Direct
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint);
PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint);
}
protected List<DifficultyIcon> GetDifficultyIcons()

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2017 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.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Direct
{
public class DownloadButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public DownloadButton()
{
Children = new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(30),
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
}
}
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Direct
public Header()
{
Tabs.Current.Value = DirectTab.Search;
Tabs.Current.Value = DirectTab.NewestMaps;
Tabs.Current.TriggerChange();
}
}

View File

@ -0,0 +1,181 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Direct
{
public class PlayButton : Container
{
public readonly Bindable<bool> Playing = new Bindable<bool>();
public Track Preview { get; private set; }
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Playing.Value = false;
trackLoader = null;
Preview = null;
}
}
private Color4 hoverColour;
private readonly SpriteIcon icon;
private readonly LoadingAnimation loadingAnimation;
private const float transition_duration = 500;
private bool loading
{
set
{
if (value)
{
loadingAnimation.Show();
icon.FadeOut(transition_duration * 5, Easing.OutQuint);
}
else
{
loadingAnimation.Hide();
icon.FadeIn(transition_duration, Easing.OutQuint);
}
}
}
public PlayButton(BeatmapSetInfo setInfo = null)
{
BeatmapSet = setInfo;
AddRange(new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_play,
},
loadingAnimation = new LoadingAnimation(),
});
Playing.ValueChanged += playing =>
{
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
updatePreviewTrack(playing);
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
hoverColour = colour.Yellow;
}
protected override bool OnClick(InputState state)
{
Playing.Value = !Playing.Value;
return true;
}
protected override bool OnHover(InputState state)
{
icon.FadeColour(hoverColour, 120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (!Playing.Value)
icon.FadeColour(Color4.White, 120, Easing.InOutQuint);
base.OnHoverLost(state);
}
protected override void Update()
{
base.Update();
if (Preview?.HasCompleted ?? false)
{
Playing.Value = false;
Preview = null;
}
}
private void updatePreviewTrack(bool playing)
{
if (playing)
{
if (Preview == null)
{
beginAudioLoad();
return;
}
Preview.Seek(0);
Preview.Start();
}
else
{
Preview?.Stop();
}
}
private TrackLoader trackLoader;
private void beginAudioLoad()
{
if (trackLoader != null) return;
Add(new AsyncLoadWrapper(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3")
{
OnLoadComplete = d =>
{
// we may have been replaced by another loader
if (trackLoader != d) return;
Preview = (d as TrackLoader)?.Preview;
Playing.TriggerChange();
},
}));
}
private class TrackLoader : Drawable
{
private readonly string preview;
public Track Preview;
public TrackLoader(string preview)
{
this.preview = preview;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
if (!string.IsNullOrEmpty(preview))
{
Preview = audio.Track.Get(preview);
Preview.Volume.Value = 0.5;
}
}
}
}
}

View File

@ -32,6 +32,7 @@ namespace osu.Game.Overlays
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
private FillFlowContainer<DirectPanel> panels;
private DirectPanel playing;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
@ -201,6 +202,12 @@ namespace osu.Game.Overlays
panels.FadeOut(200);
panels.Expire();
panels = null;
if (playing != null)
{
playing.PreviewPlaying.Value = false;
playing = null;
}
}
if (BeatmapSets == null) return;
@ -227,6 +234,17 @@ namespace osu.Game.Overlays
{
if (panels != null) ScrollFlow.Remove(panels);
ScrollFlow.Add(panels = newPanels);
foreach (DirectPanel panel in p.Children)
panel.PreviewPlaying.ValueChanged += newValue =>
{
if (newValue)
{
if (playing != null && playing != panel)
playing.PreviewPlaying.Value = false;
playing = panel;
}
};
});
}
@ -251,7 +269,7 @@ namespace osu.Game.Overlays
if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return;
getSetsRequest = new GetBeatmapSetsRequest(currentQuery,
getSetsRequest = new GetBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
((FilterControl)Filter).Ruleset.Value,
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)

View File

@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings.Sections.General;
using OpenTK.Graphics;
@ -28,35 +29,43 @@ namespace osu.Game.Overlays
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
new OsuContextMenuContainer
{
Width = 360,
AutoSizeAxes = Axes.Y,
Masking = true,
AutoSizeDuration = transition_time,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
settingsSection = new LoginSettings
{
Padding = new MarginPadding(10),
RequestHide = Hide,
},
new Box
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 3,
Colour = colours.Yellow,
Alpha = 1,
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Masking = true,
AutoSizeDuration = transition_time,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
settingsSection = new LoginSettings
{
Padding = new MarginPadding(10),
RequestHide = Hide,
},
new Box
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 3,
Colour = colours.Yellow,
Alpha = 1,
},
}
}
}
}
};

View File

@ -159,7 +159,7 @@ namespace osu.Game.Overlays
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Both,
});
}

View File

@ -90,6 +90,7 @@ namespace osu.Game.Overlays.MedalSplash
},
description = new TextFlowContainer
{
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
@ -115,15 +116,16 @@ namespace osu.Game.Overlays.MedalSplash
medalSprite.Texture = textures.Get(medal.ImageUrl);
medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow");
description.Colour = colours.BlueLight;
unlocked.Position = new Vector2(0f, medalContainer.Size.Y / 2 + 10);
infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateState();
unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10);
infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90);
}
public DisplayState State
@ -172,6 +174,7 @@ namespace osu.Game.Overlays.MedalSplash
this.ScaleTo(scale_when_full, duration, Easing.OutExpo);
this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, Easing.OutExpo);
unlocked.Show();
name.FadeInFromZero(duration + 100);
description.FadeInFromZero(duration * 2);
break;

View File

@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Mods
if (mod == null)
{
Mods = new Mod[0];
Mods = Array.Empty<Mod>();
Alpha = 0;
}
else

View File

@ -161,11 +161,15 @@ namespace osu.Game.Overlays
{
prevButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = prev,
Icon = FontAwesome.fa_step_backward,
},
playButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = play,
@ -173,6 +177,8 @@ namespace osu.Game.Overlays
},
nextButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = next,
Icon = FontAwesome.fa_step_forward,
},

View File

@ -19,6 +19,7 @@ using osu.Game.Users;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Overlays.Profile
{
@ -119,15 +120,11 @@ namespace osu.Game.Overlays.Profile
}
}
},
new LinkFlowContainer.LinkText
new LinkFlowContainer.ProfileLink(user)
{
Text = user.Username,
Url = $@"https://osu.ppy.sh/users/{user.Id}",
TextSize = 30,
Font = @"Exo2.0-RegularItalic",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -48
Y = -48,
},
countryFlag = new DrawableFlag(user.Country?.FlagName)
{
@ -534,6 +531,19 @@ namespace osu.Game.Overlays.Profile
});
}
}
public class ProfileLink : LinkText, IHasTooltip
{
public string TooltipText => "View Profile in Browser";
public ProfileLink(User user)
{
Text = user.Username;
Url = $@"https://osu.ppy.sh/users/{user.Id}";
Font = @"Exo2.0-RegularItalic";
TextSize = 30;
}
}
}
}
}

View File

@ -1,10 +1,30 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Graphics
{
public class DetailSettings : SettingsSubsection
{
protected override string Header => "Detail Settings";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
{
new SettingsCheckbox
{
LabelText = "Storyboards",
Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard)
},
new SettingsCheckbox
{
LabelText = "Rotate cursor when dragging",
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
},
};
}
}
}

View File

@ -34,6 +34,7 @@ namespace osu.Game.Overlays
public const float CONTENT_X_MARGIN = 50;
// receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
protected override bool OnClick(InputState state)

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Containers;
@ -165,10 +164,8 @@ namespace osu.Game.Overlays
wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y));
}
private class Wave : Container, IStateful<Visibility>
private class Wave : VisibilityContainer
{
public event Action<Visibility> StateChanged;
public float FinalPosition;
public Wave()
@ -183,13 +180,7 @@ namespace osu.Game.Overlays
Radius = 20f,
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
};
Child = new Box { RelativeSizeAxes = Axes.Both };
}
protected override void Update()
@ -201,28 +192,8 @@ namespace osu.Game.Overlays
Height = Parent.Parent.DrawSize.Y * 1.5f;
}
private Visibility state;
public Visibility State
{
get { return state; }
set
{
state = value;
switch (value)
{
case Visibility.Hidden:
this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
break;
case Visibility.Visible:
this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
break;
}
StateChanged?.Invoke(State);
}
}
protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
}
}
}

View File

@ -25,6 +25,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary>
public virtual Color4 AccentColour { get; set; } = Color4.Gray;
/// <summary>
/// Whether a visible judgement should be displayed when this representation is hit.
/// </summary>
public virtual bool DisplayJudgement => true;
protected DrawableHitObject(HitObject hitObject)
{
HitObject = hitObject;

View File

@ -200,4 +200,4 @@ namespace osu.Game.Rulesets.Objects
return interpolateVertices(indexOfDistance(d), d) + Offset;
}
}
}
}

View File

@ -38,7 +38,6 @@ namespace osu.Game.Rulesets
protected override void Prepare(bool reset = false)
{
Connection.CreateTable<RulesetInfo>();
if (reset)

View File

@ -40,6 +40,6 @@ namespace osu.Game.Rulesets.Scoring
public DateTimeOffset Date;
public Dictionary<string, dynamic> Statistics = new Dictionary<string, dynamic>();
public Dictionary<string, object> Statistics = new Dictionary<string, object>();
}
}

View File

@ -51,6 +51,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
public readonly BindableInt Combo = new BindableInt();
/// <summary>
/// The current rank.
/// </summary>
public readonly Bindable<ScoreRank> Rank = new Bindable<ScoreRank>(ScoreRank.X);
/// <summary>
/// THe highest combo achieved by this score.
/// </summary>
@ -74,6 +79,7 @@ namespace osu.Game.Rulesets.Scoring
protected ScoreProcessor()
{
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
Accuracy.ValueChanged += delegate { Rank.Value = rankFrom(Accuracy.Value); };
}
private ScoreRank rankFrom(double acc)
@ -101,6 +107,7 @@ namespace osu.Game.Rulesets.Scoring
Accuracy.Value = 1;
Health.Value = 1;
Combo.Value = 0;
Rank.Value = ScoreRank.X;
HighestCombo.Value = 0;
alreadyFailed = false;
@ -142,7 +149,7 @@ namespace osu.Game.Rulesets.Scoring
score.Combo = Combo;
score.MaxCombo = HighestCombo;
score.Accuracy = Accuracy;
score.Rank = rankFrom(Accuracy);
score.Rank = Rank;
score.Date = DateTimeOffset.Now;
score.Health = Health;
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays bookmarks.
/// </summary>
internal class BookmarkPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
base.LoadBeatmap(beatmap);
foreach (int bookmark in beatmap.BeatmapInfo.Bookmarks)
Add(new BookmarkVisualisation(bookmark));
}
private class BookmarkVisualisation : PointVisualisation
{
public BookmarkVisualisation(double startTime)
: base(startTime)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Blue;
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays breaks in the song.
/// </summary>
internal class BreakPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
base.LoadBeatmap(beatmap);
foreach (var breakPeriod in beatmap.Beatmap.Breaks)
Add(new BreakVisualisation(breakPeriod));
}
private class BreakVisualisation : DurationVisualisation
{
public BreakVisualisation(BreakPeriod breakPeriod)
: base(breakPeriod.StartTime, breakPeriod.EndTime)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Yellow;
}
}
}

View File

@ -0,0 +1,70 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays the control points.
/// </summary>
internal class ControlPointPart : TimelinePart
{
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
base.LoadBeatmap(beatmap);
ControlPointInfo cpi = beatmap.Beatmap.ControlPointInfo;
cpi.TimingPoints.ForEach(addTimingPoint);
// Consider all non-timing points as the same type
cpi.SoundPoints.Select(c => (ControlPoint)c)
.Concat(cpi.EffectPoints)
.Concat(cpi.DifficultyPoints)
.Distinct()
// Non-timing points should not be added where there are timing points
.Where(c => cpi.TimingPointAt(c.Time).Time != c.Time)
.ForEach(addNonTimingPoint);
}
private void addTimingPoint(ControlPoint controlPoint) => Add(new TimingPointVisualisation(controlPoint));
private void addNonTimingPoint(ControlPoint controlPoint) => Add(new NonTimingPointVisualisation(controlPoint));
private class TimingPointVisualisation : ControlPointVisualisation
{
public TimingPointVisualisation(ControlPoint controlPoint)
: base(controlPoint)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.YellowDark;
}
private class NonTimingPointVisualisation : ControlPointVisualisation
{
public NonTimingPointVisualisation(ControlPoint controlPoint)
: base(controlPoint)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Green;
}
private abstract class ControlPointVisualisation : PointVisualisation
{
protected ControlPointVisualisation(ControlPoint controlPoint)
: base(controlPoint.Time)
{
}
}
}
}

View File

@ -0,0 +1,106 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
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.Beatmaps;
using osu.Game.Graphics;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// The part of the timeline that displays the current position of the song.
/// </summary>
internal class MarkerPart : TimelinePart
{
private readonly Drawable marker;
public MarkerPart()
{
Add(marker = new MarkerVisualisation());
}
protected override bool OnDragStart(InputState state) => true;
protected override bool OnDragEnd(InputState state) => true;
protected override bool OnDrag(InputState state)
{
seekToPosition(state.Mouse.NativeState.Position);
return true;
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
seekToPosition(state.Mouse.NativeState.Position);
return true;
}
/// <summary>
/// Seeks the <see cref="SummaryTimeline"/> to the time closest to a position on the screen relative to the <see cref="SummaryTimeline"/>.
/// </summary>
/// <param name="screenPosition">The position in screen coordinates.</param>
private void seekToPosition(Vector2 screenPosition)
{
if (Beatmap.Value == null)
return;
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
seekTo(markerPos / DrawWidth * Beatmap.Value.Track.Length);
}
private void seekTo(double time) => Beatmap.Value?.Track.Seek(time);
protected override void Update()
{
base.Update();
marker.X = (float)(Beatmap.Value?.Track.CurrentTime ?? 0);
}
protected override void LoadBeatmap(WorkingBeatmap beatmap)
{
// block base call so we don't clear our marker (can be reused on beatmap change).
}
private class MarkerVisualisation : CompositeDrawable
{
public MarkerVisualisation()
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
AutoSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new Triangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Scale = new Vector2(1, -1),
Size = new Vector2(10, 5),
},
new Triangle
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(10, 5)
},
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Width = 2,
EdgeSmoothness = new Vector2(1, 0)
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours) => Colour = colours.Red;
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
{
/// <summary>
/// Represents a part of the summary timeline..
/// </summary>
internal abstract class TimelinePart : CompositeDrawable
{
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly Container timeline;
protected TimelinePart()
{
AddInternal(timeline = new Container { RelativeSizeAxes = Axes.Both });
Beatmap.ValueChanged += b =>
{
updateRelativeChildSize();
LoadBeatmap(b);
};
}
private void updateRelativeChildSize()
{
// the track may not be loaded completely (only has a length once it is).
if (!Beatmap.Value.Track.IsLoaded)
{
timeline.RelativeChildSize = Vector2.One;
Schedule(updateRelativeChildSize);
return;
}
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
}
protected void Add(Drawable visualisation) => timeline.Add(visualisation);
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
{
timeline.Clear();
}
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary
{
/// <summary>
/// The timeline that sits at the bottom of the editor.
/// </summary>
public class SummaryTimeline : CompositeDrawable
{
private const float corner_radius = 5;
private const float contents_padding = 15;
public Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly Drawable background;
private readonly Drawable timelineBar;
public SummaryTimeline()
{
Masking = true;
CornerRadius = corner_radius;
TimelinePart markerPart, controlPointPart, bookmarkPart, breakPart;
InternalChildren = new[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = contents_padding, Right = contents_padding },
Children = new[]
{
markerPart = new MarkerPart { RelativeSizeAxes = Axes.Both },
controlPointPart = new ControlPointPart
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
bookmarkPart = new BookmarkPart
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.35f
},
timelineBar = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Circle
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(5)
},
new Box
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
EdgeSmoothness = new Vector2(0, 1),
},
new Circle
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(5)
},
}
},
breakPart = new BreakPart
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Height = 0.25f
}
}
}
};
markerPart.Beatmap.BindTo(Beatmap);
controlPointPart.Beatmap.BindTo(Beatmap);
bookmarkPart.Beatmap.BindTo(Beatmap);
breakPart.Beatmap.BindTo(Beatmap);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray1;
timelineBar.Colour = colours.Gray5;
}
}
}

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2017 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.Shapes;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
{
/// <summary>
/// Represents a spanning point on a timeline part.
/// </summary>
internal class DurationVisualisation : Container
{
protected DurationVisualisation(double startTime, double endTime)
{
Masking = true;
CornerRadius = 5;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Both;
X = (float)startTime;
Width = (float)(endTime - startTime);
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
}
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
{
/// <summary>
/// Represents a singular point on a timeline part.
/// </summary>
internal class PointVisualisation : Box
{
protected PointVisualisation(double startTime)
{
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Y;
Width = 1;
EdgeSmoothness = new Vector2(1, 0);
RelativePositionAxes = Axes.X;
X = (float)startTime;
}
}
}

View File

@ -1,194 +1,141 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Select;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Menus;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Screens;
using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Screens.Edit.Screens.Design;
namespace osu.Game.Screens.Edit
{
internal class Editor : ScreenWhiteBox
internal class Editor : OsuScreen
{
protected override IEnumerable<Type> PossibleChildren => new[] { typeof(EditSongSelect) };
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
internal override bool ShowOverlays => false;
private readonly Box bottomBackground;
private readonly Container screenContainer;
private EditorScreen currentScreen;
public Editor()
{
Add(new Container
EditorMenuBar menuBar;
SummaryTimeline timeline;
Children = new[]
{
RelativeSizeAxes = Axes.X,
Height = 40,
Children = new Drawable[]
new Container
{
new Box
Name = "Screen container",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 40, Bottom = 60 },
Child = screenContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex("111")
},
new EditorMenuBar
Masking = true
}
},
new Container
{
Name = "Top bar",
RelativeSizeAxes = Axes.X,
Height = 40,
Child = menuBar = new EditorMenuBar
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
X = 100,
RelativeSizeAxes = Axes.Both,
Items = new[]
{
new EditorMenuBarItem("File")
new MenuItem("File")
{
Items = new[]
{
new EditorMenuItem("Clear all notes"),
new EditorMenuItem("Open difficulty..."),
new EditorMenuItem("Save"),
new EditorMenuItem("Create new difficulty..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Revert to saved"),
new EditorMenuItem("Revert to saved (full"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Test beatmap"),
new EditorMenuItem("Open AiMod"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Upload Beatmap..."),
new EditorMenuItem("Export package"),
new EditorMenuItem("Export map package"),
new EditorMenuItem("Import from..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Open song folder"),
new EditorMenuItem("Open .osu in Notepad"),
new EditorMenuItem("Open .osb in Notepad"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Exit", MenuItemType.Standard, Exit)
}
},
new EditorMenuBarItem("Edit")
{
Items = new[]
{
new EditorMenuItem("Undo"),
new EditorMenuItem("Redo"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Cut"),
new EditorMenuItem("Copy"),
new EditorMenuItem("Paste"),
new EditorMenuItem("Delete"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Select all"),
new EditorMenuItem("Clone"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Reverse selection"),
new EditorMenuItem("Flip horizontally"),
new EditorMenuItem("Flip vertically"),
new EditorMenuItem("Rotate 90deg clockwise"),
new EditorMenuItem("Rotate 90deg anticlockwise"),
new EditorMenuItem("Rotate by..."),
new EditorMenuItem("Scale by..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Reset selected objects' samples"),
new EditorMenuItem("Reset all samples", MenuItemType.Destructive),
new EditorMenuItem("Reset combo colours", MenuItemType.Destructive),
new EditorMenuItem("Reset breaks", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Nudge backward"),
new EditorMenuItem("Nudge forward")
}
},
new EditorMenuBarItem("View")
{
Items = new[]
{
new EditorMenuItem("Compose"),
new EditorMenuItem("Design"),
new EditorMenuItem("Timing"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Song setup..."),
new EditorMenuItem("Timing setup..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Volume"),
new EditorMenuItem("Grid level"),
new EditorMenuItem("Show video"),
new EditorMenuItem("Show sample name"),
new EditorMenuItem("Snaking sliders"),
new EditorMenuItem("Hit animations"),
new EditorMenuItem("Follow points"),
new EditorMenuItem("Stacking")
}
},
new EditorMenuBarItem("Compose")
{
Items = new[]
{
new EditorMenuItem("Snap divisor"),
new EditorMenuItem("Audio rate"),
new EditorMenuItem("Grid snapping"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Create polygon cricles..."),
new EditorMenuItem("Convert slider to stream"),
new EditorMenuItem("Enable live mapping mode"),
new EditorMenuItem("Sample import")
}
},
new EditorMenuBarItem("Design")
{
Items = new[]
{
new EditorMenuItem("Move all elements in time...")
}
},
new EditorMenuBarItem("Timing")
{
Items = new[]
{
new EditorMenuItem("Time signature"),
new EditorMenuItem("Metronome clicks"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Add timing section"),
new EditorMenuItem("Add inheriting section"),
new EditorMenuItem("Reset current section"),
new EditorMenuItem("Delete timing section"),
new EditorMenuItem("Resnap current section"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Timing setup..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Resnap all notes", MenuItemType.Destructive),
new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
new EditorMenuItem("Recalculate slider lengths", MenuItemType.Destructive),
new EditorMenuItem("Delete all timing sections", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Set current position as preview point")
}
},
new EditorMenuBarItem("Web")
{
Items = new[]
{
new EditorMenuItem("This Beatmap's information page"),
new EditorMenuItem("This Beatmap's thread"),
new EditorMenuItem("Quick reply")
}
},
new EditorMenuBarItem("Help")
{
Items = new[]
{
new EditorMenuItem("Show in-game help"),
new EditorMenuItem("View FAQ")
}
}
}
}
}
});
},
new Container
{
Name = "Bottom bar",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 60,
Children = new Drawable[]
{
bottomBackground = new Box { RelativeSizeAxes = Axes.Both },
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 5, Bottom = 5, Left = 10, Right = 10 },
Child = new FillFlowContainer
{
Name = "Bottom bar",
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new[]
{
timeline = new SummaryTimeline
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Width = 0.65f
}
}
}
}
}
},
};
timeline.Beatmap.BindTo(Beatmap);
menuBar.Mode.ValueChanged += onModeChanged;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
bottomBackground.Colour = colours.Gray2;
}
private void onModeChanged(EditorScreenMode mode)
{
currentScreen?.Exit();
switch (mode)
{
case EditorScreenMode.Compose:
currentScreen = new Compose();
break;
case EditorScreenMode.Design:
currentScreen = new Design();
break;
default:
currentScreen = new EditorScreen();
break;
}
currentScreen.Beatmap.BindTo(Beatmap);
screenContainer.Add(currentScreen);
}
protected override void OnResuming(Screen last)

View File

@ -11,16 +11,42 @@ using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Game.Screens.Edit.Screens;
namespace osu.Game.Screens.Edit.Menus
{
public class EditorMenuBar : OsuMenu
{
public readonly Bindable<EditorScreenMode> Mode = new Bindable<EditorScreenMode>();
public EditorMenuBar()
: base(Direction.Horizontal, true)
{
ItemsContainer.Padding = new MarginPadding(0);
BackgroundColour = Color4.Transparent;
RelativeSizeAxes = Axes.X;
MaskingContainer.CornerRadius = 0;
ItemsContainer.Padding = new MarginPadding { Left = 100 };
BackgroundColour = OsuColour.FromHex("111");
ScreenSelectionTabControl tabControl;
AddRangeInternal(new Drawable[]
{
tabControl = new ScreenSelectionTabControl
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
X = -15
}
});
Mode.BindTo(tabControl.Current);
}
protected override void LoadComplete()
{
base.LoadComplete();
Mode.TriggerChange();
}
protected override Framework.Graphics.UserInterface.Menu CreateSubMenu() => new SubMenu();
@ -29,27 +55,35 @@ namespace osu.Game.Screens.Edit.Menus
private class DrawableEditorBarMenuItem : DrawableOsuMenuItem
{
private Color4 openedForegroundColour;
private Color4 openedBackgroundColour;
private BackgroundBox background;
public DrawableEditorBarMenuItem(MenuItem item)
: base(item)
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
StateChanged += stateChanged;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ForegroundColour = ForegroundColourHover = colours.BlueLight;
BackgroundColour = BackgroundColourHover = Color4.Transparent;
openedForegroundColour = Color4.White;
openedBackgroundColour = colours.Gray3;
ForegroundColour = colours.BlueLight;
BackgroundColour = Color4.Transparent;
ForegroundColourHover = Color4.White;
BackgroundColourHover = colours.Gray3;
}
public override void SetFlowDirection(Direction direction)
{
AutoSizeAxes = Axes.Both;
}
protected override void UpdateBackgroundColour()
{
if (State == MenuItemState.Selected)
Background.FadeColour(openedBackgroundColour);
Background.FadeColour(BackgroundColourHover);
else
base.UpdateBackgroundColour();
}
@ -57,24 +91,58 @@ namespace osu.Game.Screens.Edit.Menus
protected override void UpdateForegroundColour()
{
if (State == MenuItemState.Selected)
Foreground.FadeColour(openedForegroundColour);
Foreground.FadeColour(ForegroundColourHover);
else
base.UpdateForegroundColour();
}
protected override Drawable CreateBackground() => new Container
private void stateChanged(MenuItemState newState)
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = new Container
if (newState == MenuItemState.Selected)
background.Expand();
else
background.Contract();
}
protected override Drawable CreateBackground() => background = new BackgroundBox();
protected override DrawableOsuMenuItem.TextContainer CreateTextContainer() => new TextContainer();
private new class TextContainer : DrawableOsuMenuItem.TextContainer
{
public TextContainer()
{
RelativeSizeAxes = Axes.Both,
Height = 2,
Masking = true,
CornerRadius = 4,
Child = new Box { RelativeSizeAxes = Axes.Both }
NormalText.TextSize = BoldText.TextSize = 14;
NormalText.Margin = BoldText.Margin = new MarginPadding { Horizontal = 10, Vertical = MARGIN_VERTICAL };
}
};
}
private class BackgroundBox : CompositeDrawable
{
private readonly Container innerBackground;
public BackgroundBox()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
InternalChild = innerBackground = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
Child = new Box { RelativeSizeAxes = Axes.Both }
};
}
/// <summary>
/// Expands the background such that it doesn't show the bottom corners.
/// </summary>
public void Expand() => innerBackground.Height = 2;
/// <summary>
/// Contracts the background such that it shows the bottom corners.
/// </summary>
public void Contract() => innerBackground.Height = 1;
}
}
private class SubMenu : OsuMenu

View File

@ -1,15 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Menus
{
public class EditorMenuBarItem : MenuItem
{
public EditorMenuBarItem(string text)
: base(text)
{
}
}
}

View File

@ -0,0 +1,75 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Screens;
using OpenTK;
namespace osu.Game.Screens.Edit.Menus
{
public class ScreenSelectionTabControl : OsuTabControl<EditorScreenMode>
{
public ScreenSelectionTabControl()
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
TabContainer.RelativeSizeAxes &= ~Axes.X;
TabContainer.AutoSizeAxes = Axes.X;
TabContainer.Padding = new MarginPadding();
Add(new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = Color4.White.Opacity(0.2f),
});
Current.Value = EditorScreenMode.Compose;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Yellow;
}
protected override Dropdown<EditorScreenMode> CreateDropdown() => null;
protected override TabItem<EditorScreenMode> CreateTabItem(EditorScreenMode value) => new TabItem(value);
private class TabItem : OsuTabItem
{
private const float transition_length = 250;
public TabItem(EditorScreenMode value)
: base(value)
{
Text.Margin = new MarginPadding();
Text.Anchor = Anchor.CentreLeft;
Text.Origin = Anchor.CentreLeft;
}
protected override void OnActivated()
{
base.OnActivated();
Bar.ScaleTo(new Vector2(1, 5), transition_length, Easing.OutQuint);
}
protected override void OnDeactivated()
{
base.OnDeactivated();
Bar.ScaleTo(Vector2.One, transition_length, Easing.OutQuint);
}
}
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Edit.Screens.Compose.Timeline;
namespace osu.Game.Screens.Edit.Screens.Compose
{
public class Compose : EditorScreen
{
public Compose()
{
ScrollableTimeline timeline;
Children = new[]
{
new Container
{
Name = "Timeline",
RelativeSizeAxes = Axes.X,
Height = 110,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f)
},
new Container
{
Name = "Content",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 17, Vertical = 10 },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 115 },
Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
}
}
}
}
}
};
timeline.Beatmap.BindTo(Beatmap);
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
{
public class BeatmapWaveformGraph : CompositeDrawable
{
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly WaveformGraph graph;
public BeatmapWaveformGraph()
{
InternalChild = graph = new WaveformGraph { RelativeSizeAxes = Axes.Both };
Beatmap.ValueChanged += b => graph.Waveform = b.Waveform;
}
/// <summary>
/// Gets or sets the <see cref="WaveformGraph.Resolution"/>.
/// </summary>
public float Resolution
{
get { return graph.Resolution; }
set { graph.Resolution = value; }
}
}
}

View File

@ -0,0 +1,131 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
{
public class ScrollableTimeline : CompositeDrawable
{
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly ScrollingTimelineContainer timelineContainer;
public ScrollableTimeline()
{
Masking = true;
CornerRadius = 5;
OsuCheckbox hitObjectsCheckbox;
OsuCheckbox hitSoundsCheckbox;
OsuCheckbox waveformCheckbox;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex("111")
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex("222")
},
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
Width = 160,
Padding = new MarginPadding { Horizontal = 15 },
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 4),
Children = new[]
{
hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hitobjects" },
hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hitsounds" },
waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" }
}
}
}
},
new Container
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex("333")
},
new Container<TimelineButton>
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
Children = new[]
{
new TimelineButton
{
RelativeSizeAxes = Axes.Y,
Height = 0.5f,
Icon = FontAwesome.fa_search_plus,
Action = () => timelineContainer.Zoom++
},
new TimelineButton
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Y,
Height = 0.5f,
Icon = FontAwesome.fa_search_minus,
Action = () => timelineContainer.Zoom--
},
}
}
}
},
timelineContainer = new ScrollingTimelineContainer { RelativeSizeAxes = Axes.Y }
}
}
};
hitObjectsCheckbox.Current.Value = true;
hitSoundsCheckbox.Current.Value = true;
waveformCheckbox.Current.Value = true;
timelineContainer.Beatmap.BindTo(Beatmap);
timelineContainer.WaveformVisible.BindTo(waveformCheckbox.Current);
}
protected override void Update()
{
base.Update();
timelineContainer.Size = new Vector2(DrawSize.X - timelineContainer.DrawPosition.X, 1);
}
}
}

View File

@ -0,0 +1,141 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
{
internal class ScrollingTimelineContainer : ScrollContainer
{
public readonly Bindable<bool> HitObjectsVisible = new Bindable<bool>();
public readonly Bindable<bool> HitSoundsVisible = new Bindable<bool>();
public readonly Bindable<bool> WaveformVisible = new Bindable<bool>();
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private readonly BeatmapWaveformGraph waveform;
public ScrollingTimelineContainer()
: base(Direction.Horizontal)
{
Masking = true;
Add(waveform = new BeatmapWaveformGraph
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex("222"),
Depth = float.MaxValue
});
Content.AutoSizeAxes = Axes.None;
Content.RelativeSizeAxes = Axes.Both;
waveform.Beatmap.BindTo(Beatmap);
WaveformVisible.ValueChanged += waveformVisibilityChanged;
Zoom = 10;
}
private float minZoom = 1;
/// <summary>
/// The minimum zoom level allowed.
/// </summary>
public float MinZoom
{
get { return minZoom; }
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value));
if (minZoom == value)
return;
minZoom = value;
// Update the zoom level
Zoom = Zoom;
}
}
private float maxZoom = 30;
/// <summary>
/// The maximum zoom level allowed.
/// </summary>
public float MaxZoom
{
get { return maxZoom; }
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException(nameof(value));
if (maxZoom == value)
return;
maxZoom = value;
// Update the zoom level
Zoom = Zoom;
}
}
private float zoom = 1;
/// <summary>
/// The current zoom level.
/// </summary>
public float Zoom
{
get { return zoom; }
set
{
value = MathHelper.Clamp(value, MinZoom, MaxZoom);
if (zoom == value)
return;
zoom = value;
// Make the zoom target default to the center of the graph if it hasn't been set
if (relativeContentZoomTarget == null)
relativeContentZoomTarget = ToSpaceOfOtherDrawable(DrawSize / 2, Content).X / Content.DrawSize.X;
if (localZoomTarget == null)
localZoomTarget = DrawSize.X / 2;
Content.ResizeWidthTo(Zoom);
// Update the scroll position to focus on the zoom target
float scrollPos = Content.DrawSize.X * relativeContentZoomTarget.Value - localZoomTarget.Value;
ScrollTo(scrollPos, false);
relativeContentZoomTarget = null;
localZoomTarget = null;
}
}
/// <summary>
/// Zoom target as a relative position in the <see cref="Content"/> space.
/// </summary>
private float? relativeContentZoomTarget;
/// <summary>
/// Zoom target as a position in our local space.
/// </summary>
private float? localZoomTarget;
protected override bool OnWheel(InputState state)
{
if (!state.Keyboard.ControlPressed)
return base.OnWheel(state);
relativeContentZoomTarget = Content.ToLocalSpace(state.Mouse.NativeState.Position).X / Content.DrawSize.X;
localZoomTarget = ToLocalSpace(state.Mouse.NativeState.Position).X;
Zoom += state.Mouse.WheelDelta;
return true;
}
private void waveformVisibilityChanged(bool visible) => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint);
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
{
public class TimelineButton : CompositeDrawable
{
public Action Action;
public readonly BindableBool Enabled = new BindableBool(true);
public FontAwesome Icon
{
get { return button.Icon; }
set { button.Icon = value; }
}
private readonly IconButton button;
public TimelineButton()
{
InternalChild = button = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
IconColour = OsuColour.Gray(0.35f),
IconHoverColour = Color4.White,
HoverColour = OsuColour.Gray(0.25f),
FlashColour = OsuColour.Gray(0.5f),
Action = () => Action?.Invoke()
};
button.Enabled.BindTo(Enabled);
Width = button.ButtonSize.X;
}
protected override void Update()
{
base.Update();
button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight);
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 2007-2017 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.Shapes;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
namespace osu.Game.Screens.Edit.Screens.Design
{
internal class Design : EditorScreen
{
public Design()
{
Add(new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.35f
},
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f
},
new Container
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Child = new OsuSpriteText { Text = "Design screen" }
}
}
}
}
});
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit.Screens
{
public class EditorScreen : Container
{
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
protected override Container<Drawable> Content => content;
private readonly Container content;
public EditorScreen()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
InternalChild = content = new Container { RelativeSizeAxes = Axes.Both };
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeTo(0)
.Then()
.FadeTo(1f, 250, Easing.OutQuint);
}
public void Exit()
{
this.FadeOut(250).Expire();
}
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Screens.Edit.Screens
{
public enum EditorScreenMode
{
[Description("setup")]
SongSetup,
[Description("compose")]
Compose,
[Description("design")]
Design,
[Description("timing")]
Timing,
}
}

View File

@ -121,13 +121,14 @@ namespace osu.Game.Screens.Menu
};
}
private bool rightward;
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
if (!IsHovered) return;
bool rightward = beatIndex % 2 == 1;
double duration = timingPoint.BeatLength / 2;
icon.RotateTo(rightward ? 10 : -10, duration * 2, Easing.InOutSine);
@ -139,6 +140,8 @@ namespace osu.Game.Screens.Menu
i => i.MoveToY(0, duration, Easing.In),
i => i.ScaleTo(new Vector2(1, 0.9f), duration, Easing.In)
);
rightward = !rightward;
}
protected override bool OnHover(InputState state)
@ -152,7 +155,7 @@ namespace osu.Game.Screens.Menu
double duration = TimeUntilNextBeat;
icon.ClearTransforms();
icon.RotateTo(10, duration, Easing.InOutSine);
icon.RotateTo(rightward ? -10 : 10, duration, Easing.InOutSine);
icon.ScaleTo(new Vector2(1, 0.9f), duration, Easing.Out);
return true;
}

View File

@ -0,0 +1,103 @@
// Copyright (c) 2007-2017 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.Framework.Graphics;
using OpenTK;
using osu.Game.Graphics.Containers;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class ArrowsOverlay : VisibilityContainer
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private const int glow_icon_size = 60;
private const int glow_icon_blur_sigma = 10;
private const float glow_icon_final_offset = 0.22f;
private const float glow_icon_offscreen_offset = 0.6f;
private const int blurred_icon_blur_sigma = 20;
private const int blurred_icon_size = 130;
private const float blurred_icon_final_offset = 0.35f;
private const float blurred_icon_offscreen_offset = 0.7f;
private readonly GlowIcon leftGlowIcon;
private readonly GlowIcon rightGlowIcon;
private readonly BlurredIcon leftBlurredIcon;
private readonly BlurredIcon rightBlurredIcon;
public ArrowsOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
leftGlowIcon = new GlowIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
X = -glow_icon_offscreen_offset,
Icon = Graphics.FontAwesome.fa_chevron_right,
BlurSigma = new Vector2(glow_icon_blur_sigma),
Size = new Vector2(glow_icon_size),
},
rightGlowIcon = new GlowIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
X = glow_icon_offscreen_offset,
Icon = Graphics.FontAwesome.fa_chevron_left,
BlurSigma = new Vector2(glow_icon_blur_sigma),
Size = new Vector2(glow_icon_size),
},
new ParallaxContainer
{
ParallaxAmount = -0.02f,
Children = new Drawable[]
{
leftBlurredIcon = new BlurredIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
Alpha = 0.7f,
X = -blurred_icon_offscreen_offset,
Icon = Graphics.FontAwesome.fa_chevron_right,
BlurSigma = new Vector2(blurred_icon_blur_sigma),
Size = new Vector2(blurred_icon_size),
},
rightBlurredIcon = new BlurredIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
Alpha = 0.7f,
X = blurred_icon_offscreen_offset,
Icon = Graphics.FontAwesome.fa_chevron_left,
BlurSigma = new Vector2(blurred_icon_blur_sigma),
Size = new Vector2(blurred_icon_size),
},
}
}
};
}
protected override void PopIn()
{
leftGlowIcon.MoveToX(-glow_icon_final_offset, fade_duration, Easing.OutQuint);
rightGlowIcon.MoveToX(glow_icon_final_offset, fade_duration, Easing.OutQuint);
leftBlurredIcon.MoveToX(-blurred_icon_final_offset, fade_duration, Easing.OutQuint);
rightBlurredIcon.MoveToX(blurred_icon_final_offset, fade_duration, Easing.OutQuint);
}
protected override void PopOut()
{
leftGlowIcon.MoveToX(-glow_icon_offscreen_offset, fade_duration, Easing.OutQuint);
rightGlowIcon.MoveToX(glow_icon_offscreen_offset, fade_duration, Easing.OutQuint);
leftBlurredIcon.MoveToX(-blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint);
rightBlurredIcon.MoveToX(blurred_icon_offscreen_offset, fade_duration, Easing.OutQuint);
}
}
}

View File

@ -0,0 +1,51 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Framework.Allocation;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class BlurredIcon : BufferedContainer
{
private readonly SpriteIcon icon;
public FontAwesome Icon
{
set { icon.Icon = value; }
get { return icon.Icon; }
}
public override Vector2 Size
{
set
{
icon.Size = value;
base.Size = value + BlurSigma * 2.5f;
ForceRedraw();
}
get { return base.Size; }
}
public BlurredIcon()
{
RelativePositionAxes = Axes.X;
CacheDrawnFrameBuffer = true;
Child = icon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Shadow = false,
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.BlueLighter;
}
}
}

View File

@ -0,0 +1,151 @@
// Copyright (c) 2007-2017 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.Shapes;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Scoring;
using System.Collections.Generic;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class BreakOverlay : Container
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private const float remaining_time_container_max_size = 0.3f;
private const int vertical_margin = 25;
private List<BreakPeriod> breaks;
public List<BreakPeriod> Breaks
{
set
{
breaks = value;
initializeBreaks();
}
get
{
return breaks;
}
}
private readonly bool letterboxing;
private readonly LetterboxOverlay letterboxOverlay;
private readonly Container remainingTimeAdjustmentBox;
private readonly Container remainingTimeBox;
private readonly RemainingTimeCounter remainingTimeCounter;
private readonly InfoContainer info;
private readonly ArrowsOverlay arrowsOverlay;
public BreakOverlay(bool letterboxing)
{
this.letterboxing = letterboxing;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
letterboxOverlay = new LetterboxOverlay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
remainingTimeAdjustmentBox = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0,
Child = remainingTimeBox = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 8,
CornerRadius = 4,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
}
},
remainingTimeCounter = new RemainingTimeCounter
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
Margin = new MarginPadding { Bottom = vertical_margin },
},
info = new InfoContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = vertical_margin },
},
arrowsOverlay = new ArrowsOverlay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
};
}
private void initializeBreaks()
{
FinishTransforms(true);
Scheduler.CancelDelayedTasks();
if (breaks == null)
return;
foreach (var b in breaks)
{
if (!b.HasEffect)
continue;
using (BeginAbsoluteSequence(b.StartTime))
{
Schedule(() => onBreakIn(b));
using (BeginDelayedSequence(b.Duration - fade_duration))
Schedule(onBreakOut);
}
}
}
private void onBreakIn(BreakPeriod b)
{
if (letterboxing)
letterboxOverlay.Show();
remainingTimeAdjustmentBox
.ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
.Delay(b.Duration - fade_duration)
.ResizeWidthTo(0);
remainingTimeBox
.ResizeWidthTo(0, b.Duration - fade_duration)
.Then()
.ResizeWidthTo(1);
remainingTimeCounter.StartCounting(b.EndTime);
remainingTimeCounter.Show();
info.Show();
arrowsOverlay.Show();
}
private void onBreakOut()
{
if (letterboxing)
letterboxOverlay.Hide();
remainingTimeCounter.Hide();
info.Hide();
arrowsOverlay.Hide();
}
public void BindProcessor(ScoreProcessor processor)
{
info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
info.GradeDisplay.Current.BindTo(processor.Rank);
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 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.Framework.Graphics;
using osu.Game.Graphics;
using OpenTK;
using osu.Framework.Allocation;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class GlowIcon : Container
{
private readonly SpriteIcon spriteIcon;
private readonly BlurredIcon blurredIcon;
public override Vector2 Size
{
set
{
blurredIcon.Size = spriteIcon.Size = value;
blurredIcon.ForceRedraw();
}
get { return base.Size; }
}
public Vector2 BlurSigma
{
set { blurredIcon.BlurSigma = value; }
get { return blurredIcon.BlurSigma; }
}
public FontAwesome Icon
{
set { spriteIcon.Icon = blurredIcon.Icon = value; }
get { return spriteIcon.Icon; }
}
public GlowIcon()
{
RelativePositionAxes = Axes.X;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
blurredIcon = new BlurredIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
spriteIcon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shadow = false,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
blurredIcon.Colour = colours.Blue;
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class InfoContainer : VisibilityContainer
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
public PercentageInfoLine AccuracyDisplay;
public InfoLine<int> RankDisplay;
public InfoLine<ScoreRank> GradeDisplay;
public InfoContainer()
{
AutoSizeAxes = Axes.Both;
Child = new FillFlowContainer
{
Direction = FillDirection.Vertical,
Spacing = new Vector2(5),
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "current progress".ToUpper(),
TextSize = 15,
Font = "Exo2.0-Black",
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
AccuracyDisplay = new PercentageInfoLine("Accuracy"),
RankDisplay = new InfoLine<int>("Rank"),
GradeDisplay = new InfoLine<ScoreRank>("Grade"),
},
}
},
};
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
}
}

View File

@ -0,0 +1,82 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class InfoLine<T> : Container
where T : struct
{
private const int margin = 2;
public Bindable<T> Current = new Bindable<T>();
private readonly OsuSpriteText text;
private readonly OsuSpriteText valueText;
private readonly string prefix;
public InfoLine(string name, string prefix = @"")
{
this.prefix = prefix;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
Text = name,
TextSize = 17,
Margin = new MarginPadding { Right = margin }
},
valueText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
Text = prefix + @"-",
TextSize = 17,
Font = "Exo2.0-Bold",
Margin = new MarginPadding { Left = margin }
}
};
Current.ValueChanged += currentValueChanged;
}
private void currentValueChanged(T newValue)
{
var newText = prefix + Format(newValue);
if (valueText.Text == newText)
return;
valueText.Text = newText;
}
protected virtual string Format(T count) => count.ToString();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
text.Colour = colours.Yellow;
valueText.Colour = colours.YellowLight;
}
}
public class PercentageInfoLine : InfoLine<double>
{
public PercentageInfoLine(string name, string prefix = "") : base(name, prefix)
{
}
protected override string Format(double count) => $@"{count:P2}";
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class LetterboxOverlay : VisibilityContainer
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private const int height = 350;
private static readonly Color4 transparent_black = new Color4(0, 0, 0, 0);
public LetterboxOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Height = height,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new ColourInfo
{
TopLeft = Color4.Black,
TopRight = Color4.Black,
BottomLeft = transparent_black,
BottomRight = transparent_black,
}
}
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = height,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new ColourInfo
{
TopLeft = transparent_black,
TopRight = transparent_black,
BottomLeft = Color4.Black,
BottomRight = Color4.Black,
}
}
}
};
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 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.Graphics.Sprites;
using osu.Framework.Graphics;
using System;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Screens.Play.BreaksOverlay
{
public class RemainingTimeCounter : VisibilityContainer
{
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
private readonly OsuSpriteText counter;
private int? previousSecond;
private double endTime;
private bool isCounting;
public RemainingTimeCounter()
{
AutoSizeAxes = Axes.Both;
Child = counter = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 33,
Font = "Venera",
};
}
public void StartCounting(double endTime)
{
this.endTime = endTime;
isCounting = true;
}
protected override void Update()
{
base.Update();
if (isCounting)
{
var currentTime = Clock.CurrentTime;
if (currentTime < endTime)
{
int currentSecond = (int)Math.Ceiling((endTime - Clock.CurrentTime) / 1000.0);
if (currentSecond != previousSecond)
{
counter.Text = currentSecond.ToString();
previousSecond = currentSecond;
}
}
else isCounting = false;
}
}
protected override void PopIn() => this.FadeIn(fade_duration);
protected override void PopOut() => this.FadeOut(fade_duration);
}
}

View File

@ -24,6 +24,9 @@ using osu.Game.Screens.Ranking;
using osu.Framework.Audio.Sample;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Screens.Play.BreaksOverlay;
using osu.Game.Storyboards.Drawables;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
@ -59,6 +62,7 @@ namespace osu.Game.Screens.Play
#region User Settings
private Bindable<double> dimLevel;
private Bindable<bool> showStoryboard;
private Bindable<bool> mouseWheelDisabled;
private Bindable<double> userAudioOffset;
@ -66,6 +70,10 @@ namespace osu.Game.Screens.Play
#endregion
private BreakOverlay breakOverlay;
private Container storyboardContainer;
private DrawableStoryboard storyboard;
private HUDOverlay hudOverlay;
private FailOverlay failOverlay;
@ -77,6 +85,7 @@ namespace osu.Game.Screens.Play
this.api = api;
dimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
@ -145,6 +154,12 @@ namespace osu.Game.Screens.Play
Children = new Drawable[]
{
storyboardContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
Alpha = 0,
},
pauseContainer = new PauseContainer
{
AudioClock = decoupledClock,
@ -166,16 +181,20 @@ namespace osu.Game.Screens.Play
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
Children = new Drawable[]
{
RulesetContainer,
}
Child = RulesetContainer,
},
hudOverlay = new HUDOverlay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Clock = decoupledClock,
Breaks = beatmap.Breaks
},
}
},
failOverlay = new FailOverlay
@ -196,6 +215,9 @@ namespace osu.Game.Screens.Play
scoreProcessor = RulesetContainer.CreateScoreProcessor();
if (showStoryboard)
initializeStoryboard(false);
hudOverlay.BindProcessor(scoreProcessor);
hudOverlay.BindRulesetContainer(RulesetContainer);
@ -206,11 +228,23 @@ namespace osu.Game.Screens.Play
hudOverlay.ModDisplay.Current.BindTo(working.Mods);
breakOverlay.BindProcessor(scoreProcessor);
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
}
private void initializeStoryboard(bool asyncLoad)
{
var beatmap = Beatmap.Value.Beatmap;
storyboard = beatmap.Storyboard.CreateDrawable(Beatmap.Value);
storyboard.Masking = true;
storyboardContainer.Add(asyncLoad ? new AsyncLoadWrapper(storyboard) { RelativeSizeAxes = Axes.Both } : (Drawable)storyboard);
}
public void Restart()
{
sampleRestart?.Play();
@ -266,12 +300,12 @@ namespace osu.Game.Screens.Play
return;
(Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1500, Easing.OutQuint);
Background?.FadeTo(1 - (float)dimLevel, 1500, Easing.OutQuint);
dimLevel.ValueChanged += dimLevel_ValueChanged;
showStoryboard.ValueChanged += showStoryboard_ValueChanged;
updateBackgroundElements();
Content.Alpha = 0;
dimLevel.ValueChanged += newDim => Background?.FadeTo(1 - (float)newDim, 800);
Content
.ScaleTo(0.7f)
.ScaleTo(1, 750, Easing.OutQuint)
@ -310,8 +344,33 @@ namespace osu.Game.Screens.Play
return true;
}
private void dimLevel_ValueChanged(double newValue)
=> updateBackgroundElements();
private void showStoryboard_ValueChanged(bool newValue)
=> updateBackgroundElements();
private void updateBackgroundElements()
{
var opacity = 1 - (float)dimLevel;
if (showStoryboard && storyboard == null)
initializeStoryboard(true);
var beatmap = Beatmap.Value;
var storyboardVisible = showStoryboard && beatmap.Beatmap.Storyboard.HasDrawable;
storyboardContainer.FadeColour(new Color4(opacity, opacity, opacity, 1), 800);
storyboardContainer.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0);
Background?.FadeTo(!storyboardVisible || beatmap.Background == null ? opacity : 0, 800, Easing.OutQuint);
}
private void fadeOut()
{
dimLevel.ValueChanged -= dimLevel_ValueChanged;
showStoryboard.ValueChanged -= showStoryboard_ValueChanged;
const float fade_out_duration = 250;
RulesetContainer?.FadeOut(fade_out_duration);

View File

@ -186,9 +186,9 @@ namespace osu.Game.Screens.Ranking
private class DrawableScoreStatistic : Container
{
private readonly KeyValuePair<string, dynamic> statistic;
private readonly KeyValuePair<string, object> statistic;
public DrawableScoreStatistic(KeyValuePair<string, dynamic> statistic)
public DrawableScoreStatistic(KeyValuePair<string, object> statistic)
{
this.statistic = statistic;

View File

@ -177,6 +177,8 @@ namespace osu.Game.Screens.Select
public Action<BeatmapSetInfo> RestoreRequested;
public Action<BeatmapInfo> EditRequested;
public Action<BeatmapInfo> HideDifficultyRequested;
public void SelectNext(int direction = 1, bool skipDifficulties = true)
@ -347,6 +349,7 @@ namespace osu.Game.Screens.Select
StartRequested = b => StartRequested?.Invoke(),
DeleteRequested = b => DeleteRequested?.Invoke(b),
RestoreHiddenRequested = s => RestoreRequested?.Invoke(s),
EditRequested = b => EditRequested?.Invoke(b),
HideDifficultyRequested = b => HideDifficultyRequested?.Invoke(b),
State = BeatmapGroupState.Collapsed
};

View File

@ -40,9 +40,9 @@ namespace osu.Game.Screens.Select.Details
firstValue.Value = Beatmap?.Difficulty?.CircleSize ?? 0;
}
hpDrain.Value = beatmap.Difficulty.DrainRate;
accuracy.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
hpDrain.Value = beatmap.Difficulty?.DrainRate ?? 0;
accuracy.Value = beatmap.Difficulty?.OverallDifficulty ?? 0;
approachRate.Value = beatmap.Difficulty?.ApproachRate ?? 0;
starDifficulty.Value = (float)beatmap.StarDifficulty;
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
@ -58,6 +59,31 @@ namespace osu.Game.Screens.Select
Action = action,
});
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
/// <param name="text">Text on the button.</param>
/// <param name="colour">Colour of the button.</param>
/// <param name="hotkey">Hotkey of the button.</param>
/// <param name="overlay">The <see cref="OverlayContainer"/> to be toggled by this button.</param>
/// <param name="depth">
/// <para>Higher depth to be put on the left, and lower to be put on the right.</para>
/// <para>Notice this is different to <see cref="Options.BeatmapOptionsOverlay"/>!</para>
/// </param>
public void AddButton(string text, Color4 colour, OverlayContainer overlay, Key? hotkey = null, float depth = 0)
{
overlays.Add(overlay);
AddButton(text, colour, () =>
{
foreach (var o in overlays)
{
if (o == overlay)
o.ToggleVisibility();
else
o.Hide();
}
}, hotkey, depth);
}
private void updateModeLight() => modeLight.FadeColour(buttons.FirstOrDefault(b => b.IsHovered)?.SelectedColour ?? Color4.Transparent, TRANSITION_LENGTH, Easing.OutQuint);
public Footer()

View File

@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility, Key.F1, float.MaxValue);
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);

View File

@ -19,6 +19,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Select.Options;
namespace osu.Game.Screens.Select
@ -108,6 +109,7 @@ namespace osu.Game.Screens.Select
BeatmapsChanged = carouselBeatmapsLoaded,
DeleteRequested = promptDelete,
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
EditRequested = editRequested,
HideDifficultyRequested = b => manager.Hide(b),
StartRequested = () => carouselRaisedStart(),
});
@ -164,7 +166,7 @@ namespace osu.Game.Screens.Select
if (Footer != null)
{
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions.ToggleVisibility, Key.F3);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => promptDelete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
}
@ -195,6 +197,12 @@ namespace osu.Game.Screens.Select
carousel.AllowSelection = !Beatmap.Disabled;
}
private void editRequested(BeatmapInfo beatmap)
{
Beatmap.Value = manager.GetWorkingBeatmap(beatmap, Beatmap);
Push(new Editor());
}
private void onBeatmapRestored(BeatmapInfo b) => carousel.UpdateBeatmap(b);
private void onBeatmapHidden(BeatmapInfo b) => carousel.UpdateBeatmap(b);

View File

@ -10,8 +10,8 @@ namespace osu.Game.Storyboards
public double LoopStartTime;
public int LoopCount;
public override double StartTime => LoopStartTime;
public override double EndTime => LoopStartTime + CommandsDuration * LoopCount;
public override double StartTime => LoopStartTime + CommandsStartTime;
public override double EndTime => StartTime + CommandsDuration * LoopCount;
public CommandLoop(double startTime, int loopCount)
{

View File

@ -5,6 +5,7 @@ using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.IO;
@ -14,6 +15,16 @@ namespace osu.Game.Storyboards.Drawables
{
public Storyboard Storyboard { get; private set; }
private readonly Background background;
public Texture BackgroundTexture
{
get { return background.Texture; }
set { background.Texture = value; }
}
private readonly Container<DrawableStoryboardLayer> content;
protected override Container<DrawableStoryboardLayer> Content => content;
protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480);
public override bool HandleInput => false;
@ -39,6 +50,18 @@ namespace osu.Game.Storyboards.Drawables
Size = new Vector2(640, 480);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
AddInternal(background = new Background
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
AddInternal(content = new Container<DrawableStoryboardLayer>
{
Size = new Vector2(640, 480),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
[BackgroundDependencyLoader]
@ -55,5 +78,10 @@ namespace osu.Game.Storyboards.Drawables
foreach (var layer in Children)
layer.Enabled = passing ? layer.Layer.EnabledWhenPassing : layer.Layer.EnabledWhenFailing;
}
private class Background : Sprite
{
protected override Vector2 DrawScale => Texture != null ? new Vector2(Parent.DrawHeight / Texture.DisplayHeight) : base.DrawScale;
}
}
}

Some files were not shown because too many files have changed in this diff Show More