Merge branch 'master' into history-graph

This commit is contained in:
Bartłomiej Dach
2020-03-05 19:52:57 +01:00
499 changed files with 12506 additions and 5556 deletions

View File

@ -33,23 +33,26 @@ namespace osu.Game.Overlays.AccountCreation
private OsuTextBox emailTextBox;
private OsuPasswordTextBox passwordTextBox;
private IAPIProvider api;
[Resolved]
private IAPIProvider api { get; set; }
private ShakeContainer registerShake;
private IEnumerable<Drawable> characterCheckText;
private OsuTextBox[] textboxes;
private ProcessingOverlay processingOverlay;
private GameHost host;
private LoadingLayer loadingLayer;
[Resolved]
private GameHost host { get; set; }
[BackgroundDependencyLoader]
private void load(OsuColour colours, IAPIProvider api, GameHost host)
private void load(OsuColour colours)
{
this.api = api;
this.host = host;
FillFlowContainer mainContent;
InternalChildren = new Drawable[]
{
new FillFlowContainer
mainContent = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
@ -121,7 +124,7 @@ namespace osu.Game.Overlays.AccountCreation
},
},
},
processingOverlay = new ProcessingOverlay { Alpha = 0 }
loadingLayer = new LoadingLayer(mainContent)
};
textboxes = new[] { usernameTextBox, emailTextBox, passwordTextBox };
@ -141,7 +144,7 @@ namespace osu.Game.Overlays.AccountCreation
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
processingOverlay.Hide();
loadingLayer.Hide();
if (host?.OnScreenKeyboardOverlapsGameWindow != true)
focusNextTextbox();
@ -159,7 +162,7 @@ namespace osu.Game.Overlays.AccountCreation
emailAddressDescription.ClearErrors();
passwordDescription.ClearErrors();
processingOverlay.Show();
loadingLayer.Show();
Task.Run(() =>
{
@ -192,7 +195,7 @@ namespace osu.Game.Overlays.AccountCreation
}
registerShake.Shake();
processingOverlay.Hide();
loadingLayer.Hide();
return;
}

View File

@ -22,7 +22,9 @@ namespace osu.Game.Overlays.AccountCreation
{
private OsuTextFlowContainer multiAccountExplanationText;
private LinkFlowContainer furtherAssistance;
private IAPIProvider api;
[Resolved(CanBeNull = true)]
private IAPIProvider api { get; set; }
private const string help_centre_url = "/help/wiki/Help_Centre#login";
@ -39,10 +41,8 @@ namespace osu.Game.Overlays.AccountCreation
}
[BackgroundDependencyLoader(true)]
private void load(OsuColour colours, IAPIProvider api, OsuGame game, TextureStore textures)
private void load(OsuColour colours, OsuGame game, TextureStore textures)
{
this.api = api;
if (string.IsNullOrEmpty(api.ProvidedUsername))
return;

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapListingHeader : OverlayHeader
{
protected override ScreenTitle CreateTitle() => new BeatmapListingTitle();
private class BeatmapListingTitle : ScreenTitle
{
public BeatmapListingTitle()
{
Title = @"beatmap";
Section = @"listing";
}
protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}

View File

@ -0,0 +1,127 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osuTK;
using osu.Framework.Bindables;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapListingSearchSection : CompositeDrawable
{
public Bindable<string> Query => textBox.Current;
public Bindable<RulesetInfo> Ruleset => modeFilter.Current;
public Bindable<BeatmapSearchCategory> Category => categoryFilter.Current;
public BeatmapSetInfo BeatmapSet
{
set
{
if (value == null || string.IsNullOrEmpty(value.OnlineInfo.Covers.Cover))
{
beatmapCover.FadeOut(600, Easing.OutQuint);
return;
}
beatmapCover.BeatmapSet = value;
beatmapCover.FadeTo(0.1f, 200, Easing.OutQuint);
}
}
private readonly BeatmapSearchTextBox textBox;
private readonly BeatmapSearchRulesetFilterRow modeFilter;
private readonly BeatmapSearchFilterRow<BeatmapSearchCategory> categoryFilter;
private readonly Box background;
private readonly UpdateableBeatmapSetCover beatmapCover;
public BeatmapListingSearchSection()
{
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both
},
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = beatmapCover = new UpdateableBeatmapSetCover
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
}
},
new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding
{
Vertical = 20,
Horizontal = 40,
},
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
textBox = new BeatmapSearchTextBox
{
RelativeSizeAxes = Axes.X,
},
new ReverseChildIDFillFlowContainer<Drawable>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{
modeFilter = new BeatmapSearchRulesetFilterRow(),
categoryFilter = new BeatmapSearchFilterRow<BeatmapSearchCategory>(@"Categories"),
}
}
}
}
}
});
Category.Value = BeatmapSearchCategory.Leaderboard;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
background.Colour = colourProvider.Dark6;
}
private class BeatmapSearchTextBox : SearchTextBox
{
protected override Color4 SelectionColour => Color4.Gray;
public BeatmapSearchTextBox()
{
PlaceholderText = @"type in keywords...";
}
}
}
}

View File

@ -0,0 +1,108 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics;
using osuTK.Graphics;
using osuTK;
using osu.Framework.Input.Events;
using osu.Game.Overlays.Direct;
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapListingSortTabControl : OverlaySortTabControl<DirectSortCriteria>
{
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>(Overlays.SortDirection.Descending);
public BeatmapListingSortTabControl()
{
Current.Value = DirectSortCriteria.Ranked;
}
protected override SortTabControl CreateControl() => new BeatmapSortTabControl
{
SortDirection = { BindTarget = SortDirection }
};
private class BeatmapSortTabControl : SortTabControl
{
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
protected override TabItem<DirectSortCriteria> CreateTabItem(DirectSortCriteria value) => new BeatmapSortTabItem(value)
{
SortDirection = { BindTarget = SortDirection }
};
}
private class BeatmapSortTabItem : SortTabItem
{
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
public BeatmapSortTabItem(DirectSortCriteria value)
: base(value)
{
}
protected override TabButton CreateTabButton(DirectSortCriteria value) => new BeatmapTabButton(value)
{
Active = { BindTarget = Active },
SortDirection = { BindTarget = SortDirection }
};
}
private class BeatmapTabButton : TabButton
{
public readonly Bindable<SortDirection> SortDirection = new Bindable<SortDirection>();
protected override Color4 ContentColour
{
set
{
base.ContentColour = value;
icon.Colour = value;
}
}
private readonly SpriteIcon icon;
public BeatmapTabButton(DirectSortCriteria value)
: base(value)
{
Add(icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AlwaysPresent = true,
Alpha = 0,
Size = new Vector2(6)
});
}
protected override void LoadComplete()
{
base.LoadComplete();
SortDirection.BindValueChanged(direction =>
{
icon.Icon = direction.NewValue == Overlays.SortDirection.Ascending ? FontAwesome.Solid.CaretUp : FontAwesome.Solid.CaretDown;
}, true);
}
protected override void UpdateState()
{
base.UpdateState();
icon.FadeTo(Active.Value || IsHovered ? 1 : 0, 200, Easing.OutQuint);
}
protected override bool OnClick(ClickEvent e)
{
if (Active.Value)
SortDirection.Value = SortDirection.Value == Overlays.SortDirection.Ascending ? Overlays.SortDirection.Descending : Overlays.SortDirection.Ascending;
return base.OnClick(e);
}
}
}
}

View File

@ -0,0 +1,173 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapSearchFilterRow<T> : CompositeDrawable, IHasCurrentValue<T>
{
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
public Bindable<T> Current
{
get => current.Current;
set => current.Current = value;
}
public BeatmapSearchFilterRow(string headerName)
{
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
AddInternal(new GridContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.Absolute, size: 100),
new Dimension()
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 10),
Text = headerName.ToUpper()
},
CreateFilter().With(f =>
{
f.Current = current;
})
}
}
});
}
[NotNull]
protected virtual BeatmapSearchFilter CreateFilter() => new BeatmapSearchFilter();
protected class BeatmapSearchFilter : TabControl<T>
{
public BeatmapSearchFilter()
{
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
RelativeSizeAxes = Axes.X;
Height = 15;
TabContainer.Spacing = new Vector2(10, 0);
if (typeof(T).IsEnum)
{
foreach (var val in (T[])Enum.GetValues(typeof(T)))
AddItem(val);
}
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
((FilterDropdown)Dropdown).AccentColour = colourProvider.Light2;
}
protected override Dropdown<T> CreateDropdown() => new FilterDropdown();
protected override TabItem<T> CreateTabItem(T value) => new FilterTabItem(value);
protected class FilterTabItem : TabItem<T>
{
protected virtual float TextSize => 13;
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private readonly OsuSpriteText text;
public FilterTabItem(T value)
: base(value)
{
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomLeft;
Origin = Anchor.BottomLeft;
AddRangeInternal(new Drawable[]
{
text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: TextSize, weight: FontWeight.Regular),
Text = (value as Enum)?.GetDescription() ?? value.ToString()
},
new HoverClickSounds()
});
Enabled.Value = true;
}
[BackgroundDependencyLoader]
private void load()
{
updateState();
}
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
updateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
private void updateState() => text.FadeColour(Active.Value ? Color4.White : getStateColour(), 200, Easing.OutQuint);
private Color4 getStateColour() => IsHovered ? colourProvider.Light1 : colourProvider.Light3;
}
private class FilterDropdown : OsuTabDropdown<T>
{
protected override DropdownHeader CreateHeader() => new FilterHeader
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight
};
private class FilterHeader : OsuTabDropdownHeader
{
public FilterHeader()
{
Background.Height = 1;
}
}
}
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapSearchRulesetFilterRow : BeatmapSearchFilterRow<RulesetInfo>
{
public BeatmapSearchRulesetFilterRow()
: base(@"Mode")
{
}
protected override BeatmapSearchFilter CreateFilter() => new RulesetFilter();
private class RulesetFilter : BeatmapSearchFilter
{
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
AddItem(new RulesetInfo
{
Name = @"Any"
});
foreach (var r in rulesets.AvailableRulesets)
AddItem(r);
}
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Overlays.BeatmapListing
{
public class BeatmapSearchSmallFilterRow<T> : BeatmapSearchFilterRow<T>
{
public BeatmapSearchSmallFilterRow(string headerName)
: base(headerName)
{
}
protected override BeatmapSearchFilter CreateFilter() => new SmallBeatmapSearchFilter();
private class SmallBeatmapSearchFilter : BeatmapSearchFilter
{
protected override TabItem<T> CreateTabItem(T value) => new SmallTabItem(value);
private class SmallTabItem : FilterTabItem
{
public SmallTabItem(T value)
: base(value)
{
}
protected override float TextSize => 10;
}
}
}
}

View File

@ -0,0 +1,313 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Threading;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.BeatmapListing;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class BeatmapListingOverlay : FullscreenOverlay
{
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
[Resolved]
private RulesetStore rulesets { get; set; }
private SearchBeatmapSetsRequest getSetsRequest;
private Drawable currentContent;
private BeatmapListingSearchSection searchSection;
private BeatmapListingSortTabControl sortControl;
public BeatmapListingOverlay()
: base(OverlayColourScheme.Blue)
{
}
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourProvider.Background6
},
new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
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 BeatmapListingHeader(),
searchSection = new BeatmapListingSearchSection(),
}
},
new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourProvider.Background4,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
Height = 40,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourProvider.Background5
},
sortControl = new BeatmapListingSortTabControl
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 20 }
}
}
},
new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Horizontal = 20 },
Children = new Drawable[]
{
panelTarget = new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
},
loadingLayer = new LoadingLayer(panelTarget),
}
},
}
}
}
}
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
var sortCriteria = sortControl.Current;
var sortDirection = sortControl.SortDirection;
searchSection.Query.BindValueChanged(query =>
{
sortCriteria.Value = string.IsNullOrEmpty(query.NewValue) ? DirectSortCriteria.Ranked : DirectSortCriteria.Relevance;
sortDirection.Value = SortDirection.Descending;
queueUpdateSearch(true);
});
searchSection.Ruleset.BindValueChanged(_ => queueUpdateSearch());
searchSection.Category.BindValueChanged(_ => queueUpdateSearch());
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
sortDirection.BindValueChanged(_ => queueUpdateSearch());
}
private ScheduledDelegate queryChangedDebounce;
private LoadingLayer loadingLayer;
private Container panelTarget;
private void queueUpdateSearch(bool queryTextChanged = false)
{
getSetsRequest?.Cancel();
queryChangedDebounce?.Cancel();
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, queryTextChanged ? 500 : 100);
}
private void updateSearch()
{
if (!IsLoaded)
return;
if (State.Value == Visibility.Hidden)
return;
if (API == null)
return;
previewTrackManager.StopAnyPlaying(this);
loadingLayer.Show();
getSetsRequest = new SearchBeatmapSetsRequest(
searchSection.Query.Value,
searchSection.Ruleset.Value,
searchSection.Category.Value,
sortControl.Current.Value,
sortControl.SortDirection.Value);
getSetsRequest.Success += response => Schedule(() => recreatePanels(response));
API.Queue(getSetsRequest);
}
private void recreatePanels(SearchBeatmapSetsResponse response)
{
if (response.Total == 0)
{
searchSection.BeatmapSet = null;
LoadComponentAsync(new NotFoundDrawable(), addContentToPlaceholder);
return;
}
var beatmaps = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
var newPanels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(10),
Alpha = 0,
Margin = new MarginPadding { Vertical = 15 },
ChildrenEnumerable = beatmaps.Select<BeatmapSetInfo, DirectPanel>(b => new DirectGridPanel(b)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
})
};
LoadComponentAsync(newPanels, loaded =>
{
addContentToPlaceholder(loaded);
searchSection.BeatmapSet = beatmaps.First();
});
}
private void addContentToPlaceholder(Drawable content)
{
loadingLayer.Hide();
Drawable lastContent = currentContent;
if (lastContent != null)
{
lastContent.FadeOut(100, Easing.OutQuint).Expire();
// Consider the case when the new content is smaller than the last content.
// If the auto-size computation is delayed until fade out completes, the background remain high for too long making the resulting transition to the smaller height look weird.
// At the same time, if the last content's height is bypassed immediately, there is a period where the new content is at Alpha = 0 when the auto-sized height will be 0.
// To resolve both of these issues, the bypass is delayed until a point when the content transitions (fade-in and fade-out) overlap and it looks good to do so.
lastContent.Delay(25).Schedule(() => lastContent.BypassAutoSizeAxes = Axes.Y);
}
panelTarget.Add(currentContent = content);
currentContent.FadeIn(200, Easing.OutQuint);
}
protected override void Dispose(bool isDisposing)
{
getSetsRequest?.Cancel();
queryChangedDebounce?.Cancel();
base.Dispose(isDisposing);
}
private class NotFoundDrawable : CompositeDrawable
{
public NotFoundDrawable()
{
RelativeSizeAxes = Axes.X;
Height = 250;
Alpha = 0;
Margin = new MarginPadding { Top = 15 };
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
AddInternal(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get(@"Online/not-found")
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = @"... nope, nothing found.",
}
}
});
}
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -50,7 +51,7 @@ namespace osu.Game.Overlays.BeatmapSet
fields.Children = new Drawable[]
{
new Field("mapped by", BeatmapSet.Metadata.Author.Username, OsuFont.GetFont(weight: FontWeight.Regular, italics: true)),
new Field("submitted on", online.Submitted.ToString(@"MMMM d, yyyy"), OsuFont.GetFont(weight: FontWeight.Bold))
new Field("submitted", online.Submitted, OsuFont.GetFont(weight: FontWeight.Bold))
{
Margin = new MarginPadding { Top = 5 },
},
@ -58,11 +59,11 @@ namespace osu.Game.Overlays.BeatmapSet
if (online.Ranked.HasValue)
{
fields.Add(new Field("ranked on", online.Ranked.Value.ToString(@"MMMM d, yyyy"), OsuFont.GetFont(weight: FontWeight.Bold)));
fields.Add(new Field(online.Status.ToString().ToLowerInvariant(), online.Ranked.Value, OsuFont.GetFont(weight: FontWeight.Bold)));
}
else if (online.LastUpdated.HasValue)
{
fields.Add(new Field("last updated on", online.LastUpdated.Value.ToString(@"MMMM d, yyyy"), OsuFont.GetFont(weight: FontWeight.Bold)));
fields.Add(new Field("last updated", online.LastUpdated.Value, OsuFont.GetFont(weight: FontWeight.Bold)));
}
}
@ -76,7 +77,7 @@ namespace osu.Game.Overlays.BeatmapSet
new Container
{
AutoSizeAxes = Axes.Both,
CornerRadius = 3,
CornerRadius = 4,
Masking = true,
Child = avatar = new UpdateableAvatar
{
@ -87,7 +88,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Radius = 4,
Offset = new Vector2(0f, 1f),
},
},
@ -117,15 +118,34 @@ namespace osu.Game.Overlays.BeatmapSet
new OsuSpriteText
{
Text = $"{first} ",
Font = OsuFont.GetFont(size: 13)
Font = OsuFont.GetFont(size: 11)
},
new OsuSpriteText
{
Text = second,
Font = secondFont.With(size: 13)
Font = secondFont.With(size: 11)
},
};
}
public Field(string first, DateTimeOffset second, FontUsage secondFont)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Children = new[]
{
new OsuSpriteText
{
Text = $"{first} ",
Font = OsuFont.GetFont(size: 13)
},
new DrawableDate(second)
{
Font = secondFont.With(size: 13)
}
};
}
}
}
}

