Merge remote-tracking branch 'origin/master' into rearrangeable-playlist

This commit is contained in:
smoogipoo
2020-02-04 16:55:55 +09:00
181 changed files with 3643 additions and 1690 deletions

View File

@ -29,9 +29,9 @@ namespace osu.Game.Graphics
}
}
public DrawableDate(DateTimeOffset date)
public DrawableDate(DateTimeOffset date, float textSize = OsuFont.DEFAULT_FONT_SIZE, bool italic = true)
{
Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true);
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: textSize, italics: italic);
Date = date;
}

View File

@ -36,6 +36,11 @@ namespace osu.Game.Graphics.UserInterface
public virtual string TooltipText { get; private set; }
/// <summary>
/// Whether to format the tooltip as a percentage or the actual value.
/// </summary>
public bool DisplayAsPercentage { get; set; }
private Color4 accentColour;
public Color4 AccentColour
@ -169,11 +174,11 @@ namespace osu.Game.Graphics.UserInterface
else
{
double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo);
double floatMinValue = CurrentNumber.MinValue.ToDouble(NumberFormatInfo.InvariantInfo);
double floatMaxValue = CurrentNumber.MaxValue.ToDouble(NumberFormatInfo.InvariantInfo);
if (floatMaxValue == 1 && floatMinValue >= -1)
TooltipText = floatValue.ToString("P0");
if (DisplayAsPercentage)
{
TooltipText = floatValue.ToString("0%");
}
else
{
var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits);

View File

@ -17,7 +17,8 @@ namespace osu.Game.Graphics.UserInterface
stack.ScreenPushed += onPushed;
stack.ScreenExited += onExited;
onPushed(null, stack.CurrentScreen);
if (stack.CurrentScreen != null)
onPushed(null, stack.CurrentScreen);
Current.ValueChanged += current => current.NewValue.MakeCurrent();
}

View File

@ -40,13 +40,14 @@ namespace osu.Game.Graphics.UserInterface
public ShowMoreButton()
{
AutoSizeAxes = Axes.Both;
Height = 30;
Width = 140;
}
protected override Drawable CreateContent() => new CircularContainer
{
Masking = true,
Size = new Vector2(140, 30),
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
background = new Box

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetSpotlightsRequest : APIRequest<SpotlightsCollection>
{
protected override string Target => "spotlights";
}
public class SpotlightsCollection
{
[JsonProperty("spotlights")]
public List<APISpotlight> Spotlights;
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using Newtonsoft.Json;
namespace osu.Game.Online.API.Requests.Responses
{
public class APISpotlight
{
[JsonProperty("id")]
public int Id;
[JsonProperty("name")]
public string Name;
[JsonProperty("type")]
public string Type;
[JsonProperty("mode_specific")]
public bool ModeSpecific;
[JsonProperty(@"start_date")]
public DateTimeOffset StartDate;
[JsonProperty(@"end_date")]
public DateTimeOffset EndDate;
public override string ToString() => Name;
}
}

View File

@ -6,8 +6,6 @@ using osu.Game.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
namespace osu.Game.Online.API.Requests.Responses
{
@ -70,12 +68,10 @@ namespace osu.Game.Online.API.Requests.Responses
public bool IsDeleted => DeletedAt.HasValue;
public bool HasMessage => !string.IsNullOrEmpty(MessageHtml);
public bool HasMessage => !string.IsNullOrEmpty(Message);
public bool IsVoted { get; set; }
public string GetMessage => HasMessage ? WebUtility.HtmlDecode(Regex.Replace(MessageHtml, @"<(.|\n)*?>", string.Empty)) : string.Empty;
public int DeletedChildrenCount => ChildComments.Count(c => c.IsDeleted);
}
}

View File

@ -47,17 +47,18 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"included_comments")]
public List<Comment> IncludedComments { get; set; }
private List<long> userVotes;
[JsonProperty(@"user_votes")]
private List<long> userVotes
public List<long> UserVotes
{
set => value.ForEach(v =>
get => userVotes;
set
{
Comments.ForEach(c =>
{
if (v == c.Id)
c.IsVoted = true;
});
});
userVotes = value;
Comments.ForEach(c => c.IsVoted = value.Contains(c.Id));
}
}
private List<User> users;

View File

@ -277,7 +277,7 @@ namespace osu.Game.Online.Leaderboards
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{
new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()),
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy)
};
protected override bool OnHover(HoverEvent e)

View File

@ -327,10 +327,10 @@ namespace osu.Game
return;
}
performFromMainMenu(() =>
PerformFromScreen(screen =>
{
// we might already be at song select, so a check is required before performing the load to solo.
if (menuScreen.IsCurrentScreen())
if (screen is MainMenu)
menuScreen.LoadToSolo();
// we might even already be at the song
@ -344,7 +344,7 @@ namespace osu.Game
Ruleset.Value = first.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
}, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect));
}, validScreens: new[] { typeof(PlaySongSelect) });
}
/// <summary>
@ -381,12 +381,12 @@ namespace osu.Game
return;
}
performFromMainMenu(() =>
PerformFromScreen(screen =>
{
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
menuScreen.Push(new ReplayPlayerLoader(databasedScore));
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
screen.Push(new ReplayPlayerLoader(databasedScore));
}, validScreens: new[] { typeof(PlaySongSelect) });
}
protected virtual Loader CreateLoader() => new Loader();
@ -441,53 +441,42 @@ namespace osu.Game
private ScheduledDelegate performFromMainMenuTask;
/// <summary>
/// Perform an action only after returning to the main menu.
/// Perform an action only after returning to a specific screen as indicated by <paramref name="validScreens"/>.
/// Eagerly tries to exit the current screen until it succeeds.
/// </summary>
/// <param name="action">The action to perform once we are in the correct state.</param>
/// <param name="taskName">The task name to display in a notification (if we can't immediately reach the main menu state).</param>
/// <param name="targetScreen">An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.</param>
/// <param name="bypassScreenAllowChecks">Whether checking <see cref="IOsuScreen.AllowExternalScreenChange"/> should be bypassed.</param>
private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false)
/// <param name="validScreens">An optional collection of valid screen types. If any of these screens are already current we can perform the action immediately, else the first valid parent will be made current before performing the action. <see cref="MainMenu"/> is used if not specified.</param>
protected void PerformFromScreen(Action<IScreen> action, IEnumerable<Type> validScreens = null)
{
performFromMainMenuTask?.Cancel();
// if the current screen does not allow screen changing, give the user an option to try again later.
if (!bypassScreenAllowChecks && (ScreenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
{
notifications.Post(new SimpleNotification
{
Text = $"Click here to {taskName}",
Activated = () =>
{
performFromMainMenu(action, taskName, targetScreen, true);
return true;
}
});
return;
}
validScreens ??= Enumerable.Empty<Type>();
validScreens = validScreens.Append(typeof(MainMenu));
CloseAllOverlays(false);
// we may already be at the target screen type.
if (targetScreen != null && ScreenStack.CurrentScreen?.GetType() == targetScreen)
if (validScreens.Contains(ScreenStack.CurrentScreen?.GetType()) && !Beatmap.Disabled)
{
action();
action(ScreenStack.CurrentScreen);
return;
}
// all conditions have been met to continue with the action.
if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled)
// find closest valid target
IScreen screen = ScreenStack.CurrentScreen;
while (screen != null)
{
action();
return;
if (validScreens.Contains(screen.GetType()))
{
screen.MakeCurrent();
break;
}
screen = screen.GetParentScreen();
}
// menuScreen may not be initialised yet (null check required).
menuScreen?.MakeCurrent();
performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
performFromMainMenuTask = Schedule(() => PerformFromScreen(action, validScreens));
}
/// <summary>

View File

@ -2,17 +2,14 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osuTK;
using System.Linq;
namespace osu.Game.Overlays.BeatmapSet
{
public class BeatmapRulesetSelector : RulesetSelector
public class BeatmapRulesetSelector : OverlayRulesetSelector
{
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
@ -28,21 +25,9 @@ namespace osu.Game.Overlays.BeatmapSet
}
}
public BeatmapRulesetSelector()
{
AutoSizeAxes = Axes.Both;
}
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new BeatmapRulesetTabItem(value)
{
BeatmapSet = { BindTarget = beatmapSet }
};
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
};
}
}

View File

