Merge pull request #2165 from jorolf/volume-control

Implement updated volume control overlay design
This commit is contained in:
Dean Herbert 2018-03-07 15:03:04 +09:00 committed by GitHub
commit 43f7139dc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 375 additions and 184 deletions

View File

@ -0,0 +1,30 @@
// Copyright (c) 2007-2018 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 osu.Framework.Graphics;
using osu.Game.Overlays.Volume;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
{
public class TestCaseVolumePieces : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(VolumeMeter), typeof(MuteButton) };
protected override void LoadComplete()
{
VolumeMeter meter;
MuteButton mute;
Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue));
Add(mute = new MuteButton
{
Margin = new MarginPadding { Top = 200 }
});
AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
AddToggleStep("mute", b => mute.Current.Value = b);
}
}
}

View File

@ -174,6 +174,7 @@
<Compile Include="Visual\TestCaseUserPanel.cs" />
<Compile Include="Visual\TestCaseUserProfile.cs" />
<Compile Include="Visual\TestCaseUserRanks.cs" />
<Compile Include="Visual\TestCaseVolumePieces.cs" />
<Compile Include="Visual\TestCaseWaveform.cs" />
</ItemGroup>
<ItemGroup>

View File

@ -1,108 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
{
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private readonly Box meterFill;
public BindableDouble Bindable { get; } = new BindableDouble();
public VolumeMeter(string meterName)
{
Size = new Vector2(40, 180);
Children = new Drawable[]
{
new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both
},
new Container
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f, 0.9f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
Colour = Color4.DarkGray,
RelativeSizeAxes = Axes.Both
},
meterFill = new Box
{
Colour = Color4.White,
Scale = new Vector2(1, 0),
RelativeSizeAxes = Axes.Both,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre
}
}
},
new OsuSpriteText
{
Text = meterName,
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
}
};
Bindable.ValueChanged += delegate { updateFill(); };
}
protected override void LoadComplete()
{
base.LoadComplete();
updateFill();
}
public double Volume
{
get => Bindable.Value;
private set => Bindable.Value = value;
}
public void Increase()
{
Volume += 0.05f;
}
public void Decrease()
{
Volume -= 0.05f;
}
private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint);
public bool OnPressed(GlobalAction action)
{
if (!IsHovered) return false;
switch (action)
{
case GlobalAction.DecreaseVolume:
Decrease();
return true;
case GlobalAction.IncreaseVolume:
Increase();
return true;
}
return false;
}
public bool OnReleased(GlobalAction action) => false;
}
}

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays;
using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume;
using osu.Framework.Allocation;
using osu.Game.Overlays.Toolbar;
using osu.Game.Screens;
@ -33,6 +32,7 @@ using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Mods;
using osu.Game.Skinning;
using OpenTK.Graphics;
using osu.Game.Overlays.Volume;
namespace osu.Game
{
@ -75,7 +75,7 @@ namespace osu.Game
private OsuScreen screenStack;
private VolumeControl volume;
private VolumeOverlay volume;
private OnScreenDisplay onscreenDisplay;
private Bindable<int> configRuleset;
@ -232,7 +232,7 @@ namespace osu.Game
},
}, overlayContent.Add);
loadComponentSingleFile(volume = new VolumeControl(), Add);
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
//overlay elements

View File