View File

@ -105,7 +105,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
TooltipText = name;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Height = 24f;
Children = new Drawable[]
{
@ -113,7 +113,8 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
new SpriteIcon
@ -121,7 +122,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = FontAwesome.Solid.Square,
Size = new Vector2(13),
Size = new Vector2(12),
Rotation = 45,
Colour = OsuColour.FromHex(@"441288"),
},
@ -130,7 +131,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = icon,
Size = new Vector2(13),
Size = new Vector2(12),
Colour = OsuColour.FromHex(@"f7dd55"),
Scale = new Vector2(0.8f),
},
@ -139,7 +140,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 10 },
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold),
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
},
},
},

View File

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -19,7 +18,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
@ -34,7 +32,6 @@ namespace osu.Game.Overlays.BeatmapSet
public readonly DifficultiesContainer Difficulties;
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet
@ -67,7 +64,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) },
Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2), Bottom = 10 },
OnLostHover = () =>
{
showBeatmap(Beatmap.Value);
@ -77,7 +74,6 @@ namespace osu.Game.Overlays.BeatmapSet
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 10 },
Spacing = new Vector2(5f),
Children = new[]
{
@ -85,13 +81,13 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold)
Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold)
},
starRating = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold),
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold),
Text = "Star Difficulty",
Alpha = 0,
Margin = new MarginPadding { Bottom = 1 },
@ -192,9 +188,11 @@ namespace osu.Game.Overlays.BeatmapSet
public class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
{
private const float transition_duration = 100;
private const float size = 52;
private const float size = 54;
private const float background_size = size - 2;
private readonly Container bg;
private readonly Container background;
private readonly Box backgroundBox;
private readonly DifficultyIcon icon;
public readonly BeatmapInfo Beatmap;
@ -230,16 +228,16 @@ namespace osu.Game.Overlays.BeatmapSet
Children = new Drawable[]
{
bg = new Container
background = new Container
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(background_size),
Masking = true,
CornerRadius = 4,
Child = new Box
Child = backgroundBox = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
Alpha = 0.5f
}
},
icon = new DifficultyIcon(beatmap, shouldShowTooltip: false)
{
@ -273,15 +271,21 @@ namespace osu.Game.Overlays.BeatmapSet
private void fadeIn()
{
bg.FadeIn(transition_duration);
background.FadeIn(transition_duration);
icon.FadeIn(transition_duration);
}
private void fadeOut()
{
bg.FadeOut();
background.FadeOut();
icon.FadeTo(0.7f, transition_duration);
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
backgroundBox.Colour = colourProvider.Background6;
}
}
private class Statistic : FillFlowContainer
@ -314,13 +318,13 @@ namespace osu.Game.Overlays.BeatmapSet
Origin = Anchor.CentreLeft,
Icon = icon,
Shadow = true,
Size = new Vector2(13),
Size = new Vector2(12),
},
text = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true)
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold, italics: true),
},
};
}

View File

@ -0,0 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet
{
public class BeatmapSetLayoutSection : Container
{
public BeatmapSetLayoutSection()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 3,
Offset = new Vector2(0f, 1f),
};
}
}
}

View File

@ -5,7 +5,6 @@ using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
@ -25,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
private readonly BindableBool favourited = new BindableBool();
private PostBeatmapFavouriteRequest request;
private DimmedLoadingLayer loading;
private LoadingLayer loading;
private readonly Bindable<User> localUser = new Bindable<User>();
@ -54,14 +53,11 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
Size = new Vector2(18),
Shadow = false,
},
loading = new DimmedLoadingLayer(0.8f, 0.5f),
loading = new LoadingLayer(icon, false),
});
Action = () =>
{
if (loading.State.Value == Visibility.Visible)
return;
// guaranteed by disabled state above.
Debug.Assert(BeatmapSet.Value.OnlineBeatmapSetID != null);

View File

@ -22,6 +22,8 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class HeaderDownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip
{
private const int text_size = 12;
private readonly bool noVideo;
public string TooltipText => button.Enabled.Value ? "download this beatmap" : "login to download";
@ -80,8 +82,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.Solid.Download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
Size = new Vector2(18),
},
}
},
@ -120,7 +121,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
new OsuSpriteText
{
Text = "Downloading...",
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold)
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
},
};
break;
@ -131,7 +132,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
new OsuSpriteText
{
Text = "Importing...",
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold)
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
},
};
break;
@ -146,12 +147,12 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
new OsuSpriteText
{
Text = "Download",
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Bold)
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
},
new OsuSpriteText
{
Text = getVideoSuffixText(),
Font = OsuFont.GetFont(size: 11, weight: FontWeight.Bold)
Font = OsuFont.GetFont(size: text_size - 2, weight: FontWeight.Bold)
},
};
this.FadeIn(200);

View File

@ -74,7 +74,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 10 },
Padding = new MarginPadding { Vertical = 10 }
},
},
new DetailBox

View File

@ -1,4 +1,4 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
@ -45,7 +45,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly FavouriteButton favouriteButton;
private readonly FillFlowContainer fadeContent;
private readonly LoadingAnimation loading;
private readonly LoadingSpinner loading;
private readonly BeatmapSetHeader beatmapSetHeader;
[Cached(typeof(IBindable<RulesetInfo>))]
@ -144,12 +144,15 @@ namespace osu.Game.Overlays.BeatmapSet
},
}
},
artist = new OsuSpriteText { Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true) },
artist = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true),
Margin = new MarginPadding { Bottom = 20 }
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 20 },
Child = author = new AuthorInfo(),
},
beatmapAvailability = new BeatmapAvailability(),
@ -176,7 +179,7 @@ namespace osu.Game.Overlays.BeatmapSet
},
}
},
loading = new LoadingAnimation
loading = new LoadingSpinner
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -3,25 +3,23 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.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 metadata_width = 175;
private const float spacing = 20;
private const float base_height = 220;
private readonly Box successRateBackground;
private readonly Box background;
@ -41,15 +39,7 @@ namespace osu.Game.Overlays.BeatmapSet
OsuSpriteText unrankedPlaceholder;
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),
};
Height = base_height;
Children = new Drawable[]
{
@ -135,6 +125,7 @@ namespace osu.Game.Overlays.BeatmapSet
var setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0;
successRate.Alpha = setHasLeaderboard ? 1 : 0;
unrankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1;
Height = setHasLeaderboard ? 270 : base_height;
};
}
@ -176,8 +167,8 @@ namespace osu.Game.Overlays.BeatmapSet
new OsuSpriteText
{
Text = title,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black),
Margin = new MarginPadding { Top = 20 },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
Margin = new MarginPadding { Top = 15 },
},
textFlow = new OsuTextFlowContainer
{

View File

@ -3,7 +3,6 @@
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osuTK.Graphics;
using osu.Framework.Graphics.UserInterface;
@ -37,7 +36,6 @@ namespace osu.Game.Overlays.BeatmapSet
public ScopeSelectorTabItem(BeatmapLeaderboardScope value)
: base(value)
{
Text.Font = OsuFont.GetFont(size: 16);
}
protected override bool OnHover(HoverEvent e)

View File

@ -17,13 +17,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
private readonly Box background;
public DrawableTopScore(ScoreInfo score, int position = 1)
public DrawableTopScore(ScoreInfo score, int? position = 1)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 5;
CornerRadius = 4;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
@ -46,7 +46,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Vertical = 10,
Left = 10,
Right = 25,
Right = 30,
},
Children = new Drawable[]
{

View File

@ -21,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
new OsuSpriteText
@ -29,9 +29,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = @"You need to be an osu!supporter to access the friend and country rankings!",
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
},
text = new LinkFlowContainer(t => t.Font = t.Font.With(size: 12))
text = new LinkFlowContainer(t => t.Font = t.Font.With(size: 11))
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public class ScoreTable : TableContainer
{
private const float horizontal_inset = 20;
private const float row_height = 25;
private const float row_height = 22;
private const int text_size = 12;
private readonly FillFlowContainer backgroundFlow;
@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
return;
for (int i = 0; i < value.Count; i++)
backgroundFlow.Add(new ScoreTableRowBackground(i, value[i]));
backgroundFlow.Add(new ScoreTableRowBackground(i, value[i], row_height));
Columns = createHeaders(value[0]);
Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
@ -77,17 +77,20 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
new TableColumn("rank", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), // grade
new TableColumn("score", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 60, maxSize: 70)),
new TableColumn("player", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 150)),
new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 110))
new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, minSize: 60, maxSize: 70)),
new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 25)), // flag
new TableColumn("player", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 125)),
new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120))
};
foreach (var statistic in score.SortedStatistics)
columns.Add(new TableColumn(statistic.Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 50, maxSize: 70)));
foreach (var statistic in score.SortedStatistics.Take(score.SortedStatistics.Count() - 1))
columns.Add(new TableColumn(statistic.Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60)));
columns.Add(new TableColumn(score.SortedStatistics.LastOrDefault().Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 45, maxSize: 95)));
columns.AddRange(new[]
{
new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 40, maxSize: 70)),
new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 30)),
new TableColumn("mods", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
});
@ -96,6 +99,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private Drawable[] createContent(int index, ScoreInfo score)
{
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: text_size)) { AutoSizeAxes = Axes.Both };
username.AddUserLink(score.User);
var content = new List<Drawable>
{
new OsuSpriteText
@ -105,7 +111,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
},
new UpdateableRank(score.Rank)
{
Size = new Vector2(30, 20)
Size = new Vector2(28, 14)
},
new OsuSpriteText
{
@ -120,35 +126,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Font = OsuFont.GetFont(size: text_size),
Colour = score.Accuracy == 1 ? highAccuracyColour : Color4.White
},
};
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: text_size)) { AutoSizeAxes = Axes.Both };
username.AddUserLink(score.User);
content.AddRange(new Drawable[]
{
new FillFlowContainer
new UpdateableFlag(score.User.Country)
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Right = horizontal_inset },
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new UpdateableFlag(score.User.Country)
{
Size = new Vector2(20, 13),
ShowPlaceholderOnNull = false,
},
username
}
Size = new Vector2(19, 13),
ShowPlaceholderOnNull = false,
},
username,
new OsuSpriteText
{
Text = $@"{score.MaxCombo:N0}x",
Font = OsuFont.GetFont(size: text_size)
Font = OsuFont.GetFont(size: text_size),
Colour = score.MaxCombo == score.Beatmap?.MaxCombo ? highAccuracyColour : Color4.White
}
});
};
foreach (var kvp in score.SortedStatistics)
{

View File

@ -22,13 +22,13 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly int index;
private readonly ScoreInfo score;
public ScoreTableRowBackground(int index, ScoreInfo score)
public ScoreTableRowBackground(int index, ScoreInfo score, float height)
{
this.index = index;
this.score = score;
RelativeSizeAxes = Axes.X;
Height = 25;
Height = height;
CornerRadius = 5;
Masking = true;

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osuTK;
using System.Linq;
using osu.Game.Online.API.Requests.Responses;
@ -13,13 +12,14 @@ using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Bindables;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ScoresContainer : CompositeDrawable
public class ScoresContainer : BeatmapSetLayoutSection
{
private const int spacing = 15;
@ -31,10 +31,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly Box background;
private readonly ScoreTable scoreTable;
private readonly FillFlowContainer topScoresContainer;
private readonly DimmedLoadingLayer loading;
private readonly LoadingLayer loading;
private readonly LeaderboardModSelector modSelector;
private readonly NoScoresPlaceholder noScoresPlaceholder;
private readonly FillFlowContainer content;
private readonly NotSupporterPlaceholder notSupporterPlaceholder;
[Resolved]
@ -76,23 +75,21 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public ScoresContainer()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
AddRange(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
content = new FillFlowContainer
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.95f,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Vertical = spacing },
Padding = new MarginPadding { Horizontal = 50 },
Margin = new MarginPadding { Vertical = 20 },
Children = new Drawable[]
{
new FillFlowContainer
@ -121,7 +118,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Vertical = spacing },
Margin = new MarginPadding { Top = spacing },
Children = new Drawable[]
{
noScoresPlaceholder = new NoScoresPlaceholder
@ -160,21 +157,12 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
}
}
},
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerRadius = 5,
Child = loading = new DimmedLoadingLayer(iconScale: 0.8f)
{
Alpha = 0,
},
}
loading = new LoadingLayer()
}
}
}
},
};
}
});
}
[BackgroundDependencyLoader]
@ -191,8 +179,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
scope.BindValueChanged(_ => getScores());
ruleset.BindValueChanged(_ => getScores());
modSelector.SelectedMods.ItemsAdded += _ => getScores();
modSelector.SelectedMods.ItemsRemoved += _ => getScores();
modSelector.SelectedMods.CollectionChanged += (_, __) => getScores();
Beatmap.BindValueChanged(onBeatmapChanged);
user.BindValueChanged(onUserChanged, true);
@ -233,7 +220,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
if (Beatmap.Value?.OnlineBeatmapID.HasValue != true || Beatmap.Value.Status <= BeatmapSetOnlineStatus.Pending)
{
Scores = null;
content.Hide();
Hide();
return;
}
@ -247,7 +234,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
notSupporterPlaceholder.Hide();
content.Show();
Show();
loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap.Value, Beatmap.Value.Ruleset, scope.Value, modSelector.SelectedMods);

