Resolve merge conflicts

This commit is contained in:
TocoToucan
2018-04-14 15:11:28 +03:00
1075 changed files with 95344 additions and 95288 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -1,59 +1,59 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class DownloadButton : HeaderButton
{
public DownloadButton(string title, string subtitle)
{
Width = 120;
Add(new Container
{
Depth = -1,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = title,
TextSize = 13,
Font = @"Exo2.0-Bold",
},
new OsuSpriteText
{
Text = subtitle,
TextSize = 11,
Font = @"Exo2.0-Bold",
},
},
},
new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
},
});
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.BeatmapSet
{
public class DownloadButton : HeaderButton
{
public DownloadButton(string title, string subtitle)
{
Width = 120;
Add(new Container
{
Depth = -1,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = title,
TextSize = 13,
Font = @"Exo2.0-Bold",
},
new OsuSpriteText
{
Text = subtitle,
TextSize = 11,
Font = @"Exo2.0-Bold",
},
},
},
new SpriteIcon
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.fa_download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
},
});
}
}
}

View File

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

View File

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

View File

@ -1,28 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.BeatmapSet
{
public class HeaderButton : TriangleButton
{
public HeaderButton()
{
Height = 0;
RelativeSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
BackgroundColour = OsuColour.FromHex(@"094c5f");
Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b");
Triangles.ColourDark = OsuColour.FromHex(@"094c5f");
Triangles.TriangleScale = 1.5f;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.BeatmapSet
{
public class HeaderButton : TriangleButton
{
public HeaderButton()
{
Height = 0;
RelativeSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
BackgroundColour = OsuColour.FromHex(@"094c5f");
Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b");
Triangles.ColourDark = OsuColour.FromHex(@"094c5f");
Triangles.TriangleScale = 1.5f;
}
}
}

View File

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

View File

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

View File

@ -1,62 +1,62 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using osu.Framework.Input;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ClickableUsername : OsuHoverContainer
{
private readonly OsuSpriteText text;
private UserProfileOverlay profile;
private User user;
public User User
{
get { return user; }
set
{
if (user == value) return;
user = value;
text.Text = user.Username;
}
}
public float TextSize
{
set
{
if (text.TextSize == value) return;
text.TextSize = value;
}
get { return text.TextSize; }
}
public ClickableUsername()
{
AutoSizeAxes = Axes.Both;
Child = text = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
};
}
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
this.profile = profile;
}
protected override bool OnClick(InputState state)
{
profile?.ShowUser(user);
return true;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using osu.Framework.Input;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ClickableUsername : OsuHoverContainer
{
private readonly OsuSpriteText text;
private UserProfileOverlay profile;
private User user;
public User User
{
get { return user; }
set
{
if (user == value) return;
user = value;
text.Text = user.Username;
}
}
public float TextSize
{
set
{
if (text.TextSize == value) return;
text.TextSize = value;
}
get { return text.TextSize; }
}
public ClickableUsername()
{
AutoSizeAxes = Axes.Both;
Child = text = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
};
}
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
this.profile = profile;
}
protected override bool OnClick(InputState state)
{
profile?.ShowUser(user);
return true;
}
}
}

View File

@ -1,142 +1,142 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class DrawableScore : Container
{
private const int fade_duration = 100;
private const float side_margin = 20;
private readonly Box background;
public DrawableScore(int index, OnlineScore score)
{
ScoreModsContainer modsContainer;
RelativeSizeAxes = Axes.X;
Height = 30;
CornerRadius = 3;
Masking = true;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = $"#{index + 1}",
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding { Left = side_margin }
},
new DrawableFlag(score.User.Country)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(30, 20),
Margin = new MarginPadding { Left = 60 }
},
new ClickableUsername
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
User = score.User,
Margin = new MarginPadding { Left = 100 }
},
modsContainer = new ScoreModsContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0.06f,
RelativePositionAxes = Axes.X,
X = 0.42f
},
new DrawableRank(score.Rank)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(30, 20),
FillMode = FillMode.Fit,
RelativePositionAxes = Axes.X,
X = 0.55f
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.TotalScore:N0}",
Font = @"Venera",
RelativePositionAxes = Axes.X,
X = 0.75f,
FixedWidth = true,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.Accuracy:P2}",
Font = @"Exo2.0-RegularItalic",
RelativePositionAxes = Axes.X,
X = 0.85f
},
new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}",
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding { Right = side_margin }
},
};
foreach (Mod mod in score.Mods)
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.35f),
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray4;
}
protected override bool OnHover(InputState state)
{
background.FadeIn(fade_duration, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
background.FadeOut(fade_duration, Easing.OutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state) => true;
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class DrawableScore : Container
{
private const int fade_duration = 100;
private const float side_margin = 20;
private readonly Box background;
public DrawableScore(int index, OnlineScore score)
{
ScoreModsContainer modsContainer;
RelativeSizeAxes = Axes.X;
Height = 30;
CornerRadius = 3;
Masking = true;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = $"#{index + 1}",
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding { Left = side_margin }
},
new DrawableFlag(score.User.Country)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(30, 20),
Margin = new MarginPadding { Left = 60 }
},
new ClickableUsername
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
User = score.User,
Margin = new MarginPadding { Left = 100 }
},
modsContainer = new ScoreModsContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0.06f,
RelativePositionAxes = Axes.X,
X = 0.42f
},
new DrawableRank(score.Rank)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(30, 20),
FillMode = FillMode.Fit,
RelativePositionAxes = Axes.X,
X = 0.55f
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.TotalScore:N0}",
Font = @"Venera",
RelativePositionAxes = Axes.X,
X = 0.75f,
FixedWidth = true,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.Accuracy:P2}",
Font = @"Exo2.0-RegularItalic",
RelativePositionAxes = Axes.X,
X = 0.85f
},
new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}",
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding { Right = side_margin }
},
};
foreach (Mod mod in score.Mods)
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.35f),
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray4;
}
protected override bool OnHover(InputState state)
{
background.FadeIn(fade_duration, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
background.FadeOut(fade_duration, Easing.OutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state) => true;
}
}

View File

@ -1,243 +1,243 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class DrawableTopScore : Container
{
private const float fade_duration = 100;
private const float height = 200;
private const float avatar_size = 80;
private const float margin = 10;
private readonly Box background;
private readonly Box bottomBackground;
private readonly Box middleLine;
private readonly UpdateableAvatar avatar;
private readonly DrawableFlag flag;
private readonly ClickableUsername username;
private readonly OsuSpriteText rankText;
private readonly OsuSpriteText date;
private readonly DrawableRank rank;
private readonly InfoColumn totalScore;
private readonly InfoColumn accuracy;
private readonly InfoColumn statistics;
private readonly ScoreModsContainer modsContainer;
private OnlineScore score;
public OnlineScore Score
{
get { return score; }
set
{
if (score == value) return;
score = value;
avatar.User = username.User = score.User;
flag.Country = score.User.Country;
date.Text = $@"achieved {score.Date:MMM d, yyyy}";
rank.UpdateRank(score.Rank);
totalScore.Value = $@"{score.TotalScore:N0}";
accuracy.Value = $@"{score.Accuracy:P2}";
statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}";
modsContainer.Clear();
foreach (Mod mod in score.Mods)
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.45f),
});
}
}
public DrawableTopScore()
{
RelativeSizeAxes = Axes.X;
Height = height;
CornerRadius = 5;
BorderThickness = 4;
Masking = true;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true, //used for correct border representation
},
avatar = new UpdateableAvatar
{
Size = new Vector2(avatar_size),
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Offset = new Vector2(0, 2),
Radius = 1,
},
Margin = new MarginPadding { Top = margin, Left = margin }
},
flag = new DrawableFlag
{
Size = new Vector2(30, 20),
Position = new Vector2(margin * 2 + avatar_size, height / 4),
},
username = new ClickableUsername
{
Origin = Anchor.BottomLeft,
TextSize = 30,
Position = new Vector2(margin * 2 + avatar_size, height / 4),
Margin = new MarginPadding { Bottom = 4 }
},
rankText = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.BottomRight,
Text = "#1",
TextSize = 40,
Font = @"Exo2.0-BoldItalic",
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
date = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
new Container
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Children = new Drawable[]
{
bottomBackground = new Box { RelativeSizeAxes = Axes.Both },
middleLine = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
},
rank = new DrawableRank(ScoreRank.F)
{
Origin = Anchor.BottomLeft,
Size = new Vector2(avatar_size, 40),
FillMode = FillMode.Fit,
Y = height / 4,
Margin = new MarginPadding { Left = margin }
},
new FillFlowContainer<InfoColumn>
{
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Position = new Vector2(height / 2, height / 4),
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new[]
{
totalScore = new InfoColumn("Score"),
accuracy = new InfoColumn("Accuracy"),
statistics = new InfoColumn("300/100/50"),
},
},
modsContainer = new ScoreModsContainer
{
AutoSizeAxes = Axes.Y,
Width = 80,
Position = new Vector2(height / 2, height / 4),
}
}
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = bottomBackground.Colour = colours.Gray4;
middleLine.Colour = colours.Gray2;
date.Colour = colours.Gray9;
BorderColour = rankText.Colour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
background.FadeIn(fade_duration, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
background.FadeOut(fade_duration, Easing.OutQuint);
base.OnHoverLost(state);
}
private class InfoColumn : FillFlowContainer
{
private readonly OsuSpriteText headerText;
private readonly OsuSpriteText valueText;
public string Value
{
set
{
if (valueText.Text == value)
return;
valueText.Text = value;
}
get { return valueText.Text; }
}
public InfoColumn(string header)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0, 3);
Children = new Drawable[]
{
headerText = new OsuSpriteText
{
TextSize = 14,
Text = header,
Font = @"Exo2.0-Bold",
},
valueText = new OsuSpriteText
{
TextSize = 25,
Font = @"Exo2.0-RegularItalic",
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
headerText.Colour = colours.Gray9;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class DrawableTopScore : Container
{
private const float fade_duration = 100;
private const float height = 200;
private const float avatar_size = 80;
private const float margin = 10;
private readonly Box background;
private readonly Box bottomBackground;
private readonly Box middleLine;
private readonly UpdateableAvatar avatar;
private readonly DrawableFlag flag;
private readonly ClickableUsername username;
private readonly OsuSpriteText rankText;
private readonly OsuSpriteText date;
private readonly DrawableRank rank;
private readonly InfoColumn totalScore;
private readonly InfoColumn accuracy;
private readonly InfoColumn statistics;
private readonly ScoreModsContainer modsContainer;
private OnlineScore score;
public OnlineScore Score
{
get { return score; }
set
{
if (score == value) return;
score = value;
avatar.User = username.User = score.User;
flag.Country = score.User.Country;
date.Text = $@"achieved {score.Date:MMM d, yyyy}";
rank.UpdateRank(score.Rank);
totalScore.Value = $@"{score.TotalScore:N0}";
accuracy.Value = $@"{score.Accuracy:P2}";
statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}";
modsContainer.Clear();
foreach (Mod mod in score.Mods)
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.45f),
});
}
}
public DrawableTopScore()
{
RelativeSizeAxes = Axes.X;
Height = height;
CornerRadius = 5;
BorderThickness = 4;
Masking = true;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true, //used for correct border representation
},
avatar = new UpdateableAvatar
{
Size = new Vector2(avatar_size),
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Offset = new Vector2(0, 2),
Radius = 1,
},
Margin = new MarginPadding { Top = margin, Left = margin }
},
flag = new DrawableFlag
{
Size = new Vector2(30, 20),
Position = new Vector2(margin * 2 + avatar_size, height / 4),
},
username = new ClickableUsername
{
Origin = Anchor.BottomLeft,
TextSize = 30,
Position = new Vector2(margin * 2 + avatar_size, height / 4),
Margin = new MarginPadding { Bottom = 4 }
},
rankText = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.BottomRight,
Text = "#1",
TextSize = 40,
Font = @"Exo2.0-BoldItalic",
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
date = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
new Container
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Children = new Drawable[]
{
bottomBackground = new Box { RelativeSizeAxes = Axes.Both },
middleLine = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
},
rank = new DrawableRank(ScoreRank.F)
{
Origin = Anchor.BottomLeft,
Size = new Vector2(avatar_size, 40),
FillMode = FillMode.Fit,
Y = height / 4,
Margin = new MarginPadding { Left = margin }
},
new FillFlowContainer<InfoColumn>
{
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Position = new Vector2(height / 2, height / 4),
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new[]
{
totalScore = new InfoColumn("Score"),
accuracy = new InfoColumn("Accuracy"),
statistics = new InfoColumn("300/100/50"),
},
},
modsContainer = new ScoreModsContainer
{
AutoSizeAxes = Axes.Y,
Width = 80,
Position = new Vector2(height / 2, height / 4),
}
}
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = bottomBackground.Colour = colours.Gray4;
middleLine.Colour = colours.Gray2;
date.Colour = colours.Gray9;
BorderColour = rankText.Colour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
background.FadeIn(fade_duration, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
background.FadeOut(fade_duration, Easing.OutQuint);
base.OnHoverLost(state);
}
private class InfoColumn : FillFlowContainer
{
private readonly OsuSpriteText headerText;
private readonly OsuSpriteText valueText;
public string Value
{
set
{
if (valueText.Text == value)
return;
valueText.Text = value;
}
get { return valueText.Text; }
}
public InfoColumn(string header)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0, 3);
Children = new Drawable[]
{
headerText = new OsuSpriteText
{
TextSize = 14,
Text = header,
Font = @"Exo2.0-Bold",
},
valueText = new OsuSpriteText
{
TextSize = 25,
Font = @"Exo2.0-RegularItalic",
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
headerText.Colour = colours.Gray9;
}
}
}
}

View File

@ -1,115 +1,115 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ScoresContainer : Container
{
private const int spacing = 15;
private const int fade_duration = 200;
private readonly FillFlowContainer flow;
private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation;
private readonly Box foreground;
private bool isLoading;
public bool IsLoading
{
get { return isLoading; }
set
{
if (isLoading == value) return;
isLoading = value;
foreground.FadeTo(isLoading ? 1 : 0, fade_duration);
loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration);
}
}
private IEnumerable<OnlineScore> scores;
public IEnumerable<OnlineScore> Scores
{
get { return scores; }
set
{
scores = value;
var scoresAmount = scores.Count();
if (scoresAmount == 0)
{
CleanAllScores();
return;
}
topScore.Score = scores.FirstOrDefault();
topScore.Show();
flow.Clear();
if (scoresAmount < 2)
return;
for (int i = 1; i < scoresAmount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i)));
}
}
public ScoresContainer()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.95f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, spacing),
Margin = new MarginPadding { Vertical = spacing },
Children = new Drawable[]
{
topScore = new DrawableTopScore(),
flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 1),
},
}
},
foreground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.7f),
Alpha = 0,
},
loadingAnimation = new LoadingAnimation
{
Alpha = 0,
},
};
}
public void CleanAllScores()
{
topScore.Hide();
flow.Clear();
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ScoresContainer : Container
{
private const int spacing = 15;
private const int fade_duration = 200;
private readonly FillFlowContainer flow;
private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation;
private readonly Box foreground;
private bool isLoading;
public bool IsLoading
{
get { return isLoading; }
set
{
if (isLoading == value) return;
isLoading = value;
foreground.FadeTo(isLoading ? 1 : 0, fade_duration);
loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration);
}
}
private IEnumerable<OnlineScore> scores;
public IEnumerable<OnlineScore> Scores
{
get { return scores; }
set
{
scores = value;
var scoresAmount = scores.Count();
if (scoresAmount == 0)
{
CleanAllScores();
return;
}
topScore.Score = scores.FirstOrDefault();
topScore.Show();
flow.Clear();
if (scoresAmount < 2)
return;
for (int i = 1; i < scoresAmount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i)));
}
}
public ScoresContainer()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.95f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, spacing),
Margin = new MarginPadding { Vertical = spacing },
Children = new Drawable[]
{
topScore = new DrawableTopScore(),
flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 1),
},
}
},
foreground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.7f),
Alpha = 0,
},
loadingAnimation = new LoadingAnimation
{
Alpha = 0,
},
};
}
public void CleanAllScores()
{
topScore.Hide();
flow.Clear();
}
}
}

View File

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

View File

@ -1,157 +1,157 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets;
using osu.Game.Overlays.BeatmapSet.Scores;
namespace osu.Game.Overlays
{
public class BeatmapSetOverlay : WaveOverlayContainer
{
public const float X_PADDING = 40;
public const float RIGHT_WIDTH = 275;
private readonly Header header;
private readonly Info info;
private readonly ScoresContainer scores;
private APIAccess api;
private RulesetStore rulesets;
private GetScoresRequest getScoresRequest;
private readonly ScrollContainer scroll;
// receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Both;
Width = 0.85f;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
scroll = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
header = new Header(),
info = new Info(),
scores = new ScoresContainer(),
},
},
},
};
header.Picker.Beatmap.ValueChanged += b =>
{
info.Beatmap = b;
updateScores(b);
};
}
private void updateScores(BeatmapInfo beatmap)
{
getScoresRequest?.Cancel();
if (!beatmap.OnlineBeatmapID.HasValue)
{
scores.CleanAllScores();
return;
}
scores.IsLoading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r =>
{
scores.Scores = r.Scores;
scores.IsLoading = false;
};
api.Queue(getScoresRequest);
}
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
this.api = api;
this.rulesets = rulesets;
}
protected override void PopIn()
{
base.PopIn();
FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
header.Details.StopPreview();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
protected override bool OnClick(InputState state)
{
State = Visibility.Hidden;
return true;
}
public void ShowBeatmapSet(int beatmapSetId)
{
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work.
var req = new GetBeatmapSetRequest(beatmapSetId);
req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets));
api.Queue(req);
}
public void ShowBeatmapSet(BeatmapSetInfo set)
{
header.BeatmapSet = info.BeatmapSet = set;
Show();
scroll.ScrollTo(0);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets;
using osu.Game.Overlays.BeatmapSet.Scores;
namespace osu.Game.Overlays
{
public class BeatmapSetOverlay : WaveOverlayContainer
{
public const float X_PADDING = 40;
public const float RIGHT_WIDTH = 275;
private readonly Header header;
private readonly Info info;
private readonly ScoresContainer scores;
private APIAccess api;
private RulesetStore rulesets;
private GetScoresRequest getScoresRequest;
private readonly ScrollContainer scroll;
// receive input outside our bounds so we can trigger a close event on ourselves.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
public BeatmapSetOverlay()
{
FirstWaveColour = OsuColour.Gray(0.4f);
SecondWaveColour = OsuColour.Gray(0.3f);
ThirdWaveColour = OsuColour.Gray(0.2f);
FourthWaveColour = OsuColour.Gray(0.1f);
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
RelativeSizeAxes = Axes.Both;
Width = 0.85f;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
scroll = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
header = new Header(),
info = new Info(),
scores = new ScoresContainer(),
},
},
},
};
header.Picker.Beatmap.ValueChanged += b =>
{
info.Beatmap = b;
updateScores(b);
};
}
private void updateScores(BeatmapInfo beatmap)
{
getScoresRequest?.Cancel();
if (!beatmap.OnlineBeatmapID.HasValue)
{
scores.CleanAllScores();
return;
}
scores.IsLoading = true;
getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
getScoresRequest.Success += r =>
{
scores.Scores = r.Scores;
scores.IsLoading = false;
};
api.Queue(getScoresRequest);
}
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
this.api = api;
this.rulesets = rulesets;
}
protected override void PopIn()
{
base.PopIn();
FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
}
protected override void PopOut()
{
base.PopOut();
header.Details.StopPreview();
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
}
protected override bool OnClick(InputState state)
{
State = Visibility.Hidden;
return true;
}
public void ShowBeatmapSet(int beatmapSetId)
{
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work.
var req = new GetBeatmapSetRequest(beatmapSetId);
req.Success += res => ShowBeatmapSet(res.ToBeatmapSet(rulesets));
api.Queue(req);
}
public void ShowBeatmapSet(BeatmapSetInfo set)
{
header.BeatmapSet = info.BeatmapSet = set;
Show();
scroll.ScrollTo(0);
}
}
}

View File

@ -1,191 +1,191 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
public class ChannelListItem : OsuClickableContainer, IFilterable
{
private const float width_padding = 5;
private const float channel_width = 150;
private const float text_size = 15;
private const float transition_duration = 100;
private readonly Channel channel;
private readonly Bindable<bool> joinedBind = new Bindable<bool>();
private readonly OsuSpriteText name;
private readonly OsuSpriteText topic;
private readonly SpriteIcon joinedCheckmark;
private Color4 joinedColour;
private Color4 topicColour;
private Color4 hoverColour;
public IEnumerable<string> FilterTerms => new[] { channel.Name };
public bool MatchingFilter
{
set
{
this.FadeTo(value ? 1f : 0f, 100);
}
}
public Action<Channel> OnRequestJoin;
public Action<Channel> OnRequestLeave;
public ChannelListItem(Channel channel)
{
this.channel = channel;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); };
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
Children = new[]
{
joinedCheckmark = new SpriteIcon
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Icon = FontAwesome.fa_check_circle,
Size = new Vector2(text_size),
Shadow = false,
Margin = new MarginPadding { Right = 10f },
},
},
},
new Container
{
Width = channel_width,
AutoSizeAxes = Axes.Y,
Children = new[]
{
name = new OsuSpriteText
{
Text = channel.ToString(),
TextSize = text_size,
Font = @"Exo2.0-Bold",
Shadow = false,
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
Width = 0.7f,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = width_padding },
Children = new[]
{
topic = new OsuSpriteText
{
Text = channel.Topic,
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = width_padding },
Spacing = new Vector2(3f, 0f),
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.fa_user,
Size = new Vector2(text_size - 2),
Shadow = false,
Margin = new MarginPadding { Top = 1 },
},
new OsuSpriteText
{
Text = @"0",
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
},
},
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
topicColour = colours.Gray9;
joinedColour = colours.Blue;
hoverColour = colours.Yellow;
joinedBind.ValueChanged += updateColour;
joinedBind.BindTo(channel.Joined);
joinedBind.TriggerChange();
FinishTransforms(true);
}
protected override bool OnHover(InputState state)
{
if (!channel.Joined.Value)
name.FadeColour(hoverColour, 50, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (!channel.Joined.Value)
name.FadeColour(Color4.White, transition_duration);
}
private void updateColour(bool joined)
{
if (joined)
{
name.FadeColour(Color4.White, transition_duration);
joinedCheckmark.FadeTo(1f, transition_duration);
topic.FadeTo(0.8f, transition_duration);
topic.FadeColour(Color4.White, transition_duration);
this.FadeColour(joinedColour, transition_duration);
}
else
{
joinedCheckmark.FadeTo(0f, transition_duration);
topic.FadeTo(1f, transition_duration);
topic.FadeColour(topicColour, transition_duration);
this.FadeColour(Color4.White, transition_duration);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
public class ChannelListItem : OsuClickableContainer, IFilterable
{
private const float width_padding = 5;
private const float channel_width = 150;
private const float text_size = 15;
private const float transition_duration = 100;
private readonly Channel channel;
private readonly Bindable<bool> joinedBind = new Bindable<bool>();
private readonly OsuSpriteText name;
private readonly OsuSpriteText topic;
private readonly SpriteIcon joinedCheckmark;
private Color4 joinedColour;
private Color4 topicColour;
private Color4 hoverColour;
public IEnumerable<string> FilterTerms => new[] { channel.Name };
public bool MatchingFilter
{
set
{
this.FadeTo(value ? 1f : 0f, 100);
}
}
public Action<Channel> OnRequestJoin;
public Action<Channel> OnRequestLeave;
public ChannelListItem(Channel channel)
{
this.channel = channel;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); };
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new Container
{
Children = new[]
{
joinedCheckmark = new SpriteIcon
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Icon = FontAwesome.fa_check_circle,
Size = new Vector2(text_size),
Shadow = false,
Margin = new MarginPadding { Right = 10f },
},
},
},
new Container
{
Width = channel_width,
AutoSizeAxes = Axes.Y,
Children = new[]
{
name = new OsuSpriteText
{
Text = channel.ToString(),
TextSize = text_size,
Font = @"Exo2.0-Bold",
Shadow = false,
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
Width = 0.7f,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = width_padding },
Children = new[]
{
topic = new OsuSpriteText
{
Text = channel.Topic,
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = width_padding },
Spacing = new Vector2(3f, 0f),
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.fa_user,
Size = new Vector2(text_size - 2),
Shadow = false,
Margin = new MarginPadding { Top = 1 },
},
new OsuSpriteText
{
Text = @"0",
TextSize = text_size,
Font = @"Exo2.0-SemiBold",
Shadow = false,
},
},
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
topicColour = colours.Gray9;
joinedColour = colours.Blue;
hoverColour = colours.Yellow;
joinedBind.ValueChanged += updateColour;
joinedBind.BindTo(channel.Joined);
joinedBind.TriggerChange();
FinishTransforms(true);
}
protected override bool OnHover(InputState state)
{
if (!channel.Joined.Value)
name.FadeColour(hoverColour, 50, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (!channel.Joined.Value)
name.FadeColour(Color4.White, transition_duration);
}
private void updateColour(bool joined)
{
if (joined)
{
name.FadeColour(Color4.White, transition_duration);
joinedCheckmark.FadeTo(1f, transition_duration);
topic.FadeTo(0.8f, transition_duration);
topic.FadeColour(Color4.White, transition_duration);
this.FadeColour(joinedColour, transition_duration);
}
else
{
joinedCheckmark.FadeTo(0f, transition_duration);
topic.FadeTo(1f, transition_duration);
topic.FadeColour(topicColour, transition_duration);
this.FadeColour(Color4.White, transition_duration);
}
}
}
}

View File

@ -1,63 +1,63 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Chat
{
public class ChannelSection : Container, IHasFilterableChildren
{
private readonly OsuSpriteText header;
public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
public IEnumerable<string> FilterTerms => new[] { Header };
public bool MatchingFilter
{
set
{
this.FadeTo(value ? 1f : 0f, 100);
}
}
public string Header
{
get { return header.Text; }
set { header.Text = value.ToUpper(); }
}
public IEnumerable<Channel> Channels
{
set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); }
}
public ChannelSection()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
header = new OsuSpriteText
{
TextSize = 15,
Font = @"Exo2.0-Bold",
},
ChannelFlow = new FillFlowContainer<ChannelListItem>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 25 },
Spacing = new Vector2(0f, 5f),
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Chat
{
public class ChannelSection : Container, IHasFilterableChildren
{
private readonly OsuSpriteText header;
public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
public IEnumerable<string> FilterTerms => new[] { Header };
public bool MatchingFilter
{
set
{
this.FadeTo(value ? 1f : 0f, 100);
}
}
public string Header
{
get { return header.Text; }
set { header.Text = value.ToUpper(); }
}
public IEnumerable<Channel> Channels
{
set { ChannelFlow.ChildrenEnumerable = value.Select(c => new ChannelListItem(c)); }
}
public ChannelSection()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
header = new OsuSpriteText
{
TextSize = 15,
Font = @"Exo2.0-Bold",
},
ChannelFlow = new FillFlowContainer<ChannelListItem>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 25 },
Spacing = new Vector2(0f, 5f),
},
};
}
}
}

View File

@ -1,185 +1,185 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
public class ChannelSelectionOverlay : OsuFocusedOverlayContainer
{
public static readonly float WIDTH_PADDING = 170;
private const float transition_duration = 500;
private readonly Box bg;
private readonly Triangles triangles;
private readonly Box headerBg;
private readonly SearchTextBox search;
private readonly SearchContainer<ChannelSection> sectionsFlow;
public Action<Channel> OnRequestJoin;
public Action<Channel> OnRequestLeave;
public IEnumerable<ChannelSection> Sections
{
set
{
sectionsFlow.ChildrenEnumerable = value;
foreach (ChannelSection s in sectionsFlow.Children)
{
foreach (ChannelListItem c in s.ChannelFlow.Children)
{
c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); };
c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); };
}
}
}
}
public ChannelSelectionOverlay()
{
RelativeSizeAxes = Axes.X;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
bg = new Box
{
RelativeSizeAxes = Axes.Both,
},
triangles = new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 5,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING },
Children = new[]
{
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new[]
{
sectionsFlow = new SearchContainer<ChannelSection>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
Spacing = new Vector2(0f, 20f),
Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING },
},
},
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
headerBg = new Box
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"Chat Channels",
TextSize = 20,
Shadow = false,
},
search = new HeaderSearchTextBox
{
RelativeSizeAxes = Axes.X,
PlaceholderText = @"Search",
Exit = Hide,
},
},
},
},
},
};
search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
bg.Colour = colours.Gray3;
triangles.ColourDark = colours.Gray3;
triangles.ColourLight = OsuColour.FromHex(@"353535");
headerBg.Colour = colours.Gray2.Opacity(0.75f);
}
protected override void OnFocus(InputState state)
{
GetContainingInputManager().ChangeFocus(search);
base.OnFocus(state);
}
protected override void PopIn()
{
if (Alpha == 0) this.MoveToY(DrawHeight);
this.FadeIn(transition_duration, Easing.OutQuint);
this.MoveToY(0, transition_duration, Easing.OutQuint);
search.HoldFocus = true;
base.PopIn();
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.InSine);
this.MoveToY(DrawHeight, transition_duration, Easing.InSine);
search.HoldFocus = false;
base.PopOut();
}
private class HeaderSearchTextBox : SearchTextBox
{
protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f);
protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
public class ChannelSelectionOverlay : OsuFocusedOverlayContainer
{
public static readonly float WIDTH_PADDING = 170;
private const float transition_duration = 500;
private readonly Box bg;
private readonly Triangles triangles;
private readonly Box headerBg;
private readonly SearchTextBox search;
private readonly SearchContainer<ChannelSection> sectionsFlow;
public Action<Channel> OnRequestJoin;
public Action<Channel> OnRequestLeave;
public IEnumerable<ChannelSection> Sections
{
set
{
sectionsFlow.ChildrenEnumerable = value;
foreach (ChannelSection s in sectionsFlow.Children)
{
foreach (ChannelListItem c in s.ChannelFlow.Children)
{
c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); };
c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); };
}
}
}
}
public ChannelSelectionOverlay()
{
RelativeSizeAxes = Axes.X;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
bg = new Box
{
RelativeSizeAxes = Axes.Both,
},
triangles = new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 5,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING },
Children = new[]
{
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
Children = new[]
{
sectionsFlow = new SearchContainer<ChannelSection>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint,
Spacing = new Vector2(0f, 20f),
Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING },
},
},
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
headerBg = new Box
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"Chat Channels",
TextSize = 20,
Shadow = false,
},
search = new HeaderSearchTextBox
{
RelativeSizeAxes = Axes.X,
PlaceholderText = @"Search",
Exit = Hide,
},
},
},
},
},
};
search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
bg.Colour = colours.Gray3;
triangles.ColourDark = colours.Gray3;
triangles.ColourLight = OsuColour.FromHex(@"353535");
headerBg.Colour = colours.Gray2.Opacity(0.75f);
}
protected override void OnFocus(InputState state)
{
GetContainingInputManager().ChangeFocus(search);
base.OnFocus(state);
}
protected override void PopIn()
{
if (Alpha == 0) this.MoveToY(DrawHeight);
this.FadeIn(transition_duration, Easing.OutQuint);
this.MoveToY(0, transition_duration, Easing.OutQuint);
search.HoldFocus = true;
base.PopIn();
}
protected override void PopOut()
{
this.FadeOut(transition_duration, Easing.InSine);
this.MoveToY(DrawHeight, transition_duration, Easing.InSine);
search.HoldFocus = false;
base.PopOut();
}
private class HeaderSearchTextBox : SearchTextBox
{
protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f);
protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f);
}
}
}

View File

@ -1,256 +1,256 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Chat
{
public class ChatLine : Container
{
private static readonly Color4[] username_colours =
{
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
OsuColour.FromHex("bc5151"),
OsuColour.FromHex("5c8bd6"),
OsuColour.FromHex("7f6ab7"),
OsuColour.FromHex("a368ad"),
OsuColour.FromHex("aa6880"),
OsuColour.FromHex("6fad9b"),
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
OsuColour.FromHex("53c4a1"),
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
OsuColour.FromHex("677c66"),
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
public const float LEFT_PADDING = message_padding + padding * 2;
private const float padding = 15;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
private Color4 customUsernameColour;
private OsuSpriteText timestamp;
public ChatLine(Message message)
{
Message = message;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = padding, Right = padding };
}
private Message message;
private OsuSpriteText username;
private LinkFlowContainer contentFlow;
public LinkFlowContainer ContentFlow => contentFlow;
public Message Message
{
get => message;
set
{
if (message == value) return;
message = MessageFormatter.FormatMessage(value);
if (!IsLoaded)
return;
updateMessageContent();
}
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, ChatOverlay chat)
{
this.chat = chat;
customUsernameColour = colours.ChatBlue;
}
private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour);
protected override void LoadComplete()
{
base.LoadComplete();
bool hasBackground = senderHasBackground;
Drawable effectedUsername = username = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
TextSize = text_size,
};
if (hasBackground)
{
// Background effect
effectedUsername = new Container
{
AutoSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
EdgeEffect = new EdgeEffectParameters
{
Roundness = 1,
Offset = new Vector2(0, 3),
Radius = 3,
Colour = Color4.Black.Opacity(0.3f),
Type = EdgeEffectType.Shadow,
},
// Drop shadow effect
Child = new Container
{
AutoSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
EdgeEffect = new EdgeEffectParameters
{
Radius = 1,
Colour = OsuColour.FromHex(message.Sender.Colour),
Type = EdgeEffectType.Shadow,
},
Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 },
Y = 3,
Child = username,
}
};
}
Children = new Drawable[]
{
new Container
{
Size = new Vector2(message_padding, text_size),
Children = new Drawable[]
{
timestamp = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBold",
FixedWidth = true,
TextSize = text_size * 0.75f,
},
new MessageSender(message.Sender)
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Child = effectedUsername,
},
}
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = message_padding + padding },
Children = new Drawable[]
{
contentFlow = new LinkFlowContainer(t =>
{
if (Message.IsAction)
{
t.Font = @"Exo2.0-MediumItalic";
if (senderHasBackground)
t.Colour = OsuColour.FromHex(message.Sender.Colour);
}
t.TextSize = text_size;
})
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
}
}
}
};
updateMessageContent();
FinishTransforms(true);
}
private ChatOverlay chat;
private void updateMessageContent()
{
this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
timestamp.FadeTo(message is LocalEchoMessage ? 0 : 1, 500, Easing.OutQuint);
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
username.Text = $@"{message.Sender.Username}" + (senderHasBackground || message.IsAction ? "" : ":");
// remove non-existent channels from the link list
message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chat?.AvailableChannels.Any(c => c.Name == link.Argument) != true);
contentFlow.Clear();
contentFlow.AddLinks(message.DisplayContent, message.Links);
}
private class MessageSender : OsuClickableContainer, IHasContextMenu
{
private readonly User sender;
public MessageSender(User sender)
{
this.sender = sender;
}
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
Action = () => profile?.ShowUser(sender);
}
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action),
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Chat
{
public class ChatLine : Container
{
private static readonly Color4[] username_colours =
{
OsuColour.FromHex("588c7e"),
OsuColour.FromHex("b2a367"),
OsuColour.FromHex("c98f65"),
OsuColour.FromHex("bc5151"),
OsuColour.FromHex("5c8bd6"),
OsuColour.FromHex("7f6ab7"),
OsuColour.FromHex("a368ad"),
OsuColour.FromHex("aa6880"),
OsuColour.FromHex("6fad9b"),
OsuColour.FromHex("f2e394"),
OsuColour.FromHex("f2ae72"),
OsuColour.FromHex("f98f8a"),
OsuColour.FromHex("7daef4"),
OsuColour.FromHex("a691f2"),
OsuColour.FromHex("c894d3"),
OsuColour.FromHex("d895b0"),
OsuColour.FromHex("53c4a1"),
OsuColour.FromHex("eace5c"),
OsuColour.FromHex("ea8c47"),
OsuColour.FromHex("fc4f4f"),
OsuColour.FromHex("3d94ea"),
OsuColour.FromHex("7760ea"),
OsuColour.FromHex("af52c6"),
OsuColour.FromHex("e25696"),
OsuColour.FromHex("677c66"),
OsuColour.FromHex("9b8732"),
OsuColour.FromHex("8c5129"),
OsuColour.FromHex("8c3030"),
OsuColour.FromHex("1f5d91"),
OsuColour.FromHex("4335a5"),
OsuColour.FromHex("812a96"),
OsuColour.FromHex("992861"),
};
public const float LEFT_PADDING = message_padding + padding * 2;
private const float padding = 15;
private const float message_padding = 200;
private const float action_padding = 3;
private const float text_size = 20;
private Color4 customUsernameColour;
private OsuSpriteText timestamp;
public ChatLine(Message message)
{
Message = message;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = padding, Right = padding };
}
private Message message;
private OsuSpriteText username;
private LinkFlowContainer contentFlow;
public LinkFlowContainer ContentFlow => contentFlow;
public Message Message
{
get => message;
set
{
if (message == value) return;
message = MessageFormatter.FormatMessage(value);
if (!IsLoaded)
return;
updateMessageContent();
}
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, ChatOverlay chat)
{
this.chat = chat;
customUsernameColour = colours.ChatBlue;
}
private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour);
protected override void LoadComplete()
{
base.LoadComplete();
bool hasBackground = senderHasBackground;
Drawable effectedUsername = username = new OsuSpriteText
{
Font = @"Exo2.0-BoldItalic",
Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length],
TextSize = text_size,
};
if (hasBackground)
{
// Background effect
effectedUsername = new Container
{
AutoSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
EdgeEffect = new EdgeEffectParameters
{
Roundness = 1,
Offset = new Vector2(0, 3),
Radius = 3,
Colour = Color4.Black.Opacity(0.3f),
Type = EdgeEffectType.Shadow,
},
// Drop shadow effect
Child = new Container
{
AutoSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 4,
EdgeEffect = new EdgeEffectParameters
{
Radius = 1,
Colour = OsuColour.FromHex(message.Sender.Colour),
Type = EdgeEffectType.Shadow,
},
Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 },
Y = 3,
Child = username,
}
};
}
Children = new Drawable[]
{
new Container
{
Size = new Vector2(message_padding, text_size),
Children = new Drawable[]
{
timestamp = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-SemiBold",
FixedWidth = true,
TextSize = text_size * 0.75f,
},
new MessageSender(message.Sender)
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Child = effectedUsername,
},
}
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = message_padding + padding },
Children = new Drawable[]
{
contentFlow = new LinkFlowContainer(t =>
{
if (Message.IsAction)
{
t.Font = @"Exo2.0-MediumItalic";
if (senderHasBackground)
t.Colour = OsuColour.FromHex(message.Sender.Colour);
}
t.TextSize = text_size;
})
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
}
}
}
};
updateMessageContent();
FinishTransforms(true);
}
private ChatOverlay chat;
private void updateMessageContent()
{
this.FadeTo(message is LocalEchoMessage ? 0.4f : 1.0f, 500, Easing.OutQuint);
timestamp.FadeTo(message is LocalEchoMessage ? 0 : 1, 500, Easing.OutQuint);
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
username.Text = $@"{message.Sender.Username}" + (senderHasBackground || message.IsAction ? "" : ":");
// remove non-existent channels from the link list
message.Links.RemoveAll(link => link.Action == LinkAction.OpenChannel && chat?.AvailableChannels.Any(c => c.Name == link.Argument) != true);
contentFlow.Clear();
contentFlow.AddLinks(message.DisplayContent, message.Links);
}
private class MessageSender : OsuClickableContainer, IHasContextMenu
{
private readonly User sender;
public MessageSender(User sender)
{
this.sender = sender;
}
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
Action = () => profile?.ShowUser(sender);
}
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem("View Profile", MenuItemType.Highlighted, Action),
};
}
}
}

