diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 9014fce18d..73ac7c9df4 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -77,7 +77,7 @@ namespace osu.Game.Online.Chat
throw new ArgumentNullException(nameof(user));
CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id))
- ?? new Channel { Name = user.Username, Users = { user } };
+ ?? new Channel { Name = user.Username, Users = { user }, Type = ChannelType.PM };
}
private void currentChannelChanged(Channel channel) => JoinChannel(channel);
@@ -223,13 +223,11 @@ namespace osu.Game.Online.Chat
{
foreach (var channel in channels)
{
- // add as available if not already
- if (AvailableChannels.All(c => c.Id != channel.Id))
- AvailableChannels.Add(channel);
+ var ch = getChannel(channel, addToAvailable: true);
// join any channels classified as "defaults"
if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase)))
- JoinChannel(channel);
+ JoinChannel(ch);
}
};
req.Failure += error =>
@@ -262,37 +260,73 @@ namespace osu.Game.Online.Chat
api.Queue(fetchInitialMsgReq);
}
- public void JoinChannel(Channel channel)
+ ///
+ /// Find an existing channel instance for the provided channel. Lookup is performed basd on ID.
+ /// The provided channel may be used if an existing instance is not found.
+ ///
+ /// A candidate channel to be used for lookup or permanently on lookup failure.
+ /// Whether the channel should be added to if not already.
+ /// Whether the channel should be added to if not already.
+ /// The found channel.
+ private Channel getChannel(Channel lookup, bool addToAvailable = false, bool addToJoined = false)
{
- if (channel == null) return;
+ Channel found = null;
- // ReSharper disable once AccessToModifiedClosure
- var existing = JoinedChannels.FirstOrDefault(c => c.Id == channel.Id);
+ bool lookupCondition(Channel ch) => lookup.Id > 0 ? ch.Id == lookup.Id : lookup.Name == ch.Name;
- if (existing != null)
+ var available = AvailableChannels.FirstOrDefault(lookupCondition);
+ if (available != null)
+ found = available;
+
+ var joined = JoinedChannels.FirstOrDefault(lookupCondition);
+ if (found == null && joined != null)
+ found = joined;
+
+ if (found == null)
{
- // if we already have this channel loaded, we don't want to make a second one.
- channel = existing;
- }
- else
- {
- var foundSelf = channel.Users.FirstOrDefault(u => u.Id == api.LocalUser.Value.Id);
+ found = lookup;
+
+ // if we're using a channel object from the server, we want to remove ourselves from the users list.
+ // this is because we check the first user in the channel to display a name/icon on tabs for now.
+ var foundSelf = found.Users.FirstOrDefault(u => u.Id == api.LocalUser.Value.Id);
if (foundSelf != null)
- channel.Users.Remove(foundSelf);
+ found.Users.Remove(foundSelf);
+ }
- JoinedChannels.Add(channel);
+ if (joined == null && addToJoined) JoinedChannels.Add(found);
+ if (available == null && addToAvailable) AvailableChannels.Add(found);
- if (channel.Type == ChannelType.Public && !channel.Joined)
+ return found;
+ }
+
+ ///
+ /// Joins a channel if it has not already been joined.
+ ///
+ /// The channel to join.
+ /// Whether the channel has already been joined server-side. Will skip a join request.
+ /// The joined channel. Note that this may not match the parameter channel as it is a backed object.
+ public Channel JoinChannel(Channel channel, bool alreadyJoined = false)
+ {
+ if (channel == null) return null;
+
+ channel = getChannel(channel, addToJoined: true);
+
+ // ensure we are joined to the channel
+ if (!channel.Joined.Value)
+ {
+ if (alreadyJoined)
+ channel.Joined.Value = true;
+ else
{
- var req = new JoinChannelRequest(channel, api.LocalUser);
- req.Success += () =>
+ switch (channel.Type)
{
- channel.Joined.Value = true;
- JoinChannel(channel);
- };
- req.Failure += ex => LeaveChannel(channel);
- api.Queue(req);
- return;
+ case ChannelType.Public:
+ var req = new JoinChannelRequest(channel, api.LocalUser);
+ req.Success += () => JoinChannel(channel, true);
+ req.Failure += ex => LeaveChannel(channel);
+ api.Queue(req);
+ return channel;
+ }
}
}
@@ -304,6 +338,8 @@ namespace osu.Game.Online.Chat
// let's fetch a small number of messages to bring us up-to-date with the backlog.
fetchInitalMessages(channel);
}
+
+ return channel;
}
public void LeaveChannel(Channel channel)
@@ -353,20 +389,8 @@ namespace osu.Game.Online.Chat
{
foreach (var channel in updates.Presence)
{
- if (!channel.Joined.Value)
- {
- // we received this from the server so should mark the channel already joined.
- channel.Joined.Value = true;
-
- JoinChannel(channel);
- }
- }
-
- if (!channelsInitialised)
- {
- channelsInitialised = true;
- // we want this to run after the first presence so we can see if the user is in any channels already.
- initializeChannels();
+ // we received this from the server so should mark the channel already joined.
+ JoinChannel(channel, true);
}
//todo: handle left channels
@@ -379,6 +403,13 @@ namespace osu.Game.Online.Chat
lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId;
}
+ if (!channelsInitialised)
+ {
+ channelsInitialised = true;
+ // we want this to run after the first presence so we can see if the user is in any channels already.
+ initializeChannels();
+ }
+
fetchUpdates();
};
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 4a358da227..2894e096fb 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -50,7 +50,9 @@ namespace osu.Game
{
public Toolbar Toolbar;
- private ChatOverlay chat;
+ private ChatOverlay chatOverlay;
+
+ private ChannelManager channelManager;
private MusicController musicController;
@@ -338,12 +340,8 @@ namespace osu.Game
//overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add);
- loadComponentSingleFile(new ChannelManager(), channelManager =>
- {
- dependencies.Cache(channelManager);
- AddInternal(channelManager);
- });
- loadComponentSingleFile(chat = new ChatOverlay { Depth = -1 }, mainContent.Add);
+ loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal);
+ loadComponentSingleFile(chatOverlay = new ChatOverlay { Depth = -1 }, mainContent.Add);
loadComponentSingleFile(settings = new MainSettings
{
GetToolbarHeight = () => ToolbarOffset,
@@ -376,7 +374,8 @@ namespace osu.Game
dependencies.Cache(onscreenDisplay);
dependencies.Cache(social);
dependencies.Cache(direct);
- dependencies.Cache(chat);
+ dependencies.Cache(chatOverlay);
+ dependencies.Cache(channelManager);
dependencies.Cache(userProfile);
dependencies.Cache(musicController);
dependencies.Cache(beatmapSetOverlay);
@@ -409,7 +408,7 @@ namespace osu.Game
}
// ensure only one of these overlays are open at once.
- var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct };
+ var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, social, direct };
overlays.AddRange(singleDisplayOverlays);
foreach (var overlay in singleDisplayOverlays)
@@ -534,7 +533,7 @@ namespace osu.Game
switch (action)
{
case GlobalAction.ToggleChat:
- chat.ToggleVisibility();
+ chatOverlay.ToggleVisibility();
return true;
case GlobalAction.ToggleSocial:
social.ToggleVisibility();
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index f46cf0f1a0..b1edfe0548 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -209,7 +209,6 @@ namespace osu.Game.Overlays
{
textbox.Current.Disabled = true;
currentChannelContainer.Clear(false);
- channelTabControl.Current.Value = null;
return;
}
diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs
index c249651f98..c67ed5b845 100644
--- a/osu.Game/Overlays/Volume/VolumeMeter.cs
+++ b/osu.Game/Overlays/Volume/VolumeMeter.cs
@@ -228,15 +228,19 @@ namespace osu.Game.Overlays.Volume
public void Decrease(double amount = 1, bool isPrecise = false) => adjust(-amount, isPrecise);
// because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible.
- private double adjustAccumulator;
+ private double scrollAccumulation;
private void adjust(double delta, bool isPrecise)
{
- adjustAccumulator += delta * adjust_step * (isPrecise ? 0.1 : 1);
- if (Math.Abs(adjustAccumulator) < Bindable.Precision)
- return;
- Volume += adjustAccumulator;
- adjustAccumulator = 0;
+ scrollAccumulation += delta * adjust_step * (isPrecise ? 0.1 : 1);
+
+ var precision = Bindable.Precision;
+
+ while (Math.Abs(scrollAccumulation) > precision)
+ {
+ Volume += Math.Sign(scrollAccumulation) * precision;
+ scrollAccumulation = scrollAccumulation < 0 ? Math.Min(0, scrollAccumulation + precision) : Math.Max(0, scrollAccumulation - precision);
+ }
}
protected override bool OnScroll(ScrollEvent e)
diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs
index 0be15de7f4..c4fb9dc419 100644
--- a/osu.Game/Screens/Edit/Editor.cs
+++ b/osu.Game/Screens/Edit/Editor.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osuTK.Graphics;
using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds;
@@ -181,12 +182,24 @@ namespace osu.Game.Screens.Edit
LoadComponentAsync(currentScreen, screenContainer.Add);
}
+ private double scrollAccumulation;
+
protected override bool OnScroll(ScrollEvent e)
{
- if (e.ScrollDelta.X + e.ScrollDelta.Y > 0)
- clock.SeekBackward(!clock.IsRunning);
- else
- clock.SeekForward(!clock.IsRunning);
+ scrollAccumulation += (e.ScrollDelta.X + e.ScrollDelta.Y) * (e.IsPrecise ? 0.1 : 1);
+
+ const int precision = 1;
+
+ while (Math.Abs(scrollAccumulation) > precision)
+ {
+ if (scrollAccumulation > 0)
+ clock.SeekBackward(!clock.IsRunning);
+ else
+ clock.SeekForward(!clock.IsRunning);
+
+ scrollAccumulation = scrollAccumulation < 0 ? Math.Min(0, scrollAccumulation + precision) : Math.Max(0, scrollAccumulation - precision);
+ }
+
return true;
}
diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index 4a677001a0..0748f68dca 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -310,9 +310,9 @@ namespace osu.Game.Screens.Select.Leaderboards
currentPlaceholder = placeholder;
}
- protected override void Update()
+ protected override void UpdateAfterChildren()
{
- base.Update();
+ base.UpdateAfterChildren();
var fadeStart = scrollContainer.Current + scrollContainer.DrawHeight;