1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 03:27:24 +08:00

Merge branch 'master' into back-button-part-2

This commit is contained in:
Dean Herbert 2019-08-13 21:38:31 +09:00 committed by GitHub
commit 5886420a5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1533 additions and 577 deletions

View File

@ -19,8 +19,9 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When running on linux, please have a system-wide ffmpeg installation available to support video decoding.
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
## Running osu!

View File

@ -49,9 +49,7 @@
<None Include="$(MSBuildThisFileDirectory)\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<AndroidNativeLibrary Include="$(MSBuildThisFileDirectory)\osu.Android\lib\**\*.so">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</AndroidNativeLibrary>
<AndroidNativeLibrary Include="$(OutputPath)\**\*.so" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
@ -63,6 +61,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.813.0" />
</ItemGroup>
</Project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -113,13 +113,14 @@ namespace osu.Desktop.Overlays
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, ChangelogOverlay changelog)
private void load(OsuColour colours, ChangelogOverlay changelog, NotificationOverlay notificationOverlay)
{
Icon = FontAwesome.Solid.CheckSquare;
IconBackgound.Colour = colours.BlueDark;
Activated = delegate
{
notificationOverlay.Hide();
changelog.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
return true;
};

View File

@ -13,14 +13,6 @@
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<None Include="..\osu.iOS\libbass.a">
<Link>libbass.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\osu.iOS\libbass_fx.a">
<Link>libbass_fx.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>

View File

@ -13,14 +13,6 @@
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<None Include="..\osu.iOS\libbass.a">
<Link>libbass.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\osu.iOS\libbass_fx.a">
<Link>libbass_fx.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>

View File

@ -13,14 +13,6 @@
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<None Include="..\osu.iOS\libbass.a">
<Link>libbass.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\osu.iOS\libbass_fx.a">
<Link>libbass_fx.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>

View File

@ -13,14 +13,6 @@
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<None Include="..\osu.iOS\libbass.a">
<Link>libbass.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\osu.iOS\libbass_fx.a">
<Link>libbass_fx.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>

View File

@ -13,14 +13,6 @@
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<None Include="..\osu.iOS\libbass.a">
<Link>libbass.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="..\osu.iOS\libbass_fx.a">
<Link>libbass_fx.a</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>

View File

@ -21,32 +21,38 @@ namespace osu.Game.Tests.Visual.Gameplay
private readonly Container<DrawableStoryboard> storyboardContainer;
private DrawableStoryboard storyboard;
[Cached]
private MusicController musicController = new MusicController();
public TestSceneStoryboard()
{
Clock = new FramedClock();
Add(new Container
AddRange(new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
musicController,
new Container
{
new Box
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
storyboardContainer = new Container<DrawableStoryboard>
{
RelativeSizeAxes = Axes.Both,
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
storyboardContainer = new Container<DrawableStoryboard>
{
RelativeSizeAxes = Axes.Both,
},
},
},
});
Add(new MusicController
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
State = { Value = Visibility.Visible },
new NowPlayingOverlay
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
State = { Value = Visibility.Visible },
}
});
AddStep("Restart", restart);

View File

@ -0,0 +1,15 @@
// 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 NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
public class TestSceneIntroTriangles : IntroTestScene
{
protected override IScreen CreateScreen() => new IntroTriangles();
}
}

View File

@ -0,0 +1,53 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Match.Components;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Audio;
using osu.Framework.Allocation;
namespace osu.Game.Tests.Visual.Multiplayer
{
[Cached(typeof(IPreviewTrackOwner))]
public class TestSceneMatchBeatmapPanel : MultiplayerTestScene, IPreviewTrackOwner
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(MatchBeatmapPanel)
};
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
public TestSceneMatchBeatmapPanel()
{
Add(new MatchBeatmapPanel
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1763072 } });
Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2101557 } });
Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1973466 } });
Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 2109801 } });
Room.Playlist.Add(new PlaylistItem { Beatmap = new BeatmapInfo { OnlineBeatmapID = 1922035 } });
}
protected override void LoadComplete()
{
base.LoadComplete();
AddStep("Select random beatmap", () =>
{
Room.CurrentItem.Value = Room.Playlist[RNG.Next(Room.Playlist.Count)];
previewTrackManager.StopAnyPlaying(this);
});
}
}
}

View File

@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
typeof(Info),
typeof(HeaderButton),
typeof(ReadyButton),
typeof(ViewBeatmapButton)
typeof(MatchBeatmapPanel)
};
[BackgroundDependencyLoader]

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 System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public class TestSceneUserProfilePreviousUsernames : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(PreviousUsernames)
};
[Resolved]
private IAPIProvider api { get; set; }
private readonly Bindable<User> user = new Bindable<User>();
public TestSceneUserProfilePreviousUsernames()
{
Child = new PreviousUsernames
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
User = { BindTarget = user },
};
User[] users =
{
new User { PreviousUsernames = new[] { "username1" } },
new User { PreviousUsernames = new[] { "longusername", "longerusername" } },
new User { PreviousUsernames = new[] { "test", "angelsim", "verylongusername" } },
new User { PreviousUsernames = new[] { "ihavenoidea", "howcani", "makethistext", "anylonger" } },
new User { PreviousUsernames = new string[0] },
null
};
AddStep("single username", () => user.Value = users[0]);
AddStep("two usernames", () => user.Value = users[1]);
AddStep("three usernames", () => user.Value = users[2]);
AddStep("four usernames", () => user.Value = users[3]);
AddStep("no username", () => user.Value = users[4]);
AddStep("null user", () => user.Value = users[5]);
}
protected override void LoadComplete()
{
base.LoadComplete();
AddStep("online user (Angelsim)", () =>
{
var request = new GetUserRequest(1777162);
request.Success += user => this.user.Value = user;
api.Queue(request);
});
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@ -22,30 +23,36 @@ namespace osu.Game.Tests.Visual.UserInterface
[TestFixture]
public class TestSceneBeatSyncedContainer : OsuTestScene
{
private readonly MusicController mc;
private readonly NowPlayingOverlay np;
[Cached]
private MusicController musicController = new MusicController();
public TestSceneBeatSyncedContainer()
{
Clock = new FramedClock();
Clock.ProcessFrame();
Add(new BeatContainer
AddRange(new Drawable[]
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
});
Add(mc = new MusicController
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
musicController,
new BeatContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
np = new NowPlayingOverlay
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
}
});
}
protected override void LoadComplete()
{
base.LoadComplete();
mc.ToggleVisibility();
np.ToggleVisibility();
}
private class BeatContainer : BeatSyncedContainer

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Overlays;
@ -9,22 +10,27 @@ using osu.Game.Overlays;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneMusicController : OsuTestScene
public class TestSceneNowPlayingOverlay : OsuTestScene
{
public TestSceneMusicController()
[Cached]
private MusicController musicController = new MusicController();
public TestSceneNowPlayingOverlay()
{
Clock = new FramedClock();
var mc = new MusicController
var np = new NowPlayingOverlay
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre
};
Add(mc);
AddStep(@"show", () => mc.Show());
Add(musicController);
Add(np);
AddStep(@"show", () => np.Show());
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
AddStep(@"show", () => mc.Hide());
AddStep(@"show", () => np.Hide());
}
}
}