@ -3,143 +3,74 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
using System.Linq;
namespace osu.Game.Overlays.BeatmapSet
{
public class BeatmapRulesetTabItem : TabItem<RulesetInfo>
public class BeatmapRulesetTabItem : OverlayRulesetTabItem
{
private readonly OsuSpriteText name, count;
private readonly Box bar;
public readonly Bindable<BeatmapSetInfo> BeatmapSet = new Bindable<BeatmapSetInfo>();
public override bool PropagatePositionalInputSubTree => Enabled.Value && !Active.Value && base.PropagatePositionalInputSubTree;
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private OsuSpriteText count;
private Container countContainer;
public BeatmapRulesetTabItem(RulesetInfo value)
: base(value)
{
AutoSizeAxes = Axes.Both;
}
FillFlowContainer nameContainer;
Children = new Drawable[]
[BackgroundDependencyLoader]
private void load()
{
Add(countContainer = new Container
{
nameContainer = new FillFlowContainer
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
CornerRadius = 4f,
Children = new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Bottom = 7.5f },
Spacing = new Vector2(2.5f),
Children = new Drawable[]
new Box
{
name = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = value.Name,
Font = OsuFont.Default.With(size: 18),
},
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
count = new OsuSpriteText
{
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 5f },
Font = OsuFont.Default.With(weight: FontWeight.SemiBold),
}
}
}
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background6
},
count = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 5f },
Font = OsuFont.Default.With(weight: FontWeight.SemiBold),
Colour = colourProvider.Foreground1,
}
},
bar = new Box
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
},
new HoverClickSounds(),
};
}
});
}
protected override void LoadComplete()
{
base.LoadComplete();
BeatmapSet.BindValueChanged(setInfo =>
{
var beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.Equals(Value)) ?? 0;
count.Text = beatmapsCount.ToString();
count.Alpha = beatmapsCount > 0 ? 1f : 0f;
countContainer.FadeTo(beatmapsCount > 0 ? 1 : 0);
Enabled.Value = beatmapsCount > 0;
}, true);
Enabled.BindValueChanged(v => nameContainer.Alpha = v.NewValue ? 1f : 0.5f, true);
}
[Resolved]
private OsuColour colour { get; set; }
protected override void LoadComplete()
{
base.LoadComplete();
count.Colour = colour.Gray9;
bar.Colour = colour.Blue;
updateState();
}
private void updateState()
{
var isHoveredOrActive = IsHovered || Active.Value;
bar.ResizeHeightTo(isHoveredOrActive ? 4 : 0, 200, Easing.OutQuint);
name.Colour = isHoveredOrActive ? colour.GrayE : colour.GrayC;
name.Font = name.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Regular);
}
#region Hovering and activation logic
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
protected override bool OnHover(HoverEvent e)
{
updateState();
return false;
}
protected override void OnHoverLost(HoverLostEvent e) => updateState();
#endregion
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.BeatmapSet
{
public class BeatmapSetHeader : OverlayHeader
{
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
public BeatmapRulesetSelector RulesetSelector { get; private set; }
protected override ScreenTitle CreateTitle() => new BeatmapHeaderTitle();
protected override Drawable CreateTitleContent() => RulesetSelector = new BeatmapRulesetSelector
{
Current = Ruleset
};
private class BeatmapHeaderTitle : ScreenTitle
{
public BeatmapHeaderTitle()
{
Title = @"beatmap";
Section = @"info";
}
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}

View File

@ -26,11 +26,9 @@ namespace osu.Game.Overlays.BeatmapSet
public class Header : BeatmapDownloadTrackingComposite
{
private const float transition_duration = 200;
private const float tabs_height = 50;
private const float buttons_height = 45;
private const float buttons_spacing = 5;
private readonly Box tabsBg;
private readonly UpdateableBeatmapSetCover cover;
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
@ -41,14 +39,13 @@ namespace osu.Game.Overlays.BeatmapSet
public bool DownloadButtonsVisible => downloadButtonsContainer.Any();
public readonly BeatmapRulesetSelector RulesetSelector;
public BeatmapRulesetSelector RulesetSelector => beatmapSetHeader.RulesetSelector;
public readonly BeatmapPicker Picker;
private readonly FavouriteButton favouriteButton;
private readonly FillFlowContainer fadeContent;
private readonly LoadingAnimation loading;
private readonly BeatmapSetHeader beatmapSetHeader;
[Cached(typeof(IBindable<RulesetInfo>))]
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
@ -69,154 +66,145 @@ namespace osu.Game.Overlays.BeatmapSet
Offset = new Vector2(0f, 1f),
};
InternalChildren = new Drawable[]
InternalChild = new FillFlowContainer
{
new Container
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.X,
Height = tabs_height,
Children = new Drawable[]
beatmapSetHeader = new BeatmapSetHeader
{
tabsBg = new Box
{
RelativeSizeAxes = Axes.Both,
},
RulesetSelector = new BeatmapRulesetSelector
{
Current = ruleset,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
}
Ruleset = { BindTarget = ruleset },
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = tabs_height },
Children = new Drawable[]
new Container
{
new Container
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
new Container
{
cover = new UpdateableBeatmapSetCover
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Masking = true,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Top = 20,
Bottom = 30,
Left = BeatmapSetOverlay.X_PADDING,
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
},
Children = new Drawable[]
{
fadeContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
cover = new UpdateableBeatmapSetCover
{
new Container
RelativeSizeAxes = Axes.Both,
Masking = true,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Top = 20,
Bottom = 30,
Left = BeatmapSetOverlay.X_PADDING,
Right = BeatmapSetOverlay.X_PADDING + BeatmapSetOverlay.RIGHT_WIDTH,
},
Children = new Drawable[]
{
fadeContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = Picker = new BeatmapPicker(),
},
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
new Container
{
title = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
},
externalLink = new ExternalLinkButton
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
},
}
},
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 20 },
Child = author = new AuthorInfo(),
},
beatmapAvailability = new BeatmapAvailability(),
new Container
{
RelativeSizeAxes = Axes.X,
Height = buttons_height,
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = Picker = new BeatmapPicker(),
},
new FillFlowContainer
{
favouriteButton = new FavouriteButton
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
BeatmapSet = { BindTarget = BeatmapSet }
},
downloadButtonsContainer = new FillFlowContainer
title = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 37, weight: FontWeight.Bold, italics: true)
},
externalLink = new ExternalLinkButton
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
},
}
},
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.SemiBold, italics: true) },
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 20 },
Child = author = new AuthorInfo(),
},
beatmapAvailability = new BeatmapAvailability(),
new Container
{
RelativeSizeAxes = Axes.X,
Height = buttons_height,
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Spacing = new Vector2(buttons_spacing),
favouriteButton = new FavouriteButton
{
BeatmapSet = { BindTarget = BeatmapSet }
},
downloadButtonsContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
Spacing = new Vector2(buttons_spacing),
},
},
},
},
},
},
}
},
loading = new LoadingAnimation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.5f),
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = BeatmapSetOverlay.TOP_PADDING, Right = BeatmapSetOverlay.X_PADDING },
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
}
},
loading = new LoadingAnimation
{
onlineStatusPill = new BeatmapSetOnlineStatusPill
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.5f),
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = BeatmapSetOverlay.TOP_PADDING, Right = BeatmapSetOverlay.X_PADDING },
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Children = new Drawable[]
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 }
onlineStatusPill = new BeatmapSetOnlineStatusPill
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 }
},
Details = new Details(),
},
Details = new Details(),
},
},
},
},
}
};
Picker.Beatmap.ValueChanged += b =>
@ -229,8 +217,6 @@ namespace osu.Game.Overlays.BeatmapSet
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabsBg.Colour = colours.Gray3;
State.BindValueChanged(_ => updateDownloadButtons());
BeatmapSet.BindValueChanged(setInfo =>

View File

@ -116,7 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
new OsuSpriteText
{
Margin = new MarginPadding { Right = horizontal_inset },
Text = $@"{score.Accuracy:P2}",
Text = score.DisplayAccuracy,
Font = OsuFont.GetFont(size: text_size),
Colour = score.Accuracy == 1 ? highAccuracyColour : Color4.White
},

View File

@ -92,7 +92,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
set
{
totalScoreColumn.Text = $@"{value.TotalScore:N0}";
accuracyColumn.Text = $@"{value.Accuracy:P2}";
accuracyColumn.Text = value.DisplayAccuracy;
maxComboColumn.Text = $@"{value.MaxCombo:N0}x";
ppColumn.Text = $@"{value.PP:N0}";

View File

@ -42,7 +42,7 @@ namespace osu.Game.Overlays.BeatmapSet
int playCount = beatmap?.OnlineInfo?.PlayCount ?? 0;
var rate = playCount != 0 ? (float)passCount / playCount : 0;
successPercent.Text = rate.ToString("P0");
successPercent.Text = rate.ToString("0%");
successRate.Length = rate;
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);

View File

@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Changelog
{
public class ChangelogHeader : BreadcrumbControlOverlayHeader
{
public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>();
public readonly Bindable<APIChangelogBuild> Build = new Bindable<APIChangelogBuild>();
public Action ListingSelected;
@ -25,18 +25,18 @@ namespace osu.Game.Overlays.Changelog
public ChangelogHeader()
{
TabControl.AddItem(listing_string);
TabControl.Current.ValueChanged += e =>
Current.ValueChanged += e =>
{
if (e.NewValue == listing_string)
ListingSelected?.Invoke();
};
Current.ValueChanged += showBuild;
Build.ValueChanged += showBuild;
Streams.Current.ValueChanged += e =>
{
if (e.NewValue?.LatestBuild != null && !e.NewValue.Equals(Current.Value?.UpdateStream))
Current.Value = e.NewValue.LatestBuild;
if (e.NewValue?.LatestBuild != null && !e.NewValue.Equals(Build.Value?.UpdateStream))
Build.Value = e.NewValue.LatestBuild;
};
}
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Changelog
if (e.NewValue != null)
{
TabControl.AddItem(e.NewValue.ToString());
TabControl.Current.Value = e.NewValue.ToString();
Current.Value = e.NewValue.ToString();
updateCurrentStream();
@ -58,7 +58,7 @@ namespace osu.Game.Overlays.Changelog
}
else
{
TabControl.Current.Value = listing_string;
Current.Value = listing_string;
Streams.Current.Value = null;
title.Version = null;
}
@ -86,10 +86,10 @@ namespace osu.Game.Overlays.Changelog
private void updateCurrentStream()
{
if (Current.Value == null)
if (Build.Value == null)
return;
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Current.Value.UpdateStream.Name);
Streams.Current.Value = Streams.Items.FirstOrDefault(s => s.Name == Build.Value.UpdateStream.Name);
}
private class ChangelogHeaderTitle : ScreenTitle

View File

@ -78,7 +78,7 @@ namespace osu.Game.Overlays
sampleBack = audio.Samples.Get(@"UI/generic-select-soft");
Header.Current.BindTo(Current);
Header.Build.BindTo(Current);
Current.BindValueChanged(e =>
{

View File

@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Chat.Selection
private const float text_size = 15;
private const float transition_duration = 100;
private readonly Channel channel;
public readonly Channel Channel;
private readonly Bindable<bool> joinedBind = new Bindable<bool>();
private readonly OsuSpriteText name;
@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Chat.Selection
private Color4 topicColour;
private Color4 hoverColour;
public IEnumerable<string> FilterTerms => new[] { channel.Name, channel.Topic };
public IEnumerable<string> FilterTerms => new[] { Channel.Name, Channel.Topic ?? string.Empty };
public bool MatchingFilter
{
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Chat.Selection
public ChannelListItem(Channel channel)
{
this.channel = channel;
Channel = channel;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Chat.Selection
hoverColour = colours.Yellow;
joinedBind.ValueChanged += joined => updateColour(joined.NewValue);
joinedBind.BindTo(channel.Joined);
joinedBind.BindTo(Channel.Joined);
joinedBind.TriggerChange();
FinishTransforms(true);
@ -156,7 +156,7 @@ namespace osu.Game.Overlays.Chat.Selection
protected override bool OnHover(HoverEvent e)
{
if (!channel.Joined.Value)
if (!Channel.Joined.Value)
name.FadeColour(hoverColour, 50, Easing.OutQuint);
return base.OnHover(e);
@ -164,7 +164,7 @@ namespace osu.Game.Overlays.Chat.Selection
protected override void OnHoverLost(HoverLostEvent e)
{
if (!channel.Joined.Value)
if (!Channel.Joined.Value)
name.FadeColour(Color4.White, transition_duration);
}

View File

@ -321,8 +321,10 @@ namespace osu.Game.Overlays
private void selectTab(int index)
{
var channel = ChannelTabControl.Items.Skip(index).FirstOrDefault();
if (channel != null && !(channel is ChannelSelectorTabItem.ChannelSelectorTabChannel))
var channel = ChannelTabControl.Items
.Where(tab => !(tab is ChannelSelectorTabItem.ChannelSelectorTabChannel))
.ElementAtOrDefault(index);
if (channel != null)
ChannelTabControl.Current.Value = channel;
}

View File

@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Comments
private int currentPage;
private FillFlowContainer content;
private DeletedChildrenPlaceholder deletedChildrenPlaceholder;
private DeletedCommentsCounter deletedCommentsCounter;
private CommentsShowMoreButton moreButton;
private TotalCommentsCounter commentCounter;
@ -68,6 +68,7 @@ namespace osu.Game.Overlays.Comments
},
new Container
{
Name = @"Footer",
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
@ -84,7 +85,7 @@ namespace osu.Game.Overlays.Comments
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
deletedChildrenPlaceholder = new DeletedChildrenPlaceholder
deletedCommentsCounter = new DeletedCommentsCounter
{
ShowDeleted = { BindTarget = ShowDeleted }
},
@ -153,7 +154,8 @@ namespace osu.Game.Overlays.Comments
private void clearComments()
{
currentPage = 1;
deletedChildrenPlaceholder.DeletedCount.Value = 0;
deletedCommentsCounter.Count.Value = 0;
moreButton.Show();
moreButton.IsLoading = true;
content.Clear();
}
@ -162,29 +164,14 @@ namespace osu.Game.Overlays.Comments
{
loadCancellation = new CancellationTokenSource();
var page = new FillFlowContainer
LoadComponentAsync(new CommentsPage(response)
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
};
foreach (var c in response.Comments)
{
if (c.IsTopLevel)
{
page.Add(new DrawableComment(c)
{
ShowDeleted = { BindTarget = ShowDeleted }
});
}
}
LoadComponentAsync(page, loaded =>
ShowDeleted = { BindTarget = ShowDeleted }
}, loaded =>
{
content.Add(loaded);
deletedChildrenPlaceholder.DeletedCount.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
deletedCommentsCounter.Count.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
if (response.HasMore)
{
@ -194,10 +181,12 @@ namespace osu.Game.Overlays.Comments
moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments;
moreButton.IsLoading = false;
}
else
{
moreButton.Hide();
}
commentCounter.Current.Value = response.Total;
moreButton.FadeTo(response.HasMore ? 1 : 0);
}, loadCancellation.Token);
}

View File

@ -0,0 +1,92 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Game.Online.API.Requests.Responses;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using System.Linq;
namespace osu.Game.Overlays.Comments
{
public class CommentsPage : CompositeDrawable
{
public readonly BindableBool ShowDeleted = new BindableBool();
private readonly CommentBundle commentBundle;
public CommentsPage(CommentBundle commentBundle)
{
this.commentBundle = commentBundle;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
FillFlowContainer flow;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5
},
flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
}
});
if (!commentBundle.Comments.Any())
{
flow.Add(new NoCommentsPlaceholder());
return;
}
foreach (var c in commentBundle.Comments)
{
if (c.IsTopLevel)
{
flow.Add(new DrawableComment(c)
{
ShowDeleted = { BindTarget = ShowDeleted }
});
}
}
}
private class NoCommentsPlaceholder : CompositeDrawable
{
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Height = 80;
RelativeSizeAxes = Axes.X;
AddRangeInternal(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background4
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 50 },
Text = @"No comments yet."
}
});
}
}
}
}