View File

@ -1,334 +1,334 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using System;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
public class ChatTabControl : OsuTabControl<Channel>
{
private const float shear_width = 10;
public Action<Channel> OnRequestLeave;
public readonly Bindable<bool> ChannelSelectorActive = new Bindable<bool>();
private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab;
public ChatTabControl()
{
TabContainer.Margin = new MarginPadding { Left = 50 };
TabContainer.Spacing = new Vector2(-shear_width, 0);
TabContainer.Masking = false;
AddInternal(new SpriteIcon
{
Icon = FontAwesome.fa_comments,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(20),
Margin = new MarginPadding(10),
});
AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" }));
ChannelSelectorActive.BindTo(selectorTab.Active);
}
protected override void AddTabItem(TabItem<Channel> item, bool addToDropdown = true)
{
if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue)
// performTabSort might've made selectorTab's position wonky, fix it
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
base.AddTabItem(item, addToDropdown);
if (SelectedTab == null)
SelectTab(item);
}
protected override TabItem<Channel> CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
protected override void SelectTab(TabItem<Channel> tab)
{
if (tab is ChannelTabItem.ChannelSelectorTabItem)
{
tab.Active.Toggle();
return;
}
selectorTab.Active.Value = false;
base.SelectTab(tab);
}
private void tabCloseRequested(TabItem<Channel> tab)
{
int totalTabs = TabContainer.Count - 1; // account for selectorTab
int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs);
if (tab == SelectedTab && totalTabs > 1)
// Select the tab after tab-to-be-removed's index, or the tab before if current == last
SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]);
else if (totalTabs == 1 && !selectorTab.Active)
// Open channel selection overlay if all channel tabs will be closed after removing this tab
SelectTab(selectorTab);
OnRequestLeave?.Invoke(tab.Value);
}
private class ChannelTabItem : TabItem<Channel>
{
private Color4 backgroundInactive;
private Color4 backgroundHover;
private Color4 backgroundActive;
public override bool IsRemovable => !Pinned;
private readonly SpriteText text;
private readonly SpriteText textBold;
private readonly ClickableContainer closeButton;
private readonly Box box;
private readonly Box highlightBox;
private readonly SpriteIcon icon;
public Action<ChannelTabItem> OnRequestClose;
private void updateState()
{
if (Active)
fadeActive();
else
fadeInactive();
}
private const float transition_length = 400;
private void fadeActive()
{
this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint);
box.FadeColour(backgroundActive, transition_length, Easing.OutQuint);
highlightBox.FadeIn(transition_length, Easing.OutQuint);
text.FadeOut(transition_length, Easing.OutQuint);
textBold.FadeIn(transition_length, Easing.OutQuint);
}
private void fadeInactive()
{
this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint);
box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint);
highlightBox.FadeOut(transition_length, Easing.OutQuint);
text.FadeIn(transition_length, Easing.OutQuint);
textBold.FadeOut(transition_length, Easing.OutQuint);
}
protected override bool OnHover(InputState state)
{
if (IsRemovable)
closeButton.FadeIn(200, Easing.OutQuint);
if (!Active)
box.FadeColour(backgroundHover, transition_length, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
closeButton.FadeOut(200, Easing.OutQuint);
updateState();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundActive = colours.ChatBlue;
backgroundInactive = colours.Gray4;
backgroundHover = colours.Gray7;
highlightBox.Colour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
updateState();
}
public ChannelTabItem(Channel value) : base(value)
{
Width = 150;
RelativeSizeAxes = Axes.Y;
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0);
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Radius = 10,
Colour = Color4.Black.Opacity(0.2f),
};
Children = new Drawable[]
{
box = new Box
{
EdgeSmoothness = new Vector2(1, 0),
RelativeSizeAxes = Axes.Both,
},
highlightBox = new Box
{
Width = 5,
Alpha = 0,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
EdgeSmoothness = new Vector2(1, 0),
RelativeSizeAxes = Axes.Y,
},
new Container
{
Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0),
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
icon = new SpriteIcon
{
Icon = FontAwesome.fa_hashtag,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Colour = Color4.Black,
X = -10,
Alpha = 0.2f,
Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT),
},
text = new OsuSpriteText
{
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
TextSize = 18,
},
textBold = new OsuSpriteText
{
Alpha = 0,
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
Font = @"Exo2.0-Bold",
TextSize = 18,
},
closeButton = new CloseButton
{
Alpha = 0,
Margin = new MarginPadding { Right = 20 },
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
Action = delegate
{
if (IsRemovable) OnRequestClose?.Invoke(this);
},
},
},
},
};
}
public class CloseButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public CloseButton()
{
Size = new Vector2(20);
Child = icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.75f),
Icon = FontAwesome.fa_close,
RelativeSizeAxes = Axes.Both,
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.5f, 1000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(0.75f, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.FadeColour(Color4.Red, 200, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.FadeColour(Color4.White, 200, Easing.OutQuint);
base.OnHoverLost(state);
}
}
public class ChannelSelectorTabItem : ChannelTabItem
{
public override bool IsRemovable => false;
public ChannelSelectorTabItem(Channel value) : base(value)
{
Depth = float.MaxValue;
Width = 45;
icon.Alpha = 0;
text.TextSize = 45;
textBold.TextSize = 45;
}
[BackgroundDependencyLoader]
private new void load(OsuColour colour)
{
backgroundInactive = colour.Gray2;
backgroundActive = colour.Gray3;
}
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Chat;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using System;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Chat
{
public class ChatTabControl : OsuTabControl<Channel>
{
private const float shear_width = 10;
public Action<Channel> OnRequestLeave;
public readonly Bindable<bool> ChannelSelectorActive = new Bindable<bool>();
private readonly ChannelTabItem.ChannelSelectorTabItem selectorTab;
public ChatTabControl()
{
TabContainer.Margin = new MarginPadding { Left = 50 };
TabContainer.Spacing = new Vector2(-shear_width, 0);
TabContainer.Masking = false;
AddInternal(new SpriteIcon
{
Icon = FontAwesome.fa_comments,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(20),
Margin = new MarginPadding(10),
});
AddTabItem(selectorTab = new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" }));
ChannelSelectorActive.BindTo(selectorTab.Active);
}
protected override void AddTabItem(TabItem<Channel> item, bool addToDropdown = true)
{
if (item != selectorTab && TabContainer.GetLayoutPosition(selectorTab) < float.MaxValue)
// performTabSort might've made selectorTab's position wonky, fix it
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
base.AddTabItem(item, addToDropdown);
if (SelectedTab == null)
SelectTab(item);
}
protected override TabItem<Channel> CreateTabItem(Channel value) => new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
protected override void SelectTab(TabItem<Channel> tab)
{
if (tab is ChannelTabItem.ChannelSelectorTabItem)
{
tab.Active.Toggle();
return;
}
selectorTab.Active.Value = false;
base.SelectTab(tab);
}
private void tabCloseRequested(TabItem<Channel> tab)
{
int totalTabs = TabContainer.Count - 1; // account for selectorTab
int currentIndex = MathHelper.Clamp(TabContainer.IndexOf(tab), 1, totalTabs);
if (tab == SelectedTab && totalTabs > 1)
// Select the tab after tab-to-be-removed's index, or the tab before if current == last
SelectTab(TabContainer[currentIndex == totalTabs ? currentIndex - 1 : currentIndex + 1]);
else if (totalTabs == 1 && !selectorTab.Active)
// Open channel selection overlay if all channel tabs will be closed after removing this tab
SelectTab(selectorTab);
OnRequestLeave?.Invoke(tab.Value);
}
private class ChannelTabItem : TabItem<Channel>
{
private Color4 backgroundInactive;
private Color4 backgroundHover;
private Color4 backgroundActive;
public override bool IsRemovable => !Pinned;
private readonly SpriteText text;
private readonly SpriteText textBold;
private readonly ClickableContainer closeButton;
private readonly Box box;
private readonly Box highlightBox;
private readonly SpriteIcon icon;
public Action<ChannelTabItem> OnRequestClose;
private void updateState()
{
if (Active)
fadeActive();
else
fadeInactive();
}
private const float transition_length = 400;
private void fadeActive()
{
this.ResizeTo(new Vector2(Width, 1.1f), transition_length, Easing.OutQuint);
box.FadeColour(backgroundActive, transition_length, Easing.OutQuint);
highlightBox.FadeIn(transition_length, Easing.OutQuint);
text.FadeOut(transition_length, Easing.OutQuint);
textBold.FadeIn(transition_length, Easing.OutQuint);
}
private void fadeInactive()
{
this.ResizeTo(new Vector2(Width, 1), transition_length, Easing.OutQuint);
box.FadeColour(backgroundInactive, transition_length, Easing.OutQuint);
highlightBox.FadeOut(transition_length, Easing.OutQuint);
text.FadeIn(transition_length, Easing.OutQuint);
textBold.FadeOut(transition_length, Easing.OutQuint);
}
protected override bool OnHover(InputState state)
{
if (IsRemovable)
closeButton.FadeIn(200, Easing.OutQuint);
if (!Active)
box.FadeColour(backgroundHover, transition_length, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
closeButton.FadeOut(200, Easing.OutQuint);
updateState();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundActive = colours.ChatBlue;
backgroundInactive = colours.Gray4;
backgroundHover = colours.Gray7;
highlightBox.Colour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
updateState();
}
public ChannelTabItem(Channel value) : base(value)
{
Width = 150;
RelativeSizeAxes = Axes.Y;
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
Shear = new Vector2(shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0);
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Radius = 10,
Colour = Color4.Black.Opacity(0.2f),
};
Children = new Drawable[]
{
box = new Box
{
EdgeSmoothness = new Vector2(1, 0),
RelativeSizeAxes = Axes.Both,
},
highlightBox = new Box
{
Width = 5,
Alpha = 0,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
EdgeSmoothness = new Vector2(1, 0),
RelativeSizeAxes = Axes.Y,
},
new Container
{
Shear = new Vector2(-shear_width / ChatOverlay.TAB_AREA_HEIGHT, 0),
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
icon = new SpriteIcon
{
Icon = FontAwesome.fa_hashtag,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Colour = Color4.Black,
X = -10,
Alpha = 0.2f,
Size = new Vector2(ChatOverlay.TAB_AREA_HEIGHT),
},
text = new OsuSpriteText
{
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
TextSize = 18,
},
textBold = new OsuSpriteText
{
Alpha = 0,
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = value.ToString(),
Font = @"Exo2.0-Bold",
TextSize = 18,
},
closeButton = new CloseButton
{
Alpha = 0,
Margin = new MarginPadding { Right = 20 },
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
Action = delegate
{
if (IsRemovable) OnRequestClose?.Invoke(this);
},
},
},
},
};
}
public class CloseButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public CloseButton()
{
Size = new Vector2(20);
Child = icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.75f),
Icon = FontAwesome.fa_close,
RelativeSizeAxes = Axes.Both,
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.5f, 1000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(0.75f, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.FadeColour(Color4.Red, 200, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.FadeColour(Color4.White, 200, Easing.OutQuint);
base.OnHoverLost(state);
}
}
public class ChannelSelectorTabItem : ChannelTabItem
{
public override bool IsRemovable => false;
public ChannelSelectorTabItem(Channel value) : base(value)
{
Depth = float.MaxValue;
Width = 45;
icon.Alpha = 0;
text.TextSize = 45;
textBold.TextSize = 45;
}
[BackgroundDependencyLoader]
private new void load(OsuColour colour)
{
backgroundInactive = colour.Gray2;
backgroundActive = colour.Gray3;
}
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
}
}
}

View File

@ -1,134 +1,134 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Chat
{
public class DrawableChannel : Container
{
public readonly Channel Channel;
private readonly ChatLineContainer flow;
private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel)
{
Channel = channel;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
scroll = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
// Some chat lines have effects that slightly protrude to the bottom,
// which we do not want to mask away, hence the padding.
Padding = new MarginPadding { Bottom = 5 },
Child = new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = flow = new ChatLineContainer
{
Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
}
},
}
};
Channel.NewMessagesArrived += newMessagesArrived;
Channel.MessageRemoved += messageRemoved;
Channel.PendingMessageResolved += pendingMessageResolved;
}
[BackgroundDependencyLoader]
private void load()
{
newMessagesArrived(Channel.Messages);
}
protected override void LoadComplete()
{
base.LoadComplete();
scrollToEnd();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Channel.NewMessagesArrived -= newMessagesArrived;
Channel.MessageRemoved -= messageRemoved;
Channel.PendingMessageResolved -= pendingMessageResolved;
}
private void newMessagesArrived(IEnumerable<Message> newMessages)
{
// Add up to last Channel.MAX_HISTORY messages
var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY));
flow.AddRange(displayMessages.Select(m => new ChatLine(m)));
if (!IsLoaded) return;
if (scroll.IsScrolledToEnd(10) || !flow.Children.Any())
scrollToEnd();
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
int count = staleMessages.Length - Channel.MAX_HISTORY;
for (int i = 0; i < count; i++)
{
var d = staleMessages[i];
if (!scroll.IsScrolledToEnd(10))
scroll.OffsetScrollPosition(-d.DrawHeight);
d.Expire();
}
}
private void pendingMessageResolved(Message existing, Message updated)
{
var found = flow.Children.LastOrDefault(c => c.Message == existing);
if (found != null)
{
Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID.");
flow.Remove(found);
found.Message = updated;
flow.Add(found);
}
}
private void messageRemoved(Message removed)
{
flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire();
}
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
private class ChatLineContainer : FillFlowContainer<ChatLine>
{
protected override int Compare(Drawable x, Drawable y)
{
var xC = (ChatLine)x;
var yC = (ChatLine)y;
return xC.Message.CompareTo(yC.Message);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.Chat;
namespace osu.Game.Overlays.Chat
{
public class DrawableChannel : Container
{
public readonly Channel Channel;
private readonly ChatLineContainer flow;
private readonly ScrollContainer scroll;
public DrawableChannel(Channel channel)
{
Channel = channel;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
scroll = new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
// Some chat lines have effects that slightly protrude to the bottom,
// which we do not want to mask away, hence the padding.
Padding = new MarginPadding { Bottom = 5 },
Child = new OsuContextMenuContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Child = flow = new ChatLineContainer
{
Padding = new MarginPadding { Left = 20, Right = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
}
},
}
};
Channel.NewMessagesArrived += newMessagesArrived;
Channel.MessageRemoved += messageRemoved;
Channel.PendingMessageResolved += pendingMessageResolved;
}
[BackgroundDependencyLoader]
private void load()
{
newMessagesArrived(Channel.Messages);
}
protected override void LoadComplete()
{
base.LoadComplete();
scrollToEnd();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Channel.NewMessagesArrived -= newMessagesArrived;
Channel.MessageRemoved -= messageRemoved;
Channel.PendingMessageResolved -= pendingMessageResolved;
}
private void newMessagesArrived(IEnumerable<Message> newMessages)
{
// Add up to last Channel.MAX_HISTORY messages
var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY));
flow.AddRange(displayMessages.Select(m => new ChatLine(m)));
if (!IsLoaded) return;
if (scroll.IsScrolledToEnd(10) || !flow.Children.Any())
scrollToEnd();
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
int count = staleMessages.Length - Channel.MAX_HISTORY;
for (int i = 0; i < count; i++)
{
var d = staleMessages[i];
if (!scroll.IsScrolledToEnd(10))
scroll.OffsetScrollPosition(-d.DrawHeight);
d.Expire();
}
}
private void pendingMessageResolved(Message existing, Message updated)
{
var found = flow.Children.LastOrDefault(c => c.Message == existing);
if (found != null)
{
Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID.");
flow.Remove(found);
found.Message = updated;
flow.Add(found);
}
}
private void messageRemoved(Message removed)
{
flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire();
}
private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd());
private class ChatLineContainer : FillFlowContainer<ChatLine>
{
protected override int Compare(Drawable x, Drawable y)
{
var xC = (ChatLine)x;
var yC = (ChatLine)y;
return xC.Message.CompareTo(yC.Message);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,247 +1,247 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialog : OsuFocusedOverlayContainer
{
public static readonly float ENTER_DURATION = 500;
public static readonly float EXIT_DURATION = 200;
private readonly Vector2 ringSize = new Vector2(100f);
private readonly Vector2 ringMinifiedSize = new Vector2(20f);
private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f);
private readonly Container content;
private readonly Container ring;
private readonly FillFlowContainer<PopupDialogButton> buttonsContainer;
private readonly SpriteIcon icon;
private readonly SpriteText header;
private readonly TextFlowContainer body;
public FontAwesome Icon
{
get { return icon.Icon; }
set { icon.Icon = value; }
}
public string HeaderText
{
get { return header.Text; }
set { header.Text = value; }
}
public string BodyText
{
set { body.Text = value; }
}
public IEnumerable<PopupDialogButton> Buttons
{
get { return buttonsContainer.Children; }
set
{
buttonsContainer.ChildrenEnumerable = value;
foreach (PopupDialogButton b in value)
{
var action = b.Action;
b.Action = () =>
{
Hide();
action?.Invoke();
};
}
}
}
private void pressButtonAtIndex(int index)
{
if (index < Buttons.Count())
Buttons.Skip(index).First().TriggerOnClick();
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
if (args.Key == Key.Enter || args.Key == Key.KeypadEnter)
{
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
return true;
}
// press button at number if 1-9 on number row or keypad are pressed
var k = args.Key;
if (k >= Key.Number1 && k <= Key.Number9)
{
pressButtonAtIndex(k - Key.Number1);
return true;
}
if (k >= Key.Keypad1 && k <= Key.Keypad9)
{
pressButtonAtIndex(k - Key.Keypad1);
return true;
}
return base.OnKeyDown(state, args);
}
protected override void PopIn()
{
base.PopIn();
// Reset various animations but only if the dialog animation fully completed
if (content.Alpha == 0)
{
buttonsContainer.TransformSpacingTo(buttonsEnterSpacing);
buttonsContainer.MoveToY(buttonsEnterSpacing.Y);
ring.ResizeTo(ringMinifiedSize);
}
content.FadeIn(ENTER_DURATION, Easing.OutQuint);
ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint);
buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint);
buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint);
}
protected override void PopOut()
{
base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine);
}
public PopupDialog()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
content = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Width = 0.4f,
Alpha = 0f,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.5f),
Radius = 8,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"221a21"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourLight = OsuColour.FromHex(@"271e26"),
ColourDark = OsuColour.FromHex(@"1e171e"),
TriangleScale = 4,
},
},
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Position = new Vector2(0f, -50f),
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
new Container
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Size = ringSize,
Margin = new MarginPadding
{
Bottom = 30,
},
Children = new Drawable[]
{
ring = new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Masking = true,
BorderColour = Color4.White,
BorderThickness = 5f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0),
},
icon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = FontAwesome.fa_close,
Size = new Vector2(50),
},
},
},
},
},
header = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 25,
Shadow = true,
},
body = new OsuTextFlowContainer(t => t.TextSize = 18)
{
Padding = new MarginPadding(15),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
TextAnchor = Anchor.TopCentre,
},
},
},
buttonsContainer = new FillFlowContainer<PopupDialogButton>
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
},
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialog : OsuFocusedOverlayContainer
{
public static readonly float ENTER_DURATION = 500;
public static readonly float EXIT_DURATION = 200;
private readonly Vector2 ringSize = new Vector2(100f);
private readonly Vector2 ringMinifiedSize = new Vector2(20f);
private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f);
private readonly Container content;
private readonly Container ring;
private readonly FillFlowContainer<PopupDialogButton> buttonsContainer;
private readonly SpriteIcon icon;
private readonly SpriteText header;
private readonly TextFlowContainer body;
public FontAwesome Icon
{
get { return icon.Icon; }
set { icon.Icon = value; }
}
public string HeaderText
{
get { return header.Text; }
set { header.Text = value; }
}
public string BodyText
{
set { body.Text = value; }
}
public IEnumerable<PopupDialogButton> Buttons
{
get { return buttonsContainer.Children; }
set
{
buttonsContainer.ChildrenEnumerable = value;
foreach (PopupDialogButton b in value)
{
var action = b.Action;
b.Action = () =>
{
Hide();
action?.Invoke();
};
}
}
}
private void pressButtonAtIndex(int index)
{
if (index < Buttons.Count())
Buttons.Skip(index).First().TriggerOnClick();
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
if (args.Key == Key.Enter || args.Key == Key.KeypadEnter)
{
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
return true;
}
// press button at number if 1-9 on number row or keypad are pressed
var k = args.Key;
if (k >= Key.Number1 && k <= Key.Number9)
{
pressButtonAtIndex(k - Key.Number1);
return true;
}
if (k >= Key.Keypad1 && k <= Key.Keypad9)
{
pressButtonAtIndex(k - Key.Keypad1);
return true;
}
return base.OnKeyDown(state, args);
}
protected override void PopIn()
{
base.PopIn();
// Reset various animations but only if the dialog animation fully completed
if (content.Alpha == 0)
{
buttonsContainer.TransformSpacingTo(buttonsEnterSpacing);
buttonsContainer.MoveToY(buttonsEnterSpacing.Y);
ring.ResizeTo(ringMinifiedSize);
}
content.FadeIn(ENTER_DURATION, Easing.OutQuint);
ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint);
buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint);
buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint);
}
protected override void PopOut()
{
base.PopOut();
content.FadeOut(EXIT_DURATION, Easing.InSine);
}
public PopupDialog()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
content = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Width = 0.4f,
Alpha = 0f,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.5f),
Radius = 8,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"221a21"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
ColourLight = OsuColour.FromHex(@"271e26"),
ColourDark = OsuColour.FromHex(@"1e171e"),
TriangleScale = 4,
},
},
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Position = new Vector2(0f, -50f),
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
new Container
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Size = ringSize,
Margin = new MarginPadding
{
Bottom = 30,
},
Children = new Drawable[]
{
ring = new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Masking = true,
BorderColour = Color4.White,
BorderThickness = 5f,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0),
},
icon = new SpriteIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = FontAwesome.fa_close,
Size = new Vector2(50),
},
},
},
},
},
header = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 25,
Shadow = true,
},
body = new OsuTextFlowContainer(t => t.TextSize = 18)
{
Padding = new MarginPadding(15),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
TextAnchor = Anchor.TopCentre,
},
},
},
buttonsContainer = new FillFlowContainer<PopupDialogButton>
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
},
},
};
}
}
}

View File

@ -1,18 +1,18 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialogButton : DialogButton
{
public PopupDialogButton()
{
Height = 50;
BackgroundColour = OsuColour.FromHex(@"150e14");
TextSize = 18;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialogButton : DialogButton
{
public PopupDialogButton()
{
Height = 50;
BackgroundColour = OsuColour.FromHex(@"150e14");
TextSize = 18;
}
}
}

View File

@ -1,17 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialogCancelButton : PopupDialogButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Blue;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialogCancelButton : PopupDialogButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Blue;
}
}
}

View File

@ -1,17 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialogOkButton : PopupDialogButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Pink;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Dialog
{
public class PopupDialogOkButton : PopupDialogButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
ButtonColour = colours.Pink;
}
}
}

View File

@ -1,81 +1,81 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Dialog;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays
{
public class DialogOverlay : OsuFocusedOverlayContainer
{
private readonly Container dialogContainer;
private PopupDialog currentDialog;
public void Push(PopupDialog dialog)
{
if (dialog == currentDialog) return;
currentDialog?.Hide();
currentDialog = dialog;
dialogContainer.Add(currentDialog);
currentDialog.Show();
currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
State = Visibility.Visible;
}
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
{
if (v != Visibility.Hidden) return;
//handle the dialog being dismissed.
dialog.Delay(PopupDialog.EXIT_DURATION).Expire();
if (dialog == currentDialog)
State = Visibility.Hidden;
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint);
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine);
}
public DialogOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
},
},
dialogContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Dialog;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays
{
public class DialogOverlay : OsuFocusedOverlayContainer
{
private readonly Container dialogContainer;
private PopupDialog currentDialog;
public void Push(PopupDialog dialog)
{
if (dialog == currentDialog) return;
currentDialog?.Hide();
currentDialog = dialog;
dialogContainer.Add(currentDialog);
currentDialog.Show();
currentDialog.StateChanged += state => onDialogOnStateChanged(dialog, state);
State = Visibility.Visible;
}
private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v)
{
if (v != Visibility.Hidden) return;
//handle the dialog being dismissed.
dialog.Delay(PopupDialog.EXIT_DURATION).Expire();
if (dialog == currentDialog)
State = Visibility.Hidden;
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(PopupDialog.ENTER_DURATION, Easing.OutQuint);
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(PopupDialog.EXIT_DURATION, Easing.InSine);
}
public DialogOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
},
},
dialogContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
};
}
}
}

View File

@ -1,237 +1,237 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
namespace osu.Game.Overlays.Direct
{
public class DirectGridPanel : DirectPanel
{
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
private FillFlowContainer bottomPanel, statusContainer;
private PlayButton playButton;
private Box progressBar;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
Width = 400;
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
}
protected override void LoadComplete()
{
base.LoadComplete();
bottomPanel.LayoutDuration = 200;
bottomPanel.LayoutEasing = Easing.Out;
bottomPanel.Origin = Anchor.BottomLeft;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
Content.CornerRadius = 4;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
bottomPanel = new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, vertical_padding),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding },
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
progressBar = new Box
{
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
BypassAutoSizeAxes = Axes.Both,
Size = new Vector2(0, 3),
Alpha = 0,
Colour = colours.Yellow,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding
{
Top = vertical_padding,
Bottom = vertical_padding,
Left = horizontal_padding,
Right = horizontal_padding,
},
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Text = "mapped by ",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author.Username,
TextSize = 14,
Font = @"Exo2.0-SemiBoldItalic",
Shadow = false,
Colour = colours.BlueDark,
},
},
},
new Container
{
AutoSizeAxes = Axes.X,
Height = 14,
Children = new[]
{
new OsuSpriteText
{
Text = $"from {SetInfo.Metadata.Source}",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
new DownloadButton
{
Size = new Vector2(30),
Margin = new MarginPadding(horizontal_padding),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Colour = colours.Gray5,
Action = StartDownload
},
},
},
},
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding },
Children = new[]
{
new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0)
{
Margin = new MarginPadding { Right = 1 },
},
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
},
},
playButton = new PlayButton(SetInfo)
{
Margin = new MarginPadding { Top = 5, Left = 10 },
Size = new Vector2(30),
Alpha = 0,
},
statusContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 5, Left = 5 },
Spacing = new Vector2(5),
},
});
if (SetInfo.OnlineInfo?.HasVideo ?? false)
{
statusContainer.Add(new IconPill(FontAwesome.fa_film));
}
statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 })
{
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
});
}
protected override bool OnHover(InputState state)
{
statusContainer.FadeOut(120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
statusContainer.FadeIn(120, Easing.InOutQuint);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
namespace osu.Game.Overlays.Direct
{
public class DirectGridPanel : DirectPanel
{
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
private FillFlowContainer bottomPanel, statusContainer;
private PlayButton playButton;
private Box progressBar;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
Width = 400;
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
}
protected override void LoadComplete()
{
base.LoadComplete();
bottomPanel.LayoutDuration = 200;
bottomPanel.LayoutEasing = Easing.Out;
bottomPanel.Origin = Anchor.BottomLeft;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
Content.CornerRadius = 4;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
bottomPanel = new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, vertical_padding),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding },
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
progressBar = new Box
{
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
BypassAutoSizeAxes = Axes.Both,
Size = new Vector2(0, 3),
Alpha = 0,
Colour = colours.Yellow,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding
{
Top = vertical_padding,
Bottom = vertical_padding,
Left = horizontal_padding,
Right = horizontal_padding,
},
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Text = "mapped by ",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author.Username,
TextSize = 14,
Font = @"Exo2.0-SemiBoldItalic",
Shadow = false,
Colour = colours.BlueDark,
},
},
},
new Container
{
AutoSizeAxes = Axes.X,
Height = 14,
Children = new[]
{
new OsuSpriteText
{
Text = $"from {SetInfo.Metadata.Source}",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
new DownloadButton
{
Size = new Vector2(30),
Margin = new MarginPadding(horizontal_padding),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Colour = colours.Gray5,
Action = StartDownload
},
},
},
},
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding },
Children = new[]
{
new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0)
{
Margin = new MarginPadding { Right = 1 },
},
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
},
},
playButton = new PlayButton(SetInfo)
{
Margin = new MarginPadding { Top = 5, Left = 10 },
Size = new Vector2(30),
Alpha = 0,
},
statusContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 5, Left = 5 },
Spacing = new Vector2(5),
},
});
if (SetInfo.OnlineInfo?.HasVideo ?? false)
{
statusContainer.Add(new IconPill(FontAwesome.fa_film));
}
statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 })
{
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
});
}
protected override bool OnHover(InputState state)
{
statusContainer.FadeOut(120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
base.OnHoverLost(state);
statusContainer.FadeIn(120, Easing.InOutQuint);
}
}
}

View File

@ -1,169 +1,169 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct
{
public class DirectListPanel : DirectPanel
{
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
private const float height = 70;
public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
RelativeSizeAxes = Axes.X;
Height = height;
}
private PlayButton playButton;
private Box progressBar;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation, OsuColour colours)
{
Content.CornerRadius = 5;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding },
Children = new Drawable[]
{
new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
LayoutEasing = Easing.OutQuint,
LayoutDuration = 120,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
playButton = new PlayButton(SetInfo)
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Size = new Vector2(height / 2),
FillMode = FillMode.Fit,
Alpha = 0,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
}
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Right = height - vertical_padding * 2 + vertical_padding },
Children = new Drawable[]
{
new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0)
{
Margin = new MarginPadding { Right = 1 },
},
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Text = "mapped by ",
TextSize = 14,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author.Username,
TextSize = 14,
Font = @"Exo2.0-SemiBoldItalic",
},
},
},
new OsuSpriteText
{
Text = $"from {SetInfo.Metadata.Source}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
},
new DownloadButton
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Size = new Vector2(height - vertical_padding * 2),
Action = StartDownload
},
},
},
progressBar = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
BypassAutoSizeAxes = Axes.Y,
Size = new Vector2(0, 3),
Alpha = 0,
Colour = colours.Yellow,
},
});
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct
{
public class DirectListPanel : DirectPanel
{
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
private const float height = 70;
public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
RelativeSizeAxes = Axes.X;
Height = height;
}
private PlayButton playButton;
private Box progressBar;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation, OsuColour colours)
{
Content.CornerRadius = 5;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding },
Children = new Drawable[]
{
new FillFlowContainer
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
LayoutEasing = Easing.OutQuint,
LayoutDuration = 120,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
playButton = new PlayButton(SetInfo)
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Size = new Vector2(height / 2),
FillMode = FillMode.Fit,
Alpha = 0,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
}
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Right = height - vertical_padding * 2 + vertical_padding },
Children = new Drawable[]
{
new Statistic(FontAwesome.fa_play_circle, SetInfo.OnlineInfo?.PlayCount ?? 0)
{
Margin = new MarginPadding { Right = 1 },
},
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Text = "mapped by ",
TextSize = 14,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author.Username,
TextSize = 14,
Font = @"Exo2.0-SemiBoldItalic",
},
},
},
new OsuSpriteText
{
Text = $"from {SetInfo.Metadata.Source}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
},
new DownloadButton
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Size = new Vector2(height - vertical_padding * 2),
Action = StartDownload
},
},
},
progressBar = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
BypassAutoSizeAxes = Axes.Y,
Size = new Vector2(0, 3),
Alpha = 0,
Colour = colours.Yellow,
},
});
}
}
}

View File

@ -1,277 +1,278 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
namespace osu.Game.Overlays.Direct
{
public abstract class DirectPanel : Container
{
public readonly BeatmapSetInfo SetInfo;
protected Box BlackBackground;
private const double hover_transition_time = 400;
private Container content;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview;
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }
protected override Container<Drawable> Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
{
SetInfo = setInfo;
}
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 2f,
Colour = Color4.Black.Opacity(0.25f),
};
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 5f),
Radius = 10f,
Colour = Color4.Black.Opacity(0.3f),
};
private OsuColour colours;
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
this.beatmaps = beatmaps;
this.beatmapSetOverlay = beatmapSetOverlay;
this.colours = colours;
AddInternal(content = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
EdgeEffect = edgeEffectNormal,
Children = new[]
{
// temporary blackness until the actual background loads.
BlackBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
CreateBackground(),
progressBar = new ProgressBar
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 0,
Alpha = 0,
BackgroundColour = Color4.Black.Opacity(0.7f),
FillColour = colours.Blue,
Depth = -1,
},
}
});
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
}
public override bool DisposeOnDeathRemoval => true;
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
}
protected override void Update()
{
base.Update();
if (PreviewPlaying && Preview != null && Preview.IsLoaded)
{
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
}
}
protected override bool OnHover(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
PlayButton.FadeIn(120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
if (!PreviewPlaying)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
ShowInformation();
PreviewPlaying.Value = false;
return true;
}
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
protected void StartDownload()
{
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
beatmaps.Download(SetInfo);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
return;
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 0;
request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
};
request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress);
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FillColour = colours.Yellow;
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint);
PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint);
}
protected List<DifficultyIcon> GetDifficultyIcons()
{
var icons = new List<DifficultyIcon>();
foreach (var b in SetInfo.Beatmaps)
icons.Add(new DifficultyIcon(b));
return icons;
}
protected Drawable CreateBackground() => new DelayedLoadWrapper(
new BeatmapSetCover(SetInfo)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
OnLoadComplete = d =>
{
d.FadeInFromZero(400, Easing.Out);
BlackBackground.Delay(400).FadeOut();
},
}, 300)
{
RelativeSizeAxes = Axes.Both,
};
public class Statistic : FillFlowContainer
{
private readonly SpriteText text;
private int value;
public int Value
{
get { return value; }
set
{
this.value = value;
text.Text = Value.ToString(@"N0");
}
}
public Statistic(FontAwesome icon, int value = 0)
{
Anchor = Anchor.TopRight;
Origin = Anchor.TopRight;
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(5f, 0f);
Children = new Drawable[]
{
text = new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
},
new SpriteIcon
{
Icon = icon,
Shadow = true,
Size = new Vector2(14),
Margin = new MarginPadding { Top = 1 },
},
};
Value = value;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using osu.Framework.Configuration;
using osu.Framework.Audio.Track;
namespace osu.Game.Overlays.Direct
{
public abstract class DirectPanel : Container
{
public readonly BeatmapSetInfo SetInfo;
protected Box BlackBackground;
private const double hover_transition_time = 400;
private Container content;
private ProgressBar progressBar;
private BeatmapManager beatmaps;
private BeatmapSetOverlay beatmapSetOverlay;
public Track Preview => PlayButton.Preview;
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }
protected override Container<Drawable> Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
{
SetInfo = setInfo;
}
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 2f,
Colour = Color4.Black.Opacity(0.25f),
};
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 5f),
Radius = 10f,
Colour = Color4.Black.Opacity(0.3f),
};
private OsuColour colours;
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{
this.beatmaps = beatmaps;
this.beatmapSetOverlay = beatmapSetOverlay;
this.colours = colours;
AddInternal(content = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
EdgeEffect = edgeEffectNormal,
Children = new[]
{
// temporary blackness until the actual background loads.
BlackBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
CreateBackground(),
progressBar = new ProgressBar
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 0,
Alpha = 0,
BackgroundColour = Color4.Black.Opacity(0.7f),
FillColour = colours.Blue,
Depth = -1,
},
}
});
var downloadRequest = beatmaps.GetExistingDownload(SetInfo);
if (downloadRequest != null)
attachDownload(downloadRequest);
beatmaps.BeatmapDownloadBegan += attachDownload;
}
public override bool DisposeOnDeathRemoval => true;
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmaps.BeatmapDownloadBegan -= attachDownload;
}
protected override void Update()
{
base.Update();
if (PreviewPlaying && Preview != null && Preview.IsLoaded)
{
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
}
}
protected override bool OnHover(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
PlayButton.FadeIn(120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
if (!PreviewPlaying)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
ShowInformation();
PreviewPlaying.Value = false;
return true;
}
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
protected void StartDownload()
{
if (beatmaps.GetExistingDownload(SetInfo) != null)
{
// we already have an active download running.
content.MoveToX(-5, 50, Easing.OutSine).Then()
.MoveToX(5, 100, Easing.InOutSine).Then()
.MoveToX(-5, 100, Easing.InOutSine).Then()
.MoveToX(0, 50, Easing.InSine).Then();
return;
}
beatmaps.Download(SetInfo);
}
private void attachDownload(DownloadBeatmapSetRequest request)
{
if (request.BeatmapSet.OnlineBeatmapSetID != SetInfo.OnlineBeatmapSetID)
return;
progressBar.FadeIn(400, Easing.OutQuint);
progressBar.ResizeHeightTo(4, 400, Easing.OutQuint);
progressBar.Current.Value = 0;
request.Failure += e =>
{
progressBar.Current.Value = 0;
progressBar.FadeOut(500);
};
request.DownloadProgressed += progress => Schedule(() => progressBar.Current.Value = progress);
request.Success += data =>
{
progressBar.Current.Value = 1;
progressBar.FillColour = colours.Yellow;
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint);
PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint);
}
protected List<DifficultyIcon> GetDifficultyIcons()
{
var icons = new List<DifficultyIcon>();
foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty))
icons.Add(new DifficultyIcon(b));
return icons;
}
protected Drawable CreateBackground() => new DelayedLoadWrapper(
new BeatmapSetCover(SetInfo)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
OnLoadComplete = d =>
{
d.FadeInFromZero(400, Easing.Out);
BlackBackground.Delay(400).FadeOut();
},
}, 300)
{
RelativeSizeAxes = Axes.Both,
};
public class Statistic : FillFlowContainer
{
private readonly SpriteText text;
private int value;
public int Value
{
get { return value; }
set
{
this.value = value;
text.Text = Value.ToString(@"N0");
}
}
public Statistic(FontAwesome icon, int value = 0)
{
Anchor = Anchor.TopRight;
Origin = Anchor.TopRight;
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(5f, 0f);
Children = new Drawable[]
{
text = new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
},
new SpriteIcon
{
Icon = icon,
Shadow = true,
Size = new Vector2(14),
Margin = new MarginPadding { Top = 1 },
},
};
Value = value;
}
}
}
}