View File

@ -0,0 +1,11 @@
// 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.
namespace osu.Game.Configuration
{
public enum IntroSequence
{
Circles,
Triangles
}
}

View File

@ -105,6 +105,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
Set(OsuSetting.IntroSequence, IntroSequence.Triangles);
}
public OsuConfigManager(Storage storage)
@ -167,6 +169,7 @@ namespace osu.Game.Configuration
ScalingPositionY,
ScalingSizeX,
ScalingSizeY,
UIScale
UIScale,
IntroSequence
}
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Input.Bindings
handler = game;
}
public override IEnumerable<KeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings);
public override IEnumerable<KeyBinding> DefaultKeyBindings => GlobalKeyBindings.Concat(InGameKeyBindings).Concat(AudioControlKeyBindings);
public IEnumerable<KeyBinding> GlobalKeyBindings => new[]
{
@ -32,11 +32,6 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
new KeyBinding(InputKey.Escape, GlobalAction.Back),
new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back),
@ -55,6 +50,22 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
};
public IEnumerable<KeyBinding> AudioControlKeyBindings => new[]
{
new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume),
new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume),
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
new KeyBinding(InputKey.TrackPrevious, GlobalAction.MusicPrev),
new KeyBinding(InputKey.F1, GlobalAction.MusicPrev),
new KeyBinding(InputKey.TrackNext, GlobalAction.MusicNext),
new KeyBinding(InputKey.F5, GlobalAction.MusicNext),
new KeyBinding(InputKey.PlayPause, GlobalAction.MusicPlay),
new KeyBinding(InputKey.F3, GlobalAction.MusicPlay)
};
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
handler == null ? base.KeyBindingInputQueue : base.KeyBindingInputQueue.Prepend(handler);
}
@ -115,5 +126,15 @@ namespace osu.Game.Input.Bindings
[Description("Quick exit (Hold)")]
QuickExit,
// Game-wide beatmap msi ccotolle keybindings
[Description("Next track")]
MusicNext,
[Description("Previous track")]
MusicPrev,
[Description("Play / pause")]
MusicPlay,
}
}

View File

@ -303,7 +303,7 @@ namespace osu.Game
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
}
#region Beatmap jukebox progression
#region Beatmap progression
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap)
{
@ -472,6 +472,8 @@ namespace osu.Game
loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add);
loadComponentSingleFile(new OnScreenDisplay(), Add, true);
loadComponentSingleFile(musicController = new MusicController(), Add, true);
loadComponentSingleFile(notifications = new NotificationOverlay
{
GetToolbarHeight = () => ToolbarOffset,
@ -498,7 +500,7 @@ namespace osu.Game
Origin = Anchor.TopRight,
}, rightFloatingOverlayContent.Add, true);
loadComponentSingleFile(musicController = new MusicController
loadComponentSingleFile(new NowPlayingOverlay
{
GetToolbarHeight = () => ToolbarOffset,
Anchor = Anchor.TopRight,

View File

@ -15,6 +15,7 @@ namespace osu.Game.Overlays.KeyBinding
public GlobalKeyBindingsSection(GlobalActionContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
Add(new AudioControlKeyBindingsSubsection(manager));
Add(new InGameKeyBindingsSubsection(manager));
}
@ -39,5 +40,16 @@ namespace osu.Game.Overlays.KeyBinding
Defaults = manager.InGameKeyBindings;
}
}
private class AudioControlKeyBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => "Audio";
public AudioControlKeyBindingsSubsection(GlobalActionContainer manager)
: base(null)
{
Defaults = manager.AudioControlKeyBindings;
}
}
}
}

View File