View File

@ -14,6 +14,8 @@ namespace osu.Game.Overlays.Comments
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Height = 20;
IdleColour = colourProvider.Background2;
HoverColour = colourProvider.Background1;
ChevronIconColour = colourProvider.Foreground1;

View File

@ -12,51 +12,56 @@ using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Comments
{
public class DeletedChildrenPlaceholder : FillFlowContainer
public class DeletedCommentsCounter : CompositeDrawable
{
public readonly BindableBool ShowDeleted = new BindableBool();
public readonly BindableInt DeletedCount = new BindableInt();
public readonly BindableInt Count = new BindableInt();
private readonly SpriteText countText;
public DeletedChildrenPlaceholder()
public DeletedCommentsCounter()
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(3, 0);
Margin = new MarginPadding { Vertical = 10, Left = 80 };
Children = new Drawable[]
InternalChild = new FillFlowContainer
{
new SpriteIcon
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(3, 0),
Children = new Drawable[]
{
Icon = FontAwesome.Solid.Trash,
Size = new Vector2(14),
},
countText = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
new SpriteIcon
{
Icon = FontAwesome.Solid.Trash,
Size = new Vector2(14),
},
countText = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
}
}
};
}
protected override void LoadComplete()
{
DeletedCount.BindValueChanged(_ => updateDisplay(), true);
ShowDeleted.BindValueChanged(_ => updateDisplay(), true);
base.LoadComplete();
Count.BindValueChanged(_ => updateDisplay(), true);
ShowDeleted.BindValueChanged(_ => updateDisplay(), true);
}
private void updateDisplay()
{
if (DeletedCount.Value != 0)
if (!ShowDeleted.Value && Count.Value != 0)
{
countText.Text = @"deleted comment".ToQuantity(DeletedCount.Value);
this.FadeTo(ShowDeleted.Value ? 0 : 1);
countText.Text = @"deleted comment".ToQuantity(Count.Value);
Show();
}
else
{
Hide();
}
}
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Comments
{
LinkFlowContainer username;
FillFlowContainer childCommentsContainer;
DeletedChildrenPlaceholder deletedChildrenPlaceholder;
DeletedCommentsCounter deletedCommentsCounter;
FillFlowContainer info;
LinkFlowContainer message;
GridContainer content;
@ -184,7 +184,7 @@ namespace osu.Game.Overlays.Comments
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical
},
deletedChildrenPlaceholder = new DeletedChildrenPlaceholder
deletedCommentsCounter = new DeletedCommentsCounter
{
ShowDeleted = { BindTarget = ShowDeleted }
}
@ -193,7 +193,7 @@ namespace osu.Game.Overlays.Comments
}
};
deletedChildrenPlaceholder.DeletedCount.Value = comment.DeletedChildrenCount;
deletedCommentsCounter.Count.Value = comment.DeletedChildrenCount;
if (comment.UserId.HasValue)
username.AddUserLink(comment.User);
@ -213,7 +213,7 @@ namespace osu.Game.Overlays.Comments
if (comment.HasMessage)
{
var formattedSource = MessageFormatter.FormatText(comment.GetMessage);
var formattedSource = MessageFormatter.FormatText(comment.Message);
message.AddLinks(formattedSource.Text, formattedSource.Links);
}
@ -343,7 +343,7 @@ namespace osu.Game.Overlays.Comments
if (parentComment == null)
return string.Empty;
return parentComment.HasMessage ? parentComment.GetMessage : parentComment.IsDeleted ? @"deleted" : string.Empty;
return parentComment.HasMessage ? parentComment.Message : parentComment.IsDeleted ? @"deleted" : string.Empty;
}
}
}

View File

@ -18,11 +18,11 @@ namespace osu.Game.Overlays
protected IAPIProvider API { get; private set; }
[Cached]
private readonly OverlayColourProvider colourProvider;
protected readonly OverlayColourProvider ColourProvider;
protected FullscreenOverlay(OverlayColourScheme colourScheme)
{
colourProvider = new OverlayColourProvider(colourScheme);
ColourProvider = new OverlayColourProvider(colourScheme);
RelativeSizeAxes = Axes.Both;
RelativePositionAxes = Axes.Both;
@ -43,10 +43,10 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load()
{
Waves.FirstWaveColour = colourProvider.Light4;
Waves.SecondWaveColour = colourProvider.Light3;
Waves.ThirdWaveColour = colourProvider.Dark4;
Waves.FourthWaveColour = colourProvider.Dark3;
Waves.FirstWaveColour = ColourProvider.Light4;
Waves.SecondWaveColour = ColourProvider.Light3;
Waves.ThirdWaveColour = ColourProvider.Dark4;
Waves.FourthWaveColour = ColourProvider.Dark3;
}
public override void Show()

View File

@ -30,6 +30,8 @@ namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
public const float HEIGHT = 510;
protected readonly TriangleButton DeselectAllButton;
protected readonly TriangleButton CustomiseButton;
protected readonly TriangleButton CloseButton;
@ -47,7 +49,7 @@ namespace osu.Game.Overlays.Mods
protected readonly Container ModSettingsContainer;
protected readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
public readonly Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
private Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods;
@ -66,7 +68,8 @@ namespace osu.Game.Overlays.Mods
Waves.ThirdWaveColour = OsuColour.FromHex(@"005774");
Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510;
RelativeSizeAxes = Axes.Both;
Padding = new MarginPadding { Horizontal = -OsuScreen.HORIZONTAL_OVERFLOW_PADDING };
Children = new Drawable[]
@ -85,8 +88,7 @@ namespace osu.Game.Overlays.Mods
new Triangles
{
TriangleScale = 5,
RelativeSizeAxes = Axes.X,
Height = Height, //set the height from the start to ensure correct triangle density.
RelativeSizeAxes = Axes.Both,
ColourLight = new Color4(53, 66, 82, 255),
ColourDark = new Color4(41, 54, 70, 255),
},
@ -321,14 +323,13 @@ namespace osu.Game.Overlays.Mods
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, AudioManager audio, Bindable<IReadOnlyList<Mod>> selectedMods, OsuGameBase osu)
private void load(OsuColour colours, AudioManager audio, OsuGameBase osu)
{
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
UnrankedLabel.Colour = colours.Blue;
availableMods = osu.AvailableMods.GetBoundCopy();
SelectedMods.BindTo(selectedMods);
sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Samples.Get(@"UI/check-off");

View File

@ -196,7 +196,7 @@ namespace osu.Game.Overlays
if (!instant)
queuedDirection = TrackChangeDirection.Next;
var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? BeatmapSets.FirstOrDefault();
var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).ElementAtOrDefault(1) ?? BeatmapSets.FirstOrDefault();
if (playable != null)
{

View File

@ -14,7 +14,7 @@ namespace osu.Game.Overlays.News
private NewsHeaderTitle title;
public readonly Bindable<string> Current = new Bindable<string>(null);
public readonly Bindable<string> Post = new Bindable<string>(null);
public Action ShowFrontPage;
@ -22,13 +22,13 @@ namespace osu.Game.Overlays.News
{
TabControl.AddItem(front_page_string);
TabControl.Current.ValueChanged += e =>
Current.ValueChanged += e =>
{
if (e.NewValue == front_page_string)
ShowFrontPage?.Invoke();
};
Current.ValueChanged += showPost;
Post.ValueChanged += showPost;
}
private void showPost(ValueChangedEvent<string> e)
@ -39,13 +39,13 @@ namespace osu.Game.Overlays.News
if (e.NewValue != null)
{
TabControl.AddItem(e.NewValue);
TabControl.Current.Value = e.NewValue;
Current.Value = e.NewValue;
title.IsReadingPost = true;
}
else
{
TabControl.Current.Value = front_page_string;
Current.Value = front_page_string;
title.IsReadingPost = false;
}
}

View File

@ -60,7 +60,7 @@ namespace osu.Game.Overlays
},
};
header.Current.BindTo(Current);
header.Post.BindTo(Current);
Current.TriggerChange();
}

View File

@ -50,14 +50,29 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Both,
Colour = Color4.Gray,
},
title = CreateTitle().With(title =>
new Container
{
title.Margin = new MarginPadding
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Vertical = 10,
Left = UserProfileOverlay.CONTENT_X_MARGIN
};
})
},
Children = new[]
{
title = CreateTitle().With(title =>
{
title.Anchor = Anchor.CentreLeft;
title.Origin = Anchor.CentreLeft;
}),
CreateTitleContent().With(content =>
{
content.Anchor = Anchor.CentreRight;
content.Origin = Anchor.CentreRight;
})
}
}
}
},
}
@ -75,10 +90,16 @@ namespace osu.Game.Overlays
}
[NotNull]
protected virtual Drawable CreateContent() => Drawable.Empty();
protected virtual Drawable CreateContent() => Empty();
[NotNull]
protected virtual Drawable CreateBackground() => Drawable.Empty();
protected virtual Drawable CreateBackground() => Empty();
/// <summary>
/// Creates a <see cref="Drawable"/> on the opposite side of the <see cref="ScreenTitle"/>. Used mostly to create <see cref="OverlayRulesetSelector"/>.
/// </summary>
[NotNull]
protected virtual Drawable CreateTitleContent() => Empty();
protected abstract ScreenTitle CreateTitle();
}

View File

@ -0,0 +1,28 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.Overlays
{
public class OverlayRulesetSelector : RulesetSelector
{
public OverlayRulesetSelector()
{
AutoSizeAxes = Axes.Both;
}
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new OverlayRulesetTabItem(value);
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(25, 0),
};
}
}

View File

@ -0,0 +1,97 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK.Graphics;
using osuTK;
using osu.Framework.Allocation;
namespace osu.Game.Overlays
{
public class OverlayRulesetTabItem : TabItem<RulesetInfo>
{
private Color4 accentColour;
protected virtual Color4 AccentColour
{
get => accentColour;
set
{
accentColour = value;
text.FadeColour(value, 120, Easing.OutQuint);
}
}
protected override Container<Drawable> Content { get; }
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private readonly OsuSpriteText text;
public OverlayRulesetTabItem(RulesetInfo value)
: base(value)
{
AutoSizeAxes = Axes.Both;
AddRangeInternal(new Drawable[]
{
Content = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(3, 0),
Child = text = new OsuSpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Text = value.Name,
}
},
new HoverClickSounds()
});
Enabled.Value = true;
}
protected override void LoadComplete()
{
base.LoadComplete();
Enabled.BindValueChanged(_ => updateState(), true);
}
public override bool PropagatePositionalInputSubTree => Enabled.Value && !Active.Value && base.PropagatePositionalInputSubTree;
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
updateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
private void updateState()
{
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
AccentColour = Enabled.Value ? getActiveColour() : colourProvider.Foreground1;
}
private Color4 getActiveColour() => IsHovered || Active.Value ? Color4.White : colourProvider.Highlight1;
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
@ -33,16 +33,16 @@ namespace osu.Game.Overlays.Profile.Header
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
iconColour = colours.GreySeafoamLighter;
iconColour = colourProvider.Foreground1;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDark,
Colour = colourProvider.Background4
},
new FillFlowContainer
{
@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Profile.Header
else
{
topLinkContainer.AddText("Joined ");
topLinkContainer.AddText(new DrawableDate(user.JoinDate), embolden);
topLinkContainer.AddText(new DrawableDate(user.JoinDate, italic: false), embolden);
}
addSpacer(topLinkContainer);
@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Profile.Header
else if (user.LastVisit.HasValue)
{
topLinkContainer.AddText("Last seen ");
topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value), embolden);
topLinkContainer.AddText(new DrawableDate(user.LastVisit.Value, italic: false), embolden);
addSpacer(topLinkContainer);
}
@ -111,34 +111,42 @@ namespace osu.Game.Overlays.Profile.Header
topLinkContainer.AddText("Contributed ");
topLinkContainer.AddLink($@"{user.PostCount:#,##0} forum posts", $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: embolden);
string websiteWithoutProtcol = user.Website;
string websiteWithoutProtocol = user.Website;
if (!string.IsNullOrEmpty(websiteWithoutProtcol))
if (!string.IsNullOrEmpty(websiteWithoutProtocol))
{
if (Uri.TryCreate(websiteWithoutProtcol, UriKind.Absolute, out var uri))
if (Uri.TryCreate(websiteWithoutProtocol, UriKind.Absolute, out var uri))
{
websiteWithoutProtcol = uri.Host + uri.PathAndQuery + uri.Fragment;
websiteWithoutProtcol = websiteWithoutProtcol.TrimEnd('/');
websiteWithoutProtocol = uri.Host + uri.PathAndQuery + uri.Fragment;
websiteWithoutProtocol = websiteWithoutProtocol.TrimEnd('/');
}
}
tryAddInfo(FontAwesome.Solid.MapMarker, user.Location);
tryAddInfo(OsuIcon.Heart, user.Interests);
tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation);
bottomLinkContainer.NewLine();
bool anyInfoAdded = false;
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.MapMarker, user.Location);
anyInfoAdded |= tryAddInfo(OsuIcon.Heart, user.Interests);
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation);
if (anyInfoAdded)
bottomLinkContainer.NewLine();
if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtcol, user.Website);
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Discord, user.Discord);
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Skype, user.Skype, @"skype:" + user.Skype + @"?chat");
anyInfoAdded |= tryAddInfo(FontAwesome.Brands.Lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Link, websiteWithoutProtocol, user.Website);
// If no information was added to the bottomLinkContainer, hide it to avoid unwanted padding
bottomLinkContainer.Alpha = anyInfoAdded ? 1 : 0;
}
private void addSpacer(OsuTextFlowContainer textFlow) => textFlow.AddArbitraryDrawable(new Container { Width = 15 });
private void tryAddInfo(IconUsage icon, string content, string link = null)
private bool tryAddInfo(IconUsage icon, string content, string link = null)
{
if (string.IsNullOrEmpty(content)) return;
if (string.IsNullOrEmpty(content)) return false;
// newlines could be contained in API returned user content.
content = content.Replace("\n", " ");
@ -155,6 +163,7 @@ namespace osu.Game.Overlays.Profile.Header
bottomLinkContainer.AddText(" " + content, embolden);
addSpacer(bottomLinkContainer);
return true;
}
private void embolden(SpriteText text) => text.Font = text.Font.With(weight: FontWeight.Bold);