View File

@ -1,53 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Direct
{
public class DownloadButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public DownloadButton()
{
Children = new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(30),
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Direct
{
public class DownloadButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
public DownloadButton()
{
Children = new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(30),
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, Easing.OutElastic);
}
}
}

View File

@ -1,117 +1,117 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.Direct
{
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, RankStatus>
{
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private FillFlowContainer<RulesetToggleButton> modeButtons;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552");
protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked;
protected override Drawable CreateSupplementaryControls()
{
modeButtons = new FillFlowContainer<RulesetToggleButton>
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(10f, 0f),
};
return modeButtons;
}
[BackgroundDependencyLoader(true)]
private void load(OsuGame game, RulesetStore rulesets, OsuColour colours)
{
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
Ruleset.BindTo(game?.Ruleset ?? new Bindable<RulesetInfo> { Value = rulesets.GetRuleset(0) });
foreach (var r in rulesets.AvailableRulesets)
{
modeButtons.Add(new RulesetToggleButton(Ruleset, r));
}
}
private class RulesetToggleButton : OsuClickableContainer
{
private Drawable icon
{
get { return iconContainer.Icon; }
set { iconContainer.Icon = value; }
}
private RulesetInfo ruleset;
public RulesetInfo Ruleset
{
get { return ruleset; }
set
{
ruleset = value;
icon = Ruleset.CreateInstance().CreateIcon();
}
}
private readonly Bindable<RulesetInfo> bindable;
private readonly ConstrainedIconContainer iconContainer;
private void Bindable_ValueChanged(RulesetInfo obj)
{
iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100);
}
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
{
this.bindable = bindable;
AutoSizeAxes = Axes.Both;
Children = new[]
{
iconContainer = new ConstrainedIconContainer
{
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Size = new Vector2(32),
}
};
Ruleset = ruleset;
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(bindable.Value);
Action = () => bindable.Value = Ruleset;
}
protected override void Dispose(bool isDisposing)
{
if (bindable != null)
bindable.ValueChanged -= Bindable_ValueChanged;
base.Dispose(isDisposing);
}
}
}
public enum DirectSortCriteria
{
Relevance,
Title,
Artist,
Creator,
Difficulty,
Ranked,
Rating,
Plays,
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.Direct
{
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, RankStatus>
{
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private FillFlowContainer<RulesetToggleButton> modeButtons;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552");
protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked;
protected override Drawable CreateSupplementaryControls()
{
modeButtons = new FillFlowContainer<RulesetToggleButton>
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(10f, 0f),
};
return modeButtons;
}
[BackgroundDependencyLoader(true)]
private void load(OsuGame game, RulesetStore rulesets, OsuColour colours)
{
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
Ruleset.BindTo(game?.Ruleset ?? new Bindable<RulesetInfo> { Value = rulesets.GetRuleset(0) });
foreach (var r in rulesets.AvailableRulesets)
{
modeButtons.Add(new RulesetToggleButton(Ruleset, r));
}
}
private class RulesetToggleButton : OsuClickableContainer
{
private Drawable icon
{
get { return iconContainer.Icon; }
set { iconContainer.Icon = value; }
}
private RulesetInfo ruleset;
public RulesetInfo Ruleset
{
get { return ruleset; }
set
{
ruleset = value;
icon = Ruleset.CreateInstance().CreateIcon();
}
}
private readonly Bindable<RulesetInfo> bindable;
private readonly ConstrainedIconContainer iconContainer;
private void Bindable_ValueChanged(RulesetInfo obj)
{
iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100);
}
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
{
this.bindable = bindable;
AutoSizeAxes = Axes.Both;
Children = new[]
{
iconContainer = new ConstrainedIconContainer
{
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Size = new Vector2(32),
}
};
Ruleset = ruleset;
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(bindable.Value);
Action = () => bindable.Value = Ruleset;
}
protected override void Dispose(bool isDisposing)
{
if (bindable != null)
bindable.ValueChanged -= Bindable_ValueChanged;
base.Dispose(isDisposing);
}
}
}
public enum DirectSortCriteria
{
Relevance,
Title,
Artist,
Creator,
Difficulty,
Ranked,
Rating,
Plays,
}
}

View File

@ -1,38 +1,38 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.SearchableList;
namespace osu.Game.Overlays.Direct
{
public class Header : SearchableListHeader<DirectTab>
{
protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a");
protected override DirectTab DefaultTab => DirectTab.Search;
protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 };
protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o;
public Header()
{
Tabs.Current.Value = DirectTab.NewestMaps;
Tabs.Current.TriggerChange();
}
}
public enum DirectTab
{
Search,
[Description("Newest Maps")]
NewestMaps = DirectSortCriteria.Ranked,
[Description("Top Rated")]
TopRated = DirectSortCriteria.Rating,
[Description("Most Played")]
MostPlayed = DirectSortCriteria.Plays,
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.SearchableList;
namespace osu.Game.Overlays.Direct
{
public class Header : SearchableListHeader<DirectTab>
{
protected override Color4 BackgroundColour => OsuColour.FromHex(@"252f3a");
protected override DirectTab DefaultTab => DirectTab.Search;
protected override Drawable CreateHeaderText() => new OsuSpriteText { Text = @"osu!direct", TextSize = 25 };
protected override FontAwesome Icon => FontAwesome.fa_osu_chevron_down_o;
public Header()
{
Tabs.Current.Value = DirectTab.NewestMaps;
Tabs.Current.TriggerChange();
}
}
public enum DirectTab
{
Search,
[Description("Newest Maps")]
NewestMaps = DirectSortCriteria.Ranked,
[Description("Top Rated")]
TopRated = DirectSortCriteria.Rating,
[Description("Most Played")]
MostPlayed = DirectSortCriteria.Plays,
}
}

View File

@ -1,43 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Direct
{
public class IconPill : CircularContainer
{
public IconPill(FontAwesome icon)
{
AutoSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new Container
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding(5),
Child = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(12),
},
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Direct
{
public class IconPill : CircularContainer
{
public IconPill(FontAwesome icon)
{
AutoSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new Container
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding(5),
Child = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(12),
},
},
};
}
}
}

View File

@ -1,213 +1,213 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Direct
{
public class PlayButton : Container
{
public readonly Bindable<bool> Playing = new Bindable<bool>();
public Track Preview { get; private set; }
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Playing.Value = false;
trackLoader = null;
Preview = null;
}
}
private Color4 hoverColour;
private readonly SpriteIcon icon;
private readonly LoadingAnimation loadingAnimation;
private readonly BindableDouble muteBindable = new BindableDouble();
private const float transition_duration = 500;
private bool loading
{
set
{
if (value)
{
loadingAnimation.Show();
icon.FadeOut(transition_duration * 5, Easing.OutQuint);
}
else
{
loadingAnimation.Hide();
icon.FadeIn(transition_duration, Easing.OutQuint);
}
}
}
public PlayButton(BeatmapSetInfo setInfo = null)
{
BeatmapSet = setInfo;
AddRange(new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_play,
},
loadingAnimation = new LoadingAnimation(),
});
Playing.ValueChanged += playing =>
{
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
updatePreviewTrack(playing);
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour, AudioManager audio)
{
hoverColour = colour.Yellow;
this.audio = audio;
}
protected override bool OnClick(InputState state)
{
Playing.Value = !Playing.Value;
return true;
}
protected override bool OnHover(InputState state)
{
icon.FadeColour(hoverColour, 120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (!Playing.Value)
icon.FadeColour(Color4.White, 120, Easing.InOutQuint);
base.OnHoverLost(state);
}
protected override void Update()
{
base.Update();
if (Preview?.HasCompleted ?? false)
{
Playing.Value = false;
Preview = null;
}
}
private void updatePreviewTrack(bool playing)
{
if (playing)
{
if (Preview == null)
{
beginAudioLoad();
return;
}
Preview.Restart();
audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable);
}
else
{
audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
Preview?.Stop();
loading = false;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Playing.Value = false;
}
private TrackLoader trackLoader;
private AudioManager audio;
private void beginAudioLoad()
{
if (trackLoader != null)
{
Preview = trackLoader.Preview;
Playing.TriggerChange();
return;
}
loading = true;
LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"),
d =>
{
// We may have been replaced by another loader
if (trackLoader != d) return;
Preview = d?.Preview;
Playing.TriggerChange();
loading = false;
Add(trackLoader);
});
}
private class TrackLoader : Drawable
{
private readonly string preview;
public Track Preview;
private TrackManager trackManager;
public TrackLoader(string preview)
{
this.preview = preview;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, FrameworkConfigManager config)
{
// create a local trackManager to bypass the mute we are applying above.
audio.AddItem(trackManager = new TrackManager(new OnlineStore()));
// add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy).
config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume);
Preview = trackManager.Get(preview);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
trackManager?.Dispose();
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Direct
{
public class PlayButton : Container
{
public readonly Bindable<bool> Playing = new Bindable<bool>();
public Track Preview { get; private set; }
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
set
{
if (value == beatmapSet) return;
beatmapSet = value;
Playing.Value = false;
trackLoader = null;
Preview = null;
}
}
private Color4 hoverColour;
private readonly SpriteIcon icon;
private readonly LoadingAnimation loadingAnimation;
private readonly BindableDouble muteBindable = new BindableDouble();
private const float transition_duration = 500;
private bool loading
{
set
{
if (value)
{
loadingAnimation.Show();
icon.FadeOut(transition_duration * 5, Easing.OutQuint);
}
else
{
loadingAnimation.Hide();
icon.FadeIn(transition_duration, Easing.OutQuint);
}
}
}
public PlayButton(BeatmapSetInfo setInfo = null)
{
BeatmapSet = setInfo;
AddRange(new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_play,
},
loadingAnimation = new LoadingAnimation(),
});
Playing.ValueChanged += playing =>
{
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
updatePreviewTrack(playing);
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour, AudioManager audio)
{
hoverColour = colour.Yellow;
this.audio = audio;
}
protected override bool OnClick(InputState state)
{
Playing.Value = !Playing.Value;
return true;
}
protected override bool OnHover(InputState state)
{
icon.FadeColour(hoverColour, 120, Easing.InOutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (!Playing.Value)
icon.FadeColour(Color4.White, 120, Easing.InOutQuint);
base.OnHoverLost(state);
}
protected override void Update()
{
base.Update();
if (Preview?.HasCompleted ?? false)
{
Playing.Value = false;
Preview = null;
}
}
private void updatePreviewTrack(bool playing)
{
if (playing)
{
if (Preview == null)
{
beginAudioLoad();
return;
}
Preview.Restart();
audio.Track.AddAdjustment(AdjustableProperty.Volume, muteBindable);
}
else
{
audio.Track.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
Preview?.Stop();
loading = false;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Playing.Value = false;
}
private TrackLoader trackLoader;
private AudioManager audio;
private void beginAudioLoad()
{
if (trackLoader != null)
{
Preview = trackLoader.Preview;
Playing.TriggerChange();
return;
}
loading = true;
LoadComponentAsync(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3"),
d =>
{
// We may have been replaced by another loader
if (trackLoader != d) return;
Preview = d?.Preview;
Playing.TriggerChange();
loading = false;
Add(trackLoader);
});
}
private class TrackLoader : Drawable
{
private readonly string preview;
public Track Preview;
private TrackManager trackManager;
public TrackLoader(string preview)
{
this.preview = preview;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, FrameworkConfigManager config)
{
// create a local trackManager to bypass the mute we are applying above.
audio.AddItem(trackManager = new TrackManager(new OnlineStore()));
// add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy).
config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume);
Preview = trackManager.Get(preview);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
trackManager?.Dispose();
}
}
}
}

View File

@ -1,340 +1,348 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, RankStatus>
{
private const float panel_padding = 10f;
private APIAccess api;
private RulesetStore rulesets;
private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
private FillFlowContainer<DirectPanel> panels;
private DirectPanel playing;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
protected override SearchableListFilterControl<DirectSortCriteria, RankStatus> CreateFilterControl() => new FilterControl();
private IEnumerable<BeatmapSetInfo> beatmapSets;
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get { return beatmapSets; }
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value?.ToList();
if (beatmapSets == null) return;
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in beatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
}
}
private ResultCounts resultAmounts;
public ResultCounts ResultAmounts
{
get { return resultAmounts; }
set
{
if (value == ResultAmounts) return;
resultAmounts = value;
updateResultCounts();
}
}
public DirectOverlay()
{
RelativeSizeAxes = Axes.Both;
// osu!direct colours are not part of the standard palette
FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e");
ScrollFlow.Children = new Drawable[]
{
resultCountsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Top = 5 },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Found ",
TextSize = 15,
},
resultCountsText = new OsuSpriteText
{
TextSize = 15,
Font = @"Exo2.0-Bold",
},
}
},
};
Filter.Search.Current.ValueChanged += text =>
{
if (text != string.Empty)
{
Header.Tabs.Current.Value = DirectTab.Search;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked)
Filter.Tabs.Current.Value = DirectSortCriteria.Relevance;
}
else
{
Header.Tabs.Current.Value = DirectTab.NewestMaps;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance)
Filter.Tabs.Current.Value = DirectSortCriteria.Ranked;
}
};
((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch);
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch);
Header.Tabs.Current.ValueChanged += tab =>
{
if (tab != DirectTab.Search)
{
currentQuery.Value = string.Empty;
Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value;
Scheduler.AddOnce(updateSearch);
}
};
currentQuery.ValueChanged += v =>
{
queryChangedDebounce?.Cancel();
if (string.IsNullOrEmpty(v))
Scheduler.AddOnce(updateSearch);
else
{
BeatmapSets = null;
ResultAmounts = null;
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
}
};
currentQuery.BindTo(Filter.Search.Current);
Filter.Tabs.Current.ValueChanged += sortCriteria =>
{
if (Header.Tabs.Current.Value != DirectTab.Search && sortCriteria != (DirectSortCriteria)Header.Tabs.Current.Value)
Header.Tabs.Current.Value = DirectTab.Search;
Scheduler.AddOnce(updateSearch);
};
updateResultCounts();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps)
{
this.api = api;
this.rulesets = rulesets;
this.beatmaps = beatmaps;
resultCountsContainer.Colour = colours.Yellow;
beatmaps.ItemAdded += setAdded;
}
private void setAdded(BeatmapSetInfo set)
{
// if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
}
private void updateResultCounts()
{
resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint);
if (ResultAmounts == null) return;
resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " +
pluralize("Song", ResultAmounts.Songs) + ", " +
pluralize("Tag", ResultAmounts.Tags);
}
private string pluralize(string prefix, int value)
{
return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s");
}
private void recreatePanels(PanelDisplayStyle displayStyle)
{
if (panels != null)
{
panels.FadeOut(200);
panels.Expire();
panels = null;
if (playing != null)
{
playing.PreviewPlaying.Value = false;
playing = null;
}
}
if (BeatmapSets == null) return;
var newPanels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(panel_padding),
Margin = new MarginPadding { Top = 10 },
ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
{
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
return new DirectGridPanel(b)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
};
default:
return new DirectListPanel(b);
}
})
};
LoadComponentAsync(newPanels, p =>
{
if (panels != null) ScrollFlow.Remove(panels);
ScrollFlow.Add(panels = newPanels);
foreach (DirectPanel panel in p.Children)
panel.PreviewPlaying.ValueChanged += newValue =>
{
if (newValue)
{
if (playing != null && playing != panel)
playing.PreviewPlaying.Value = false;
playing = panel;
}
};
});
}
private SearchBeatmapSetsRequest getSetsRequest;
private readonly Bindable<string> currentQuery = new Bindable<string>();
private ScheduledDelegate queryChangedDebounce;
private void updateSearch()
{
queryChangedDebounce?.Cancel();
if (!IsLoaded) return;
BeatmapSets = null;
ResultAmounts = null;
getSetsRequest?.Cancel();
if (api == null) return;
if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return;
getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
((FilterControl)Filter).Ruleset.Value,
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
getSetsRequest.Success += response =>
{
Task.Run(() =>
{
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList();
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
// may not need scheduling; loads async internally.
Schedule(() =>
{
BeatmapSets = sets;
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
});
});
};
api.Queue(getSetsRequest);
}
protected override void PopOut()
{
base.PopOut();
if (playing != null)
playing.PreviewPlaying.Value = false;
}
private int distinctCount(List<string> list) => list.Distinct().ToArray().Length;
public class ResultCounts
{
public readonly int Artists;
public readonly int Songs;
public readonly int Tags;
public ResultCounts(int artists, int songs, int tags)
{
Artists = artists;
Songs = songs;
Tags = tags;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, RankStatus>
{
private const float panel_padding = 10f;
private APIAccess api;
private RulesetStore rulesets;
private BeatmapManager beatmaps;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
private FillFlowContainer<DirectPanel> panels;
private DirectPanel playing;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
protected override SearchableListFilterControl<DirectSortCriteria, RankStatus> CreateFilterControl() => new FilterControl();
private IEnumerable<BeatmapSetInfo> beatmapSets;
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get { return beatmapSets; }
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value?.ToList();
if (beatmapSets == null) return;
var artists = new List<string>();
var songs = new List<string>();
var tags = new List<string>();
foreach (var s in beatmapSets)
{
artists.Add(s.Metadata.Artist);
songs.Add(s.Metadata.Title);
tags.AddRange(s.Metadata.Tags.Split(' '));
}
ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags));
}
}
private ResultCounts resultAmounts;
public ResultCounts ResultAmounts
{
get { return resultAmounts; }
set
{
if (value == ResultAmounts) return;
resultAmounts = value;
updateResultCounts();
}
}
public DirectOverlay()
{
RelativeSizeAxes = Axes.Both;
// osu!direct colours are not part of the standard palette
FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e");
ScrollFlow.Children = new Drawable[]
{
resultCountsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Top = 5 },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Found ",
TextSize = 15,
},
resultCountsText = new OsuSpriteText
{
TextSize = 15,
Font = @"Exo2.0-Bold",
},
}
},
};
Filter.Search.Current.ValueChanged += text =>
{
if (text != string.Empty)
{
Header.Tabs.Current.Value = DirectTab.Search;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked)
Filter.Tabs.Current.Value = DirectSortCriteria.Relevance;
}
else
{
Header.Tabs.Current.Value = DirectTab.NewestMaps;
if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance)
Filter.Tabs.Current.Value = DirectSortCriteria.Ranked;
}
};
((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch);
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch);
Header.Tabs.Current.ValueChanged += tab =>
{
if (tab != DirectTab.Search)
{
currentQuery.Value = string.Empty;
Filter.Tabs.Current.Value = (DirectSortCriteria)Header.Tabs.Current.Value;
Scheduler.AddOnce(updateSearch);
}
};
currentQuery.ValueChanged += v =>
{
queryChangedDebounce?.Cancel();
if (string.IsNullOrEmpty(v))
Scheduler.AddOnce(updateSearch);
else
{
BeatmapSets = null;
ResultAmounts = null;
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
}
};
currentQuery.BindTo(Filter.Search.Current);
Filter.Tabs.Current.ValueChanged += sortCriteria =>
{
if (Header.Tabs.Current.Value != DirectTab.Search && sortCriteria != (DirectSortCriteria)Header.Tabs.Current.Value)
Header.Tabs.Current.Value = DirectTab.Search;
Scheduler.AddOnce(updateSearch);
};
updateResultCounts();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, APIAccess api, RulesetStore rulesets, BeatmapManager beatmaps)
{
this.api = api;
this.rulesets = rulesets;
this.beatmaps = beatmaps;
resultCountsContainer.Colour = colours.Yellow;
beatmaps.ItemAdded += setAdded;
}
private void setAdded(BeatmapSetInfo set) => Schedule(() =>
{
// if a new map was imported, we should remove it from search results (download completed etc.)
panels?.FirstOrDefault(p => p.SetInfo.OnlineBeatmapSetID == set.OnlineBeatmapSetID)?.FadeOut(400).Expire();
BeatmapSets = BeatmapSets?.Where(b => b.OnlineBeatmapSetID != set.OnlineBeatmapSetID);
});
private void updateResultCounts()
{
resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, Easing.OutQuint);
if (ResultAmounts == null) return;
resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " +
pluralize("Song", ResultAmounts.Songs) + ", " +
pluralize("Tag", ResultAmounts.Tags);
}
private string pluralize(string prefix, int value)
{
return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s");
}
private void recreatePanels(PanelDisplayStyle displayStyle)
{
if (panels != null)
{
panels.FadeOut(200);
panels.Expire();
panels = null;
if (playing != null)
{
playing.PreviewPlaying.Value = false;
playing = null;
}
}
if (BeatmapSets == null) return;
var newPanels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(panel_padding),
Margin = new MarginPadding { Top = 10 },
ChildrenEnumerable = BeatmapSets.Select<BeatmapSetInfo, DirectPanel>(b =>
{
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
return new DirectGridPanel(b)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
};
default:
return new DirectListPanel(b);
}
})
};
LoadComponentAsync(newPanels, p =>
{
if (panels != null) ScrollFlow.Remove(panels);
ScrollFlow.Add(panels = newPanels);
foreach (DirectPanel panel in p.Children)
panel.PreviewPlaying.ValueChanged += newValue =>
{
if (newValue)
{
if (playing != null && playing != panel)
playing.PreviewPlaying.Value = false;
playing = panel;
}
};
});
}
private SearchBeatmapSetsRequest getSetsRequest;
private readonly Bindable<string> currentQuery = new Bindable<string>();
private ScheduledDelegate queryChangedDebounce;
private void updateSearch()
{
queryChangedDebounce?.Cancel();
if (!IsLoaded) return;
BeatmapSets = null;
ResultAmounts = null;
getSetsRequest?.Cancel();
if (api == null) return;
if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return;
getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
((FilterControl)Filter).Ruleset.Value,
Filter.DisplayStyleControl.Dropdown.Current.Value,
Filter.Tabs.Current.Value); //todo: sort direction (?)
getSetsRequest.Success += response =>
{
Task.Run(() =>
{
var onlineIds = response.Select(r => r.OnlineBeatmapSetID).ToList();
var presentOnlineIds = beatmaps.QueryBeatmapSets(s => onlineIds.Contains(s.OnlineBeatmapSetID) && !s.DeletePending).Select(r => r.OnlineBeatmapSetID).ToList();
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList();
// may not need scheduling; loads async internally.
Schedule(() =>
{
BeatmapSets = sets;
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
});
});
};
api.Queue(getSetsRequest);
}
protected override void PopOut()
{
base.PopOut();
if (playing != null)
playing.PreviewPlaying.Value = false;
}
private int distinctCount(List<string> list) => list.Distinct().ToArray().Length;
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
beatmaps.ItemAdded -= setAdded;
}
public class ResultCounts
{
public readonly int Artists;
public readonly int Songs;
public readonly int Tags;
public ResultCounts(int artists, int songs, int tags)
{
Artists = artists;
Songs = songs;
Tags = tags;
}
}
}
}

View File

@ -1,43 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.Settings;
namespace osu.Game.Overlays.KeyBinding
{
public class GlobalKeyBindingsSection : SettingsSection
{
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => "Global";
public GlobalKeyBindingsSection(GlobalActionContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
Add(new InGameKeyBindingsSubsection(manager));
}
private class DefaultBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => string.Empty;
public DefaultBindingsSubsection(GlobalActionContainer manager)
: base(null)
{
Defaults = manager.GlobalKeyBindings;
}
}
private class InGameKeyBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => "In Game";
public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null)
{
Defaults = manager.InGameKeyBindings;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.Settings;
namespace osu.Game.Overlays.KeyBinding
{
public class GlobalKeyBindingsSection : SettingsSection
{
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => "Global";
public GlobalKeyBindingsSection(GlobalActionContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
Add(new InGameKeyBindingsSubsection(manager));
}
private class DefaultBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => string.Empty;
public DefaultBindingsSubsection(GlobalActionContainer manager)
: base(null)
{
Defaults = manager.GlobalKeyBindings;
}
}
private class InGameKeyBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => "In Game";
public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null)
{
Defaults = manager.InGameKeyBindings;
}
}
}
}

View File

@ -1,374 +1,374 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input;
using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Game.Overlays.KeyBinding
{
public class KeyBindingRow : Container, IFilterable
{
private readonly object action;
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;
private const float transition_time = 150;
private const float height = 20;
private const float padding = 5;
private bool matchingFilter;
public bool MatchingFilter
{
get { return matchingFilter; }
set
{
matchingFilter = value;
this.FadeTo(!matchingFilter ? 0 : 1);
}
}
private OsuSpriteText text;
private OsuSpriteText pressAKey;
private FillFlowContainer<KeyButton> buttons;
public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text);
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
{
this.action = action;
this.bindings = bindings;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = padding;
}
private KeyBindingStore store;
[BackgroundDependencyLoader]
private void load(OsuColour colours, KeyBindingStore store)
{
this.store = store;
EdgeEffect = new EdgeEffectParameters
{
Radius = 2,
Colour = colours.YellowDark.Opacity(0),
Type = EdgeEffectType.Shadow,
Hollow = true,
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
text = new OsuSpriteText
{
Text = action.GetDescription(),
Margin = new MarginPadding(padding),
},
buttons = new FillFlowContainer<KeyButton>
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
},
pressAKey = new OsuSpriteText
{
Text = "Press a key to change binding, DEL to delete, ESC to cancel.",
Y = height,
Margin = new MarginPadding(padding),
Alpha = 0,
Colour = colours.YellowDark
}
};
foreach (var b in bindings)
buttons.Add(new KeyButton(b));
}
public void RestoreDefaults()
{
int i = 0;
foreach (var d in Defaults)
{
var button = buttons[i++];
button.UpdateKeyCombination(d);
store.Update(button.KeyBinding);
}
}
protected override bool OnHover(InputState state)
{
FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
base.OnHoverLost(state);
}
public override bool AcceptsFocus => bindTarget == null;
private KeyButton bindTarget;
public bool AllowMainMouseButtons;
public IEnumerable<KeyCombination> Defaults;
private bool isModifier(Key k) => k < Key.F1;
protected override bool OnClick(InputState state) => true;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
if (!HasFocus || !bindTarget.IsHovered)
return base.OnMouseDown(state, args);
if (!AllowMainMouseButtons)
{
switch (args.Button)
{
case MouseButton.Left:
case MouseButton.Right:
return true;
}
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
return true;
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
// don't do anything until the last button is released.
if (!HasFocus || state.Mouse.Buttons.Any())
return base.OnMouseUp(state, args);
if (bindTarget.IsHovered)
finalise();
else
updateBindTarget();
return true;
}
protected override bool OnWheel(InputState state)
{
if (HasFocus)
{
if (bindTarget.IsHovered)
{
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
finalise();
return true;
}
}
return base.OnWheel(state);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!HasFocus)
return false;
switch (args.Key)
{
case Key.Escape:
finalise();
return true;
case Key.Delete:
bindTarget.UpdateKeyCombination(InputKey.None);
finalise();
return true;
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
if (!isModifier(args.Key)) finalise();
return true;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{
if (!HasFocus) return base.OnKeyUp(state, args);
finalise();
return true;
}
private void finalise()
{
if (bindTarget != null)
{
store.Update(bindTarget.KeyBinding);
bindTarget.IsBinding = false;
Schedule(() =>
{
// schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.)
bindTarget = null;
});
}
if (HasFocus)
GetContainingInputManager().ChangeFocus(null);
pressAKey.FadeOut(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
}
protected override void OnFocus(InputState state)
{
AutoSizeDuration = 500;
AutoSizeEasing = Easing.OutQuint;
pressAKey.FadeIn(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding();
updateBindTarget();
base.OnFocus(state);
}
protected override void OnFocusLost(InputState state)
{
finalise();
base.OnFocusLost(state);
}
private void updateBindTarget()
{
if (bindTarget != null) bindTarget.IsBinding = false;
bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault();
if (bindTarget != null) bindTarget.IsBinding = true;
}
private class KeyButton : Container
{
public readonly Framework.Input.Bindings.KeyBinding KeyBinding;
private readonly Box box;
public readonly OsuSpriteText Text;
private Color4 hoverColour;
private bool isBinding;
public bool IsBinding
{
get { return isBinding; }
set
{
if (value == isBinding) return;
isBinding = value;
updateHoverState();
}
}
public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding)
{
KeyBinding = keyBinding;
Margin = new MarginPadding(padding);
// todo: use this in a meaningful way
// var isDefault = keyBinding.Action is Enum;
Masking = true;
CornerRadius = padding;
Height = height;
AutoSizeAxes = Axes.X;
Children = new Drawable[]
{
new Container
{
AlwaysPresent = true,
Width = 80,
Height = height,
},
box = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
Text = new OsuSpriteText
{
Font = "Venera",
TextSize = 10,
Margin = new MarginPadding(5),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = keyBinding.KeyCombination.ReadableString(),
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoverColour = colours.YellowDark;
}
protected override bool OnHover(InputState state)
{
updateHoverState();
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
updateHoverState();
base.OnHoverLost(state);
}
private void updateHoverState()
{
if (isBinding)
{
box.FadeColour(Color4.White, transition_time, Easing.OutQuint);
Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint);
}
else
{
box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint);
Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint);
}
}
public void UpdateKeyCombination(KeyCombination newCombination)
{
KeyBinding.KeyCombination = newCombination;
Text.Text = KeyBinding.KeyCombination.ReadableString();
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input;
using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Game.Overlays.KeyBinding
{
public class KeyBindingRow : Container, IFilterable
{
private readonly object action;
private readonly IEnumerable<Framework.Input.Bindings.KeyBinding> bindings;
private const float transition_time = 150;
private const float height = 20;
private const float padding = 5;
private bool matchingFilter;
public bool MatchingFilter
{
get { return matchingFilter; }
set
{
matchingFilter = value;
this.FadeTo(!matchingFilter ? 0 : 1);
}
}
private OsuSpriteText text;
private OsuSpriteText pressAKey;
private FillFlowContainer<KeyButton> buttons;
public IEnumerable<string> FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text);
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
{
this.action = action;
this.bindings = bindings;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = padding;
}
private KeyBindingStore store;
[BackgroundDependencyLoader]
private void load(OsuColour colours, KeyBindingStore store)
{
this.store = store;
EdgeEffect = new EdgeEffectParameters
{
Radius = 2,
Colour = colours.YellowDark.Opacity(0),
Type = EdgeEffectType.Shadow,
Hollow = true,
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
text = new OsuSpriteText
{
Text = action.GetDescription(),
Margin = new MarginPadding(padding),
},
buttons = new FillFlowContainer<KeyButton>
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
},
pressAKey = new OsuSpriteText
{
Text = "Press a key to change binding, DEL to delete, ESC to cancel.",
Y = height,
Margin = new MarginPadding(padding),
Alpha = 0,
Colour = colours.YellowDark
}
};
foreach (var b in bindings)
buttons.Add(new KeyButton(b));
}
public void RestoreDefaults()
{
int i = 0;
foreach (var d in Defaults)
{
var button = buttons[i++];
button.UpdateKeyCombination(d);
store.Update(button.KeyBinding);
}
}
protected override bool OnHover(InputState state)
{
FadeEdgeEffectTo(1, transition_time, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
FadeEdgeEffectTo(0, transition_time, Easing.OutQuint);
base.OnHoverLost(state);
}
public override bool AcceptsFocus => bindTarget == null;
private KeyButton bindTarget;
public bool AllowMainMouseButtons;
public IEnumerable<KeyCombination> Defaults;
private bool isModifier(Key k) => k < Key.F1;
protected override bool OnClick(InputState state) => true;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
if (!HasFocus || !bindTarget.IsHovered)
return base.OnMouseDown(state, args);
if (!AllowMainMouseButtons)
{
switch (args.Button)
{
case MouseButton.Left:
case MouseButton.Right:
return true;
}
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
return true;
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
// don't do anything until the last button is released.
if (!HasFocus || state.Mouse.Buttons.Any())
return base.OnMouseUp(state, args);
if (bindTarget.IsHovered)
finalise();
else
updateBindTarget();
return true;
}
protected override bool OnWheel(InputState state)
{
if (HasFocus)
{
if (bindTarget.IsHovered)
{
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
finalise();
return true;
}
}
return base.OnWheel(state);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!HasFocus)
return false;
switch (args.Key)
{
case Key.Escape:
finalise();
return true;
case Key.Delete:
bindTarget.UpdateKeyCombination(InputKey.None);
finalise();
return true;
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
if (!isModifier(args.Key)) finalise();
return true;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{
if (!HasFocus) return base.OnKeyUp(state, args);
finalise();
return true;
}
private void finalise()
{
if (bindTarget != null)
{
store.Update(bindTarget.KeyBinding);
bindTarget.IsBinding = false;
Schedule(() =>
{
// schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.)
bindTarget = null;
});
}
if (HasFocus)
GetContainingInputManager().ChangeFocus(null);
pressAKey.FadeOut(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
}
protected override void OnFocus(InputState state)
{
AutoSizeDuration = 500;
AutoSizeEasing = Easing.OutQuint;
pressAKey.FadeIn(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding();
updateBindTarget();
base.OnFocus(state);
}
protected override void OnFocusLost(InputState state)
{
finalise();
base.OnFocusLost(state);
}
private void updateBindTarget()
{
if (bindTarget != null) bindTarget.IsBinding = false;
bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault();
if (bindTarget != null) bindTarget.IsBinding = true;
}
private class KeyButton : Container
{
public readonly Framework.Input.Bindings.KeyBinding KeyBinding;
private readonly Box box;
public readonly OsuSpriteText Text;
private Color4 hoverColour;
private bool isBinding;
public bool IsBinding
{
get { return isBinding; }
set
{
if (value == isBinding) return;
isBinding = value;
updateHoverState();
}
}
public KeyButton(Framework.Input.Bindings.KeyBinding keyBinding)
{
KeyBinding = keyBinding;
Margin = new MarginPadding(padding);
// todo: use this in a meaningful way
// var isDefault = keyBinding.Action is Enum;
Masking = true;
CornerRadius = padding;
Height = height;
AutoSizeAxes = Axes.X;
Children = new Drawable[]
{
new Container
{
AlwaysPresent = true,
Width = 80,
Height = height,
},
box = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
Text = new OsuSpriteText
{
Font = "Venera",
TextSize = 10,
Margin = new MarginPadding(5),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = keyBinding.KeyCombination.ReadableString(),
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoverColour = colours.YellowDark;
}
protected override bool OnHover(InputState state)
{
updateHoverState();
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
updateHoverState();
base.OnHoverLost(state);
}
private void updateHoverState()
{
if (isBinding)
{
box.FadeColour(Color4.White, transition_time, Easing.OutQuint);
Text.FadeColour(Color4.Black, transition_time, Easing.OutQuint);
}
else
{
box.FadeColour(IsHovered ? hoverColour : Color4.Black, transition_time, Easing.OutQuint);
Text.FadeColour(IsHovered ? Color4.Black : Color4.White, transition_time, Easing.OutQuint);
}
}
public void UpdateKeyCombination(KeyCombination newCombination)
{
KeyBinding.KeyCombination = newCombination;
Text.Text = KeyBinding.KeyCombination.ReadableString();
}
}
}
}

View File

@ -1,75 +1,75 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using OpenTK;
using osu.Game.Graphics;
namespace osu.Game.Overlays.KeyBinding
{
public abstract class KeyBindingsSubsection : SettingsSubsection
{
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
protected RulesetInfo Ruleset;
private readonly int? variant;
protected KeyBindingsSubsection(int? variant)
{
this.variant = variant;
FlowContent.Spacing = new Vector2(0, 1);
FlowContent.Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS };
}
[BackgroundDependencyLoader]
private void load(KeyBindingStore store)
{
var bindings = store.Query(Ruleset?.ID, variant);
foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
{
int intKey = (int)defaultGroup.Key;
// one row per valid action.
Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey)))
{
AllowMainMouseButtons = Ruleset != null,
Defaults = defaultGroup.Select(d => d.KeyCombination)
});
}
Add(new ResetButton
{
Action = () => Children.OfType<KeyBindingRow>().ForEach(k => k.RestoreDefaults())
});
}
}
public class ResetButton : TriangleButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Text = "Reset";
RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Top = 5 };
Height = 20;
Content.CornerRadius = 5;
BackgroundColour = colours.PinkDark;
Triangles.ColourDark = colours.PinkDarker;
Triangles.ColourLight = colours.Pink;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
using OpenTK;
using osu.Game.Graphics;
namespace osu.Game.Overlays.KeyBinding
{
public abstract class KeyBindingsSubsection : SettingsSubsection
{
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
protected RulesetInfo Ruleset;
private readonly int? variant;
protected KeyBindingsSubsection(int? variant)
{
this.variant = variant;
FlowContent.Spacing = new Vector2(0, 1);
FlowContent.Padding = new MarginPadding { Left = SettingsOverlay.CONTENT_MARGINS, Right = SettingsOverlay.CONTENT_MARGINS };
}
[BackgroundDependencyLoader]
private void load(KeyBindingStore store)
{
var bindings = store.Query(Ruleset?.ID, variant);
foreach (var defaultGroup in Defaults.GroupBy(d => d.Action))
{
int intKey = (int)defaultGroup.Key;
// one row per valid action.
Add(new KeyBindingRow(defaultGroup.Key, bindings.Where(b => ((int)b.Action).Equals(intKey)))
{
AllowMainMouseButtons = Ruleset != null,
Defaults = defaultGroup.Select(d => d.KeyCombination)
});
}
Add(new ResetButton
{
Action = () => Children.OfType<KeyBindingRow>().ForEach(k => k.RestoreDefaults())
});
}
}
public class ResetButton : TriangleButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Text = "Reset";
RelativeSizeAxes = Axes.X;
Margin = new MarginPadding { Top = 5 };
Height = 20;
Content.CornerRadius = 5;
BackgroundColour = colours.PinkDark;
Triangles.ColourDark = colours.PinkDarker;
Triangles.ColourLight = colours.Pink;
}
}
}

View File

