1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:52:53 +08:00

Merge branch 'master' into osu-beatsnapping-grid

This commit is contained in:
Dan Balasescu 2019-10-16 20:12:31 +09:00 committed by GitHub
commit 05c6d1a66d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1372 additions and 53 deletions

View File

@ -15,7 +15,7 @@ using osuTK.Graphics;
namespace osu.Desktop.Overlays
{
public class VersionManager : OverlayContainer
public class VersionManager : VisibilityContainer
{
[BackgroundDependencyLoader]
private void load(OsuColour colours, TextureStore textures, OsuGameBase game)

View File

@ -0,0 +1,58 @@
// 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 NUnit.Framework;
using osu.Game.Online.API.Requests;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Overlays.Comments;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public class TestSceneCommentsContainer : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CommentsContainer),
typeof(CommentsHeader),
typeof(DrawableComment),
typeof(HeaderButton),
typeof(SortTabControl),
typeof(ShowChildrenButton),
typeof(DeletedChildrenPlaceholder)
};
protected override bool UseOnlineAPI => true;
public TestSceneCommentsContainer()
{
BasicScrollContainer scrollFlow;
Add(scrollFlow = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
});
AddStep("Big Black comments", () =>
{
scrollFlow.Clear();
scrollFlow.Add(new CommentsContainer(CommentableType.Beatmapset, 41823));
});
AddStep("Airman comments", () =>
{
scrollFlow.Clear();
scrollFlow.Add(new CommentsContainer(CommentableType.Beatmapset, 24313));
});
AddStep("lazer build comments", () =>
{
scrollFlow.Clear();
scrollFlow.Add(new CommentsContainer(CommentableType.Build, 4772));
});
}
}
}

View File

@ -0,0 +1,39 @@
// 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 NUnit.Framework;
using osu.Framework.Bindables;
using osu.Game.Overlays.Comments;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public class TestSceneCommentsHeader : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CommentsHeader),
typeof(HeaderButton),
typeof(SortTabControl),
};
private readonly Bindable<CommentsSortCriteria> sort = new Bindable<CommentsSortCriteria>();
private readonly BindableBool showDeleted = new BindableBool();
public TestSceneCommentsHeader()
{
Add(new CommentsHeader
{
Sort = { BindTarget = sort },
ShowDeleted = { BindTarget = showDeleted }
});
AddStep("Trigger ShowDeleted", () => showDeleted.Value = !showDeleted.Value);
AddStep("Select old", () => sort.Value = CommentsSortCriteria.Old);
AddStep("Select new", () => sort.Value = CommentsSortCriteria.New);
AddStep("Select top", () => sort.Value = CommentsSortCriteria.Top);
}
}
}

View File

@ -1,10 +1,12 @@
// 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.Game.Overlays.Profile.Sections;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Tests.Visual.Online
{
@ -17,11 +19,11 @@ namespace osu.Game.Tests.Visual.Online
public TestSceneShowMoreButton()
{
ShowMoreButton button = null;
TestButton button = null;
int fireCount = 0;
Add(button = new ShowMoreButton
Add(button = new TestButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -51,5 +53,16 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("action fired twice", () => fireCount == 2);
AddAssert("is in loading state", () => button.IsLoading);
}
private class TestButton : ShowMoreButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
IdleColour = colors.YellowDark;
HoverColour = colors.Yellow;
ChevronIconColour = colors.Red;
}
}
}
}

View File

@ -196,7 +196,7 @@ namespace osu.Game.Tournament.Screens.MapPool
setNextMode();
if (pickType == ChoiceType.Pick)
if (pickType == ChoiceType.Pick && currentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick))
{
scheduledChange?.Cancel();
scheduledChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000);

View File