View File

@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users;
using osuTK;
@ -28,7 +27,7 @@ namespace osu.Game.Overlays.Profile.Header
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures)
private void load(OverlayColourProvider colourProvider, TextureStore textures)
{
Container<Drawable> hiddenDetailContainer;
Container<Drawable> expandedDetailContainer;
@ -38,7 +37,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoam
Colour = colourProvider.Background4
},
new FillFlowContainer
{
@ -119,12 +118,12 @@ namespace osu.Game.Overlays.Profile.Header
hiddenDetailGlobal = new OverlinedInfoContainer
{
Title = "Global Ranking",
LineColour = colours.Yellow
LineColour = colourProvider.Highlight1
},
hiddenDetailCountry = new OverlinedInfoContainer
{
Title = "Country Ranking",
LineColour = colours.Yellow
LineColour = colourProvider.Highlight1
},
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osuTK;
namespace osu.Game.Overlays.Profile.Header.Components
@ -25,10 +24,10 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
IdleColour = colours.GreySeafoamLight;
HoverColour = colours.GreySeafoamLight.Darken(0.2f);
IdleColour = colourProvider.Background2;
HoverColour = colourProvider.Background2.Lighten(0.2f);
Child = icon = new SpriteIcon
{

View File

@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
InternalChildren = new Drawable[]
{
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
RelativeSizeAxes = Axes.Both,
BackgroundColour = Color4.Black,
Direction = BarDirection.LeftToRight,
AccentColour = colours.Yellow
AccentColour = colourProvider.Highlight1
}
},
levelProgressText = new OsuSpriteText

View File

@ -43,7 +43,8 @@ namespace osu.Game.Overlays.Profile.Header.Components
line = new Circle
{
RelativeSizeAxes = Axes.X,
Height = 4,
Height = 2,
Margin = new MarginPadding { Bottom = 2 }
},
title = new OsuSpriteText
{

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Header.Components
@ -27,12 +26,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
InternalChild = info = new OverlinedInfoContainer
{
Title = "Total Play Time",
LineColour = colours.Yellow,
LineColour = colourProvider.Highlight1,
};
User.BindValueChanged(updateTime, true);

View File

@ -2,12 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile.Header.Components
{
@ -24,9 +23,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
{
AutoSizeAxes = Axes.X;
IdleColour = Color4.Black;
HoverColour = OsuColour.Gray(0.1f);
base.Content.Add(new CircularContainer
{
Masking = true,
@ -47,5 +43,12 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
});
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
IdleColour = colourProvider.Background6;
HoverColour = colourProvider.Background5;
}
}
}

View File

@ -1,63 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Rulesets;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile.Header.Components
{
public class ProfileRulesetSelector : RulesetSelector
public class ProfileRulesetSelector : OverlayRulesetSelector
{
private Color4 accentColour = Color4.White;
public readonly Bindable<User> User = new Bindable<User>();
public ProfileRulesetSelector()
{
TabContainer.Masking = false;
TabContainer.Spacing = new Vector2(10, 0);
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
accentColour = colours.Seafoam;
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
((ProfileRulesetTabItem)tabItem).AccentColour = accentColour;
}
protected override void LoadComplete()
{
base.LoadComplete();
User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true);
}
public void SetDefaultRuleset(RulesetInfo ruleset)
{
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
foreach (var tabItem in TabContainer)
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
}
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value)
{
AccentColour = accentColour
};
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
};
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value);
}
}

View File

@ -2,40 +2,15 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile.Header.Components
{
public class ProfileRulesetTabItem : TabItem<RulesetInfo>, IHasAccentColour
public class ProfileRulesetTabItem : OverlayRulesetTabItem
{
private readonly OsuSpriteText text;
private readonly SpriteIcon icon;
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
updateState();
}
}
private bool isDefault;
public bool IsDefault
@ -52,74 +27,30 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
}
protected override Color4 AccentColour
{
get => base.AccentColour;
set
{
base.AccentColour = value;
icon.FadeColour(value, 120, Easing.OutQuint);
}
}
private readonly SpriteIcon icon;
public ProfileRulesetTabItem(RulesetInfo value)
: base(value)
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
Add(icon = new SpriteIcon
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(3, 0),
Children = new Drawable[]
{
text = new OsuSpriteText
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Text = value.Name,
},
icon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0,
AlwaysPresent = true,
Icon = FontAwesome.Solid.Star,
Size = new Vector2(12),
},
}
},
new HoverClickSounds()
};
}
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
updateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
private void updateState()
{
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
if (IsHovered || Active.Value)
{
text.FadeColour(Color4.White, 120, Easing.InQuad);
icon.FadeColour(Color4.White, 120, Easing.InQuad);
}
else
{
text.FadeColour(AccentColour, 120, Easing.InQuad);
icon.FadeColour(AccentColour, 120, Easing.InQuad);
}
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0,
AlwaysPresent = true,
Icon = FontAwesome.Solid.Star,
Size = new Vector2(12),
});
}
}
}

View File

@ -167,9 +167,9 @@ namespace osu.Game.Overlays.Profile.Header.Components
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
ballBg.Colour = colours.GreySeafoamDarker;
ballBg.Colour = colourProvider.Background5;
movingBall.BorderColour = line.Colour = colours.Yellow;
}
@ -270,7 +270,9 @@ namespace osu.Game.Overlays.Profile.Header.Components
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.GreySeafoamDark;
// Temporary colour since it's currently impossible to change it without bugs (see https://github.com/ppy/osu-framework/issues/3231)
// If above is fixed, this should use OverlayColourProvider
background.Colour = colours.Gray1;
}
public bool SetContent(object content)

View File

@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Profile.Header
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider, OsuColour colours)
{
AutoSizeAxes = Axes.Y;
@ -65,7 +65,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDarker,
Colour = colourProvider.Background5,
},
fillFlow = new FillFlowContainer
{
@ -152,12 +152,12 @@ namespace osu.Game.Overlays.Profile.Header
detailGlobalRank = new OverlinedInfoContainer(true, 110)
{
Title = "Global Ranking",
LineColour = colours.Yellow,
LineColour = colourProvider.Highlight1,
},
detailCountryRank = new OverlinedInfoContainer(false, 110)
{
Title = "Country Ranking",
LineColour = colours.Yellow,
LineColour = colourProvider.Highlight1,
},
}
}

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users;
using osuTK;
@ -23,7 +22,7 @@ namespace osu.Game.Overlays.Profile.Header
public readonly Bindable<User> User = new Bindable<User>();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
Alpha = 0;
AutoSizeAxes = Axes.Y;
@ -34,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDarker,
Colour = colourProvider.Background5,
},
new Container //artificial shadow
{

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -33,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Header
private FillFlowContainer userStats;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
Height = 150;
@ -42,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Header
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.GreySeafoamDark,
Colour = colourProvider.Background5,
},
new FillFlowContainer
{
@ -117,7 +116,7 @@ namespace osu.Game.Overlays.Profile.Header
RelativeSizeAxes = Axes.X,
Height = 1.5f,
Margin = new MarginPadding { Top = 10 },
Colour = colours.GreySeafoamLighter,
Colour = colourProvider.Light1,
},
new FillFlowContainer
{
@ -137,7 +136,7 @@ namespace osu.Game.Overlays.Profile.Header
Margin = new MarginPadding { Left = 10 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Colour = colours.GreySeafoamLighter,
Colour = colourProvider.Light1,
}
}
},
@ -178,7 +177,7 @@ namespace osu.Game.Overlays.Profile.Header
if (user?.Statistics != null)
{
userStats.Add(new UserStatsLine("Ranked Score", user.Statistics.RankedScore.ToString("#,##0")));
userStats.Add(new UserStatsLine("Hit Accuracy", Math.Round(user.Statistics.Accuracy, 2).ToString("#0.00'%'")));
userStats.Add(new UserStatsLine("Hit Accuracy", user.Statistics.DisplayAccuracy));
userStats.Add(new UserStatsLine("Play Count", user.Statistics.PlayCount.ToString("#,##0")));
userStats.Add(new UserStatsLine("Total Score", user.Statistics.TotalScore.ToString("#,##0")));
userStats.Add(new UserStatsLine("Total Hits", user.Statistics.TotalHits.ToString("#,##0")));

View File

@ -95,10 +95,10 @@ namespace osu.Game.Overlays.Profile
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
background.Colour = colours.GreySeafoamDarker;
underscore.Colour = colours.Seafoam;
background.Colour = colourProvider.Background5;
underscore.Colour = colourProvider.Highlight1;
}
private class SectionTriangles : Container
@ -128,11 +128,11 @@ namespace osu.Game.Overlays.Profile
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
triangles.ColourLight = colours.GreySeafoamDark;
triangles.ColourDark = colours.GreySeafoamDarker.Darken(0.2f);
foreground.Colour = ColourInfo.GradientVertical(colours.GreySeafoamDarker, colours.GreySeafoamDarker.Opacity(0));
triangles.ColourLight = colourProvider.Background4;
triangles.ColourDark = colourProvider.Background5.Darken(0.2f);
foreground.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Background5.Opacity(0));
}
}
}

View File