@ -1,27 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
public class RulesetBindingsSection : SettingsSection
{
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => ruleset.Name;
private readonly RulesetInfo ruleset;
public RulesetBindingsSection(RulesetInfo ruleset)
{
this.ruleset = ruleset;
var r = ruleset.CreateInstance();
foreach (var variant in r.AvailableVariants)
Add(new VariantBindingsSubsection(ruleset, variant));
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
public class RulesetBindingsSection : SettingsSection
{
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => ruleset.Name;
private readonly RulesetInfo ruleset;
public RulesetBindingsSection(RulesetInfo ruleset)
{
this.ruleset = ruleset;
var r = ruleset.CreateInstance();
foreach (var variant in r.AvailableVariants)
Add(new VariantBindingsSubsection(ruleset, variant));
}
}
}

View File

@ -1,24 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
public class VariantBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => variantName;
private readonly string variantName;
public VariantBindingsSubsection(RulesetInfo ruleset, int variant)
: base(variant)
{
Ruleset = ruleset;
var rulesetInstance = ruleset.CreateInstance();
variantName = rulesetInstance.GetVariantName(variant);
Defaults = rulesetInstance.GetDefaultKeyBindings(variant);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets;
namespace osu.Game.Overlays.KeyBinding
{
public class VariantBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => variantName;
private readonly string variantName;
public VariantBindingsSubsection(RulesetInfo ruleset, int variant)
: base(variant)
{
Ruleset = ruleset;
var rulesetInstance = ruleset.CreateInstance();
variantName = rulesetInstance.GetVariantName(variant);
Defaults = rulesetInstance.GetDefaultKeyBindings(variant);
}
}
}

View File

@ -1,31 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.KeyBinding;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
namespace osu.Game.Overlays
{
public class KeyBindingOverlay : SettingsOverlay
{
protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!");
[BackgroundDependencyLoader(permitNulls: true)]
private void load(RulesetStore rulesets, GlobalActionContainer global)
{
AddSection(new GlobalKeyBindingsSection(global));
foreach (var ruleset in rulesets.AvailableRulesets)
AddSection(new RulesetBindingsSection(ruleset));
}
public KeyBindingOverlay()
: base(false)
{
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.KeyBinding;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets;
namespace osu.Game.Overlays
{
public class KeyBindingOverlay : SettingsOverlay
{
protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!");
[BackgroundDependencyLoader(permitNulls: true)]
private void load(RulesetStore rulesets, GlobalActionContainer global)
{
AddSection(new GlobalKeyBindingsSection(global));
foreach (var ruleset in rulesets.AvailableRulesets)
AddSection(new RulesetBindingsSection(ruleset));
}
public KeyBindingOverlay()
: base(false)
{
}
}
}

View File

@ -1,92 +1,92 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings.Sections.General;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
namespace osu.Game.Overlays
{
public class LoginOverlay : OsuFocusedOverlayContainer
{
private LoginSettings settingsSection;
private const float transition_time = 400;
public LoginOverlay()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new OsuContextMenuContainer
{
Width = 360,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Masking = true,
AutoSizeDuration = transition_time,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
settingsSection = new LoginSettings
{
Padding = new MarginPadding(10),
RequestHide = Hide,
},
new Box
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 3,
Colour = colours.Yellow,
Alpha = 1,
},
}
}
}
}
};
}
protected override void PopIn()
{
base.PopIn();
settingsSection.Bounding = true;
this.FadeIn(transition_time, Easing.OutQuint);
GetContainingInputManager().ChangeFocus(settingsSection);
}
protected override void PopOut()
{
base.PopOut();
settingsSection.Bounding = false;
this.FadeOut(transition_time);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings.Sections.General;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
namespace osu.Game.Overlays
{
public class LoginOverlay : OsuFocusedOverlayContainer
{
private LoginSettings settingsSection;
private const float transition_time = 400;
public LoginOverlay()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new OsuContextMenuContainer
{
Width = 360,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Masking = true,
AutoSizeDuration = transition_time,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
settingsSection = new LoginSettings
{
Padding = new MarginPadding(10),
RequestHide = Hide,
},
new Box
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = 3,
Colour = colours.Yellow,
Alpha = 1,
},
}
}
}
}
};
}
protected override void PopIn()
{
base.PopIn();
settingsSection.Bounding = true;
this.FadeIn(transition_time, Easing.OutQuint);
GetContainingInputManager().ChangeFocus(settingsSection);
}
protected override void PopOut()
{
base.PopOut();
settingsSection.Bounding = false;
this.FadeOut(transition_time);
}
}
}

View File

@ -1,151 +1,151 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Screens.Ranking;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
public class MainSettings : SettingsOverlay
{
private readonly KeyBindingOverlay keyBindingOverlay;
private BackButton backButton;
protected override IEnumerable<SettingsSection> CreateSections() => new SettingsSection[]
{
new GeneralSection(),
new GraphicsSection(),
new GameplaySection(),
new AudioSection(),
new SkinSection(),
new InputSection(keyBindingOverlay),
new OnlineSection(),
new MaintenanceSection(),
new DebugSection(),
};
protected override Drawable CreateHeader() => new SettingsHeader("settings", "Change the way osu! behaves");
protected override Drawable CreateFooter() => new SettingsFooter();
public MainSettings()
: base(true)
{
keyBindingOverlay = new KeyBindingOverlay
{
Depth = 1,
Anchor = Anchor.TopRight,
};
keyBindingOverlay.StateChanged += keyBindingOverlay_StateChanged;
}
public override bool AcceptsFocus => keyBindingOverlay.State != Visibility.Visible;
private const float hidden_width = 120;
private void keyBindingOverlay_StateChanged(Visibility visibility)
{
switch (visibility)
{
case Visibility.Visible:
Background.FadeTo(0.9f, 300, Easing.OutQuint);
Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint);
SectionsContainer.FadeOut(300, Easing.OutQuint);
ContentContainer.MoveToX(hidden_width - WIDTH, 500, Easing.OutQuint);
backButton.Delay(100).FadeIn(100);
break;
case Visibility.Hidden:
Background.FadeTo(0.6f, 500, Easing.OutQuint);
Sidebar?.FadeColour(Color4.White, 300, Easing.OutQuint);
SectionsContainer.FadeIn(500, Easing.OutQuint);
ContentContainer.MoveToX(0, 500, Easing.OutQuint);
backButton.FadeOut(100);
break;
}
}
protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? hidden_width - WIDTH : base.ExpandedPosition;
[BackgroundDependencyLoader]
private void load()
{
ContentContainer.Add(keyBindingOverlay);
ContentContainer.Add(backButton = new BackButton
{
Alpha = 0,
Width = hidden_width,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Action = () => keyBindingOverlay.Hide()
});
}
private class BackButton : OsuClickableContainer
{
private AspectContainer aspect;
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
aspect = new AspectContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Y = -15,
Size = new Vector2(15),
Shadow = true,
Icon = FontAwesome.fa_chevron_left
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Y = 15,
TextSize = 12,
Font = @"Exo2.0-Bold",
Text = @"back",
},
}
}
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
aspect.ScaleTo(0.75f, 2000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
aspect.ScaleTo(1, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Overlays.Settings.Sections;
using osu.Game.Screens.Ranking;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
public class MainSettings : SettingsOverlay
{
private readonly KeyBindingOverlay keyBindingOverlay;
private BackButton backButton;
protected override IEnumerable<SettingsSection> CreateSections() => new SettingsSection[]
{
new GeneralSection(),
new GraphicsSection(),
new GameplaySection(),
new AudioSection(),
new SkinSection(),
new InputSection(keyBindingOverlay),
new OnlineSection(),
new MaintenanceSection(),
new DebugSection(),
};
protected override Drawable CreateHeader() => new SettingsHeader("settings", "Change the way osu! behaves");
protected override Drawable CreateFooter() => new SettingsFooter();
public MainSettings()
: base(true)
{
keyBindingOverlay = new KeyBindingOverlay
{
Depth = 1,
Anchor = Anchor.TopRight,
};
keyBindingOverlay.StateChanged += keyBindingOverlay_StateChanged;
}
public override bool AcceptsFocus => keyBindingOverlay.State != Visibility.Visible;
private const float hidden_width = 120;
private void keyBindingOverlay_StateChanged(Visibility visibility)
{
switch (visibility)
{
case Visibility.Visible:
Background.FadeTo(0.9f, 300, Easing.OutQuint);
Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint);
SectionsContainer.FadeOut(300, Easing.OutQuint);
ContentContainer.MoveToX(hidden_width - WIDTH, 500, Easing.OutQuint);
backButton.Delay(100).FadeIn(100);
break;
case Visibility.Hidden:
Background.FadeTo(0.6f, 500, Easing.OutQuint);
Sidebar?.FadeColour(Color4.White, 300, Easing.OutQuint);
SectionsContainer.FadeIn(500, Easing.OutQuint);
ContentContainer.MoveToX(0, 500, Easing.OutQuint);
backButton.FadeOut(100);
break;
}
}
protected override float ExpandedPosition => keyBindingOverlay.State == Visibility.Visible ? hidden_width - WIDTH : base.ExpandedPosition;
[BackgroundDependencyLoader]
private void load()
{
ContentContainer.Add(keyBindingOverlay);
ContentContainer.Add(backButton = new BackButton
{
Alpha = 0,
Width = hidden_width,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Action = () => keyBindingOverlay.Hide()
});
}
private class BackButton : OsuClickableContainer
{
private AspectContainer aspect;
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
aspect = new AspectContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Y = -15,
Size = new Vector2(15),
Shadow = true,
Icon = FontAwesome.fa_chevron_left
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Y = 15,
TextSize = 12,
Font = @"Exo2.0-Bold",
Text = @"back",
},
}
}
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
aspect.ScaleTo(0.75f, 2000, Easing.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
aspect.ScaleTo(1, 1000, Easing.OutElastic);
return base.OnMouseUp(state, args);
}
}
}
}

View File

@ -1,309 +1,309 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Sprites;
using osu.Game.Users;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Overlays.MedalSplash;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using OpenTK.Input;
using System.Linq;
using osu.Framework.Graphics.Shapes;
using System;
using osu.Framework.MathUtils;
namespace osu.Game.Overlays
{
public class MedalOverlay : FocusedOverlayContainer
{
public const float DISC_SIZE = 400;
private const float border_width = 5;
private readonly Medal medal;
private readonly Box background;
private readonly Container backgroundStrip, particleContainer;
private readonly BackgroundStrip leftStrip, rightStrip;
private readonly CircularContainer disc;
private readonly Sprite innerSpin, outerSpin;
private DrawableMedal drawableMedal;
private SampleChannel getSample;
public MedalOverlay(Medal medal)
{
this.medal = medal;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(60),
},
outerSpin = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(DISC_SIZE + 500),
Alpha = 0f,
},
backgroundStrip = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = border_width,
Alpha = 0f,
Children = new[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
Width = 0.5f,
Padding = new MarginPadding { Right = DISC_SIZE / 2 },
Children = new[]
{
leftStrip = new BackgroundStrip(0f, 1f)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
Width = 0.5f,
Padding = new MarginPadding { Left = DISC_SIZE / 2 },
Children = new[]
{
rightStrip = new BackgroundStrip(1f, 0f),
},
},
},
},
particleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
},
disc = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0f,
Masking = true,
AlwaysPresent = true,
BorderColour = Color4.White,
BorderThickness = border_width,
Size = new Vector2(DISC_SIZE),
Scale = new Vector2(0.8f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"05262f"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 2,
ColourDark = OsuColour.FromHex(@"04222b"),
ColourLight = OsuColour.FromHex(@"052933"),
},
innerSpin = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1.05f),
Alpha = 0.25f,
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures, AudioManager audio)
{
getSample = audio.Sample.Get(@"MedalSplash/medal-get");
innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin");
disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = colours.Blue.Opacity(0.5f),
Radius = 50,
};
disc.Add(drawableMedal = new DrawableMedal(medal)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
});
}
protected override void LoadComplete()
{
base.LoadComplete();
Show();
}
protected override void Update()
{
base.Update();
particleContainer.Add(new MedalParticle(RNG.Next(0, 359)));
}
protected override bool OnClick(InputState state)
{
dismiss();
return true;
}
protected override void OnFocusLost(InputState state)
{
if (state.Keyboard.Keys.Contains(Key.Escape)) dismiss();
}
private const double initial_duration = 400;
private const double step_duration = 900;
protected override void PopIn()
{
base.PopIn();
this.FadeIn(200);
background.FlashColour(Color4.White.Opacity(0.25f), 400);
getSample.Play();
innerSpin.Spin(20000, RotationDirection.Clockwise);
outerSpin.Spin(40000, RotationDirection.Clockwise);
using (BeginDelayedSequence(200, true))
{
disc.FadeIn(initial_duration)
.ScaleTo(1f, initial_duration * 2, Easing.OutElastic);
particleContainer.FadeIn(initial_duration);
outerSpin.FadeTo(0.1f, initial_duration * 2);
using (BeginDelayedSequence(initial_duration + 200, true))
{
backgroundStrip.FadeIn(step_duration);
leftStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint);
rightStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint);
this.Animate().Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.Icon;
})
.Delay(step_duration).Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.MedalUnlocked;
})
.Delay(step_duration).Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.Full;
});
}
}
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(200);
}
private void dismiss()
{
if (drawableMedal.State != DisplayState.Full)
{
// if we haven't yet, play out the animation fully
drawableMedal.State = DisplayState.Full;
FinishTransforms(true);
return;
}
Hide();
Expire();
}
private class BackgroundStrip : Container
{
public BackgroundStrip(float start, float end)
{
RelativeSizeAxes = Axes.Both;
Width = 0f;
Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(start), Color4.White.Opacity(end));
Masking = true;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
}
};
}
}
private class MedalParticle : CircularContainer
{
private readonly float direction;
private Vector2 positionForOffset(float offset) => new Vector2((float)(offset * Math.Sin(direction)), (float)(offset * Math.Cos(direction)));
public MedalParticle(float direction)
{
this.direction = direction;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Position = positionForOffset(DISC_SIZE / 2);
Masking = true;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = colours.Blue.Opacity(0.5f),
Radius = 5,
};
this.MoveTo(positionForOffset(DISC_SIZE / 2 + 200), 500);
this.FadeOut(500);
Expire();
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Sprites;
using osu.Game.Users;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Overlays.MedalSplash;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using OpenTK.Input;
using System.Linq;
using osu.Framework.Graphics.Shapes;
using System;
using osu.Framework.MathUtils;
namespace osu.Game.Overlays
{
public class MedalOverlay : FocusedOverlayContainer
{
public const float DISC_SIZE = 400;
private const float border_width = 5;
private readonly Medal medal;
private readonly Box background;
private readonly Container backgroundStrip, particleContainer;
private readonly BackgroundStrip leftStrip, rightStrip;
private readonly CircularContainer disc;
private readonly Sprite innerSpin, outerSpin;
private DrawableMedal drawableMedal;
private SampleChannel getSample;
public MedalOverlay(Medal medal)
{
this.medal = medal;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(60),
},
outerSpin = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(DISC_SIZE + 500),
Alpha = 0f,
},
backgroundStrip = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = border_width,
Alpha = 0f,
Children = new[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
Width = 0.5f,
Padding = new MarginPadding { Right = DISC_SIZE / 2 },
Children = new[]
{
leftStrip = new BackgroundStrip(0f, 1f)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
Width = 0.5f,
Padding = new MarginPadding { Left = DISC_SIZE / 2 },
Children = new[]
{
rightStrip = new BackgroundStrip(1f, 0f),
},
},
},
},
particleContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Alpha = 0f,
},
disc = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0f,
Masking = true,
AlwaysPresent = true,
BorderColour = Color4.White,
BorderThickness = border_width,
Size = new Vector2(DISC_SIZE),
Scale = new Vector2(0.8f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"05262f"),
},
new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 2,
ColourDark = OsuColour.FromHex(@"04222b"),
ColourLight = OsuColour.FromHex(@"052933"),
},
innerSpin = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1.05f),
Alpha = 0.25f,
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures, AudioManager audio)
{
getSample = audio.Sample.Get(@"MedalSplash/medal-get");
innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin");
disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = colours.Blue.Opacity(0.5f),
Radius = 50,
};
disc.Add(drawableMedal = new DrawableMedal(medal)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
});
}
protected override void LoadComplete()
{
base.LoadComplete();
Show();
}
protected override void Update()
{
base.Update();
particleContainer.Add(new MedalParticle(RNG.Next(0, 359)));
}
protected override bool OnClick(InputState state)
{
dismiss();
return true;
}
protected override void OnFocusLost(InputState state)
{
if (state.Keyboard.Keys.Contains(Key.Escape)) dismiss();
}
private const double initial_duration = 400;
private const double step_duration = 900;
protected override void PopIn()
{
base.PopIn();
this.FadeIn(200);
background.FlashColour(Color4.White.Opacity(0.25f), 400);
getSample.Play();
innerSpin.Spin(20000, RotationDirection.Clockwise);
outerSpin.Spin(40000, RotationDirection.Clockwise);
using (BeginDelayedSequence(200, true))
{
disc.FadeIn(initial_duration)
.ScaleTo(1f, initial_duration * 2, Easing.OutElastic);
particleContainer.FadeIn(initial_duration);
outerSpin.FadeTo(0.1f, initial_duration * 2);
using (BeginDelayedSequence(initial_duration + 200, true))
{
backgroundStrip.FadeIn(step_duration);
leftStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint);
rightStrip.ResizeWidthTo(1f, step_duration, Easing.OutQuint);
this.Animate().Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.Icon;
})
.Delay(step_duration).Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.MedalUnlocked;
})
.Delay(step_duration).Schedule(() =>
{
if (drawableMedal.State != DisplayState.Full)
drawableMedal.State = DisplayState.Full;
});
}
}
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(200);
}
private void dismiss()
{
if (drawableMedal.State != DisplayState.Full)
{
// if we haven't yet, play out the animation fully
drawableMedal.State = DisplayState.Full;
FinishTransforms(true);
return;
}
Hide();
Expire();
}
private class BackgroundStrip : Container
{
public BackgroundStrip(float start, float end)
{
RelativeSizeAxes = Axes.Both;
Width = 0f;
Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(start), Color4.White.Opacity(end));
Masking = true;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
}
};
}
}
private class MedalParticle : CircularContainer
{
private readonly float direction;
private Vector2 positionForOffset(float offset) => new Vector2((float)(offset * Math.Sin(direction)), (float)(offset * Math.Cos(direction)));
public MedalParticle(float direction)
{
this.direction = direction;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Position = positionForOffset(DISC_SIZE / 2);
Masking = true;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = colours.Blue.Opacity(0.5f),
Radius = 5,
};
this.MoveTo(positionForOffset(DISC_SIZE / 2 + 200), 500);
this.FadeOut(500);
Expire();
}
}
}
}

View File

@ -1,196 +1,196 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.MedalSplash
{
public class DrawableMedal : Container, IStateful<DisplayState>
{
private const float scale_when_unlocked = 0.76f;
private const float scale_when_full = 0.6f;
public event Action<DisplayState> StateChanged;
private readonly Medal medal;
private readonly Container medalContainer;
private readonly Sprite medalSprite, medalGlow;
private readonly OsuSpriteText unlocked, name;
private readonly TextFlowContainer description;
private DisplayState state;
public DrawableMedal(Medal medal)
{
this.medal = medal;
Position = new Vector2(0f, MedalOverlay.DISC_SIZE / 2);
FillFlowContainer infoFlow;
Children = new Drawable[]
{
medalContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Alpha = 0f,
Children = new Drawable[]
{
medalSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.81f),
},
medalGlow = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
},
},
unlocked = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Medal Unlocked".ToUpper(),
TextSize = 24,
Font = @"Exo2.0-Light",
Alpha = 0f,
Scale = new Vector2(1f / scale_when_unlocked),
},
infoFlow = new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.6f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 5f),
Children = new Drawable[]
{
name = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = medal.Name,
TextSize = 20,
Font = @"Exo2.0-Bold",
Alpha = 0f,
Scale = new Vector2(1f / scale_when_full),
},
description = new OsuTextFlowContainer
{
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0f,
Scale = new Vector2(1f / scale_when_full),
},
},
},
};
description.AddText(medal.Description, s =>
{
s.Anchor = Anchor.TopCentre;
s.Origin = Anchor.TopCentre;
s.TextSize = 16;
});
medalContainer.OnLoadComplete = d =>
{
unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10);
infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90);
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures)
{
medalSprite.Texture = textures.Get(medal.ImageUrl);
medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow");
description.Colour = colours.BlueLight;
}
protected override void LoadComplete()
{
base.LoadComplete();
updateState();
}
public DisplayState State
{
get { return state; }
set
{
if (state == value) return;
state = value;
updateState();
StateChanged?.Invoke(State);
}
}
private void updateState()
{
if (!IsLoaded) return;
const double duration = 900;
switch (state)
{
case DisplayState.None:
medalContainer.ScaleTo(0);
break;
case DisplayState.Icon:
medalContainer
.FadeIn(duration)
.ScaleTo(1, duration, Easing.OutElastic);
break;
case DisplayState.MedalUnlocked:
medalContainer
.FadeTo(1)
.ScaleTo(1);
this.ScaleTo(scale_when_unlocked, duration, Easing.OutExpo);
this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 30, duration, Easing.OutExpo);
unlocked.FadeInFromZero(duration);
break;
case DisplayState.Full:
medalContainer
.FadeTo(1)
.ScaleTo(1);
this.ScaleTo(scale_when_full, duration, Easing.OutExpo);
this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, Easing.OutExpo);
unlocked.Show();
name.FadeInFromZero(duration + 100);
description.FadeInFromZero(duration * 2);
break;
}
}
}
public enum DisplayState
{
None,
Icon,
MedalUnlocked,
Full,
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.MedalSplash
{
public class DrawableMedal : Container, IStateful<DisplayState>
{
private const float scale_when_unlocked = 0.76f;
private const float scale_when_full = 0.6f;
public event Action<DisplayState> StateChanged;
private readonly Medal medal;
private readonly Container medalContainer;
private readonly Sprite medalSprite, medalGlow;
private readonly OsuSpriteText unlocked, name;
private readonly TextFlowContainer description;
private DisplayState state;
public DrawableMedal(Medal medal)
{
this.medal = medal;
Position = new Vector2(0f, MedalOverlay.DISC_SIZE / 2);
FillFlowContainer infoFlow;
Children = new Drawable[]
{
medalContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Alpha = 0f,
Children = new Drawable[]
{
medalSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(0.81f),
},
medalGlow = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
},
},
unlocked = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Medal Unlocked".ToUpper(),
TextSize = 24,
Font = @"Exo2.0-Light",
Alpha = 0f,
Scale = new Vector2(1f / scale_when_unlocked),
},
infoFlow = new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.6f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 5f),
Children = new Drawable[]
{
name = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = medal.Name,
TextSize = 20,
Font = @"Exo2.0-Bold",
Alpha = 0f,
Scale = new Vector2(1f / scale_when_full),
},
description = new OsuTextFlowContainer
{
TextAnchor = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0f,
Scale = new Vector2(1f / scale_when_full),
},
},
},
};
description.AddText(medal.Description, s =>
{
s.Anchor = Anchor.TopCentre;
s.Origin = Anchor.TopCentre;
s.TextSize = 16;
});
medalContainer.OnLoadComplete = d =>
{
unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10);
infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90);
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures)
{
medalSprite.Texture = textures.Get(medal.ImageUrl);
medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow");
description.Colour = colours.BlueLight;
}
protected override void LoadComplete()
{
base.LoadComplete();
updateState();
}
public DisplayState State
{
get { return state; }
set
{
if (state == value) return;
state = value;
updateState();
StateChanged?.Invoke(State);
}
}
private void updateState()
{
if (!IsLoaded) return;
const double duration = 900;
switch (state)
{
case DisplayState.None:
medalContainer.ScaleTo(0);
break;
case DisplayState.Icon:
medalContainer
.FadeIn(duration)
.ScaleTo(1, duration, Easing.OutElastic);
break;
case DisplayState.MedalUnlocked:
medalContainer
.FadeTo(1)
.ScaleTo(1);
this.ScaleTo(scale_when_unlocked, duration, Easing.OutExpo);
this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 30, duration, Easing.OutExpo);
unlocked.FadeInFromZero(duration);
break;
case DisplayState.Full:
medalContainer
.FadeTo(1)
.ScaleTo(1);
this.ScaleTo(scale_when_full, duration, Easing.OutExpo);
this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, Easing.OutExpo);
unlocked.Show();
name.FadeInFromZero(duration + 100);
description.FadeInFromZero(duration * 2);
break;
}
}
}
public enum DisplayState
{
None,
Icon,
MedalUnlocked,
Full,
}
}

View File

@ -1,27 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class DifficultyIncreaseSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L };
public override ModType ModType => ModType.DifficultyIncrease;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.YellowLight;
}
public DifficultyIncreaseSection()
{
Header = @"Difficulty Increase";
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class DifficultyIncreaseSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L };
public override ModType ModType => ModType.DifficultyIncrease;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.YellowLight;
}
public DifficultyIncreaseSection()
{
Header = @"Difficulty Increase";
}
}
}

View File

@ -1,27 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class DifficultyReductionSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P };
public override ModType ModType => ModType.DifficultyReduction;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.GreenLight;
}
public DifficultyReductionSection()
{
Header = @"Difficulty Reduction";
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class DifficultyReductionSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P };
public override ModType ModType => ModType.DifficultyReduction;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.GreenLight;
}
public DifficultyReductionSection()
{
Header = @"Difficulty Reduction";
}
}
}

View File

@ -1,276 +1,276 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System;
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
{
/// <summary>
/// Represents a clickable button which can cycle through one of more mods.
/// </summary>
public class ModButton : ModButtonEmpty, IHasTooltip
{
private ModIcon foregroundIcon;
private ModIcon backgroundIcon;
private readonly SpriteText text;
private readonly Container<ModIcon> iconsContainer;
/// <summary>
/// Fired when the selection changes.
/// </summary>
public Action<Mod> SelectionChanged;
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
private const Easing mod_switch_easing = Easing.InOutSine;
private const double mod_switch_duration = 120;
// A selected index of -1 means not selected.
private int selectedIndex = -1;
/// <summary>
/// Change the selected mod index of this button.
/// </summary>
/// <param name="newIndex">The new index.</param>
/// <returns>Whether the selection changed.</returns>
private bool changeSelectedIndex(int newIndex)
{
if (newIndex == selectedIndex) return false;
int direction = newIndex < selectedIndex ? -1 : 1;
bool beforeSelected = Selected;
Mod modBefore = SelectedMod ?? Mods[0];
if (newIndex >= Mods.Length)
newIndex = -1;
else if (newIndex < -1)
newIndex = Mods.Length - 1;
if (newIndex >= 0 && !Mods[newIndex].HasImplementation)
return false;
selectedIndex = newIndex;
Mod modAfter = SelectedMod ?? Mods[0];
if (beforeSelected != Selected)
{
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic);
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic);
}
if (modBefore != modAfter)
{
const float rotate_angle = 16;
foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.Icon = modAfter.Icon;
using (BeginDelayedSequence(mod_switch_duration, true))
{
foregroundIcon
.RotateTo(-rotate_angle * direction)
.RotateTo(0f, mod_switch_duration, mod_switch_easing);
backgroundIcon
.RotateTo(rotate_angle * direction)
.RotateTo(0f, mod_switch_duration, mod_switch_easing);
Schedule(() => displayMod(modAfter));
}
}
foregroundIcon.Highlighted = Selected;
SelectionChanged?.Invoke(SelectedMod);
return true;
}
public bool Selected => selectedIndex != -1;
private Color4 selectedColour;
public Color4 SelectedColour
{
get { return selectedColour; }
set
{
if (value == selectedColour) return;
selectedColour = value;
if (Selected) foregroundIcon.Colour = value;
}
}
private Mod mod;
public Mod Mod
{
get { return mod; }
set
{
mod = value;
if (mod == null)
{
Mods = Array.Empty<Mod>();
Alpha = 0;
}
else
{
Mods = (mod as MultiMod)?.Mods ?? new[] { mod };
Alpha = 1;
}
createIcons();
if (Mods.Length > 0)
{
displayMod(Mods[0]);
}
}
}
public Mod[] Mods { get; private set; }
public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
switch (args.Button)
{
case MouseButton.Left:
SelectNext(1);
break;
case MouseButton.Right:
SelectNext(-1);
break;
}
return true;
}
/// <summary>
/// Select the next available mod in a specified direction.
/// </summary>
/// <param name="direction">1 for forwards, -1 for backwards.</param>
public void SelectNext(int direction)
{
int start = selectedIndex + direction;
// wrap around if we are at an extremity.
if (start >= Mods.Length)
start = -1;
else if (start < -1)
start = Mods.Length - 1;
for (int i = start; i < Mods.Length && i >= 0; i += direction)
if (SelectAt(i)) return;
Deselect();
}
public bool SelectAt(int index)
{
if (!Mods[index].HasImplementation) return false;
changeSelectedIndex(index);
return true;
}
public void Deselect() => changeSelectedIndex(-1);
private void displayMod(Mod mod)
{
if (backgroundIcon != null)
backgroundIcon.Icon = foregroundIcon.Icon;
foregroundIcon.Icon = mod.Icon;
text.Text = mod.Name;
Colour = mod.HasImplementation ? Color4.White : Color4.Gray;
}
private void createIcons()
{
iconsContainer.Clear();
if (Mods.Length > 1)
{
iconsContainer.AddRange(new[]
{
backgroundIcon = new PassThroughTooltipModIcon(Mods[1])
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Position = new Vector2(1.5f),
},
foregroundIcon = new PassThroughTooltipModIcon(Mods[0])
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Position = new Vector2(-1.5f),
},
});
}
else
{
iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
});
}
}
public ModButton(Mod mod)
{
Children = new Drawable[]
{
new Container
{
Size = new Vector2(77f, 80f),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
iconsContainer = new Container<ModIcon>
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
}
}
},
text = new OsuSpriteText
{
Y = 75,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 18,
},
new HoverClickSounds()
};
Mod = mod;
}
private class PassThroughTooltipModIcon : ModIcon
{
public override string TooltipText => null;
public PassThroughTooltipModIcon(Mod mod)
: base(mod)
{
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System;
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
{
/// <summary>
/// Represents a clickable button which can cycle through one of more mods.
/// </summary>
public class ModButton : ModButtonEmpty, IHasTooltip
{
private ModIcon foregroundIcon;
private ModIcon backgroundIcon;
private readonly SpriteText text;
private readonly Container<ModIcon> iconsContainer;
/// <summary>
/// Fired when the selection changes.
/// </summary>
public Action<Mod> SelectionChanged;
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
private const Easing mod_switch_easing = Easing.InOutSine;
private const double mod_switch_duration = 120;
// A selected index of -1 means not selected.
private int selectedIndex = -1;
/// <summary>
/// Change the selected mod index of this button.
/// </summary>
/// <param name="newIndex">The new index.</param>
/// <returns>Whether the selection changed.</returns>
private bool changeSelectedIndex(int newIndex)
{
if (newIndex == selectedIndex) return false;
int direction = newIndex < selectedIndex ? -1 : 1;
bool beforeSelected = Selected;
Mod modBefore = SelectedMod ?? Mods[0];
if (newIndex >= Mods.Length)
newIndex = -1;
else if (newIndex < -1)
newIndex = Mods.Length - 1;
if (newIndex >= 0 && !Mods[newIndex].HasImplementation)
return false;
selectedIndex = newIndex;
Mod modAfter = SelectedMod ?? Mods[0];
if (beforeSelected != Selected)
{
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, Easing.OutElastic);
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, Easing.OutElastic);
}
if (modBefore != modAfter)
{
const float rotate_angle = 16;
foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
backgroundIcon.Icon = modAfter.Icon;
using (BeginDelayedSequence(mod_switch_duration, true))
{
foregroundIcon
.RotateTo(-rotate_angle * direction)
.RotateTo(0f, mod_switch_duration, mod_switch_easing);
backgroundIcon
.RotateTo(rotate_angle * direction)
.RotateTo(0f, mod_switch_duration, mod_switch_easing);
Schedule(() => displayMod(modAfter));
}
}
foregroundIcon.Highlighted = Selected;
SelectionChanged?.Invoke(SelectedMod);
return true;
}
public bool Selected => selectedIndex != -1;
private Color4 selectedColour;
public Color4 SelectedColour
{
get { return selectedColour; }
set
{
if (value == selectedColour) return;
selectedColour = value;
if (Selected) foregroundIcon.Colour = value;
}
}
private Mod mod;
public Mod Mod
{
get { return mod; }
set
{
mod = value;
if (mod == null)
{
Mods = Array.Empty<Mod>();
Alpha = 0;
}
else
{
Mods = (mod as MultiMod)?.Mods ?? new[] { mod };
Alpha = 1;
}
createIcons();
if (Mods.Length > 0)
{
displayMod(Mods[0]);
}
}
}
public Mod[] Mods { get; private set; }
public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
switch (args.Button)
{
case MouseButton.Left:
SelectNext(1);
break;
case MouseButton.Right:
SelectNext(-1);
break;
}
return true;
}
/// <summary>
/// Select the next available mod in a specified direction.
/// </summary>
/// <param name="direction">1 for forwards, -1 for backwards.</param>
public void SelectNext(int direction)
{
int start = selectedIndex + direction;
// wrap around if we are at an extremity.
if (start >= Mods.Length)
start = -1;
else if (start < -1)
start = Mods.Length - 1;
for (int i = start; i < Mods.Length && i >= 0; i += direction)
if (SelectAt(i)) return;
Deselect();
}
public bool SelectAt(int index)
{
if (!Mods[index].HasImplementation) return false;
changeSelectedIndex(index);
return true;
}
public void Deselect() => changeSelectedIndex(-1);
private void displayMod(Mod mod)
{
if (backgroundIcon != null)
backgroundIcon.Icon = foregroundIcon.Icon;
foregroundIcon.Icon = mod.Icon;
text.Text = mod.Name;
Colour = mod.HasImplementation ? Color4.White : Color4.Gray;
}
private void createIcons()
{
iconsContainer.Clear();
if (Mods.Length > 1)
{
iconsContainer.AddRange(new[]
{
backgroundIcon = new PassThroughTooltipModIcon(Mods[1])
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Position = new Vector2(1.5f),
},
foregroundIcon = new PassThroughTooltipModIcon(Mods[0])
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Position = new Vector2(-1.5f),
},
});
}
else
{
iconsContainer.Add(foregroundIcon = new PassThroughTooltipModIcon(Mod)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
});
}
}
public ModButton(Mod mod)
{
Children = new Drawable[]
{
new Container
{
Size = new Vector2(77f, 80f),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
iconsContainer = new Container<ModIcon>
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
}
}
},
text = new OsuSpriteText
{
Y = 75,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 18,
},
new HoverClickSounds()
};
Mod = mod;
}
private class PassThroughTooltipModIcon : ModIcon
{
public override string TooltipText => null;
public PassThroughTooltipModIcon(Mod mod)
: base(mod)
{
}
}
}
}

View File

@ -1,20 +1,20 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.Mods
{
/// <summary>
/// A mod button used exclusively for providing an empty space the size of a mod button.
/// </summary>
public class ModButtonEmpty : Container
{
public ModButtonEmpty()
{
Size = new Vector2(100f);
AlwaysPresent = true;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Overlays.Mods
{
/// <summary>
/// A mod button used exclusively for providing an empty space the size of a mod button.
/// </summary>
public class ModButtonEmpty : Container
{
public ModButtonEmpty()
{
Size = new Vector2(100f);
AlwaysPresent = true;
}
}
}

View File

@ -1,152 +1,152 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Linq;
using System.Collections.Generic;
namespace osu.Game.Overlays.Mods
{
public abstract class ModSection : Container
{
private readonly OsuSpriteText headerLabel;
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
public Action<Mod> Action;
protected abstract Key[] ToggleKeys { get; }
public abstract ModType ModType { get; }
public string Header
{
get => headerLabel.Text;
set => headerLabel.Text = value;
}
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
public IEnumerable<Mod> Mods
{
set
{
var modContainers = value.Select(m =>
{
if (m == null)
return new ModButtonEmpty();
return new ModButton(m)
{
SelectedColour = selectedColour,
SelectionChanged = Action,
};
}).ToArray();
ButtonsContainer.Children = modContainers;
buttons = modContainers.OfType<ModButton>().ToArray();
}
}
private ModButton[] buttons = { };
private Color4 selectedColour = Color4.White;
public Color4 SelectedColour
{
get => selectedColour;
set
{
if (value == selectedColour) return;
selectedColour = value;
foreach (ModButton button in buttons)
button.SelectedColour = value;
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
var index = Array.IndexOf(ToggleKeys, args.Key);
if (index > -1 && index < buttons.Length)
buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1);
return base.OnKeyDown(state, args);
}
public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
/// <summary>
/// Deselect one or more mods in this section.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
{
int delay = 0;
foreach (var button in buttons)
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
foreach (var type in modTypes)
if (type.IsInstanceOfType(selected))
{
if (immediate)
button.Deselect();
else
Scheduler.AddDelayed(button.Deselect, delay += 50);
}
}
}
/// <summary>
/// Select one or more mods in this section and deselects all other ones.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be selected.</param>
public void SelectTypes(IEnumerable<Type> modTypes)
{
foreach (var button in buttons)
{
int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m)));
if (i >= 0)
button.SelectAt(i);
else
button.Deselect();
}
}
protected ModSection()
{
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
headerLabel = new OsuSpriteText
{
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Position = new Vector2(0f, 0f),
Font = @"Exo2.0-Bold"
},
ButtonsContainer = new FillFlowContainer<ModButtonEmpty>
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Spacing = new Vector2(50f, 0f),
Margin = new MarginPadding
{
Top = 6,
},
AlwaysPresent = true
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Linq;
using System.Collections.Generic;
namespace osu.Game.Overlays.Mods
{
public abstract class ModSection : Container
{
private readonly OsuSpriteText headerLabel;
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
public Action<Mod> Action;
protected abstract Key[] ToggleKeys { get; }
public abstract ModType ModType { get; }
public string Header
{
get => headerLabel.Text;
set => headerLabel.Text = value;
}
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
public IEnumerable<Mod> Mods
{
set
{
var modContainers = value.Select(m =>
{
if (m == null)
return new ModButtonEmpty();
return new ModButton(m)
{
SelectedColour = selectedColour,
SelectionChanged = Action,
};
}).ToArray();
ButtonsContainer.Children = modContainers;
buttons = modContainers.OfType<ModButton>().ToArray();
}
}
private ModButton[] buttons = { };
private Color4 selectedColour = Color4.White;
public Color4 SelectedColour
{
get => selectedColour;
set
{
if (value == selectedColour) return;
selectedColour = value;
foreach (ModButton button in buttons)
button.SelectedColour = value;
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
var index = Array.IndexOf(ToggleKeys, args.Key);
if (index > -1 && index < buttons.Length)
buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1);
return base.OnKeyDown(state, args);
}
public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
/// <summary>
/// Deselect one or more mods in this section.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
{
int delay = 0;
foreach (var button in buttons)
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
foreach (var type in modTypes)
if (type.IsInstanceOfType(selected))
{
if (immediate)
button.Deselect();
else
Scheduler.AddDelayed(button.Deselect, delay += 50);
}
}
}
/// <summary>
/// Select one or more mods in this section and deselects all other ones.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be selected.</param>
public void SelectTypes(IEnumerable<Type> modTypes)
{
foreach (var button in buttons)
{
int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m)));
if (i >= 0)
button.SelectAt(i);
else
button.Deselect();
}
}
protected ModSection()
{
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
headerLabel = new OsuSpriteText
{
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Position = new Vector2(0f, 0f),
Font = @"Exo2.0-Bold"
},
ButtonsContainer = new FillFlowContainer<ModButtonEmpty>
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Spacing = new Vector2(50f, 0f),
Margin = new MarginPadding
{
Top = 6,
},
AlwaysPresent = true
},
};
}
}
}

