diff --git a/osu-framework b/osu-framework index 21fd81a848..e776f6f272 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 21fd81a84860cf6854026458ac82905b6248f41d +Subproject commit e776f6f2729bbe93206c4e8c089eeca57522fcf7 diff --git a/osu.Desktop.Tests/BenchmarkTest.cs b/osu.Desktop.Tests/BenchmarkTest.cs new file mode 100644 index 0000000000..c55b98aa57 --- /dev/null +++ b/osu.Desktop.Tests/BenchmarkTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; +using osu.Desktop.VisualTests; +using osu.Framework.Desktop.Platform; +using osu.Game.Modes; +using osu.Game.Modes.Catch; +using osu.Game.Modes.Mania; +using osu.Game.Modes.Osu; +using osu.Game.Modes.Taiko; + +namespace osu.Desktop.Tests +{ + [TestFixture] + public class BenchmarkTest + { + [Test] + public void TestBenchmark() + { + using (var host = new HeadlessGameHost()) + { + Ruleset.Register(new OsuRuleset()); + Ruleset.Register(new TaikoRuleset()); + Ruleset.Register(new ManiaRuleset()); + Ruleset.Register(new CatchRuleset()); + + host.Add(new Benchmark()); + host.Run(); + } + } + } +} diff --git a/osu.Desktop.Tests/app.config b/osu.Desktop.Tests/app.config new file mode 100644 index 0000000000..44ccc4b77a --- /dev/null +++ b/osu.Desktop.Tests/app.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration> \ No newline at end of file diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj new file mode 100644 index 0000000000..2c88548c3e --- /dev/null +++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>osu.Desktop.Tests</RootNamespace> + <AssemblyName>osu.Desktop.Tests</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> + </PropertyGroup> + <ItemGroup> + <Reference Include="mscorlib" /> + <Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> + <HintPath>..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="Newtonsoft.Json"> + <HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> + </Reference> + <Reference Include="SQLiteNetExtensions"> + <HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath> + </Reference> + <Reference Include="SQLite.Net.Platform.Win32"> + <HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath> + </Reference> + <Reference Include="SQLite.Net.Platform.Generic"> + <HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath> + </Reference> + <Reference Include="OpenTK"> + <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1339\lib\net45\OpenTK.dll</HintPath> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="BenchmarkTest.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\osu-framework\osu.Framework.Desktop\osu.Framework.Desktop.csproj"> + <Project>{65DC628F-A640-4111-AB35-3A5652BC1E17}</Project> + <Name>osu.Framework.Desktop</Name> + </ProjectReference> + <ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj"> + <Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project> + <Name>osu.Framework</Name> + </ProjectReference> + <ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj"> + <Project>{d9a367c9-4c1a-489f-9b05-a0cea2b53b58}</Project> + <Name>osu.Game.Resources</Name> + </ProjectReference> + <ProjectReference Include="..\osu.Desktop.VisualTests\osu.Desktop.VisualTests.csproj"> + <Project>{69051C69-12AE-4E7D-A3E6-460D2E282312}</Project> + <Name>osu.Desktop.VisualTests</Name> + </ProjectReference> + <ProjectReference Include="..\osu.Game.Modes.Catch\osu.Game.Modes.Catch.csproj"> + <Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project> + <Name>osu.Game.Modes.Catch</Name> + </ProjectReference> + <ProjectReference Include="..\osu.Game.Modes.Mania\osu.Game.Modes.Mania.csproj"> + <Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project> + <Name>osu.Game.Modes.Mania</Name> + </ProjectReference> + <ProjectReference Include="..\osu.Game.Modes.Osu\osu.Game.Modes.Osu.csproj"> + <Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project> + <Name>osu.Game.Modes.Osu</Name> + </ProjectReference> + <ProjectReference Include="..\osu.Game.Modes.Taiko\osu.Game.Modes.Taiko.csproj"> + <Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project> + <Name>osu.Game.Modes.Taiko</Name> + </ProjectReference> + <ProjectReference Include="..\osu.Game\osu.Game.csproj"> + <Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project> + <Name>osu.Game</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="app.config" /> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <Folder Include="Properties\" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file diff --git a/osu.Desktop.Tests/packages.config b/osu.Desktop.Tests/packages.config new file mode 100644 index 0000000000..05b53c019c --- /dev/null +++ b/osu.Desktop.Tests/packages.config @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> + <package id="NUnit" version="3.5.0" targetFramework="net45" /> + <package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> + <package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" /> + <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" /> +</packages> \ No newline at end of file diff --git a/osu.Desktop.VisualTests/Benchmark.cs b/osu.Desktop.VisualTests/Benchmark.cs new file mode 100644 index 0000000000..506788e93c --- /dev/null +++ b/osu.Desktop.VisualTests/Benchmark.cs @@ -0,0 +1,56 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Desktop.Platform; +using osu.Framework.GameModes.Testing; +using osu.Game; +using osu.Game.Modes; +using osu.Game.Modes.Catch; +using osu.Game.Modes.Mania; +using osu.Game.Modes.Osu; +using osu.Game.Modes.Taiko; + +namespace osu.Desktop.VisualTests +{ + public class Benchmark : OsuGameBase + { + private double timePerTest = 200; + + [BackgroundDependencyLoader] + private void load(BaseGame game) + { + Host.MaximumDrawHz = int.MaxValue; + Host.MaximumUpdateHz = int.MaxValue; + Host.MaximumInactiveHz = int.MaxValue; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + TestBrowser f = new TestBrowser(); + Add(f); + + Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {timePerTest}ms each..."); + + for (int i = 1; i < f.TestCount; i++) + { + int loadableCase = i; + Scheduler.AddDelayed(delegate + { + f.LoadTest(loadableCase); + Console.WriteLine($@"{Time}: Switching to test #{loadableCase}"); + }, loadableCase * timePerTest); + } + + Scheduler.AddDelayed(Host.Exit, f.TestCount * timePerTest); + } + } +} diff --git a/osu.Desktop.VisualTests/Program.cs b/osu.Desktop.VisualTests/Program.cs index df54297812..b89c6bcf4d 100644 --- a/osu.Desktop.VisualTests/Program.cs +++ b/osu.Desktop.VisualTests/Program.cs @@ -18,6 +18,8 @@ namespace osu.Desktop.VisualTests [STAThread] public static void Main(string[] args) { + bool benchmark = args.Length > 0 && args[0] == @"-benchmark"; + using (BasicGameHost host = Host.GetSuitableHost(@"osu")) { Ruleset.Register(new OsuRuleset()); @@ -25,7 +27,10 @@ namespace osu.Desktop.VisualTests Ruleset.Register(new ManiaRuleset()); Ruleset.Register(new CatchRuleset()); - host.Add(new VisualTestGame()); + if (benchmark) + host.Add(new Benchmark()); + else + host.Add(new VisualTestGame()); host.Run(); } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs new file mode 100644 index 0000000000..77b313f4ad --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs @@ -0,0 +1,121 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.GameModes.Testing; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using osu.Game.Overlays; +using System.Linq; +using osu.Game.Overlays.Notifications; +using osu.Game.Screens.Backgrounds; + +namespace osu.Desktop.VisualTests.Tests +{ + class TestCaseNotificationManager : TestCase + { + public override string Name => @"Notification Manager"; + public override string Description => @"I handle notifications"; + + NotificationManager manager; + + public override void Reset() + { + base.Reset(); + + progressingNotifications.Clear(); + + AddInternal(new BackgroundModeDefault() { Depth = 10 }); + + Content.Add(manager = new NotificationManager + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }); + + AddToggle(@"show", manager.ToggleVisibility); + + AddButton(@"simple #1", sendNotification1); + AddButton(@"simple #2", sendNotification2); + AddButton(@"progress #1", sendProgress1); + AddButton(@"progress #2", sendProgress2); + AddButton(@"barrage", () => sendBarrage()); + } + + private void sendBarrage(int remaining = 100) + { + switch (RNG.Next(0, 4)) + { + case 0: + sendNotification1(); + break; + case 1: + sendNotification2(); + break; + case 2: + sendProgress1(); + break; + case 3: + sendProgress2(); + break; + } + + if (remaining > 0) + { + Delay(80); + Schedule(() => sendBarrage(remaining - 1)); + } + } + + protected override void Update() + { + base.Update(); + + progressingNotifications.RemoveAll(n => n.State == ProgressNotificationState.Completed); + + while (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3) + { + var p = progressingNotifications.FirstOrDefault(n => n.IsLoaded && n.State == ProgressNotificationState.Queued); + if (p == null) + break; + + p.State = ProgressNotificationState.Active; + } + + foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) + { + if (n.Progress < 1) + n.Progress += (float)(Time.Elapsed / 2000) * RNG.NextSingle(); + else + n.State = ProgressNotificationState.Completed; + } + } + + private void sendProgress2() + { + var n = new ProgressNotification { Text = @"Downloading Haitai..." }; + manager.Post(n); + progressingNotifications.Add(n); + } + + List<ProgressNotification> progressingNotifications = new List<ProgressNotification>(); + + private void sendProgress1() + { + var n = new ProgressNotification { Text = @"Uploading to BSS..." }; + manager.Post(n); + progressingNotifications.Add(n); + } + + private void sendNotification2() + { + manager.Post(new SimpleNotification { Text = @"You are amazing" }); + } + + private void sendNotification1() + { + manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" }); + } + } +} diff --git a/osu.Desktop.VisualTests/VisualTestGame.cs b/osu.Desktop.VisualTests/VisualTestGame.cs index 7489559c98..952de4ab26 100644 --- a/osu.Desktop.VisualTests/VisualTestGame.cs +++ b/osu.Desktop.VisualTests/VisualTestGame.cs @@ -10,7 +10,6 @@ using osu.Framework.Desktop.Platform; using System.Reflection; using System.IO; using System.Collections.Generic; -using SQLiteNetExtensions.Extensions; using osu.Framework.Allocation; namespace osu.Desktop.VisualTests diff --git a/osu.Desktop.VisualTests/app.config b/osu.Desktop.VisualTests/app.config new file mode 100644 index 0000000000..9bad888bf3 --- /dev/null +++ b/osu.Desktop.VisualTests/app.config @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>. +Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +--> + +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration> \ No newline at end of file diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 69df007013..cd899d4dd2 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -59,6 +59,7 @@ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <RunCodeAnalysis>false</RunCodeAnalysis> <Prefer32Bit>false</Prefer32Bit> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>none</DebugType> @@ -73,6 +74,7 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <Prefer32Bit>false</Prefer32Bit> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup> <Win32Resource> @@ -100,13 +102,12 @@ <Reference Include="OpenTK"> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1339\lib\net45\OpenTK.dll</HintPath> </Reference> - <Reference Include="System.Drawing" /> - <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <None Include="..\osu.licenseheader"> <Link>osu.licenseheader</Link> </None> + <None Include="app.config" /> <None Include="packages.config" /> <None Include="OpenTK.dll.config" /> </ItemGroup> @@ -172,10 +173,12 @@ </ProjectReference> </ItemGroup> <ItemGroup> + <Compile Include="Benchmark.cs" /> <Compile Include="Program.cs" /> <Compile Include="Tests\TestCaseChatDisplay.cs" /> <Compile Include="Tests\TestCaseGamefield.cs" /> <Compile Include="Tests\TestCaseMusicController.cs" /> + <Compile Include="Tests\TestCaseNotificationManager.cs" /> <Compile Include="Tests\TestCasePlayer.cs" /> <Compile Include="Tests\TestCaseHitObjects.cs" /> <Compile Include="Tests\TestCaseKeyCounter.cs" /> diff --git a/osu.Desktop.VisualTests/packages.config b/osu.Desktop.VisualTests/packages.config index f39a585e49..75affafd35 100644 --- a/osu.Desktop.VisualTests/packages.config +++ b/osu.Desktop.VisualTests/packages.config @@ -1,13 +1,12 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>. -Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE ---> - -<packages> - <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> - <package id="ppy.OpenTK" version="2.0.50727.1339" targetFramework="net45" /> - <package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> - <package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" /> - <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" /> +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>. +Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +--> +<packages> + <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> + <package id="ppy.OpenTK" version="2.0.50727.1339" targetFramework="net45" /> + <package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> + <package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" /> + <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" /> </packages> \ No newline at end of file diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 5edeffd448..167d4644b7 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -11,6 +11,9 @@ using System.Windows.Forms; using osu.Framework.Platform; using osu.Framework.Desktop.Platform; using osu.Game.Database; +using osu.Desktop.Overlays; +using System.Reflection; +using System.Drawing; namespace osu.Desktop { @@ -22,12 +25,22 @@ namespace osu.Desktop } + protected override void LoadComplete() + { + base.LoadComplete(); + + (new VersionManager()).Preload(this, Add); + } + public override void SetHost(BasicGameHost host) { base.SetHost(host); var desktopWindow = host.Window as DesktopGameWindow; if (desktopWindow != null) { + desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); + desktopWindow.Title = @"osu!lazer"; + desktopWindow.DragEnter += dragEnter; desktopWindow.DragDrop += dragDrop; } diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs new file mode 100644 index 0000000000..a3648e1f12 --- /dev/null +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -0,0 +1,93 @@ +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; +using Squirrel; +using System.Reflection; + +namespace osu.Desktop.Overlays +{ + public class VersionManager : OverlayContainer + { + private UpdateManager updateManager; + private NotificationManager notification; + + [BackgroundDependencyLoader] + private void load(NotificationManager notification) + { + this.notification = notification; + + AutoSizeAxes = Axes.Both; + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + + var asm = Assembly.GetEntryAssembly().GetName(); + Add(new OsuSpriteText + { + Text = $@"osu!lazer v{asm.Version}" + }); + + updateChecker(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + State = Visibility.Visible; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + updateManager?.Dispose(); + } + + private async void updateChecker() + { + updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true); + var info = await updateManager.CheckForUpdate(); + if (info.ReleasesToApply.Count > 0) + { + ProgressNotification n = new UpdateProgressNotification + { + Text = @"Downloading update..." + }; + Schedule(() => notification.Post(n)); + Schedule(() => n.State = ProgressNotificationState.Active); + await updateManager.DownloadReleases(info.ReleasesToApply, (int p) => Schedule(() => n.Progress = p / 100f)); + Schedule(() => n.Text = @"Installing update..."); + await updateManager.ApplyReleases(info, (int p) => Schedule(() => n.Progress = p / 100f)); + Schedule(() => n.State = ProgressNotificationState.Completed); + + } + else + { + //check again every 30 minutes. + Scheduler.AddDelayed(updateChecker, 60000 * 30); + } + } + + protected override void PopIn() + { + } + + protected override void PopOut() + { + } + + class UpdateProgressNotification : ProgressNotification + { + protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification(this) + { + Text = @"Update ready to install. Click to restart!", + Activated = () => + { + UpdateManager.RestartApp(); + return true; + } + }; + } + } +} diff --git a/osu.Desktop/Properties/AssemblyInfo.cs b/osu.Desktop/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d7391080a6 --- /dev/null +++ b/osu.Desktop/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("osu!lazer")] +[assembly: AssemblyDescription("click the circles. to the beat.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ppy Pty Ltd")] +[assembly: AssemblyProduct("osu!lazer")] +[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("55e28cb2-7b6c-4595-8dcc-9871d8aad7e9")] + +[assembly: AssemblyVersion("0.0.3")] +[assembly: AssemblyFileVersion("0.0.3")] diff --git a/osu.Desktop/app.config b/osu.Desktop/app.config new file mode 100644 index 0000000000..44ccc4b77a --- /dev/null +++ b/osu.Desktop/app.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration> \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 9df4148ac0..2ab913d706 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -59,6 +59,7 @@ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <RunCodeAnalysis>false</RunCodeAnalysis> <Prefer32Bit>false</Prefer32Bit> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> <Commandlineparameters> </Commandlineparameters> </PropertyGroup> @@ -75,21 +76,76 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> <Prefer32Bit>false</Prefer32Bit> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup> - <Win32Resource>osu!.res</Win32Resource> + <Win32Resource> + </Win32Resource> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>lazer.ico</ApplicationIcon> + </PropertyGroup> + <PropertyGroup> + <ApplicationManifest>Properties\app.manifest</ApplicationManifest> </PropertyGroup> <ItemGroup> + <Reference Include="DeltaCompressionDotNet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL"> + <HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="DeltaCompressionDotNet.MsDelta, Version=1.0.0.0, Culture=neutral, PublicKeyToken=46b2138a390abf55, processorArchitecture=MSIL"> + <HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.MsDelta.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="DeltaCompressionDotNet.PatchApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3e8888ee913ed789, processorArchitecture=MSIL"> + <HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.PatchApi.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> + <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> + <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Mdb.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> + <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Pdb.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> + <HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath> + <Private>True</Private> + </Reference> <Reference Include="mscorlib" /> + <Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath> + <Private>True</Private> + </Reference> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4" /> + <Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\Splat.1.6.2\lib\Net45\Splat.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="Squirrel, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL"> + <HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll</HintPath> + <Private>True</Private> + </Reference> <Reference Include="System" /> + <Reference Include="System.Drawing" /> <Reference Include="System.Windows.Forms" /> </ItemGroup> <ItemGroup> <None Include="..\osu.licenseheader"> <Link>osu.licenseheader</Link> </None> + <None Include="app.config" /> <None Include="osu!.res" /> + <None Include="packages.config" /> <None Include="Properties\app.manifest" /> </ItemGroup> <ItemGroup> @@ -155,10 +211,14 @@ </ItemGroup> <ItemGroup> <Compile Include="OsuGameDesktop.cs" /> + <Compile Include="Overlays\VersionManager.cs" /> <Compile Include="Program.cs" /> <Compile Include="Beatmaps\IO\LegacyFilesystemReader.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <Content Include="lazer.ico" /> </ItemGroup> - <ItemGroup /> <ItemGroup /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config new file mode 100644 index 0000000000..8d1361bd0a --- /dev/null +++ b/osu.Desktop/packages.config @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="DeltaCompressionDotNet" version="1.0.0" targetFramework="net45" /> + <package id="Mono.Cecil" version="0.9.6.1" targetFramework="net45" /> + <package id="Splat" version="1.6.2" targetFramework="net45" /> + <package id="squirrel.windows" version="1.5.2" targetFramework="net45" /> +</packages> \ No newline at end of file diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj index e68203872e..8d32c23b9b 100644 --- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj +++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj @@ -20,6 +20,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -28,6 +29,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj index 22b16c6a9a..7ff501c86d 100644 --- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj +++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj @@ -20,6 +20,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -28,6 +29,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index a9a346f563..a659683c27 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -21,6 +21,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -29,6 +30,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index 355f71d0df..47c71ce09b 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -20,6 +20,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -28,6 +29,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 5e1ce553d8..c6f0c6fa55 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -18,6 +18,7 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <ConsolePause>false</ConsolePause> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <Optimize>true</Optimize> @@ -25,6 +26,7 @@ <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> <ConsolePause>false</ConsolePause> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 674ab76802..7f1cf236ac 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps { if (background != null) return background; - if (BeatmapInfo.Metadata?.BackgroundFile == null) return null; + if (BeatmapInfo?.Metadata?.BackgroundFile == null) return null; try { diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 1b678b2148..a81c1fe552 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -83,65 +83,67 @@ namespace osu.Game.Database connection.DeleteAll<BeatmapInfo>(); } - public void Import(params string[] paths) + public void Import(IEnumerable<string> paths) { foreach (string p in paths) + Import(p); + } + + public void Import(string path) + { + string hash = null; + + BeatmapMetadata metadata; + + using (var reader = ArchiveReader.GetReader(storage, path)) + metadata = reader.ReadMetadata(); + + if (metadata.OnlineBeatmapSetID.HasValue && + connection.Table<BeatmapSetInfo>().Count(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) != 0) + return; // TODO: Update this beatmap instead + + if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader { - var path = p; - string hash = null; - - BeatmapMetadata metadata; - - using (var reader = ArchiveReader.GetReader(storage, path)) - metadata = reader.ReadMetadata(); - - if (metadata.OnlineBeatmapSetID.HasValue && - connection.Table<BeatmapSetInfo>().Count(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) != 0) - return; // TODO: Update this beatmap instead - - if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader + using (var md5 = MD5.Create()) + using (var input = storage.GetStream(path)) { - using (var md5 = MD5.Create()) - using (var input = storage.GetStream(path)) - { - hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant(); - input.Seek(0, SeekOrigin.Begin); - path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); - using (var output = storage.GetStream(path, FileAccess.Write)) - input.CopyTo(output); - } + hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant(); + input.Seek(0, SeekOrigin.Begin); + path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); + using (var output = storage.GetStream(path, FileAccess.Write)) + input.CopyTo(output); } - var beatmapSet = new BeatmapSetInfo - { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, - Beatmaps = new List<BeatmapInfo>(), - Path = path, - Hash = hash, - Metadata = metadata - }; + } + var beatmapSet = new BeatmapSetInfo + { + OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + Beatmaps = new List<BeatmapInfo>(), + Path = path, + Hash = hash, + Metadata = metadata + }; - using (var reader = ArchiveReader.GetReader(storage, path)) + using (var reader = ArchiveReader.GetReader(storage, path)) + { + string[] mapNames = reader.ReadBeatmaps(); + foreach (var name in mapNames) { - string[] mapNames = reader.ReadBeatmaps(); - foreach (var name in mapNames) + using (var stream = new StreamReader(reader.GetStream(name))) { - using (var stream = new StreamReader(reader.GetStream(name))) - { - var decoder = BeatmapDecoder.GetDecoder(stream); - Beatmap beatmap = decoder.Decode(stream); - beatmap.BeatmapInfo.Path = name; + var decoder = BeatmapDecoder.GetDecoder(stream); + Beatmap beatmap = decoder.Decode(stream); + beatmap.BeatmapInfo.Path = name; - // TODO: Diff beatmap metadata with set metadata and leave it here if necessary - beatmap.BeatmapInfo.Metadata = null; + // TODO: Diff beatmap metadata with set metadata and leave it here if necessary + beatmap.BeatmapInfo.Metadata = null; - beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); - } + beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); } beatmapSet.StoryboardFile = reader.ReadStoryboard(); } - - Import(new[] { beatmapSet }); } + + Import(new[] { beatmapSet }); } public void Import(IEnumerable<BeatmapSetInfo> beatmapSets) diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index c5e53e14a5..fe3601a5f2 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -45,7 +45,8 @@ namespace osu.Game.Graphics.Containers { base.Update(); - content.MoveTo((ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount, firstUpdate ? 0 : 1000, EasingTypes.OutQuint); + Vector2 offset = input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2; + content.MoveTo(offset * ParallaxAmount, firstUpdate ? 0 : 1000, EasingTypes.OutQuint); content.Scale = new Vector2(1 + ParallaxAmount); firstUpdate = false; diff --git a/osu.Game/Graphics/Cursor/OsuCursorContainer.cs b/osu.Game/Graphics/Cursor/OsuCursorContainer.cs index 8df0777989..691fb701f1 100644 --- a/osu.Game/Graphics/Cursor/OsuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuCursorContainer.cs @@ -67,6 +67,11 @@ namespace osu.Game.Graphics.Cursor Masking = true, BorderThickness = Size.X / 6, BorderColour = Color4.White, + EdgeEffect = new EdgeEffect { + Type = EdgeEffectType.Shadow, + Colour = Color4.Pink.Opacity(0.5f), + Radius = 5, + }, Children = new Drawable[] { new Box diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index 5bc76b74b4..f5749846be 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -9,7 +9,7 @@ using OpenTK.Graphics; namespace osu.Game.Graphics.Sprites { - class OsuSpriteText : SpriteText + public class OsuSpriteText : SpriteText { public const float FONT_SIZE = 16; diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index c8174ef546..61b59d6b51 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.UserInterface; namespace osu.Game.Graphics.UserInterface { - class Nub : CircularContainer, IStateful<CheckBoxState> + public class Nub : CircularContainer, IStateful<CheckBoxState> { public const float COLLAPSED_SIZE = 20; public const float EXPANDED_SIZE = 40; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 97c9dbb2e0..9188fab355 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -24,6 +24,7 @@ using osu.Game.Screens.Menu; using OpenTK; using System.Linq; using osu.Framework.Graphics.Primitives; +using System.Collections.Generic; namespace osu.Game { @@ -35,6 +36,8 @@ namespace osu.Game private MusicController musicController; + private NotificationManager notificationManager; + private MainMenu mainMenu => modeStack?.ChildGameMode as MainMenu; private Intro intro => modeStack as Intro; @@ -65,14 +68,17 @@ namespace osu.Game } if (args?.Length > 0) - ImportBeatmaps(args); + { + var paths = args.Where(a => !a.StartsWith(@"-")); + ImportBeatmaps(paths); + } Dependencies.Cache(this); PlayMode = LocalConfig.GetBindable<PlayMode>(OsuConfig.PlayMode); } - public void ImportBeatmaps(params string[] paths) + public void ImportBeatmaps(IEnumerable<string> paths) { Schedule(delegate { Dependencies.Get<BeatmapDatabase>().Import(paths); }); } @@ -117,8 +123,16 @@ namespace osu.Game Origin = Anchor.TopRight, }).Preload(this, overlayContent.Add); + (notificationManager = new NotificationManager + { + Depth = -2, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }).Preload(this, overlayContent.Add); + Dependencies.Cache(options); Dependencies.Cache(musicController); + Dependencies.Cache(notificationManager); (Toolbar = new Toolbar { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 457dc1e7e1..3b13c7bf88 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -240,7 +240,6 @@ namespace osu.Game.Overlays if (current?.TrackLoaded ?? false) { - progress.UpdatePosition((float)(current.Track.CurrentTime / current.Track.Length)); playButton.Icon = current.Track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; diff --git a/osu.Game/Overlays/NotificationManager.cs b/osu.Game/Overlays/NotificationManager.cs new file mode 100644 index 0000000000..ecfa3daa38 --- /dev/null +++ b/osu.Game/Overlays/NotificationManager.cs @@ -0,0 +1,113 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using osu.Game.Graphics; +using osu.Game.Overlays.Notifications; +using OpenTK.Graphics; + +namespace osu.Game.Overlays +{ + public class NotificationManager : FocusedOverlayContainer + { + private const float width = 320; + + public const float TRANSITION_LENGTH = 600; + + private ScrollContainer scrollContainer; + private FlowContainer<NotificationSection> sections; + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours) + { + Width = width; + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.6f, + }, + scrollContainer = new ScrollContainer() + { + Margin = new MarginPadding { Top = Toolbar.Toolbar.HEIGHT }, + Children = new[] + { + sections = new FlowContainer<NotificationSection> + { + Direction = FlowDirection.VerticalOnly, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Children = new [] + { + new NotificationSection + { + Title = @"Notifications", + ClearText = @"Clear All", + AcceptTypes = new [] { typeof(SimpleNotification) }, + }, + new NotificationSection + { + Title = @"Running Tasks", + ClearText = @"Cancel All", + AcceptTypes = new [] { typeof(ProgressNotification) }, + }, + } + } + } + } + }; + } + + int runningDepth = 0; + + public void Post(Notification notification) + { + State = Visibility.Visible; + + ++runningDepth; + notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth; + + var hasCompletionTarget = notification as IHasCompletionTarget; + if (hasCompletionTarget != null) + hasCompletionTarget.CompletionTarget = Post; + + var ourType = notification.GetType(); + sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => ourType == accept || ourType.IsSubclassOf(accept)))?.Add(notification); + } + + protected override void PopIn() + { + base.PopIn(); + + scrollContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint); + MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint); + FadeTo(1, TRANSITION_LENGTH / 2); + } + + private void markAllRead() + { + sections.Children.ForEach(s => s.MarkAllRead()); + } + + protected override void PopOut() + { + base.PopOut(); + + markAllRead(); + + MoveToX(width, TRANSITION_LENGTH, EasingTypes.OutQuint); + FadeTo(0, TRANSITION_LENGTH / 2); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs b/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs new file mode 100644 index 0000000000..7fba75c4fc --- /dev/null +++ b/osu.Game/Overlays/Notifications/IHasCompletionTarget.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Overlays.Notifications +{ + public interface IHasCompletionTarget + { + Action<Notification> CompletionTarget { get; set; } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs new file mode 100644 index 0000000000..6faa79433e --- /dev/null +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -0,0 +1,284 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Notifications +{ + public abstract class Notification : Container + { + /// <summary> + /// Use requested close. + /// </summary> + public Action Closed; + + /// <summary> + /// Run on user activating the notification. Return true to close. + /// </summary> + public Func<bool> Activated; + + /// <summary> + /// Should we show at the top of our section on display? + /// </summary> + public virtual bool DisplayOnTop => true; + + protected NotificationLight Light; + private CloseButton closeButton; + protected Container IconContent; + private Container content; + + protected override Container<Drawable> Content => content; + + protected Container NotificationContent; + + private bool read; + + public virtual bool Read + { + get { return read; } + set + { + read = value; + } + } + + public Notification() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + AddInternal(new Drawable[] + { + Light = new NotificationLight + { + Margin = new MarginPadding { Right = 5 }, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreRight, + }, + NotificationContent = new Container + { + CornerRadius = 8, + Masking = true, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Padding = new MarginPadding(5), + Height = 60, + Children = new Drawable[] + { + IconContent = new Container + { + Size = new Vector2(40), + Colour = Color4.DarkGray, + Masking = true, + CornerRadius = 5, + }, + content = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding + { + Top = 5, + Left = 45, + Right = 30 + }, + } + } + }, + closeButton = new CloseButton + { + Alpha = 0, + Action = Close, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding + { + Right = 5 + }, + } + } + } + }); + } + + protected override bool OnHover(InputState state) + { + closeButton.FadeIn(75); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + closeButton.FadeOut(75); + base.OnHoverLost(state); + } + + protected override bool OnClick(InputState state) + { + if (Activated?.Invoke() ?? true) + Close(); + + return true; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + FadeInFromZero(200); + NotificationContent.MoveToX(DrawSize.X); + NotificationContent.MoveToX(0, 500, EasingTypes.OutQuint); + } + + private bool wasClosed; + + public virtual void Close() + { + if (wasClosed) return; + wasClosed = true; + + Closed?.Invoke(); + FadeOut(100); + Expire(); + } + + class CloseButton : ClickableContainer + { + private Color4 hoverColour; + + public CloseButton() + { + Colour = OsuColour.Gray(0.2f); + AutoSizeAxes = Axes.Both; + + Children = new[] + { + new TextAwesome + { + Anchor = Anchor.Centre, + Icon = FontAwesome.fa_times_circle, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverColour = colours.Yellow; + } + + protected override bool OnHover(InputState state) + { + FadeColour(hoverColour, 200); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + FadeColour(OsuColour.Gray(0.2f), 200); + base.OnHoverLost(state); + } + } + + public class NotificationLight : Container + { + private bool pulsate; + private Container pulsateLayer; + + public bool Pulsate + { + get { return pulsate; } + set + { + pulsate = value; + + pulsateLayer.ClearTransformations(); + pulsateLayer.Alpha = 1; + + if (pulsate) + { + const float length = 1000; + pulsateLayer.Transforms.Add(new TransformAlpha + { + StartTime = Time.Current, + EndTime = Time.Current + length, + StartValue = 1, + EndValue = 0.4f, + Easing = EasingTypes.In + }); + pulsateLayer.Transforms.Add(new TransformAlpha + { + StartTime = Time.Current + length, + EndTime = Time.Current + length * 2, + StartValue = 0.4f, + EndValue = 1, + Easing = EasingTypes.Out + }); + + //todo: figure why we can't add arbitrary delays at the end of loop. + pulsateLayer.Loop(length * 2); + } + } + } + + public new SRGBColour Colour + { + set + { + base.Colour = value; + pulsateLayer.EdgeEffect = new EdgeEffect + { + Colour = ((Color4)value).Opacity(0.5f), //todo: avoid cast + Type = EdgeEffectType.Glow, + Radius = 12, + Roundness = 12, + }; + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Size = new Vector2(6, 15); + + Children = new[] + { + pulsateLayer = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Masking = true, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + }, + } + } + }; + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs new file mode 100644 index 0000000000..ec286302b8 --- /dev/null +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -0,0 +1,162 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Transformations; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using OpenTK; + +namespace osu.Game.Overlays.Notifications +{ + public class NotificationSection : FlowContainer + { + private OsuSpriteText titleText; + private OsuSpriteText countText; + + private ClearAllButton clearButton; + + private FlowContainer<Notification> notifications; + + public void Add(Notification notification) + { + notifications.Add(notification); + } + + public IEnumerable<Type> AcceptTypes; + + private string clearText; + public string ClearText + { + get { return clearText; } + set + { + clearText = value; + if (clearButton != null) clearButton.Text = clearText; + } + } + + private string title; + + public string Title + { + get { return title; } + set + { + title = value; + if (titleText != null) titleText.Text = title.ToUpper(); + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FlowDirection.VerticalOnly; + + Padding = new MarginPadding + { + Top = 10, + Bottom = 5, + Right = 20, + Left = 20, + }; + + AddInternal(new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + clearButton = new ClearAllButton + { + Text = clearText, + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Action = clearAll + }, + new FlowContainer + { + Margin = new MarginPadding + { + Bottom = 5 + }, + Spacing = new Vector2(5, 0), + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + titleText = new OsuSpriteText + { + Text = title.ToUpper(), + Font = @"Exo2.0-Black", + }, + countText = new OsuSpriteText + { + Text = "3", + Colour = colours.Yellow, + Font = @"Exo2.0-Black", + }, + } + }, + }, + }, + notifications = new FlowContainer<Notification> + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + LayoutDuration = 150, + LayoutEasing = EasingTypes.OutQuart, + Spacing = new Vector2(3), + } + }); + } + + private void clearAll() + { + notifications.Children.ForEach(c => c.Close()); + } + + protected override void Update() + { + base.Update(); + + countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString(); + } + + class ClearAllButton : ClickableContainer + { + private OsuSpriteText text; + + public ClearAllButton() + { + AutoSizeAxes = Axes.Both; + + Children = new[] + { + text = new OsuSpriteText() + }; + } + + public string Text + { + get { return text.Text; } + set { text.Text = value.ToUpper(); } + } + } + + public void MarkAllRead() + { + notifications.Children.ForEach(n => n.Read = true); + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs b/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs new file mode 100644 index 0000000000..a5ec9a3545 --- /dev/null +++ b/osu.Game/Overlays/Notifications/ProgressCompletionNotification.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Notifications +{ + public class ProgressCompletionNotification : SimpleNotification + { + private ProgressNotification progressNotification; + + public ProgressCompletionNotification(ProgressNotification progressNotification) + { + this.progressNotification = progressNotification; + Icon = FontAwesome.fa_check; + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs new file mode 100644 index 0000000000..8b0fc27d36 --- /dev/null +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -0,0 +1,231 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Diagnostics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Notifications +{ + public class ProgressNotification : Notification, IHasCompletionTarget + { + public string Text + { + get { return textDrawable.Text; } + set + { + textDrawable.Text = value; + } + } + + public float Progress + { + get { return progressBar.Progress; } + set + { + progressBar.Progress = value; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + //we may have received changes before we were displayed. + State = state; + } + + public virtual ProgressNotificationState State + { + get { return state; } + set + { + bool stateChanged = state != value; + state = value; + + if (IsLoaded) + { + switch (state) + { + case ProgressNotificationState.Queued: + Light.Colour = colourQueued; + Light.Pulsate = false; + progressBar.Active = false; + break; + case ProgressNotificationState.Active: + Light.Colour = colourActive; + Light.Pulsate = true; + progressBar.Active = true; + break; + case ProgressNotificationState.Cancelled: + Light.Colour = colourCancelled; + Light.Pulsate = false; + progressBar.Active = false; + break; + } + } + + if (stateChanged) + { + switch (state) + { + case ProgressNotificationState.Completed: + NotificationContent.MoveToY(-DrawSize.Y / 2, 200, EasingTypes.OutQuint); + FadeTo(0.01f, 200); //don't completely fade out or our scheduled task won't run. + + Delay(100); + Schedule(Completed); + break; + } + } + } + } + + private ProgressNotificationState state; + + protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification(this) + { + Activated = CompletionClickAction, + Text = $"Task \"{Text}\" has completed!" + }; + + protected virtual void Completed() + { + Expire(); + CompletionTarget?.Invoke(CreateCompletionNotification()); + } + + public override bool DisplayOnTop => false; + + private ProgressBar progressBar; + private Color4 colourQueued; + private Color4 colourActive; + private Color4 colourCancelled; + + private SpriteText textDrawable; + + public ProgressNotification() + { + IconContent.Add(new Box + { + RelativeSizeAxes = Axes.Both, + }); + + Content.Add(textDrawable = new SpriteText + { + TextSize = 16, + Colour = OsuColour.Gray(128), + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + }); + + NotificationContent.Add(progressBar = new ProgressBar + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + }); + + State = ProgressNotificationState.Queued; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + colourQueued = colours.YellowDark; + colourActive = colours.Blue; + colourCancelled = colours.Red; + } + + public override void Close() + { + switch (State) + { + case ProgressNotificationState.Cancelled: + base.Close(); + break; + case ProgressNotificationState.Active: + case ProgressNotificationState.Queued: + State = ProgressNotificationState.Cancelled; + break; + } + } + + /// <summary> + /// The function to post completion notifications back to. + /// </summary> + public Action<Notification> CompletionTarget { get; set; } + + /// <summary> + /// An action to complete when the completion notification is clicked. + /// </summary> + public Func<bool> CompletionClickAction; + + class ProgressBar : Container + { + private Box box; + + private Color4 colourActive; + private Color4 colourInactive; + + private float progress; + public float Progress + { + get { return progress; } + set + { + if (progress == value) return; + + progress = value; + box.ResizeTo(new Vector2(progress, 1), 100, EasingTypes.OutQuad); + } + } + + private bool active; + + public bool Active + { + get { return active; } + set + { + active = value; + FadeColour(active ? colourActive : colourInactive, 100); + } + } + + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + colourActive = colours.Blue; + Colour = colourInactive = OsuColour.Gray(0.5f); + + Height = 5; + + Children = new[] + { + box = new Box + { + RelativeSizeAxes = Axes.Both, + Width = 0, + } + }; + } + } + } + + public enum ProgressNotificationState + { + Queued, + Active, + Completed, + Cancelled + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs new file mode 100644 index 0000000000..cf16978c3e --- /dev/null +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Notifications +{ + public class SimpleNotification : Notification + { + private string text; + public string Text + { + get { return text; } + set + { + text = value; + textDrawable.Text = text; + } + } + + private FontAwesome icon = FontAwesome.fa_info_circle; + public FontAwesome Icon + { + get { return icon; } + set + { + icon = value; + iconDrawable.Icon = icon; + } + } + + private SpriteText textDrawable; + private TextAwesome iconDrawable; + + public SimpleNotification() + { + IconContent.Add(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + ColourInfo = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.5f)) + }, + iconDrawable = new TextAwesome + { + Anchor = Anchor.Centre, + Icon = icon , + } + }); + + Content.Add(textDrawable = new SpriteText + { + TextSize = 16, + Colour = OsuColour.Gray(128), + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Text = text + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Light.Colour = colours.Green; + } + + public override bool Read + { + get + { + return base.Read; + } + + set + { + if (base.Read = value) + Light.FadeOut(100); + else + Light.FadeIn(100); + } + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Options/OptionDropDown.cs b/osu.Game/Overlays/Options/OptionDropDown.cs index ee355d4016..b9d676109e 100644 --- a/osu.Game/Overlays/Options/OptionDropDown.cs +++ b/osu.Game/Overlays/Options/OptionDropDown.cs @@ -71,7 +71,14 @@ namespace osu.Game.Overlays.Options { items = value; if(dropdown != null) + { dropdown.Items = value; + + // We need to refresh the dropdown because our items changed, + // thus its selected value may be outdated. + if (bindable != null) + dropdown.SelectedValue = bindable.Value; + } } } diff --git a/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs b/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs index 370676ead6..06ddf584b2 100644 --- a/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs +++ b/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs @@ -14,6 +14,7 @@ namespace osu.Game.Overlays.Options.Sections.Audio protected override string Header => "Devices"; private AudioManager audio; + private OptionDropDown<string> dropdown; [BackgroundDependencyLoader] private void load(AudioManager audio) @@ -21,21 +22,45 @@ namespace osu.Game.Overlays.Options.Sections.Audio this.audio = audio; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + audio.OnNewDevice -= onDeviceChanged; + audio.OnLostDevice -= onDeviceChanged; + } + + private void updateItems() + { + var deviceItems = new List<KeyValuePair<string, string>>(); + deviceItems.Add(new KeyValuePair<string, string>("Default", string.Empty)); + deviceItems.AddRange(audio.AudioDeviceNames.Select(d => new KeyValuePair<string, string>(d, d))); + + var preferredDeviceName = audio.AudioDevice.Value; + if (!deviceItems.Any(kv => kv.Value == preferredDeviceName)) + deviceItems.Add(new KeyValuePair<string, string>(preferredDeviceName, preferredDeviceName)); + + dropdown.Items = deviceItems; + } + + private void onDeviceChanged(string name) => updateItems(); + protected override void LoadComplete() { base.LoadComplete(); - var deviceItems = new List<KeyValuePair<string, string>>(); - deviceItems.Add(new KeyValuePair<string, string>("Default", string.Empty)); - deviceItems.AddRange(audio.GetDeviceNames().Select(d => new KeyValuePair<string, string>(d, d))); Children = new Drawable[] { - new OptionDropDown<string>() + dropdown = new OptionDropDown<string>() { - Items = deviceItems, Bindable = audio.AudioDevice }, }; + + updateItems(); + + audio.OnNewDevice += onDeviceChanged; + audio.OnLostDevice += onDeviceChanged; } } } \ No newline at end of file diff --git a/osu.Game/Overlays/OptionsOverlay.cs b/osu.Game/Overlays/OptionsOverlay.cs index 1fce450104..a93bbc10d3 100644 --- a/osu.Game/Overlays/OptionsOverlay.cs +++ b/osu.Game/Overlays/OptionsOverlay.cs @@ -90,7 +90,7 @@ namespace osu.Game.Overlays { Text = "settings", TextSize = 40, - Margin = new MarginPadding { Left = CONTENT_MARGINS, Top = 30 }, + Margin = new MarginPadding { Left = CONTENT_MARGINS, Top = Toolbar.Toolbar.TOOLTIP_HEIGHT }, }, new OsuSpriteText { diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 503a3e0bf5..dc8b8d1e9a 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -17,6 +17,7 @@ namespace osu.Game.Overlays.Toolbar public class Toolbar : OverlayContainer { public const float HEIGHT = 40; + public const float TOOLTIP_HEIGHT = 30; public Action OnHome; public Action<PlayMode> OnPlayModeChange; @@ -73,10 +74,7 @@ namespace osu.Game.Overlays.Toolbar Icon = FontAwesome.fa_search }, userArea = new ToolbarUserArea(), - new ToolbarButton - { - Icon = FontAwesome.fa_bars - }, + new ToolbarNotificationButton(), } } }; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 160e4460d9..67b0039971 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -56,6 +56,8 @@ namespace osu.Game.Overlays.Toolbar } } + protected virtual Anchor TooltipAnchor => Anchor.TopLeft; + public Action Action; protected TextAwesome DrawableIcon; protected SpriteText DrawableText; @@ -107,19 +109,24 @@ namespace osu.Game.Overlays.Toolbar { Direction = FlowDirection.VerticalOnly, RelativeSizeAxes = Axes.Both, //stops us being considered in parent's autosize - Anchor = Anchor.BottomLeft, - Position = new Vector2(5, 5), + Anchor = (TooltipAnchor & Anchor.x0) > 0 ? Anchor.BottomLeft : Anchor.BottomRight, + Origin = TooltipAnchor, + Position = new Vector2((TooltipAnchor & Anchor.x0) > 0 ? 5 : -5, 5), Alpha = 0, Children = new[] { tooltip1 = new OsuSpriteText { + Anchor = TooltipAnchor, + Origin = TooltipAnchor, Shadow = true, TextSize = 22, Font = @"Exo2.0-Bold", }, tooltip2 = new OsuSpriteText { + Anchor = TooltipAnchor, + Origin = TooltipAnchor, Shadow = true, TextSize = 16 } diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs new file mode 100644 index 0000000000..973f9f2d8a --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Toolbar +{ + class ToolbarNotificationButton : ToolbarOverlayToggleButton + { + protected override Anchor TooltipAnchor => Anchor.TopRight; + + public ToolbarNotificationButton() + { + Icon = FontAwesome.fa_bars; + TooltipMain = "Notifications"; + TooltipSub = "Waiting for 'ya"; + } + + [BackgroundDependencyLoader] + private void load(NotificationManager notificationManager) + { + StateContainer = notificationManager; + Action = notificationManager.ToggleVisibility; + } + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Backgrounds/BackgroundModeBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundModeBeatmap.cs index 61b3a89ca0..65b50542ce 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundModeBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundModeBeatmap.cs @@ -84,7 +84,7 @@ namespace osu.Game.Screens.Backgrounds [BackgroundDependencyLoader] private void load() { - Sprite.Texture = beatmap.Background; + Sprite.Texture = beatmap?.Background; } } } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index a76cdbe99b..6d720569df 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -119,11 +119,11 @@ namespace osu.Game.Screens.Menu buttonFlow.Add(buttonsTopLevel); } - [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuGame game) + [BackgroundDependencyLoader(true)] + private void load(AudioManager audio, OsuGame game = null) { sampleOsuClick = audio.Sample.Get(@"Menu/menuhit"); - toolbar = game.Toolbar; + toolbar = game?.Toolbar; } protected override void LoadComplete() diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2ead6aad48..4cbc229596 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,6 +23,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -31,6 +32,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <TreatWarningsAsErrors>false</TreatWarningsAsErrors> </PropertyGroup> <ItemGroup> <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> @@ -100,6 +102,13 @@ <Compile Include="Beatmaps\Timing\SampleChange.cs" /> <Compile Include="Beatmaps\Timing\TimingChange.cs" /> <Compile Include="Configuration\OsuConfigManager.cs" /> + <Compile Include="Overlays\Notifications\IHasCompletionTarget.cs" /> + <Compile Include="Overlays\Notifications\Notification.cs" /> + <Compile Include="Overlays\NotificationManager.cs" /> + <Compile Include="Overlays\Notifications\NotificationSection.cs" /> + <Compile Include="Overlays\Notifications\ProgressCompletionNotification.cs" /> + <Compile Include="Overlays\Notifications\ProgressNotification.cs" /> + <Compile Include="Overlays\Notifications\SimpleNotification.cs" /> <Compile Include="Overlays\Options\OptionDropDown.cs" /> <Compile Include="Overlays\Options\OptionLabel.cs" /> <Compile Include="Graphics\UserInterface\OsuDropDownHeader.cs" /> @@ -107,6 +116,7 @@ <Compile Include="Graphics\UserInterface\OsuDropDownMenuItem.cs" /> <Compile Include="Overlays\Toolbar\ToolbarHomeButton.cs" /> <Compile Include="Overlays\Toolbar\ToolbarMusicButton.cs" /> + <Compile Include="Overlays\Toolbar\ToolbarNotificationButton.cs" /> <Compile Include="Overlays\Toolbar\ToolbarSettingsButton.cs" /> <Compile Include="Overlays\Toolbar\ToolbarOverlayToggleButton.cs" /> <Compile Include="Overlays\Toolbar\ToolbarUserArea.cs" /> diff --git a/osu.sln b/osu.sln index f3736c16c0..588cabf6b6 100644 --- a/osu.sln +++ b/osu.sln @@ -29,6 +29,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Modes.Taiko", "osu EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Modes.Mania", "osu.Game.Modes.Mania\osu.Game.Modes.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Desktop.Tests", "osu.Desktop.Tests\osu.Desktop.Tests.csproj", "{230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -79,6 +81,10 @@ Global {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -95,6 +101,7 @@ Global {58F6C80C-1253-4A0E-A465-B8C85EBEADF3} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} {F167E17A-7DE6-4AF5-B920-A5112296C695} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} {48F4582B-7687-4621-9CBE-5C24197CB536} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} + {230AC4F3-7783-49FB-9AEC-B83CDA3B9F3D} = {0D37A2AD-80A4-464F-A1DE-1560B70F1CE3} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0