@ -1,30 +1,36 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
using osuTK.Graphics;
using System.Collections.Generic;
namespace osu.Game.Overlays.Profile.Sections
namespace osu.Game.Graphics.UserInterface
{
public class ShowMoreButton : OsuHoverContainer
{
private const float fade_duration = 200;
private readonly Box background;
private readonly LoadingAnimation loading;
private readonly FillFlowContainer content;
private Color4 chevronIconColour;
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
protected Color4 ChevronIconColour
{
get => chevronIconColour;
set => chevronIconColour = leftChevron.Colour = rightChevron.Colour = value;
}
public string Text
{
get => text.Text;
set => text.Text = value;
}
private bool isLoading;
@ -33,26 +39,32 @@ namespace osu.Game.Overlays.Profile.Sections
get => isLoading;
set
{
if (isLoading == value)
return;
isLoading = value;
Enabled.Value = !isLoading;
if (value)
{
loading.FadeIn(fade_duration, Easing.OutQuint);
loading.Show();
content.FadeOut(fade_duration, Easing.OutQuint);
}
else
{
loading.FadeOut(fade_duration, Easing.OutQuint);
loading.Hide();
content.FadeIn(fade_duration, Easing.OutQuint);
}
}
}
private readonly Box background;
private readonly LoadingAnimation loading;
private readonly FillFlowContainer content;
private readonly ChevronIcon leftChevron;
private readonly ChevronIcon rightChevron;
private readonly SpriteText text;
protected override IEnumerable<Drawable> EffectTargets => new[] { background };
public ShowMoreButton()
{
AutoSizeAxes = Axes.Both;
@ -77,15 +89,15 @@ namespace osu.Game.Overlays.Profile.Sections
Spacing = new Vector2(7),
Children = new Drawable[]
{
new ChevronIcon(),
new OsuSpriteText
leftChevron = new ChevronIcon(),
text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
Text = "show more".ToUpper(),
},
new ChevronIcon(),
rightChevron = new ChevronIcon(),
}
},
loading = new LoadingAnimation
@ -99,13 +111,6 @@ namespace osu.Game.Overlays.Profile.Sections
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
IdleColour = colors.GreySeafoamDark;
HoverColour = colors.GreySeafoam;
}
protected override bool OnClick(ClickEvent e)
{
if (!Enabled.Value)
@ -133,12 +138,6 @@ namespace osu.Game.Overlays.Profile.Sections
Size = new Vector2(icon_size);
Icon = FontAwesome.Solid.ChevronDown;
}
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
Colour = colors.Yellow;
}
}
}
}

View File

@ -0,0 +1,47 @@
// 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.IO.Network;
using Humanizer;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.Comments;
namespace osu.Game.Online.API.Requests
{
public class GetCommentsRequest : APIRequest<CommentBundle>
{
private readonly long id;
private readonly int page;
private readonly CommentableType type;
private readonly CommentsSortCriteria sort;
public GetCommentsRequest(CommentableType type, long id, CommentsSortCriteria sort = CommentsSortCriteria.New, int page = 1)
{
this.type = type;
this.sort = sort;
this.id = id;
this.page = page;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.AddParameter("commentable_type", type.ToString().Underscore().ToLowerInvariant());
req.AddParameter("commentable_id", id.ToString());
req.AddParameter("sort", sort.ToString().ToLowerInvariant());
req.AddParameter("page", page.ToString());
return req;
}
protected override string Target => "comments";
}
public enum CommentableType
{
Build,
Beatmapset,
NewsPost
}
}

View File

@ -0,0 +1,79 @@
// 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 Newtonsoft.Json;
using osu.Game.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
namespace osu.Game.Online.API.Requests.Responses
{
public class Comment
{
[JsonProperty(@"id")]
public long Id { get; set; }
[JsonProperty(@"parent_id")]
public long? ParentId { get; set; }
public readonly List<Comment> ChildComments = new List<Comment>();
public Comment ParentComment { get; set; }
[JsonProperty(@"user_id")]
public long? UserId { get; set; }
public User User { get; set; }
[JsonProperty(@"message")]
public string Message { get; set; }
[JsonProperty(@"message_html")]
public string MessageHtml { get; set; }
[JsonProperty(@"replies_count")]
public int RepliesCount { get; set; }
[JsonProperty(@"votes_count")]
public int VotesCount { get; set; }
[JsonProperty(@"commenatble_type")]
public string CommentableType { get; set; }
[JsonProperty(@"commentable_id")]
public int CommentableId { get; set; }
[JsonProperty(@"legacy_name")]
public string LegacyName { get; set; }
[JsonProperty(@"created_at")]
public DateTimeOffset CreatedAt { get; set; }
[JsonProperty(@"updated_at")]
public DateTimeOffset? UpdatedAt { get; set; }
[JsonProperty(@"deleted_at")]
public DateTimeOffset? DeletedAt { get; set; }
[JsonProperty(@"edited_at")]
public DateTimeOffset? EditedAt { get; set; }
[JsonProperty(@"edited_by_id")]
public long? EditedById { get; set; }
public User EditedUser { get; set; }
public bool IsTopLevel => !ParentId.HasValue;
public bool IsDeleted => DeletedAt.HasValue;
public bool HasMessage => !string.IsNullOrEmpty(MessageHtml);
public string GetMessage => HasMessage ? WebUtility.HtmlDecode(Regex.Replace(MessageHtml, @"<(.|\n)*?>", string.Empty)) : string.Empty;
public int DeletedChildrenCount => ChildComments.Count(c => c.IsDeleted);
}
}