View File

@ -1,383 +1,383 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
private const float content_width = 0.8f;
protected Color4 LowMultiplierColour, HighMultiplierColour;
protected readonly TriangleButton DeselectAllButton;
protected readonly OsuSpriteText MultiplierLabel;
private readonly FillFlowContainer footerContainer;
protected override bool BlockPassThroughKeyboard => false;
protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private void rulesetChanged(RulesetInfo newRuleset)
{
var instance = newRuleset.CreateInstance();
foreach (ModSection section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
refreshSelectedMods();
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio)
{
SelectedMods.ValueChanged += selectedModsChanged;
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
if (osu != null)
Ruleset.BindTo(osu.Ruleset);
else
Ruleset.Value = rulesets.AvailableRulesets.First();
Ruleset.ValueChanged += rulesetChanged;
Ruleset.TriggerChange();
sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off");
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Ruleset.UnbindAll();
SelectedMods.UnbindAll();
}
private void selectedModsChanged(IEnumerable<Mod> obj)
{
foreach (ModSection section in ModSectionsContainer.Children)
section.SelectTypes(obj.Select(m => m.GetType()).ToList());
updateMods();
}
private void updateMods()
{
double multiplier = 1.0;
bool ranked = true;
foreach (Mod mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
ranked &= mod.Ranked;
}
MultiplierLabel.Text = $"{multiplier:N2}x";
if (!ranked)
MultiplierLabel.Text += " (Unranked)";
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
}
protected override void PopOut()
{
base.PopOut();
footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine);
footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
}
}
protected override void PopIn()
{
base.PopIn();
footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
}
}
public void DeselectAll()
{
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectAll();
refreshSelectedMods();
}
/// <summary>
/// Deselect one or more mods.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
public void DeselectTypes(Type[] modTypes, bool immediate = false)
{
if (modTypes.Length == 0) return;
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectTypes(modTypes, immediate);
}
private SampleChannel sampleOn, sampleOff;
private void modButtonPressed(Mod selectedMod)
{
if (selectedMod != null)
{
if (State == Visibility.Visible) sampleOn?.Play();
DeselectTypes(selectedMod.IncompatibleMods, true);
}
else
{
if (State == Visibility.Visible) sampleOff?.Play();
}
refreshSelectedMods();
}
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{
FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510;
Content.RelativeSizeAxes = Axes.X;
Content.AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(36, 50, 68, 255)
},
new Triangles
{
TriangleScale = 5,
RelativeSizeAxes = Axes.X,
Height = Height, //set the height from the start to ensure correct triangle density.
ColourLight = new Color4(53, 66, 82, 255),
ColourDark = new Color4(41, 54, 70, 255),
},
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
// Header
new Container
{
RelativeSizeAxes = Axes.X,
Height = 82,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(10).Opacity(100),
},
new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Width = content_width,
Padding = new MarginPadding
{
Top = 10,
Bottom = 10,
},
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = @"Gameplay Mods",
TextSize = 22,
Shadow = true,
Margin = new MarginPadding
{
Bottom = 4,
},
},
new OsuSpriteText
{
Text = @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.",
TextSize = 18,
Shadow = true,
},
new OsuSpriteText
{
Text = @"Others are just for fun.",
TextSize = 18,
Shadow = true,
},
},
},
},
},
// Body
ModSectionsContainer = new FillFlowContainer<ModSection>
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Width = content_width,
Children = new ModSection[]
{
new DifficultyReductionSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new DifficultyIncreaseSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new SpecialSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
}
},
// Footer
new Container
{
RelativeSizeAxes = Axes.X,
Height = 70,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(172, 20, 116, 255),
Alpha = 0.5f,
},
footerContainer = new FillFlowContainer
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = content_width,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding
{
Vertical = 15
},
Children = new Drawable[]
{
DeselectAllButton = new TriangleButton
{
Width = 180,
Text = "Deselect All",
Action = DeselectAll,
Margin = new MarginPadding
{
Right = 20
}
},
new OsuSpriteText
{
Text = @"Score Multiplier: ",
TextSize = 30,
Shadow = true,
Margin = new MarginPadding
{
Top = 5
}
},
MultiplierLabel = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
TextSize = 30,
Shadow = true,
Margin = new MarginPadding
{
Top = 5
}
}
}
}
},
},
},
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
private const float content_width = 0.8f;
protected Color4 LowMultiplierColour, HighMultiplierColour;
protected readonly TriangleButton DeselectAllButton;
protected readonly OsuSpriteText MultiplierLabel;
private readonly FillFlowContainer footerContainer;
protected override bool BlockPassThroughKeyboard => false;
protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private void rulesetChanged(RulesetInfo newRuleset)
{
var instance = newRuleset.CreateInstance();
foreach (ModSection section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
refreshSelectedMods();
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio)
{
SelectedMods.ValueChanged += selectedModsChanged;
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
if (osu != null)
Ruleset.BindTo(osu.Ruleset);
else
Ruleset.Value = rulesets.AvailableRulesets.First();
Ruleset.ValueChanged += rulesetChanged;
Ruleset.TriggerChange();
sampleOn = audio.Sample.Get(@"UI/check-on");
sampleOff = audio.Sample.Get(@"UI/check-off");
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
Ruleset.UnbindAll();
SelectedMods.UnbindAll();
}
private void selectedModsChanged(IEnumerable<Mod> obj)
{
foreach (ModSection section in ModSectionsContainer.Children)
section.SelectTypes(obj.Select(m => m.GetType()).ToList());
updateMods();
}
private void updateMods()
{
double multiplier = 1.0;
bool ranked = true;
foreach (Mod mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
ranked &= mod.Ranked;
}
MultiplierLabel.Text = $"{multiplier:N2}x";
if (!ranked)
MultiplierLabel.Text += " (Unranked)";
if (multiplier > 1.0)
MultiplierLabel.FadeColour(HighMultiplierColour, 200);
else if (multiplier < 1.0)
MultiplierLabel.FadeColour(LowMultiplierColour, 200);
else
MultiplierLabel.FadeColour(Color4.White, 200);
}
protected override void PopOut()
{
base.PopOut();
footerContainer.MoveToX(footerContainer.DrawSize.X, DISAPPEAR_DURATION, Easing.InSine);
footerContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(100f, 0f), DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.MoveToX(100f, DISAPPEAR_DURATION, Easing.InSine);
section.ButtonsContainer.FadeOut(DISAPPEAR_DURATION, Easing.InSine);
}
}
protected override void PopIn()
{
base.PopIn();
footerContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
footerContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
foreach (ModSection section in ModSectionsContainer.Children)
{
section.ButtonsContainer.TransformSpacingTo(new Vector2(50f, 0f), APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.MoveToX(0, APPEAR_DURATION, Easing.OutQuint);
section.ButtonsContainer.FadeIn(APPEAR_DURATION, Easing.OutQuint);
}
}
public void DeselectAll()
{
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectAll();
refreshSelectedMods();
}
/// <summary>
/// Deselect one or more mods.
/// </summary>
/// <param name="modTypes">The types of <see cref="Mod"/>s which should be deselected.</param>
/// <param name="immediate">Set to true to bypass animations and update selections immediately.</param>
public void DeselectTypes(Type[] modTypes, bool immediate = false)
{
if (modTypes.Length == 0) return;
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectTypes(modTypes, immediate);
}
private SampleChannel sampleOn, sampleOff;
private void modButtonPressed(Mod selectedMod)
{
if (selectedMod != null)
{
if (State == Visibility.Visible) sampleOn?.Play();
DeselectTypes(selectedMod.IncompatibleMods, true);
}
else
{
if (State == Visibility.Visible) sampleOff?.Play();
}
refreshSelectedMods();
}
private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{
FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510;
Content.RelativeSizeAxes = Axes.X;
Content.AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(36, 50, 68, 255)
},
new Triangles
{
TriangleScale = 5,
RelativeSizeAxes = Axes.X,
Height = Height, //set the height from the start to ensure correct triangle density.
ColourLight = new Color4(53, 66, 82, 255),
ColourDark = new Color4(41, 54, 70, 255),
},
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
// Header
new Container
{
RelativeSizeAxes = Axes.X,
Height = 82,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(10).Opacity(100),
},
new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Width = content_width,
Padding = new MarginPadding
{
Top = 10,
Bottom = 10,
},
Children = new Drawable[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = @"Gameplay Mods",
TextSize = 22,
Shadow = true,
Margin = new MarginPadding
{
Bottom = 4,
},
},
new OsuSpriteText
{
Text = @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play.",
TextSize = 18,
Shadow = true,
},
new OsuSpriteText
{
Text = @"Others are just for fun.",
TextSize = 18,
Shadow = true,
},
},
},
},
},
// Body
ModSectionsContainer = new FillFlowContainer<ModSection>
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Width = content_width,
Children = new ModSection[]
{
new DifficultyReductionSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new DifficultyIncreaseSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new SpecialSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
}
},
// Footer
new Container
{
RelativeSizeAxes = Axes.X,
Height = 70,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(172, 20, 116, 255),
Alpha = 0.5f,
},
footerContainer = new FillFlowContainer
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = content_width,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding
{
Vertical = 15
},
Children = new Drawable[]
{
DeselectAllButton = new TriangleButton
{
Width = 180,
Text = "Deselect All",
Action = DeselectAll,
Margin = new MarginPadding
{
Right = 20
}
},
new OsuSpriteText
{
Text = @"Score Multiplier: ",
TextSize = 30,
Shadow = true,
Margin = new MarginPadding
{
Top = 5
}
},
MultiplierLabel = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
TextSize = 30,
Shadow = true,
Margin = new MarginPadding
{
Top = 5
}
}
}
}
},
},
},
},
};
}
}
}

View File

@ -1,27 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class SpecialSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
public override ModType ModType => ModType.Special;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.BlueLight;
}
public SpecialSection()
{
Header = @"Special";
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Overlays.Mods
{
public class SpecialSection : ModSection
{
protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
public override ModType ModType => ModType.Special;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
SelectedColour = colours.BlueLight;
}
public SpecialSection()
{
Header = @"Special";
}
}
}

View File

@ -1,74 +1,74 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Music
{
public class CollectionsDropdown<T> : OsuDropdown<T>
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Gray6;
}
protected override DropdownHeader CreateHeader() => new CollectionsHeader();
protected override DropdownMenu CreateMenu() => new CollectionsMenu();
private class CollectionsMenu : OsuDropdownMenu
{
public CollectionsMenu()
{
CornerRadius = 5;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.3f),
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray4;
}
}
private class CollectionsHeader : OsuDropdownHeader
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray4;
}
public CollectionsHeader()
{
CornerRadius = 5;
Height = 30;
Icon.Size = new Vector2(14);
Icon.Margin = new MarginPadding(0);
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 };
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.3f),
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Music
{
public class CollectionsDropdown<T> : OsuDropdown<T>
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Gray6;
}
protected override DropdownHeader CreateHeader() => new CollectionsHeader();
protected override DropdownMenu CreateMenu() => new CollectionsMenu();
private class CollectionsMenu : OsuDropdownMenu
{
public CollectionsMenu()
{
CornerRadius = 5;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.3f),
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray4;
}
}
private class CollectionsHeader : OsuDropdownHeader
{
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray4;
}
public CollectionsHeader()
{
CornerRadius = 5;
Height = 30;
Icon.Size = new Vector2(14);
Icon.Margin = new MarginPadding(0);
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 };
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.3f),
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
}
}
}

View File

@ -1,76 +1,76 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
using System;
namespace osu.Game.Overlays.Music
{
public class FilterControl : Container
{
public readonly FilterTextBox Search;
public FilterControl()
{
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
Search = new FilterTextBox
{
RelativeSizeAxes = Axes.X,
Height = 40,
Exit = () => ExitRequested?.Invoke(),
},
new CollectionsDropdown<PlaylistCollection>
{
RelativeSizeAxes = Axes.X,
Items = new[] { new KeyValuePair<string, PlaylistCollection>(@"All", PlaylistCollection.All) },
}
},
},
};
Search.Current.ValueChanged += current_ValueChanged;
}
private void current_ValueChanged(string newValue) => FilterChanged?.Invoke(newValue);
public Action ExitRequested;
public Action<string> FilterChanged;
public class FilterTextBox : SearchTextBox
{
private Color4 backgroundColour;
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
protected override bool AllowCommit => true;
public FilterTextBox()
{
Masking = true;
CornerRadius = 5;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundColour = colours.Gray2;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
using System;
namespace osu.Game.Overlays.Music
{
public class FilterControl : Container
{
public readonly FilterTextBox Search;
public FilterControl()
{
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
Search = new FilterTextBox
{
RelativeSizeAxes = Axes.X,
Height = 40,
Exit = () => ExitRequested?.Invoke(),
},
new CollectionsDropdown<PlaylistCollection>
{
RelativeSizeAxes = Axes.X,
Items = new[] { new KeyValuePair<string, PlaylistCollection>(@"All", PlaylistCollection.All) },
}
},
},
};
Search.Current.ValueChanged += current_ValueChanged;
}
private void current_ValueChanged(string newValue) => FilterChanged?.Invoke(newValue);
public Action ExitRequested;
public Action<string> FilterChanged;
public class FilterTextBox : SearchTextBox
{
private Color4 backgroundColour;
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
protected override bool AllowCommit => true;
public FilterTextBox()
{
Masking = true;
CornerRadius = 5;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundColour = colours.Gray2;
}
}
}
}

View File

@ -1,182 +1,182 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Music
{
public class PlaylistItem : Container, IFilterable, IDraggable
{
private const float fade_duration = 100;
private Color4 hoverColour;
private Color4 artistColour;
private SpriteIcon handle;
private TextFlowContainer text;
private IEnumerable<SpriteText> titleSprites;
private UnicodeBindableString titleBind;
private UnicodeBindableString artistBind;
public readonly BeatmapSetInfo BeatmapSetInfo;
public Action<BeatmapSetInfo> OnSelect;
public bool IsDraggable { get; private set; }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
IsDraggable = handle.IsHovered;
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
IsDraggable = false;
return base.OnMouseUp(state, args);
}
private bool selected;
public bool Selected
{
get { return selected; }
set
{
if (value == selected) return;
selected = value;
FinishTransforms(true);
foreach (SpriteText s in titleSprites)
s.FadeColour(Selected ? hoverColour : Color4.White, fade_duration);
}
}
public PlaylistItem(BeatmapSetInfo setInfo)
{
BeatmapSetInfo = setInfo;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Top = 3, Bottom = 3 };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
hoverColour = colours.Yellow;
artistColour = colours.Gray9;
var metadata = BeatmapSetInfo.Metadata;
FilterTerms = metadata.SearchableTerms;
Children = new Drawable[]
{
handle = new PlaylistItemHandle
{
Colour = colours.Gray5
},
text = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = 20 },
ContentIndent = 10f,
},
};
titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
artistBind.ValueChanged += newText => recreateText();
artistBind.TriggerChange();
}
private void recreateText()
{
text.Clear();
//space after the title to put a space between the title and artist
titleSprites = text.AddText(titleBind.Value + @" ", sprite =>
{
sprite.TextSize = 16;
sprite.Font = @"Exo2.0-Regular";
});
text.AddText(artistBind.Value, sprite =>
{
sprite.TextSize = 14;
sprite.Font = @"Exo2.0-Bold";
sprite.Colour = artistColour;
sprite.Padding = new MarginPadding { Top = 1 };
});
}
protected override bool OnHover(InputState state)
{
handle.FadeIn(fade_duration);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
handle.FadeOut(fade_duration);
}
protected override bool OnClick(InputState state)
{
OnSelect?.Invoke(BeatmapSetInfo);
return true;
}
public IEnumerable<string> FilterTerms { get; private set; }
private bool matching = true;
public bool MatchingFilter
{
get { return matching; }
set
{
if (matching == value) return;
matching = value;
this.FadeTo(matching ? 1 : 0, 200);
}
}
private class PlaylistItemHandle : SpriteIcon
{
public PlaylistItemHandle()
{
Anchor = Anchor.TopLeft;
Origin = Anchor.TopLeft;
Size = new Vector2(12);
Icon = FontAwesome.fa_bars;
Alpha = 0f;
Margin = new MarginPadding { Left = 5, Top = 2 };
}
}
}
public interface IDraggable : IDrawable
{
/// <summary>
/// Whether this <see cref="IDraggable"/> can be dragged in its current state.
/// </summary>
bool IsDraggable { get; }
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Music
{
public class PlaylistItem : Container, IFilterable, IDraggable
{
private const float fade_duration = 100;
private Color4 hoverColour;
private Color4 artistColour;
private SpriteIcon handle;
private TextFlowContainer text;
private IEnumerable<SpriteText> titleSprites;
private UnicodeBindableString titleBind;
private UnicodeBindableString artistBind;
public readonly BeatmapSetInfo BeatmapSetInfo;
public Action<BeatmapSetInfo> OnSelect;
public bool IsDraggable { get; private set; }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
IsDraggable = handle.IsHovered;
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
IsDraggable = false;
return base.OnMouseUp(state, args);
}
private bool selected;
public bool Selected
{
get { return selected; }
set
{
if (value == selected) return;
selected = value;
FinishTransforms(true);
foreach (SpriteText s in titleSprites)
s.FadeColour(Selected ? hoverColour : Color4.White, fade_duration);
}
}
public PlaylistItem(BeatmapSetInfo setInfo)
{
BeatmapSetInfo = setInfo;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Top = 3, Bottom = 3 };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
hoverColour = colours.Yellow;
artistColour = colours.Gray9;
var metadata = BeatmapSetInfo.Metadata;
FilterTerms = metadata.SearchableTerms;
Children = new Drawable[]
{
handle = new PlaylistItemHandle
{
Colour = colours.Gray5
},
text = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = 20 },
ContentIndent = 10f,
},
};
titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
artistBind.ValueChanged += newText => recreateText();
artistBind.TriggerChange();
}
private void recreateText()
{
text.Clear();
//space after the title to put a space between the title and artist
titleSprites = text.AddText(titleBind.Value + @" ", sprite =>
{
sprite.TextSize = 16;
sprite.Font = @"Exo2.0-Regular";
});
text.AddText(artistBind.Value, sprite =>
{
sprite.TextSize = 14;
sprite.Font = @"Exo2.0-Bold";
sprite.Colour = artistColour;
sprite.Padding = new MarginPadding { Top = 1 };
});
}
protected override bool OnHover(InputState state)
{
handle.FadeIn(fade_duration);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
handle.FadeOut(fade_duration);
}
protected override bool OnClick(InputState state)
{
OnSelect?.Invoke(BeatmapSetInfo);
return true;
}
public IEnumerable<string> FilterTerms { get; private set; }
private bool matching = true;
public bool MatchingFilter
{
get { return matching; }
set
{
if (matching == value) return;
matching = value;
this.FadeTo(matching ? 1 : 0, 200);
}
}
private class PlaylistItemHandle : SpriteIcon
{
public PlaylistItemHandle()
{
Anchor = Anchor.TopLeft;
Origin = Anchor.TopLeft;
Size = new Vector2(12);
Icon = FontAwesome.fa_bars;
Alpha = 0f;
Margin = new MarginPadding { Left = 5, Top = 2 };
}
}
}
public interface IDraggable : IDrawable
{
/// <summary>
/// Whether this <see cref="IDraggable"/> can be dragged in its current state.
/// </summary>
bool IsDraggable { get; }
}
}

View File

@ -1,255 +1,255 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Music
{
public class PlaylistList : CompositeDrawable
{
public Action<BeatmapSetInfo> OnSelect;
private readonly ItemsScrollContainer items;
public PlaylistList()
{
InternalChild = items = new ItemsScrollContainer
{
RelativeSizeAxes = Axes.Both,
OnSelect = set => OnSelect?.Invoke(set)
};
}
public new MarginPadding Padding
{
get { return base.Padding; }
set { base.Padding = value; }
}
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get { return items.Sets; }
set { items.Sets = value; }
}
public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet;
public BeatmapSetInfo NextSet => items.NextSet;
public BeatmapSetInfo PreviousSet => items.PreviousSet;
public BeatmapSetInfo SelectedSet
{
get { return items.SelectedSet; }
set { items.SelectedSet = value; }
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet);
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet);
public void Filter(string searchTerm) => items.SearchTerm = searchTerm;
private class ItemsScrollContainer : OsuScrollContainer
{
public Action<BeatmapSetInfo> OnSelect;
private readonly SearchContainer search;
private readonly FillFlowContainer<PlaylistItem> items;
public ItemsScrollContainer()
{
Children = new Drawable[]
{
search = new SearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
items = new ItemSearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}
}
};
}
public IEnumerable<BeatmapSetInfo> Sets
{
get { return items.Select(x => x.BeatmapSetInfo).ToList(); }
set
{
items.Clear();
value.ForEach(AddBeatmapSet);
}
}
public string SearchTerm
{
get { return search.SearchTerm; }
set { search.SearchTerm = value; }
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
{
var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) };
items.Add(newItem);
items.SetLayoutPosition(newItem, items.Count);
}
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
{
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID);
if (itemToRemove != null)
items.Remove(itemToRemove);
}
public BeatmapSetInfo SelectedSet
{
get { return items.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; }
set
{
foreach (PlaylistItem s in items.Children)
s.Selected = s.BeatmapSetInfo.ID == value?.ID;
}
}
public BeatmapSetInfo FirstVisibleSet => items.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo;
public BeatmapSetInfo NextSet => (items.SkipWhile(i => !i.Selected).Skip(1).FirstOrDefault() ?? items.FirstOrDefault())?.BeatmapSetInfo;
public BeatmapSetInfo PreviousSet => (items.TakeWhile(i => !i.Selected).LastOrDefault() ?? items.LastOrDefault())?.BeatmapSetInfo;
private Vector2 nativeDragPosition;
private PlaylistItem draggedItem;
protected override bool OnDragStart(InputState state)
{
nativeDragPosition = state.Mouse.NativeState.Position;
draggedItem = items.FirstOrDefault(d => d.IsDraggable);
return draggedItem != null || base.OnDragStart(state);
}
protected override bool OnDrag(InputState state)
{
nativeDragPosition = state.Mouse.NativeState.Position;
if (draggedItem == null)
return base.OnDrag(state);
return true;
}
protected override bool OnDragEnd(InputState state)
{
nativeDragPosition = state.Mouse.NativeState.Position;
var handled = draggedItem != null || base.OnDragEnd(state);
draggedItem = null;
return handled;
}
protected override void Update()
{
base.Update();
if (draggedItem == null)
return;
updateScrollPosition();
updateDragPosition();
}
private void updateScrollPosition()
{
const float start_offset = 10;
const double max_power = 50;
const double exp_base = 1.05;
var localPos = ToLocalSpace(nativeDragPosition);
if (localPos.Y < start_offset)
{
if (Current <= 0)
return;
var power = Math.Min(max_power, Math.Abs(start_offset - localPos.Y));
ScrollBy(-(float)Math.Pow(exp_base, power));
}
else if (localPos.Y > DrawHeight - start_offset)
{
if (IsScrolledToEnd())
return;
var power = Math.Min(max_power, Math.Abs(DrawHeight - start_offset - localPos.Y));
ScrollBy((float)Math.Pow(exp_base, power));
}
}
private void updateDragPosition()
{
var itemsPos = items.ToLocalSpace(nativeDragPosition);
int srcIndex = (int)items.GetLayoutPosition(draggedItem);
// Find the last item with position < mouse position. Note we can't directly use
// the item positions as they are being transformed
float heightAccumulator = 0;
int dstIndex = 0;
for (; dstIndex < items.Count; dstIndex++)
{
// Using BoundingBox here takes care of scale, paddings, etc...
heightAccumulator += items[dstIndex].BoundingBox.Height;
if (heightAccumulator > itemsPos.Y)
break;
}
dstIndex = MathHelper.Clamp(dstIndex, 0, items.Count - 1);
if (srcIndex == dstIndex)
return;
if (srcIndex < dstIndex)
{
for (int i = srcIndex + 1; i <= dstIndex; i++)
items.SetLayoutPosition(items[i], i - 1);
}
else
{
for (int i = dstIndex; i < srcIndex; i++)
items.SetLayoutPosition(items[i], i + 1);
}
items.SetLayoutPosition(draggedItem, dstIndex);
}
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
{
public IEnumerable<string> FilterTerms => new string[] { };
public bool MatchingFilter
{
set
{
if (value)
InvalidateLayout();
}
}
public IEnumerable<IFilterable> FilterableChildren => Children;
public ItemSearchContainer()
{
LayoutDuration = 200;
LayoutEasing = Easing.OutQuint;
}
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Music
{
public class PlaylistList : CompositeDrawable
{
public Action<BeatmapSetInfo> OnSelect;
private readonly ItemsScrollContainer items;
public PlaylistList()
{
InternalChild = items = new ItemsScrollContainer
{
RelativeSizeAxes = Axes.Both,
OnSelect = set => OnSelect?.Invoke(set)
};
}
public new MarginPadding Padding
{
get { return base.Padding; }
set { base.Padding = value; }
}
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get { return items.Sets; }
set { items.Sets = value; }
}
public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet;
public BeatmapSetInfo NextSet => items.NextSet;
public BeatmapSetInfo PreviousSet => items.PreviousSet;
public BeatmapSetInfo SelectedSet
{
get { return items.SelectedSet; }
set { items.SelectedSet = value; }
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet);
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet);
public void Filter(string searchTerm) => items.SearchTerm = searchTerm;
private class ItemsScrollContainer : OsuScrollContainer
{
public Action<BeatmapSetInfo> OnSelect;
private readonly SearchContainer search;
private readonly FillFlowContainer<PlaylistItem> items;
public ItemsScrollContainer()
{
Children = new Drawable[]
{
search = new SearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
items = new ItemSearchContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
},
}
}
};
}
public IEnumerable<BeatmapSetInfo> Sets
{
get { return items.Select(x => x.BeatmapSetInfo).ToList(); }
set
{
items.Clear();
value.ForEach(AddBeatmapSet);
}
}
public string SearchTerm
{
get { return search.SearchTerm; }
set { search.SearchTerm = value; }
}
public void AddBeatmapSet(BeatmapSetInfo beatmapSet)
{
var newItem = new PlaylistItem(beatmapSet) { OnSelect = set => OnSelect?.Invoke(set) };
items.Add(newItem);
items.SetLayoutPosition(newItem, items.Count);
}
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
{
var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID);
if (itemToRemove != null)
items.Remove(itemToRemove);
}
public BeatmapSetInfo SelectedSet
{
get { return items.FirstOrDefault(i => i.Selected)?.BeatmapSetInfo; }
set
{
foreach (PlaylistItem s in items.Children)
s.Selected = s.BeatmapSetInfo.ID == value?.ID;
}
}
public BeatmapSetInfo FirstVisibleSet => items.FirstOrDefault(i => i.MatchingFilter)?.BeatmapSetInfo;
public BeatmapSetInfo NextSet => (items.SkipWhile(i => !i.Selected).Skip(1).FirstOrDefault() ?? items.FirstOrDefault())?.BeatmapSetInfo;
public BeatmapSetInfo PreviousSet => (items.TakeWhile(i => !i.Selected).LastOrDefault() ?? items.LastOrDefault())?.BeatmapSetInfo;
private Vector2 nativeDragPosition;
private PlaylistItem draggedItem;
protected override bool OnDragStart(InputState state)
{
nativeDragPosition = state.Mouse.NativeState.Position;
draggedItem = items.FirstOrDefault(d => d.IsDraggable);
return draggedItem != null || base.OnDragStart(state);
}
protected override bool OnDrag(InputState state)
{
nativeDragPosition = state.Mouse.NativeState.Position;
if (draggedItem == null)
return base.OnDrag(state);
return true;
}
protected override bool OnDragEnd(InputState state)
{
nativeDragPosition = state.Mouse.NativeState.Position;
var handled = draggedItem != null || base.OnDragEnd(state);
draggedItem = null;
return handled;
}
protected override void Update()
{
base.Update();
if (draggedItem == null)
return;
updateScrollPosition();
updateDragPosition();
}
private void updateScrollPosition()
{
const float start_offset = 10;
const double max_power = 50;
const double exp_base = 1.05;
var localPos = ToLocalSpace(nativeDragPosition);
if (localPos.Y < start_offset)
{
if (Current <= 0)
return;
var power = Math.Min(max_power, Math.Abs(start_offset - localPos.Y));
ScrollBy(-(float)Math.Pow(exp_base, power));
}
else if (localPos.Y > DrawHeight - start_offset)
{
if (IsScrolledToEnd())
return;
var power = Math.Min(max_power, Math.Abs(DrawHeight - start_offset - localPos.Y));
ScrollBy((float)Math.Pow(exp_base, power));
}
}
private void updateDragPosition()
{
var itemsPos = items.ToLocalSpace(nativeDragPosition);
int srcIndex = (int)items.GetLayoutPosition(draggedItem);
// Find the last item with position < mouse position. Note we can't directly use
// the item positions as they are being transformed
float heightAccumulator = 0;
int dstIndex = 0;
for (; dstIndex < items.Count; dstIndex++)
{
// Using BoundingBox here takes care of scale, paddings, etc...
heightAccumulator += items[dstIndex].BoundingBox.Height;
if (heightAccumulator > itemsPos.Y)
break;
}
dstIndex = MathHelper.Clamp(dstIndex, 0, items.Count - 1);
if (srcIndex == dstIndex)
return;
if (srcIndex < dstIndex)
{
for (int i = srcIndex + 1; i <= dstIndex; i++)
items.SetLayoutPosition(items[i], i - 1);
}
else
{
for (int i = dstIndex; i < srcIndex; i++)
items.SetLayoutPosition(items[i], i + 1);
}
items.SetLayoutPosition(draggedItem, dstIndex);
}
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
{
public IEnumerable<string> FilterTerms => new string[] { };
public bool MatchingFilter
{
set
{
if (value)
InvalidateLayout();
}
}
public IEnumerable<IFilterable> FilterableChildren => Children;
public ItemSearchContainer()
{
LayoutDuration = 200;
LayoutEasing = Easing.OutQuint;
}
}
}
}
}

View File

@ -1,163 +1,177 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Music
{
public class PlaylistOverlay : OverlayContainer
{
private const float transition_duration = 600;
private const float playlist_height = 510;
private FilterControl filter;
private PlaylistList list;
private BeatmapManager beatmaps;
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
public IEnumerable<BeatmapSetInfo> BeatmapSets => list.BeatmapSets;
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours)
{
this.beatmaps = beatmaps;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 5,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
Colour = colours.Gray3,
RelativeSizeAxes = Axes.Both,
},
list = new PlaylistList
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 },
OnSelect = itemSelected,
},
filter = new FilterControl
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ExitRequested = () => State = Visibility.Hidden,
FilterChanged = search => list.Filter(search),
Padding = new MarginPadding(10),
},
},
},
};
beatmaps.ItemAdded += list.AddBeatmapSet;
beatmaps.ItemRemoved += list.RemoveBeatmapSet;
list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
beatmapBacking.BindTo(game.Beatmap);
filter.Search.OnCommit = (sender, newText) =>
{
var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
if (beatmap != null) playSpecified(beatmap);
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo;
beatmapBacking.TriggerChange();
}
protected override void PopIn()
{
filter.Search.HoldFocus = true;
Schedule(() => GetContainingInputManager().ChangeFocus(filter.Search));
this.ResizeTo(new Vector2(1, playlist_height), transition_duration, Easing.OutQuint);
this.FadeIn(transition_duration, Easing.OutQuint);
}
protected override void PopOut()
{
filter.Search.HoldFocus = false;
this.ResizeTo(new Vector2(1, 0), transition_duration, Easing.OutQuint);
this.FadeOut(transition_duration);
}
private void itemSelected(BeatmapSetInfo set)
{
if (set.ID == (beatmapBacking.Value?.BeatmapSetInfo?.ID ?? -1))
{
beatmapBacking.Value?.Track?.Seek(0);
return;
}
playSpecified(set.Beatmaps.First());
}
public void PlayPrevious()
{
var playable = list.PreviousSet;
if (playable != null)
{
playSpecified(playable.Beatmaps.First());
list.SelectedSet = playable;
}
}
public void PlayNext()
{
var playable = list.NextSet;
if (playable != null)
{
playSpecified(playable.Beatmaps.First());
list.SelectedSet = playable;
}
}
private void playSpecified(BeatmapInfo info)
{
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking);
var track = beatmapBacking.Value.Track;
track.Restart();
}
}
//todo: placeholder
public enum PlaylistCollection
{
All
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Music
{
public class PlaylistOverlay : OverlayContainer
{
private const float transition_duration = 600;
private const float playlist_height = 510;
private FilterControl filter;
private PlaylistList list;
private BeatmapManager beatmaps;
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
public IEnumerable<BeatmapSetInfo> BeatmapSets => list.BeatmapSets;
[BackgroundDependencyLoader]
private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours)
{
this.beatmaps = beatmaps;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 5,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
Colour = colours.Gray3,
RelativeSizeAxes = Axes.Both,
},
list = new PlaylistList
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 95, Bottom = 10, Right = 10 },
OnSelect = itemSelected,
},
filter = new FilterControl
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ExitRequested = () => State = Visibility.Hidden,
FilterChanged = search => list.Filter(search),
Padding = new MarginPadding(10),
},
},
},
};
beatmaps.ItemAdded += handleBeatmapAdded;
beatmaps.ItemRemoved += handleBeatmapRemoved;
list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets();
beatmapBacking.BindTo(game.Beatmap);
filter.Search.OnCommit = (sender, newText) =>
{
var beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
if (beatmap != null) playSpecified(beatmap);
};
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmapBacking.ValueChanged += b => list.SelectedSet = b?.BeatmapSetInfo;
beatmapBacking.TriggerChange();
}
private void handleBeatmapAdded(BeatmapSetInfo setInfo) => Schedule(() => list.AddBeatmapSet(setInfo));
private void handleBeatmapRemoved(BeatmapSetInfo setInfo) => Schedule(() => list.RemoveBeatmapSet(setInfo));
protected override void PopIn()
{
filter.Search.HoldFocus = true;
Schedule(() => GetContainingInputManager().ChangeFocus(filter.Search));
this.ResizeTo(new Vector2(1, playlist_height), transition_duration, Easing.OutQuint);
this.FadeIn(transition_duration, Easing.OutQuint);
}
protected override void PopOut()
{
filter.Search.HoldFocus = false;
this.ResizeTo(new Vector2(1, 0), transition_duration, Easing.OutQuint);
this.FadeOut(transition_duration);
}
private void itemSelected(BeatmapSetInfo set)
{
if (set.ID == (beatmapBacking.Value?.BeatmapSetInfo?.ID ?? -1))
{
beatmapBacking.Value?.Track?.Seek(0);
return;
}
playSpecified(set.Beatmaps.First());
}
public void PlayPrevious()
{
var playable = list.PreviousSet;
if (playable != null)
{
playSpecified(playable.Beatmaps.First());
list.SelectedSet = playable;
}
}
public void PlayNext()
{
var playable = list.NextSet;
if (playable != null)
{
playSpecified(playable.Beatmaps.First());
list.SelectedSet = playable;
}
}
private void playSpecified(BeatmapInfo info)
{
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking);
var track = beatmapBacking.Value.Track;
track.Restart();
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (beatmaps != null)
{
beatmaps.ItemAdded -= handleBeatmapAdded;
beatmaps.ItemRemoved -= handleBeatmapRemoved;
}
}
}
//todo: placeholder
public enum PlaylistCollection
{
All
}
}

View File