@ -1,292 +1,112 @@
// 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
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.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Input.Bindings;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Music;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.OSD;
using osu.Game.Rulesets.Mods;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class MusicController : OsuFocusedOverlayContainer
/// <summary>
/// Handles playback of the global music track.
/// </summary>
public class MusicController : Component, IKeyBindingHandler<GlobalAction>
{
private const float player_height = 130;
private const float transition_length = 800;
private const float progress_height = 10;
private const float bottom_black_area_height = 55;
private Drawable background;
private ProgressBar progressBar;
private IconButton prevButton;
private IconButton playButton;
private IconButton nextButton;
private IconButton playlistButton;
private SpriteText title, artist;
private PlaylistOverlay playlist;
private BeatmapManager beatmaps;
[Resolved]
private BeatmapManager beatmaps { get; set; }
private List<BeatmapSetInfo> beatmapSets;
private Container dragContainer;
private Container playerContainer;
public bool IsUserPaused { get; private set; }
/// <summary>
/// Fired when the global <see cref="WorkingBeatmap"/> has changed.
/// Includes direction information for display purposes.
/// </summary>
public event Action<WorkingBeatmap, TrackChangeDirection> TrackChanged;
[Resolved]
private Bindable<WorkingBeatmap> beatmap { get; set; }
private IBindable<WorkingBeatmap> beatmap { get; set; }
[Resolved]
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
/// <summary>
/// Provide a source for the toolbar height.
/// </summary>
public Func<float> GetToolbarHeight;
public MusicController()
{
Width = 400;
Margin = new MarginPadding(10);
}
[Resolved(canBeNull: true)]
private OnScreenDisplay onScreenDisplay { get; set; }
[BackgroundDependencyLoader]
private void load(Bindable<WorkingBeatmap> beatmap, BeatmapManager beatmaps, OsuColour colours)
private void load()
{
this.beatmaps = beatmaps;
Children = new Drawable[]
{
dragContainer = new DragContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
playlist = new PlaylistOverlay
{
RelativeSizeAxes = Axes.X,
Y = player_height + 10,
OrderChanged = playlistOrderChanged
},
playerContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = player_height,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new[]
{
background = new Background(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 40),
Font = OsuFont.GetFont(size: 25, italics: true),
Colour = Color4.White,
Text = @"Nothing to play",
},
artist = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 45),
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold, italics: true),
Colour = Color4.White,
Text = @"Nothing to play",
},
new Container
{
Padding = new MarginPadding { Bottom = progress_height },
Height = bottom_black_area_height,
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Children = new Drawable[]
{
new FillFlowContainer<IconButton>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new[]
{
prevButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = prev,
Icon = FontAwesome.Solid.StepBackward,
},
playButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = togglePause,
Icon = FontAwesome.Regular.PlayCircle,
},
nextButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = () => next(),
Icon = FontAwesome.Solid.StepForward,
},
}
},
playlistButton = new MusicIconButton
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-bottom_black_area_height / 2, 0),
Icon = FontAwesome.Solid.Bars,
Action = () => playlist.ToggleVisibility(),
},
}
},
progressBar = new ProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = progress_height,
FillColour = colours.Yellow,
OnSeek = attemptSeek
}
},
},
}
}
};
beatmapSets = beatmaps.GetAllUsableBeatmapSets();
beatmaps.ItemAdded += handleBeatmapAdded;
beatmaps.ItemRemoved += handleBeatmapRemoved;
playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
private ScheduledDelegate seekDelegate;
private void attemptSeek(double progress)
protected override void LoadComplete()
{
seekDelegate?.Cancel();
seekDelegate = Schedule(() =>
{
if (!beatmap.Disabled)
current?.Track.Seek(progress);
});
beatmap.BindValueChanged(beatmapChanged, true);
mods.BindValueChanged(_ => updateAudioAdjustments(), true);
base.LoadComplete();
}
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
/// <summary>
/// Change the position of a <see cref="BeatmapSetInfo"/> in the current playlist.
/// </summary>
/// <param name="beatmapSetInfo">The beatmap to move.</param>
/// <param name="index">The new position.</param>
public void ChangeBeatmapSetPosition(BeatmapSetInfo beatmapSetInfo, int index)
{
beatmapSets.Remove(beatmapSetInfo);
beatmapSets.Insert(index, beatmapSetInfo);
}
private void handleBeatmapAdded(BeatmapSetInfo set) => Schedule(() => beatmapSets.Add(set));
/// <summary>
/// Returns whether the current beatmap track is playing.
/// </summary>
public bool IsPlaying => beatmap.Value.Track.IsRunning;
private void handleBeatmapRemoved(BeatmapSetInfo set) => Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
private void handleBeatmapAdded(BeatmapSetInfo set) =>
Schedule(() => beatmapSets.Add(set));
protected override void LoadComplete()
private void handleBeatmapRemoved(BeatmapSetInfo set) =>
Schedule(() => beatmapSets.RemoveAll(s => s.ID == set.ID));
private ScheduledDelegate seekDelegate;
public void SeekTo(double position)
{
beatmap.BindValueChanged(beatmapChanged, true);
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
mods.BindValueChanged(_ => updateAudioAdjustments(), true);
base.LoadComplete();
}
private void beatmapDisabledChanged(bool disabled)
{
if (disabled)
playlist.Hide();
playButton.Enabled.Value = !disabled;
prevButton.Enabled.Value = !disabled;
nextButton.Enabled.Value = !disabled;
playlistButton.Enabled.Value = !disabled;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Height = dragContainer.Height;
dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
protected override void Update()
{
base.Update();
if (pendingBeatmapSwitch != null)
seekDelegate?.Cancel();
seekDelegate = Schedule(() =>
{
pendingBeatmapSwitch();
pendingBeatmapSwitch = null;
}
var track = current?.TrackLoaded ?? false ? current.Track : null;
if (track?.IsDummyDevice == false)
{
progressBar.EndTime = track.Length;
progressBar.CurrentTime = track.CurrentTime;
playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
}
else
{
progressBar.CurrentTime = 0;
progressBar.EndTime = 1;
playButton.Icon = FontAwesome.Regular.PlayCircle;
}
if (!beatmap.Disabled)
current?.Track.Seek(position);
});
}
private void togglePause()
/// <summary>
/// Toggle pause / play.
/// </summary>
/// <returns>Whether the operation was successful.</returns>
public bool TogglePause()
{
var track = current?.Track;
if (track == null)
{
if (!beatmap.Disabled)
next(true);
return;
if (beatmap.Disabled)
return false;
next(true);
return true;
}
if (track.IsRunning)
@ -299,48 +119,70 @@ namespace osu.Game.Overlays
track.Start();
IsUserPaused = false;
}
return true;
}
private void prev()
/// <summary>
/// Play the previous track.
/// </summary>
/// <returns>Whether the operation was successful.</returns>
public bool PrevTrack()
{
queuedDirection = TransformDirection.Prev;
queuedDirection = TrackChangeDirection.Prev;
var playable = beatmapSets.TakeWhile(i => i.ID != current.BeatmapSetInfo.ID).LastOrDefault() ?? beatmapSets.LastOrDefault();
if (playable != null)
{
beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
if (beatmap is Bindable<WorkingBeatmap> working)
working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
beatmap.Value.Track.Restart();
return true;
}
return false;
}
private void next(bool instant = false)
/// <summary>
/// Play the next random or playlist track.
/// </summary>
/// <returns>Whether the operation was successful.</returns>
public bool NextTrack() => next();
private bool next(bool instant = false)
{
if (!instant)
queuedDirection = TransformDirection.Next;
queuedDirection = TrackChangeDirection.Next;
var playable = beatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).Skip(1).FirstOrDefault() ?? beatmapSets.FirstOrDefault();
if (playable != null)
{
beatmap.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
if (beatmap is Bindable<WorkingBeatmap> working)
working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value);
beatmap.Value.Track.Restart();
return true;
}
return false;
}
private WorkingBeatmap current;
private TransformDirection? queuedDirection;
private TrackChangeDirection? queuedDirection;
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> beatmap)
{
TransformDirection direction = TransformDirection.None;
TrackChangeDirection direction = TrackChangeDirection.None;
if (current != null)
{
bool audioEquals = beatmap.NewValue?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false;
if (audioEquals)
direction = TransformDirection.None;
direction = TrackChangeDirection.None;
else if (queuedDirection.HasValue)
{
direction = queuedDirection.Value;
@ -352,13 +194,13 @@ namespace osu.Game.Overlays
var last = beatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count();
var next = beatmap.NewValue == null ? -1 : beatmapSets.TakeWhile(b => b.ID != beatmap.NewValue.BeatmapSetInfo?.ID).Count();
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next;
}
}
progressBar.CurrentTime = 0;
current = beatmap.NewValue;
TrackChanged?.Invoke(current, direction);
updateDisplay(current = beatmap.NewValue, direction);
updateAudioAdjustments();
queuedDirection = null;
@ -376,167 +218,57 @@ namespace osu.Game.Overlays
mod.ApplyToClock(track);
}
private Action pendingBeatmapSwitch;
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
protected override void Dispose(bool isDisposing)
{
// avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps.
pendingBeatmapSwitch = delegate
base.Dispose(isDisposing);
if (beatmaps != null)
{
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() =>
{
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
{
title.Text = @"Nothing to play";
artist.Text = @"Nothing to play";
}
else
{
BeatmapMetadata metadata = beatmap.Metadata;
title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
}
});
LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
{
switch (direction)
{
case TransformDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(-400, 500, Easing.OutCubic);
break;
case TransformDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(400, 500, Easing.OutCubic);
break;
}
background.Expire();
background = newBackground;
playerContainer.Add(newBackground);
});
};
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(1, transition_length, Easing.OutElastic);
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
}
private enum TransformDirection
{
None,
Next,
Prev
}
private class MusicIconButton : IconButton
{
public MusicIconButton()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
HoverColour = colours.YellowDark.Opacity(0.6f);
FlashColour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
// works with AutoSizeAxes above to make buttons autosize with the scale animation.
Content.AutoSizeAxes = Axes.None;
Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
beatmaps.ItemAdded -= handleBeatmapAdded;
beatmaps.ItemRemoved -= handleBeatmapRemoved;
}
}
private class Background : BufferedContainer
public bool OnPressed(GlobalAction action)
{
private readonly Sprite sprite;
private readonly WorkingBeatmap beatmap;
public Background(WorkingBeatmap beatmap = null)
switch (action)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
Depth = float.MaxValue;
RelativeSizeAxes = Axes.Both;
case GlobalAction.MusicPlay:
if (TogglePause())
onScreenDisplay?.Display(new MusicControllerToast(IsPlaying ? "Play track" : "Pause track"));
return true;
Children = new Drawable[]
{
sprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(150),
FillMode = FillMode.Fill,
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = bottom_black_area_height,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Colour = Color4.Black.Opacity(0.5f)
}
};
case GlobalAction.MusicNext:
if (NextTrack())
onScreenDisplay?.Display(new MusicControllerToast("Next track"));
return true;
case GlobalAction.MusicPrev:
if (PrevTrack())
onScreenDisplay?.Display(new MusicControllerToast("Previous track"));
return true;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
return false;
}
private class DragContainer : Container
public bool OnReleased(GlobalAction action) => false;
public class MusicControllerToast : Toast
{
protected override bool OnDragStart(DragStartEvent e)
public MusicControllerToast(string action)
: base("Music Playback", action, string.Empty)
{
return true;
}
protected override bool OnDrag(DragEvent e)
{
Vector2 change = e.MousePosition - e.MouseDownPosition;
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
this.MoveTo(change);
return true;
}
protected override bool OnDragEnd(DragEndEvent e)
{
this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
return base.OnDragEnd(e);
}
}
}
/// <summary>
/// Play the next random or playlist track.
/// </summary>
public void NextTrack() => next();
public enum TrackChangeDirection
{
None,
Next,
Prev
}
}