View File

@ -0,0 +1,80 @@
// 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 Newtonsoft.Json;
using osu.Game.Users;
using System.Collections.Generic;
namespace osu.Game.Online.API.Requests.Responses
{
public class CommentBundle
{
private List<Comment> comments;
[JsonProperty(@"comments")]
public List<Comment> Comments
{
get => comments;
set
{
comments = value;
comments.ForEach(child =>
{
if (child.ParentId != null)
{
comments.ForEach(parent =>
{
if (parent.Id == child.ParentId)
{
parent.ChildComments.Add(child);
child.ParentComment = parent;
}
});
}
});
}
}
[JsonProperty(@"has_more")]
public bool HasMore { get; set; }
[JsonProperty(@"has_more_id")]
public long? HasMoreId { get; set; }
[JsonProperty(@"user_follow")]
public bool UserFollow { get; set; }
[JsonProperty(@"included_comments")]
public List<Comment> IncludedComments { get; set; }
private List<User> users;
[JsonProperty(@"users")]
public List<User> Users
{
get => users;
set
{
users = value;
value.ForEach(u =>
{
Comments.ForEach(c =>
{
if (c.UserId == u.Id)
c.User = u;
if (c.EditedById == u.Id)
c.EditedUser = u;
});
});
}
}
[JsonProperty(@"total")]
public int Total { get; set; }
[JsonProperty(@"top_level_count")]
public int TopLevelCount { get; set; }
}
}

View File

@ -102,7 +102,7 @@ namespace osu.Game
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
private readonly List<OverlayContainer> toolbarElements = new List<OverlayContainer>();
private readonly List<VisibilityContainer> toolbarElements = new List<VisibilityContainer>();
private readonly List<OverlayContainer> visibleBlockingOverlays = new List<OverlayContainer>();

View File

@ -0,0 +1,197 @@
// 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.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using System.Threading;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Overlays.Comments
{
public class CommentsContainer : CompositeDrawable
{
private readonly CommentableType type;
private readonly long id;
public readonly Bindable<CommentsSortCriteria> Sort = new Bindable<CommentsSortCriteria>();
public readonly BindableBool ShowDeleted = new BindableBool();
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private OsuColour colours { get; set; }
private GetCommentsRequest request;
private CancellationTokenSource loadCancellation;
private int currentPage;
private readonly Box background;
private readonly FillFlowContainer content;
private readonly DeletedChildrenPlaceholder deletedChildrenPlaceholder;
private readonly CommentsShowMoreButton moreButton;
public CommentsContainer(CommentableType type, long id)
{
this.type = type;
this.id = id;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new CommentsHeader
{
Sort = { BindTarget = Sort },
ShowDeleted = { BindTarget = ShowDeleted }
},
content = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.2f)
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
deletedChildrenPlaceholder = new DeletedChildrenPlaceholder
{
ShowDeleted = { BindTarget = ShowDeleted }
},
new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Child = moreButton = new CommentsShowMoreButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding(5),
Action = getComments
}
}
}
}
}
}
}
}
});
}
[BackgroundDependencyLoader]
private void load()
{
background.Colour = colours.Gray2;
}
protected override void LoadComplete()
{
Sort.BindValueChanged(onSortChanged, true);
base.LoadComplete();
}
private void onSortChanged(ValueChangedEvent<CommentsSortCriteria> sort)
{
clearComments();
getComments();
}
private void getComments()
{
request?.Cancel();
loadCancellation?.Cancel();
request = new GetCommentsRequest(type, id, Sort.Value, currentPage++);
request.Success += onSuccess;
api.Queue(request);
}
private void clearComments()
{
currentPage = 1;
deletedChildrenPlaceholder.DeletedCount.Value = 0;
moreButton.IsLoading = true;
content.Clear();
}
private void onSuccess(CommentBundle response)
{
loadCancellation = new CancellationTokenSource();
FillFlowContainer page = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
};
foreach (var c in response.Comments)
{
if (c.IsTopLevel)
page.Add(new DrawableComment(c)
{
ShowDeleted = { BindTarget = ShowDeleted }
});
}
LoadComponentAsync(page, loaded =>
{
content.Add(loaded);
deletedChildrenPlaceholder.DeletedCount.Value += response.Comments.Count(c => c.IsDeleted && c.IsTopLevel);
if (response.HasMore)
{
int loadedTopLevelComments = 0;
content.Children.OfType<FillFlowContainer>().ForEach(p => loadedTopLevelComments += p.Children.OfType<DrawableComment>().Count());
moreButton.Current.Value = response.TopLevelCount - loadedTopLevelComments;
moreButton.IsLoading = false;
}
moreButton.FadeTo(response.HasMore ? 1 : 0);
}, loadCancellation.Token);
}
protected override void Dispose(bool isDisposing)
{
request?.Cancel();
loadCancellation?.Cancel();
base.Dispose(isDisposing);
}
}
}