@ -10,7 +10,7 @@ using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Profile.Sections
{
/// <summary>
/// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row (see <see cref="DrawableProfileRow"/>).
/// Display artist/title/mapper information, commonly used as the left portion of a profile or score display row.
/// </summary>
public abstract class BeatmapMetadataContainer : OsuHoverContainer
{

View File

@ -1,124 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile.Sections
{
public abstract class DrawableProfileRow : Container
{
private const int fade_duration = 200;
private Box underscoreLine;
private Box coloredBackground;
private Container background;
/// <summary>
/// A visual element displayed to the left of <see cref="LeftFlowContainer"/> content.
/// </summary>
protected abstract Drawable CreateLeftVisual();
protected FillFlowContainer LeftFlowContainer { get; private set; }
protected FillFlowContainer RightFlowContainer { get; private set; }
protected override Container<Drawable> Content { get; }
protected DrawableProfileRow()
{
RelativeSizeAxes = Axes.X;
Height = 60;
Content = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Width = 0.97f,
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
{
InternalChildren = new Drawable[]
{
background = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 3,
Alpha = 0,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 1f,
Colour = Color4.Black.Opacity(0.2f),
},
Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both }
},
Content,
underscoreLine = new Box
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = 1,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
CreateLeftVisual(),
LeftFlowContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 10 },
Direction = FillDirection.Vertical,
},
}
},
RightFlowContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Vertical,
},
};
coloredBackground.Colour = underscoreLine.Colour = colour.Gray4;
}
protected override bool OnClick(ClickEvent e) => true;
protected override bool OnHover(HoverEvent e)
{
background.FadeIn(fade_duration, Easing.OutQuint);
underscoreLine.FadeOut(fade_duration, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
background.FadeOut(fade_duration, Easing.OutQuint);
underscoreLine.FadeIn(fade_duration, Easing.OutQuint);
base.OnHoverLost(e);
}
}
}

View File

@ -4,7 +4,6 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
@ -13,32 +12,25 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osuTK;
using System.Collections.Generic;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Overlays.Profile.Sections.Historical
{
public class DrawableMostPlayedBeatmap : OsuHoverContainer
public class DrawableMostPlayedBeatmap : CompositeDrawable
{
private const int cover_width = 100;
private const int corner_radius = 6;
private const int height = 50;
private readonly BeatmapInfo beatmap;
private readonly int playCount;
private Box background;
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
public DrawableMostPlayedBeatmap(BeatmapInfo beatmap, int playCount)
{
this.beatmap = beatmap;
this.playCount = playCount;
Enabled.Value = true; //manually enabled, because we have no action
RelativeSizeAxes = Axes.X;
Height = height;
Height = 50;
Masking = true;
CornerRadius = corner_radius;
@ -47,10 +39,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IdleColour = colours.GreySeafoam;
HoverColour = colours.GreySeafoamLight;
Children = new Drawable[]
AddRangeInternal(new Drawable[]
{
new UpdateableBeatmapSetCover
{
@ -72,46 +61,48 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
CornerRadius = corner_radius,
Children = new Drawable[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Container
new ProfileItemContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(10),
Children = new Drawable[]
Child = new Container
{
new FillFlowContainer
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(10),
Children = new Drawable[]
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
new FillFlowContainer
{
new MostPlayedBeatmapMetadataContainer(beatmap),
new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular))
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Colour = colours.GreySeafoamLighter
}.With(d =>
{
d.AddText("mapped by ");
d.AddUserLink(beatmap.Metadata.Author);
}),
}
},
new PlayCountText(playCount)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight
},
}
},
new MostPlayedBeatmapMetadataContainer(beatmap),
new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular))
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Colour = colours.GreySeafoamLighter
}.With(d =>
{
d.AddText("mapped by ");
d.AddUserLink(beatmap.Metadata.Author);
}),
}
},
new PlayCountText(playCount)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight
},
}
},
}
}
}
}
}
};
});
}
private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer

View File

@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
{
Masking = true,
RelativeSizeAxes = Axes.X,
Height = 5,
Height = 2,
Child = lineBackground = new Box
{
RelativeSizeAxes = Axes.Both,
@ -128,10 +128,10 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
lineBackground.Colour = colours.Yellow;
DescriptionText.Colour = colours.GreySeafoamLighter;
lineBackground.Colour = colourProvider.Highlight1;
DescriptionText.Colour = colourProvider.Foreground1;
}
}
}

View File

@ -0,0 +1,63 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osuTK.Graphics;
namespace osu.Game.Overlays.Profile.Sections
{
public class ProfileItemContainer : Container
{
private const int hover_duration = 200;
protected override Container<Drawable> Content => content;
private Color4 idleColour;
private Color4 hoverColour;
private readonly Box background;
private readonly Container content;
public ProfileItemContainer()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
CornerRadius = 6;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
}
});
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = idleColour = colourProvider.Background3;
hoverColour = colourProvider.Background2;
}
protected override bool OnHover(HoverEvent e)
{
background.FadeColour(hoverColour, hover_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
background.FadeColour(idleColour, hover_duration, Easing.OutQuint);
}
}
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Profile.Sections
@ -10,11 +9,11 @@ namespace osu.Game.Overlays.Profile.Sections
public class ProfileShowMoreButton : ShowMoreButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colors)
private void load(OverlayColourProvider colourProvider)
{
IdleColour = colors.GreySeafoamDark;
HoverColour = colors.GreySeafoam;
ChevronIconColour = colors.Yellow;
IdleColour = colourProvider.Background2;
HoverColour = colourProvider.Background1;
ChevronIconColour = colourProvider.Foreground1;
}
}
}

View File

@ -1,47 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawablePerformanceScore : DrawableProfileScore
{
private readonly double? weight;
public DrawablePerformanceScore(ScoreInfo score, double? weight = null)
: base(score)
{
this.weight = weight;
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
double pp = Score.PP ?? 0;
RightFlowContainer.Add(new OsuSpriteText
{
Text = $"{pp:0}pp",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
});
if (weight.HasValue)
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = $"weighted: {pp * weight:0}pp ({weight:P0})",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Regular, italics: true)
});
}
}
}
}

View File

@ -1,76 +1,233 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK;
using System;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Beatmaps;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Containers;
using osuTK;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public abstract class DrawableProfileScore : DrawableProfileRow
public class DrawableProfileScore : CompositeDrawable
{
private readonly FillFlowContainer modsContainer;
private const int height = 40;
private const int performance_width = 80;
private const float performance_background_shear = 0.45f;
private static readonly float performance_background_width = performance_width + (height / 4f * MathF.Tan(performance_background_shear));
protected readonly ScoreInfo Score;
protected DrawableProfileScore(ScoreInfo score)
[Resolved]
private OsuColour colours { get; set; }
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
public DrawableProfileScore(ScoreInfo score)
{
Score = score;
RelativeSizeAxes = Axes.X;
Height = 60;
Children = new Drawable[]
Height = height;
}
[BackgroundDependencyLoader]
private void load()
{
AddInternal(new ProfileItemContainer
{
modsContainer = new FillFlowContainer
Children = new Drawable[]
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Spacing = new Vector2(1),
Margin = new MarginPadding { Right = 160 }
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 10, Right = performance_width + 30 },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(8, 0),
Children = new Drawable[]
{
new UpdateableRank(Score.Rank)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(50, 20),
},
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 2),
Children = new Drawable[]
{
new ScoreBeatmapMetadataContainer(Score.Beatmap),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = $"{Score.Beatmap.Version}",
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
Colour = colours.Yellow
},
new DrawableDate(Score.Date, 12)
{
Colour = colourProvider.Foreground1
}
}
}
}
}
}
},
new FillFlowContainer
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15),
Children = new[]
{
CreateRightContent().With(c =>
{
c.Anchor = Anchor.CentreRight;
c.Origin = Anchor.CentreRight;
}),
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2),
Children = Score.Mods.Select(mod => new ModIcon(mod)
{
Scale = new Vector2(0.35f)
}).ToList(),
}
}
}
}
},
new Container
{
RelativeSizeAxes = Axes.Y,
Width = performance_width,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Children = new[]
{
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = performance_background_width,
Height = 0.5f,
Colour = colourProvider.Background4,
Shear = new Vector2(-performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
RelativePositionAxes = Axes.Y,
Width = performance_background_width,
Height = -0.5f,
Position = new Vector2(0, 1),
Colour = colourProvider.Background4,
Shear = new Vector2(performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
createDrawablePerformance().With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
})
}
}
}
};
});
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
[NotNull]
protected virtual Drawable CreateRightContent() => CreateDrawableAccuracy();
protected OsuSpriteText CreateDrawableAccuracy() => new OsuSpriteText
{
var text = new OsuSpriteText
{
Text = $"accuracy: {Score.Accuracy:P2}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Regular, italics: true)
};
RightFlowContainer.Insert(1, text);
LeftFlowContainer.Add(new ProfileScoreBeatmapMetadataContainer(Score.Beatmap));
LeftFlowContainer.Add(new DrawableDate(Score.Date));
foreach (Mod mod in Score.Mods)
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) });
}
protected override Drawable CreateLeftVisual() => new UpdateableRank(Score.Rank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
Text = Score.DisplayAccuracy,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Colour = colours.Yellow,
};
private class ProfileScoreBeatmapMetadataContainer : BeatmapMetadataContainer
private Drawable createDrawablePerformance()
{
public ProfileScoreBeatmapMetadataContainer(BeatmapInfo beatmap)
if (Score.PP.HasValue)
{
return new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = $"{Score.PP:0}",
Colour = colourProvider.Highlight1
},
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Text = "pp",
Colour = colourProvider.Light3
}
}
};
}
return new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = "-",
Colour = colourProvider.Highlight1
};
}
private class ScoreBeatmapMetadataContainer : BeatmapMetadataContainer
{
public ScoreBeatmapMetadataContainer(BeatmapInfo beatmap)
: base(beatmap)
{
}
@ -79,16 +236,19 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = new LocalisedString((
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")),
Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold, italics: true)
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} ",
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} ")),
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
},
new OsuSpriteText
{
Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
Padding = new MarginPadding { Top = 3 },
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular, italics: true)
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = "by " + new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)),
Font = OsuFont.GetFont(size: 12, italics: true)
},
};
}

View File

@ -0,0 +1,55 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableProfileWeightedScore : DrawableProfileScore
{
private readonly double weight;
public DrawableProfileWeightedScore(ScoreInfo score, double weight)
: base(score)
{
this.weight = weight;
}
protected override Drawable CreateRightContent() => new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
AutoSizeAxes = Axes.Y,
Width = 60,
Child = CreateDrawableAccuracy()
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = $"{Score.PP * weight:0}pp",
},
}
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12),
Text = $@"weighted {weight:0%}"
}
}
};
}
}

View File

@ -1,31 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableTotalScore : DrawableProfileScore
{
public DrawableTotalScore(ScoreInfo score)
: base(score)
{
}
[BackgroundDependencyLoader]
private void load()
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = Score.TotalScore.ToString("#,###"),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true)
});
}
}
}

View File

@ -15,14 +15,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class PaginatedScoreContainer : PaginatedContainer<APILegacyScoreInfo>
{
private readonly bool includeWeight;
private readonly ScoreType type;
public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, string missing, bool includeWeight = false)
public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, string missing)
: base(user, header, missing)
{
this.type = type;
this.includeWeight = includeWeight;
ItemsPerPage = 5;
@ -43,10 +41,10 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
switch (type)
{
default:
return new DrawablePerformanceScore(model.CreateScoreInfo(Rulesets), includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null);
return new DrawableProfileScore(model.CreateScoreInfo(Rulesets));
case ScoreType.Recent:
return new DrawableTotalScore(model.CreateScoreInfo(Rulesets));
case ScoreType.Best:
return new DrawableProfileWeightedScore(model.CreateScoreInfo(Rulesets), Math.Pow(0.95, ItemsContainer.Count));
}
}
}

View File