@ -1,447 +1,447 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Music;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
public class MusicController : OsuFocusedOverlayContainer
{
private const float player_height = 130;
private const float transition_length = 800;
private const float progress_height = 10;
private const float bottom_black_area_height = 55;
private Drawable background;
private ProgressBar progressBar;
private IconButton prevButton;
private IconButton playButton;
private IconButton nextButton;
private IconButton playlistButton;
private SpriteText title, artist;
private PlaylistOverlay playlist;
private LocalisationEngine localisation;
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
private Container dragContainer;
private Container playerContainer;
public MusicController()
{
Width = 400;
Margin = new MarginPadding(10);
// required to let MusicController handle beatmap cycling.
AlwaysPresent = true;
}
private Vector2 dragStart;
protected override bool OnDragStart(InputState state)
{
base.OnDragStart(state);
dragStart = state.Mouse.Position;
return true;
}
protected override bool OnDrag(InputState state)
{
if (base.OnDrag(state)) return true;
Vector2 change = state.Mouse.Position - dragStart;
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
dragContainer.MoveTo(change);
return true;
}
protected override bool OnDragEnd(InputState state)
{
dragContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
return base.OnDragEnd(state);
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game, OsuColour colours, LocalisationEngine localisation)
{
this.localisation = localisation;
Children = new Drawable[]
{
dragContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
playlist = new PlaylistOverlay
{
RelativeSizeAxes = Axes.X,
Y = player_height + 10,
},
playerContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = player_height,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new[]
{
background = new Background(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 40),
TextSize = 25,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-MediumItalic"
},
artist = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 45),
TextSize = 15,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-BoldItalic"
},
new Container
{
Padding = new MarginPadding { Bottom = progress_height },
Height = bottom_black_area_height,
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Children = new Drawable[]
{
new FillFlowContainer<IconButton>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new[]
{
prevButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = prev,
Icon = FontAwesome.fa_step_backward,
},
playButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = play,
Icon = FontAwesome.fa_play_circle_o,
},
nextButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = next,
Icon = FontAwesome.fa_step_forward,
},
}
},
playlistButton = new IconButton
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-bottom_black_area_height / 2, 0),
Icon = FontAwesome.fa_bars,
Action = () => playlist.ToggleVisibility(),
},
}
},
progressBar = new ProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = progress_height,
FillColour = colours.Yellow,
OnSeek = progress => current?.Track.Seek(progress)
}
},
},
}
}
};
beatmapBacking.BindTo(game.Beatmap);
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
protected override void LoadComplete()
{
beatmapBacking.ValueChanged += beatmapChanged;
beatmapBacking.DisabledChanged += beatmapDisabledChanged;
beatmapBacking.TriggerChange();
base.LoadComplete();
}
private void beatmapDisabledChanged(bool disabled)
{
if (disabled)
playlist.Hide();
prevButton.Enabled.Value = !disabled;
nextButton.Enabled.Value = !disabled;
playlistButton.Enabled.Value = !disabled;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Height = dragContainer.Height;
}
protected override void Update()
{
base.Update();
if (current?.TrackLoaded ?? false)
{
var track = current.Track;
progressBar.EndTime = track.Length;
progressBar.CurrentTime = track.CurrentTime;
playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && playlist.BeatmapSets.Any())
next();
}
else
playButton.Icon = FontAwesome.fa_play_circle_o;
}
private void play()
{
var track = current?.Track;
if (track == null)
{
if (!beatmapBacking.Disabled)
playlist.PlayNext();
return;
}
if (track.IsRunning)
track.Stop();
else
track.Start();
}
private void prev()
{
queuedDirection = TransformDirection.Prev;
playlist.PlayPrevious();
}
private void next()
{
queuedDirection = TransformDirection.Next;
playlist.PlayNext();
}
private WorkingBeatmap current;
private TransformDirection? queuedDirection;
private void beatmapChanged(WorkingBeatmap beatmap)
{
TransformDirection direction = TransformDirection.None;
if (current != null)
{
bool audioEquals = beatmap?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false;
if (audioEquals)
direction = TransformDirection.None;
else if (queuedDirection.HasValue)
{
direction = queuedDirection.Value;
queuedDirection = null;
}
else
{
//figure out the best direction based on order in playlist.
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}
}
current = beatmap;
progressBar.CurrentTime = 0;
updateDisplay(current, direction);
queuedDirection = null;
}
private ScheduledDelegate pendingBeatmapSwitch;
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
{
//we might be off-screen when this update comes in.
//rather than Scheduling, manually handle this to avoid possible memory contention.
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(delegate
{
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() =>
{
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
{
title.Current = null;
title.Text = @"Nothing to play";
artist.Current = null;
artist.Text = @"Nothing to play";
}
else
{
BeatmapMetadata metadata = beatmap.Metadata;
title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
}
});
LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
{
switch (direction)
{
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(400, 500, Easing.OutCubic);
break;
}
background.Expire();
background = newBackground;
playerContainer.Add(newBackground);
});
});
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(1, transition_length, Easing.OutElastic);
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
}
private enum TransformDirection
{
None,
Next,
Prev
}
private class Background : BufferedContainer
{
private readonly Sprite sprite;
private readonly WorkingBeatmap beatmap;
public Background(WorkingBeatmap beatmap = null)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
Depth = float.MaxValue;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
sprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(150),
FillMode = FillMode.Fill,
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = bottom_black_area_height,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Colour = Color4.Black.Opacity(0.5f)
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Music;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays
{
public class MusicController : OsuFocusedOverlayContainer
{
private const float player_height = 130;
private const float transition_length = 800;
private const float progress_height = 10;
private const float bottom_black_area_height = 55;
private Drawable background;
private ProgressBar progressBar;
private IconButton prevButton;
private IconButton playButton;
private IconButton nextButton;
private IconButton playlistButton;
private SpriteText title, artist;
private PlaylistOverlay playlist;
private LocalisationEngine localisation;
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
private Container dragContainer;
private Container playerContainer;
public MusicController()
{
Width = 400;
Margin = new MarginPadding(10);
// required to let MusicController handle beatmap cycling.
AlwaysPresent = true;
}
private Vector2 dragStart;
protected override bool OnDragStart(InputState state)
{
base.OnDragStart(state);
dragStart = state.Mouse.Position;
return true;
}
protected override bool OnDrag(InputState state)
{
if (base.OnDrag(state)) return true;
Vector2 change = state.Mouse.Position - dragStart;
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
dragContainer.MoveTo(change);
return true;
}
protected override bool OnDragEnd(InputState state)
{
dragContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
return base.OnDragEnd(state);
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game, OsuColour colours, LocalisationEngine localisation)
{
this.localisation = localisation;
Children = new Drawable[]
{
dragContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
playlist = new PlaylistOverlay
{
RelativeSizeAxes = Axes.X,
Y = player_height + 10,
},
playerContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = player_height,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new[]
{
background = new Background(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 40),
TextSize = 25,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-MediumItalic"
},
artist = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 45),
TextSize = 15,
Colour = Color4.White,
Text = @"Nothing to play",
Font = @"Exo2.0-BoldItalic"
},
new Container
{
Padding = new MarginPadding { Bottom = progress_height },
Height = bottom_black_area_height,
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Children = new Drawable[]
{
new FillFlowContainer<IconButton>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new[]
{
prevButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = prev,
Icon = FontAwesome.fa_step_backward,
},
playButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = play,
Icon = FontAwesome.fa_play_circle_o,
},
nextButton = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = next,
Icon = FontAwesome.fa_step_forward,
},
}
},
playlistButton = new IconButton
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-bottom_black_area_height / 2, 0),
Icon = FontAwesome.fa_bars,
Action = () => playlist.ToggleVisibility(),
},
}
},
progressBar = new ProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = progress_height,
FillColour = colours.Yellow,
OnSeek = progress => current?.Track.Seek(progress)
}
},
},
}
}
};
beatmapBacking.BindTo(game.Beatmap);
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
protected override void LoadComplete()
{
beatmapBacking.ValueChanged += beatmapChanged;
beatmapBacking.DisabledChanged += beatmapDisabledChanged;
beatmapBacking.TriggerChange();
base.LoadComplete();
}
private void beatmapDisabledChanged(bool disabled)
{
if (disabled)
playlist.Hide();
prevButton.Enabled.Value = !disabled;
nextButton.Enabled.Value = !disabled;
playlistButton.Enabled.Value = !disabled;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Height = dragContainer.Height;
}
protected override void Update()
{
base.Update();
if (current?.TrackLoaded ?? false)
{
var track = current.Track;
progressBar.EndTime = track.Length;
progressBar.CurrentTime = track.CurrentTime;
playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o;
if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled && playlist.BeatmapSets.Any())
next();
}
else
playButton.Icon = FontAwesome.fa_play_circle_o;
}
private void play()
{
var track = current?.Track;
if (track == null)
{
if (!beatmapBacking.Disabled)
playlist.PlayNext();
return;
}
if (track.IsRunning)
track.Stop();
else
track.Start();
}
private void prev()
{
queuedDirection = TransformDirection.Prev;
playlist.PlayPrevious();
}
private void next()
{
queuedDirection = TransformDirection.Next;
playlist.PlayNext();
}
private WorkingBeatmap current;
private TransformDirection? queuedDirection;
private void beatmapChanged(WorkingBeatmap beatmap)
{
TransformDirection direction = TransformDirection.None;
if (current != null)
{
bool audioEquals = beatmap?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false;
if (audioEquals)
direction = TransformDirection.None;
else if (queuedDirection.HasValue)
{
direction = queuedDirection.Value;
queuedDirection = null;
}
else
{
//figure out the best direction based on order in playlist.
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
var next = beatmap == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmap.BeatmapSetInfo?.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
}
}
current = beatmap;
progressBar.CurrentTime = 0;
updateDisplay(current, direction);
queuedDirection = null;
}
private ScheduledDelegate pendingBeatmapSwitch;
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
{
//we might be off-screen when this update comes in.
//rather than Scheduling, manually handle this to avoid possible memory contention.
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(delegate
{
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() =>
{
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
{
title.Current = null;
title.Text = @"Nothing to play";
artist.Current = null;
artist.Text = @"Nothing to play";
}
else
{
BeatmapMetadata metadata = beatmap.Metadata;
title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
}
});
LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
{
switch (direction)
{
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(400, 500, Easing.OutCubic);
break;
}
background.Expire();
background = newBackground;
playerContainer.Add(newBackground);
});
});
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(1, transition_length, Easing.OutElastic);
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
}
private enum TransformDirection
{
None,
Next,
Prev
}
private class Background : BufferedContainer
{
private readonly Sprite sprite;
private readonly WorkingBeatmap beatmap;
public Background(WorkingBeatmap beatmap = null)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
Depth = float.MaxValue;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
sprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(150),
FillMode = FillMode.Fill,
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = bottom_black_area_height,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Colour = Color4.Black.Opacity(0.5f)
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
}
}
}

View File

@ -1,183 +1,183 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Notifications;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Threading;
namespace osu.Game.Overlays
{
public class NotificationOverlay : OsuFocusedOverlayContainer
{
private const float width = 320;
public const float TRANSITION_LENGTH = 600;
/// <summary>
/// Whether posted notifications should be processed.
/// </summary>
public readonly BindableBool Enabled = new BindableBool(true);
private FlowContainer<NotificationSection> sections;
/// <summary>
/// Provide a source for the toolbar height.
/// </summary>
public Func<float> GetToolbarHeight;
public NotificationOverlay()
{
ScheduledDelegate notificationsEnabler = null;
Enabled.ValueChanged += v =>
{
if (!IsLoaded)
{
processingPosts = v;
return;
}
notificationsEnabler?.Cancel();
if (v)
// we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000);
else
processingPosts = false;
};
}
[BackgroundDependencyLoader]
private void load()
{
Width = width;
RelativeSizeAxes = Axes.Y;
AlwaysPresent = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f
},
new OsuScrollContainer
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
sections = new FillFlowContainer<NotificationSection>
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Children = new[]
{
new NotificationSection
{
Title = @"Notifications",
ClearText = @"Clear All",
AcceptTypes = new[] { typeof(SimpleNotification) }
},
new NotificationSection
{
Title = @"Running Tasks",
ClearText = @"Cancel All",
AcceptTypes = new[] { typeof(ProgressNotification) }
}
}
}
}
}
};
}
private int totalCount => sections.Select(c => c.DisplayedCount).Sum();
private int unreadCount => sections.Select(c => c.UnreadCount).Sum();
public readonly BindableInt UnreadCount = new BindableInt();
private int runningDepth;
private void notificationClosed() => updateCounts();
private readonly Scheduler postScheduler = new Scheduler();
private bool processingPosts = true;
public void Post(Notification notification) => postScheduler.Add(() =>
{
++runningDepth;
notification.Closed += notificationClosed;
var hasCompletionTarget = notification as IHasCompletionTarget;
if (hasCompletionTarget != null)
hasCompletionTarget.CompletionTarget = Post;
var ourType = notification.GetType();
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
State = Visibility.Visible;
updateCounts();
});
protected override void Update()
{
base.Update();
if (processingPosts)
postScheduler.Update();
}
protected override void PopIn()
{
base.PopIn();
this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint);
}
protected override void PopOut()
{
base.PopOut();
markAllRead();
this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
}
private void updateCounts()
{
UnreadCount.Value = unreadCount;
}
private void markAllRead()
{
sections.Children.ForEach(s => s.MarkAllRead());
updateCounts();
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Notifications;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Threading;
namespace osu.Game.Overlays
{
public class NotificationOverlay : OsuFocusedOverlayContainer
{
private const float width = 320;
public const float TRANSITION_LENGTH = 600;
/// <summary>
/// Whether posted notifications should be processed.
/// </summary>
public readonly BindableBool Enabled = new BindableBool(true);
private FlowContainer<NotificationSection> sections;
/// <summary>
/// Provide a source for the toolbar height.
/// </summary>
public Func<float> GetToolbarHeight;
public NotificationOverlay()
{
ScheduledDelegate notificationsEnabler = null;
Enabled.ValueChanged += v =>
{
if (!IsLoaded)
{
processingPosts = v;
return;
}
notificationsEnabler?.Cancel();
if (v)
// we want a slight delay before toggling notifications on to avoid the user becoming overwhelmed.
notificationsEnabler = Scheduler.AddDelayed(() => processingPosts = true, 1000);
else
processingPosts = false;
};
}
[BackgroundDependencyLoader]
private void load()
{
Width = width;
RelativeSizeAxes = Axes.Y;
AlwaysPresent = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.6f
},
new OsuScrollContainer
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
sections = new FillFlowContainer<NotificationSection>
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Children = new[]
{
new NotificationSection
{
Title = @"Notifications",
ClearText = @"Clear All",
AcceptTypes = new[] { typeof(SimpleNotification) }
},
new NotificationSection
{
Title = @"Running Tasks",
ClearText = @"Cancel All",
AcceptTypes = new[] { typeof(ProgressNotification) }
}
}
}
}
}
};
}
private int totalCount => sections.Select(c => c.DisplayedCount).Sum();
private int unreadCount => sections.Select(c => c.UnreadCount).Sum();
public readonly BindableInt UnreadCount = new BindableInt();
private int runningDepth;
private void notificationClosed() => updateCounts();
private readonly Scheduler postScheduler = new Scheduler();
private bool processingPosts = true;
public void Post(Notification notification) => postScheduler.Add(() =>
{
++runningDepth;
notification.Closed += notificationClosed;
var hasCompletionTarget = notification as IHasCompletionTarget;
if (hasCompletionTarget != null)
hasCompletionTarget.CompletionTarget = Post;
var ourType = notification.GetType();
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
State = Visibility.Visible;
updateCounts();
});
protected override void Update()
{
base.Update();
if (processingPosts)
postScheduler.Update();
}
protected override void PopIn()
{
base.PopIn();
this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint);
}
protected override void PopOut()
{
base.PopOut();
markAllRead();
this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint);
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
}
private void updateCounts()
{
UnreadCount.Value = unreadCount;
}
private void markAllRead()
{
sections.Children.ForEach(s => s.MarkAllRead());
updateCounts();
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
}
}

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
namespace osu.Game.Overlays.Notifications
{
public interface IHasCompletionTarget
{
Action<Notification> CompletionTarget { get; set; }
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
namespace osu.Game.Overlays.Notifications
{
public interface IHasCompletionTarget
{
Action<Notification> CompletionTarget { get; set; }
}
}

View File

@ -1,263 +1,263 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Notifications
{
public abstract class Notification : Container
{
/// <summary>
/// User requested close.
/// </summary>
public event Action Closed;
/// <summary>
/// Run on user activating the notification. Return true to close.
/// </summary>
public Func<bool> Activated;
/// <summary>
/// Should we show at the top of our section on display?
/// </summary>
public virtual bool DisplayOnTop => true;
protected NotificationLight Light;
private readonly CloseButton closeButton;
protected Container IconContent;
private readonly Container content;
protected override Container<Drawable> Content => content;
protected Container NotificationContent;
public virtual bool Read { get; set; }
protected Notification()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
Light = new NotificationLight
{
Margin = new MarginPadding { Right = 5 },
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
},
NotificationContent = new Container
{
CornerRadius = 8,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AutoSizeDuration = 400,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
new Container
{
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding(5),
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
IconContent = new Container
{
Size = new Vector2(40),
Masking = true,
CornerRadius = 5,
},
content = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Left = 45,
Right = 30
},
}
}
},
closeButton = new CloseButton
{
Alpha = 0,
Action = Close,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding
{
Right = 5
},
}
}
}
});
}
protected override bool OnHover(InputState state)
{
closeButton.FadeIn(75);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
closeButton.FadeOut(75);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
if (Activated?.Invoke() ?? true)
Close();
return true;
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200);
NotificationContent.MoveToX(DrawSize.X);
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
}
public bool WasClosed;
public virtual void Close()
{
if (WasClosed) return;
WasClosed = true;
Closed?.Invoke();
this.FadeOut(100);
Expire();
}
private class CloseButton : OsuClickableContainer
{
private Color4 hoverColour;
public CloseButton()
{
Colour = OsuColour.Gray(0.2f);
AutoSizeAxes = Axes.Both;
Children = new[]
{
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_times_circle,
Size = new Vector2(20),
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoverColour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
this.FadeColour(hoverColour, 200);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
this.FadeColour(OsuColour.Gray(0.2f), 200);
base.OnHoverLost(state);
}
}
public class NotificationLight : Container
{
private bool pulsate;
private Container pulsateLayer;
public bool Pulsate
{
get { return pulsate; }
set
{
if (pulsate == value) return;
pulsate = value;
pulsateLayer.ClearTransforms();
pulsateLayer.Alpha = 1;
if (pulsate)
{
const float length = 1000;
pulsateLayer.Loop(length / 2,
p => p.FadeTo(0.4f, length, Easing.In).Then().FadeTo(1, length, Easing.Out)
);
}
}
}
public new SRGBColour Colour
{
set
{
base.Colour = value;
pulsateLayer.EdgeEffect = new EdgeEffectParameters
{
Colour = ((Color4)value).Opacity(0.5f), //todo: avoid cast
Type = EdgeEffectType.Glow,
Radius = 12,
Roundness = 12,
};
}
}
[BackgroundDependencyLoader]
private void load()
{
Size = new Vector2(6, 15);
Children = new[]
{
pulsateLayer = new CircularContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
}
}
};
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Notifications
{
public abstract class Notification : Container
{
/// <summary>
/// User requested close.
/// </summary>
public event Action Closed;
/// <summary>
/// Run on user activating the notification. Return true to close.
/// </summary>
public Func<bool> Activated;
/// <summary>
/// Should we show at the top of our section on display?
/// </summary>
public virtual bool DisplayOnTop => true;
protected NotificationLight Light;
private readonly CloseButton closeButton;
protected Container IconContent;
private readonly Container content;
protected override Container<Drawable> Content => content;
protected Container NotificationContent;
public virtual bool Read { get; set; }
protected Notification()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
Light = new NotificationLight
{
Margin = new MarginPadding { Right = 5 },
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
},
NotificationContent = new Container
{
CornerRadius = 8,
Masking = true,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AutoSizeDuration = 400,
AutoSizeEasing = Easing.OutQuint,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
new Container
{
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding(5),
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
IconContent = new Container
{
Size = new Vector2(40),
Masking = true,
CornerRadius = 5,
},
content = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Left = 45,
Right = 30
},
}
}
},
closeButton = new CloseButton
{
Alpha = 0,
Action = Close,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding
{
Right = 5
},
}
}
}
});
}
protected override bool OnHover(InputState state)
{
closeButton.FadeIn(75);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
closeButton.FadeOut(75);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
if (Activated?.Invoke() ?? true)
Close();
return true;
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200);
NotificationContent.MoveToX(DrawSize.X);
NotificationContent.MoveToX(0, 500, Easing.OutQuint);
}
public bool WasClosed;
public virtual void Close()
{
if (WasClosed) return;
WasClosed = true;
Closed?.Invoke();
this.FadeOut(100);
Expire();
}
private class CloseButton : OsuClickableContainer
{
private Color4 hoverColour;
public CloseButton()
{
Colour = OsuColour.Gray(0.2f);
AutoSizeAxes = Axes.Both;
Children = new[]
{
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_times_circle,
Size = new Vector2(20),
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoverColour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
this.FadeColour(hoverColour, 200);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
this.FadeColour(OsuColour.Gray(0.2f), 200);
base.OnHoverLost(state);
}
}
public class NotificationLight : Container
{
private bool pulsate;
private Container pulsateLayer;
public bool Pulsate
{
get { return pulsate; }
set
{
if (pulsate == value) return;
pulsate = value;
pulsateLayer.ClearTransforms();
pulsateLayer.Alpha = 1;
if (pulsate)
{
const float length = 1000;
pulsateLayer.Loop(length / 2,
p => p.FadeTo(0.4f, length, Easing.In).Then().FadeTo(1, length, Easing.Out)
);
}
}
}
public new SRGBColour Colour
{
set
{
base.Colour = value;
pulsateLayer.EdgeEffect = new EdgeEffectParameters
{
Colour = ((Color4)value).Opacity(0.5f), //todo: avoid cast
Type = EdgeEffectType.Glow,
Radius = 12,
Roundness = 12,
};
}
}
[BackgroundDependencyLoader]
private void load()
{
Size = new Vector2(6, 15);
Children = new[]
{
pulsateLayer = new CircularContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
}
}
};
}
}
}
}

View File

@ -1,174 +1,174 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Notifications
{
public class NotificationSection : AlwaysUpdateFillFlowContainer<Drawable>
{
private OsuSpriteText titleText;
private OsuSpriteText countText;
private ClearAllButton clearButton;
private FlowContainer<Notification> notifications;
public int DisplayedCount => notifications.Count(n => !n.WasClosed);
public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read);
public void Add(Notification notification, float position)
{
notifications.Add(notification);
notifications.SetLayoutPosition(notification, position);
}
public IEnumerable<Type> AcceptTypes;
private string clearText;
public string ClearText
{
get { return clearText; }
set
{
clearText = value;
if (clearButton != null) clearButton.Text = clearText;
}
}
private string title;
public string Title
{
get { return title; }
set
{
title = value;
if (titleText != null) titleText.Text = title.ToUpper();
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Padding = new MarginPadding
{
Top = 10,
Bottom = 5,
Right = 20,
Left = 20,
};
AddRangeInternal(new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
clearButton = new ClearAllButton
{
Text = clearText,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Action = clearAll
},
new FillFlowContainer
{
Margin = new MarginPadding
{
Bottom = 5
},
Spacing = new Vector2(5, 0),
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
titleText = new OsuSpriteText
{
Text = title.ToUpper(),
Font = @"Exo2.0-Black",
},
countText = new OsuSpriteText
{
Text = "3",
Colour = colours.Yellow,
Font = @"Exo2.0-Black",
},
}
},
},
},
notifications = new AlwaysUpdateFillFlowContainer<Notification>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
LayoutDuration = 150,
LayoutEasing = Easing.OutQuart,
Spacing = new Vector2(3),
}
});
}
private void clearAll()
{
notifications.Children.ForEach(c => c.Close());
}
protected override void Update()
{
base.Update();
countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString();
}
private class ClearAllButton : OsuClickableContainer
{
private readonly OsuSpriteText text;
public ClearAllButton()
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
text = new OsuSpriteText()
};
}
public string Text
{
get { return text.Text; }
set { text.Text = value.ToUpper(); }
}
}
public void MarkAllRead()
{
notifications?.Children.ForEach(n => n.Read = true);
}
}
public class AlwaysUpdateFillFlowContainer<T> : FillFlowContainer<T>
where T : Drawable
{
// this is required to ensure correct layout and scheduling on children.
// the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297).
protected override bool RequiresChildrenUpdate => true;
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Notifications
{
public class NotificationSection : AlwaysUpdateFillFlowContainer<Drawable>
{
private OsuSpriteText titleText;
private OsuSpriteText countText;
private ClearAllButton clearButton;
private FlowContainer<Notification> notifications;
public int DisplayedCount => notifications.Count(n => !n.WasClosed);
public int UnreadCount => notifications.Count(n => !n.WasClosed && !n.Read);
public void Add(Notification notification, float position)
{
notifications.Add(notification);
notifications.SetLayoutPosition(notification, position);
}
public IEnumerable<Type> AcceptTypes;
private string clearText;
public string ClearText
{
get { return clearText; }
set
{
clearText = value;
if (clearButton != null) clearButton.Text = clearText;
}
}
private string title;
public string Title
{
get { return title; }
set
{
title = value;
if (titleText != null) titleText.Text = title.ToUpper();
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Padding = new MarginPadding
{
Top = 10,
Bottom = 5,
Right = 20,
Left = 20,
};
AddRangeInternal(new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
clearButton = new ClearAllButton
{
Text = clearText,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Action = clearAll
},
new FillFlowContainer
{
Margin = new MarginPadding
{
Bottom = 5
},
Spacing = new Vector2(5, 0),
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
titleText = new OsuSpriteText
{
Text = title.ToUpper(),
Font = @"Exo2.0-Black",
},
countText = new OsuSpriteText
{
Text = "3",
Colour = colours.Yellow,
Font = @"Exo2.0-Black",
},
}
},
},
},
notifications = new AlwaysUpdateFillFlowContainer<Notification>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
LayoutDuration = 150,
LayoutEasing = Easing.OutQuart,
Spacing = new Vector2(3),
}
});
}
private void clearAll()
{
notifications.Children.ForEach(c => c.Close());
}
protected override void Update()
{
base.Update();
countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString();
}
private class ClearAllButton : OsuClickableContainer
{
private readonly OsuSpriteText text;
public ClearAllButton()
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
text = new OsuSpriteText()
};
}
public string Text
{
get { return text.Text; }
set { text.Text = value.ToUpper(); }
}
}
public void MarkAllRead()
{
notifications?.Children.ForEach(n => n.Read = true);
}
}
public class AlwaysUpdateFillFlowContainer<T> : FillFlowContainer<T>
where T : Drawable
{
// this is required to ensure correct layout and scheduling on children.
// the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297).
protected override bool RequiresChildrenUpdate => true;
}
}

View File

@ -1,23 +1,23 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Framework.Graphics.Colour;
namespace osu.Game.Overlays.Notifications
{
public class ProgressCompletionNotification : SimpleNotification
{
public ProgressCompletionNotification()
{
Icon = FontAwesome.fa_check;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IconBackgound.Colour = ColourInfo.GradientVertical(colours.GreenDark, colours.GreenLight);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Framework.Graphics.Colour;
namespace osu.Game.Overlays.Notifications
{
public class ProgressCompletionNotification : SimpleNotification
{
public ProgressCompletionNotification()
{
Icon = FontAwesome.fa_check;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IconBackgound.Colour = ColourInfo.GradientVertical(colours.GreenDark, colours.GreenLight);
}
}
}

View File

@ -1,237 +1,237 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
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 OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Notifications
{
public class ProgressNotification : Notification, IHasCompletionTarget
{
public string Text
{
set
{
Schedule(() => textDrawable.Text = value);
}
}
public string CompletionText { get; set; } = "Task has completed!";
public float Progress
{
get { return progressBar.Progress; }
set { Schedule(() => progressBar.Progress = value); }
}
protected override void LoadComplete()
{
base.LoadComplete();
//we may have received changes before we were displayed.
State = state;
}
public virtual ProgressNotificationState State
{
get { return state; }
set
{
Schedule(() =>
{
bool stateChanged = state != value;
state = value;
if (IsLoaded)
{
switch (state)
{
case ProgressNotificationState.Queued:
Light.Colour = colourQueued;
Light.Pulsate = false;
progressBar.Active = false;
break;
case ProgressNotificationState.Active:
Light.Colour = colourActive;
Light.Pulsate = true;
progressBar.Active = true;
break;
case ProgressNotificationState.Cancelled:
Light.Colour = colourCancelled;
Light.Pulsate = false;
progressBar.Active = false;
break;
}
}
if (stateChanged)
{
switch (state)
{
case ProgressNotificationState.Completed:
NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint);
this.FadeOut(200).Finally(d => Completed());
break;
}
}
});
}
}
private ProgressNotificationState state;
protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification
{
Activated = CompletionClickAction,
Text = CompletionText
};
protected virtual void Completed()
{
CompletionTarget?.Invoke(CreateCompletionNotification());
base.Close();
}
public override bool DisplayOnTop => false;
private readonly ProgressBar progressBar;
private Color4 colourQueued;
private Color4 colourActive;
private Color4 colourCancelled;
private readonly TextFlowContainer textDrawable;
public ProgressNotification()
{
IconContent.Add(new Box
{
RelativeSizeAxes = Axes.Both,
});
Content.Add(textDrawable = new OsuTextFlowContainer(t =>
{
t.TextSize = 16;
})
{
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
});
NotificationContent.Add(progressBar = new ProgressBar
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
});
State = ProgressNotificationState.Queued;
// don't close on click by default.
Activated = () => false;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
colourQueued = colours.YellowDark;
colourActive = colours.Blue;
colourCancelled = colours.Red;
}
public override void Close()
{
switch (State)
{
case ProgressNotificationState.Cancelled:
base.Close();
break;
case ProgressNotificationState.Active:
case ProgressNotificationState.Queued:
if (CancelRequested?.Invoke() != false)
State = ProgressNotificationState.Cancelled;
break;
}
}
public Func<bool> CancelRequested { get; set; }
/// <summary>
/// The function to post completion notifications back to.
/// </summary>
public Action<Notification> CompletionTarget { get; set; }
/// <summary>
/// An action to complete when the completion notification is clicked.
/// </summary>
public Func<bool> CompletionClickAction;
private class ProgressBar : Container
{
private readonly Box box;
private Color4 colourActive;
private Color4 colourInactive;
private float progress;
public float Progress
{
get { return progress; }
set
{
if (progress == value) return;
progress = value;
box.ResizeTo(new Vector2(progress, 1), 100, Easing.OutQuad);
}
}
private bool active;
public bool Active
{
get { return active; }
set
{
active = value;
this.FadeColour(active ? colourActive : colourInactive, 100);
}
}
public ProgressBar()
{
Children = new[]
{
box = new Box
{
RelativeSizeAxes = Axes.Both,
Width = 0,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
colourActive = colours.Blue;
Colour = colourInactive = OsuColour.Gray(0.5f);
Height = 5;
}
}
}
public enum ProgressNotificationState
{
Queued,
Active,
Completed,
Cancelled
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
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 OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Notifications
{
public class ProgressNotification : Notification, IHasCompletionTarget
{
public string Text
{
set
{
Schedule(() => textDrawable.Text = value);
}
}
public string CompletionText { get; set; } = "Task has completed!";
public float Progress
{
get { return progressBar.Progress; }
set { Schedule(() => progressBar.Progress = value); }
}
protected override void LoadComplete()
{
base.LoadComplete();
//we may have received changes before we were displayed.
State = state;
}
public virtual ProgressNotificationState State
{
get { return state; }
set
{
Schedule(() =>
{
bool stateChanged = state != value;
state = value;
if (IsLoaded)
{
switch (state)
{
case ProgressNotificationState.Queued:
Light.Colour = colourQueued;
Light.Pulsate = false;
progressBar.Active = false;
break;
case ProgressNotificationState.Active:
Light.Colour = colourActive;
Light.Pulsate = true;
progressBar.Active = true;
break;
case ProgressNotificationState.Cancelled:
Light.Colour = colourCancelled;
Light.Pulsate = false;
progressBar.Active = false;
break;
}
}
if (stateChanged)
{
switch (state)
{
case ProgressNotificationState.Completed:
NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint);
this.FadeOut(200).Finally(d => Completed());
break;
}
}
});
}
}
private ProgressNotificationState state;
protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification
{
Activated = CompletionClickAction,
Text = CompletionText
};
protected virtual void Completed()
{
CompletionTarget?.Invoke(CreateCompletionNotification());
base.Close();
}
public override bool DisplayOnTop => false;
private readonly ProgressBar progressBar;
private Color4 colourQueued;
private Color4 colourActive;
private Color4 colourCancelled;
private readonly TextFlowContainer textDrawable;
public ProgressNotification()
{
IconContent.Add(new Box
{
RelativeSizeAxes = Axes.Both,
});
Content.Add(textDrawable = new OsuTextFlowContainer(t =>
{
t.TextSize = 16;
})
{
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
});
NotificationContent.Add(progressBar = new ProgressBar
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
});
State = ProgressNotificationState.Queued;
// don't close on click by default.
Activated = () => false;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
colourQueued = colours.YellowDark;
colourActive = colours.Blue;
colourCancelled = colours.Red;
}
public override void Close()
{
switch (State)
{
case ProgressNotificationState.Cancelled:
base.Close();
break;
case ProgressNotificationState.Active:
case ProgressNotificationState.Queued:
if (CancelRequested?.Invoke() != false)
State = ProgressNotificationState.Cancelled;
break;
}
}
public Func<bool> CancelRequested { get; set; }
/// <summary>
/// The function to post completion notifications back to.
/// </summary>
public Action<Notification> CompletionTarget { get; set; }
/// <summary>
/// An action to complete when the completion notification is clicked.
/// </summary>
public Func<bool> CompletionClickAction;
private class ProgressBar : Container
{
private readonly Box box;
private Color4 colourActive;
private Color4 colourInactive;
private float progress;
public float Progress
{
get { return progress; }
set
{
if (progress == value) return;
progress = value;
box.ResizeTo(new Vector2(progress, 1), 100, Easing.OutQuad);
}
}
private bool active;
public bool Active
{
get { return active; }
set
{
active = value;
this.FadeColour(active ? colourActive : colourInactive, 100);
}
}
public ProgressBar()
{
Children = new[]
{
box = new Box
{
RelativeSizeAxes = Axes.Both,
Width = 0,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
colourActive = colours.Blue;
Colour = colourInactive = OsuColour.Gray(0.5f);
Height = 5;
}
}
}
public enum ProgressNotificationState
{
Queued,
Active,
Completed,
Cancelled
}
}

View File

@ -1,93 +1,93 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
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.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Notifications
{
public class SimpleNotification : Notification
{
private string text = string.Empty;
public string Text
{
get { return text; }
set
{
text = value;
textDrawable.Text = text;
}
}
private FontAwesome icon = FontAwesome.fa_info_circle;
public FontAwesome Icon
{
get { return icon; }
set
{
icon = value;
iconDrawable.Icon = icon;
}
}
private readonly TextFlowContainer textDrawable;
private readonly SpriteIcon iconDrawable;
protected Box IconBackgound;
public SimpleNotification()
{
IconContent.AddRange(new Drawable[]
{
IconBackgound = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.6f))
},
iconDrawable = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(20),
}
});
Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16)
{
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Text = text
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Light.Colour = colours.Green;
}
public override bool Read
{
get
{
return base.Read;
}
set
{
if (value == base.Read) return;
base.Read = value;
Light.FadeTo(value ? 0 : 1, 100);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
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.Graphics.Containers;
using OpenTK;
namespace osu.Game.Overlays.Notifications
{
public class SimpleNotification : Notification
{
private string text = string.Empty;
public string Text
{
get { return text; }
set
{
text = value;
textDrawable.Text = text;
}
}
private FontAwesome icon = FontAwesome.fa_info_circle;
public FontAwesome Icon
{
get { return icon; }
set
{
icon = value;
iconDrawable.Icon = icon;
}
}
private readonly TextFlowContainer textDrawable;
private readonly SpriteIcon iconDrawable;
protected Box IconBackgound;
public SimpleNotification()
{
IconContent.AddRange(new Drawable[]
{
IconBackgound = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.6f))
},
iconDrawable = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(20),
}
});
Content.Add(textDrawable = new OsuTextFlowContainer(t => t.TextSize = 16)
{
Colour = OsuColour.Gray(128),
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Text = text
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Light.Colour = colours.Green;
}
public override bool Read
{
get
{
return base.Read;
}
set
{
if (value == base.Read) return;
base.Read = value;
Light.FadeTo(value ? 0 : 1, 100);
}
}
}
}

View File

@ -1,291 +1,291 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays
{
public class OnScreenDisplay : Container
{
private readonly Container box;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
private readonly SpriteText textLine1;
private readonly SpriteText textLine2;
private readonly SpriteText textLine3;
private const float height = 110;
private const float height_contracted = height * 0.9f;
private readonly FillFlowContainer<OptionLight> optionLights;
public OnScreenDisplay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
box = new Container
{
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Both,
Position = new Vector2(0.5f, 0.75f),
Masking = true,
AutoSizeAxes = Axes.X,
Height = height_contracted,
Alpha = 0,
CornerRadius = 20,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.7f,
},
new Container // purely to add a minimum width
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 240,
RelativeSizeAxes = Axes.Y,
},
textLine1 = new OsuSpriteText
{
Padding = new MarginPadding(10),
Font = @"Exo2.0-Black",
Spacing = new Vector2(1, 0),
TextSize = 14,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
textLine2 = new OsuSpriteText
{
TextSize = 24,
Font = @"Exo2.0-Light",
Padding = new MarginPadding { Left = 10, Right = 10 },
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
},
new FillFlowContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
optionLights = new FillFlowContainer<OptionLight>
{
Padding = new MarginPadding { Top = 20, Bottom = 5 },
Spacing = new Vector2(5, 0),
Direction = FillDirection.Horizontal,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both
},
textLine3 = new OsuSpriteText
{
Padding = new MarginPadding { Bottom = 15 },
Font = @"Exo2.0-Bold",
TextSize = 12,
Alpha = 0.3f,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
}
}
}
},
};
}
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig)
{
BeginTracking(this, frameworkConfig);
}
private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>();
/// <summary>
/// Registers a <see cref="ConfigManager{T}"/> to have its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that is registering the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
public void BeginTracking(object source, ITrackableConfigManager configManager)
{
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (trackedConfigManagers.ContainsKey((source, configManager)))
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
var trackedSettings = configManager.CreateTrackedSettings();
if (trackedSettings == null)
return;
configManager.LoadInto(trackedSettings);
trackedSettings.SettingChanged += display;
trackedConfigManagers.Add((source, configManager), trackedSettings);
}
/// <summary>
/// Unregisters a <see cref="ConfigManager{T}"/> from having its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that registered the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> that is being tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
public void StopTracking(object source, ITrackableConfigManager configManager)
{
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
existing.Unload();
existing.SettingChanged -= display;
trackedConfigManagers.Remove((source, configManager));
}
private void display(SettingDescription description)
{
Schedule(() =>
{
textLine1.Text = description.Name.ToUpper();
textLine2.Text = description.Value;
textLine3.Text = description.Shortcut.ToUpper();
box.Animate(
b => b.FadeIn(500, Easing.OutQuint),
b => b.ResizeHeightTo(height, 500, Easing.OutQuint)
).Then(
b => b.FadeOutFromOne(1500, Easing.InQuint),
b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint)
);
int optionCount = 0;
int selectedOption = -1;
if (description.RawValue is bool)
{
optionCount = 1;
if ((bool)description.RawValue) selectedOption = 0;
}
else if (description.RawValue is Enum)
{
var values = Enum.GetValues(description.RawValue.GetType());
optionCount = values.Length;
selectedOption = Convert.ToInt32(description.RawValue);
}
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
textLine2.Y = optionCount > 0 ? 0 : 5;
if (optionLights.Children.Count != optionCount)
{
optionLights.Clear();
for (int i = 0; i < optionCount; i++)
optionLights.Add(new OptionLight());
}
for (int i = 0; i < optionCount; i++)
optionLights.Children[i].Glowing = i == selectedOption;
});
}
private class OptionLight : Container
{
private Color4 glowingColour, idleColour;
private const float transition_speed = 300;
private const float glow_strength = 0.4f;
private readonly Box fill;
public OptionLight()
{
Children = new[]
{
fill = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
},
};
}
private bool glowing;
public bool Glowing
{
set
{
glowing = value;
if (!IsLoaded) return;
updateGlow();
}
}
private void updateGlow()
{
if (glowing)
{
fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint);
FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint);
}
else
{
FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint);
fill.FadeColour(idleColour, transition_speed, Easing.OutQuint);
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
fill.Colour = idleColour = Color4.White.Opacity(0.4f);
glowingColour = Color4.White;
Size = new Vector2(25, 5);
Masking = true;
CornerRadius = 3;
EdgeEffect = new EdgeEffectParameters
{
Colour = colours.BlueDark.Opacity(glow_strength),
Type = EdgeEffectType.Glow,
Radius = 8,
};
}
protected override void LoadComplete()
{
updateGlow();
FinishTransforms(true);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays
{
public class OnScreenDisplay : Container
{
private readonly Container box;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
private readonly SpriteText textLine1;
private readonly SpriteText textLine2;
private readonly SpriteText textLine3;
private const float height = 110;
private const float height_contracted = height * 0.9f;
private readonly FillFlowContainer<OptionLight> optionLights;
public OnScreenDisplay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
box = new Container
{
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Both,
Position = new Vector2(0.5f, 0.75f),
Masking = true,
AutoSizeAxes = Axes.X,
Height = height_contracted,
Alpha = 0,
CornerRadius = 20,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.7f,
},
new Container // purely to add a minimum width
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 240,
RelativeSizeAxes = Axes.Y,
},
textLine1 = new OsuSpriteText
{
Padding = new MarginPadding(10),
Font = @"Exo2.0-Black",
Spacing = new Vector2(1, 0),
TextSize = 14,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
textLine2 = new OsuSpriteText
{
TextSize = 24,
Font = @"Exo2.0-Light",
Padding = new MarginPadding { Left = 10, Right = 10 },
Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre,
},
new FillFlowContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
optionLights = new FillFlowContainer<OptionLight>
{
Padding = new MarginPadding { Top = 20, Bottom = 5 },
Spacing = new Vector2(5, 0),
Direction = FillDirection.Horizontal,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both
},
textLine3 = new OsuSpriteText
{
Padding = new MarginPadding { Bottom = 15 },
Font = @"Exo2.0-Bold",
TextSize = 12,
Alpha = 0.3f,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
}
}
}
},
};
}
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig)
{
BeginTracking(this, frameworkConfig);
}
private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>();
/// <summary>
/// Registers a <see cref="ConfigManager{T}"/> to have its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that is registering the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
public void BeginTracking(object source, ITrackableConfigManager configManager)
{
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (trackedConfigManagers.ContainsKey((source, configManager)))
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
var trackedSettings = configManager.CreateTrackedSettings();
if (trackedSettings == null)
return;
configManager.LoadInto(trackedSettings);
trackedSettings.SettingChanged += display;
trackedConfigManagers.Add((source, configManager), trackedSettings);
}
/// <summary>
/// Unregisters a <see cref="ConfigManager{T}"/> from having its settings tracked by this <see cref="OnScreenDisplay"/>.
/// </summary>
/// <param name="source">The object that registered the <see cref="ConfigManager{T}"/> to be tracked.</param>
/// <param name="configManager">The <see cref="ConfigManager{T}"/> that is being tracked.</param>
/// <exception cref="ArgumentNullException">If <paramref name="configManager"/> is null.</exception>
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
public void StopTracking(object source, ITrackableConfigManager configManager)
{
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
existing.Unload();
existing.SettingChanged -= display;
trackedConfigManagers.Remove((source, configManager));
}
private void display(SettingDescription description)
{
Schedule(() =>
{
textLine1.Text = description.Name.ToUpper();
textLine2.Text = description.Value;
textLine3.Text = description.Shortcut.ToUpper();
box.Animate(
b => b.FadeIn(500, Easing.OutQuint),
b => b.ResizeHeightTo(height, 500, Easing.OutQuint)
).Then(
b => b.FadeOutFromOne(1500, Easing.InQuint),
b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint)
);
int optionCount = 0;
int selectedOption = -1;
if (description.RawValue is bool)
{
optionCount = 1;
if ((bool)description.RawValue) selectedOption = 0;
}
else if (description.RawValue is Enum)
{
var values = Enum.GetValues(description.RawValue.GetType());
optionCount = values.Length;
selectedOption = Convert.ToInt32(description.RawValue);
}
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
textLine2.Y = optionCount > 0 ? 0 : 5;
if (optionLights.Children.Count != optionCount)
{
optionLights.Clear();
for (int i = 0; i < optionCount; i++)
optionLights.Add(new OptionLight());
}
for (int i = 0; i < optionCount; i++)
optionLights.Children[i].Glowing = i == selectedOption;
});
}
private class OptionLight : Container
{
private Color4 glowingColour, idleColour;
private const float transition_speed = 300;
private const float glow_strength = 0.4f;
private readonly Box fill;
public OptionLight()
{
Children = new[]
{
fill = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
},
};
}
private bool glowing;
public bool Glowing
{
set
{
glowing = value;
if (!IsLoaded) return;
updateGlow();
}
}
private void updateGlow()
{
if (glowing)
{
fill.FadeColour(glowingColour, transition_speed, Easing.OutQuint);
FadeEdgeEffectTo(glow_strength, transition_speed, Easing.OutQuint);
}
else
{
FadeEdgeEffectTo(0, transition_speed, Easing.OutQuint);
fill.FadeColour(idleColour, transition_speed, Easing.OutQuint);
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
fill.Colour = idleColour = Color4.White.Opacity(0.4f);
glowingColour = Color4.White;
Size = new Vector2(25, 5);
Masking = true;
CornerRadius = 3;
EdgeEffect = new EdgeEffectParameters
{
Colour = colours.BlueDark.Opacity(glow_strength),
Type = EdgeEffectType.Glow,
Radius = 8,
};
}
protected override void LoadComplete()
{
updateGlow();
FinishTransforms(true);
}
}
}
}