View File

@ -0,0 +1,128 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Framework.Graphics.Sprites;
using osuTK;
using osu.Framework.Input.Events;
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();
private readonly Box background;
public CommentsHeader()
{
RelativeSizeAxes = Axes.X;
Height = 40;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 50 },
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Children = new Drawable[]
{
new SpriteText
{
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
}
}
},
new ShowDeletedButton
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Checked = { BindTarget = ShowDeleted }
}
}
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray3;
}
private class ShowDeletedButton : HeaderButton
{
public readonly BindableBool Checked = new BindableBool();
private readonly SpriteIcon checkboxIcon;
public ShowDeletedButton()
{
Add(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
checkboxIcon = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(10),
},
new SpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: font_size),
Text = @"Show deleted"
}
},
});
}
protected override void LoadComplete()
{
Checked.BindValueChanged(isChecked => checkboxIcon.Icon = isChecked.NewValue ? FontAwesome.Solid.CheckSquare : FontAwesome.Regular.Square, true);
base.LoadComplete();
}
protected override bool OnClick(ClickEvent e)
{
Checked.Value = !Checked.Value;
return true;
}
}
}
}

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.Bindables;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Comments
{
public class CommentsShowMoreButton : ShowMoreButton
{
public readonly BindableInt Current = new BindableInt();
public CommentsShowMoreButton()
{
IdleColour = OsuColour.Gray(0.3f);
HoverColour = OsuColour.Gray(0.4f);
ChevronIconColour = OsuColour.Gray(0.5f);
}
protected override void LoadComplete()
{
Current.BindValueChanged(onCurrentChanged, true);
base.LoadComplete();
}
private void onCurrentChanged(ValueChangedEvent<int> count)
{
Text = $@"Show More ({count.NewValue})".ToUpper();
}
}
}

View File

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

View File