@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections
{
Children = new[]
{
new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true),
new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :("),
new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("),
};
}

View File

@ -1,9 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
@ -11,12 +13,19 @@ using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class DrawableRecentActivity : DrawableProfileRow
public class DrawableRecentActivity : CompositeDrawable
{
private IAPIProvider api;
private const int font_size = 14;
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private RulesetStore rulesets { get; set; }
private readonly APIRecentActivity activity;
@ -28,139 +37,191 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api)
private void load(OverlayColourProvider colourProvider)
{
this.api = api;
LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 };
LeftFlowContainer.Add(content = new LinkFlowContainer
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddInternal(new GridContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, size: 28),
new Dimension(),
new Dimension(GridSizeMode.AutoSize)
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = createIcon().With(icon =>
{
icon.Anchor = Anchor.Centre;
icon.Origin = Anchor.Centre;
})
},
content = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: font_size))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
},
new DrawableDate(activity.CreatedAt)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Colour = colourProvider.Foreground1,
Font = OsuFont.GetFont(size: font_size),
}
}
}
});
RightFlowContainer.Add(new DrawableDate(activity.CreatedAt)
{
Font = OsuFont.GetFont(size: 13),
Colour = OsuColour.Gray(0xAA),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
});
var formatted = createMessage();
content.AddLinks(formatted.Text, formatted.Links);
createMessage();
}
protected override Drawable CreateLeftVisual()
private Drawable createIcon()
{
switch (activity.Type)
{
case RecentActivityType.Rank:
return new UpdateableRank(activity.ScoreRank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
RelativeSizeAxes = Axes.X,
Height = 11,
FillMode = FillMode.Fit,
Margin = new MarginPadding { Top = 2 }
};
case RecentActivityType.Achievement:
return new DelayedLoadWrapper(new MedalIcon(activity.Achievement.Slug)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
})
{
RelativeSizeAxes = Axes.Y,
Width = 60,
RelativeSizeAxes = Axes.X,
Width = 0.5f,
Height = 18
};
default:
return new Container
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
return Empty();
}
}
private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}";
private MessageFormatter.MessageFormatterResult createMessage()
private void createMessage()
{
string userLinkTemplate() => $"[{toAbsoluteUrl(activity.User?.Url)} {activity.User?.Username}]";
string beatmapLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmap?.Url)} {activity.Beatmap?.Title}]";
string beatmapsetLinkTemplate() => $"[{toAbsoluteUrl(activity.Beatmapset?.Url)} {activity.Beatmapset?.Title}]";
string message;
switch (activity.Type)
{
case RecentActivityType.Achievement:
message = $"{userLinkTemplate()} unlocked the {activity.Achievement.Name} medal!";
addUserLink();
addText($" unlocked the \"{activity.Achievement.Name}\" medal!");
break;
case RecentActivityType.BeatmapPlaycount:
message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!";
addBeatmapLink();
addText($" has been played {activity.Count} times!");
break;
case RecentActivityType.BeatmapsetApprove:
message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!";
addBeatmapsetLink();
addText($" has been {activity.Approval.ToString().ToLowerInvariant()}!");
break;
case RecentActivityType.BeatmapsetDelete:
message = $"{beatmapsetLinkTemplate()} has been deleted.";
addBeatmapsetLink();
addText(" has been deleted.");
break;
case RecentActivityType.BeatmapsetRevive:
message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}.";
addBeatmapsetLink();
addText(" has been revived from eternal slumber by ");
addUserLink();
break;
case RecentActivityType.BeatmapsetUpdate:
message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!";
addUserLink();
addText(" has updated the beatmap ");
addBeatmapsetLink();
break;
case RecentActivityType.BeatmapsetUpload:
message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!";
addUserLink();
addText(" has submitted a new beatmap ");
addBeatmapsetLink();
break;
case RecentActivityType.Medal:
// apparently this shouldn't exist look at achievement instead (https://github.com/ppy/osu-web/blob/master/resources/assets/coffee/react/profile-page/recent-activity.coffee#L111)
message = string.Empty;
break;
case RecentActivityType.Rank:
message = $"{userLinkTemplate()} achieved rank #{activity.Rank} on {beatmapLinkTemplate()} ({activity.Mode}!)";
addUserLink();
addText($" achieved rank #{activity.Rank} on ");
addBeatmapLink();
addText($" ({getRulesetName()})");
break;
case RecentActivityType.RankLost:
message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)";
addUserLink();
addText(" has lost first place on ");
addBeatmapLink();
addText($" ({getRulesetName()})");
break;
case RecentActivityType.UserSupportAgain:
message = $"{userLinkTemplate()} has once again chosen to support osu! - thanks for your generosity!";
addUserLink();
addText(" has once again chosen to support osu! - thanks for your generosity!");
break;
case RecentActivityType.UserSupportFirst:
message = $"{userLinkTemplate()} has become an osu!supporter - thanks for your generosity!";
addUserLink();
addText(" has become an osu!supporter - thanks for your generosity!");
break;
case RecentActivityType.UserSupportGift:
message = $"{userLinkTemplate()} has received the gift of osu!supporter!";
addUserLink();
addText(" has received the gift of osu!supporter!");
break;
case RecentActivityType.UsernameChange:
message = $"{activity.User?.PreviousUsername} has changed their username to {userLinkTemplate()}!";
break;
default:
message = string.Empty;
addText($"{activity.User?.PreviousUsername} has changed their username to ");
addUserLink();
break;
}
return MessageFormatter.FormatText(message);
}
private string getRulesetName() =>
rulesets.AvailableRulesets.FirstOrDefault(r => r.ShortName == activity.Mode)?.Name ?? activity.Mode;
private void addUserLink()
=> content.AddLink(activity.User?.Username, LinkAction.OpenUserProfile, getLinkArgument(activity.User?.Url), creationParameters: t => t.Font = getLinkFont(FontWeight.Bold));
private void addBeatmapLink()
=> content.AddLink(activity.Beatmap?.Title, LinkAction.OpenBeatmap, getLinkArgument(activity.Beatmap?.Url), creationParameters: t => t.Font = getLinkFont());
private void addBeatmapsetLink()
=> content.AddLink(activity.Beatmapset?.Title, LinkAction.OpenBeatmapSet, getLinkArgument(activity.Beatmapset?.Url), creationParameters: t => t.Font = getLinkFont());
private string getLinkArgument(string url) => MessageFormatter.GetLinkDetails($"{api.Endpoint}{url}").Argument;
private FontUsage getLinkFont(FontWeight fontWeight = FontWeight.Regular)
=> OsuFont.GetFont(size: font_size, weight: fontWeight, italics: true);
private void addText(string text)
=> content.AddText(text, t => t.Font = OsuFont.GetFont(size: font_size, weight: FontWeight.SemiBold));
}
}

View File

@ -23,8 +23,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
Child = sprite = new Sprite
{
Height = 40,
Width = 40,
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};

View File

@ -8,6 +8,7 @@ using osu.Framework.Bindables;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.API;
using System.Collections.Generic;
using osuTK;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
@ -16,7 +17,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
public PaginatedRecentActivityContainer(Bindable<User> user, string header, string missing)
: base(user, header, missing)
{
ItemsPerPage = 5;
ItemsPerPage = 10;
ItemsContainer.Spacing = new Vector2(0, 8);
}
protected override APIRequest<List<APIRecentActivity>> CreateRequest() =>

View File

@ -0,0 +1,161 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
using osuTK;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Overlays.Rankings
{
public class SpotlightSelector : CompositeDrawable, IHasCurrentValue<APISpotlight>
{
private readonly Box background;
private readonly SpotlightsDropdown dropdown;
private readonly BindableWithCurrent<APISpotlight> current = new BindableWithCurrent<APISpotlight>();
public Bindable<APISpotlight> Current
{
get => current.Current;
set => current.Current = value;
}
public IEnumerable<APISpotlight> Spotlights
{
get => dropdown.Items;
set => dropdown.Items = value;
}
private readonly InfoColumn startDateColumn;
private readonly InfoColumn endDateColumn;
public SpotlightSelector()
{
RelativeSizeAxes = Axes.X;
Height = 100;
InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 },
Children = new Drawable[]
{
dropdown = new SpotlightsDropdown
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Current = Current,
Depth = -float.MaxValue
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new Drawable[]
{
startDateColumn = new InfoColumn(@"Start Date"),
endDateColumn = new InfoColumn(@"End Date"),
}
}
}
},
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = colourProvider.Dark3;
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(onCurrentChanged);
}
private void onCurrentChanged(ValueChangedEvent<APISpotlight> spotlight)
{
startDateColumn.Value = dateToString(spotlight.NewValue.StartDate);
endDateColumn.Value = dateToString(spotlight.NewValue.EndDate);
}
private string dateToString(DateTimeOffset date) => date.ToString("yyyy-MM-dd");
private class InfoColumn : FillFlowContainer
{
public string Value
{
set => valueText.Text = value;
}
private readonly OsuSpriteText valueText;
public InfoColumn(string name)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Vertical;
Children = new Drawable[]
{
new OsuSpriteText
{
Text = name,
Font = OsuFont.GetFont(size: 10),
},
new Container
{
AutoSizeAxes = Axes.X,
Height = 20,
Child = valueText = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Light),
}
}
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
valueText.Colour = colourProvider.Content2;
}
}
private class SpotlightsDropdown : OsuDropdown<APISpotlight>
{
private DropdownMenu menu;
protected override DropdownMenu CreateMenu() => menu = base.CreateMenu().With(m => m.MaxHeight = 400);
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
menu.BackgroundColour = colourProvider.Background5;
AccentColour = colourProvider.Background6;
}
}
}
}

View File

@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[]
{
new ColoredRowText { Text = $@"{item.Accuracy:F2}%", },
new ColoredRowText { Text = item.DisplayAccuracy, },
new ColoredRowText { Text = $@"{item.PlayCount:N0}", },
}.Concat(CreateUniqueContent(item)).Concat(new[]
{

View File

@ -17,10 +17,34 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
Children = new Drawable[]
{
new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
new SettingsSlider<double> { LabelText = "Master (window inactive)", Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
new SettingsSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f },
new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
new SettingsSlider<double>
{
LabelText = "Master",
Bindable = audio.Volume,
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Master (window inactive)",
Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive),
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Effect",
Bindable = audio.VolumeSample,
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Music",
Bindable = audio.VolumeTrack,
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
};
}
}

View File

@ -21,13 +21,15 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
LabelText = "Background dim",
Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<double>
{
LabelText = "Background blur",
Bindable = config.GetBindable<double>(OsuSetting.BlurLevel),
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsCheckbox
{

View File

@ -98,25 +98,29 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
LabelText = "Horizontal position",
Bindable = scalingPositionX,
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<float>
{
LabelText = "Vertical position",
Bindable = scalingPositionY,
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<float>
{
LabelText = "Horizontal scale",
Bindable = scalingSizeX,
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
new SettingsSlider<float>
{
LabelText = "Vertical scale",
Bindable = scalingSizeY,
KeyboardStep = 0.01f
KeyboardStep = 0.01f,
DisplayAsPercentage = true
},
}
},

View File

@ -3,6 +3,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
@ -22,16 +23,32 @@ namespace osu.Game.Overlays.Settings
RelativeSizeAxes = Axes.X
};
/// <summary>
/// When set, value changes based on user input are only transferred to any bound control's Current on commit.
/// This is useful if the UI interaction could be adversely affected by the value changing, such as the position of the <see cref="SliderBar{T}"/> on the screen.
/// </summary>
public bool TransferValueOnCommit
{
get => ((TSlider)Control).TransferValueOnCommit;
set => ((TSlider)Control).TransferValueOnCommit = value;
}
/// <summary>
/// A custom step value for each key press which actuates a change on this control.
/// </summary>
public float KeyboardStep
{
get => ((TSlider)Control).KeyboardStep;
set => ((TSlider)Control).KeyboardStep = value;
}
/// <summary>
/// Whether to format the tooltip as a percentage or the actual value.
/// </summary>
public bool DisplayAsPercentage
{
get => ((TSlider)Control).DisplayAsPercentage;
set => ((TSlider)Control).DisplayAsPercentage = value;
}
}
}

View File

@ -3,6 +3,7 @@
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -17,10 +18,18 @@ namespace osu.Game.Overlays
/// An overlay header which contains a <see cref="OsuTabControl{T}"/>.
/// </summary>
/// <typeparam name="T">The type of item to be represented by tabs.</typeparam>
public abstract class TabControlOverlayHeader<T> : OverlayHeader
public abstract class TabControlOverlayHeader<T> : OverlayHeader, IHasCurrentValue<T>
{
protected OsuTabControl<T> TabControl;
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
public Bindable<T> Current
{
get => current.Current;
set => current.Current = value;
}
private readonly Box controlBackground;
protected TabControlOverlayHeader()
@ -35,7 +44,11 @@ namespace osu.Game.Overlays
{
RelativeSizeAxes = Axes.Both,
},
TabControl = CreateTabControl().With(control => control.Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN })
TabControl = CreateTabControl().With(control =>
{
control.Margin = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN };
control.Current = current;
})
}
});
}

View File