View File

@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private const float bottom_columns_min_width = 45;
private readonly FontUsage smallFont = OsuFont.GetFont(size: 16);
private readonly FontUsage largeFont = OsuFont.GetFont(size: 22);
private readonly FontUsage largeFont = OsuFont.GetFont(size: 22, weight: FontWeight.Light);
private readonly TextColumn totalScoreColumn;
private readonly TextColumn accuracyColumn;
@ -47,7 +47,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10, 8),
Children = new Drawable[]
{
new FillFlowContainer
@ -117,6 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public InfoColumn(string title, Drawable content, float? minWidth = null)
{
AutoSizeAxes = Axes.Both;
Margin = new MarginPadding { Vertical = 5 };
InternalChild = new GridContainer
{
@ -128,7 +128,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Absolute, 4),
new Dimension(GridSizeMode.Absolute, 2),
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
@ -138,21 +138,24 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold),
Text = title.ToUpper()
Text = title.ToUpper(),
// 2px padding bottom + 1px vertical to compensate for the additional spacing because of 1.25 line-height in osu-web
Padding = new MarginPadding { Top = 1, Bottom = 3 }
}
},
new Drawable[]
{
separator = new Box
{
Anchor = Anchor.CentreLeft,
Anchor = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Height = 2
}
Height = 2,
},
},
new[]
{
content
// osu-web has 4px margin here but also uses 0.9 line-height, reducing margin to 2px seems like a good alternative to that
content.With(c => c.Margin = new MarginPadding { Top = 2 })
}
}
};
@ -194,9 +197,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
public ModsInfoColumn()
: this(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
AutoSizeAxes = Axes.X,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(1),
Height = 18f
})
{
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -12,7 +13,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Scoring;
using osu.Game.Users.Drawables;
using osu.Game.Utils;
using osuTK;
using osuTK.Graphics;
@ -24,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly UpdateableRank rank;
private readonly UpdateableAvatar avatar;
private readonly LinkFlowContainer usernameText;
private readonly SpriteText date;
private readonly DrawableDate achievedOn;
private readonly UpdateableFlag flag;
public TopScoreUserSection()
@ -67,7 +67,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Origin = Anchor.Centre,
Size = new Vector2(70),
Masking = true,
CornerRadius = 5,
CornerRadius = 4,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
@ -92,11 +92,24 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
},
date = new OsuSpriteText
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 10)
Children = new[]
{
new OsuSpriteText
{
Text = "achieved ",
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold)
},
achievedOn = new DrawableDate(DateTimeOffset.MinValue)
{
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold)
},
}
},
flag = new UpdateableFlag
{
@ -112,9 +125,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
};
}
public int ScorePosition
public int? ScorePosition
{
set => rankText.Text = $"#{value}";
set => rankText.Text = value == null ? "-" : $"#{value}";
}
/// <summary>
@ -126,7 +139,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
avatar.User = value.User;
flag.Country = value.User.Country;
date.Text = $@"achieved {HumanizerUtils.Humanize(value.Date)}";
achievedOn.Date = value.Date;
usernameText.Clear();
usernameText.AddUserLink(value.User);

View File

@ -65,7 +65,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Success Rate",
Font = OsuFont.GetFont(size: 13)
Font = OsuFont.GetFont(size: 12)
},
successRate = new Bar
{
@ -82,7 +82,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre,
Font = OsuFont.GetFont(size: 13),
Font = OsuFont.GetFont(size: 12),
},
},
new OsuSpriteText
@ -90,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapSet
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "Points of Failure",
Font = OsuFont.GetFont(size: 13),
Font = OsuFont.GetFont(size: 12),
Margin = new MarginPadding { Vertical = 20 },
},
},

View File

@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Overlays.Comments;
using osu.Game.Rulesets;
using osuTK;
@ -25,7 +26,8 @@ namespace osu.Game.Overlays
public const float RIGHT_WIDTH = 275;
protected readonly Header Header;
private RulesetStore rulesets;
[Resolved]
private RulesetStore rulesets { get; set; }
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
@ -39,6 +41,7 @@ namespace osu.Game.Overlays
{
OsuScrollContainer scroll;
Info info;
CommentsSection comments;
Children = new Drawable[]
{
@ -50,29 +53,33 @@ namespace osu.Game.Overlays
{
RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false,
Child = new ReverseChildIDFillFlowContainer<Drawable>
Child = new ReverseChildIDFillFlowContainer<BeatmapSetLayoutSection>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new Drawable[]
Children = new[]
{
new ReverseChildIDFillFlowContainer<Drawable>
new BeatmapSetLayoutSection
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
Child = new ReverseChildIDFillFlowContainer<Drawable>
{
Header = new Header(),
info = new Info()
}
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Header = new Header(),
info = new Info()
}
},
},
new ScoresContainer
{
Beatmap = { BindTarget = Header.Picker.Beatmap }
}
},
comments = new CommentsSection()
},
},
},
@ -80,6 +87,7 @@ namespace osu.Game.Overlays
Header.BeatmapSet.BindTo(beatmapSet);
info.BeatmapSet.BindTo(beatmapSet);
comments.BeatmapSet.BindTo(beatmapSet);
Header.Picker.Beatmap.ValueChanged += b =>
{
@ -90,10 +98,8 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
private void load()
{
this.rulesets = rulesets;
background.Colour = ColourProvider.Background6;
}
@ -144,5 +150,30 @@ namespace osu.Game.Overlays
beatmapSet.Value = set;
Show();
}
private class CommentsSection : BeatmapSetLayoutSection
{
public readonly Bindable<BeatmapSetInfo> BeatmapSet = new Bindable<BeatmapSetInfo>();
public CommentsSection()
{
CommentsContainer comments;
Add(comments = new CommentsContainer());
BeatmapSet.BindValueChanged(beatmapSet =>
{
if (beatmapSet.NewValue?.OnlineBeatmapSetID is int onlineBeatmapSetID)
{
Show();
comments.ShowComments(CommentableType.Beatmapset, onlineBeatmapSetID);
}
else
{
Hide();
}
}, true);
}
}
}
}

View File

@ -16,6 +16,7 @@ using osuTK.Graphics;
using osu.Framework.Allocation;
using System.Net;
using osuTK;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Overlays.Changelog
{
@ -51,28 +52,27 @@ namespace osu.Game.Overlays.Changelog
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load(OsuColour colours, OverlayColourProvider colourProvider)
{
foreach (var categoryEntries in Build.ChangelogEntries.GroupBy(b => b.Category).OrderBy(c => c.Key))
{
ChangelogEntries.Add(new OsuSpriteText
{
Text = categoryEntries.Key,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 24),
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18),
Margin = new MarginPadding { Top = 35, Bottom = 15 },
});
var fontLarge = OsuFont.GetFont(size: 18);
var fontMedium = OsuFont.GetFont(size: 14);
var fontSmall = OsuFont.GetFont(size: 12);
var fontLarge = OsuFont.GetFont(size: 16);
var fontMedium = OsuFont.GetFont(size: 12);
foreach (APIChangelogEntry entry in categoryEntries)
foreach (var entry in categoryEntries)
{
var entryColour = entry.Major ? colours.YellowLight : Color4.White;
LinkFlowContainer title;
Container titleContainer = new Container
var titleContainer = new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@ -83,9 +83,9 @@ namespace osu.Game.Overlays.Changelog
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Size = new Vector2(fontSmall.Size),
Size = new Vector2(10),
Icon = entry.Type == ChangelogEntryType.Fix ? FontAwesome.Solid.Check : FontAwesome.Solid.Plus,
Colour = entryColour,
Colour = entryColour.Opacity(0.5f),
Margin = new MarginPadding { Right = 5 },
},
title = new LinkFlowContainer
@ -123,10 +123,11 @@ namespace osu.Game.Overlays.Changelog
});
}
title.AddText(" by ", t =>
title.AddText("by ", t =>
{
t.Font = fontMedium;
t.Font = fontMedium.With(italics: true);
t.Colour = entryColour;
t.Padding = new MarginPadding { Left = 10 };
});
if (entry.GithubUser.UserId != null)
@ -137,7 +138,7 @@ namespace osu.Game.Overlays.Changelog
Id = entry.GithubUser.UserId.Value
}, t =>
{
t.Font = fontMedium;
t.Font = fontMedium.With(italics: true);
t.Colour = entryColour;
});
}
@ -145,7 +146,7 @@ namespace osu.Game.Overlays.Changelog
{
title.AddLink(entry.GithubUser.DisplayName, entry.GithubUser.GithubUrl, t =>
{
t.Font = fontMedium;
t.Font = fontMedium.With(italics: true);
t.Colour = entryColour;
});
}
@ -153,7 +154,7 @@ namespace osu.Game.Overlays.Changelog
{
title.AddText(entry.GithubUser.DisplayName, t =>
{
t.Font = fontSmall;
t.Font = fontMedium.With(italics: true);
t.Colour = entryColour;
});
}
@ -162,7 +163,7 @@ namespace osu.Game.Overlays.Changelog
if (!string.IsNullOrEmpty(entry.MessageHtml))
{
TextFlowContainer message = new TextFlowContainer
var message = new TextFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
@ -171,8 +172,8 @@ namespace osu.Game.Overlays.Changelog
// todo: use markdown parsing once API returns markdown
message.AddText(WebUtility.HtmlDecode(Regex.Replace(entry.MessageHtml, @"<(.|\n)*?>", string.Empty)), t =>
{
t.Font = fontSmall;
t.Colour = new Color4(235, 184, 254, 255);
t.Font = fontMedium;
t.Colour = colourProvider.Foreground1;
});
ChangelogEntries.Add(message);

View File

@ -19,7 +19,6 @@ namespace osu.Game.Overlays.Changelog
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Padding = new MarginPadding { Bottom = 100 };
}
}
}

View File

@ -4,9 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
@ -22,6 +24,8 @@ namespace osu.Game.Overlays.Changelog
private const string listing_string = "listing";
private Box streamsBackground;
public ChangelogHeader()
{
TabControl.AddItem(listing_string);
@ -40,6 +44,12 @@ namespace osu.Game.Overlays.Changelog
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
streamsBackground.Colour = colourProvider.Background5;
}
private ChangelogHeaderTitle title;
private void showBuild(ValueChangedEvent<APIChangelogBuild> e)
@ -72,7 +82,21 @@ namespace osu.Game.Overlays.Changelog
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
Streams = new UpdateStreamBadgeArea(),
streamsBackground = new Box
{
RelativeSizeAxes = Axes.Both
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding
{
Horizontal = 65,
Vertical = 20
},
Child = Streams = new UpdateStreamBadgeArea()
}
}
};

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.API.Requests.Responses;
using osuTK.Graphics;
namespace osu.Game.Overlays.Changelog
{
@ -24,13 +23,13 @@ namespace osu.Game.Overlays.Changelog
}
[BackgroundDependencyLoader]
private void load()
private void load(OverlayColourProvider colourProvider)
{
DateTime currentDate = DateTime.MinValue;
var currentDate = DateTime.MinValue;
if (entries == null) return;
foreach (APIChangelogBuild build in entries)
foreach (var build in entries)
{
if (build.CreatedAt.Date != currentDate)
{
@ -40,7 +39,7 @@ namespace osu.Game.Overlays.Changelog
{
RelativeSizeAxes = Axes.X,
Height = 2,
Colour = new Color4(17, 17, 17, 255),
Colour = colourProvider.Background6,
Margin = new MarginPadding { Top = 30 },
});
}
@ -49,10 +48,9 @@ namespace osu.Game.Overlays.Changelog
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 15 },
Text = build.CreatedAt.Date.ToString("dd MMM yyyy"),
Margin = new MarginPadding { Top = 20 },
Text = build.CreatedAt.Date.ToString("dd MMMM yyyy"),
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 24),
Colour = OsuColour.FromHex(@"FD5"),
});
currentDate = build.CreatedAt.Date;
@ -68,7 +66,7 @@ namespace osu.Game.Overlays.Changelog
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = new Color4(32, 24, 35, 255),
Colour = colourProvider.Background6,
}
});
}