@ -0,0 +1,363 @@
// 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.Game.Graphics;
using osu.Framework.Graphics.Sprites;
using osuTK;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Users.Drawables;
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.Online.Chat;
namespace osu.Game.Overlays.Comments
{
public class DrawableComment : CompositeDrawable
{
private const int avatar_size = 40;
private const int margin = 10;
public readonly BindableBool ShowDeleted = new BindableBool();
private readonly BindableBool childrenExpanded = new BindableBool(true);
private readonly FillFlowContainer childCommentsVisibilityContainer;
private readonly Comment comment;
public DrawableComment(Comment comment)
{
LinkFlowContainer username;
FillFlowContainer childCommentsContainer;
DeletedChildrenPlaceholder deletedChildrenPlaceholder;
FillFlowContainer info;
LinkFlowContainer message;
GridContainer content;
VotePill votePill;
this.comment = comment;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(margin),
Child = content = new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize)
},
Content = new[]
{
new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Horizontal = margin },
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
votePill = new VotePill(comment.VotesCount)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AlwaysPresent = true,
},
new UpdateableAvatar(comment.User)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(avatar_size),
Masking = true,
CornerRadius = avatar_size / 2f,
},
}
},
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))
{
AutoSizeAxes = Axes.Both,
},
new ParentUsername(comment),
new SpriteText
{
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),
Colour = OsuColour.Gray(0.7f),
Children = new Drawable[]
{
new SpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Text = HumanizerUtils.Humanize(comment.CreatedAt)
},
new RepliesButton(comment.RepliesCount)
{
Expanded = { BindTarget = childrenExpanded }
},
}
}
}
}
}
}
}
},
childCommentsVisibilityContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
childCommentsContainer = new FillFlowContainer
{
Padding = new MarginPadding { Left = 20 },
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical
},
deletedChildrenPlaceholder = new DeletedChildrenPlaceholder
{
ShowDeleted = { BindTarget = ShowDeleted }
}
}
}
}
};
deletedChildrenPlaceholder.DeletedCount.Value = comment.DeletedChildrenCount;
if (comment.UserId.HasValue)
username.AddUserLink(comment.User);
else
username.AddText(comment.LegacyName);
if (comment.EditedAt.HasValue)
{
info.Add(new SpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Text = $@"edited {HumanizerUtils.Humanize(comment.EditedAt.Value)} by {comment.EditedUser.Username}"
});
}
if (comment.HasMessage)
{
var formattedSource = MessageFormatter.FormatText(comment.GetMessage);
message.AddLinks(formattedSource.Text, formattedSource.Links);
}
if (comment.IsDeleted)
{
content.FadeColour(OsuColour.Gray(0.5f));
votePill.Hide();
}
if (comment.IsTopLevel)
{
AddInternal(new Container
{
RelativeSizeAxes = Axes.X,
Height = 1.5f,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
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)
{
ShowDeleted = { BindTarget = ShowDeleted }
}));
}
protected override void LoadComplete()
{
ShowDeleted.BindValueChanged(show =>
{
if (comment.IsDeleted)
this.FadeTo(show.NewValue ? 1 : 0);
}, true);
childrenExpanded.BindValueChanged(expanded => childCommentsVisibilityContainer.FadeTo(expanded.NewValue ? 1 : 0), true);
base.LoadComplete();
}
private class ChevronButton : ShowChildrenButton
{
private readonly SpriteIcon icon;
public ChevronButton(Comment comment)
{
Alpha = comment.IsTopLevel && comment.ChildComments.Any() ? 1 : 0;
Child = icon = new SpriteIcon
{
Size = new Vector2(12),
Colour = OsuColour.Gray(0.7f)
};
}
protected override void OnExpandedChanged(ValueChangedEvent<bool> expanded)
{
icon.Icon = expanded.NewValue ? FontAwesome.Solid.ChevronUp : FontAwesome.Solid.ChevronDown;
}
}
private class RepliesButton : ShowChildrenButton
{
private readonly SpriteText text;
private readonly int count;
public RepliesButton(int count)
{
this.count = count;
Alpha = count == 0 ? 0 : 1;
Child = text = new SpriteText
{
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
};
}
protected override void OnExpandedChanged(ValueChangedEvent<bool> expanded)
{
text.Text = $@"{(expanded.NewValue ? "[+]" : "[-]")} replies ({count})";
}
}
private class ParentUsername : FillFlowContainer, IHasTooltip
{
public string TooltipText => getParentMessage();
private readonly Comment parentComment;
public ParentUsername(Comment comment)
{
parentComment = comment.ParentComment;
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(3, 0);
Alpha = comment.ParentId == null ? 0 : 1;
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.Solid.Reply,
Size = new Vector2(14),
},
new SpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true),
Text = parentComment?.User?.Username ?? parentComment?.LegacyName
}
};
}
private string getParentMessage()
{
if (parentComment == null)
return string.Empty;
return parentComment.HasMessage ? parentComment.GetMessage : parentComment.IsDeleted ? @"deleted" : string.Empty;
}
}
private class VotePill : CircularContainer
{
public VotePill(int count)
{
AutoSizeAxes = Axes.X;
Height = 20;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(0.05f)
},
new SpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = margin },
Font = OsuFont.GetFont(size: 14),
Text = $"+{count}"
}
};
}
}
}
}

View File

@ -0,0 +1,69 @@
// 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.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Comments
{
public class HeaderButton : Container
{
private const int transition_duration = 200;
protected override Container<Drawable> Content => content;
private readonly Box background;
private readonly Container content;
public HeaderButton()
{
AutoSizeAxes = Axes.X;
Height = 20;
Masking = true;
CornerRadius = 3;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
content = new Container
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Margin = new MarginPadding { Horizontal = 10 }
},
new HoverClickSounds(),
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray4;
}
protected override bool OnHover(HoverEvent e)
{
ShowBackground();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
HideBackground();
}
protected void ShowBackground() => background.FadeIn(transition_duration, Easing.OutQuint);
protected void HideBackground() => background.FadeOut(transition_duration, Easing.OutQuint);
}
}

