mirror of
https://github.com/ppy/osu.git
synced 2025-01-19 10:12:53 +08:00
Merge branch 'url-parsing-support' of https://github.com/freezylemon/osu into url-parsing-support
This commit is contained in:
commit
37490c65cc
@ -2,7 +2,9 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
@ -10,12 +12,26 @@ namespace osu.Game.Tests.Visual
|
||||
[Description("Testing chat api and overlay")]
|
||||
internal class TestCaseChatDisplay : OsuTestCase
|
||||
{
|
||||
private BeatmapSetOverlay beatmapSetOverlay;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
||||
|
||||
public TestCaseChatDisplay()
|
||||
{
|
||||
Add(new ChatOverlay
|
||||
{
|
||||
State = Visibility.Visible
|
||||
});
|
||||
|
||||
Add(beatmapSetOverlay = new BeatmapSetOverlay());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
dependencies.Cache(beatmapSetOverlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
44
osu.Game/Graphics/Containers/OsuLinkTextFlowContainer.cs
Normal file
44
osu.Game/Graphics/Containers/OsuLinkTextFlowContainer.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
public class OsuLinkTextFlowContainer : OsuLinkTextFlowContainer<OsuLinkSpriteText>
|
||||
{
|
||||
public OsuLinkTextFlowContainer(Action<SpriteText> defaultCreationParameters = null)
|
||||
: base(defaultCreationParameters)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class OsuLinkTextFlowContainer<T> : OsuTextFlowContainer
|
||||
where T : OsuLinkSpriteText, new()
|
||||
{
|
||||
public override bool HandleInput => true;
|
||||
|
||||
public OsuLinkTextFlowContainer(Action<SpriteText> defaultCreationParameters = null) : base(defaultCreationParameters)
|
||||
{
|
||||
}
|
||||
|
||||
protected override SpriteText CreateSpriteText() => new T();
|
||||
|
||||
public void AddLink(string text, string url, Action<SpriteText> creationParameters = null)
|
||||
{
|
||||
AddText(text, link =>
|
||||
{
|
||||
((T)link).Url = url;
|
||||
creationParameters?.Invoke(link);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
98
osu.Game/Graphics/Sprites/OsuLinkSpriteText.cs
Normal file
98
osu.Game/Graphics/Sprites/OsuLinkSpriteText.cs
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Graphics.Sprites
|
||||
{
|
||||
public class OsuLinkSpriteText : OsuSpriteText
|
||||
{
|
||||
private readonly OsuHoverContainer content;
|
||||
|
||||
private BeatmapSetOverlay beatmapSetOverlay;
|
||||
|
||||
public override bool HandleInput => content.Action != null;
|
||||
|
||||
protected override Container<Drawable> Content => content ?? (Container<Drawable>)this;
|
||||
|
||||
protected override IEnumerable<Drawable> FlowingChildren => Children;
|
||||
|
||||
private string url;
|
||||
|
||||
public string Url
|
||||
{
|
||||
get
|
||||
{
|
||||
return url;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
url = value;
|
||||
loadAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OsuLinkSpriteText()
|
||||
{
|
||||
AddInternal(content = new OsuHoverContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapSetOverlay beatmapSetOverlay)
|
||||
{
|
||||
this.beatmapSetOverlay = beatmapSetOverlay;
|
||||
}
|
||||
|
||||
private void loadAction()
|
||||
{
|
||||
if (Url == null || String.IsNullOrEmpty(Url))
|
||||
return;
|
||||
|
||||
var url = Url;
|
||||
|
||||
if (url.StartsWith("https://")) url = url.Substring(8);
|
||||
else if (url.StartsWith("http://")) url = url.Substring(7);
|
||||
else content.Action = () => Process.Start(Url);
|
||||
|
||||
if (url.StartsWith("osu.ppy.sh/"))
|
||||
{
|
||||
url = url.Substring(11);
|
||||
if (url.StartsWith("s") || url.StartsWith("beatmapsets"))
|
||||
content.Action = () => beatmapSetOverlay.ShowBeatmapSet(getIdFromUrl(url));
|
||||
else if (url.StartsWith("b") || url.StartsWith("beatmaps"))
|
||||
content.Action = () => beatmapSetOverlay.ShowBeatmap(getIdFromUrl(url));
|
||||
// else if (url.StartsWith("d")) Maybe later
|
||||
}
|
||||
}
|
||||
|
||||
private int getIdFromUrl(string url)
|
||||
{
|
||||
var lastSlashIndex = url.LastIndexOf('/');
|
||||
if (lastSlashIndex == url.Length)
|
||||
{
|
||||
url = url.Substring(url.Length - 1);
|
||||
lastSlashIndex = url.LastIndexOf('/');
|
||||
}
|
||||
|
||||
return int.Parse(url.Substring(lastSlashIndex + 1));
|
||||
}
|
||||
}
|
||||
}
|
21
osu.Game/Online/API/Requests/GetBeatmapRequest.cs
Normal file
21
osu.Game/Online/API/Requests/GetBeatmapRequest.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using osu.Game.Beatmaps;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetBeatmapRequest : APIRequest<BeatmapInfo>
|
||||
{
|
||||
private readonly int beatmapId;
|
||||
|
||||
public GetBeatmapRequest(int beatmapId)
|
||||
{
|
||||
this.beatmapId = beatmapId;
|
||||
}
|
||||
|
||||
protected override string Target => $@"beatmaps/{beatmapId}";
|
||||
}
|
||||
}
|
66
osu.Game/Online/Chat/ChatLinkSpriteText.cs
Normal file
66
osu.Game/Online/Chat/ChatLinkSpriteText.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class ChatLinkSpriteText : OsuLinkSpriteText
|
||||
{
|
||||
public int LinkId;
|
||||
|
||||
private Color4 hoverColour;
|
||||
private Color4 urlColour;
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
// Every word is one sprite in chat (for word wrap) so we need to find all other sprites that display the same link
|
||||
var otherSpritesWithSameLink = ((Container<Drawable>)Parent).Children.Where(child => (child as ChatLinkSpriteText)?.LinkId == LinkId && !Equals(child));
|
||||
|
||||
var hoverResult = base.OnHover(state);
|
||||
|
||||
if (!otherSpritesWithSameLink.Any(sprite => sprite.IsHovered))
|
||||
foreach (ChatLinkSpriteText sprite in otherSpritesWithSameLink)
|
||||
sprite.TriggerOnHover(state);
|
||||
|
||||
Content.FadeColour(hoverColour, 500, Easing.OutQuint);
|
||||
|
||||
return hoverResult;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
var spritesWithSameLink = ((Container<Drawable>)Parent).Children.Where(child => (child as ChatLinkSpriteText)?.LinkId == LinkId);
|
||||
|
||||
if (spritesWithSameLink.Any(sprite => sprite.IsHovered))
|
||||
{
|
||||
// We have to do this so this sprite does not fade its colour back
|
||||
Content.FadeColour(hoverColour, 500, Easing.OutQuint);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (ChatLinkSpriteText sprite in spritesWithSameLink)
|
||||
sprite.Content.FadeColour(urlColour, 500, Easing.OutQuint);
|
||||
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
hoverColour = colours.Yellow;
|
||||
urlColour = colours.Blue;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Users;
|
||||
@ -40,6 +42,8 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
}
|
||||
|
||||
public List<MessageFormatter.Link> Links;
|
||||
|
||||
public Message(long? id)
|
||||
{
|
||||
Id = id;
|
||||
|
170
osu.Game/Online/Chat/MessageFormatter.cs
Normal file
170
osu.Game/Online/Chat/MessageFormatter.cs
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public static class MessageFormatter
|
||||
{
|
||||
// [[Performance Points]] -> wiki:Performance Points (https://osu.ppy.sh/wiki/Performance_Points)
|
||||
private static Regex wikiRegex = new Regex(@"\[\[([^\]]+)\]\]");
|
||||
|
||||
// (test)[https://osu.ppy.sh/b/1234] -> test (https://osu.ppy.sh/b/1234)
|
||||
private static Regex oldLinkRegex = new Regex(@"\(([^\)]*)\)\[([a-z]+://[^ ]+)\]");
|
||||
|
||||
// [https://osu.ppy.sh/b/1234 Beatmap [Hard] (poop)] -> Beatmap [hard] (poop) (https://osu.ppy.sh/b/1234)
|
||||
private static Regex newLinkRegex = new Regex(@"\[([a-z]+://[^ ]+) ([^\[\]]*(((?<open>\[)[^\[\]]*)+((?<close-open>\])[^\[\]]*)+)*(?(open)(?!)))\]");
|
||||
|
||||
// advanced, RFC-compatible regular expression that matches any possible URL, *but* allows certain invalid characters that are widely used
|
||||
// This is in the format (<required>, [optional]):
|
||||
// http[s]://<domain>.<tld>[:port][/path][?query][#fragment]
|
||||
private static Regex advancedLinkRegex = new Regex(@"(?<paren>\([^)]*)?" +
|
||||
@"(?<link>https?:\/\/" +
|
||||
@"(?<domain>(?:[a-z0-9]\.|[a-z0-9][a-z0-9-]*[a-z0-9]\.)*[a-z][a-z0-9-]*[a-z0-9]" + // domain, TLD
|
||||
@"(?::\d+)?)" + // port
|
||||
@"(?<path>(?:(?:\/+(?:[a-z0-9$_\.\+!\*\',;:\(\)@&~=-]|%[0-9a-f]{2})*)*" + // path
|
||||
@"(?:\?(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?" + // query
|
||||
@"(?:#(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?)", // fragment
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
// 00:00:000 (1,2,3) - test
|
||||
private static Regex timeRegex = new Regex(@"\d\d:\d\d:\d\d\d? [^-]*");
|
||||
|
||||
// #osu
|
||||
private static Regex channelRegex = new Regex(@"#[a-zA-Z]+[a-zA-Z0-9]+");
|
||||
|
||||
// Unicode emojis
|
||||
private static Regex emojiRegex = new Regex(@"(\uD83D[\uDC00-\uDE4F])");
|
||||
|
||||
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0)
|
||||
{
|
||||
int captureOffset = 0;
|
||||
foreach (Match m in regex.Matches(result.Text, startIndex))
|
||||
{
|
||||
var index = m.Index - captureOffset;
|
||||
|
||||
var displayText = string.Format(display,
|
||||
m.Groups[0],
|
||||
m.Groups.Count > 1 ? m.Groups[1].Value : "",
|
||||
m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
|
||||
|
||||
var linkText = string.Format(link,
|
||||
m.Groups[0],
|
||||
m.Groups.Count > 1 ? m.Groups[1].Value : "",
|
||||
m.Groups.Count > 2 ? m.Groups[2].Value : "").Trim();
|
||||
|
||||
if (displayText.Length == 0 || linkText.Length == 0) continue;
|
||||
|
||||
// Check for encapsulated links
|
||||
if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || index <= l.Index && index + m.Length >= l.Index + l.Length) == null)
|
||||
{
|
||||
result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText);
|
||||
|
||||
//since we just changed the line display text, offset any already processed links.
|
||||
result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0);
|
||||
|
||||
result.Links.Add(new Link(linkText, index, displayText.Length));
|
||||
|
||||
//adjust the offset for processing the current matches group.
|
||||
captureOffset += (m.Length - displayText.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleAdvanced(Regex regex, MessageFormatterResult result, int startIndex = 0)
|
||||
{
|
||||
foreach (Match m in regex.Matches(result.Text, startIndex))
|
||||
{
|
||||
var index = m.Index;
|
||||
var prefix = m.Groups["paren"].Value;
|
||||
var link = m.Groups["link"].Value;
|
||||
var indexLength = link.Length;
|
||||
|
||||
if (!String.IsNullOrEmpty(prefix))
|
||||
{
|
||||
index += prefix.Length;
|
||||
if (link.EndsWith(")"))
|
||||
{
|
||||
indexLength = indexLength - 1;
|
||||
link = link.Remove(link.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
result.Links.Add(new Link(link, index, indexLength));
|
||||
}
|
||||
}
|
||||
|
||||
private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3)
|
||||
{
|
||||
var result = new MessageFormatterResult(toFormat);
|
||||
|
||||
// handle the [link display] format
|
||||
handleMatches(newLinkRegex, "{2}", "{1}", result, startIndex);
|
||||
|
||||
// handle the ()[] link format
|
||||
handleMatches(oldLinkRegex, "{1}", "{2}", result, startIndex);
|
||||
|
||||
// handle wiki links
|
||||
handleMatches(wikiRegex, "wiki:{1}", "https://osu.ppy.sh/wiki/{1}", result, startIndex);
|
||||
|
||||
// handle bare links
|
||||
handleAdvanced(advancedLinkRegex, result, startIndex);
|
||||
|
||||
// handle editor times
|
||||
handleMatches(timeRegex, "{0}", "osu://edit/{0}", result, startIndex);
|
||||
|
||||
// handle channels
|
||||
handleMatches(channelRegex, "{0}", "osu://chan/{0}", result, startIndex);
|
||||
|
||||
var empty = "";
|
||||
while (space-- > 0)
|
||||
empty += "\0";
|
||||
|
||||
handleMatches(emojiRegex, empty, "{0}", result, startIndex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Message FormatMessage(Message inputMessage)
|
||||
{
|
||||
var result = format(inputMessage.Content);
|
||||
var formatted = inputMessage;
|
||||
|
||||
formatted.Content = result.Text;
|
||||
formatted.Links = result.Links;
|
||||
return formatted;
|
||||
}
|
||||
|
||||
public class MessageFormatterResult
|
||||
{
|
||||
public List<Link> Links = new List<Link>();
|
||||
public string Text;
|
||||
public string OriginalText;
|
||||
|
||||
public MessageFormatterResult(string text)
|
||||
{
|
||||
OriginalText = Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public class Link
|
||||
{
|
||||
public string Url;
|
||||
public int Index;
|
||||
public int Length;
|
||||
|
||||
public Link(string url, int startIndex, int length)
|
||||
{
|
||||
Url = url;
|
||||
Index = startIndex;
|
||||
Length = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -80,7 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Playing.Value && preview != null)
|
||||
if (Playing.Value && preview != null && preview.Length > 0)
|
||||
{
|
||||
progress.Width = (float)(preview.CurrentTime / preview.Length);
|
||||
}
|
||||
|
@ -139,6 +139,20 @@ namespace osu.Game.Overlays
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ShowBeatmap(int beatmapId)
|
||||
{
|
||||
var req = new GetBeatmapRequest(beatmapId);
|
||||
req.Success += res =>
|
||||
{
|
||||
if (!res.OnlineBeatmapSetID.HasValue)
|
||||
return;
|
||||
|
||||
ShowBeatmapSet(res.OnlineBeatmapSetID.Value);
|
||||
};
|
||||
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
public void ShowBeatmapSet(int beatmapSetId)
|
||||
{
|
||||
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work.
|
||||
|
@ -67,12 +67,13 @@ namespace osu.Game.Overlays.Chat
|
||||
private const float text_size = 20;
|
||||
|
||||
private Color4 customUsernameColour;
|
||||
private Color4 urlColour;
|
||||
|
||||
private OsuSpriteText timestamp;
|
||||
|
||||
public ChatLine(Message message)
|
||||
{
|
||||
Message = message;
|
||||
Message = MessageFormatter.FormatMessage(message);
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
@ -82,7 +83,7 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
private Message message;
|
||||
private OsuSpriteText username;
|
||||
private OsuTextFlowContainer contentFlow;
|
||||
private OsuLinkTextFlowContainer<ChatLinkSpriteText> contentFlow;
|
||||
|
||||
public Message Message
|
||||
{
|
||||
@ -104,6 +105,7 @@ namespace osu.Game.Overlays.Chat
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
customUsernameColour = colours.ChatBlue;
|
||||
urlColour = colours.Blue;
|
||||
}
|
||||
|
||||
private bool senderHasBackground => !string.IsNullOrEmpty(message.Sender.Colour);
|
||||
@ -187,7 +189,7 @@ namespace osu.Game.Overlays.Chat
|
||||
Padding = new MarginPadding { Left = message_padding + padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
contentFlow = new OsuTextFlowContainer(t => { t.TextSize = text_size; })
|
||||
contentFlow = new OsuLinkTextFlowContainer<ChatLinkSpriteText>(t => { t.TextSize = text_size; })
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -210,16 +212,40 @@ namespace osu.Game.Overlays.Chat
|
||||
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
|
||||
username.Text = $@"{message.Sender.Username}" + (senderHasBackground ? "" : ":");
|
||||
|
||||
if (message.IsAction)
|
||||
contentFlow.Clear();
|
||||
|
||||
if (message.Links == null || message.Links.Count == 0)
|
||||
{
|
||||
contentFlow.Clear();
|
||||
contentFlow.AddText("[", sprite => sprite.Padding = new MarginPadding { Right = action_padding });
|
||||
contentFlow.AddText(message.Content, sprite => sprite.Font = @"Exo2.0-MediumItalic");
|
||||
contentFlow.AddText("]", sprite => sprite.Padding = new MarginPadding { Left = action_padding });
|
||||
contentFlow.AddText(message.Content, sprite =>
|
||||
{
|
||||
if (message.IsAction)
|
||||
sprite.Font = @"Exo2.0-MediumItalic";
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
contentFlow.Text = message.Content;
|
||||
{
|
||||
int prevIndex = 0;
|
||||
foreach (var link in message.Links)
|
||||
{
|
||||
contentFlow.AddText(message.Content.Substring(prevIndex, link.Index - prevIndex));
|
||||
prevIndex = link.Index + link.Length;
|
||||
|
||||
contentFlow.AddLink(message.Content.Substring(link.Index, link.Length), link.Url, sprite =>
|
||||
{
|
||||
if (message.IsAction)
|
||||
sprite.Font = @"Exo2.0-MediumItalic";
|
||||
sprite.Colour = urlColour;
|
||||
// We want to use something that is unique to every formatted link, so I just use the position of the link
|
||||
((ChatLinkSpriteText)sprite).LinkId = link.Index;
|
||||
});
|
||||
}
|
||||
|
||||
// Add text after the last link
|
||||
var lastLink = message.Links[message.Links.Count - 1];
|
||||
contentFlow.AddText(message.Content.Substring(lastLink.Index + lastLink.Length));
|
||||
}
|
||||
}
|
||||
|
||||
private class MessageSender : OsuClickableContainer, IHasContextMenu
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Profile
|
||||
public class ProfileHeader : Container
|
||||
{
|
||||
private readonly OsuTextFlowContainer infoTextLeft;
|
||||
private readonly LinkFlowContainer infoTextRight;
|
||||
private readonly OsuLinkTextFlowContainer infoTextRight;
|
||||
private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText;
|
||||
|
||||
private readonly Container coverContainer, chartContainer, supporterTag;
|
||||
@ -119,7 +119,7 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer.ProfileLink(user)
|
||||
new ProfileLink(user)
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
@ -160,7 +160,7 @@ namespace osu.Game.Overlays.Profile
|
||||
ParagraphSpacing = 0.8f,
|
||||
LineSpacing = 0.2f
|
||||
},
|
||||
infoTextRight = new LinkFlowContainer(t =>
|
||||
infoTextRight = new OsuLinkTextFlowContainer(t =>
|
||||
{
|
||||
t.TextSize = 14;
|
||||
t.Font = @"Exo2.0-RegularItalic";
|
||||
@ -488,57 +488,16 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
}
|
||||
|
||||
private class LinkFlowContainer : OsuTextFlowContainer
|
||||
private class ProfileLink : OsuLinkSpriteText, IHasTooltip
|
||||
{
|
||||
public override bool HandleInput => true;
|
||||
public string TooltipText => "View Profile in Browser";
|
||||
|
||||
public LinkFlowContainer(Action<SpriteText> defaultCreationParameters = null) : base(defaultCreationParameters)
|
||||
public ProfileLink(User user)
|
||||
{
|
||||
}
|
||||
|
||||
protected override SpriteText CreateSpriteText() => new LinkText();
|
||||
|
||||
public void AddLink(string text, string url) => AddText(text, link => ((LinkText)link).Url = url);
|
||||
|
||||
public class LinkText : OsuSpriteText
|
||||
{
|
||||
private readonly OsuHoverContainer content;
|
||||
|
||||
public override bool HandleInput => content.Action != null;
|
||||
|
||||
protected override Container<Drawable> Content => content ?? (Container<Drawable>)this;
|
||||
|
||||
protected override IEnumerable<Drawable> FlowingChildren => Children;
|
||||
|
||||
public string Url
|
||||
{
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
content.Action = () => Process.Start(value);
|
||||
}
|
||||
}
|
||||
|
||||
public LinkText()
|
||||
{
|
||||
AddInternal(content = new OsuHoverContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ProfileLink : LinkText, IHasTooltip
|
||||
{
|
||||
public string TooltipText => "View Profile in Browser";
|
||||
|
||||
public ProfileLink(User user)
|
||||
{
|
||||
Text = user.Username;
|
||||
Url = $@"https://osu.ppy.sh/users/{user.Id}";
|
||||
Font = @"Exo2.0-RegularItalic";
|
||||
TextSize = 30;
|
||||
}
|
||||
Text = user.Username;
|
||||
Url = $@"https://osu.ppy.sh/users/{user.Id}";
|
||||
Font = @"Exo2.0-RegularItalic";
|
||||
TextSize = 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -268,6 +268,8 @@
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
|
||||
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||
<Compile Include="Database\IHasPrimaryKey.cs" />
|
||||
<Compile Include="Graphics\Containers\OsuLinkTextFlowContainer.cs" />
|
||||
<Compile Include="Graphics\Sprites\OsuLinkSpriteText.cs" />
|
||||
<Compile Include="Graphics\UserInterface\HoverClickSounds.cs" />
|
||||
<Compile Include="Graphics\UserInterface\HoverSounds.cs" />
|
||||
<Compile Include="Graphics\UserInterface\OsuButton.cs" />
|
||||
@ -284,8 +286,11 @@
|
||||
<DependentUpon>20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migrations\OsuDbContextModelSnapshot.cs" />
|
||||
<Compile Include="Online\API\Requests\GetBeatmapRequest.cs" />
|
||||
<Compile Include="Online\API\Requests\GetBeatmapSetRequest.cs" />
|
||||
<Compile Include="Online\API\Requests\GetBeatmapSetsResponse.cs" />
|
||||
<Compile Include="Online\Chat\ChatLinkSpriteText.cs" />
|
||||
<Compile Include="Online\Chat\MessageFormatter.cs" />
|
||||
<Compile Include="Overlays\BeatmapSet\Scores\ClickableUsername.cs" />
|
||||
<Compile Include="Overlays\BeatmapSet\Scores\DrawableScore.cs" />
|
||||
<Compile Include="Overlays\BeatmapSet\Scores\DrawableTopScore.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user