diff --git a/appveyor.yml b/appveyor.yml index cc6dfb9c88..b26a895788 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,4 +20,4 @@ build: verbosity: minimal after_build: - cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln - - cmd: NVika parsereport "inspectcodereport.xml" \ No newline at end of file + - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file diff --git a/osu-framework b/osu-framework index 00e48bcd31..3ad1dd52ae 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 00e48bcd31b7debda89bbcf62927706c43eba644 +Subproject commit 3ad1dd52ae511b816fb928f70ef811ec605c5c18 diff --git a/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs new file mode 100644 index 0000000000..b2c211b7f0 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseReplaySettingsOverlay.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Play; +using osu.Game.Screens.Play.ReplaySettings; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseReplaySettingsOverlay : TestCase + { + public override string Description => @"Settings visible in replay/auto"; + + private ExampleContainer container; + + public override void Reset() + { + base.Reset(); + + Add(new ReplaySettingsOverlay() + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }); + + Add(container = new ExampleContainer()); + + AddStep(@"Add button", () => container.Add(new OsuButton + { + RelativeSizeAxes = Axes.X, + Text = @"Button", + })); + + AddStep(@"Add checkbox", () => container.Add(new ReplayCheckbox + { + LabelText = "Checkbox", + })); + + AddStep(@"Add textbox", () => container.Add(new FocusedTextBox + { + RelativeSizeAxes = Axes.X, + Height = 30, + PlaceholderText = "Textbox", + HoldFocus = false, + })); + } + + private class ExampleContainer : ReplayGroup + { + protected override string Title => @"example"; + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs new file mode 100644 index 0000000000..fb5be719c1 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseSkipButton.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Testing; +using osu.Game.Screens.Play; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseSkipButton : TestCase + { + public override string Description => @"Skip skip skippediskip"; + + public override void Reset() + { + base.Reset(); + Add(new SkipButton(Clock.CurrentTime + 5000)); + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs index ba17cfc3d8..2decb4c469 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs @@ -3,20 +3,18 @@ using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Play; namespace osu.Desktop.VisualTests.Tests { internal class TestCaseTwoLayerButton : TestCase { - public override string Description => @"Back and skip and what not"; + public override string Description => @"Mostly back button"; public override void Reset() { base.Reset(); Add(new BackButton()); - Add(new SkipButton(Clock.CurrentTime + 5000)); } } } diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 850c38cc24..f72b08adde 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -196,6 +196,7 @@ + @@ -203,6 +204,7 @@ + diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 8b2a06ad0b..3f56dc0b79 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -71,6 +71,9 @@ namespace osu.Game.Configuration Set(OsuSetting.ShowInterface, true); Set(OsuSetting.KeyOverlay, false); + Set(OsuSetting.FloatingComments, false); + Set(OsuSetting.PlaybackSpeed, 1.0, 0.5f, 2); + // Update Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); @@ -90,6 +93,8 @@ namespace osu.Game.Configuration AutoCursorSize, DimLevel, KeyOverlay, + FloatingComments, + PlaybackSpeed, ShowInterface, MouseDisableButtons, MouseDisableWheel, diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index 46addf5ac2..815d820a47 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; @@ -24,6 +25,7 @@ namespace osu.Game.Graphics.Cursor { private readonly Box background; private readonly OsuSpriteText text; + private bool instantMovement = true; public override string TooltipText { @@ -32,7 +34,7 @@ namespace osu.Game.Graphics.Cursor if (value == text.Text) return; text.Text = value; - if (Alpha > 0) + if (IsPresent) { AutoSizeDuration = 250; background.FlashColour(OsuColour.Gray(0.4f), 1000, EasingTypes.OutQuint); @@ -80,6 +82,7 @@ namespace osu.Game.Graphics.Cursor protected override void PopIn() { + instantMovement |= !IsPresent; FadeIn(500, EasingTypes.OutQuint); } @@ -88,6 +91,19 @@ namespace osu.Game.Graphics.Cursor using (BeginDelayedSequence(150)) FadeOut(500, EasingTypes.OutQuint); } + + public override void Move(Vector2 pos) + { + if (instantMovement) + { + Position = pos; + instantMovement = false; + } + else + { + MoveTo(pos, 200, EasingTypes.OutQuint); + } + } } } } diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs new file mode 100644 index 0000000000..d2a87d2dd0 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + public class IconButton : ClickableContainer + { + private readonly TextAwesome icon; + private readonly Box hover; + private readonly Container content; + + public FontAwesome Icon + { + get { return icon.Icon; } + set { icon.Icon = value; } + } + + private const float button_size = 30; + private Color4 flashColour; + + public Vector2 IconScale + { + get { return icon.Scale; } + set { icon.Scale = value; } + } + + public IconButton() + { + AutoSizeAxes = Axes.Both; + + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + + Children = new Drawable[] + { + content = new Container + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2 (button_size), + + CornerRadius = 5, + Masking = true, + EdgeEffect = new EdgeEffect + { + Colour = Color4.Black.Opacity(0.04f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Children = new Drawable[] + { + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + icon = new TextAwesome + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + TextSize = 18, + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hover.Colour = colours.Yellow.Opacity(0.6f); + flashColour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + hover.FadeIn(500, EasingTypes.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hover.FadeOut(500, EasingTypes.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + hover.FlashColour(flashColour, 800, EasingTypes.OutQuint); + return base.OnClick(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + content.ScaleTo(0.75f, 2000, EasingTypes.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + content.ScaleTo(1, 1000, EasingTypes.OutElastic); + return base.OnMouseUp(state, args); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 82ede8f079..a5cbc9f53f 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -12,13 +12,12 @@ using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { - public class Nub : CircularContainer, IHasCurrentValue + public class Nub : CircularContainer, IHasCurrentValue, IHasAccentColour { public const float COLLAPSED_SIZE = 20; public const float EXPANDED_SIZE = 40; private const float border_width = 3; - private Color4 glowingColour, idleColour; public Nub() { @@ -53,33 +52,41 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - Colour = idleColour = colours.Pink; - glowingColour = colours.PinkLighter; + AccentColour = colours.Pink; + GlowingAccentColour = colours.PinkLighter; + GlowColour = colours.PinkDarker; EdgeEffect = new EdgeEffect { - Colour = colours.PinkDarker, + Colour = GlowColour, Type = EdgeEffectType.Glow, Radius = 10, Roundness = 8, }; + } + protected override void LoadComplete() + { FadeEdgeEffectTo(0); } + private bool glowing; public bool Glowing { + get { return glowing; } set { + glowing = value; + if (value) { - FadeColour(glowingColour, 500, EasingTypes.OutQuint); + FadeColour(GlowingAccentColour, 500, EasingTypes.OutQuint); FadeEdgeEffectTo(1, 500, EasingTypes.OutQuint); } else { FadeEdgeEffectTo(0, 500); - FadeColour(idleColour, 500); + FadeColour(AccentColour, 500); } } } @@ -93,5 +100,43 @@ namespace osu.Game.Graphics.UserInterface } public Bindable Current { get; } = new Bindable(); + + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + if (!Glowing) + Colour = value; + } + } + + private Color4 glowingAccentColour; + public Color4 GlowingAccentColour + { + get { return glowingAccentColour; } + set + { + glowingAccentColour = value; + if (Glowing) + Colour = value; + } + } + + private Color4 glowColour; + public Color4 GlowColour + { + get { return glowColour; } + set + { + glowColour = value; + + var effect = EdgeEffect; + effect.Colour = value; + EdgeEffect = effect; + } + } } } diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 85231ffab9..198a01b5a4 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -51,7 +51,8 @@ namespace osu.Game.Graphics.UserInterface } } - private readonly Nub nub; + protected readonly Nub Nub; + private readonly SpriteText labelSpriteText; private SampleChannel sampleChecked; private SampleChannel sampleUnchecked; @@ -64,7 +65,7 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { labelSpriteText = new OsuSpriteText(), - nub = new Nub + Nub = new Nub { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, @@ -72,7 +73,7 @@ namespace osu.Game.Graphics.UserInterface } }; - nub.Current.BindTo(Current); + Nub.Current.BindTo(Current); Current.ValueChanged += newValue => { @@ -90,15 +91,15 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(InputState state) { - nub.Glowing = true; - nub.Expanded = true; + Nub.Glowing = true; + Nub.Expanded = true; return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - nub.Glowing = false; - nub.Expanded = false; + Nub.Glowing = false; + Nub.Expanded = false; base.OnHoverLost(state); } diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index c8be085b51..2dab952204 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -3,6 +3,7 @@ using System; using OpenTK; +using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -15,14 +16,14 @@ using osu.Framework.Graphics.Cursor; namespace osu.Game.Graphics.UserInterface { - public class OsuSliderBar : SliderBar, IHasTooltip + public class OsuSliderBar : SliderBar, IHasTooltip, IHasAccentColour where T : struct, IEquatable { private SampleChannel sample; private double lastSampleTime; private T lastSampleValue; - private readonly Nub nub; + protected readonly Nub Nub; private readonly Box leftBox; private readonly Box rightBox; @@ -46,6 +47,18 @@ namespace osu.Game.Graphics.UserInterface } } + private Color4 accentColour; + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + leftBox.Colour = value; + rightBox.Colour = value; + } + } + public OsuSliderBar() { Height = 12; @@ -71,7 +84,7 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.CentreRight, Alpha = 0.5f, }, - nub = new Nub + Nub = new Nub { Origin = Anchor.TopCentre, Expanded = true, @@ -88,19 +101,18 @@ namespace osu.Game.Graphics.UserInterface private void load(AudioManager audio, OsuColour colours) { sample = audio.Sample.Get(@"Sliderbar/sliderbar"); - leftBox.Colour = colours.Pink; - rightBox.Colour = colours.Pink; + AccentColour = colours.Pink; } protected override bool OnHover(InputState state) { - nub.Glowing = true; + Nub.Glowing = true; return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - nub.Glowing = false; + Nub.Glowing = false; base.OnHoverLost(state); } @@ -133,13 +145,13 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - nub.Current.Value = true; + Nub.Current.Value = true; return base.OnMouseDown(state, args); } protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - nub.Current.Value = false; + Nub.Current.Value = false; return base.OnMouseUp(state, args); } @@ -147,14 +159,14 @@ namespace osu.Game.Graphics.UserInterface { base.UpdateAfterChildren(); leftBox.Scale = new Vector2(MathHelper.Clamp( - nub.DrawPosition.X - nub.DrawWidth / 2, 0, DrawWidth), 1); + Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); rightBox.Scale = new Vector2(MathHelper.Clamp( - DrawWidth - nub.DrawPosition.X - nub.DrawWidth / 2, 0, DrawWidth), 1); + DrawWidth - Nub.DrawPosition.X - Nub.DrawWidth / 2, 0, DrawWidth), 1); } protected override void UpdateValue(float value) { - nub.MoveToX(RangePadding + UsableWidth * value, 250, EasingTypes.OutQuint); + Nub.MoveToX(RangePadding + UsableWidth * value, 250, EasingTypes.OutQuint); } } } diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 93fd0a8956..01685cc7dc 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; +using osu.Framework.Configuration; using osu.Framework.Lists; namespace osu.Game.Online.Chat @@ -25,7 +26,7 @@ namespace osu.Game.Online.Chat public readonly SortedList Messages = new SortedList(Comparer.Default); - //internal bool Joined; + public Bindable Joined = new Bindable(); public bool ReadOnly => Name != "#lazer"; diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs new file mode 100644 index 0000000000..9aa11cdd4f --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -0,0 +1,188 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelListItem : ClickableContainer, IFilterable + { + private const float width_padding = 5; + private const float channel_width = 150; + private const float text_size = 15; + private const float transition_duration = 100; + + private readonly Channel channel; + + private readonly Bindable joinedBind = new Bindable(); + private readonly OsuSpriteText name; + private readonly OsuSpriteText topic; + private readonly TextAwesome joinedCheckmark; + + private Color4 joinedColour; + private Color4 topicColour; + private Color4 hoverColour; + + public string[] FilterTerms => new[] { channel.Name }; + public bool MatchingFilter + { + set + { + FadeTo(value ? 1f : 0f, 100); + } + } + + public Action OnRequestJoin; + public Action OnRequestLeave; + + public ChannelListItem(Channel channel) + { + this.channel = channel; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Action = () => { (channel.Joined ? OnRequestLeave : OnRequestJoin)?.Invoke(channel); }; + + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + Children = new[] + { + joinedCheckmark = new TextAwesome + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Icon = FontAwesome.fa_check_circle, + TextSize = text_size, + Shadow = false, + Margin = new MarginPadding { Right = 10f }, + Alpha = 0f, + }, + }, + }, + new Container + { + Width = channel_width, + AutoSizeAxes = Axes.Y, + Children = new[] + { + name = new OsuSpriteText + { + Text = channel.ToString(), + TextSize = text_size, + Font = @"Exo2.0-Bold", + Shadow = false, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Width = 0.7f, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Left = width_padding }, + Children = new[] + { + topic = new OsuSpriteText + { + Text = channel.Topic, + TextSize = text_size, + Font = @"Exo2.0-SemiBold", + Shadow = false, + Alpha = 0.8f, + }, + }, + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Left = width_padding }, + Spacing = new Vector2(3f, 0f), + Children = new Drawable[] + { + new TextAwesome + { + Icon = FontAwesome.fa_user, + TextSize = text_size - 2, + Shadow = false, + Margin = new MarginPadding { Top = 1 }, + }, + new OsuSpriteText + { + Text = @"0", + TextSize = text_size, + Font = @"Exo2.0-SemiBold", + Shadow = false, + }, + }, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + topicColour = colours.Gray9; + joinedColour = colours.Blue; + hoverColour = colours.Yellow; + + joinedBind.ValueChanged += updateColour; + joinedBind.BindTo(channel.Joined); + } + + protected override bool OnHover(InputState state) + { + if (!channel.Joined.Value) + name.FadeColour(hoverColour, 50, EasingTypes.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (!channel.Joined.Value) + name.FadeColour(Color4.White, transition_duration); + } + + private void updateColour(bool joined) + { + if (joined) + { + name.FadeColour(Color4.White, transition_duration); + joinedCheckmark.FadeTo(1f, transition_duration); + topic.FadeTo(0.8f, transition_duration); + topic.FadeColour(Color4.White, transition_duration); + FadeColour(joinedColour, transition_duration); + } + else + { + joinedCheckmark.FadeTo(0f, transition_duration); + topic.FadeTo(1f, transition_duration); + topic.FadeColour(topicColour, transition_duration); + FadeColour(Color4.White, transition_duration); + } + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs new file mode 100644 index 0000000000..f12ec53605 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelSection.cs @@ -0,0 +1,63 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelSection : Container, IHasFilterableChildren + { + private readonly OsuSpriteText header; + + public readonly FillFlowContainer ChannelFlow; + + public IEnumerable FilterableChildren => ChannelFlow.Children; + public string[] FilterTerms => new[] { Header }; + public bool MatchingFilter + { + set + { + FadeTo(value ? 1f : 0f, 100); + } + } + + public string Header + { + get { return header.Text; } + set { header.Text = value.ToUpper(); } + } + + public IEnumerable Channels + { + set { ChannelFlow.Children = value.Select(c => new ChannelListItem(c)); } + } + + public ChannelSection() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + header = new OsuSpriteText + { + TextSize = 15, + Font = @"Exo2.0-Bold", + }, + ChannelFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Margin = new MarginPadding { Top = 25 }, + Spacing = new Vector2(0f, 5f), + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs new file mode 100644 index 0000000000..cd736a5fe9 --- /dev/null +++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Chat; + +namespace osu.Game.Overlays.Chat +{ + public class ChannelSelectionOverlay : FocusedOverlayContainer + { + public static readonly float WIDTH_PADDING = 170; + + private const float transition_duration = 500; + + private readonly Box bg; + private readonly Triangles triangles; + private readonly Box headerBg; + private readonly SearchTextBox search; + private readonly SearchContainer sectionsFlow; + + public Action OnRequestJoin; + public Action OnRequestLeave; + + public IEnumerable Sections + { + set + { + sectionsFlow.Children = value; + + foreach (ChannelSection s in sectionsFlow.Children) + { + foreach (ChannelListItem c in s.ChannelFlow.Children) + { + c.OnRequestJoin = channel => { OnRequestJoin?.Invoke(channel); }; + c.OnRequestLeave = channel => { OnRequestLeave?.Invoke(channel); }; + } + } + } + } + + public ChannelSelectionOverlay() + { + RelativeSizeAxes = Axes.X; + + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Children = new Drawable[] + { + bg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + triangles = new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 5, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = 85, Right = WIDTH_PADDING }, + Children = new[] + { + new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Children = new[] + { + sectionsFlow = new SearchContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + LayoutDuration = 200, + LayoutEasing = EasingTypes.OutQuint, + Spacing = new Vector2(0f, 20f), + Padding = new MarginPadding { Vertical = 20, Left = WIDTH_PADDING }, + }, + }, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + headerBg = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Padding = new MarginPadding { Top = 10f, Bottom = 10f, Left = WIDTH_PADDING, Right = WIDTH_PADDING }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"Chat Channels", + TextSize = 20, + Shadow = false, + }, + search = new HeaderSearchTextBox + { + RelativeSizeAxes = Axes.X, + PlaceholderText = @"Search", + Exit = Hide, + }, + }, + }, + }, + }, + }; + + search.Current.ValueChanged += newValue => sectionsFlow.SearchTerm = newValue; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + bg.Colour = colours.Gray3; + triangles.ColourDark = colours.Gray3; + triangles.ColourLight = OsuColour.FromHex(@"353535"); + + headerBg.Colour = colours.Gray2.Opacity(0.75f); + } + + protected override void OnFocus(InputState state) + { + InputManager.ChangeFocus(search); + base.OnFocus(state); + } + + protected override void PopIn() + { + if (Alpha == 0) MoveToY(DrawHeight); + + FadeIn(transition_duration, EasingTypes.OutQuint); + MoveToY(0, transition_duration, EasingTypes.OutQuint); + + search.HoldFocus = true; + base.PopIn(); + } + + protected override void PopOut() + { + FadeOut(transition_duration, EasingTypes.InSine); + MoveToY(DrawHeight, transition_duration, EasingTypes.InSine); + + search.HoldFocus = false; + base.PopOut(); + } + + private class HeaderSearchTextBox : SearchTextBox + { + protected override Color4 BackgroundFocused => Color4.Black.Opacity(0.2f); + protected override Color4 BackgroundUnfocused => Color4.Black.Opacity(0.2f); + } + } +} diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index a281cff7db..23ddff9381 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -207,11 +207,15 @@ namespace osu.Game.Overlays.Chat { public override bool Active { - get { return base.Active; } + get { return false; } + // ReSharper disable once ValueParameterNotUsed set { - activeBindable.Value = value; - base.Active = value; + // we basically never want this tab to become active. + // this allows us to become a "toggle" tab. + // is a bit hacky, to say the least. + activeBindable.Value = !activeBindable.Value; + base.Active = false; } } @@ -220,6 +224,9 @@ namespace osu.Game.Overlays.Chat public ChannelSelectorTabItem(Channel value, Bindable active) : base(value) { activeBindable = active; + activeBindable.ValueChanged += v => selectorUpdateState(); + + Depth = float.MaxValue; Width = 45; @@ -235,6 +242,26 @@ namespace osu.Game.Overlays.Chat backgroundInactive = colour.Gray2; backgroundActive = colour.Gray3; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectorUpdateState(); + } + + protected override void OnHoverLost(InputState state) + { + selectorUpdateState(); + } + + private void selectorUpdateState() + { + if (activeBindable.Value) + fadeActive(); + else + fadeInactive(); + } } } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index f81c0ea922..c32199f881 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -29,6 +29,7 @@ namespace osu.Game.Overlays public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent { private const float textbox_height = 60; + private const float channel_selection_min_height = 0.3f; private ScheduledDelegate messageRequest; @@ -48,16 +49,21 @@ namespace osu.Game.Overlays private readonly ChatTabControl channelTabs; + private readonly Container chatContainer; private readonly Box chatBackground; private readonly Box tabBackground; private Bindable chatHeight; + private readonly Container channelSelectionContainer; + private readonly ChannelSelectionOverlay channelSelection; + + protected override bool InternalContains(Vector2 screenSpacePos) => chatContainer.Contains(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.Contains(screenSpacePos); + public ChatOverlay() { RelativeSizeAxes = Axes.Both; RelativePositionAxes = Axes.Both; - Size = new Vector2(1, DEFAULT_HEIGHT); Anchor = Anchor.BottomLeft; Origin = Anchor.BottomLeft; @@ -65,74 +71,120 @@ namespace osu.Game.Overlays Children = new Drawable[] { - new Container + channelSelectionContainer = new Container { - Name = @"chat area", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = TAB_AREA_HEIGHT }, - Children = new Drawable[] + Height = 1f - DEFAULT_HEIGHT, + Masking = true, + Children = new[] { - chatBackground = new Box + channelSelection = new ChannelSelectionOverlay { RelativeSizeAxes = Axes.Both, }, - currentChannelContainer = new Container + }, + }, + chatContainer = new Container + { + Name = @"chat container", + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.Both, + Height = DEFAULT_HEIGHT, + Children = new[] + { + new Container { + Name = @"chat area", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding + Padding = new MarginPadding { Top = TAB_AREA_HEIGHT }, + Children = new Drawable[] { - Bottom = textbox_height + padding - }, + chatBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + currentChannelContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Bottom = textbox_height + padding + }, + }, + new Container + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + Height = textbox_height, + Padding = new MarginPadding + { + Top = padding * 2, + Bottom = padding * 2, + Left = ChatLine.LEFT_PADDING + padding * 2, + Right = padding * 2, + }, + Children = new Drawable[] + { + inputTextBox = new FocusedTextBox + { + RelativeSizeAxes = Axes.Both, + Height = 1, + PlaceholderText = "type your message", + Exit = () => State = Visibility.Hidden, + OnCommit = postMessage, + ReleaseFocusOnCommit = false, + HoldFocus = true, + } + } + } + } }, new Container { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, + Name = @"tabs area", RelativeSizeAxes = Axes.X, - Height = textbox_height, - Padding = new MarginPadding - { - Top = padding * 2, - Bottom = padding * 2, - Left = ChatLine.LEFT_PADDING + padding * 2, - Right = padding * 2, - }, + Height = TAB_AREA_HEIGHT, Children = new Drawable[] { - inputTextBox = new FocusedTextBox + tabBackground = new Box { RelativeSizeAxes = Axes.Both, - Height = 1, - PlaceholderText = "type your message", - Exit = () => State = Visibility.Hidden, - OnCommit = postMessage, - HoldFocus = true, - } + Colour = Color4.Black, + }, + channelTabs = new ChatTabControl + { + RelativeSizeAxes = Axes.Both, + }, } - } - } - }, - new Container - { - Name = @"tabs area", - RelativeSizeAxes = Axes.X, - Height = TAB_AREA_HEIGHT, - Children = new Drawable[] - { - tabBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, }, - channelTabs = new ChatTabControl - { - RelativeSizeAxes = Axes.Both, - }, - } + }, }, }; channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel; + channelTabs.ChannelSelectorActive.ValueChanged += value => channelSelection.State = value ? Visibility.Visible : Visibility.Hidden; + channelSelection.StateChanged += (overlay, state) => + { + channelTabs.ChannelSelectorActive.Value = state == Visibility.Visible; + + if (state == Visibility.Visible) + { + inputTextBox.HoldFocus = false; + if (1f - chatHeight.Value < channel_selection_min_height) + { + chatContainer.ResizeHeightTo(1f - channel_selection_min_height, 800, EasingTypes.OutQuint); + channelSelectionContainer.ResizeHeightTo(channel_selection_min_height, 800, EasingTypes.OutQuint); + channelSelection.Show(); + chatHeight.Value = 1f - channel_selection_min_height; + } + } + else + { + inputTextBox.HoldFocus = true; + } + }; } private double startDragChatHeight; @@ -205,8 +257,9 @@ namespace osu.Game.Overlays chatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight); chatHeight.ValueChanged += h => { - Height = (float)h; - tabBackground.FadeTo(Height == 1 ? 1 : 0.8f, 200); + chatContainer.Height = (float)h; + channelSelectionContainer.Height = 1f - (float)h; + tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200); }; chatHeight.TriggerChange(); @@ -243,6 +296,16 @@ namespace osu.Game.Overlays addChannel(channels.Find(c => c.Name == @"#lazer")); addChannel(channels.Find(c => c.Name == @"#osu")); addChannel(channels.Find(c => c.Name == @"#lobby")); + + channelSelection.OnRequestJoin = addChannel; + channelSelection.Sections = new[] + { + new ChannelSection + { + Header = "All Channels", + Channels = channels, + }, + }; }); messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); @@ -262,9 +325,7 @@ namespace osu.Game.Overlays set { - if (currentChannel == value) return; - - if (channelTabs.ChannelSelectorActive) return; + if (currentChannel == value || value == null) return; currentChannel = value; @@ -316,6 +377,8 @@ namespace osu.Game.Overlays if (CurrentChannel == null) CurrentChannel = channel; + + channel.Joined.Value = true; } private void fetchInitialMessages(Channel channel) diff --git a/osu.Game/Overlays/Music/CollectionsDropdown.cs b/osu.Game/Overlays/Music/CollectionsDropdown.cs new file mode 100644 index 0000000000..fd2ef23b9c --- /dev/null +++ b/osu.Game/Overlays/Music/CollectionsDropdown.cs @@ -0,0 +1,73 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Music +{ + public class CollectionsDropdown : OsuDropdown + { + protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour }; + protected override Menu CreateMenu() => new CollectionsMenu(); + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Gray6; + } + + private class CollectionsHeader : OsuDropdownHeader + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray4; + } + + public CollectionsHeader() + { + CornerRadius = 5; + Height = 30; + Icon.TextSize = 14; + Icon.Margin = new MarginPadding(0); + Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.3f), + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + } + } + + private class CollectionsMenu : OsuMenu + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Background.Colour = colours.Gray4; + } + + public CollectionsMenu() + { + CornerRadius = 5; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.3f), + Radius = 3, + Offset = new Vector2(0f, 1f), + }; + } + } + } +} diff --git a/osu.Game/Overlays/Music/FilterControl.cs b/osu.Game/Overlays/Music/FilterControl.cs index 6d8790a9e8..56cd6e864b 100644 --- a/osu.Game/Overlays/Music/FilterControl.cs +++ b/osu.Game/Overlays/Music/FilterControl.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; @@ -74,63 +72,5 @@ namespace osu.Game.Overlays.Music backgroundColour = colours.Gray2; } } - - private class CollectionsDropdown : OsuDropdown - { - protected override DropdownHeader CreateHeader() => new CollectionsHeader { AccentColour = AccentColour }; - protected override Menu CreateMenu() => new CollectionsMenu(); - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - AccentColour = colours.Gray6; - } - - private class CollectionsHeader : OsuDropdownHeader - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - BackgroundColour = colours.Gray4; - } - - public CollectionsHeader() - { - CornerRadius = 5; - Height = 30; - Icon.TextSize = 14; - Icon.Margin = new MarginPadding(0); - Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 10, Right = 10 }; - EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.3f), - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - } - } - - private class CollectionsMenu : OsuMenu - { - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Background.Colour = colours.Gray4; - } - - public CollectionsMenu() - { - CornerRadius = 5; - EdgeEffect = new EdgeEffect - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.3f), - Radius = 3, - Offset = new Vector2(0f, 1f), - }; - } - } - } } } diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 0618f96cac..789e45adfc 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Music private Color4 artistColour; private TextAwesome handle; - private Paragraph text; + private TextFlowContainer text; private IEnumerable titleSprites; private UnicodeBindableString titleBind; private UnicodeBindableString artistBind; @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Music Margin = new MarginPadding { Left = 5 }, Padding = new MarginPadding { Top = 2 }, }, - text = new Paragraph + text = new TextFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index f1be55801f..bfe10e845e 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -22,6 +22,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Threading; using osu.Game.Overlays.Music; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays { @@ -38,8 +39,8 @@ namespace osu.Game.Overlays private Drawable currentBackground; private DragBar progressBar; - private Button playButton; - private Button playlistButton; + private IconButton playButton; + private IconButton playlistButton; private SpriteText title, artist; @@ -143,7 +144,7 @@ namespace osu.Game.Overlays Anchor = Anchor.BottomCentre, Children = new Drawable[] { - new FillFlowContainer