View File

@ -0,0 +1,34 @@
// 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.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Bindables;
namespace osu.Game.Overlays.Comments
{
public abstract class ShowChildrenButton : OsuHoverContainer
{
public readonly BindableBool Expanded = new BindableBool(true);
protected ShowChildrenButton()
{
AutoSizeAxes = Axes.Both;
}
protected override void LoadComplete()
{
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

@ -0,0 +1,109 @@
// 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 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 OsuColour colours { get; set; }
private readonly SpriteText text;
public TabButton(CommentsSortCriteria value)
{
Add(text = new SpriteText
{
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 ? colours.BlueLighter : 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

@ -16,7 +16,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays.Music
{
public class PlaylistOverlay : OverlayContainer
public class PlaylistOverlay : VisibilityContainer
{
private const float transition_duration = 600;
private const float playlist_height = 510;

View File

@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Profile.Sections
{
public abstract class PaginatedContainer<TModel> : FillFlowContainer
{
private readonly ShowMoreButton moreButton;
private readonly ProfileShowMoreButton moreButton;
private readonly OsuSpriteText missingText;
private APIRequest<List<TModel>> retrievalRequest;
private CancellationTokenSource loadCancellation;
@ -56,7 +56,7 @@ namespace osu.Game.Overlays.Profile.Sections
RelativeSizeAxes = Axes.X,
Spacing = new Vector2(0, 2),
},
moreButton = new ShowMoreButton
moreButton = new ProfileShowMoreButton
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Profile.Sections
{
public class ProfileShowMoreButton : ShowMoreButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
IdleColour = colors.GreySeafoamDark;
HoverColour = colors.GreySeafoam;
ChevronIconColour = colors.Yellow;
}
}
}

View File

@ -16,7 +16,7 @@ using osu.Game.Rulesets;
namespace osu.Game.Overlays.Toolbar
{
public class Toolbar : OverlayContainer
public class Toolbar : VisibilityContainer
{
public const float HEIGHT = 40;
public const float TOOLTIP_HEIGHT = 30;
@ -26,8 +26,6 @@ namespace osu.Game.Overlays.Toolbar
private ToolbarUserButton userButton;
private ToolbarRulesetSelector rulesetSelector;
protected override bool BlockPositionalInput => false;
private const double transition_time = 500;
private const float alpha_hovering = 0.8f;

View File

@ -19,7 +19,7 @@ using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class VolumeOverlay : OverlayContainer
public class VolumeOverlay : VisibilityContainer
{
private const float offset = 10;
@ -28,8 +28,6 @@ namespace osu.Game.Overlays
private VolumeMeter volumeMeterMusic;
private MuteButton muteButton;
protected override bool BlockPositionalInput => false;
private readonly BindableDouble muteAdjustment = new BindableDouble();
private readonly Bindable<bool> isMuted = new Bindable<bool>();

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Play
/// <summary>
/// An overlay which can be used to require further user actions before gameplay is resumed.
/// </summary>
public abstract class ResumeOverlay : OverlayContainer
public abstract class ResumeOverlay : VisibilityContainer
{
public CursorContainer GameplayCursor { get; set; }
@ -29,8 +29,6 @@ namespace osu.Game.Screens.Play
protected const float TRANSITION_TIME = 500;
protected override bool BlockPositionalInput => false;
protected abstract string Message { get; }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;

View File

@ -23,7 +23,7 @@ using osu.Game.Input.Bindings;
namespace osu.Game.Screens.Play
{
public class SkipOverlay : OverlayContainer, IKeyBindingHandler<GlobalAction>
public class SkipOverlay : VisibilityContainer, IKeyBindingHandler<GlobalAction>
{
private readonly double startTime;
@ -36,7 +36,6 @@ namespace osu.Game.Screens.Play
private double displayTime;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
protected override bool BlockPositionalInput => false;
/// <summary>
/// Displays a skip overlay, giving the user the ability to skip forward.

View File

@ -29,7 +29,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Screens.Select
{
public class BeatmapInfoWedge : OverlayContainer
public class BeatmapInfoWedge : VisibilityContainer
{
private const float shear_width = 36.75f;
@ -62,8 +62,6 @@ namespace osu.Game.Screens.Select
ruleset.ValueChanged += _ => updateDisplay();
}
protected override bool BlockPositionalInput => false;
protected override void PopIn()
{
this.MoveToX(0, 800, Easing.OutQuint);