View File

@ -8,6 +8,7 @@ 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.Game.Graphics;
using osu.Game.Graphics.Containers;
@ -16,6 +17,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Comments;
using osuTK;
namespace osu.Game.Overlays.Changelog
@ -30,7 +32,7 @@ namespace osu.Game.Overlays.Changelog
}
[BackgroundDependencyLoader]
private void load(CancellationToken? cancellation, IAPIProvider api)
private void load(CancellationToken? cancellation, IAPIProvider api, OverlayColourProvider colourProvider)
{
bool complete = false;
@ -57,11 +59,22 @@ namespace osu.Game.Overlays.Changelog
if (build != null)
{
CommentsContainer comments;
Children = new Drawable[]
{
new ChangelogBuildWithNavigation(build) { SelectBuild = SelectBuild },
new Comments(build)
new Box
{
RelativeSizeAxes = Axes.X,
Height = 2,
Colour = colourProvider.Background6,
Margin = new MarginPadding { Top = 30 },
},
comments = new CommentsContainer()
};
comments.ShowComments(CommentableType.Build, build.Id);
}
}
@ -72,6 +85,8 @@ namespace osu.Game.Overlays.Changelog
{
}
private OsuSpriteText date;
protected override FillFlowContainer CreateHeader()
{
var fill = base.CreateHeader();
@ -81,11 +96,10 @@ namespace osu.Game.Overlays.Changelog
existing.Scale = new Vector2(1.25f);
existing.Action = null;
existing.Add(new OsuSpriteText
existing.Add(date = new OsuSpriteText
{
Text = Build.CreatedAt.Date.ToString("dd MMM yyyy"),
Text = Build.CreatedAt.Date.ToString("dd MMMM yyyy"),
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 14),
Colour = OsuColour.FromHex(@"FD5"),
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 5 },
@ -105,6 +119,12 @@ namespace osu.Game.Overlays.Changelog
return fill;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
date.Colour = colourProvider.Light1;
}
}
private class NavigationIconButton : IconButton

View File

@ -3,8 +3,6 @@
using Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -16,99 +14,81 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Changelog
{
public class UpdateStreamBadge : TabItem<APIUpdateStream>
{
private const float badge_height = 66.5f;
private const float badge_width = 100;
private const float transition_duration = 100;
private readonly ExpandingBar expandingBar;
private SampleChannel sampleClick;
private SampleChannel sampleHover;
private readonly FillFlowContainer<SpriteText> text;
public readonly Bindable<APIUpdateStream> SelectedTab = new Bindable<APIUpdateStream>();
private readonly Container fadeContainer;
private readonly APIUpdateStream stream;
private FillFlowContainer<SpriteText> text;
private ExpandingBar expandingBar;
public UpdateStreamBadge(APIUpdateStream stream)
: base(stream)
{
Size = new Vector2(stream.IsFeatured ? badge_width * 2 : badge_width, badge_height);
Padding = new MarginPadding(5);
Child = fadeContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
text = new FillFlowContainer<SpriteText>
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = stream.DisplayName,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 12),
Margin = new MarginPadding { Top = 6 },
},
new OsuSpriteText
{
Text = stream.LatestBuild.DisplayVersion,
Font = OsuFont.GetFont(weight: FontWeight.Light, size: 16),
},
new OsuSpriteText
{
Text = stream.LatestBuild.Users > 0 ? $"{stream.LatestBuild.Users:N0} {"user".Pluralize(stream.LatestBuild.Users == 1)} online" : null,
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 10),
Colour = new Color4(203, 164, 218, 255),
},
}
},
expandingBar = new ExpandingBar
{
Anchor = Anchor.TopCentre,
Colour = stream.Colour,
ExpandedSize = 4,
CollapsedSize = 2,
IsCollapsed = true
},
}
};
SelectedTab.BindValueChanged(_ => updateState(), true);
this.stream = stream;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
private void load(OverlayColourProvider colourProvider)
{
sampleClick = audio.Samples.Get(@"UI/generic-select-soft");
sampleHover = audio.Samples.Get(@"UI/generic-hover-soft");
Size = new Vector2(stream.IsFeatured ? badge_width * 2 : badge_width, 60);
Padding = new MarginPadding(5);
AddRange(new Drawable[]
{
text = new FillFlowContainer<SpriteText>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 6 },
Children = new[]
{
new OsuSpriteText
{
Text = stream.DisplayName,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
},
new OsuSpriteText
{
Text = stream.LatestBuild.DisplayVersion,
Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular),
},
new OsuSpriteText
{
Text = stream.LatestBuild.Users > 0 ? $"{"user".ToQuantity(stream.LatestBuild.Users, "N0")} online" : null,
Font = OsuFont.GetFont(size: 10),
Colour = colourProvider.Foreground1
},
}
},
expandingBar = new ExpandingBar
{
Anchor = Anchor.TopCentre,
Colour = stream.Colour,
ExpandedSize = 4,
CollapsedSize = 2,
Expanded = true
},
new HoverClickSounds()
});
SelectedTab.BindValueChanged(_ => updateState(), true);
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
protected override bool OnClick(ClickEvent e)
{
sampleClick?.Play();
return base.OnClick(e);
}
protected override bool OnHover(HoverEvent e)
{
sampleHover?.Play();
updateState();
return base.OnHover(e);
}
@ -120,38 +100,41 @@ namespace osu.Game.Overlays.Changelog
private void updateState()
{
// Expand based on the local state
bool shouldExpand = Active.Value || IsHovered;
// highlighted regardless if we are hovered
bool textHighlighted = IsHovered;
bool barExpanded = IsHovered;
// Expand based on whether no build is selected and the badge area is hovered
shouldExpand |= SelectedTab.Value == null && !externalDimRequested;
if (shouldExpand)
if (SelectedTab.Value == null)
{
expandingBar.Expand();
fadeContainer.FadeTo(1, transition_duration);
// at listing, all badges are highlighted when user is not hovering any badge.
textHighlighted |= !userHoveringArea;
barExpanded |= !userHoveringArea;
}
else
{
expandingBar.Collapse();
fadeContainer.FadeTo(0.5f, transition_duration);
// bar is always expanded when active
barExpanded |= Active.Value;
// text is highlighted only when hovered or active (but not if in selection mode)
textHighlighted |= Active.Value && !userHoveringArea;
}
text.FadeTo(externalDimRequested && !IsHovered ? 0.5f : 1, transition_duration);
expandingBar.Expanded = barExpanded;
text.FadeTo(textHighlighted ? 1 : 0.5f, transition_duration, Easing.OutQuint);
}
private bool externalDimRequested;
private bool userHoveringArea;
public void EnableDim()
public bool UserHoveringArea
{
externalDimRequested = true;
updateState();
}
set
{
if (value == userHoveringArea)
return;
public void DisableDim()
{
externalDimRequested = false;
updateState();
userHoveringArea = value;
updateState();
}
}
}
}

View File

@ -6,9 +6,7 @@ using osu.Framework.Input.Events;
using osu.Game.Online.API.Requests.Responses;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Overlays.Changelog
{
@ -18,52 +16,36 @@ namespace osu.Game.Overlays.Changelog
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddInternal(new Box
{
Colour = Color4.Black,
Alpha = 0.12f,
RelativeSizeAxes = Axes.Both,
});
}
public void Populate(List<APIUpdateStream> streams)
{
foreach (APIUpdateStream updateStream in streams)
foreach (var updateStream in streams)
AddItem(updateStream);
}
protected override bool OnHover(HoverEvent e)
{
foreach (UpdateStreamBadge streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>())
streamBadge.EnableDim();
foreach (var streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>())
streamBadge.UserHoveringArea = true;
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
foreach (UpdateStreamBadge streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>())
streamBadge.DisableDim();
foreach (var streamBadge in TabContainer.Children.OfType<UpdateStreamBadge>())
streamBadge.UserHoveringArea = false;
base.OnHoverLost(e);
}
protected override TabFillFlowContainer CreateTabFlow()
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
var flow = base.CreateTabFlow();
flow.RelativeSizeAxes = Axes.X;
flow.AutoSizeAxes = Axes.Y;
flow.AllowMultiline = true;
flow.Padding = new MarginPadding
{
Vertical = 20,
Horizontal = 85,
};
return flow;
}
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AllowMultiline = true,
};
protected override Dropdown<APIUpdateStream> CreateDropdown() => null;

View File

@ -13,7 +13,6 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Input.Bindings;
using osu.Game.Online.API.Requests;
@ -42,14 +41,14 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colour)
private void load(AudioManager audio)
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colour.PurpleDarkAlternative,
Colour = ColourProvider.Background4,
},
new OsuScrollContainer
{

View File

@ -30,13 +30,14 @@ namespace osu.Game.Overlays
private const float textbox_height = 60;
private const float channel_selection_min_height = 0.3f;
private ChannelManager channelManager;
[Resolved]
private ChannelManager channelManager { get; set; }
private Container<DrawableChannel> currentChannelContainer;
private readonly List<DrawableChannel> loadedChannels = new List<DrawableChannel>();
private LoadingAnimation loading;
private LoadingSpinner loading;
private FocusedTextBox textbox;
@ -72,7 +73,7 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, OsuColour colours, ChannelManager channelManager)
private void load(OsuConfigManager config, OsuColour colours)
{
const float padding = 5;
@ -145,7 +146,7 @@ namespace osu.Game.Overlays
}
}
},
loading = new LoadingAnimation(),
loading = new LoadingSpinner(),
}
},
tabsArea = new TabsArea
@ -209,8 +210,6 @@ namespace osu.Game.Overlays
chatBackground.Colour = colours.ChatBlue;
this.channelManager = channelManager;
loading.Show();
// This is a relatively expensive (and blocking) operation.

View File

@ -0,0 +1,71 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using 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 osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Comments
{
public abstract class CancellableCommentEditor : CommentEditor
{
public Action OnCancel;
[BackgroundDependencyLoader]
private void load()
{
ButtonsContainer.Add(new CancelButton
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Action = () => OnCancel?.Invoke()
});
}
private class CancelButton : OsuHoverContainer
{
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
private readonly Box background;
public CancelButton()
{
AutoSizeAxes = Axes.Both;
Child = new CircularContainer
{
Masking = true,
Height = 25,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Margin = new MarginPadding { Horizontal = 20 },
Text = @"Cancel"
}
}
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
IdleColour = colourProvider.Light4;
HoverColour = colourProvider.Light3;
}
}
}
}

View File

@ -0,0 +1,241 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Sprites;
using osuTK.Graphics;
using osu.Game.Graphics.UserInterface;
using System.Collections.Generic;
using System;
using osuTK;
using osu.Framework.Bindables;
namespace osu.Game.Overlays.Comments
{
public abstract class CommentEditor : CompositeDrawable
{
private const int side_padding = 8;
public Action<string> OnCommit;
public bool IsLoading
{
get => commitButton.IsLoading;
set => commitButton.IsLoading = value;
}
protected abstract string FooterText { get; }
protected abstract string CommitButtonText { get; }
protected abstract string TextBoxPlaceholder { get; }
protected FillFlowContainer ButtonsContainer { get; private set; }
protected readonly Bindable<string> Current = new Bindable<string>();
private CommitButton commitButton;
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
EditorTextBox textBox;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Masking = true;
CornerRadius = 6;
BorderThickness = 3;
BorderColour = colourProvider.Background3;
AddRangeInternal(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background3
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
textBox = new EditorTextBox
{
Height = 40,
RelativeSizeAxes = Axes.X,
PlaceholderText = TextBoxPlaceholder,
Current = Current
},
new Container
{
Name = "Footer",
RelativeSizeAxes = Axes.X,
Height = 35,
Padding = new MarginPadding { Horizontal = side_padding },
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Text = FooterText
},
ButtonsContainer = new FillFlowContainer
{
Name = "Buttons",
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Child = commitButton = new CommitButton(CommitButtonText)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Action = () =>
{
OnCommit?.Invoke(Current.Value);
Current.Value = string.Empty;
}
}
}
}
}
}
}
});
textBox.OnCommit += (u, v) =>
{
if (commitButton.IsBlocked.Value)
return;
commitButton.Click();
};
}
protected override void LoadComplete()
{
base.LoadComplete();
Current.BindValueChanged(text => commitButton.IsBlocked.Value = string.IsNullOrEmpty(text.NewValue), true);
}
private class EditorTextBox : BasicTextBox
{
protected override float LeftRightPadding => side_padding;
protected override Color4 SelectionColour => Color4.Gray;
private OsuSpriteText placeholder;
public EditorTextBox()
{
Masking = false;
TextContainer.Height = 0.4f;
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
BackgroundUnfocused = BackgroundFocused = colourProvider.Background5;
placeholder.Colour = colourProvider.Background3;
BackgroundCommit = colourProvider.Background3;
}
protected override SpriteText CreatePlaceholder() => placeholder = new OsuSpriteText
{
Font = OsuFont.GetFont(weight: FontWeight.Regular),
};
protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) };
}
private class CommitButton : LoadingButton
{
private const int duration = 200;
public readonly BindableBool IsBlocked = new BindableBool();
public override bool PropagatePositionalInputSubTree => !IsBlocked.Value && base.PropagatePositionalInputSubTree;
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private OsuSpriteText drawableText;
private Box background;
private Box blockedBackground;
public CommitButton(string text)
{
AutoSizeAxes = Axes.Both;
LoadingAnimationSize = new Vector2(10);
drawableText.Text = text;
}
[BackgroundDependencyLoader]
private void load()
{
IdleColour = colourProvider.Light4;
HoverColour = colourProvider.Light3;
blockedBackground.Colour = colourProvider.Background5;
}
protected override void LoadComplete()
{
base.LoadComplete();
IsBlocked.BindValueChanged(onBlockedStateChanged, true);
}
private void onBlockedStateChanged(ValueChangedEvent<bool> isBlocked)
{
drawableText.FadeColour(isBlocked.NewValue ? colourProvider.Foreground1 : Color4.White, duration, Easing.OutQuint);
background.FadeTo(isBlocked.NewValue ? 0 : 1, duration, Easing.OutQuint);
}
protected override Drawable CreateContent() => new CircularContainer
{
Masking = true,
Height = 25,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
blockedBackground = new Box
{
RelativeSizeAxes = Axes.Both
},
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0
},
drawableText = new OsuSpriteText
{
AlwaysPresent = true,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Margin = new MarginPadding { Horizontal = 20 }
}
}
};
protected override void OnLoadStarted() => drawableText.FadeOut(duration, Easing.OutQuint);
protected override void OnLoadFinished() => drawableText.FadeIn(duration, Easing.OutQuint);
}
}
}