View File

@ -1,499 +1,499 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile
{
public class ProfileHeader : Container
{
private readonly OsuTextFlowContainer infoTextLeft;
private readonly LinkFlowContainer infoTextRight;
private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText;
private readonly RankGraph rankGraph;
public readonly SupporterIcon SupporterTag;
private readonly Container coverContainer;
private readonly Sprite levelBadge;
private readonly SpriteText levelText;
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar;
private readonly DrawableFlag countryFlag;
private const float cover_height = 350;
private const float info_height = 150;
private const float info_width = 220;
private const float avatar_size = 110;
private const float level_position = 30;
private const float level_height = 60;
public ProfileHeader(User user)
{
RelativeSizeAxes = Axes.X;
Height = cover_height + info_height;
Children = new Drawable[]
{
coverContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = cover_height,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f))
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = -20,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new UpdateableAvatar
{
User = user,
Size = new Vector2(avatar_size),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
},
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = avatar_size + 10,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
SupporterTag = new SupporterIcon
{
Alpha = 0,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -75,
Size = new Vector2(25, 25)
},
new ProfileLink(user)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -48,
},
countryFlag = new DrawableFlag(user.Country)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Width = 30,
Height = 20
}
}
}
}
},
colourBar = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN,
Height = 5,
Width = info_width,
Alpha = 0
}
}
},
infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14)
{
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20,
Width = info_width,
AutoSizeAxes = Axes.Y,
ParagraphSpacing = 0.8f,
LineSpacing = 0.2f
},
infoTextRight = new LinkFlowContainer(t =>
{
t.TextSize = 14;
t.Font = @"Exo2.0-RegularItalic";
})
{
X = UserProfileOverlay.CONTENT_X_MARGIN + info_width + 20,
Y = cover_height + 20,
Width = info_width,
AutoSizeAxes = Axes.Y,
ParagraphSpacing = 0.8f,
LineSpacing = 0.2f
},
new Container
{
X = -UserProfileOverlay.CONTENT_X_MARGIN,
RelativeSizeAxes = Axes.Y,
Width = 280,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Y = level_position,
Height = level_height,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black.Opacity(0.5f),
RelativeSizeAxes = Axes.Both
},
levelBadge = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Height = 50,
Width = 50,
Alpha = 0
},
levelText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 11,
TextSize = 20
}
}
},
new Container
{
RelativeSizeAxes = Axes.X,
Y = cover_height,
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Height = cover_height - level_height - level_position - 5,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black.Opacity(0.5f),
RelativeSizeAxes = Axes.Both
},
scoreText = new FillFlowContainer<SpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = 20, Vertical = 18 },
Spacing = new Vector2(0, 2)
},
scoreNumberText = new FillFlowContainer<SpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = 20, Vertical = 18 },
Spacing = new Vector2(0, 2)
},
new FillFlowContainer<GradeBadge>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Y = -64,
Spacing = new Vector2(20, 0),
Children = new[]
{
gradeSSPlus = new GradeBadge("SSPlus") { Alpha = 0 },
gradeSS = new GradeBadge("SS") { Alpha = 0 },
}
},
new FillFlowContainer<GradeBadge>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Y = -18,
Spacing = new Vector2(20, 0),
Children = new[]
{
gradeSPlus = new GradeBadge("SPlus") { Alpha = 0 },
gradeS = new GradeBadge("S") { Alpha = 0 },
gradeA = new GradeBadge("A") { Alpha = 0 },
}
}
}
},
new Container
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Height = info_height - 15,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black.Opacity(0.25f),
RelativeSizeAxes = Axes.Both
},
rankGraph = new RankGraph
{
RelativeSizeAxes = Axes.Both
}
}
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
levelBadge.Texture = textures.Get(@"Profile/levelbadge");
}
private User user;
public User User
{
get { return user; }
set
{
user = value;
loadUser();
}
}
private void loadUser()
{
LoadComponentAsync(new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200),
Depth = float.MaxValue,
}, coverContainer.Add);
if (user.IsSupporter)
SupporterTag.Show();
if (!string.IsNullOrEmpty(user.Colour))
{
colourBar.Colour = OsuColour.FromHex(user.Colour);
colourBar.Show();
}
void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic";
void lightText(SpriteText t) => t.Alpha = 0.8f;
OsuSpriteText createScoreText(string text) => new OsuSpriteText
{
TextSize = 14,
Text = text
};
OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = text
};
if (user.Age != null)
{
infoTextLeft.AddText($"{user.Age} years old ", boldItalic);
}
if (user.Country != null)
{
infoTextLeft.AddText("from ", lightText);
infoTextLeft.AddText(user.Country.FullName, boldItalic);
countryFlag.Country = user.Country;
}
infoTextLeft.NewParagraph();
if (user.JoinDate.ToUniversalTime().Year < 2008)
{
infoTextLeft.AddText("Here since the beginning", boldItalic);
}
else
{
infoTextLeft.AddText("Joined ", lightText);
infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic);
}
infoTextLeft.NewLine();
infoTextLeft.AddText("Last seen ", lightText);
infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic);
infoTextLeft.NewParagraph();
if (user.PlayStyle?.Length > 0)
{
infoTextLeft.AddText("Plays with ", lightText);
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
}
string websiteWithoutProtcol = user.Website;
if (!string.IsNullOrEmpty(websiteWithoutProtcol))
{
int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal);
if (protocolIndex >= 0)
websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2);
}
tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location);
tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Interests);
tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation);
infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website);
tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat");
if (user.Statistics != null)
{
levelBadge.Show();
levelText.Text = user.Statistics.Level.Current.ToString();
scoreText.Add(createScoreText("Ranked Score"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0")));
scoreText.Add(createScoreText("Accuracy"));
scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy:0.##}%"));
scoreText.Add(createScoreText("Play Count"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0")));
scoreText.Add(createScoreText("Total Score"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalScore.ToString(@"#,0")));
scoreText.Add(createScoreText("Total Hits"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0")));
scoreText.Add(createScoreText("Max Combo"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0")));
scoreText.Add(createScoreText("Replays Watched by Others"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0")));
gradeSSPlus.DisplayCount = user.Statistics.GradesCount.SSPlus;
gradeSSPlus.Show();
gradeSS.DisplayCount = user.Statistics.GradesCount.SS;
gradeSS.Show();
gradeSPlus.DisplayCount = user.Statistics.GradesCount.SPlus;
gradeSPlus.Show();
gradeS.DisplayCount = user.Statistics.GradesCount.S;
gradeS.Show();
gradeA.DisplayCount = user.Statistics.GradesCount.A;
gradeA.Show();
rankGraph.User.Value = user;
}
}
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)
{
if (string.IsNullOrEmpty(str)) return;
infoTextRight.AddIcon(icon);
if (url != null)
{
infoTextRight.AddLink(" " + str, url);
}
else
{
infoTextRight.AddText(" " + str);
}
infoTextRight.NewLine();
}
private class ProfileLink : OsuHoverContainer, IHasTooltip
{
public string TooltipText => "View Profile in Browser";
public override bool HandleMouseInput => true;
public ProfileLink(User user)
{
Action = () => Process.Start($@"https://osu.ppy.sh/users/{user.Id}");
AutoSizeAxes = Axes.Both;
Child = new OsuSpriteText
{
Text = user.Username,
Font = @"Exo2.0-RegularItalic",
TextSize = 30,
};
}
}
private class GradeBadge : Container
{
private const float width = 50;
private readonly string grade;
private readonly Sprite badge;
private readonly SpriteText numberText;
public int DisplayCount
{
set { numberText.Text = value.ToString(@"#,0"); }
}
public GradeBadge(string grade)
{
this.grade = grade;
Width = width;
Height = 41;
Add(badge = new Sprite
{
Width = width,
Height = 26
});
Add(numberText = new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
TextSize = 14,
Font = @"Exo2.0-Bold"
});
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
badge.Texture = textures.Get($"Grades/{grade}");
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile
{
public class ProfileHeader : Container
{
private readonly OsuTextFlowContainer infoTextLeft;
private readonly LinkFlowContainer infoTextRight;
private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText;
private readonly RankGraph rankGraph;
public readonly SupporterIcon SupporterTag;
private readonly Container coverContainer;
private readonly Sprite levelBadge;
private readonly SpriteText levelText;
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar;
private readonly DrawableFlag countryFlag;
private const float cover_height = 350;
private const float info_height = 150;
private const float info_width = 220;
private const float avatar_size = 110;
private const float level_position = 30;
private const float level_height = 60;
public ProfileHeader(User user)
{
RelativeSizeAxes = Axes.X;
Height = cover_height + info_height;
Children = new Drawable[]
{
coverContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = cover_height,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.75f))
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = -20,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new UpdateableAvatar
{
User = user,
Size = new Vector2(avatar_size),
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
},
},
new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = avatar_size + 10,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
SupporterTag = new SupporterIcon
{
Alpha = 0,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -75,
Size = new Vector2(25, 25)
},
new ProfileLink(user)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Y = -48,
},
countryFlag = new DrawableFlag(user.Country)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Width = 30,
Height = 20
}
}
}
}
},
colourBar = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN,
Height = 5,
Width = info_width,
Alpha = 0
}
}
},
infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14)
{
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20,
Width = info_width,
AutoSizeAxes = Axes.Y,
ParagraphSpacing = 0.8f,
LineSpacing = 0.2f
},
infoTextRight = new LinkFlowContainer(t =>
{
t.TextSize = 14;
t.Font = @"Exo2.0-RegularItalic";
})
{
X = UserProfileOverlay.CONTENT_X_MARGIN + info_width + 20,
Y = cover_height + 20,
Width = info_width,
AutoSizeAxes = Axes.Y,
ParagraphSpacing = 0.8f,
LineSpacing = 0.2f
},
new Container
{
X = -UserProfileOverlay.CONTENT_X_MARGIN,
RelativeSizeAxes = Axes.Y,
Width = 280,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Y = level_position,
Height = level_height,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black.Opacity(0.5f),
RelativeSizeAxes = Axes.Both
},
levelBadge = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Height = 50,
Width = 50,
Alpha = 0
},
levelText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 11,
TextSize = 20
}
}
},
new Container
{
RelativeSizeAxes = Axes.X,
Y = cover_height,
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
Height = cover_height - level_height - level_position - 5,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black.Opacity(0.5f),
RelativeSizeAxes = Axes.Both
},
scoreText = new FillFlowContainer<SpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = 20, Vertical = 18 },
Spacing = new Vector2(0, 2)
},
scoreNumberText = new FillFlowContainer<SpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = 20, Vertical = 18 },
Spacing = new Vector2(0, 2)
},
new FillFlowContainer<GradeBadge>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Y = -64,
Spacing = new Vector2(20, 0),
Children = new[]
{
gradeSSPlus = new GradeBadge("SSPlus") { Alpha = 0 },
gradeSS = new GradeBadge("SS") { Alpha = 0 },
}
},
new FillFlowContainer<GradeBadge>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Y = -18,
Spacing = new Vector2(20, 0),
Children = new[]
{
gradeSPlus = new GradeBadge("SPlus") { Alpha = 0 },
gradeS = new GradeBadge("S") { Alpha = 0 },
gradeA = new GradeBadge("A") { Alpha = 0 },
}
}
}
},
new Container
{
RelativeSizeAxes = Axes.X,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Height = info_height - 15,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black.Opacity(0.25f),
RelativeSizeAxes = Axes.Both
},
rankGraph = new RankGraph
{
RelativeSizeAxes = Axes.Both
}
}
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
levelBadge.Texture = textures.Get(@"Profile/levelbadge");
}
private User user;
public User User
{
get { return user; }
set
{
user = value;
loadUser();
}
}
private void loadUser()
{
LoadComponentAsync(new UserCoverBackground(user)
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200),
Depth = float.MaxValue,
}, coverContainer.Add);
if (user.IsSupporter)
SupporterTag.Show();
if (!string.IsNullOrEmpty(user.Colour))
{
colourBar.Colour = OsuColour.FromHex(user.Colour);
colourBar.Show();
}
void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic";
void lightText(SpriteText t) => t.Alpha = 0.8f;
OsuSpriteText createScoreText(string text) => new OsuSpriteText
{
TextSize = 14,
Text = text
};
OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Text = text
};
if (user.Age != null)
{
infoTextLeft.AddText($"{user.Age} years old ", boldItalic);
}
if (user.Country != null)
{
infoTextLeft.AddText("from ", lightText);
infoTextLeft.AddText(user.Country.FullName, boldItalic);
countryFlag.Country = user.Country;
}
infoTextLeft.NewParagraph();
if (user.JoinDate.ToUniversalTime().Year < 2008)
{
infoTextLeft.AddText("Here since the beginning", boldItalic);
}
else
{
infoTextLeft.AddText("Joined ", lightText);
infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic);
}
infoTextLeft.NewLine();
infoTextLeft.AddText("Last seen ", lightText);
infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic);
infoTextLeft.NewParagraph();
if (user.PlayStyle?.Length > 0)
{
infoTextLeft.AddText("Plays with ", lightText);
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
}
string websiteWithoutProtcol = user.Website;
if (!string.IsNullOrEmpty(websiteWithoutProtcol))
{
int protocolIndex = websiteWithoutProtcol.IndexOf("//", StringComparison.Ordinal);
if (protocolIndex >= 0)
websiteWithoutProtcol = websiteWithoutProtcol.Substring(protocolIndex + 2);
}
tryAddInfoRightLine(FontAwesome.fa_map_marker, user.Location);
tryAddInfoRightLine(FontAwesome.fa_heart_o, user.Interests);
tryAddInfoRightLine(FontAwesome.fa_suitcase, user.Occupation);
infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website);
tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat");
if (user.Statistics != null)
{
levelBadge.Show();
levelText.Text = user.Statistics.Level.Current.ToString();
scoreText.Add(createScoreText("Ranked Score"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.RankedScore.ToString(@"#,0")));
scoreText.Add(createScoreText("Accuracy"));
scoreNumberText.Add(createScoreNumberText($"{user.Statistics.Accuracy:0.##}%"));
scoreText.Add(createScoreText("Play Count"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.PlayCount.ToString(@"#,0")));
scoreText.Add(createScoreText("Total Score"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalScore.ToString(@"#,0")));
scoreText.Add(createScoreText("Total Hits"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0")));
scoreText.Add(createScoreText("Max Combo"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0")));
scoreText.Add(createScoreText("Replays Watched by Others"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0")));
gradeSSPlus.DisplayCount = user.Statistics.GradesCount.SSPlus;
gradeSSPlus.Show();
gradeSS.DisplayCount = user.Statistics.GradesCount.SS;
gradeSS.Show();
gradeSPlus.DisplayCount = user.Statistics.GradesCount.SPlus;
gradeSPlus.Show();
gradeS.DisplayCount = user.Statistics.GradesCount.S;
gradeS.Show();
gradeA.DisplayCount = user.Statistics.GradesCount.A;
gradeA.Show();
rankGraph.User.Value = user;
}
}
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)
{
if (string.IsNullOrEmpty(str)) return;
infoTextRight.AddIcon(icon);
if (url != null)
{
infoTextRight.AddLink(" " + str, url);
}
else
{
infoTextRight.AddText(" " + str);
}
infoTextRight.NewLine();
}
private class ProfileLink : OsuHoverContainer, IHasTooltip
{
public string TooltipText => "View Profile in Browser";
public override bool HandleMouseInput => true;
public ProfileLink(User user)
{
Action = () => Process.Start($@"https://osu.ppy.sh/users/{user.Id}");
AutoSizeAxes = Axes.Both;
Child = new OsuSpriteText
{
Text = user.Username,
Font = @"Exo2.0-RegularItalic",
TextSize = 30,
};
}
}
private class GradeBadge : Container
{
private const float width = 50;
private readonly string grade;
private readonly Sprite badge;
private readonly SpriteText numberText;
public int DisplayCount
{
set { numberText.Text = value.ToString(@"#,0"); }
}
public GradeBadge(string grade)
{
this.grade = grade;
Width = width;
Height = 41;
Add(badge = new Sprite
{
Width = width,
Height = 26
});
Add(numberText = new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
TextSize = 14,
Font = @"Exo2.0-Bold"
});
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
badge.Texture = textures.Get($"Grades/{grade}");
}
}
}
}

View File

@ -1,79 +1,79 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using OpenTK.Graphics;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile
{
public abstract class ProfileSection : FillFlowContainer
{
public abstract string Title { get; }
public abstract string Identifier { get; }
private readonly FillFlowContainer content;
protected override Container<Drawable> Content => content;
public readonly Bindable<User> User = new Bindable<User>();
protected ProfileSection()
{
Direction = FillDirection.Vertical;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new OsuSpriteText
{
Text = Title,
TextSize = 20,
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Vertical = 10
}
},
content = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Bottom = 20
}
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(34),
EdgeSmoothness = new Vector2(1)
}
};
// placeholder
Add(new OsuSpriteText
{
Text = @"coming soon!",
TextSize = 16,
Font = @"Exo2.0-Medium",
Colour = Color4.Gray,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Top = 100, Bottom = 100 }
});
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using OpenTK.Graphics;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile
{
public abstract class ProfileSection : FillFlowContainer
{
public abstract string Title { get; }
public abstract string Identifier { get; }
private readonly FillFlowContainer content;
protected override Container<Drawable> Content => content;
public readonly Bindable<User> User = new Bindable<User>();
protected ProfileSection()
{
Direction = FillDirection.Vertical;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new OsuSpriteText
{
Text = Title,
TextSize = 20,
Font = @"Exo2.0-RegularItalic",
Margin = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Vertical = 10
}
},
content = new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
{
Horizontal = UserProfileOverlay.CONTENT_X_MARGIN,
Bottom = 20
}
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(34),
EdgeSmoothness = new Vector2(1)
}
};
// placeholder
Add(new OsuSpriteText
{
Text = @"coming soon!",
TextSize = 16,
Font = @"Exo2.0-Medium",
Colour = Color4.Gray,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Top = 100, Bottom = 100 }
});
}
}
}

View File

@ -1,219 +1,219 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using OpenTK;
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.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Users;
using System.Collections.Generic;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile
{
public class RankGraph : Container
{
private const float primary_textsize = 25;
private const float secondary_textsize = 13;
private const float padding = 10;
private const float fade_duration = 150;
private const int ranked_days = 88;
private readonly SpriteText rankText, performanceText, relativeText;
private readonly RankChartLineGraph graph;
private readonly OsuSpriteText placeholder;
private KeyValuePair<int, int>[] ranks;
public Bindable<User> User = new Bindable<User>();
public RankGraph()
{
Padding = new MarginPadding { Vertical = padding };
Children = new Drawable[]
{
placeholder = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "No recent plays",
TextSize = 14,
Font = @"Exo2.0-RegularItalic",
},
rankText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = @"Exo2.0-RegularItalic",
TextSize = primary_textsize
},
relativeText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = @"Exo2.0-RegularItalic",
Y = 25,
TextSize = secondary_textsize
},
performanceText = new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = @"Exo2.0-RegularItalic",
TextSize = secondary_textsize
},
graph = new RankChartLineGraph
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = 75,
Y = -secondary_textsize,
Alpha = 0,
}
};
graph.OnBallMove += showHistoryRankTexts;
User.ValueChanged += userChanged;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
graph.Colour = colours.Yellow;
}
private void userChanged(User user)
{
placeholder.FadeIn(fade_duration, Easing.Out);
if (user?.Statistics?.Ranks.Global == null)
{
rankText.Text = string.Empty;
performanceText.Text = string.Empty;
relativeText.Text = string.Empty;
graph.FadeOut(fade_duration, Easing.Out);
ranks = null;
return;
}
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value };
ranks = userRanks.Select((x, index) => new KeyValuePair<int, int>(index, x)).Where(x => x.Value != 0).ToArray();
if (ranks.Length > 1)
{
placeholder.FadeOut(fade_duration, Easing.Out);
graph.DefaultValueCount = ranks.Length;
graph.Values = ranks.Select(x => -(float)Math.Log(x.Value));
graph.SetStaticBallPosition();
}
graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out);
updateRankTexts();
}
private void updateRankTexts()
{
var user = User.Value;
performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty;
rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank";
relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank";
}
private void showHistoryRankTexts(int dayIndex)
{
rankText.Text = $"#{ranks[dayIndex].Value:#,0}";
relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago";
}
protected override bool OnHover(InputState state)
{
if (ranks?.Length > 1)
{
graph.UpdateBallPosition(state.Mouse.Position.X);
graph.ShowBall();
}
return base.OnHover(state);
}
protected override bool OnMouseMove(InputState state)
{
if (ranks?.Length > 1)
graph.UpdateBallPosition(state.Mouse.Position.X);
return base.OnMouseMove(state);
}
protected override void OnHoverLost(InputState state)
{
if (ranks?.Length > 1)
{
graph.HideBall();
updateRankTexts();
}
base.OnHoverLost(state);
}
private class RankChartLineGraph : LineGraph
{
private readonly CircularContainer staticBall;
private readonly CircularContainer movingBall;
public Action<int> OnBallMove;
public RankChartLineGraph()
{
Add(staticBall = new CircularContainer
{
Origin = Anchor.Centre,
Size = new Vector2(8),
Masking = true,
RelativePositionAxes = Axes.Both,
Child = new Box { RelativeSizeAxes = Axes.Both }
});
Add(movingBall = new CircularContainer
{
Origin = Anchor.Centre,
Size = new Vector2(8),
Alpha = 0,
Masking = true,
RelativePositionAxes = Axes.Both,
Child = new Box { RelativeSizeAxes = Axes.Both }
});
}
public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last()));
public void UpdateBallPosition(float mouseXPosition)
{
int index = calculateIndex(mouseXPosition);
movingBall.Position = calculateBallPosition(index);
OnBallMove.Invoke(index);
}
public void ShowBall() => movingBall.FadeIn(fade_duration);
public void HideBall() => movingBall.FadeOut(fade_duration);
private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1));
private Vector2 calculateBallPosition(int index)
{
float y = GetYPosition(Values.ElementAt(index));
return new Vector2(index / (float)(DefaultValueCount - 1), y);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using OpenTK;
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.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Users;
using System.Collections.Generic;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile
{
public class RankGraph : Container
{
private const float primary_textsize = 25;
private const float secondary_textsize = 13;
private const float padding = 10;
private const float fade_duration = 150;
private const int ranked_days = 88;
private readonly SpriteText rankText, performanceText, relativeText;
private readonly RankChartLineGraph graph;
private readonly OsuSpriteText placeholder;
private KeyValuePair<int, int>[] ranks;
public Bindable<User> User = new Bindable<User>();
public RankGraph()
{
Padding = new MarginPadding { Vertical = padding };
Children = new Drawable[]
{
placeholder = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "No recent plays",
TextSize = 14,
Font = @"Exo2.0-RegularItalic",
},
rankText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = @"Exo2.0-RegularItalic",
TextSize = primary_textsize
},
relativeText = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = @"Exo2.0-RegularItalic",
Y = 25,
TextSize = secondary_textsize
},
performanceText = new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Font = @"Exo2.0-RegularItalic",
TextSize = secondary_textsize
},
graph = new RankChartLineGraph
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = 75,
Y = -secondary_textsize,
Alpha = 0,
}
};
graph.OnBallMove += showHistoryRankTexts;
User.ValueChanged += userChanged;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
graph.Colour = colours.Yellow;
}
private void userChanged(User user)
{
placeholder.FadeIn(fade_duration, Easing.Out);
if (user?.Statistics?.Ranks.Global == null)
{
rankText.Text = string.Empty;
performanceText.Text = string.Empty;
relativeText.Text = string.Empty;
graph.FadeOut(fade_duration, Easing.Out);
ranks = null;
return;
}
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value };
ranks = userRanks.Select((x, index) => new KeyValuePair<int, int>(index, x)).Where(x => x.Value != 0).ToArray();
if (ranks.Length > 1)
{
placeholder.FadeOut(fade_duration, Easing.Out);
graph.DefaultValueCount = ranks.Length;
graph.Values = ranks.Select(x => -(float)Math.Log(x.Value));
graph.SetStaticBallPosition();
}
graph.FadeTo(ranks.Length > 1 ? 1 : 0, fade_duration, Easing.Out);
updateRankTexts();
}
private void updateRankTexts()
{
var user = User.Value;
performanceText.Text = user.Statistics.PP != null ? $"{user.Statistics.PP:#,0}pp" : string.Empty;
rankText.Text = user.Statistics.Ranks.Global > 0 ? $"#{user.Statistics.Ranks.Global:#,0}" : "no rank";
relativeText.Text = user.Country != null && user.Statistics.Ranks.Country > 0 ? $"{user.Country.FullName} #{user.Statistics.Ranks.Country:#,0}" : "no rank";
}
private void showHistoryRankTexts(int dayIndex)
{
rankText.Text = $"#{ranks[dayIndex].Value:#,0}";
relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago";
}
protected override bool OnHover(InputState state)
{
if (ranks?.Length > 1)
{
graph.UpdateBallPosition(state.Mouse.Position.X);
graph.ShowBall();
}
return base.OnHover(state);
}
protected override bool OnMouseMove(InputState state)
{
if (ranks?.Length > 1)
graph.UpdateBallPosition(state.Mouse.Position.X);
return base.OnMouseMove(state);
}
protected override void OnHoverLost(InputState state)
{
if (ranks?.Length > 1)
{
graph.HideBall();
updateRankTexts();
}
base.OnHoverLost(state);
}
private class RankChartLineGraph : LineGraph
{
private readonly CircularContainer staticBall;
private readonly CircularContainer movingBall;
public Action<int> OnBallMove;
public RankChartLineGraph()
{
Add(staticBall = new CircularContainer
{
Origin = Anchor.Centre,
Size = new Vector2(8),
Masking = true,
RelativePositionAxes = Axes.Both,
Child = new Box { RelativeSizeAxes = Axes.Both }
});
Add(movingBall = new CircularContainer
{
Origin = Anchor.Centre,
Size = new Vector2(8),
Alpha = 0,
Masking = true,
RelativePositionAxes = Axes.Both,
Child = new Box { RelativeSizeAxes = Axes.Both }
});
}
public void SetStaticBallPosition() => staticBall.Position = new Vector2(1, GetYPosition(Values.Last()));
public void UpdateBallPosition(float mouseXPosition)
{
int index = calculateIndex(mouseXPosition);
movingBall.Position = calculateBallPosition(index);
OnBallMove.Invoke(index);
}
public void ShowBall() => movingBall.FadeIn(fade_duration);
public void HideBall() => movingBall.FadeOut(fade_duration);
private int calculateIndex(float mouseXPosition) => (int)Math.Round(mouseXPosition / DrawWidth * (DefaultValueCount - 1));
private Vector2 calculateBallPosition(int index)
{
float y = GetYPosition(Values.ElementAt(index));
return new Vector2(index / (float)(DefaultValueCount - 1), y);
}
}
}
}

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class AboutSection : ProfileSection
{
public override string Title => "me!";
public override string Identifier => "me";
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class AboutSection : ProfileSection
{
public override string Title => "me!";
public override string Identifier => "me";
}
}

View File

@ -1,63 +1,63 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
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"/>).
/// </summary>
public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip
{
private readonly BeatmapInfo beatmap;
public BeatmapMetadataContainer(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
AutoSizeAxes = Axes.Both;
TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}";
}
public string TooltipText { get; }
[BackgroundDependencyLoader(true)]
private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay)
{
Action = () =>
{
if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value);
};
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = locale.GetUnicodePreference(
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] "
),
TextSize = 15,
Font = "Exo2.0-SemiBoldItalic",
},
new OsuSpriteText
{
Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
TextSize = 12,
Padding = new MarginPadding { Top = 3 },
Font = "Exo2.0-RegularItalic",
},
},
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
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"/>).
/// </summary>
public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip
{
private readonly BeatmapInfo beatmap;
public BeatmapMetadataContainer(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
AutoSizeAxes = Axes.Both;
TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}";
}
public string TooltipText { get; }
[BackgroundDependencyLoader(true)]
private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay)
{
Action = () =>
{
if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value);
};
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = locale.GetUnicodePreference(
$"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ",
$"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] "
),
TextSize = 15,
Font = "Exo2.0-SemiBoldItalic",
},
new OsuSpriteText
{
Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist),
TextSize = 12,
Padding = new MarginPadding { Top = 3 },
Font = "Exo2.0-RegularItalic",
},
},
};
}
}
}

View File

@ -1,82 +1,82 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Users;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Beatmaps
{
public class PaginatedBeatmapContainer : PaginatedContainer
{
private const float panel_padding = 10f;
private readonly BeatmapSetType type;
private DirectPanel currentlyPlaying;
public event Action<PaginatedBeatmapContainer> BeganPlayingPreview;
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<User> user, string header, string missing = "None... yet.")
: base(user, header, missing)
{
this.type = type;
ItemsPerPage = 6;
ItemsContainer.Spacing = new Vector2(panel_padding);
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
req.Success += sets =>
{
ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!sets.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
foreach (var s in sets)
{
if (!s.OnlineBeatmapSetID.HasValue)
continue;
var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets));
ItemsContainer.Add(panel);
panel.PreviewPlaying.ValueChanged += isPlaying =>
{
if (!isPlaying) return;
BeganPlayingPreview?.Invoke(this);
if (currentlyPlaying != null && currentlyPlaying != panel)
StopPlayingPreview();
currentlyPlaying = panel;
};
}
};
Api.Queue(req);
}
public void StopPlayingPreview()
{
if (currentlyPlaying != null)
currentlyPlaying.PreviewPlaying.Value = false;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Users;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Beatmaps
{
public class PaginatedBeatmapContainer : PaginatedContainer
{
private const float panel_padding = 10f;
private readonly BeatmapSetType type;
private DirectPanel currentlyPlaying;
public event Action<PaginatedBeatmapContainer> BeganPlayingPreview;
public PaginatedBeatmapContainer(BeatmapSetType type, Bindable<User> user, string header, string missing = "None... yet.")
: base(user, header, missing)
{
this.type = type;
ItemsPerPage = 6;
ItemsContainer.Spacing = new Vector2(panel_padding);
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
req.Success += sets =>
{
ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!sets.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
foreach (var s in sets)
{
if (!s.OnlineBeatmapSetID.HasValue)
continue;
var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets));
ItemsContainer.Add(panel);
panel.PreviewPlaying.ValueChanged += isPlaying =>
{
if (!isPlaying) return;
BeganPlayingPreview?.Invoke(this);
if (currentlyPlaying != null && currentlyPlaying != panel)
StopPlayingPreview();
currentlyPlaying = panel;
};
}
};
Api.Queue(req);
}
public void StopPlayingPreview()
{
if (currentlyPlaying != null)
currentlyPlaying.PreviewPlaying.Value = false;
}
}
}

View File