View File

@ -0,0 +1,404 @@
// 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.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
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.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Music;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays
{
public class NowPlayingOverlay : OsuFocusedOverlayContainer
{
private const float player_height = 130;
private const float transition_length = 800;
private const float progress_height = 10;
private const float bottom_black_area_height = 55;
private Drawable background;
private ProgressBar progressBar;
private IconButton prevButton;
private IconButton playButton;
private IconButton nextButton;
private IconButton playlistButton;
private SpriteText title, artist;
private PlaylistOverlay playlist;
private Container dragContainer;
private Container playerContainer;
/// <summary>
/// Provide a source for the toolbar height.
/// </summary>
public Func<float> GetToolbarHeight;
[Resolved]
private MusicController musicController { get; set; }
[Resolved]
private Bindable<WorkingBeatmap> beatmap { get; set; }
public NowPlayingOverlay()
{
Width = 400;
Margin = new MarginPadding(10);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
dragContainer = new DragContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
playlist = new PlaylistOverlay
{
RelativeSizeAxes = Axes.X,
Y = player_height + 10,
OrderChanged = musicController.ChangeBeatmapSetPosition
},
playerContainer = new Container
{
RelativeSizeAxes = Axes.X,
Height = player_height,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
},
Children = new[]
{
background = new Background(),
title = new OsuSpriteText
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 40),
Font = OsuFont.GetFont(size: 25, italics: true),
Colour = Color4.White,
Text = @"Nothing to play",
},
artist = new OsuSpriteText
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Position = new Vector2(0, 45),
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold, italics: true),
Colour = Color4.White,
Text = @"Nothing to play",
},
new Container
{
Padding = new MarginPadding { Bottom = progress_height },
Height = bottom_black_area_height,
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Children = new Drawable[]
{
new FillFlowContainer<IconButton>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new[]
{
prevButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = () => musicController.PrevTrack(),
Icon = FontAwesome.Solid.StepBackward,
},
playButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1.4f),
IconScale = new Vector2(1.4f),
Action = () => musicController.TogglePause(),
Icon = FontAwesome.Regular.PlayCircle,
},
nextButton = new MusicIconButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Action = () => musicController.NextTrack(),
Icon = FontAwesome.Solid.StepForward,
},
}
},
playlistButton = new MusicIconButton
{
Origin = Anchor.Centre,
Anchor = Anchor.CentreRight,
Position = new Vector2(-bottom_black_area_height / 2, 0),
Icon = FontAwesome.Solid.Bars,
Action = () => playlist.ToggleVisibility(),
},
}
},
progressBar = new ProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = progress_height,
FillColour = colours.Yellow,
OnSeek = musicController.SeekTo
}
},
},
}
}
};
playlist.State.ValueChanged += s => playlistButton.FadeColour(s.NewValue == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
protected override void LoadComplete()
{
base.LoadComplete();
beatmap.BindDisabledChanged(beatmapDisabledChanged, true);
musicController.TrackChanged += trackChanged;
trackChanged(beatmap.Value);
}
protected override void PopIn()
{
base.PopIn();
this.FadeIn(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(1, transition_length, Easing.OutElastic);
}
protected override void PopOut()
{
base.PopOut();
this.FadeOut(transition_length, Easing.OutQuint);
dragContainer.ScaleTo(0.9f, transition_length, Easing.OutQuint);
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
Height = dragContainer.Height;
dragContainer.Padding = new MarginPadding { Top = GetToolbarHeight?.Invoke() ?? 0 };
}
protected override void Update()
{
base.Update();
if (pendingBeatmapSwitch != null)
{
pendingBeatmapSwitch();
pendingBeatmapSwitch = null;
}
var track = beatmap.Value?.TrackLoaded ?? false ? beatmap.Value.Track : null;
if (track?.IsDummyDevice == false)
{
progressBar.EndTime = track.Length;
progressBar.CurrentTime = track.CurrentTime;
playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
}
else
{
progressBar.CurrentTime = 0;
progressBar.EndTime = 1;
playButton.Icon = FontAwesome.Regular.PlayCircle;
}
}
private Action pendingBeatmapSwitch;
private void trackChanged(WorkingBeatmap beatmap, TrackChangeDirection direction = TrackChangeDirection.None)
{
// avoid using scheduler as our scheduler may not be run for a long time, holding references to beatmaps.
pendingBeatmapSwitch = delegate
{
// todo: this can likely be replaced with WorkingBeatmap.GetBeatmapAsync()
Task.Run(() =>
{
if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists
{
title.Text = @"Nothing to play";
artist.Text = @"Nothing to play";
}
else
{
BeatmapMetadata metadata = beatmap.Metadata;
title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title));
artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist));
}
});
LoadComponentAsync(new Background(beatmap) { Depth = float.MaxValue }, newBackground =>
{
switch (direction)
{
case TrackChangeDirection.Next:
newBackground.Position = new Vector2(400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(-400, 500, Easing.OutCubic);
break;
case TrackChangeDirection.Prev:
newBackground.Position = new Vector2(-400, 0);
newBackground.MoveToX(0, 500, Easing.OutCubic);
background.MoveToX(400, 500, Easing.OutCubic);
break;
}
background.Expire();
background = newBackground;
playerContainer.Add(newBackground);
});
};
}
private void beatmapDisabledChanged(bool disabled)
{
if (disabled)
playlist.Hide();
playButton.Enabled.Value = !disabled;
prevButton.Enabled.Value = !disabled;
nextButton.Enabled.Value = !disabled;
playlistButton.Enabled.Value = !disabled;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (musicController != null)
musicController.TrackChanged -= trackChanged;
}
private class MusicIconButton : IconButton
{
public MusicIconButton()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
HoverColour = colours.YellowDark.Opacity(0.6f);
FlashColour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
// works with AutoSizeAxes above to make buttons autosize with the scale animation.
Content.AutoSizeAxes = Axes.None;
Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
}
}
private class Background : BufferedContainer
{
private readonly Sprite sprite;
private readonly WorkingBeatmap beatmap;
public Background(WorkingBeatmap beatmap = null)
{
this.beatmap = beatmap;
CacheDrawnFrameBuffer = true;
Depth = float.MaxValue;
RelativeSizeAxes = Axes.Both;
Children = new Drawable[]
{
sprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(150),
FillMode = FillMode.Fill,
},
new Box
{
RelativeSizeAxes = Axes.X,
Height = bottom_black_area_height,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Colour = Color4.Black.Opacity(0.5f)
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
}
private class DragContainer : Container
{
protected override bool OnDragStart(DragStartEvent e)
{
return true;
}
protected override bool OnDrag(DragEvent e)
{
Vector2 change = e.MousePosition - e.MouseDownPosition;
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
this.MoveTo(change);
return true;
}
protected override bool OnDragEnd(DragEndEvent e)
{
this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
return base.OnDragEnd(e);
}
}
}
}