View File

@ -18,8 +18,8 @@ namespace osu.Game.Overlays.Comments
{
public class CommentsContainer : CompositeDrawable
{
private CommentableType type;
private long? id;
private readonly Bindable<CommentableType> type = new Bindable<CommentableType>();
private readonly BindableLong id = new BindableLong();
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
public readonly BindableBool ShowDeleted = new BindableBool();
@ -127,8 +127,8 @@ namespace osu.Game.Overlays.Comments
/// <param name="id">The id of the resource to get comments for.</param>
public void ShowComments(CommentableType type, long id)
{
this.type = type;
this.id = id;
this.type.Value = type;
this.id.Value = id;
if (!IsLoaded)
return;
@ -147,12 +147,12 @@ namespace osu.Game.Overlays.Comments
private void getComments()
{
if (!id.HasValue)
if (id.Value <= 0)
return;
request?.Cancel();
loadCancellation?.Cancel();
request = new GetCommentsRequest(type, id.Value, Sort.Value, currentPage++);
request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0);
request.Success += onSuccess;
api.PerformAsync(request);
}
@ -172,7 +172,10 @@ namespace osu.Game.Overlays.Comments
LoadComponentAsync(new CommentsPage(response)
{
ShowDeleted = { BindTarget = ShowDeleted }
ShowDeleted = { BindTarget = ShowDeleted },
Sort = { BindTarget = Sort },
Type = { BindTarget = type },
CommentableId = { BindTarget = id }
}, loaded =>
{
content.Add(loaded);

View File

@ -16,8 +16,6 @@ namespace osu.Game.Overlays.Comments
{
public class CommentsHeader : CompositeDrawable
{
private const int font_size = 14;
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
public readonly BindableBool ShowDeleted = new BindableBool();
@ -40,29 +38,11 @@ namespace osu.Game.Overlays.Comments
Padding = new MarginPadding { Horizontal = 50 },
Children = new Drawable[]
{
new FillFlowContainer
new OverlaySortTabControl<CommentsSortCriteria>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: font_size),
Text = @"Sort by"
},
new SortTabControl
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Current = Sort
}
}
Current = Sort
},
new ShowDeletedButton
{
@ -106,7 +86,7 @@ namespace osu.Game.Overlays.Comments
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: font_size),
Font = OsuFont.GetFont(size: 12),
Text = @"Show deleted"
}
},
@ -126,4 +106,12 @@ namespace osu.Game.Overlays.Comments
}
}
}
public enum CommentsSortCriteria
{
[System.ComponentModel.Description(@"Recent")]
New,
Old,
Top
}
}

View File

@ -9,14 +9,25 @@ using osu.Game.Online.API.Requests.Responses;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using System.Linq;
using osu.Game.Online.API.Requests;
using osu.Game.Online.API;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace osu.Game.Overlays.Comments
{
public class CommentsPage : CompositeDrawable
{
public readonly BindableBool ShowDeleted = new BindableBool();
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
public readonly Bindable<CommentableType> Type = new Bindable<CommentableType>();
public readonly BindableLong CommentableId = new BindableLong();
[Resolved]
private IAPIProvider api { get; set; }
private readonly CommentBundle commentBundle;
private FillFlowContainer flow;
public CommentsPage(CommentBundle commentBundle)
{
@ -26,8 +37,6 @@ namespace osu.Game.Overlays.Comments
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
FillFlowContainer flow;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@ -52,14 +61,74 @@ namespace osu.Game.Overlays.Comments
return;
}
foreach (var c in commentBundle.Comments)
AppendComments(commentBundle);
}
private DrawableComment getDrawableComment(Comment comment)
{
if (CommentDictionary.TryGetValue(comment.Id, out var existing))
return existing;
return CommentDictionary[comment.Id] = new DrawableComment(comment)
{
if (c.IsTopLevel)
ShowDeleted = { BindTarget = ShowDeleted },
Sort = { BindTarget = Sort },
RepliesRequested = onCommentRepliesRequested
};
}
private void onCommentRepliesRequested(DrawableComment drawableComment, int page)
{
var request = new GetCommentsRequest(CommentableId.Value, Type.Value, Sort.Value, page, drawableComment.Comment.Id);
request.Success += response => Schedule(() => AppendComments(response));
api.PerformAsync(request);
}
protected readonly Dictionary<long, DrawableComment> CommentDictionary = new Dictionary<long, DrawableComment>();
/// <summary>
/// Appends retrieved comments to the subtree rooted of comments in this page.
/// </summary>
/// <param name="bundle">The bundle of comments to add.</param>
protected void AppendComments([NotNull] CommentBundle bundle)
{
var orphaned = new List<Comment>();
foreach (var comment in bundle.Comments.Concat(bundle.IncludedComments))
{
// Exclude possible duplicated comments.
if (CommentDictionary.ContainsKey(comment.Id))
continue;
addNewComment(comment);
}
// Comments whose parents were seen later than themselves can now be added.
foreach (var o in orphaned)
addNewComment(o);
void addNewComment(Comment comment)
{
var drawableComment = getDrawableComment(comment);
if (comment.ParentId == null)
{
flow.Add(new DrawableComment(c)
{
ShowDeleted = { BindTarget = ShowDeleted }
});
// Comments that have no parent are added as top-level comments to the flow.
flow.Add(drawableComment);
}
else if (CommentDictionary.TryGetValue(comment.ParentId.Value, out var parentDrawable))
{
// The comment's parent has already been seen, so the parent<-> child links can be added.
comment.ParentComment = parentDrawable.Comment;
parentDrawable.Replies.Add(drawableComment);
}
else
{
// The comment's parent has not been seen yet, so keep it orphaned for the time being. This can occur if the comments arrive out of order.
// Since this comment has now been seen, any further children can be added to it without being orphaned themselves.
orphaned.Add(comment);
}
}
}

View File

@ -12,11 +12,16 @@ using osu.Game.Graphics.Containers;
using osu.Game.Utils;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes;
using System.Linq;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat;
using osu.Framework.Allocation;
using osuTK.Graphics;
using System.Collections.Generic;
using System;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.IEnumerableExtensions;
using System.Collections.Specialized;
namespace osu.Game.Overlays.Comments
{
@ -25,24 +30,37 @@ namespace osu.Game.Overlays.Comments
private const int avatar_size = 40;
private const int margin = 10;
public Action<DrawableComment, int> RepliesRequested;
public readonly Comment Comment;
public readonly BindableBool ShowDeleted = new BindableBool();
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
private readonly Dictionary<long, Comment> loadedReplies = new Dictionary<long, Comment>();
public readonly BindableList<DrawableComment> Replies = new BindableList<DrawableComment>();
private readonly BindableBool childrenExpanded = new BindableBool(true);
private int currentPage;
private FillFlowContainer childCommentsVisibilityContainer;
private readonly Comment comment;
private FillFlowContainer childCommentsContainer;
private LoadMoreCommentsButton loadMoreCommentsButton;
private ShowMoreButton showMoreButton;
private RepliesButton repliesButton;
private ChevronButton chevronButton;
private DeletedCommentsCounter deletedCommentsCounter;
public DrawableComment(Comment comment)
{
this.comment = comment;
Comment = comment;
}
[BackgroundDependencyLoader]
private void load()
{
LinkFlowContainer username;
FillFlowContainer childCommentsContainer;
DeletedCommentsCounter deletedCommentsCounter;
FillFlowContainer info;
LinkFlowContainer message;
GridContainer content;
@ -50,234 +68,296 @@ namespace osu.Game.Overlays.Comments
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new FillFlowContainer
InternalChildren = new Drawable[]
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
new FillFlowContainer
{
new Container
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(margin) { Left = margin + 5 },
Child = content = new GridContainer
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ColumnDimensions = new[]
Padding = new MarginPadding(margin) { Left = margin + 5 },
Child = content = new GridContainer
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[]
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ColumnDimensions = new[]
{
new FillFlowContainer
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[]
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Horizontal = margin },
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
new FillFlowContainer
{
new Container
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Horizontal = margin },
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 40,
AutoSizeAxes = Axes.Y,
Child = votePill = new VotePill(comment)
new Container
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
}
},
new UpdateableAvatar(comment.User)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(avatar_size),
Masking = true,
CornerRadius = avatar_size / 2f,
CornerExponent = 2,
},
}
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0, 3),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(7, 0),
Children = new Drawable[]
{
username = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true))
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Width = 40,
AutoSizeAxes = Axes.Y,
Child = votePill = new VotePill(Comment)
{
AutoSizeAxes = Axes.Both,
},
new ParentUsername(comment),
new OsuSpriteText
{
Alpha = comment.IsDeleted ? 1 : 0,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = @"deleted",
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
}
}
},
message = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14))
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Right = 40 }
},
info = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Colour = OsuColour.Gray(0.7f),
Children = new Drawable[]
},
new UpdateableAvatar(Comment.User)
{
new OsuSpriteText
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(avatar_size),
Masking = true,
CornerRadius = avatar_size / 2f,
CornerExponent = 2,
},
}
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0, 3),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(7, 0),
Children = new Drawable[]
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Text = HumanizerUtils.Humanize(comment.CreatedAt)
},
new RepliesButton(comment.RepliesCount)
username = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true))
{
AutoSizeAxes = Axes.Both,
},
new ParentUsername(Comment),
new OsuSpriteText
{
Alpha = Comment.IsDeleted ? 1 : 0,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = @"deleted",
}
}
},
message = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 14))
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Right = 40 }
},
info = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
Expanded = { BindTarget = childrenExpanded }
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Colour = OsuColour.Gray(0.7f),
Text = HumanizerUtils.Humanize(Comment.CreatedAt)
},
repliesButton = new RepliesButton(Comment.RepliesCount)
{
Expanded = { BindTarget = childrenExpanded }
},
loadMoreCommentsButton = new LoadMoreCommentsButton
{
Action = () => RepliesRequested(this, ++currentPage)
}
}
}
}
}
}
}
}
}
},
childCommentsVisibilityContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
},
childCommentsVisibilityContainer = new FillFlowContainer
{
childCommentsContainer = new FillFlowContainer
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
Padding = new MarginPadding { Left = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical
},
deletedCommentsCounter = new DeletedCommentsCounter
{
ShowDeleted = { BindTarget = ShowDeleted }
childCommentsContainer = new FillFlowContainer
{
Padding = new MarginPadding { Left = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical
},
deletedCommentsCounter = new DeletedCommentsCounter
{
ShowDeleted = { BindTarget = ShowDeleted }
},
showMoreButton = new ShowMoreButton
{
Action = () => RepliesRequested(this, ++currentPage)
}
}
}
},
}
},
chevronButton = new ChevronButton
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Margin = new MarginPadding { Right = 30, Top = margin },
Expanded = { BindTarget = childrenExpanded },
Alpha = 0
}
};
deletedCommentsCounter.Count.Value = comment.DeletedChildrenCount;
if (comment.UserId.HasValue)
username.AddUserLink(comment.User);
if (Comment.UserId.HasValue)
username.AddUserLink(Comment.User);
else
username.AddText(comment.LegacyName);
username.AddText(Comment.LegacyName);
if (comment.EditedAt.HasValue)
if (Comment.EditedAt.HasValue)
{
info.Add(new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Text = $@"edited {HumanizerUtils.Humanize(comment.EditedAt.Value)} by {comment.EditedUser.Username}"
Text = $@"edited {HumanizerUtils.Humanize(Comment.EditedAt.Value)} by {Comment.EditedUser.Username}"
});
}
if (comment.HasMessage)
if (Comment.HasMessage)
{
var formattedSource = MessageFormatter.FormatText(comment.Message);
var formattedSource = MessageFormatter.FormatText(Comment.Message);
message.AddLinks(formattedSource.Text, formattedSource.Links);
}
if (comment.IsDeleted)
if (Comment.IsDeleted)
{
content.FadeColour(OsuColour.Gray(0.5f));
votePill.Hide();
}
if (comment.IsTopLevel)
if (Comment.IsTopLevel)
{
AddInternal(new Container
AddInternal(new Box
{
RelativeSizeAxes = Axes.X,
Height = 1.5f,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.1f)
}
RelativeSizeAxes = Axes.X,
Height = 1.5f,
Colour = OsuColour.Gray(0.1f)
});
if (comment.ChildComments.Any())
{
AddInternal(new ChevronButton(comment)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Margin = new MarginPadding { Right = 30, Top = margin },
Expanded = { BindTarget = childrenExpanded }
});
}
}
comment.ChildComments.ForEach(c => childCommentsContainer.Add(new DrawableComment(c)
if (Replies.Any())
onRepliesAdded(Replies);
Replies.CollectionChanged += (_, args) =>
{
ShowDeleted = { BindTarget = ShowDeleted }
}));
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
onRepliesAdded(args.NewItems.Cast<DrawableComment>());
break;
default:
throw new NotSupportedException(@"You can only add replies to this list. Other actions are not supported.");
}
};
}
protected override void LoadComplete()
{
ShowDeleted.BindValueChanged(show =>
{
if (comment.IsDeleted)
if (Comment.IsDeleted)
this.FadeTo(show.NewValue ? 1 : 0);
}, true);
childrenExpanded.BindValueChanged(expanded => childCommentsVisibilityContainer.FadeTo(expanded.NewValue ? 1 : 0), true);
updateButtonsState();
base.LoadComplete();
}
public bool ContainsReply(long replyId) => loadedReplies.ContainsKey(replyId);
private void onRepliesAdded(IEnumerable<DrawableComment> replies)
{
var page = createRepliesPage(replies);
if (LoadState == LoadState.Loading)
{
addRepliesPage(page, replies);
return;
}
LoadComponentAsync(page, loaded => addRepliesPage(loaded, replies));
}
private void addRepliesPage(FillFlowContainer<DrawableComment> page, IEnumerable<DrawableComment> replies)
{
childCommentsContainer.Add(page);
var newReplies = replies.Select(reply => reply.Comment);
newReplies.ForEach(reply => loadedReplies.Add(reply.Id, reply));
deletedCommentsCounter.Count.Value += newReplies.Count(reply => reply.IsDeleted);
updateButtonsState();
}
private FillFlowContainer<DrawableComment> createRepliesPage(IEnumerable<DrawableComment> replies) => new FillFlowContainer<DrawableComment>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = replies.ToList()
};
private void updateButtonsState()
{
var loadedReplesCount = loadedReplies.Count;
var hasUnloadedReplies = loadedReplesCount != Comment.RepliesCount;
loadMoreCommentsButton.FadeTo(hasUnloadedReplies && loadedReplesCount == 0 ? 1 : 0);
showMoreButton.FadeTo(hasUnloadedReplies && loadedReplesCount > 0 ? 1 : 0);
repliesButton.FadeTo(loadedReplesCount != 0 ? 1 : 0);
if (Comment.IsTopLevel)
chevronButton.FadeTo(loadedReplesCount != 0 ? 1 : 0);
showMoreButton.IsLoading = loadMoreCommentsButton.IsLoading = false;
}
private class ChevronButton : ShowChildrenButton
{
private readonly SpriteIcon icon;
public ChevronButton(Comment comment)
public ChevronButton()
{
Alpha = comment.IsTopLevel && comment.ChildComments.Any() ? 1 : 0;
Child = icon = new SpriteIcon
{
Size = new Vector2(12),
Colour = OsuColour.Gray(0.7f)
};
}
@ -296,7 +376,6 @@ namespace osu.Game.Overlays.Comments
{
this.count = count;
Alpha = count == 0 ? 0 : 1;
Child = text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
@ -305,10 +384,34 @@ namespace osu.Game.Overlays.Comments
protected override void OnExpandedChanged(ValueChangedEvent<bool> expanded)
{
text.Text = $@"{(expanded.NewValue ? "[+]" : "[-]")} replies ({count})";
text.Text = $@"{(expanded.NewValue ? "[-]" : "[+]")} replies ({count})";
}
}
private class LoadMoreCommentsButton : GetCommentRepliesButton
{
public LoadMoreCommentsButton()
{
IdleColour = OsuColour.Gray(0.7f);
HoverColour = Color4.White;
}
protected override string GetText() => @"[+] load replies";
}
private class ShowMoreButton : GetCommentRepliesButton
{
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Margin = new MarginPadding { Vertical = 10, Left = 80 };
IdleColour = colourProvider.Light2;
HoverColour = colourProvider.Light1;
}
protected override string GetText() => @"Show More";
}
private class ParentUsername : FillFlowContainer, IHasTooltip
{
public string TooltipText => getParentMessage();

View File

@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using System.Collections.Generic;
using osuTK;
namespace osu.Game.Overlays.Comments
{
public abstract class GetCommentRepliesButton : LoadingButton
{
private const int duration = 200;
protected override IEnumerable<Drawable> EffectTargets => new[] { text };
private OsuSpriteText text;
protected GetCommentRepliesButton()
{
AutoSizeAxes = Axes.Both;
LoadingAnimationSize = new Vector2(8);
}
protected override Drawable CreateContent() => new Container
{
AutoSizeAxes = Axes.Both,
Child = text = new OsuSpriteText
{
AlwaysPresent = true,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Text = GetText()
}
};
protected abstract string GetText();
protected override void OnLoadStarted() => text.FadeOut(duration, Easing.OutQuint);
protected override void OnLoadFinished() => text.FadeIn(duration, Easing.OutQuint);
}
}

View File

@ -3,8 +3,9 @@
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Bindables;
using osuTK.Graphics;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Comments
{
@ -15,20 +16,18 @@ namespace osu.Game.Overlays.Comments
protected ShowChildrenButton()
{
AutoSizeAxes = Axes.Both;
IdleColour = OsuColour.Gray(0.7f);
HoverColour = Color4.White;
}
protected override void LoadComplete()
{
Action = Expanded.Toggle;
Expanded.BindValueChanged(OnExpandedChanged, true);
base.LoadComplete();
}
protected abstract void OnExpandedChanged(ValueChangedEvent<bool> expanded);
protected override bool OnClick(ClickEvent e)
{
Expanded.Value = !Expanded.Value;
return true;
}
}
}