@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Toolbar
{
int requested = e.Key - Key.Number1;
RulesetInfo found = Rulesets.AvailableRulesets.Skip(requested).FirstOrDefault();
RulesetInfo found = Rulesets.AvailableRulesets.ElementAtOrDefault(requested);
if (found != null)
Current.Value = found;
return true;

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile;
@ -30,7 +29,7 @@ namespace osu.Game.Overlays
public const float CONTENT_X_MARGIN = 70;
public UserProfileOverlay()
: base(OverlayColourScheme.Green)
: base(OverlayColourScheme.Pink)
{
}
@ -68,13 +67,13 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 30
Height = 34
};
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.1f)
Colour = ColourProvider.Background6
});
Add(sectionsContainer = new ProfileSectionsContainer
@ -83,7 +82,8 @@ namespace osu.Game.Overlays
FixedHeader = tabs,
HeaderBackground = new Box
{
Colour = OsuColour.Gray(34),
// this is only visible as the ProfileTabControl background
Colour = ColourProvider.Background5,
RelativeSizeAxes = Axes.Both
},
});
@ -165,9 +165,9 @@ namespace osu.Game.Overlays
};
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OverlayColourProvider colourProvider)
{
AccentColour = colours.Seafoam;
AccentColour = colourProvider.Highlight1;
}
private class ProfileTabItem : OverlayTabItem

View File

@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Framework.Timing;
@ -25,6 +26,7 @@ using osu.Game.Screens.Edit.Components.RadioButtons;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
using Key = osuTK.Input.Key;
namespace osu.Game.Rulesets.Edit
{
@ -58,6 +60,8 @@ namespace osu.Game.Rulesets.Edit
private InputManager inputManager;
private RadioButtonCollection toolboxCollection;
protected HitObjectComposer(Ruleset ruleset)
{
Ruleset = ruleset;
@ -100,7 +104,6 @@ namespace osu.Game.Rulesets.Edit
layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset);
RadioButtonCollection toolboxCollection;
InternalChild = new GridContainer
{
RelativeSizeAxes = Axes.Both,
@ -137,16 +140,32 @@ namespace osu.Game.Rulesets.Edit
}
};
toolboxCollection.Items =
CompositionTools.Select(t => new RadioButton(t.Name, () => selectTool(t)))
.Prepend(new RadioButton("Select", () => selectTool(null)))
.ToList();
toolboxCollection.Items = CompositionTools
.Prepend(new SelectTool())
.Select(t => new RadioButton(t.Name, () => toolSelected(t)))
.ToList();
toolboxCollection.Items[0].Select();
setSelectTool();
blueprintContainer.SelectionChanged += selectionChanged;
}
protected override bool OnKeyDown(KeyDownEvent e)
{
if (e.Key >= Key.Number1 && e.Key <= Key.Number9)
{
var item = toolboxCollection.Items.ElementAtOrDefault(e.Key - Key.Number1);
if (item != null)
{
item.Select();
return true;
}
}
return base.OnKeyDown(e);
}
protected override void LoadComplete()
{
base.LoadComplete();
@ -181,20 +200,30 @@ namespace osu.Game.Rulesets.Edit
{
var hitObjects = selectedHitObjects.ToArray();
if (!hitObjects.Any())
distanceSnapGridContainer.Hide();
else
if (hitObjects.Any())
{
// ensure in selection mode if a selection is made.
setSelectTool();
showGridFor(hitObjects);
}
else
distanceSnapGridContainer.Hide();
}
private void selectTool(HitObjectCompositionTool tool)
private void setSelectTool() => toolboxCollection.Items.First().Select();
private void toolSelected(HitObjectCompositionTool tool)
{
blueprintContainer.CurrentTool = tool;
if (tool == null)
if (tool is SelectTool)
distanceSnapGridContainer.Hide();
else
{
EditorBeatmap.SelectedHitObjects.Clear();
showGridFor(Enumerable.Empty<HitObject>());
}
}
private void showGridFor(IEnumerable<HitObject> selectedHitObjects)
@ -275,10 +304,10 @@ namespace osu.Game.Rulesets.Edit
}
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
=> beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance));
=> beatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime) - referenceTime;
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
=> DurationToDistance(referenceTime, beatSnapProvider.SnapTime(referenceTime, DistanceToDuration(referenceTime, distance)));
=> DurationToDistance(referenceTime, beatSnapProvider.SnapTime(DistanceToDuration(referenceTime, distance), referenceTime));
protected override void Dispose(bool isDisposing)
{

View File

@ -8,10 +8,10 @@ namespace osu.Game.Rulesets.Edit
/// <summary>
/// Snaps a duration to the closest beat of a timing point applicable at the reference time.
/// </summary>
/// <param name="referenceTime">The time of the timing point which <paramref name="duration"/> resides in.</param>
/// <param name="duration">The duration to snap.</param>
/// <returns>A value that represents <paramref name="duration"/> snapped to the closest beat of the timing point.</returns>
double SnapTime(double referenceTime, double duration);
/// <param name="time">The time to snap.</param>
/// <param name="referenceTime">An optional reference point to use for timing point lookup.</param>
/// <returns>A value that represents <paramref name="time"/> snapped to the closest beat of the timing point.</returns>
double SnapTime(double time, double? referenceTime = null);
/// <summary>
/// Get the most appropriate beat length at a given time.

View File

@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Edit.Tools
}
public abstract PlacementBlueprint CreatePlacementBlueprint();
public override string ToString() => Name;
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Rulesets.Edit.Tools
{
public class SelectTool : HitObjectCompositionTool
{
public SelectTool()
: base("Select")
{
}
public override PlacementBlueprint CreatePlacementBlueprint() => null;
}
}

View File

@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
private const double final_rate_progress = 0.75f;
[SettingSource("Initial rate", "The starting speed of the track")]
public abstract BindableNumber<double> InitialRate { get; }
[SettingSource("Final rate", "The final speed to ramp to")]
public abstract BindableNumber<double> FinalRate { get; }
@ -69,6 +72,6 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
/// <param name="amount">The amount of adjustment to apply (from 0..1).</param>
private void applyAdjustment(double amount) =>
SpeedChange.Value = 1 + (FinalRate.Value - 1) * Math.Clamp(amount, 0, 1);
SpeedChange.Value = InitialRate.Value + (FinalRate.Value - InitialRate.Value) * Math.Clamp(amount, 0, 1);
}
}

View File

@ -17,6 +17,16 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleDown;
public override double ScoreMultiplier => 1.0;
[SettingSource("Initial rate", "The starting speed of the track")]
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
{
MinValue = 1,
MaxValue = 2,
Default = 1,
Value = 1,
Precision = 0.01,
};
[SettingSource("Final rate", "The speed increase to ramp towards")]
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
{

View File

@ -17,6 +17,16 @@ namespace osu.Game.Rulesets.Mods
public override IconUsage? Icon => FontAwesome.Solid.ChevronCircleUp;
public override double ScoreMultiplier => 1.0;
[SettingSource("Initial rate", "The starting speed of the track")]
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
{
MinValue = 0.5,
MaxValue = 1,
Default = 1,
Value = 1,
Precision = 0.01,
};
[SettingSource("Final rate", "The speed increase to ramp towards")]
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
{

View File

@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects
/// </summary>
public event Action DefaultsApplied;
public readonly Bindable<double> StartTimeBindable = new Bindable<double>();
public readonly Bindable<double> StartTimeBindable = new BindableDouble();
/// <summary>
/// The time at which the HitObject starts.

View File

@ -220,9 +220,11 @@ namespace osu.Game.Scoring.Legacy
float lastTime = 0;
ReplayFrame currentFrame = null;
foreach (var l in reader.ReadToEnd().Split(','))
var frames = reader.ReadToEnd().Split(',');
for (var i = 0; i < frames.Length; i++)
{
var split = l.Split('|');
var split = frames[i].Split('|');
if (split.Length < 4)
continue;
@ -234,8 +236,14 @@ namespace osu.Game.Scoring.Legacy
}
var diff = Parsing.ParseFloat(split[0]);
lastTime += diff;
if (i == 0 && diff == 0)
// osu-stable adds a zero-time frame before potentially valid negative user frames.
// we need to ignore this.
continue;
// Todo: At some point we probably want to rewind and play back the negative-time frames
// but for now we'll achieve equal playback to stable by skipping negative frames
if (diff < 0)

View File

@ -13,6 +13,7 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
using osu.Game.Utils;
namespace osu.Game.Scoring
{
@ -31,6 +32,9 @@ namespace osu.Game.Scoring
[Column(TypeName = "DECIMAL(1,4)")]
public double Accuracy { get; set; }
[JsonIgnore]
public string DisplayAccuracy => Accuracy.FormatAccuracy();
[JsonProperty(@"pp")]
public double? PP { get; set; }

View File

@ -36,7 +36,7 @@ namespace osu.Game.Screens.Backgrounds
/// <summary>
/// The amount of blur to be applied in addition to user-specified blur.
/// </summary>
public readonly Bindable<float> BlurAmount = new Bindable<float>();
public readonly Bindable<float> BlurAmount = new BindableFloat();
internal readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
@ -119,7 +119,7 @@ namespace osu.Game.Screens.Backgrounds
/// <remarks>
/// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in <see cref="PlayerLoader"/>
/// </remarks>
public readonly Bindable<float> BlurAmount = new Bindable<float>();
public readonly Bindable<float> BlurAmount = new BindableFloat();
public Background Background
{

View File

@ -52,30 +52,52 @@ namespace osu.Game.Screens.Edit
{
switch (beatDivisor)
{
case 1:
return Color4.White;
case 2:
return colours.BlueLight;
return colours.Red;
case 4:
return colours.Blue;
case 8:
return colours.BlueDarker;
return colours.Yellow;
case 16:
return colours.PurpleDark;
case 3:
return colours.YellowLight;
return colours.Purple;
case 6:
return colours.Yellow;
return colours.YellowDark;
case 12:
return colours.YellowDarker;
default:
return Color4.White;
return Color4.Red;
}
}
/// <summary>
/// Retrieves the applicable divisor for a specific beat index.
/// </summary>
/// <param name="index">The 0-based beat index.</param>
/// <param name="beatDivisor">The beat divisor.</param>
/// <returns>The applicable divisor.</returns>
public static int GetDivisorForBeatIndex(int index, int beatDivisor)
{
int beat = index % beatDivisor;
foreach (var divisor in BindableBeatDivisor.VALID_DIVISORS)
{
if ((beat * divisor) % beatDivisor == 0)
return divisor;
}
return 0;
}
}
}

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@ -37,8 +36,8 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
{
this.button = button;
Text = button.Text;
Action = button.Action;
Text = button.Item.ToString();
Action = button.Select;
RelativeSizeAxes = Axes.X;
@ -100,19 +99,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
bubble.Colour = button.Selected.Value ? selectedBubbleColour : defaultBubbleColour;
}
protected override bool OnClick(ClickEvent e)
{
if (button.Selected.Value)
return true;
if (!Enabled.Value)
return true;
button.Selected.Value = true;
return base.OnClick(e);
}
protected override SpriteText CreateText() => new OsuSpriteText
{
Depth = -1,

View File

@ -15,33 +15,37 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons
public readonly BindableBool Selected;
/// <summary>
/// The text that should be displayed in this button.
/// The item related to this button.
/// </summary>
public string Text;
public object Item;
/// <summary>
/// The <see cref="Action"/> that should be invoked when this button is selected.
/// </summary>
public Action Action;
private readonly Action action;
public RadioButton(string text, Action action)
public RadioButton(object item, Action action)
{
Text = text;
Action = action;
Item = item;
this.action = action;
Selected = new BindableBool();
}
public RadioButton(string text)
: this(text, null)
public RadioButton(string item)
: this(item, null)
{
Text = text;
Action = null;
Item = item;
action = null;
}
/// <summary>
/// Selects this <see cref="RadioButton"/>.
/// </summary>
public void Select() => Selected.Value = true;
public void Select()
{
if (!Selected.Value)
{
Selected.Value = true;
action?.Invoke();
}
}
/// <summary>
/// Deselects this <see cref="RadioButton"/>.

View File

@ -12,7 +12,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations
/// </summary>
public class PointVisualisation : Box
{
protected PointVisualisation(double startTime)
public PointVisualisation(double startTime)
{
Origin = Anchor.TopCentre;

View File

@ -74,12 +74,16 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Select();
SelectionChanged?.Invoke(selectedHitObjects);
};
selectedHitObjects.ItemsRemoved += objects =>
{
foreach (var o in objects)
selectionBlueprints.FirstOrDefault(b => b.HitObject == o)?.Deselect();
SelectionChanged?.Invoke(selectedHitObjects);
};
}
@ -332,8 +336,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionHandler.HandleSelected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 1);
beatmap.SelectedHitObjects.Add(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
private void onBlueprintDeselected(SelectionBlueprint blueprint)
@ -341,8 +343,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
selectionHandler.HandleDeselected(blueprint);
selectionBlueprints.ChangeChildDepth(blueprint, 0);
beatmap.SelectedHitObjects.Remove(blueprint.HitObject);
SelectionChanged?.Invoke(selectionHandler.SelectedHitObjects);
}
#endregion

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
}
protected override void CreateContent(Vector2 startPosition)
protected override void CreateContent()
{
const float crosshair_thickness = 1;
const float crosshair_max_size = 10;
@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
new Box
{
Origin = Anchor.Centre,
Position = startPosition,
Position = StartPosition,
Width = crosshair_thickness,
EdgeSmoothness = new Vector2(1),
Height = Math.Min(crosshair_max_size, DistanceSpacing * 2),
@ -34,15 +34,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
new Box
{
Origin = Anchor.Centre,
Position = startPosition,
Position = StartPosition,
EdgeSmoothness = new Vector2(1),
Width = Math.Min(crosshair_max_size, DistanceSpacing * 2),
Height = crosshair_thickness,
}
});
float dx = Math.Max(startPosition.X, DrawWidth - startPosition.X);
float dy = Math.Max(startPosition.Y, DrawHeight - startPosition.Y);
float dx = Math.Max(StartPosition.X, DrawWidth - StartPosition.X);
float dy = Math.Max(StartPosition.Y, DrawHeight - StartPosition.Y);
float maxDistance = new Vector2(dx, dy).Length;
int requiredCircles = Math.Min(MaxIntervals, (int)(maxDistance / DistanceSpacing));
@ -53,11 +53,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
AddInternal(new CircularProgress
{
Origin = Anchor.Centre,
Position = startPosition,
Position = StartPosition,
Current = { Value = 1 },
Size = new Vector2(radius),
InnerRadius = 4 * 1f / radius,
Colour = GetColourForBeatIndex(i)
Colour = GetColourForIndexFromPlacement(i)
});
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics;
@ -106,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!gridCache.IsValid)
{
ClearInternal();
CreateContent(StartPosition);
CreateContent();
gridCache.Validate();
}
}
@ -114,7 +115,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary>
/// Creates the content which visualises the grid ticks.
/// </summary>
protected abstract void CreateContent(Vector2 startPosition);
protected abstract void CreateContent();
/// <summary>
/// Snaps a position to this grid.
@ -126,25 +127,17 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <summary>
/// Retrieves the applicable colour for a beat index.
/// </summary>
/// <param name="index">The 0-based beat index.</param>
/// <param name="placementIndex">The 0-based beat index from the point of placement.</param>
/// <returns>The applicable colour.</returns>
protected ColourInfo GetColourForBeatIndex(int index)
protected ColourInfo GetColourForIndexFromPlacement(int placementIndex)
{
int beat = (index + 1) % beatDivisor.Value;
ColourInfo colour = Colours.Gray5;
var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime);
var beatLength = timingPoint.BeatLength / beatDivisor.Value;
var beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength);
for (int i = 0; i < BindableBeatDivisor.VALID_DIVISORS.Length; i++)
{
int divisor = BindableBeatDivisor.VALID_DIVISORS[i];
var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours);
if ((beat * divisor) % beatDivisor.Value == 0)
{
colour = BindableBeatDivisor.GetColourFor(divisor, Colours);
break;
}
}
int repeatIndex = index / beatDivisor.Value;
int repeatIndex = placementIndex / beatDivisor.Value;
return colour.MultiplyAlpha(0.5f / (repeatIndex + 1));
}
}