View File

@ -0,0 +1,168 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
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.Users;
using osuTK;
namespace osu.Game.Overlays.Profile.Header.Components
{
public class PreviousUsernames : CompositeDrawable
{
private const int duration = 200;
private const int margin = 10;
private const int width = 310;
private const int move_offset = 15;
public readonly Bindable<User> User = new Bindable<User>();
private readonly TextFlowContainer text;
private readonly Box background;
private readonly SpriteText header;
public PreviousUsernames()
{
HoverIconContainer hoverIcon;
AutoSizeAxes = Axes.Y;
Width = width;
Masking = true;
CornerRadius = 5;
AddRangeInternal(new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
new GridContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize)
},
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Distributed)
},
Content = new[]
{
new Drawable[]
{
hoverIcon = new HoverIconContainer(),
header = new SpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Text = @"formerly known as",
Font = OsuFont.GetFont(size: 10, italics: true)
}
},
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
},
text = new TextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true))
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Margin = new MarginPadding { Bottom = margin, Top = margin / 2f }
}
}
}
}
});
hoverIcon.ActivateHover += showContent;
hideContent();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.GreySeafoamDarker;
}
protected override void LoadComplete()
{
base.LoadComplete();
User.BindValueChanged(onUserChanged, true);
}
private void onUserChanged(ValueChangedEvent<User> user)
{
text.Text = string.Empty;
var usernames = user.NewValue?.PreviousUsernames;
if (usernames?.Any() ?? false)
{
text.Text = string.Join(", ", usernames);
Show();
return;
}
Hide();
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
hideContent();
}
private void showContent()
{
text.FadeIn(duration, Easing.OutQuint);
header.FadeIn(duration, Easing.OutQuint);
background.FadeIn(duration, Easing.OutQuint);
this.MoveToY(-move_offset, duration, Easing.OutQuint);
}
private void hideContent()
{
text.FadeOut(duration, Easing.OutQuint);
header.FadeOut(duration, Easing.OutQuint);
background.FadeOut(duration, Easing.OutQuint);
this.MoveToY(0, duration, Easing.OutQuint);
}
private class HoverIconContainer : Container
{
public Action ActivateHover;
public HoverIconContainer()
{
AutoSizeAxes = Axes.Both;
Child = new SpriteIcon
{
Margin = new MarginPadding { Top = 6, Left = margin, Right = margin * 2 },
Size = new Vector2(15),
Icon = FontAwesome.Solid.IdCard,
};
}
protected override bool OnHover(HoverEvent e)
{
ActivateHover?.Invoke();
return base.OnHover(e);
}
}
}
}

View File

@ -1,7 +1,10 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Audio
@ -13,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
Children = new Drawable[]
{
new SettingsCheckbox
{
@ -25,6 +28,12 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
LabelText = "osu! music theme",
Bindable = config.GetBindable<bool>(OsuSetting.MenuMusic)
},
new SettingsDropdown<IntroSequence>
{
LabelText = "Intro sequence",
Bindable = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence),
Items = Enum.GetValues(typeof(IntroSequence)).Cast<IntroSequence>()
},
};
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Toolbar
}
[BackgroundDependencyLoader(true)]
private void load(MusicController music)
private void load(NowPlayingOverlay music)
{
StateContainer = music;
}