@ -0,0 +1,83 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Volume
{
public class MuteButton : Container, IHasCurrentValue<bool>
{
public Bindable<bool> Current { get; } = new Bindable<bool>();
private Color4 hoveredColour, unhoveredColour;
private const float width = 100;
public const float HEIGHT = 35;
public MuteButton()
{
Masking = true;
BorderThickness = 3;
CornerRadius = HEIGHT / 2;
Size = new Vector2(width, HEIGHT);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoveredColour = colours.YellowDark;
BorderColour = unhoveredColour = colours.Gray1.Opacity(0.9f);
SpriteIcon icon;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.9f,
},
icon = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(20),
}
});
Current.ValueChanged += newValue =>
{
icon.Icon = newValue ? FontAwesome.fa_volume_off : FontAwesome.fa_volume_up;
icon.Margin = new MarginPadding { Left = newValue ? width / 2 - 15 : width / 2 - 10 }; //Magic numbers to line up both icons because they're different widths
};
Current.TriggerChange();
}
protected override bool OnHover(InputState state)
{
this.TransformTo<MuteButton, SRGBColour>("BorderColour", hoveredColour, 500, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(InputState state)
{
this.TransformTo<MuteButton, SRGBColour>("BorderColour", unhoveredColour, 500, Easing.OutQuint);
}
protected override bool OnClick(InputState state)
{
Current.Value = !Current.Value;
return true;
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
namespace osu.Game.Overlays.Volume
{
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
{

View File

@ -0,0 +1,186 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input.Bindings;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Volume
{
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
{
private CircularProgress volumeCircle;
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 };
private readonly float circleSize;
private readonly Color4 meterColour;
private readonly string name;
private OsuSpriteText text;
private BufferedContainer maxGlow;
public VolumeMeter(string name, float circleSize, Color4 meterColour)
{
this.circleSize = circleSize;
this.meterColour = meterColour;
this.name = name;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Add(new Container
{
Size = new Vector2(120, 20),
CornerRadius = 10,
Masking = true,
Margin = new MarginPadding { Left = circleSize + 10 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.9f,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = "Exo2.0-Bold",
Text = name
}
}
});
CircularProgress bgProgress;
Add(new CircularContainer
{
Masking = true,
Size = new Vector2(circleSize),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.9f,
},
bgProgress = new CircularProgress
{
RelativeSizeAxes = Axes.Both,
InnerRadius = 0.05f,
Rotation = 180,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colours.Gray2,
Size = new Vector2(0.8f)
},
(volumeCircle = new CircularProgress
{
RelativeSizeAxes = Axes.Both,
InnerRadius = 0.05f,
Rotation = 180,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(0.8f)
}).WithEffect(new GlowEffect
{
Colour = meterColour,
Strength = 2
}),
maxGlow = (text = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = "Venera",
TextSize = 0.16f * circleSize
}).WithEffect(new GlowEffect
{
Colour = Color4.Transparent,
PadExtent = true,
})
}
});
Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); };
bgProgress.Current.Value = 0.75f;
}
protected override void LoadComplete()
{
base.LoadComplete();
Bindable.TriggerChange();
}
private double displayVolume;
protected double DisplayVolume
{
get => displayVolume;
set
{
displayVolume = value;
if (displayVolume > 0.99f)
{
text.Text = "MAX";
maxGlow.EffectColour = meterColour.Opacity(2f);
}
else
{
maxGlow.EffectColour = Color4.Transparent;
text.Text = Math.Round(displayVolume * 100).ToString(CultureInfo.CurrentCulture);
}
volumeCircle.Current.Value = displayVolume * 0.75f;
}
}
public double Volume
{
get => Bindable;
private set => Bindable.Value = value;
}
public void Increase() => Volume += 0.05f;
public void Decrease() => Volume -= 0.05f;
public bool OnPressed(GlobalAction action)
{
if (!IsHovered) return false;
switch (action)
{
case GlobalAction.DecreaseVolume:
Decrease();
return true;
case GlobalAction.IncreaseVolume:
Increase();
return true;
}
return false;
}
public bool OnReleased(GlobalAction action) => false;
}
}

View File