View File

@ -30,7 +30,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
ZoomDuration = 200;
ZoomEasing = Easing.OutQuint;
Zoom = 10;
ScrollbarVisible = false;
}
@ -61,9 +60,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track;
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
MaxZoom = getZoomLevelForVisibleMilliseconds(500);
Zoom = getZoomLevelForVisibleMilliseconds(2000);
}, true);
}
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(track.Length / milliseconds);
/// <summary>
/// The timeline's scroll position in the last frame.
/// </summary>
@ -177,7 +182,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time)
{
var targetTime = (position.X / Content.DrawWidth) * track.Length;
return (position, beatSnapProvider.SnapTime(targetTime, targetTime));
return (position, beatSnapProvider.SnapTime(targetTime));
}
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();

View File

@ -0,0 +1,90 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{
public class TimelineTickDisplay : TimelinePart
{
[Resolved]
private EditorBeatmap beatmap { get; set; }
[Resolved]
private Bindable<WorkingBeatmap> working { get; set; }
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
[Resolved]
private OsuColour colours { get; set; }
public TimelineTickDisplay()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
beatDivisor.BindValueChanged(_ => createLines(), true);
}
private void createLines()
{
Clear();
for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++)
{
var point = beatmap.ControlPointInfo.TimingPoints[i];
var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length;
int beat = 0;
for (double t = point.Time; t < until; t += point.BeatLength / beatDivisor.Value)
{
var indexInBeat = beat % beatDivisor.Value;
if (indexInBeat == 0)
{
Add(new PointVisualisation(t)
{
Colour = BindableBeatDivisor.GetColourFor(1, colours),
Origin = Anchor.TopCentre,
});
}
else
{
var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value);
var colour = BindableBeatDivisor.GetColourFor(divisor, colours);
var height = 0.1f - (float)divisor / BindableBeatDivisor.VALID_DIVISORS.Last() * 0.08f;
Add(new PointVisualisation(t)
{
Colour = colour,
Height = height,
Origin = Anchor.TopCentre,
});
Add(new PointVisualisation(t)
{
Colour = colour,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomCentre,
Height = height,
});
}
beat++;
}
}
}
}
}

View File

@ -36,12 +36,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
base.Content.Add(zoomedContent = new Container { RelativeSizeAxes = Axes.Y });
}
private int minZoom = 1;
private float minZoom = 1;
/// <summary>
/// The minimum zoom level allowed.
/// </summary>
public int MinZoom
public float MinZoom
{
get => minZoom;
set
@ -56,12 +56,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}
}
private int maxZoom = 60;
private float maxZoom = 60;
/// <summary>
/// The maximum zoom level allowed.
/// </summary>
public int MaxZoom
public float MaxZoom
{
get => maxZoom;
set

View File

@ -348,7 +348,7 @@ namespace osu.Game.Screens.Edit
beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
}
public double SnapTime(double referenceTime, double duration) => editorBeatmap.SnapTime(referenceTime, duration);
public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);
public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);

View File

@ -128,12 +128,12 @@ namespace osu.Game.Screens.Edit
return list.Count - 1;
}
public double SnapTime(double referenceTime, double duration)
public double SnapTime(double time, double? referenceTime)
{
double beatLength = GetBeatLengthAtTime(referenceTime);
var timingPoint = ControlPointInfo.TimingPointAt(referenceTime ?? time);
var beatLength = timingPoint.BeatLength / BeatDivisor;
// A 1ms offset prevents rounding errors due to minute variations in duration
return (int)((duration + 1) / beatLength) * beatLength;
return timingPoint.Time + (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero) * beatLength;
}
public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor;

View File

@ -102,7 +102,11 @@ namespace osu.Game.Screens.Edit
LoadComponentAsync(new TimelineArea
{
RelativeSizeAxes = Axes.Both,
Child = CreateTimelineContent()
Children = new[]
{
new TimelineTickDisplay(),
CreateTimelineContent(),
}
}, timelineContainer.Add);
});
}

View File

@ -28,7 +28,7 @@ namespace osu.Game.Screens.Multi.Match.Components
protected override IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)),
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", model.DisplayAccuracy),
new LeaderboardScoreStatistic(FontAwesome.Solid.Sync, "Total Attempts", score.TotalAttempts.ToString()),
new LeaderboardScoreStatistic(FontAwesome.Solid.Check, "Completed Beatmaps", score.CompletedBeatmaps.ToString()),
};

View File

@ -96,7 +96,7 @@ namespace osu.Game.Screens.Multi
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Header.HEIGHT },
Child = screenStack = new OsuScreenStack(loungeSubScreen = new LoungeSubScreen()) { RelativeSizeAxes = Axes.Both }
Child = screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both }
},
new Header(screenStack),
createButton = new HeaderButton
@ -120,6 +120,8 @@ namespace osu.Game.Screens.Multi
}
};
screenStack.Push(loungeSubScreen = new LoungeSubScreen());
screenStack.ScreenPushed += screenPushed;
screenStack.ScreenExited += screenExited;
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Internal;
using osu.Framework.Allocation;
@ -95,15 +96,30 @@ namespace osu.Game.Screens
public Bindable<IReadOnlyList<Mod>> Mods { get; private set; }
private OsuScreenDependencies screenDependencies;
internal void CreateLeasedDependencies(IReadOnlyDependencyContainer dependencies) => createDependencies(dependencies);
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, parent);
if (screenDependencies == null)
{
if (DisallowExternalBeatmapRulesetChanges)
throw new InvalidOperationException($"Screens that specify {nameof(DisallowExternalBeatmapRulesetChanges)} must be pushed immediately.");
createDependencies(parent);
}
return base.CreateChildDependencies(screenDependencies);
}
private void createDependencies(IReadOnlyDependencyContainer dependencies)
{
screenDependencies = new OsuScreenDependencies(DisallowExternalBeatmapRulesetChanges, dependencies);
Beatmap = screenDependencies.Beatmap;
Ruleset = screenDependencies.Ruleset;
Mods = screenDependencies.Mods;
return base.CreateChildDependencies(screenDependencies);
}
protected BackgroundScreen Background => backgroundStack?.CurrentScreen as BackgroundScreen;

View File

@ -13,22 +13,11 @@ namespace osu.Game.Screens
[Cached]
private BackgroundScreenStack backgroundScreenStack;
private ParallaxContainer parallaxContainer;
private readonly ParallaxContainer parallaxContainer;
protected float ParallaxAmount => parallaxContainer.ParallaxAmount;
public OsuScreenStack()
{
initializeStack();
}
public OsuScreenStack(IScreen baseScreen)
: base(baseScreen)
{
initializeStack();
}
private void initializeStack()
{
InternalChild = parallaxContainer = new ParallaxContainer
{
@ -36,13 +25,32 @@ namespace osu.Game.Screens
Child = backgroundScreenStack = new BackgroundScreenStack { RelativeSizeAxes = Axes.Both },
};
ScreenPushed += onScreenChange;
ScreenExited += onScreenChange;
ScreenPushed += screenPushed;
ScreenExited += screenExited;
}
private void onScreenChange(IScreen prev, IScreen next)
private void screenPushed(IScreen prev, IScreen next)
{
parallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * ((IOsuScreen)next)?.BackgroundParallaxAmount ?? 1.0f;
if (LoadState < LoadState.Ready)
{
// dependencies must be present to stay in a sane state.
// this is generally only ever hit by test scenes.
Schedule(() => screenPushed(prev, next));
return;
}
// create dependencies synchronously to ensure leases are in a sane state.
((OsuScreen)next).CreateLeasedDependencies((prev as OsuScreen)?.Dependencies ?? Dependencies);
setParallax(next);
}
private void screenExited(IScreen prev, IScreen next)
{
setParallax(next);
}
private void setParallax(IScreen next) =>
parallaxContainer.ParallaxAmount = ParallaxContainer.DEFAULT_PARALLAX_AMOUNT * ((IOsuScreen)next)?.BackgroundParallaxAmount ?? 1.0f;
}
}

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