View File

@ -1,110 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osuTK;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Bindables;
using osu.Framework.Allocation;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
namespace osu.Game.Overlays.Comments
{
public class SortTabControl : OsuTabControl<CommentsSortCriteria>
{
protected override Dropdown<CommentsSortCriteria> CreateDropdown() => null;
protected override TabItem<CommentsSortCriteria> CreateTabItem(CommentsSortCriteria value) => new SortTabItem(value);
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
};
public SortTabControl()
{
AutoSizeAxes = Axes.Both;
}
private class SortTabItem : TabItem<CommentsSortCriteria>
{
public SortTabItem(CommentsSortCriteria value)
: base(value)
{
AutoSizeAxes = Axes.Both;
Child = new TabButton(value) { Active = { BindTarget = Active } };
}
protected override void OnActivated()
{
}
protected override void OnDeactivated()
{
}
private class TabButton : HeaderButton
{
public readonly BindableBool Active = new BindableBool();
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private readonly SpriteText text;
public TabButton(CommentsSortCriteria value)
{
Add(text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14),
Text = value.ToString()
});
}
protected override void LoadComplete()
{
base.LoadComplete();
Active.BindValueChanged(active =>
{
updateBackgroundState();
text.Font = text.Font.With(weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium);
text.Colour = active.NewValue ? colourProvider.Light1 : Color4.White;
}, true);
}
protected override bool OnHover(HoverEvent e)
{
updateBackgroundState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e) => updateBackgroundState();
private void updateBackgroundState()
{
if (Active.Value || IsHovered)
ShowBackground();
else
HideBackground();
}
}
}
}
public enum CommentsSortCriteria
{
New,
Old,
Top
}
}

View File

@ -33,6 +33,9 @@ namespace osu.Game.Overlays.Comments
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private readonly Comment comment;
private Box background;
private Box hoverLayer;
@ -68,7 +71,7 @@ namespace osu.Game.Overlays.Comments
base.LoadComplete();
isVoted.Value = comment.IsVoted;
votesCount.Value = comment.VotesCount;
isVoted.BindValueChanged(voted => background.Colour = voted.NewValue ? AccentColour : OsuColour.Gray(0.05f), true);
isVoted.BindValueChanged(voted => background.Colour = voted.NewValue ? AccentColour : colourProvider.Background6, true);
votesCount.BindValueChanged(count => votesCounter.Text = $"+{count.NewValue}", true);
}

View File

@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Direct
public DownloadProgressBar(BeatmapSetInfo beatmapSet)
: base(beatmapSet)
{
AddInternal(progressBar = new ProgressBar
AddInternal(progressBar = new InteractionDisabledProgressBar
{
Height = 0,
Alpha = 0,
@ -64,5 +64,11 @@ namespace osu.Game.Overlays.Direct
}
}, true);
}
private class InteractionDisabledProgressBar : ProgressBar
{
public override bool HandlePositionalInput => false;
public override bool HandleNonPositionalInput => false;
}
}
}

View File

@ -34,14 +34,13 @@ namespace osu.Game.Overlays.Direct
public enum DirectSortCriteria
{
Relevance,
Title,
Artist,
Creator,
Difficulty,
Ranked,
Rating,
Plays,
Favourites,
Relevance,
}
}

View File

@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Direct
[BackgroundDependencyLoader(true)]
private void load(OsuGame game, BeatmapManager beatmaps)
{
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false)
{
button.Enabled.Value = false;
button.TooltipText = "this beatmap is currently not available for download.";
@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Direct
break;
case DownloadState.LocallyAvailable:
game.PresentBeatmap(BeatmapSet.Value);
game?.PresentBeatmap(BeatmapSet.Value);
break;
default:

View File

@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Direct
private Color4 hoverColour;
private readonly SpriteIcon icon;
private readonly LoadingAnimation loadingAnimation;
private readonly LoadingSpinner loadingSpinner;
private const float transition_duration = 500;
@ -53,12 +53,12 @@ namespace osu.Game.Overlays.Direct
if (value)
{
icon.FadeTo(0.5f, transition_duration, Easing.OutQuint);
loadingAnimation.Show();
loadingSpinner.Show();
}
else
{
icon.FadeTo(1, transition_duration, Easing.OutQuint);
loadingAnimation.Hide();
loadingSpinner.Hide();
}
}
}
@ -76,7 +76,7 @@ namespace osu.Game.Overlays.Direct
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.Play,
},
loadingAnimation = new LoadingAnimation
loadingSpinner = new LoadingSpinner
{
Size = new Vector2(15),
},
@ -85,13 +85,12 @@ namespace osu.Game.Overlays.Direct
Playing.ValueChanged += playingStateChanged;
}
private PreviewTrackManager previewTrackManager;
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
[BackgroundDependencyLoader]
private void load(OsuColour colour, PreviewTrackManager previewTrackManager)
private void load(OsuColour colour)
{
this.previewTrackManager = previewTrackManager;
hoverColour = colour.Yellow;
}

View File

@ -27,7 +27,8 @@ namespace osu.Game.Overlays
{
private const float panel_padding = 10f;
private RulesetStore rulesets;
[Resolved]
private RulesetStore rulesets { get; set; }
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
@ -155,11 +156,8 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, RulesetStore rulesets, PreviewTrackManager previewTrackManager)
private void load(OsuColour colours)
{
this.rulesets = rulesets;
this.previewTrackManager = previewTrackManager;
resultCountsContainer.Colour = colours.Yellow;
}
@ -228,7 +226,9 @@ namespace osu.Game.Overlays
private readonly Bindable<string> currentQuery = new Bindable<string>(string.Empty);
private ScheduledDelegate queryChangedDebounce;
private PreviewTrackManager previewTrackManager;
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
private void queueUpdateSearch(bool queryTextChanged = false)
{

View File

@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Bindables;
namespace osu.Game.Overlays.Home.Friends
{
public class UserListToolbar : CompositeDrawable
{
public Bindable<UserSortCriteria> SortCriteria => sortControl.Current;
public Bindable<OverlayPanelDisplayStyle> DisplayStyle => styleControl.Current;
private readonly UserSortTabControl sortControl;
private readonly OverlayPanelDisplayStyleControl styleControl;
public UserListToolbar()
{
AutoSizeAxes = Axes.Both;
AddInternal(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
sortControl = new UserSortTabControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
styleControl = new OverlayPanelDisplayStyleControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
});
}
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.ComponentModel;
namespace osu.Game.Overlays.Home.Friends
{
public class UserSortTabControl : OverlaySortTabControl<UserSortCriteria>
{
}
public enum UserSortCriteria
{
[Description(@"Recently Active")]
LastVisit,
Rank,
Username
}
}

View File

@ -66,13 +66,12 @@ namespace osu.Game.Overlays.KeyBinding
CornerRadius = padding;
}
private KeyBindingStore store;
[Resolved]
private KeyBindingStore store { get; set; }
[BackgroundDependencyLoader]
private void load(OsuColour colours, KeyBindingStore store)
private void load(OsuColour colours)
{
this.store = store;
EdgeEffect = new EdgeEffectParameters
{
Radius = 2,

View File

@ -12,17 +12,12 @@ using osuTK;
namespace osu.Game.Overlays.Music
{
public class Playlist : RearrangeableListContainer<BeatmapSetInfo>
public class Playlist : OsuRearrangeableListContainer<BeatmapSetInfo>
{
public Action<BeatmapSetInfo> RequestSelection;
public readonly Bindable<BeatmapSetInfo> SelectedSet = new Bindable<BeatmapSetInfo>();
/// <summary>
/// Whether any item is currently being dragged. Used to hide other items' drag handles.
/// </summary>
private readonly BindableBool playlistDragActive = new BindableBool();
public new MarginPadding Padding
{
get => base.Padding;
@ -33,15 +28,12 @@ namespace osu.Game.Overlays.Music
public BeatmapSetInfo FirstVisibleSet => Items.FirstOrDefault(i => ((PlaylistItem)ItemMap[i]).MatchingFilter);
protected override RearrangeableListItem<BeatmapSetInfo> CreateDrawable(BeatmapSetInfo item) => new PlaylistItem(item)
protected override OsuRearrangeableListItem<BeatmapSetInfo> CreateOsuDrawable(BeatmapSetInfo item) => new PlaylistItem(item)
{
SelectedSet = { BindTarget = SelectedSet },
PlaylistDragActive = { BindTarget = playlistDragActive },
RequestSelection = set => RequestSelection?.Invoke(set)
};
protected override ScrollContainer<Drawable> CreateScrollContainer() => new OsuScrollContainer();
protected override FillFlowContainer<RearrangeableListItem<BeatmapSetInfo>> CreateListFillFlowContainer() => new SearchContainer<RearrangeableListItem<BeatmapSetInfo>>
{
Spacing = new Vector2(0, 3),

View File

@ -14,36 +14,28 @@ using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Music
{
public class PlaylistItem : RearrangeableListItem<BeatmapSetInfo>, IFilterable
public class PlaylistItem : OsuRearrangeableListItem<BeatmapSetInfo>, IFilterable
{
private const float fade_duration = 100;
public BindableBool PlaylistDragActive = new BindableBool();
public readonly Bindable<BeatmapSetInfo> SelectedSet = new Bindable<BeatmapSetInfo>();
public Action<BeatmapSetInfo> RequestSelection;
private PlaylistItemHandle handle;
private TextFlowContainer text;
private IEnumerable<Drawable> titleSprites;
private ILocalisedBindableString titleBind;
private ILocalisedBindableString artistBind;
private Color4 hoverColour;
private ILocalisedBindableString title;
private ILocalisedBindableString artist;
private Color4 selectedColour;
private Color4 artistColour;
public PlaylistItem(BeatmapSetInfo item)
: base(item)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Left = 5 };
FilterTerms = item.Metadata.SearchableTerms;
@ -52,66 +44,44 @@ namespace osu.Game.Overlays.Music
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationManager localisation)
{
hoverColour = colours.Yellow;
selectedColour = colours.Yellow;
artistColour = colours.Gray9;
HandleColour = colours.Gray5;
InternalChild = new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Content = new[]
{
new Drawable[]
{
handle = new PlaylistItemHandle
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(12),
Colour = colours.Gray5,
AlwaysPresent = true,
Alpha = 0
},
text = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = 5 },
},
}
},
ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
};
titleBind = localisation.GetLocalisedString(new LocalisedString((Model.Metadata.TitleUnicode, Model.Metadata.Title)));
artistBind = localisation.GetLocalisedString(new LocalisedString((Model.Metadata.ArtistUnicode, Model.Metadata.Artist)));
artistBind.BindValueChanged(_ => recreateText(), true);
title = localisation.GetLocalisedString(new LocalisedString((Model.Metadata.TitleUnicode, Model.Metadata.Title)));
artist = localisation.GetLocalisedString(new LocalisedString((Model.Metadata.ArtistUnicode, Model.Metadata.Artist)));
}
protected override void LoadComplete()
{
base.LoadComplete();
artist.BindValueChanged(_ => recreateText(), true);
SelectedSet.BindValueChanged(set =>
{
if (set.OldValue != Model && set.NewValue != Model)
if (set.OldValue?.Equals(Model) != true && set.NewValue?.Equals(Model) != true)
return;
foreach (Drawable s in titleSprites)
s.FadeColour(set.NewValue == Model ? hoverColour : Color4.White, fade_duration);
s.FadeColour(set.NewValue.Equals(Model) ? selectedColour : Color4.White, FADE_DURATION);
}, true);
}
protected override Drawable CreateContent() => text = new OsuTextFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
};
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.Font = OsuFont.GetFont(weight: FontWeight.Regular)).OfType<SpriteText>();
titleSprites = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)).OfType<SpriteText>();
text.AddText(artistBind.Value, sprite =>
text.AddText(artist.Value, sprite =>
{
sprite.Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold);
sprite.Colour = artistColour;
@ -125,31 +95,6 @@ namespace osu.Game.Overlays.Music
return true;
}
protected override bool OnDragStart(DragStartEvent e)
{
if (!base.OnDragStart(e))
return false;
PlaylistDragActive.Value = true;
return true;
}
protected override void OnDragEnd(DragEndEvent e)
{
PlaylistDragActive.Value = false;
base.OnDragEnd(e);
}
protected override bool IsDraggableAt(Vector2 screenSpacePos) => handle.HandlingDrag;
protected override bool OnHover(HoverEvent e)
{
handle.UpdateHoverState(IsDragged || !PlaylistDragActive.Value);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e) => handle.UpdateHoverState(false);
public IEnumerable<string> FilterTerms { get; }
private bool matching = true;
@ -168,44 +113,5 @@ namespace osu.Game.Overlays.Music
}
public bool FilteringActive { get; set; }
private class PlaylistItemHandle : SpriteIcon
{
public bool HandlingDrag { get; private set; }
private bool isHovering;
public PlaylistItemHandle()
{
Icon = FontAwesome.Solid.Bars;
}
protected override bool OnMouseDown(MouseDownEvent e)
{
base.OnMouseDown(e);
HandlingDrag = true;
UpdateHoverState(isHovering);
return false;
}
protected override void OnMouseUp(MouseUpEvent e)
{
base.OnMouseUp(e);
HandlingDrag = false;
UpdateHoverState(isHovering);
}
public void UpdateHoverState(bool hovering)
{
isHovering = hovering;
if (isHovering || HandlingDrag)
this.FadeIn(fade_duration);
else
this.FadeOut(fade_duration);
}
}
}
}