View File

@ -11,8 +11,11 @@ namespace osu.Game.Screens
{
public abstract class BackgroundScreen : Screen, IEquatable<BackgroundScreen>
{
protected BackgroundScreen()
private readonly bool animateOnEnter;
protected BackgroundScreen(bool animateOnEnter = true)
{
this.animateOnEnter = animateOnEnter;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
@ -39,11 +42,14 @@ namespace osu.Game.Screens
public override void OnEntering(IScreen last)
{
this.FadeOut();
this.MoveToX(x_movement_amount);
if (animateOnEnter)
{
this.FadeOut();
this.MoveToX(x_movement_amount);
this.FadeIn(transition_length, Easing.InOutQuart);
this.MoveToX(0, transition_length, Easing.InOutQuart);
this.FadeIn(transition_length, Easing.InOutQuart);
this.MoveToX(0, transition_length, Easing.InOutQuart);
}
base.OnEntering(last);
}

View File

@ -25,6 +25,11 @@ namespace osu.Game.Screens.Backgrounds
private Bindable<User> user;
private Bindable<Skin> skin;
public BackgroundScreenDefault(bool animateOnEnter = true)
: base(animateOnEnter)
{
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api, SkinManager skinManager)
{

View File

@ -9,6 +9,8 @@ using osu.Framework.Graphics.Shaders;
using osu.Game.Screens.Menu;
using osuTK;
using osu.Framework.Screens;
using osu.Game.Configuration;
using IntroSequence = osu.Game.Configuration.IntroSequence;
namespace osu.Game.Screens
{
@ -45,6 +47,8 @@ namespace osu.Game.Screens
private OsuScreen loadableScreen;
private ShaderPrecompiler precompiler;
private IntroSequence introSequence;
protected virtual OsuScreen CreateLoadableScreen()
{
if (showDisclaimer)
@ -53,7 +57,17 @@ namespace osu.Game.Screens
return getIntroSequence();
}
private IntroScreen getIntroSequence() => new IntroCircles();
private IntroScreen getIntroSequence()
{
switch (introSequence)
{
case IntroSequence.Circles:
return new IntroCircles();
default:
return new IntroTriangles();
}
}
protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();
@ -79,9 +93,10 @@ namespace osu.Game.Screens
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
private void load(OsuGameBase game, OsuConfigManager config)
{
showDisclaimer = game.IsDeployedBuild;
introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
}
/// <summary>

View File

@ -0,0 +1,413 @@
// 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 System.IO;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.IO.Archives;
using osu.Game.Rulesets;
using osu.Game.Screens.Backgrounds;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Menu
{
public class IntroTriangles : IntroScreen
{
private const string menu_music_beatmap_hash = "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5";
private SampleChannel welcome;
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
{
Alpha = 0,
};
[Resolved]
private AudioManager audio { get; set; }
private Bindable<bool> menuMusic;
private Track track;
private WorkingBeatmap introBeatmap;
private BackgroundScreenDefault background;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
{
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
BeatmapSetInfo setInfo = null;
if (!menuMusic.Value)
{
var sets = beatmaps.GetAllUsableBeatmapSets();
if (sets.Count > 0)
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
}
if (setInfo == null)
{
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
if (setInfo == null)
{
// we need to import the default menu background beatmap
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/triangles.osz"), "triangles.osz")).Result;
setInfo.Protected = true;
beatmaps.Update(setInfo);
}
}
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = introBeatmap.Track;
track.Reset();
if (config.Get<bool>(OsuSetting.MenuVoice) && !menuMusic.Value)
// triangles has welcome sound included in the track. only play this if the user doesn't want menu music.
welcome = audio.Samples.Get(@"welcome");
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
logo.Triangles = true;
if (!resuming)
{
Beatmap.Value = introBeatmap;
introBeatmap = null;
PrepareMenuLoad();
LoadComponentAsync(new TrianglesIntroSequence(logo, background)
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedClock(menuMusic.Value ? track : null),
LoadMenu = LoadMenu
}, t =>
{
AddInternal(t);
welcome?.Play();
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
if (menuMusic.Value)
track.Start();
});
}
}
public override void OnResuming(IScreen last)
{
base.OnResuming(last);
background.FadeOut(100);
}
public override void OnSuspending(IScreen next)
{
track = null;
base.OnSuspending(next);
}
private class TrianglesIntroSequence : CompositeDrawable
{
private readonly OsuLogo logo;
private readonly BackgroundScreenDefault background;
private OsuSpriteText welcomeText;
private RulesetFlow rulesets;
private Container rulesetsScale;
private Drawable logoContainerSecondary;
private Drawable logoContainer;
private GlitchingTriangles triangles;
public Action LoadMenu;
public TrianglesIntroSequence(OsuLogo logo, BackgroundScreenDefault background)
{
this.logo = logo;
this.background = background;
}
private OsuGameBase game;
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuGameBase game)
{
this.game = game;
InternalChildren = new[]
{
triangles = new GlitchingTriangles
{
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(0.4f, 0.16f)
},
welcomeText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Padding = new MarginPadding { Bottom = 10 },
Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
Alpha = 1,
Spacing = new Vector2(5),
},
rulesetsScale = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
rulesets = new RulesetFlow()
}
},
logoContainerSecondary = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = logoContainer = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4"))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
},
};
}
private const double text_1 = 200;
private const double text_2 = 400;
private const double text_3 = 700;
private const double text_4 = 900;
private const double text_glitch = 1060;
private const double rulesets_1 = 1450;
private const double rulesets_2 = 1650;
private const double rulesets_3 = 1850;
private const double logo_scale_duration = 920;
private const double logo_1 = 2080;
private const double logo_2 = logo_1 + logo_scale_duration;
protected override void LoadComplete()
{
base.LoadComplete();
const float scale_start = 1.2f;
const float scale_adjust = 0.8f;
rulesets.Hide();
logoContainer.Hide();
background.Hide();
using (BeginAbsoluteSequence(0, true))
{
using (BeginDelayedSequence(text_1, true))
welcomeText.FadeIn().OnComplete(t => t.Text = "wel");
using (BeginDelayedSequence(text_2, true))
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome");
using (BeginDelayedSequence(text_3, true))
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to");
using (BeginDelayedSequence(text_4, true))
{
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000);
}
using (BeginDelayedSequence(text_glitch, true))
triangles.FadeIn();
using (BeginDelayedSequence(rulesets_1, true))
{
rulesetsScale.ScaleTo(0.8f, 1000);
rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0));
welcomeText.FadeOut();
triangles.FadeOut();
}
using (BeginDelayedSequence(rulesets_2, true))
{
rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0));
}
using (BeginDelayedSequence(rulesets_3, true))
{
rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0));
rulesetsScale.ScaleTo(1.3f, 1000);
}
using (BeginDelayedSequence(logo_1, true))
{
rulesets.FadeOut();
// matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5
logoContainer.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint);
logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad);
}
using (BeginDelayedSequence(logo_2, true))
{
logoContainer.FadeOut().OnComplete(_ =>
{
logo.FadeIn();
background.FadeIn();
game.Add(new GameWideFlash());
LoadMenu();
});
}
}
}
private class GameWideFlash : Box
{
private const double flash_length = 1000;
public GameWideFlash()
{
Colour = Color4.White;
RelativeSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeOutFromOne(flash_length, Easing.Out);
}
}
private class LazerLogo : CompositeDrawable
{
public LazerLogo(Stream videoStream)
{
Size = new Vector2(960);
InternalChild = new VideoSprite(videoStream)
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 }
};
}
}
private class RulesetFlow : FillFlowContainer
{
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
var modes = new List<Drawable>();
foreach (var ruleset in rulesets.AvailableRulesets)
{
var icon = new ConstrainedIconContainer
{
Icon = ruleset.CreateInstance().CreateIcon(),
Size = new Vector2(30),
};
modes.Add(icon);
}
AutoSizeAxes = Axes.Both;
Children = modes;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
}
private class GlitchingTriangles : CompositeDrawable
{
public GlitchingTriangles()
{
RelativeSizeAxes = Axes.Both;
}
private double? lastGenTime;
private const double time_between_triangles = 22;
protected override void Update()
{
base.Update();
if (lastGenTime == null || Time.Current - lastGenTime > time_between_triangles)
{
lastGenTime = (lastGenTime ?? Time.Current) + time_between_triangles;
Drawable triangle = new OutlineTriangle(RNG.NextBool(), (RNG.NextSingle() + 0.2f) * 80)
{
RelativePositionAxes = Axes.Both,
Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
};
AddInternal(triangle);
triangle.FadeOutFromOne(120);
}
}
/// <summary>
/// Represents a sprite that is drawn in a triangle shape, instead of a rectangle shape.
/// </summary>
public class OutlineTriangle : BufferedContainer
{
public OutlineTriangle(bool outlineOnly, float size)
{
Size = new Vector2(size);
InternalChildren = new Drawable[]
{
new Triangle { RelativeSizeAxes = Axes.Both },
};
if (outlineOnly)
{
AddInternal(new Triangle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = Color4.Black,
Size = new Vector2(size - 5),
Blending = BlendingMode.None,
});
}
Blending = BlendingMode.Additive;
CacheDrawnFrameBuffer = true;
}
}
}
}
}
}