@ -1,42 +1,42 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Overlays.Profile.Sections.Beatmaps;
namespace osu.Game.Overlays.Profile.Sections
{
public class BeatmapsSection : ProfileSection
{
public override string Title => "Beatmaps";
public override string Identifier => "beatmaps";
private DirectPanel currentlyPlaying;
public BeatmapsSection()
{
Children = new[]
{
new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
};
foreach (var paginatedBeatmapContainer in Children.OfType<PaginatedBeatmapContainer>())
{
paginatedBeatmapContainer.BeganPlayingPreview += beatmapContainer =>
{
foreach (var bc in Children.OfType<PaginatedBeatmapContainer>())
{
if (bc != beatmapContainer)
bc.StopPlayingPreview();
}
};
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Overlays.Profile.Sections.Beatmaps;
namespace osu.Game.Overlays.Profile.Sections
{
public class BeatmapsSection : ProfileSection
{
public override string Title => "Beatmaps";
public override string Identifier => "beatmaps";
private DirectPanel currentlyPlaying;
public BeatmapsSection()
{
Children = new[]
{
new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
};
foreach (var paginatedBeatmapContainer in Children.OfType<PaginatedBeatmapContainer>())
{
paginatedBeatmapContainer.BeganPlayingPreview += beatmapContainer =>
{
foreach (var bc in Children.OfType<PaginatedBeatmapContainer>())
{
if (bc != beatmapContainer)
bc.StopPlayingPreview();
}
};
}
}
}
}

View File

@ -1,124 +1,124 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Profile.Sections
{
public abstract class DrawableProfileRow : Container
{
private const int fade_duration = 200;
private Box underscoreLine;
private readonly Box coloredBackground;
private readonly 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;
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 = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Width = 0.97f,
},
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
{
AddRange(new Drawable[]
{
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(InputState state) => true;
protected override bool OnHover(InputState state)
{
background.FadeIn(fade_duration, Easing.OutQuint);
underscoreLine.FadeOut(fade_duration, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
background.FadeOut(fade_duration, Easing.OutQuint);
underscoreLine.FadeIn(fade_duration, Easing.OutQuint);
base.OnHoverLost(state);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Profile.Sections
{
public abstract class DrawableProfileRow : Container
{
private const int fade_duration = 200;
private Box underscoreLine;
private readonly Box coloredBackground;
private readonly 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;
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 = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Width = 0.97f,
},
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
{
AddRange(new Drawable[]
{
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(InputState state) => true;
protected override bool OnHover(InputState state)
{
background.FadeIn(fade_duration, Easing.OutQuint);
underscoreLine.FadeOut(fade_duration, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
background.FadeOut(fade_duration, Easing.OutQuint);
underscoreLine.FadeIn(fade_duration, Easing.OutQuint);
base.OnHoverLost(state);
}
}
}

View File

@ -1,105 +1,105 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.Profile.Sections.Historical
{
public class DrawableMostPlayedRow : DrawableProfileRow
{
private readonly BeatmapInfo beatmap;
private readonly int playCount;
private OsuHoverContainer mapperContainer;
public DrawableMostPlayedRow(BeatmapInfo beatmap, int playCount)
{
this.beatmap = beatmap;
this.playCount = playCount;
}
protected override Drawable CreateLeftVisual() => new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
OnLoadComplete = d => d.FadeInFromZero(500, Easing.OutQuint)
})
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.None,
Size = new Vector2(80, 50),
};
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profileOverlay)
{
LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap));
LeftFlowContainer.Add(new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"mapped by ",
TextSize = 12,
},
mapperContainer = new OsuHoverContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = beatmap.Metadata.AuthorString,
TextSize = 12,
Font = @"Exo2.0-MediumItalic"
}
}
},
}
});
RightFlowContainer.Add(new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Text = playCount.ToString(),
TextSize = 18,
Font = @"Exo2.0-SemiBoldItalic"
},
new OsuSpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Text = @"times played ",
TextSize = 12,
Font = @"Exo2.0-RegularItalic"
},
}
});
if (profileOverlay != null)
mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Overlays.Profile.Sections.Historical
{
public class DrawableMostPlayedRow : DrawableProfileRow
{
private readonly BeatmapInfo beatmap;
private readonly int playCount;
private OsuHoverContainer mapperContainer;
public DrawableMostPlayedRow(BeatmapInfo beatmap, int playCount)
{
this.beatmap = beatmap;
this.playCount = playCount;
}
protected override Drawable CreateLeftVisual() => new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
RelativeSizeAxes = Axes.Both,
OnLoadComplete = d => d.FadeInFromZero(500, Easing.OutQuint)
})
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.None,
Size = new Vector2(80, 50),
};
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profileOverlay)
{
LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap));
LeftFlowContainer.Add(new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"mapped by ",
TextSize = 12,
},
mapperContainer = new OsuHoverContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = beatmap.Metadata.AuthorString,
TextSize = 12,
Font = @"Exo2.0-MediumItalic"
}
}
},
}
});
RightFlowContainer.Add(new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Text = playCount.ToString(),
TextSize = 18,
Font = @"Exo2.0-SemiBoldItalic"
},
new OsuSpriteText
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Text = @"times played ",
TextSize = 12,
Font = @"Exo2.0-RegularItalic"
},
}
});
if (profileOverlay != null)
mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author);
}
}
}

View File

@ -1,51 +1,51 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections.Historical
{
public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer
{
public PaginatedMostPlayedBeatmapContainer(Bindable<User> user)
:base(user, "Most Played Beatmaps", "No records. :(")
{
ItemsPerPage = 5;
ItemsContainer.Direction = FillDirection.Vertical;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
req.Success += beatmaps =>
{
ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!beatmaps.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (var beatmap in beatmaps)
{
ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount));
}
};
Api.Queue(req);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections.Historical
{
public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer
{
public PaginatedMostPlayedBeatmapContainer(Bindable<User> user)
:base(user, "Most Played Beatmaps", "No records. :(")
{
ItemsPerPage = 5;
ItemsContainer.Direction = FillDirection.Vertical;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
req.Success += beatmaps =>
{
ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!beatmaps.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (var beatmap in beatmaps)
{
ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount));
}
};
Api.Queue(req);
}
}
}

View File

@ -1,26 +1,26 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Overlays.Profile.Sections.Ranks;
namespace osu.Game.Overlays.Profile.Sections
{
public class HistoricalSection : ProfileSection
{
public override string Title => "Historical";
public override string Identifier => "historical";
public HistoricalSection()
{
Children = new Drawable[]
{
new PaginatedMostPlayedBeatmapContainer(User),
new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("),
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Sections.Historical;
using osu.Game.Overlays.Profile.Sections.Ranks;
namespace osu.Game.Overlays.Profile.Sections
{
public class HistoricalSection : ProfileSection
{
public override string Title => "Historical";
public override string Identifier => "historical";
public HistoricalSection()
{
Children = new Drawable[]
{
new PaginatedMostPlayedBeatmapContainer(User),
new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("),
};
}
}
}

View File

@ -1,135 +1,135 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections.Kudosu
{
public class KudosuInfo : Container
{
private readonly Bindable<User> user = new Bindable<User>();
public KudosuInfo(Bindable<User> user)
{
this.user.BindTo(user);
CountSection total;
CountSection avaliable;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 3;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 3f,
Colour = Color4.Black.Opacity(0.2f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
total = new CountSection(
"Total Kudosu Earned",
"Based on how much of a contribution the user has made to beatmap moderation. See this link for more information."
),
avaliable = new CountSection(
"Kudosu Avaliable",
"Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet."
),
}
}
};
this.user.ValueChanged += newUser =>
{
total.Count = newUser?.Kudosu.Total ?? 0;
avaliable.Count = newUser?.Kudosu.Available ?? 0;
};
}
protected override bool OnClick(InputState state) => true;
private class CountSection : Container
{
private readonly OsuSpriteText valueText;
public new int Count
{
set { valueText.Text = value.ToString(); }
}
public CountSection(string header, string description)
{
RelativeSizeAxes = Axes.X;
Width = 0.5f;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Horizontal = 10, Top = 10, Bottom = 20 };
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = header + ":",
TextSize = 20,
Font = @"Exo2.0-RegularItalic",
},
valueText = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = "0",
TextSize = 40,
UseFullGlyphHeight = false,
Font = @"Exo2.0-RegularItalic"
}
}
},
new OsuTextFlowContainer(t => { t.TextSize = 19; })
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Text = description
}
}
};
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections.Kudosu
{
public class KudosuInfo : Container
{
private readonly Bindable<User> user = new Bindable<User>();
public KudosuInfo(Bindable<User> user)
{
this.user.BindTo(user);
CountSection total;
CountSection avaliable;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 3;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 3f,
Colour = Color4.Black.Opacity(0.2f),
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
total = new CountSection(
"Total Kudosu Earned",
"Based on how much of a contribution the user has made to beatmap moderation. See this link for more information."
),
avaliable = new CountSection(
"Kudosu Avaliable",
"Kudosu can be traded for kudosu stars, which will help your beatmap get more attention. This is the number of kudosu you haven't traded in yet."
),
}
}
};
this.user.ValueChanged += newUser =>
{
total.Count = newUser?.Kudosu.Total ?? 0;
avaliable.Count = newUser?.Kudosu.Available ?? 0;
};
}
protected override bool OnClick(InputState state) => true;
private class CountSection : Container
{
private readonly OsuSpriteText valueText;
public new int Count
{
set { valueText.Text = value.ToString(); }
}
public CountSection(string header, string description)
{
RelativeSizeAxes = Axes.X;
Width = 0.5f;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Horizontal = 10, Top = 10, Bottom = 20 };
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = header + ":",
TextSize = 20,
Font = @"Exo2.0-RegularItalic",
},
valueText = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = "0",
TextSize = 40,
UseFullGlyphHeight = false,
Font = @"Exo2.0-RegularItalic"
}
}
},
new OsuTextFlowContainer(t => { t.TextSize = 19; })
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Text = description
}
}
};
}
}
}
}

View File

@ -1,22 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile.Sections.Kudosu;
namespace osu.Game.Overlays.Profile.Sections
{
public class KudosuSection : ProfileSection
{
public override string Title => "Kudosu!";
public override string Identifier => "kudosu";
public KudosuSection()
{
Children = new[]
{
new KudosuInfo(User),
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile.Sections.Kudosu;
namespace osu.Game.Overlays.Profile.Sections
{
public class KudosuSection : ProfileSection
{
public override string Title => "Kudosu!";
public override string Identifier => "kudosu";
public KudosuSection()
{
Children = new[]
{
new KudosuInfo(User),
};
}
}
}

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class MedalsSection : ProfileSection
{
public override string Title => "Medals";
public override string Identifier => "medals";
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Overlays.Profile.Sections
{
public class MedalsSection : ProfileSection
{
public override string Title => "Medals";
public override string Identifier => "medals";
}
}

View File

@ -1,110 +1,110 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections
{
public class PaginatedContainer : FillFlowContainer
{
protected readonly FillFlowContainer ItemsContainer;
protected readonly OsuHoverContainer ShowMoreButton;
protected readonly LoadingAnimation ShowMoreLoading;
protected readonly OsuSpriteText MissingText;
protected int VisiblePages;
protected int ItemsPerPage;
protected readonly Bindable<User> User = new Bindable<User>();
protected APIAccess Api;
protected RulesetStore Rulesets;
public PaginatedContainer(Bindable<User> user, string header, string missing)
{
User.BindTo(user);
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Children = new Drawable[]
{
new OsuSpriteText
{
TextSize = 15,
Text = header,
Font = "Exo2.0-RegularItalic",
Margin = new MarginPadding { Top = 10, Bottom = 10 },
},
ItemsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Bottom = 10 }
},
ShowMoreButton = new OsuHoverContainer
{
Alpha = 0,
Action = ShowMore,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Child = new OsuSpriteText
{
TextSize = 14,
Text = "show more",
}
},
ShowMoreLoading = new LoadingAnimation
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(14),
},
MissingText = new OsuSpriteText
{
TextSize = 14,
Text = missing,
Alpha = 0,
},
};
}
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
Api = api;
Rulesets = rulesets;
User.ValueChanged += onUserChanged;
User.TriggerChange();
}
private void onUserChanged(User newUser)
{
VisiblePages = 0;
ItemsContainer.Clear();
ShowMoreButton.Hide();
if (newUser != null)
ShowMore();
}
protected virtual void ShowMore()
{
ShowMoreLoading.Show();
ShowMoreButton.Hide();
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile.Sections
{
public class PaginatedContainer : FillFlowContainer
{
protected readonly FillFlowContainer ItemsContainer;
protected readonly OsuHoverContainer ShowMoreButton;
protected readonly LoadingAnimation ShowMoreLoading;
protected readonly OsuSpriteText MissingText;
protected int VisiblePages;
protected int ItemsPerPage;
protected readonly Bindable<User> User = new Bindable<User>();
protected APIAccess Api;
protected RulesetStore Rulesets;
public PaginatedContainer(Bindable<User> user, string header, string missing)
{
User.BindTo(user);
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Children = new Drawable[]
{
new OsuSpriteText
{
TextSize = 15,
Text = header,
Font = "Exo2.0-RegularItalic",
Margin = new MarginPadding { Top = 10, Bottom = 10 },
},
ItemsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Bottom = 10 }
},
ShowMoreButton = new OsuHoverContainer
{
Alpha = 0,
Action = ShowMore,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Child = new OsuSpriteText
{
TextSize = 14,
Text = "show more",
}
},
ShowMoreLoading = new LoadingAnimation
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(14),
},
MissingText = new OsuSpriteText
{
TextSize = 14,
Text = missing,
Alpha = 0,
},
};
}
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
Api = api;
Rulesets = rulesets;
User.ValueChanged += onUserChanged;
User.TriggerChange();
}
private void onUserChanged(User newUser)
{
VisiblePages = 0;
ItemsContainer.Clear();
ShowMoreButton.Hide();
if (newUser != null)
ShowMore();
}
protected virtual void ShowMore()
{
ShowMoreLoading.Show();
ShowMoreButton.Hide();
}
}
}

View File

@ -1,49 +1,49 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawablePerformanceScore : DrawableProfileScore
{
private readonly double? weight;
public DrawablePerformanceScore(Score 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,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
if (weight.HasValue)
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = $"weighted: {pp * weight:0}pp ({weight:P0})",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic",
});
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawablePerformanceScore : DrawableProfileScore
{
private readonly double? weight;
public DrawablePerformanceScore(Score 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,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
if (weight.HasValue)
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = $"weighted: {pp * weight:0}pp ({weight:P0})",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic",
});
}
}
}
}

View File

@ -1,70 +1,70 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public abstract class DrawableProfileScore : DrawableProfileRow
{
private readonly ScoreModsContainer modsContainer;
protected readonly Score Score;
protected DrawableProfileScore(Score score)
{
Score = score;
RelativeSizeAxes = Axes.X;
Height = 60;
Children = new Drawable[]
{
modsContainer = new ScoreModsContainer
{
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Width = 60,
Margin = new MarginPadding { Right = 160 }
}
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
{
var text = new OsuSpriteText
{
Text = $"accuracy: {Score.Accuracy:P2}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic"
};
RightFlowContainer.Add(text);
RightFlowContainer.SetLayoutPosition(text, 1);
LeftFlowContainer.Add(new BeatmapMetadataContainer(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 DrawableRank(Score.Rank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public abstract class DrawableProfileScore : DrawableProfileRow
{
private readonly ScoreModsContainer modsContainer;
protected readonly Score Score;
protected DrawableProfileScore(Score score)
{
Score = score;
RelativeSizeAxes = Axes.X;
Height = 60;
Children = new Drawable[]
{
modsContainer = new ScoreModsContainer
{
AutoSizeAxes = Axes.Y,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Width = 60,
Margin = new MarginPadding { Right = 160 }
}
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colour)
{
var text = new OsuSpriteText
{
Text = $"accuracy: {Score.Accuracy:P2}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = colour.GrayA,
TextSize = 11,
Font = "Exo2.0-RegularItalic"
};
RightFlowContainer.Add(text);
RightFlowContainer.SetLayoutPosition(text, 1);
LeftFlowContainer.Add(new BeatmapMetadataContainer(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 DrawableRank(Score.Rank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
}
}

View File

@ -1,31 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableTotalScore : DrawableProfileScore
{
public DrawableTotalScore(Score score)
: base(score)
{
}
[BackgroundDependencyLoader]
private void load()
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = Score.TotalScore.ToString("#,###"),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class DrawableTotalScore : DrawableProfileScore
{
public DrawableTotalScore(Score score)
: base(score)
{
}
[BackgroundDependencyLoader]
private void load()
{
RightFlowContainer.Add(new OsuSpriteText
{
Text = Score.TotalScore.ToString("#,###"),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 18,
Font = "Exo2.0-BoldItalic",
});
}
}
}

View File

@ -1,73 +1,73 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
using System;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class PaginatedScoreContainer : PaginatedContainer
{
private readonly bool includeWeight;
private readonly ScoreType type;
public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, string missing, bool includeWeight = false)
: base(user, header, missing)
{
this.type = type;
this.includeWeight = includeWeight;
ItemsPerPage = 5;
ItemsContainer.Direction = FillDirection.Vertical;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
req.Success += scores =>
{
foreach (var s in scores)
s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID));
ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!scores.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (OnlineScore score in scores)
{
DrawableProfileScore drawableScore;
switch (type)
{
default:
drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null);
break;
case ScoreType.Recent:
drawableScore = new DrawableTotalScore(score);
break;
}
ItemsContainer.Add(drawableScore);
}
};
Api.Queue(req);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
using System;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class PaginatedScoreContainer : PaginatedContainer
{
private readonly bool includeWeight;
private readonly ScoreType type;
public PaginatedScoreContainer(ScoreType type, Bindable<User> user, string header, string missing, bool includeWeight = false)
: base(user, header, missing)
{
this.type = type;
this.includeWeight = includeWeight;
ItemsPerPage = 5;
ItemsContainer.Direction = FillDirection.Vertical;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage);
req.Success += scores =>
{
foreach (var s in scores)
s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID));
ShowMoreButton.FadeTo(scores.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!scores.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (OnlineScore score in scores)
{
DrawableProfileScore drawableScore;
switch (type)
{
default:
drawableScore = new DrawablePerformanceScore(score, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null);
break;
case ScoreType.Recent:
drawableScore = new DrawableTotalScore(score);
break;
}
ItemsContainer.Add(drawableScore);
}
};
Api.Queue(req);
}
}
}

View File

@ -1,21 +1,21 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class ScoreModsContainer : FlowContainer<ModIcon>
{
protected override IEnumerable<Vector2> ComputeLayoutPositions()
{
int count = FlowingChildren.Count();
for (int i = 0; i < count; i++)
yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
public class ScoreModsContainer : FlowContainer<ModIcon>
{
protected override IEnumerable<Vector2> ComputeLayoutPositions()
{
int count = FlowingChildren.Count();
for (int i = 0; i < count; i++)
yield return new Vector2(DrawWidth * i * (count == 1 ? 0 : 1f / (count - 1)), 0);
}
}
}

View File

@ -1,24 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Profile.Sections
{
public class RanksSection : ProfileSection
{
public override string Title => "Ranks";
public override string Identifier => "top_ranks";
public RanksSection()
{
Children = new[]
{
new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true),
new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("),
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Online.API.Requests;
namespace osu.Game.Overlays.Profile.Sections
{
public class RanksSection : ProfileSection
{
public override string Title => "Ranks";
public override string Identifier => "top_ranks";
public RanksSection()
{
Children = new[]
{
new PaginatedScoreContainer(ScoreType.Best, User, "Best Performance", "No performance records. :(", true),
new PaginatedScoreContainer(ScoreType.Firsts, User, "First Place Ranks", "No awesome performance records yet. :("),
};
}
}
}

View File

@ -1,162 +1,162 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Screens.Select.Leaderboards;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class DrawableRecentActivity : DrawableProfileRow
{
private APIAccess api;
private readonly RecentActivity activity;
private LinkFlowContainer content;
public DrawableRecentActivity(RecentActivity activity)
{
this.activity = activity;
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
this.api = api;
LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 };
LeftFlowContainer.Add(content = new LinkFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
});
RightFlowContainer.Add(new DrawableDate(activity.CreatedAt)
{
TextSize = 13,
Colour = OsuColour.Gray(0xAA),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
});
var formatted = createMessage();
content.AddLinks(formatted.Text, formatted.Links);
}
protected override Drawable CreateLeftVisual()
{
switch (activity.Type)
{
case RecentActivityType.Rank:
return new DrawableRank(activity.ScoreRank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
case RecentActivityType.Achievement:
return new MedalIcon(activity.Achievement.Slug)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
default:
return new Container
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
}
}
private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}";
private MessageFormatter.MessageFormatterResult 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!";
break;
case RecentActivityType.BeatmapPlaycount:
message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!";
break;
case RecentActivityType.BeatmapsetApprove:
message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!";
break;
case RecentActivityType.BeatmapsetDelete:
message = $"{beatmapsetLinkTemplate()} has been deleted.";
break;
case RecentActivityType.BeatmapsetRevive:
message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}.";
break;
case RecentActivityType.BeatmapsetUpdate:
message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!";
break;
case RecentActivityType.BeatmapsetUpload:
message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!";
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}!)";
break;
case RecentActivityType.RankLost:
message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)";
break;
case RecentActivityType.UserSupportAgain:
message = $"{userLinkTemplate()} 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!";
break;
case RecentActivityType.UserSupportGift:
message = $"{userLinkTemplate()} 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;
break;
}
return MessageFormatter.FormatText(message);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Chat;
using osu.Game.Screens.Select.Leaderboards;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class DrawableRecentActivity : DrawableProfileRow
{
private APIAccess api;
private readonly RecentActivity activity;
private LinkFlowContainer content;
public DrawableRecentActivity(RecentActivity activity)
{
this.activity = activity;
}
[BackgroundDependencyLoader]
private void load(APIAccess api)
{
this.api = api;
LeftFlowContainer.Padding = new MarginPadding { Left = 10, Right = 160 };
LeftFlowContainer.Add(content = new LinkFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
});
RightFlowContainer.Add(new DrawableDate(activity.CreatedAt)
{
TextSize = 13,
Colour = OsuColour.Gray(0xAA),
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
});
var formatted = createMessage();
content.AddLinks(formatted.Text, formatted.Links);
}
protected override Drawable CreateLeftVisual()
{
switch (activity.Type)
{
case RecentActivityType.Rank:
return new DrawableRank(activity.ScoreRank)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
case RecentActivityType.Achievement:
return new MedalIcon(activity.Achievement.Slug)
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
default:
return new Container
{
RelativeSizeAxes = Axes.Y,
Width = 60,
FillMode = FillMode.Fit,
};
}
}
private string toAbsoluteUrl(string url) => $"{api.Endpoint}{url}";
private MessageFormatter.MessageFormatterResult 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!";
break;
case RecentActivityType.BeatmapPlaycount:
message = $"{beatmapLinkTemplate()} has been played {activity.Count} times!";
break;
case RecentActivityType.BeatmapsetApprove:
message = $"{beatmapsetLinkTemplate()} has been {activity.Approval.ToString().ToLowerInvariant()}!";
break;
case RecentActivityType.BeatmapsetDelete:
message = $"{beatmapsetLinkTemplate()} has been deleted.";
break;
case RecentActivityType.BeatmapsetRevive:
message = $"{beatmapsetLinkTemplate()} has been revived from eternal slumber by {userLinkTemplate()}.";
break;
case RecentActivityType.BeatmapsetUpdate:
message = $"{userLinkTemplate()} has updated the beatmap {beatmapsetLinkTemplate()}!";
break;
case RecentActivityType.BeatmapsetUpload:
message = $"{userLinkTemplate()} has submitted a new beatmap {beatmapsetLinkTemplate()}!";
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}!)";
break;
case RecentActivityType.RankLost:
message = $"{userLinkTemplate()} has lost first place on {beatmapLinkTemplate()} ({activity.Mode}!)";
break;
case RecentActivityType.UserSupportAgain:
message = $"{userLinkTemplate()} 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!";
break;
case RecentActivityType.UserSupportGift:
message = $"{userLinkTemplate()} 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;
break;
}
return MessageFormatter.FormatText(message);
}
}
}

View File

@ -1,38 +1,38 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class MedalIcon : Container
{
private readonly string slug;
private readonly Sprite sprite;
private string url => $@"https://s.ppy.sh/images/medals-client/{slug}@2x.png";
public MedalIcon(string slug)
{
this.slug = slug;
Child = sprite = new Sprite
{
Height = 40,
Width = 40,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = textures.Get(url);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class MedalIcon : Container
{
private readonly string slug;
private readonly Sprite sprite;
private string url => $@"https://s.ppy.sh/images/medals-client/{slug}@2x.png";
public MedalIcon(string slug)
{
this.slug = slug;
Child = sprite = new Sprite
{
Height = 40,
Width = 40,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = textures.Get(url);
}
}
}

View File

@ -1,48 +1,48 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class PaginatedRecentActivityContainer : PaginatedContainer
{
public PaginatedRecentActivityContainer(Bindable<User> user, string header, string missing)
: base(user, header, missing)
{
ItemsPerPage = 5;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
req.Success += activities =>
{
ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!activities.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (RecentActivity activity in activities)
{
ItemsContainer.Add(new DrawableRecentActivity(activity));
}
};
Api.Queue(req);
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Online.API.Requests;
using osu.Game.Users;
using System.Linq;
namespace osu.Game.Overlays.Profile.Sections.Recent
{
public class PaginatedRecentActivityContainer : PaginatedContainer
{
public PaginatedRecentActivityContainer(Bindable<User> user, string header, string missing)
: base(user, header, missing)
{
ItemsPerPage = 5;
}
protected override void ShowMore()
{
base.ShowMore();
var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage);
req.Success += activities =>
{
ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0);
ShowMoreLoading.Hide();
if (!activities.Any() && VisiblePages == 1)
{
MissingText.Show();
return;
}
MissingText.Hide();
foreach (RecentActivity activity in activities)
{
ItemsContainer.Add(new DrawableRecentActivity(activity));
}
};
Api.Queue(req);
}
}
}

View File

@ -1,22 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile.Sections.Recent;
namespace osu.Game.Overlays.Profile.Sections
{
public class RecentSection : ProfileSection
{
public override string Title => "Recent";
public override string Identifier => "recent_activity";
public RecentSection()
{
Children = new[]
{
new PaginatedRecentActivityContainer(User, null, @"This user hasn't done anything notable recently!"),
};
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Overlays.Profile.Sections.Recent;
namespace osu.Game.Overlays.Profile.Sections
{
public class RecentSection : ProfileSection
{
public override string Title => "Recent";
public override string Identifier => "recent_activity";
public RecentSection()
{
Children = new[]
{
new PaginatedRecentActivityContainer(User, null, @"This user hasn't done anything notable recently!"),
};
}
}
}

View File

@ -1,64 +1,64 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Overlays.Profile
{
public class SupporterIcon : CircularContainer, IHasTooltip
{
private readonly Box background;
public string TooltipText => "osu!supporter";
public SupporterIcon()
{
Masking = true;
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both },
new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.8f),
Masking = true,
Children = new Drawable[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Triangles
{
TriangleScale = 0.2f,
ColourLight = OsuColour.FromHex(@"ff7db7"),
ColourDark = OsuColour.FromHex(@"de5b95"),
RelativeSizeAxes = Axes.Both,
Velocity = 0.3f,
},
}
},
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_heart,
Scale = new Vector2(0.45f),
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Pink;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
namespace osu.Game.Overlays.Profile
{
public class SupporterIcon : CircularContainer, IHasTooltip
{
private readonly Box background;
public string TooltipText => "osu!supporter";
public SupporterIcon()
{
Masking = true;
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both },
new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.8f),
Masking = true,
Children = new Drawable[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
new Triangles
{
TriangleScale = 0.2f,
ColourLight = OsuColour.FromHex(@"ff7db7"),
ColourDark = OsuColour.FromHex(@"de5b95"),
RelativeSizeAxes = Axes.Both,
Velocity = 0.3f,
},
}
},
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_heart,
Scale = new Vector2(0.45f),
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Pink;
}
}
}

View File

@ -1,102 +1,102 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
public class DisplayStyleControl<T> : Container
{
public readonly SlimEnumDropdown<T> Dropdown;
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
public DisplayStyleControl()
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Spacing = new Vector2(10f, 0f),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5f, 0f),
Direction = FillDirection.Horizontal,
Children = new[]
{
new DisplayStyleToggleButton(FontAwesome.fa_th_large, PanelDisplayStyle.Grid, DisplayStyle),
new DisplayStyleToggleButton(FontAwesome.fa_list_ul, PanelDisplayStyle.List, DisplayStyle),
},
},
Dropdown = new SlimEnumDropdown<T>
{
RelativeSizeAxes = Axes.None,
Width = 160f,
},
},
},
};
DisplayStyle.Value = PanelDisplayStyle.Grid;
}
private class DisplayStyleToggleButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
private readonly PanelDisplayStyle style;
private readonly Bindable<PanelDisplayStyle> bindable;
public DisplayStyleToggleButton(FontAwesome icon, PanelDisplayStyle style, Bindable<PanelDisplayStyle> bindable)
{
this.bindable = bindable;
this.style = style;
Size = new Vector2(25f);
Children = new Drawable[]
{
this.icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(18),
Alpha = 0.5f,
},
};
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(bindable.Value);
Action = () => bindable.Value = this.style;
}
private void Bindable_ValueChanged(PanelDisplayStyle style)
{
icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100);
}
protected override void Dispose(bool isDisposing)
{
bindable.ValueChanged -= Bindable_ValueChanged;
}
}
}
public enum PanelDisplayStyle
{
Grid,
List,
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
public class DisplayStyleControl<T> : Container
{
public readonly SlimEnumDropdown<T> Dropdown;
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
public DisplayStyleControl()
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Spacing = new Vector2(10f, 0f),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5f, 0f),
Direction = FillDirection.Horizontal,
Children = new[]
{
new DisplayStyleToggleButton(FontAwesome.fa_th_large, PanelDisplayStyle.Grid, DisplayStyle),
new DisplayStyleToggleButton(FontAwesome.fa_list_ul, PanelDisplayStyle.List, DisplayStyle),
},
},
Dropdown = new SlimEnumDropdown<T>
{
RelativeSizeAxes = Axes.None,
Width = 160f,
},
},
},
};
DisplayStyle.Value = PanelDisplayStyle.Grid;
}
private class DisplayStyleToggleButton : OsuClickableContainer
{
private readonly SpriteIcon icon;
private readonly PanelDisplayStyle style;
private readonly Bindable<PanelDisplayStyle> bindable;
public DisplayStyleToggleButton(FontAwesome icon, PanelDisplayStyle style, Bindable<PanelDisplayStyle> bindable)
{
this.bindable = bindable;
this.style = style;
Size = new Vector2(25f);
Children = new Drawable[]
{
this.icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(18),
Alpha = 0.5f,
},
};
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(bindable.Value);
Action = () => bindable.Value = this.style;
}
private void Bindable_ValueChanged(PanelDisplayStyle style)
{
icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100);
}
protected override void Dispose(bool isDisposing)
{
bindable.ValueChanged -= Bindable_ValueChanged;
}
}
}
public enum PanelDisplayStyle
{
Grid,
List,
}
}

View File

@ -1,28 +1,28 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.SearchableList
{
public class HeaderTabControl<T> : OsuTabControl<T>
{
protected override TabItem<T> CreateTabItem(T value) => new HeaderTabItem(value);
public HeaderTabControl()
{
Height = 26;
AccentColour = Color4.White;
}
private class HeaderTabItem : OsuTabItem
{
public HeaderTabItem(T value) : base(value)
{
Text.TextSize = 16;
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.SearchableList
{
public class HeaderTabControl<T> : OsuTabControl<T>
{
protected override TabItem<T> CreateTabItem(T value) => new HeaderTabItem(value);
public HeaderTabControl()
{
Height = 26;
AccentColour = Color4.White;
}
private class HeaderTabItem : OsuTabItem
{
public HeaderTabItem(T value) : base(value)
{
Text.TextSize = 16;
}
}
}
}

View File

@ -1,134 +1,134 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListFilterControl<T, U> : Container
{
private const float padding = 10;
private readonly Container filterContainer;
private readonly Box tabStrip;
public readonly SearchTextBox Search;
public readonly PageTabControl<T> Tabs;
public readonly DisplayStyleControl<U> DisplayStyleControl;
protected abstract Color4 BackgroundColour { get; }
protected abstract T DefaultTab { get; }
protected virtual Drawable CreateSupplementaryControls() => null;
protected SearchableListFilterControl()
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument");
RelativeSizeAxes = Axes.X;
var controls = CreateSupplementaryControls();
Container controlsContainer;
Children = new Drawable[]
{
filterContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
Alpha = 0.9f,
},
tabStrip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = padding, Horizontal = SearchableListOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
Search = new FilterSearchTextBox
{
RelativeSizeAxes = Axes.X,
},
controlsContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = controls != null ? padding : 0 },
},
Tabs = new PageTabControl<T>
{
RelativeSizeAxes = Axes.X,
},
new Box //keep the tab strip part of autosize, but don't put it in the flow container
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = Color4.White.Opacity(0),
},
},
},
},
},
DisplayStyleControl = new DisplayStyleControl<U>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
};
if (controls != null) controlsContainer.Children = new[] { controls };
Tabs.Current.Value = DefaultTab;
Tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabStrip.Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
Height = filterContainer.Height;
DisplayStyleControl.Margin = new MarginPadding { Top = filterContainer.Height - 35, Right = SearchableListOverlay.WIDTH_PADDING };
}
private class FilterSearchTextBox : SearchTextBox
{
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
protected override bool AllowCommit => true;
private Color4 backgroundColour;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundColour = colours.Gray2.Opacity(0.9f);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListFilterControl<T, U> : Container
{
private const float padding = 10;
private readonly Container filterContainer;
private readonly Box tabStrip;
public readonly SearchTextBox Search;
public readonly PageTabControl<T> Tabs;
public readonly DisplayStyleControl<U> DisplayStyleControl;
protected abstract Color4 BackgroundColour { get; }
protected abstract T DefaultTab { get; }
protected virtual Drawable CreateSupplementaryControls() => null;
protected SearchableListFilterControl()
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument");
RelativeSizeAxes = Axes.X;
var controls = CreateSupplementaryControls();
Container controlsContainer;
Children = new Drawable[]
{
filterContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
Alpha = 0.9f,
},
tabStrip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = padding, Horizontal = SearchableListOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
Search = new FilterSearchTextBox
{
RelativeSizeAxes = Axes.X,
},
controlsContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = controls != null ? padding : 0 },
},
Tabs = new PageTabControl<T>
{
RelativeSizeAxes = Axes.X,
},
new Box //keep the tab strip part of autosize, but don't put it in the flow container
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = Color4.White.Opacity(0),
},
},
},
},
},
DisplayStyleControl = new DisplayStyleControl<U>
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
};
if (controls != null) controlsContainer.Children = new[] { controls };
Tabs.Current.Value = DefaultTab;
Tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabStrip.Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
Height = filterContainer.Height;
DisplayStyleControl.Margin = new MarginPadding { Top = filterContainer.Height - 35, Right = SearchableListOverlay.WIDTH_PADDING };
}
private class FilterSearchTextBox : SearchTextBox
{
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
protected override bool AllowCommit => true;
private Color4 backgroundColour;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundColour = colours.Gray2.Opacity(0.9f);
}
}
}
}

View File

@ -1,83 +1,83 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListHeader<T> : Container
{
public readonly HeaderTabControl<T> Tabs;
protected abstract Color4 BackgroundColour { get; }
protected abstract T DefaultTab { get; }
protected abstract Drawable CreateHeaderText();
protected abstract FontAwesome Icon { get; }
protected SearchableListHeader()
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument");
RelativeSizeAxes = Axes.X;
Height = 90;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft,
Position = new Vector2(-35f, 5f),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Children = new[]
{
new SpriteIcon
{
Size = new Vector2(25),
Icon = Icon,
},
CreateHeaderText(),
},
},
Tabs = new HeaderTabControl<T>
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
},
},
},
};
Tabs.Current.Value = DefaultTab;
Tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Tabs.StripColour = colours.Green;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListHeader<T> : Container
{
public readonly HeaderTabControl<T> Tabs;
protected abstract Color4 BackgroundColour { get; }
protected abstract T DefaultTab { get; }
protected abstract Drawable CreateHeaderText();
protected abstract FontAwesome Icon { get; }
protected SearchableListHeader()
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument");
RelativeSizeAxes = Axes.X;
Height = 90;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING, Right = SearchableListOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft,
Position = new Vector2(-35f, 5f),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Children = new[]
{
new SpriteIcon
{
Size = new Vector2(25),
Icon = Icon,
},
CreateHeaderText(),
},
},
Tabs = new HeaderTabControl<T>
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
},
},
},
};
Tabs.Current.Value = DefaultTab;
Tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Tabs.StripColour = colours.Green;
}
}
}

View File

@ -1,123 +1,123 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListOverlay : WaveOverlayContainer
{
public static readonly float WIDTH_PADDING = 80;
}
public abstract class SearchableListOverlay<T, U, S> : SearchableListOverlay
{
private readonly Container scrollContainer;
protected readonly SearchableListHeader<T> Header;
protected readonly SearchableListFilterControl<U, S> Filter;
protected readonly FillFlowContainer ScrollFlow;
protected abstract Color4 BackgroundColour { get; }
protected abstract Color4 TrianglesColourLight { get; }
protected abstract Color4 TrianglesColourDark { get; }
protected abstract SearchableListHeader<T> CreateHeader();
protected abstract SearchableListFilterControl<U, S> CreateFilterControl();
protected SearchableListOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new[]
{
new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 5,
ColourLight = TrianglesColourLight,
ColourDark = TrianglesColourDark,
},
},
},
scrollContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Children = new[]
{
ScrollFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
Direction = FillDirection.Vertical,
},
},
},
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Header = CreateHeader(),
Filter = CreateFilterControl(),
},
},
};
Filter.Search.Exit = Hide;
}
protected override void Update()
{
base.Update();
scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height };
}
protected override void OnFocus(InputState state)
{
GetContainingInputManager().ChangeFocus(Filter.Search);
}
protected override void PopIn()
{
base.PopIn();
Filter.Search.HoldFocus = true;
}
protected override void PopOut()
{
base.PopOut();
Filter.Search.HoldFocus = false;
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.SearchableList
{
public abstract class SearchableListOverlay : WaveOverlayContainer
{
public static readonly float WIDTH_PADDING = 80;
}
public abstract class SearchableListOverlay<T, U, S> : SearchableListOverlay
{
private readonly Container scrollContainer;
protected readonly SearchableListHeader<T> Header;
protected readonly SearchableListFilterControl<U, S> Filter;
protected readonly FillFlowContainer ScrollFlow;
protected abstract Color4 BackgroundColour { get; }
protected abstract Color4 TrianglesColourLight { get; }
protected abstract Color4 TrianglesColourDark { get; }
protected abstract SearchableListHeader<T> CreateHeader();
protected abstract SearchableListFilterControl<U, S> CreateFilterControl();
protected SearchableListOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = BackgroundColour,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new[]
{
new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 5,
ColourLight = TrianglesColourLight,
ColourDark = TrianglesColourDark,
},
},
},
scrollContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new OsuScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Children = new[]
{
ScrollFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
Direction = FillDirection.Vertical,
},
},
},
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Header = CreateHeader(),
Filter = CreateFilterControl(),
},
},
};
Filter.Search.Exit = Hide;
}
protected override void Update()
{
base.Update();
scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height };
}
protected override void OnFocus(InputState state)
{
GetContainingInputManager().ChangeFocus(Filter.Search);
}
protected override void PopIn()
{
base.PopIn();
Filter.Search.HoldFocus = true;
}
protected override void PopOut()
{
base.PopOut();
Filter.Search.HoldFocus = false;
}
}
}

View File

@ -1,43 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Overlays.SearchableList
{
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
{
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
protected override DropdownMenu CreateMenu() => new SlimMenu();
private class SlimDropdownHeader : OsuDropdownHeader
{
public SlimDropdownHeader()
{
Height = 25;
Icon.Size = new Vector2(16);
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 };
}
protected override void LoadComplete()
{
base.LoadComplete();
BackgroundColour = Color4.Black.Opacity(0.25f);
}
}
private class SlimMenu : OsuDropdownMenu
{
public SlimMenu()
{
BackgroundColour = Color4.Black.Opacity(0.7f);
}
}
}
}
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Overlays.SearchableList
{
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
{
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
protected override DropdownMenu CreateMenu() => new SlimMenu();
private class SlimDropdownHeader : OsuDropdownHeader
{
public SlimDropdownHeader()
{
Height = 25;
Icon.Size = new Vector2(16);
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 };
}
protected override void LoadComplete()
{
base.LoadComplete();
BackgroundColour = Color4.Black.Opacity(0.25f);
}
}
private class SlimMenu : OsuDropdownMenu
{
public SlimMenu()
{
BackgroundColour = Color4.Black.Opacity(0.7f);
}
}
}
}

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