View File

@ -26,16 +26,17 @@ namespace osu.Game.Overlays.Music
private readonly BindableList<BeatmapSetInfo> beatmapSets = new BindableList<BeatmapSetInfo>();
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private BeatmapManager beatmaps;
[Resolved]
private BeatmapManager beatmaps { get; set; }
private FilterControl filter;
private Playlist list;
[BackgroundDependencyLoader]
private void load(OsuColour colours, Bindable<WorkingBeatmap> beatmap, BeatmapManager beatmaps)
private void load(OsuColour colours, Bindable<WorkingBeatmap> beatmap)
{
this.beatmap.BindTo(beatmap);
this.beatmaps = beatmaps;
Children = new Drawable[]
{
@ -74,8 +75,6 @@ namespace osu.Game.Overlays.Music
},
};
list.Items.BindTo(beatmapSets);
filter.Search.OnCommit = (sender, newText) =>
{
BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
@ -86,7 +85,13 @@ namespace osu.Game.Overlays.Music
beatmap.Value.Track.Restart();
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
list.Items.BindTo(beatmapSets);
beatmap.BindValueChanged(working => list.SelectedSet.Value = working.NewValue.BeatmapSetInfo, true);
}

View File

@ -25,7 +25,16 @@ namespace osu.Game.Overlays
[Resolved]
private BeatmapManager beatmaps { get; set; }
public IBindableList<BeatmapSetInfo> BeatmapSets => beatmapSets;
public IBindableList<BeatmapSetInfo> BeatmapSets
{
get
{
if (LoadState < LoadState.Ready)
throw new InvalidOperationException($"{nameof(BeatmapSets)} should not be accessed before the music controller is loaded.");
return beatmapSets;
}
}
/// <summary>
/// Point in time after which the current track will be restarted on triggering a "previous track" action.
@ -54,16 +63,18 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load()
{
beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next()));
beatmaps.ItemAdded += handleBeatmapAdded;
beatmaps.ItemRemoved += handleBeatmapRemoved;
beatmapSets.AddRange(beatmaps.GetAllUsableBeatmapSets().OrderBy(_ => RNG.Next()));
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindValueChanged(beatmapChanged, true);
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
base.LoadComplete();
}
/// <summary>
@ -82,11 +93,16 @@ namespace osu.Game.Overlays
/// </summary>
public bool IsPlaying => current?.Track.IsRunning ?? false;
private void handleBeatmapAdded(BeatmapSetInfo set) =>
Schedule(() => beatmapSets.Add(set));
private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() =>
{
if (!beatmapSets.Contains(set))
beatmapSets.Add(set);
});
private void handleBeatmapRemoved(BeatmapSetInfo set) =>
Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() =>
{
beatmapSets.RemoveAll(s => s.ID == set.ID);
});
private ScheduledDelegate seekDelegate;

View File

@ -58,6 +58,9 @@ namespace osu.Game.Overlays
[Resolved]
private Bindable<WorkingBeatmap> beatmap { get; set; }
[Resolved]
private OsuColour colours { get; set; }
public NowPlayingOverlay()
{
Width = 400;
@ -65,7 +68,7 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
private void load()
{
Children = new Drawable[]
{
@ -182,15 +185,15 @@ namespace osu.Game.Overlays
}
}
};
playlist.BeatmapSets.BindTo(musicController.BeatmapSets);
playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
protected override void LoadComplete()
{
base.LoadComplete();
playlist.BeatmapSets.BindTo(musicController.BeatmapSets);
playlist.State.BindValueChanged(s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint), true);
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
musicController.TrackChanged += trackChanged;

View File

@ -0,0 +1,101 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
using osuTK.Graphics;
using osu.Framework.Graphics.Cursor;
namespace osu.Game.Overlays
{
public class OverlayPanelDisplayStyleControl : OsuTabControl<OverlayPanelDisplayStyle>
{
protected override Dropdown<OverlayPanelDisplayStyle> CreateDropdown() => null;
protected override TabItem<OverlayPanelDisplayStyle> CreateTabItem(OverlayPanelDisplayStyle value) => new PanelDisplayTabItem(value);
protected override bool AddEnumEntriesAutomatically => false;
public OverlayPanelDisplayStyleControl()
{
AutoSizeAxes = Axes.Both;
AddTabItem(new PanelDisplayTabItem(OverlayPanelDisplayStyle.Card)
{
Icon = FontAwesome.Solid.Square
});
AddTabItem(new PanelDisplayTabItem(OverlayPanelDisplayStyle.List)
{
Icon = FontAwesome.Solid.Bars
});
}
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal
};
private class PanelDisplayTabItem : TabItem<OverlayPanelDisplayStyle>, IHasTooltip
{
public IconUsage Icon
{
set => icon.Icon = value;
}
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
public string TooltipText => $@"{Value} view";
private readonly SpriteIcon icon;
public PanelDisplayTabItem(OverlayPanelDisplayStyle value)
: base(value)
{
Size = new Vector2(11);
AddRange(new Drawable[]
{
icon = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit
},
new HoverClickSounds()
});
}
protected override void OnActivated() => updateState();
protected override void OnDeactivated() => updateState();
protected override bool OnHover(HoverEvent e)
{
updateState();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateState();
base.OnHoverLost(e);
}
private void updateState() => icon.Colour = Active.Value || IsHovered ? colourProvider.Light1 : Color4.White;
}
}
public enum OverlayPanelDisplayStyle
{
Card,
List
}
}

View File

@ -0,0 +1,170 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osuTK;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Bindables;
using osu.Framework.Allocation;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
using osu.Game.Overlays.Comments;
using JetBrains.Annotations;
using System;
using osu.Framework.Extensions;
namespace osu.Game.Overlays
{
public class OverlaySortTabControl<T> : CompositeDrawable, IHasCurrentValue<T>
{
private readonly BindableWithCurrent<T> current = new BindableWithCurrent<T>();
public Bindable<T> Current
{
get => current.Current;
set => current.Current = value;
}
public OverlaySortTabControl()
{
AutoSizeAxes = Axes.Both;
AddInternal(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Text = @"Sort by"
},
CreateControl().With(c =>
{
c.Anchor = Anchor.CentreLeft;
c.Origin = Anchor.CentreLeft;
c.Current = current;
})
}
});
}
[NotNull]
protected virtual SortTabControl CreateControl() => new SortTabControl();
protected class SortTabControl : OsuTabControl<T>
{
protected override Dropdown<T> CreateDropdown() => null;
protected override TabItem<T> CreateTabItem(T value) => new SortTabItem(value);
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
};
public SortTabControl()
{
AutoSizeAxes = Axes.Both;
}
}
protected class SortTabItem : TabItem<T>
{
public SortTabItem(T value)
: base(value)
{
AutoSizeAxes = Axes.Both;
Child = CreateTabButton(value);
}
[NotNull]
protected virtual TabButton CreateTabButton(T value) => new TabButton(value)
{
Active = { BindTarget = Active }
};
protected override void OnActivated()
{
}
protected override void OnDeactivated()
{
}
}
protected class TabButton : HeaderButton
{
public readonly BindableBool Active = new BindableBool();
protected override Container<Drawable> Content => content;
protected virtual Color4 ContentColour
{
set => text.Colour = value;
}
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private readonly SpriteText text;
private readonly FillFlowContainer content;
public TabButton(T value)
{
base.Content.Add(content = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(3, 0),
Children = new Drawable[]
{
text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12),
Text = (value as Enum)?.GetDescription() ?? value.ToString()
}
}
});
}
protected override void LoadComplete()
{
base.LoadComplete();
Active.BindValueChanged(_ => UpdateState(), true);
}
protected override bool OnHover(HoverEvent e)
{
UpdateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e) => UpdateState();
protected virtual void UpdateState()
{
if (Active.Value || IsHovered)
ShowBackground();
else
HideBackground();
ContentColour = Active.Value && !IsHovered ? colourProvider.Light1 : Color4.White;
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium);
}
}
}
}

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Bindables;
using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.Profile.Sections
{
@ -16,9 +17,10 @@ namespace osu.Game.Overlays.Profile.Sections
public readonly BindableInt Current = new BindableInt();
private readonly OsuSpriteText counter;
private OsuSpriteText counter;
public CounterPill()
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
AutoSizeAxes = Axes.Both;
Alpha = 0;
@ -28,14 +30,15 @@ namespace osu.Game.Overlays.Profile.Sections
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.05f)
Colour = colourProvider.Background6
},
counter = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 10, Vertical = 5 },
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Colour = colourProvider.Foreground1
}
};
}
@ -54,7 +57,7 @@ namespace osu.Game.Overlays.Profile.Sections
return;
}
counter.Text = value.NewValue.ToString();
counter.Text = value.NewValue.ToString("N0");
this.FadeIn(duration, Easing.OutQuint);
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
@ -22,10 +21,9 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
public class DrawableProfileScore : CompositeDrawable
{
private const int height = 40;
private const int performance_width = 80;
private const int performance_width = 100;
private const float performance_background_shear = 0.45f;
private static readonly float performance_background_width = performance_width + (height / 4f * MathF.Tan(performance_background_shear));
protected readonly ScoreInfo Score;
@ -53,7 +51,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 10, Right = performance_width + 30 },
Padding = new MarginPadding { Left = 20, Right = performance_width },
Children = new Drawable[]
{
new FillFlowContainer
@ -62,7 +60,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(8, 0),
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new UpdateableRank(Score.Rank)
@ -85,7 +83,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Spacing = new Vector2(15, 0),
Children = new Drawable[]
{
new OsuSpriteText
@ -108,16 +106,21 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15),
Children = new[]
Children = new Drawable[]
{
CreateRightContent().With(c =>
new Container
{
c.Anchor = Anchor.CentreRight;
c.Origin = Anchor.CentreRight;
}),
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Child = CreateRightContent()
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
@ -140,14 +143,13 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
Width = performance_width,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Children = new[]
Children = new Drawable[]
{
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = performance_background_width,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Colour = colourProvider.Background4,
Shear = new Vector2(-performance_background_shear, 0),
@ -157,20 +159,29 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Width = performance_background_width,
Height = -0.5f,
Position = new Vector2(0, 1),
Colour = colourProvider.Background4,
Shear = new Vector2(performance_background_shear, 0),
EdgeSmoothness = new Vector2(2, 0),
},
createDrawablePerformance().With(d =>
new Container
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
})
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Vertical = 5,
Left = 30,
Right = 20
},
Child = createDrawablePerformance().With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
})
}
}
}
}
@ -180,11 +191,18 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
[NotNull]
protected virtual Drawable CreateRightContent() => CreateDrawableAccuracy();
protected OsuSpriteText CreateDrawableAccuracy() => new OsuSpriteText
protected Drawable CreateDrawableAccuracy() => new Container
{
Text = Score.DisplayAccuracy,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Colour = colours.Yellow,
Width = 65,
RelativeSizeAxes = Axes.Y,
Child = new OsuSpriteText
{
Text = Score.DisplayAccuracy,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Colour = colours.Yellow,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
}
};
private Drawable createDrawablePerformance()