View File

@ -25,10 +25,14 @@ namespace osu.Game.Screens.Multi.Match.Components
{
public const float HEIGHT = 200;
public MatchTabControl Tabs;
public readonly BindableBool ShowBeatmapPanel = new BindableBool();
public MatchTabControl Tabs { get; private set; }
public Action RequestBeatmapSelection;
private MatchBeatmapPanel beatmapPanel;
public Header()
{
RelativeSizeAxes = Axes.X;
@ -53,8 +57,14 @@ namespace osu.Game.Screens.Multi.Match.Components
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.4f), Color4.Black.Opacity(0.6f)),
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.7f), Color4.Black.Opacity(0.8f)),
},
beatmapPanel = new MatchBeatmapPanel
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 100 },
}
}
},
new Box
@ -114,6 +124,12 @@ namespace osu.Game.Screens.Multi.Match.Components
beatmapButton.Action = () => RequestBeatmapSelection?.Invoke();
}
protected override void LoadComplete()
{
base.LoadComplete();
ShowBeatmapPanel.BindValueChanged(value => beatmapPanel.FadeTo(value.NewValue ? 1 : 0, 200, Easing.OutQuint), true);
}
private class BeatmapSelectButton : HeaderButton
{
[Resolved(typeof(Room), nameof(Room.RoomID))]

View File

@ -28,7 +28,6 @@ namespace osu.Game.Screens.Multi.Match.Components
private void load()
{
ReadyButton readyButton;
ViewBeatmapButton viewBeatmapButton;
HostInfo hostInfo;
InternalChildren = new Drawable[]
@ -80,7 +79,6 @@ namespace osu.Game.Screens.Multi.Match.Components
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
viewBeatmapButton = new ViewBeatmapButton(),
readyButton = new ReadyButton
{
Action = () => OnStart?.Invoke()
@ -91,11 +89,7 @@ namespace osu.Game.Screens.Multi.Match.Components
},
};
CurrentItem.BindValueChanged(item =>
{
viewBeatmapButton.Beatmap.Value = item.NewValue?.Beatmap;
readyButton.Beatmap.Value = item.NewValue?.Beatmap;
}, true);
CurrentItem.BindValueChanged(item => readyButton.Beatmap.Value = item.NewValue?.Beatmap, true);
hostInfo.Host.BindTo(Host);
}

View File

@ -0,0 +1,62 @@
// 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.Threading;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Multi.Match.Components
{
public class MatchBeatmapPanel : MultiplayerComposite
{
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private RulesetStore rulesets { get; set; }
private CancellationTokenSource loadCancellation;
private GetBeatmapSetRequest request;
private DirectGridPanel panel;
public MatchBeatmapPanel()
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
CurrentItem.BindValueChanged(item => loadNewPanel(item.NewValue?.Beatmap), true);
}
private void loadNewPanel(BeatmapInfo beatmap)
{
loadCancellation?.Cancel();
request?.Cancel();
panel?.FadeOut(200);
panel?.Expire();
panel = null;
if (beatmap?.OnlineBeatmapID == null)
return;
loadCancellation = new CancellationTokenSource();
request = new GetBeatmapSetRequest(beatmap.OnlineBeatmapID.Value, BeatmapSetLookupType.BeatmapId);
request.Success += res => Schedule(() =>
{
panel = new DirectGridPanel(res.ToBeatmapSet(rulesets));
LoadComponentAsync(panel, AddInternal, loadCancellation.Token);
});
api.Queue(request);
}
}
}

View File