@ -1,57 +1,84 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using OpenTK;
using osu.Framework.Audio;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.Volume;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Graphics.UserInterface.Volume
namespace osu.Game.Overlays
{
public class VolumeControl : OverlayContainer
public class VolumeOverlay : OverlayContainer
{
private readonly VolumeMeter volumeMeterMaster;
private readonly IconButton muteIcon;
private const float offset = 10;
private VolumeMeter volumeMeterMaster;
private VolumeMeter volumeMeterEffect;
private VolumeMeter volumeMeterMusic;
private MuteButton muteButton;
protected override bool BlockPassThroughMouse => false;
public VolumeControl()
{
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomRight;
Origin = Anchor.BottomRight;
private readonly BindableDouble muteAdjustment = new BindableDouble();
Children = new Drawable[]
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
AddRange(new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Y,
Width = 300,
Colour = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.75f), Color4.Black.Opacity(0))
},
new FillFlowContainer
{
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding { Left = 10, Right = 10, Top = 30, Bottom = 30 },
Spacing = new Vector2(15, 0),
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Spacing = new Vector2(0, offset),
Margin = new MarginPadding { Left = offset },
Children = new Drawable[]
{
new Container
volumeMeterEffect = new VolumeMeter("EFFECTS", 125, colours.BlueDarker)
{
Size = new Vector2(IconButton.BUTTON_SIZE),
Child = muteIcon = new IconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_volume_up,
Action = () => Adjust(GlobalAction.ToggleMute),
}
Margin = new MarginPadding { Top = 100 + MuteButton.HEIGHT } //to counter the mute button and re-center the volume meters
},
volumeMeterMaster = new VolumeMeter("Master"),
volumeMeterEffect = new VolumeMeter("Effects"),
volumeMeterMusic = new VolumeMeter("Music")
volumeMeterMaster = new VolumeMeter("MASTER", 150, colours.PinkDarker),
volumeMeterMusic = new VolumeMeter("MUSIC", 125, colours.BlueDarker),
muteButton = new MuteButton
{
Margin = new MarginPadding { Top = 100 }
}
}
}
},
});
volumeMeterMaster.Bindable.BindTo(audio.Volume);
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
muteButton.Current.ValueChanged += mute =>
{
if (mute)
audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
else
audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
};
}
@ -62,7 +89,13 @@ namespace osu.Game.Graphics.UserInterface.Volume
volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged();
volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged();
volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged();
muted.ValueChanged += _ => settingChanged();
muteButton.Current.ValueChanged += _ => settingChanged();
}
private void settingChanged()
{
Show();
schedulePopOut();
}
public bool Adjust(GlobalAction action)
@ -83,50 +116,15 @@ namespace osu.Game.Graphics.UserInterface.Volume
return true;
case GlobalAction.ToggleMute:
Show();
muted.Toggle();
muteButton.Current.Value = !muteButton.Current;
return true;
}
return false;
}
private void settingChanged()
{
Show();
schedulePopOut();
}
private readonly BindableDouble muteAdjustment = new BindableDouble();
private readonly BindableBool muted = new BindableBool();
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
volumeMeterMaster.Bindable.BindTo(audio.Volume);
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
muted.ValueChanged += mute =>
{
if (mute)
{
audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
muteIcon.Icon = FontAwesome.fa_volume_off;
}
else
{
audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
muteIcon.Icon = FontAwesome.fa_volume_up;
}
};
}
private ScheduledDelegate popOutDelegate;
private readonly VolumeMeter volumeMeterEffect;
private readonly VolumeMeter volumeMeterMusic;
protected override void PopIn()
{
ClearTransforms();

View File

@ -351,6 +351,10 @@
<Compile Include="Overlays\Profile\Sections\Ranks\ScoreModsContainer.cs" />
<Compile Include="Overlays\Settings\Sections\Gameplay\ScrollingSettings.cs" />
<Compile Include="Overlays\Settings\Sections\Maintenance\DeleteAllBeatmapsDialog.cs" />
<Compile Include="Overlays\VolumeOverlay.cs" />
<Compile Include="Overlays\Volume\MuteButton.cs" />
<Compile Include="Overlays\Volume\VolumeControlReceptor.cs" />
<Compile Include="Overlays\Volume\VolumeMeter.cs" />
<Compile Include="Rulesets\Configuration\IRulesetConfigManager.cs" />
<Compile Include="Rulesets\Configuration\RulesetConfigManager.cs" />
<Compile Include="Rulesets\Edit\Layers\BorderLayer.cs" />
@ -474,9 +478,6 @@
<Compile Include="Graphics\UserInterface\SimpleComboCounter.cs" />
<Compile Include="Graphics\UserInterface\StarCounter.cs" />
<Compile Include="Graphics\UserInterface\TwoLayerButton.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeControl.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeControlReceptor.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeMeter.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBinding.cs" />
<Compile Include="Input\Bindings\DatabasedKeyBindingContainer.cs" />
<Compile Include="Input\Bindings\GlobalActionContainer.cs" />