View File

@ -6,6 +6,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Overlays.Profile.Sections.Ranks
{
@ -23,25 +24,28 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
Spacing = new Vector2(10, 0),
Children = new[]
{
CreateDrawableAccuracy(),
new Container
{
AutoSizeAxes = Axes.Y,
Width = 60,
Child = CreateDrawableAccuracy()
},
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = $"{Score.PP * weight:0}pp",
},
Width = 50,
Child = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = $"{Score.PP * weight:0}pp",
},
}
}
},
new OsuSpriteText

View File

@ -11,23 +11,23 @@ namespace osu.Game.Overlays.Rankings
{
public class RankingsOverlayHeader : TabControlOverlayHeader<RankingsScope>
{
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
public readonly Bindable<Country> Country = new Bindable<Country>();
public Bindable<RulesetInfo> Ruleset => rulesetSelector.Current;
public Bindable<Country> Country => countryFilter.Current;
private OverlayRulesetSelector rulesetSelector;
private CountryFilter countryFilter;
protected override ScreenTitle CreateTitle() => new RankingsTitle
{
Scope = { BindTarget = Current }
};
protected override Drawable CreateTitleContent() => new OverlayRulesetSelector
{
Current = Ruleset
};
protected override Drawable CreateTitleContent() => rulesetSelector = new OverlayRulesetSelector();
protected override Drawable CreateContent() => new CountryFilter
{
Current = Country
};
protected override Drawable CreateContent() => countryFilter = new CountryFilter();
protected override Drawable CreateBackground() => new OverlayHeaderBackground(@"Headers/rankings");
private class RankingsTitle : ScreenTitle
{

View File

@ -37,7 +37,7 @@ namespace osu.Game.Overlays.Rankings
private SpotlightSelector selector;
private Container content;
private DimmedLoadingLayer loading;
private LoadingLayer loading;
[BackgroundDependencyLoader]
private void load()
@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Rankings
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Vertical = 10 }
},
loading = new DimmedLoadingLayer()
loading = new LoadingLayer(content)
}
}
}
@ -89,7 +89,7 @@ namespace osu.Game.Overlays.Rankings
private void getSpotlights()
{
spotlightsRequest = new GetSpotlightsRequest();
spotlightsRequest.Success += response => selector.Spotlights = response.Spotlights;
spotlightsRequest.Success += response => Schedule(() => selector.Spotlights = response.Spotlights);
api.Queue(spotlightsRequest);
}
@ -151,11 +151,11 @@ namespace osu.Game.Overlays.Rankings
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
spotlightsRequest?.Cancel();
getRankingsRequest?.Cancel();
cancellationToken?.Cancel();
base.Dispose(isDisposing);
}
}
}

View File

@ -8,6 +8,8 @@ using osu.Game.Users;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Graphics.Containers;
namespace osu.Game.Overlays.Rankings.Tables
{
@ -30,11 +32,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected override Country GetCountry(CountryStatistics item) => item.Country;
protected override Drawable CreateFlagContent(CountryStatistics item) => new OsuSpriteText
{
Font = OsuFont.GetFont(size: TEXT_SIZE),
Text = $@"{item.Country.FullName}",
};
protected override Drawable CreateFlagContent(CountryStatistics item) => new CountryName(item.Country);
protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[]
{
@ -63,5 +61,37 @@ namespace osu.Game.Overlays.Rankings.Tables
Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}",
}
};
private class CountryName : OsuHoverContainer
{
protected override IEnumerable<Drawable> EffectTargets => new[] { text };
[Resolved(canBeNull: true)]
private RankingsOverlay rankings { get; set; }
private readonly OsuSpriteText text;
private readonly Country country;
public CountryName(Country country)
{
this.country = country;
AutoSizeAxes = Axes.Both;
Add(text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12),
Text = country.FullName ?? string.Empty,
});
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
IdleColour = colourProvider.Light2;
HoverColour = colourProvider.Content2;
Action = () => rankings?.ShowCountry(country);
}
}
}
}

View File

@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected sealed override Drawable CreateFlagContent(UserStatistics item)
{
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both };
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE, italics: true)) { AutoSizeAxes = Axes.Both };
username.AddUserLink(item.User);
return username;
}

View File

@ -9,9 +9,9 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Overlays.Rankings;
using osu.Game.Users;
using osu.Game.Rulesets;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using System.Threading;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Rankings.Tables;
@ -19,14 +19,15 @@ namespace osu.Game.Overlays
{
public class RankingsOverlay : FullscreenOverlay
{
protected readonly Bindable<Country> Country = new Bindable<Country>();
protected readonly Bindable<RankingsScope> Scope = new Bindable<RankingsScope>();
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
protected Bindable<Country> Country => header.Country;
protected Bindable<RankingsScope> Scope => header.Current;
private readonly BasicScrollContainer scrollFlow;
private readonly Container contentContainer;
private readonly DimmedLoadingLayer loading;
private readonly LoadingLayer loading;
private readonly Box background;
private readonly RankingsOverlayHeader header;
private APIRequest lastRequest;
private CancellationTokenSource cancellationToken;
@ -54,14 +55,11 @@ namespace osu.Game.Overlays
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new RankingsOverlayHeader
header = new RankingsOverlayHeader
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Depth = -float.MaxValue,
Country = { BindTarget = Country },
Current = { BindTarget = Scope },
Ruleset = { BindTarget = ruleset }
Depth = -float.MaxValue
},
new Container
{
@ -77,7 +75,7 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Bottom = 10 }
},
loading = new DimmedLoadingLayer(),
loading = new LoadingLayer(contentContainer),
}
}
}
@ -92,8 +90,15 @@ namespace osu.Game.Overlays
background.Colour = ColourProvider.Background5;
}
[Resolved]
private Bindable<RulesetInfo> ruleset { get; set; }
protected override void LoadComplete()
{
base.LoadComplete();
header.Ruleset.BindTo(ruleset);
Country.BindValueChanged(_ =>
{
// if a country is requested, force performance scope.
@ -101,7 +106,7 @@ namespace osu.Game.Overlays
Scope.Value = RankingsScope.Performance;
Scheduler.AddOnce(loadNewContent);
}, true);
});
Scope.BindValueChanged(_ =>
{
@ -110,7 +115,7 @@ namespace osu.Game.Overlays
Country.Value = null;
Scheduler.AddOnce(loadNewContent);
}, true);
});
ruleset.BindValueChanged(_ =>
{
@ -118,9 +123,9 @@ namespace osu.Game.Overlays
return;
Scheduler.AddOnce(loadNewContent);
}, true);
});
base.LoadComplete();
Scheduler.AddOnce(loadNewContent);
}
public void ShowCountry(Country requested)
@ -133,6 +138,12 @@ namespace osu.Game.Overlays
Country.Value = requested;
}
public void ShowSpotlights()
{
Scope.Value = RankingsScope.Spotlights;
Show();
}
private void loadNewContent()
{
loading.Show();
@ -158,8 +169,8 @@ namespace osu.Game.Overlays
return;
}
request.Success += () => loadContent(createTableFromResponse(request));
request.Failure += _ => loadContent(null);
request.Success += () => Schedule(() => loadContent(createTableFromResponse(request)));
request.Failure += _ => Schedule(() => loadContent(null));
api.Queue(request);
}
@ -221,5 +232,13 @@ namespace osu.Game.Overlays
contentContainer.Child = loaded;
}, (cancellationToken = new CancellationTokenSource()).Token);
}
protected override void Dispose(bool isDisposing)
{
lastRequest?.Cancel();
cancellationToken?.Cancel();
base.Dispose(isDisposing);
}
}
}

View File

@ -80,7 +80,7 @@ namespace osu.Game.Overlays.SearchableList
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Horizontal = WIDTH_PADDING, Bottom = 50 },
Padding = new MarginPadding { Horizontal = 10, Bottom = 50 },
Direction = FillDirection.Vertical,
},
},

View File

@ -14,14 +14,10 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
protected override string Header => "Devices";
private AudioManager audio;
private SettingsDropdown<string> dropdown;
[Resolved]
private AudioManager audio { get; set; }
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
this.audio = audio;
}
private SettingsDropdown<string> dropdown;
protected override void Dispose(bool isDisposing)
{

View File

@ -22,11 +22,6 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
Bindable = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay)
},
new SettingsCheckbox
{
LabelText = "Performance logging",
Bindable = config.GetBindable<bool>(DebugSetting.PerformanceLogging)
},
new SettingsCheckbox
{
LabelText = "Bypass front-to-back render pass",
Bindable = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)

View File

@ -29,7 +29,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
{
private bool bounding = true;
private LoginForm form;
private OsuColour colours;
[Resolved]
private OsuColour colours { get; set; }
private UserPanel panel;
private UserDropdown dropdown;
@ -60,10 +62,8 @@ namespace osu.Game.Overlays.Settings.Sections.General
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, IAPIProvider api)
private void load(IAPIProvider api)
{
this.colours = colours;
api?.Register(this);
}
@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
Children = new Drawable[]
{
new LoadingAnimation
new LoadingSpinner
{
State = { Value = Visibility.Visible },
Anchor = Anchor.TopCentre,
@ -201,7 +201,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
private TextBox username;
private TextBox password;
private ShakeContainer shakeSignIn;
private IAPIProvider api;
[Resolved(CanBeNull = true)]
private IAPIProvider api { get; set; }
public Action RequestHide;
@ -214,9 +216,8 @@ namespace osu.Game.Overlays.Settings.Sections.General
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(IAPIProvider api, OsuConfigManager config, AccountCreationOverlay accountCreation)
private void load(OsuConfigManager config, AccountCreationOverlay accountCreation)
{
this.api = api;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0, 5);
AutoSizeAxes = Axes.Y;

View File

@ -29,7 +29,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
private Bindable<Size> sizeFullscreen;
private readonly IBindableList<WindowMode> windowModes = new BindableList<WindowMode>();
private OsuGameBase game;
[Resolved]
private OsuGameBase game { get; set; }
private SettingsDropdown<Size> resolutionDropdown;
private SettingsDropdown<WindowMode> windowModeDropdown;
@ -41,10 +43,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
private const int transition_duration = 400;
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, OsuGameBase game, GameHost host)
private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, GameHost host)
{
this.game = game;
scalingMode = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling);
sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen);
scalingSizeX = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeX);

View File

@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Graphics
@ -24,6 +25,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
LabelText = "Frame limiter",
Bindable = config.GetBindable<FrameSync>(FrameworkSetting.FrameSync)
},
new SettingsEnumDropdown<ExecutionMode>
{
LabelText = "Threading mode",
Bindable = config.GetBindable<ExecutionMode>(FrameworkSetting.ExecutionMode)
},
new SettingsCheckbox
{
LabelText = "Show FPS",

View File

@ -24,13 +24,12 @@ namespace osu.Game.Overlays.Settings.Sections
private readonly Bindable<SkinInfo> dropdownBindable = new Bindable<SkinInfo> { Default = SkinInfo.Default };
private readonly Bindable<int> configBindable = new Bindable<int>();
private SkinManager skins;
[Resolved]
private SkinManager skins { get; set; }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, SkinManager skins)
private void load(OsuConfigManager config)
{
this.skins = skins;
FlowContent.Spacing = new Vector2(0, 5);
Children = new Drawable[]
{

View File

@ -24,7 +24,7 @@ namespace osu.Game.Overlays
{
public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>
{
private readonly LoadingAnimation loading;
private readonly LoadingSpinner loading;
private FillFlowContainer<SocialPanel> panels;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b");
@ -54,7 +54,7 @@ namespace osu.Game.Overlays
public SocialOverlay()
: base(OverlayColourScheme.Pink)
{
Add(loading = new LoadingAnimation());
Add(loading = new LoadingSpinner());
Filter.Search.Current.ValueChanged += text =>
{

View File

@ -70,6 +70,7 @@ namespace osu.Game.Overlays.Toolbar
Children = new Drawable[]
{
new ToolbarChangelogButton(),
new ToolbarRankingsButton(),
new ToolbarDirectButton(),
new ToolbarChatButton(),
new ToolbarSocialButton(),

View File

@ -0,0 +1,22 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Overlays.Toolbar
{
public class ToolbarRankingsButton : ToolbarOverlayToggleButton
{
public ToolbarRankingsButton()
{
SetIcon(FontAwesome.Regular.ChartBar);
}
[BackgroundDependencyLoader(true)]
private void load(RankingsOverlay rankings)
{
StateContainer = rankings;
}
}
}

View File

@ -14,8 +14,25 @@ namespace osu.Game.Overlays.Volume
public Func<GlobalAction, bool> ActionRequested;
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
public bool OnPressed(GlobalAction action)
{
// if nothing else handles selection actions in the game, it's safe to let volume be adjusted.
switch (action)
{
case GlobalAction.SelectPrevious:
action = GlobalAction.IncreaseVolume;
break;
case GlobalAction.SelectNext:
action = GlobalAction.DecreaseVolume;
break;
}
return ActionRequested?.Invoke(action) ?? false;
}
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) =>
ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
public void OnReleased(GlobalAction action)
{

View File

@ -11,16 +11,18 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.Volume
{
public class VolumeMeter : Container
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private CircularProgress volumeCircle;
private CircularProgress volumeCircleGlow;
@ -260,5 +262,28 @@ namespace osu.Game.Overlays.Volume
{
this.ScaleTo(1f, transition_length, Easing.OutExpo);
}
public bool OnPressed(GlobalAction action)
{
if (!IsHovered)
return false;
switch (action)
{
case GlobalAction.SelectPrevious:
adjust(1, false);
return true;
case GlobalAction.SelectNext:
adjust(-1, false);
return true;
}
return false;
}
public void OnReleased(GlobalAction action)
{
}
}
}