@ -1,46 +0,0 @@
// 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.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osuTK;
namespace osu.Game.Screens.Multi.Match.Components
{
public class ViewBeatmapButton : HeaderButton
{
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
[Resolved(CanBeNull = true)]
private OsuGame osuGame { get; set; }
public ViewBeatmapButton()
{
RelativeSizeAxes = Axes.Y;
Size = new Vector2(200, 1);
Text = "View beatmap";
}
[BackgroundDependencyLoader]
private void load()
{
if (osuGame != null)
Beatmap.BindValueChanged(beatmap => updateAction(beatmap.NewValue), true);
}
private void updateAction(BeatmapInfo beatmap)
{
if (beatmap == null)
{
Enabled.Value = false;
return;
}
Action = () => osuGame.ShowBeatmap(beatmap.OnlineBeatmapID ?? 0);
Enabled.Value = true;
}
}
}

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.GameTypes;
@ -18,7 +19,8 @@ using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem;
namespace osu.Game.Screens.Multi.Match
{
public class MatchSubScreen : MultiplayerSubScreen
[Cached(typeof(IPreviewTrackOwner))]
public class MatchSubScreen : MultiplayerSubScreen, IPreviewTrackOwner
{
public override bool DisallowExternalBeatmapRulesetChanges => true;
@ -44,6 +46,9 @@ namespace osu.Game.Screens.Multi.Match
[Resolved]
private BeatmapManager beatmapManager { get; set; }
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
[Resolved(CanBeNull = true)]
private OsuGame game { get; set; }
@ -146,18 +151,12 @@ namespace osu.Game.Screens.Multi.Match
{
const float fade_duration = 500;
if (tab.NewValue is SettingsMatchPage)
{
settings.Show();
info.FadeOut(fade_duration, Easing.OutQuint);
bottomRow.FadeOut(fade_duration, Easing.OutQuint);
}
else
{
settings.Hide();
info.FadeIn(fade_duration, Easing.OutQuint);
bottomRow.FadeIn(fade_duration, Easing.OutQuint);
}
var settingsDisplayed = tab.NewValue is SettingsMatchPage;
header.ShowBeatmapPanel.Value = !settingsDisplayed;
settings.State.Value = settingsDisplayed ? Visibility.Visible : Visibility.Hidden;
info.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint);
bottomRow.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint);
}, true);
chat.Exit += () =>
@ -179,8 +178,8 @@ namespace osu.Game.Screens.Multi.Match
public override bool OnExiting(IScreen next)
{
RoomManager?.PartRoom();
Mods.Value = Array.Empty<Mod>();
previewTrackManager.StopAnyPlaying(this);
return base.OnExiting(next);
}
@ -198,6 +197,8 @@ namespace osu.Game.Screens.Multi.Match
if (e.NewValue?.Ruleset != null)
Ruleset.Value = e.NewValue.Ruleset;
previewTrackManager.StopAnyPlaying(this);
}
/// <summary>
@ -223,6 +224,8 @@ namespace osu.Game.Screens.Multi.Match
private void onStart()
{
previewTrackManager.StopAnyPlaying(this);
switch (type.Value)
{
default:

View File

@ -20,6 +20,9 @@ namespace osu.Game.Users
[JsonProperty(@"username")]
public string Username;
[JsonProperty(@"previous_usernames")]
public string[] PreviousUsernames;
[JsonProperty(@"country")]
public Country Country;

View File

@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.813.0" />
<PackageReference Include="SharpCompress" Version="0.23.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -3,7 +3,8 @@
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<DefaultMtouchExtraArgs>--nolinkaway -gcc_flags "-lstdc++ -framework AudioToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate</DefaultMtouchExtraArgs>
<DefaultMtouchExtraArgs>--nolinkaway</DefaultMtouchExtraArgs>
<DefaultMtouchGccFlags>-lstdc++ -lbz2 -framework AudioToolbox -framework AVFoundation -framework CoreMedia -framework VideoToolbox -framework SystemConfiguration -framework CFNetwork -framework Accelerate</DefaultMtouchGccFlags>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
<DebugSymbols>true</DebugSymbols>
@ -24,7 +25,7 @@
<DeviceSpecificBuild>false</DeviceSpecificBuild>
<MtouchVerbosity></MtouchVerbosity>
<LangVersion>Default</LangVersion>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"</MtouchExtraArgs>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"</MtouchExtraArgs>
<OptimizePNGs>false</OptimizePNGs>
<MtouchI18n>cjk,mideast,other,rare,west</MtouchI18n>
</PropertyGroup>
@ -44,7 +45,7 @@
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<MtouchVerbosity></MtouchVerbosity>
<LangVersion>Default</LangVersion>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"</MtouchExtraArgs>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"</MtouchExtraArgs>
<OptimizePNGs>false</OptimizePNGs>
<MtouchI18n>cjk,mideast,other,rare,west</MtouchI18n>
</PropertyGroup>
@ -62,7 +63,7 @@
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<MtouchVerbosity></MtouchVerbosity>
<LangVersion>Default</LangVersion>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"</MtouchExtraArgs>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"</MtouchExtraArgs>
<OptimizePNGs>false</OptimizePNGs>
<MtouchI18n>cjk,mideast,other,rare,west</MtouchI18n>
</PropertyGroup>
@ -86,10 +87,22 @@
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler>
<MtouchVerbosity></MtouchVerbosity>
<LangVersion>Default</LangVersion>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -force_load $(OutputPath)\libbass.a -force_load $(OutputPath)\libbass_fx.a"</MtouchExtraArgs>
<MtouchExtraArgs>$(DefaultMtouchExtraArgs) -gcc_flags "$(DefaultMtouchGccFlags)"</MtouchExtraArgs>
<OptimizePNGs>false</OptimizePNGs>
<MtouchI18n>cjk,mideast,other,rare,west</MtouchI18n>
</PropertyGroup>
<ItemGroup>
<NativeReference Include="$(OutputPath)\libbass.a;$(OutputPath)\libbass_fx.a">
<Kind>Static</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>True</ForceLoad>
</NativeReference>
<NativeReference Include="$(OutputPath)\libavcodec.a;$(OutputPath)\libavdevice.a;$(OutputPath)\libavfilter.a;$(OutputPath)\libavformat.a;$(OutputPath)\libavutil.a;$(OutputPath)\libswresample.a;$(OutputPath)\libswscale.a">
<Kind>Static</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>True</ForceLoad>
</NativeReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
@ -105,12 +118,12 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.809.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.813.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.813.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.307.0" ExcludeAssets="all" />
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.813.0" ExcludeAssets="all" />
</ItemGroup>
</Project>

Binary file not shown.

Binary file not shown.

View File

@ -19,12 +19,6 @@
<ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<None Include="libbass_fx.a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="libbass.a">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Compile Include="Application.cs" />