diff --git a/PpCalculator/PpCalculator.cs b/PpCalculator/PpCalculator.cs index be66a458..53864f96 100644 --- a/PpCalculator/PpCalculator.cs +++ b/PpCalculator/PpCalculator.cs @@ -25,7 +25,7 @@ public abstract class PpCalculator public virtual int Score { get; set; } - private string[] _Mods { get; set; } = { "--" }; + private string[] _Mods { get; set; } = null; public virtual string[] Mods { @@ -57,6 +57,26 @@ public void PreProcess(ProcessorWorkingBeatmap workingBeatmap) protected string LastMods { get; set; } = null; protected bool ResetPerformanceCalculator { get; set; } + public double Calculate(double startTime, double endTime = double.NaN, Dictionary categoryAttribs = null) + { + + var orginalWorkingBeatmap = WorkingBeatmap; + var tempMap = new Beatmap(); + tempMap.HitObjects.AddRange(WorkingBeatmap.Beatmap.HitObjects.Where(h => h.StartTime >= startTime && h.StartTime <= endTime)); + tempMap.ControlPointInfo = WorkingBeatmap.Beatmap.ControlPointInfo; + tempMap.BeatmapInfo = WorkingBeatmap.BeatmapInfo; + + WorkingBeatmap = new ProcessorWorkingBeatmap(tempMap); + + ResetPerformanceCalculator = true; + var result = Calculate(null, categoryAttribs); + + WorkingBeatmap = orginalWorkingBeatmap; + + ResetPerformanceCalculator = true; + + return result; + } public double Calculate(double? time = null, Dictionary categoryAttribs = null) { @@ -107,7 +127,16 @@ public double Calculate(double? time = null, Dictionary category if (time.HasValue) pp = PerformanceCalculator.Calculate(time.Value, categoryAttribs); else - pp = PerformanceCalculator.Calculate(categoryAttribs); + { + try + { + pp = PerformanceCalculator.Calculate(categoryAttribs); + } + catch (InvalidOperationException) + { + pp = -1; + } + } return pp; } diff --git a/PpCalculator/ProcessorWorkingBeatmap.cs b/PpCalculator/ProcessorWorkingBeatmap.cs index d4d0ffce..9b6b3094 100644 --- a/PpCalculator/ProcessorWorkingBeatmap.cs +++ b/PpCalculator/ProcessorWorkingBeatmap.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.IO; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; @@ -12,6 +10,9 @@ using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Taiko; +using System; +using System.IO; +using System.Linq; namespace PpCalculator { @@ -22,6 +23,9 @@ public class ProcessorWorkingBeatmap : WorkingBeatmap { private readonly Beatmap beatmap; public int RulesetID => beatmap.BeatmapInfo.RulesetID; + public double Length => beatmap.HitObjects.Any() ? beatmap.HitObjects.Last().StartTime : 0; + + public string BackgroundFile => beatmap.Metadata.BackgroundFile; /// /// Constructs a new from a .osu file. /// @@ -32,7 +36,7 @@ public ProcessorWorkingBeatmap(string file, int? beatmapId = null) { } - private ProcessorWorkingBeatmap(Beatmap beatmap, int? beatmapId = null) + internal ProcessorWorkingBeatmap(Beatmap beatmap, int? beatmapId = null) : base(beatmap.BeatmapInfo) { this.beatmap = beatmap; diff --git a/plugins/BeatmapPpReplacements/BeatmapHelpers.cs b/StreamCompanionTypes/BeatmapHelpers.cs similarity index 63% rename from plugins/BeatmapPpReplacements/BeatmapHelpers.cs rename to StreamCompanionTypes/BeatmapHelpers.cs index 494de5bc..0da1efd0 100644 --- a/plugins/BeatmapPpReplacements/BeatmapHelpers.cs +++ b/StreamCompanionTypes/BeatmapHelpers.cs @@ -1,11 +1,8 @@ -using System.Diagnostics; -using System.IO; -using CollectionManager.DataTypes; -using StreamCompanionTypes; +using System.IO; +using StreamCompanionTypes.DataTypes; using StreamCompanionTypes.Interfaces; -using Beatmap = StreamCompanionTypes.DataTypes.Beatmap; -namespace BeatmapPpReplacements +namespace StreamCompanionTypes { public static class BeatmapHelpers { @@ -32,5 +29,20 @@ public static string GetFullSongsLocation(ISettingsHandler settings) } return dir; } + + public static string FullOsuFileLocation(this Beatmap beatmap, ISettingsHandler settings) + { + return beatmap.FullOsuFileLocation(GetFullSongsLocation(settings)); + } + + public static bool IsValidBeatmap(this Beatmap beatmap, ISettingsHandler settings, out string fullFileLocation) + { + fullFileLocation = beatmap.FullOsuFileLocation(settings); + + if (!File.Exists(fullFileLocation)) return false; + FileInfo file = new FileInfo(fullFileLocation); + + return file.Length != 0; + } } } \ No newline at end of file diff --git a/StreamCompanionTypes/DataTypes/Token.cs b/StreamCompanionTypes/DataTypes/Token.cs index 5c361844..75b5b79f 100644 --- a/StreamCompanionTypes/DataTypes/Token.cs +++ b/StreamCompanionTypes/DataTypes/Token.cs @@ -26,8 +26,11 @@ public Token Clone() public class TokenWithFormat : Token { - public TokenWithFormat(object value, TokenType type = TokenType.Normal, string format = null) : base(value, type) + private readonly object _defaultValue; + + public TokenWithFormat(object value, TokenType type = TokenType.Normal, string format = null, object defaultValue = null) : base(value, type) { + _defaultValue = defaultValue; Format = format; Value = base.Value; } @@ -49,6 +52,10 @@ public TokenWithFormat(object value, TokenType type = TokenType.Normal, string f } } + public void Reset() + { + Value = _defaultValue; + } public string Format { get; set; } public string FormatedValue { get; set; } } diff --git a/StreamCompanionTypes/StreamCompanionTypes.csproj b/StreamCompanionTypes/StreamCompanionTypes.csproj index 8b21369e..034d5eed 100644 --- a/StreamCompanionTypes/StreamCompanionTypes.csproj +++ b/StreamCompanionTypes/StreamCompanionTypes.csproj @@ -54,6 +54,7 @@ + diff --git a/osu!StreamCompanion.sln b/osu!StreamCompanion.sln index ef9d54a1..8bb0767f 100644 --- a/osu!StreamCompanion.sln +++ b/osu!StreamCompanion.sln @@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "submodules\osu\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{330603A4-F36B-4073-82D3-41A95A6D35B2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiveVisualizer", "plugins\LiveVisualizer\LiveVisualizer.csproj", "{6FB20D7D-F84A-41DE-B086-DA920C7B5E86}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -381,6 +383,18 @@ Global {330603A4-F36B-4073-82D3-41A95A6D35B2}.Remote debug|Any CPU.Build.0 = Release|Any CPU {330603A4-F36B-4073-82D3-41A95A6D35B2}.Remote debug|x86.ActiveCfg = Release|Any CPU {330603A4-F36B-4073-82D3-41A95A6D35B2}.Remote debug|x86.Build.0 = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Debug|x86.ActiveCfg = Debug|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Debug|x86.Build.0 = Debug|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Release|Any CPU.Build.0 = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Release|x86.ActiveCfg = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Release|x86.Build.0 = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Remote debug|Any CPU.ActiveCfg = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Remote debug|Any CPU.Build.0 = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Remote debug|x86.ActiveCfg = Release|Any CPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86}.Remote debug|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -409,6 +423,7 @@ Global {4CEEFDDB-FBDE-4E4A-BD01-019AD6E16EDF} = {DDE5F7B5-68C3-4143-8726-0BB91B83EED3} {A86B0A57-2968-460A-982D-4251FF02498A} = {DDE5F7B5-68C3-4143-8726-0BB91B83EED3} {330603A4-F36B-4073-82D3-41A95A6D35B2} = {DDE5F7B5-68C3-4143-8726-0BB91B83EED3} + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86} = {7357CB17-E9D7-493F-BC1B-354F717D7AA3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {90CD718D-2503-4BBC-867D-E8EF9C9DBC77} diff --git a/osu!StreamCompanion.sln.DotSettings b/osu!StreamCompanion.sln.DotSettings index 601ec126..a2dd1d00 100644 --- a/osu!StreamCompanion.sln.DotSettings +++ b/osu!StreamCompanion.sln.DotSettings @@ -1,2 +1,3 @@  + True True \ No newline at end of file diff --git a/osu!StreamCompanion/Program.cs b/osu!StreamCompanion/Program.cs index 41ecb3b4..4c8874ff 100644 --- a/osu!StreamCompanion/Program.cs +++ b/osu!StreamCompanion/Program.cs @@ -17,7 +17,7 @@ namespace osu_StreamCompanion { static class Program { - public static string ScVersion ="v180629.20"; + public static string ScVersion ="v190301.18"; private static Initializer _initializer; /// /// The main entry point for the application. diff --git a/plugins/BeatmapPpReplacements/BeatmapPpReplacements.csproj b/plugins/BeatmapPpReplacements/BeatmapPpReplacements.csproj index fa032480..e798faff 100644 --- a/plugins/BeatmapPpReplacements/BeatmapPpReplacements.csproj +++ b/plugins/BeatmapPpReplacements/BeatmapPpReplacements.csproj @@ -47,7 +47,6 @@ - diff --git a/plugins/BeatmapPpReplacements/PpReplacements.cs b/plugins/BeatmapPpReplacements/PpReplacements.cs index 0ba3d732..13fb5fd1 100644 --- a/plugins/BeatmapPpReplacements/PpReplacements.cs +++ b/plugins/BeatmapPpReplacements/PpReplacements.cs @@ -5,6 +5,7 @@ using StreamCompanionTypes.DataTypes; using StreamCompanionTypes.Interfaces; using System; +using System.Collections.Generic; using System.IO; namespace BeatmapPpReplacements @@ -75,13 +76,11 @@ public Tokens GetMapReplacements(MapSearchResult map) {"m600 000PP", new Token(null)}, }; - if (!map.FoundBeatmaps) return ret; - var mapLocation = map.BeatmapsFound[0].FullOsuFileLocation(BeatmapHelpers.GetFullSongsLocation(_settings)); - - if (!File.Exists(mapLocation)) return ret; - FileInfo file = new FileInfo(mapLocation); - - if (file.Length == 0) return ret; + if (!map.FoundBeatmaps || + !map.BeatmapsFound[0].IsValidBeatmap(_settings, out var mapLocation) + ) + return ret; + var workingBeatmap = new ProcessorWorkingBeatmap(mapLocation); @@ -104,6 +103,7 @@ public Tokens GetMapReplacements(MapSearchResult map) Mods mods = Mods.Omod; + if (playMode == PlayMode.OsuMania) { ret["1 000 000PP"] = new TokenWithFormat(GetPp(_ppCalculator, 0, mods, 1_000_000), format: PpFormat); @@ -123,7 +123,7 @@ public Tokens GetMapReplacements(MapSearchResult map) ret["95PP"] = new TokenWithFormat(GetPp(_ppCalculator, 95d), format: PpFormat); ret["90PP"] = new TokenWithFormat(GetPp(_ppCalculator, 90d), format: PpFormat); } - + ret["MaxCombo"] = new TokenWithFormat(_ppCalculator.GetMaxCombo()); string modsStr; diff --git a/plugins/LiveVisualizer/Chart.xaml b/plugins/LiveVisualizer/Chart.xaml new file mode 100644 index 00000000..e5faa2fe --- /dev/null +++ b/plugins/LiveVisualizer/Chart.xaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/LiveVisualizer/Chart.xaml.cs b/plugins/LiveVisualizer/Chart.xaml.cs new file mode 100644 index 00000000..36708913 --- /dev/null +++ b/plugins/LiveVisualizer/Chart.xaml.cs @@ -0,0 +1,39 @@ +using LiveCharts; +using LiveCharts.Helpers; +using LiveCharts.Wpf; +using LiveVisualizer.Annotations; +using System; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Windows.Controls; +using System.Windows.Data; +using Separator = LiveCharts.Wpf.Separator; + +namespace LiveVisualizer +{ + /// + /// Interaction logic for Chart.xaml + /// + public partial class Chart : UserControl + { + public Chart(IWpfVisualizerData data, string bindingName, bool isProgressChart) + { + InitializeComponent(); + + var binding = new Binding(bindingName); + binding.Source = data; + LineSeries.SetBinding(Series.FillProperty, binding); + + if (isProgressChart) + { //Disable drawing of AxisY separator + binding = new Binding(); + AxisYSeparator.SetBinding(Separator.IsEnabledProperty, binding); + AxisYSeparator.SetValue(Separator.StrokeThicknessProperty, 0d); + } + + DataContext = data; + } + + } +} diff --git a/plugins/LiveVisualizer/ColorHelpers.cs b/plugins/LiveVisualizer/ColorHelpers.cs new file mode 100644 index 00000000..35aaf062 --- /dev/null +++ b/plugins/LiveVisualizer/ColorHelpers.cs @@ -0,0 +1,34 @@ +using StreamCompanionTypes.DataTypes; +using StreamCompanionTypes.Interfaces; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +namespace LiveVisualizer +{ + internal static class ColorHelpers + { + public static string GetArgbColor(ISettingsHandler settings, ConfigEntry entry) + => string.Join("", GetArgbColorList(settings, entry).Select(n => n.ToString("X2"))); + + public static List GetArgbColorList(ISettingsHandler settings, ConfigEntry entry) + => settings.Get(entry).Split(new[] { ';' }, 4).Select(v => int.TryParse(v, out int num) ? num : 0).ToList(); + + public static Color GetColor(ISettingsHandler settings, ConfigEntry entry) + { + var argb = GetArgbColorList(settings, entry); + + return Color.FromArgb(argb[0], argb[1], argb[2], argb[3]); + } + + public static void SaveColor(ISettingsHandler settings, ConfigEntry entry, Color color) + { + var colors = BitConverter.GetBytes(color.ToArgb()) + .Reverse() + .ToList(); + + settings.Add(entry.Name, string.Join(";", colors.Select(v => v.ToString())), true); + } + } +} \ No newline at end of file diff --git a/plugins/LiveVisualizer/ColorPickerWithPreview.Designer.cs b/plugins/LiveVisualizer/ColorPickerWithPreview.Designer.cs new file mode 100644 index 00000000..eda0af18 --- /dev/null +++ b/plugins/LiveVisualizer/ColorPickerWithPreview.Designer.cs @@ -0,0 +1,115 @@ +namespace LiveVisualizer +{ + partial class ColorPickerWithPreview + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel_colorPreview = new System.Windows.Forms.Panel(); + this.Label = new System.Windows.Forms.Label(); + this.button_change = new System.Windows.Forms.Button(); + this.numericUpDown_alpha = new System.Windows.Forms.NumericUpDown(); + this.label1 = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown_alpha)).BeginInit(); + this.SuspendLayout(); + // + // panel_colorPreview + // + this.panel_colorPreview.BackColor = System.Drawing.SystemColors.ActiveCaption; + this.panel_colorPreview.Cursor = System.Windows.Forms.Cursors.Hand; + this.panel_colorPreview.Location = new System.Drawing.Point(213, 0); + this.panel_colorPreview.Name = "panel_colorPreview"; + this.panel_colorPreview.Size = new System.Drawing.Size(25, 25); + this.panel_colorPreview.TabIndex = 0; + this.panel_colorPreview.Click += new System.EventHandler(this.panel_ColorPreview_Click); + // + // Label + // + this.Label.AutoSize = true; + this.Label.Location = new System.Drawing.Point(3, 6); + this.Label.Name = "Label"; + this.Label.Size = new System.Drawing.Size(64, 13); + this.Label.TabIndex = 1; + this.Label.Text = "Default_text"; + // + // button_change + // + this.button_change.Location = new System.Drawing.Point(132, 1); + this.button_change.Name = "button_change"; + this.button_change.Size = new System.Drawing.Size(75, 23); + this.button_change.TabIndex = 2; + this.button_change.Text = "Change"; + this.button_change.UseVisualStyleBackColor = true; + this.button_change.Click += new System.EventHandler(this.button_Click); + // + // numericUpDown_alpha + // + this.numericUpDown_alpha.Location = new System.Drawing.Point(283, 3); + this.numericUpDown_alpha.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericUpDown_alpha.Name = "numericUpDown_alpha"; + this.numericUpDown_alpha.Size = new System.Drawing.Size(53, 20); + this.numericUpDown_alpha.TabIndex = 3; + this.numericUpDown_alpha.ValueChanged += new System.EventHandler(this.numericUpDown_alpha_ValueChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(242, 7); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(37, 13); + this.label1.TabIndex = 4; + this.label1.Text = "Alpha:"; + // + // ColorPickerWithPreview + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.label1); + this.Controls.Add(this.numericUpDown_alpha); + this.Controls.Add(this.button_change); + this.Controls.Add(this.Label); + this.Controls.Add(this.panel_colorPreview); + this.Name = "ColorPickerWithPreview"; + this.Size = new System.Drawing.Size(507, 26); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDown_alpha)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Panel panel_colorPreview; + private System.Windows.Forms.Button button_change; + public System.Windows.Forms.Label Label; + private System.Windows.Forms.NumericUpDown numericUpDown_alpha; + public System.Windows.Forms.Label label1; + } +} diff --git a/plugins/LiveVisualizer/ColorPickerWithPreview.cs b/plugins/LiveVisualizer/ColorPickerWithPreview.cs new file mode 100644 index 00000000..942ef5c8 --- /dev/null +++ b/plugins/LiveVisualizer/ColorPickerWithPreview.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace LiveVisualizer +{ + public partial class ColorPickerWithPreview : UserControl + { + public event EventHandler ColorChanged; + public Color Color + { + get => panel_colorPreview.BackColor; + set + { + panel_colorPreview.BackColor = value; + numericUpDown_alpha.Value = value.A; + } + } + [ + Category("Appearance"), + Browsable(true), + Description("innerLabel"), + DesignerSerializationVisibility(DesignerSerializationVisibility.Content) + ] + public Label LabelDesigner + { + get => Label; + set => Label = value; + } + + + public ColorPickerWithPreview() + { + InitializeComponent(); + } + + private void button_Click(object sender, EventArgs e) + { + ColorDialog colorDialog = new ColorDialog(); + colorDialog.AllowFullOpen = true; + colorDialog.ShowHelp = true; + colorDialog.Color = Color; + if (colorDialog.ShowDialog() == DialogResult.OK) + { + colorDialog.Color = Color.FromArgb((int)numericUpDown_alpha.Value, colorDialog.Color); + panel_colorPreview.BackColor = colorDialog.Color; + ColorChanged?.Invoke(this, colorDialog.Color); + } + } + + private void panel_ColorPreview_Click(object sender, EventArgs e) + { + button_Click(sender, null); + } + + private void numericUpDown_alpha_ValueChanged(object sender, EventArgs e) + { + Color = Color.FromArgb((int)numericUpDown_alpha.Value, Color); + ColorChanged?.Invoke(this, Color); + } + } +} diff --git a/plugins/LiveVisualizer/ColorPickerWithPreview.resx b/plugins/LiveVisualizer/ColorPickerWithPreview.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/plugins/LiveVisualizer/ColorPickerWithPreview.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/plugins/LiveVisualizer/ConfigEntrys.cs b/plugins/LiveVisualizer/ConfigEntrys.cs new file mode 100644 index 00000000..0966ba53 --- /dev/null +++ b/plugins/LiveVisualizer/ConfigEntrys.cs @@ -0,0 +1,19 @@ +using StreamCompanionTypes.DataTypes; +using System.Net.NetworkInformation; + +namespace LiveVisualizer +{ + internal static class ConfigEntrys + { + public const string Prefix = "LiveVisualizer_"; + public static readonly ConfigEntry Enable = new ConfigEntry($"{Prefix}Enable", true); + public static readonly ConfigEntry Font = new ConfigEntry($"{Prefix}Font", "Arial"); + public static readonly ConfigEntry ChartColor = new ConfigEntry($"{Prefix}ChartColor", "102;255;178;227");//66FFB2E3 + public static readonly ConfigEntry ChartProgressColor = new ConfigEntry($"{Prefix}ChartProgressColor", "178;255;178;227");//B2FFB2E3 + public static readonly ConfigEntry AutoSizeAxisY = new ConfigEntry($"{Prefix}AutoSizeAxisY", true); + public static readonly ConfigEntry ManualAxisCutoffs = new ConfigEntry($"{Prefix}ManualAxisCutoffs", "30;60;100;200;350"); + public static readonly ConfigEntry ShowAxisYSeparator = new ConfigEntry($"{Prefix}ShowAxisYSeparator", true); + public static readonly ConfigEntry AxisYSeparatorColor = new ConfigEntry($"{Prefix}AxisYSeparatorColor", "102;255;178;227"); + + } +} \ No newline at end of file diff --git a/plugins/LiveVisualizer/FodyWeavers.xml b/plugins/LiveVisualizer/FodyWeavers.xml new file mode 100644 index 00000000..4e68ed1a --- /dev/null +++ b/plugins/LiveVisualizer/FodyWeavers.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/plugins/LiveVisualizer/FodyWeavers.xsd b/plugins/LiveVisualizer/FodyWeavers.xsd new file mode 100644 index 00000000..2f1b8aae --- /dev/null +++ b/plugins/LiveVisualizer/FodyWeavers.xsd @@ -0,0 +1,54 @@ + + + + + + + + + + + Used to control if the On_PropertyName_Changed feature is enabled. + + + + + Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. + + + + + Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. + + + + + Used to control if equality checks should use the Equals method resolved from the base class. + + + + + Used to control if equality checks should use the static Equals method resolved from the base class. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/plugins/LiveVisualizer/IVisualizerForm.cs b/plugins/LiveVisualizer/IVisualizerForm.cs new file mode 100644 index 00000000..abf8e408 --- /dev/null +++ b/plugins/LiveVisualizer/IVisualizerForm.cs @@ -0,0 +1,49 @@ +using LiveCharts; +using System; +using System.ComponentModel; + +namespace LiveVisualizer +{ + public interface IVisualizerData + { + ChartValues Strains { get; set; } + + string Title { get; set; } + string Artist { get; set; } + string ImageLocation { get; set; } + + int Hit300 { get; set; } + int Hit100 { get; set; } + int Hit50 { get; set; } + int HitMiss { get; set; } + double Pp { get; set; } + double Acc { get; set; } + + double CurrentTime { get; set; } + double TotalTime { get; set; } + } + + public interface IWpfVisualizerData : IVisualizerData, INotifyPropertyChanged + { + double FontsizeTitle { get; set; } + double FontsizeArtist { get; set; } + + double PixelMapProgress { get; set; } + string ChartColor { get; set; } + string ChartProgressColor { get; set; } + + double MaxYValue { get; set; } + + string FillColor { get; set; } + string Font { get; set; } + + bool ShowAxisYSeparator { get; set; } + string AxisYSeparatorColor { get; set; } + double AxisYStep { get; set; } + } + + public interface IWpfChartConfiguration + { + + } +} \ No newline at end of file diff --git a/plugins/LiveVisualizer/LiveVisualizer.csproj b/plugins/LiveVisualizer/LiveVisualizer.csproj new file mode 100644 index 00000000..e231a714 --- /dev/null +++ b/plugins/LiveVisualizer/LiveVisualizer.csproj @@ -0,0 +1,186 @@ + + + + + + Debug + AnyCPU + {6FB20D7D-F84A-41DE-B086-DA920C7B5E86} + Library + Properties + LiveVisualizer + LiveVisualizer + v4.6.1 + 512 + true + + + + + true + full + false + bin\Debug_temp\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release_temp\ + TRACE + prompt + 4 + + + + ..\..\osu!StreamCompanion\Resources\CollectionManager.dll + + + ..\..\packages\LiveCharts.0.9.7\lib\net45\LiveCharts.dll + + + ..\..\packages\LiveCharts.Wpf.0.9.7\lib\net45\LiveCharts.Wpf.dll + + + + + ..\..\packages\PropertyChanged.Fody.2.6.0\lib\net452\PropertyChanged.dll + + + + + + + + + + + + + + + + + + + + Chart.xaml + + + + UserControl + + + ColorPickerWithPreview.cs + + + + + + + UserControl + + + LiveVisualizerSettings.cs + + + MainWindow.xaml + + + + + True + True + Resources.resx + + + + + + {3d97e9c4-a884-4664-a40c-f19c4533aa00} + PpCalculator + True + + + {2b90289d-8c11-4d91-8a41-c5c99620e537} + StreamCompanionTypes + + + + + ColorPickerWithPreview.cs + + + LiveVisualizerSettings.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + + + + mkdir "$(TargetDir)\..\$(ConfigurationName)" +mkdir "$(TargetDir)\..\$(ConfigurationName)\Dlls" + +copy "$(TargetDir)\FFmpeg.AutoGen.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\FFmpeg.AutoGen.dll" +copy "$(TargetDir)\Humanizer.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\Humanizer.dll" +copy "$(TargetDir)\JetBrains.Annotations.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\JetBrains.Annotations.dll" +copy "$(TargetDir)\ManagedBass.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\ManagedBass.dll" +copy "$(TargetDir)\ManagedBass.Fx.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\ManagedBass.Fx.dll" +copy "$(TargetDir)\Markdig.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\Markdig.dll" +copy "$(TargetDir)\netstandard.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\netstandard.dll" +copy "$(TargetDir)\Newtonsoft.Json.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\Newtonsoft.Json.dll" +copy "$(TargetDir)\nunit.framework.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\nunit.framework.dll" +copy "$(TargetDir)\osu.Framework.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osu.Framework.dll" +copy "$(TargetDir)\osu.Game.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osu.Game.dll" +copy "$(TargetDir)\osu.Game.Rulesets.Catch.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osu.Game.Rulesets.Catch.dll" +copy "$(TargetDir)\osu.Game.Rulesets.Mania.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osu.Game.Rulesets.Mania.dll" +copy "$(TargetDir)\osu.Game.Rulesets.Osu.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osu.Game.Rulesets.Osu.dll" +copy "$(TargetDir)\osu.Game.Rulesets.Taiko.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osu.Game.Rulesets.Taiko.dll" +copy "$(TargetDir)\osuTK.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\osuTK.dll" +copy "$(TargetDir)\PpCalculator.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\PpCalculator.dll" +copy "$(TargetDir)\Remotion.Linq.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\Remotion.Linq.dll" +copy "$(TargetDir)\SQLitePCLRaw.batteries_green.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\SQLitePCLRaw.batteries_green.dll" +copy "$(TargetDir)\SQLitePCLRaw.batteries_v2.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\SQLitePCLRaw.batteries_v2.dll" +copy "$(TargetDir)\SQLitePCLRaw.core.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\SQLitePCLRaw.core.dll" +copy "$(TargetDir)\SQLitePCLRaw.provider.e_sqlite3.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\SQLitePCLRaw.provider.e_sqlite3.dll" +copy "$(TargetDir)\System.Memory.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.Memory.dll" +copy "$(TargetDir)\System.ObjectModel.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.ObjectModel.dll" +copy "$(TargetDir)\System.Runtime.CompilerServices.Unsafe.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.Runtime.CompilerServices.Unsafe.dll" +copy "$(TargetDir)\System.Runtime.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.Runtime.dll" +copy "$(TargetDir)\System.Runtime.InteropServices.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.Runtime.InteropServices.dll" +copy "$(TargetDir)\System.Threading.Tasks.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.Threading.Tasks.dll" +copy "$(TargetDir)\System.ValueTuple.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\System.ValueTuple.dll" + +copy "$(TargetDir)\LiveVisualizer.dll" "$(TargetDir)\..\$(ConfigurationName)\LiveVisualizer.dll" +copy "$(TargetDir)\LiveCharts.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\LiveCharts.dll" +copy "$(TargetDir)\LiveCharts.Wpf.dll" "$(TargetDir)\..\$(ConfigurationName)\Dlls\LiveCharts.Wpf.dll" + +copy "$(TargetDir)\..\$(ConfigurationName)\*" "$(SolutionDir)\build\$(ConfigurationName)\Plugins\" +copy "$(TargetDir)\..\$(ConfigurationName)\Dlls\*" "$(SolutionDir)\build\$(ConfigurationName)\Plugins\Dlls\" + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/plugins/LiveVisualizer/LiveVisualizerPlugin.cs b/plugins/LiveVisualizer/LiveVisualizerPlugin.cs new file mode 100644 index 00000000..91276967 --- /dev/null +++ b/plugins/LiveVisualizer/LiveVisualizerPlugin.cs @@ -0,0 +1,272 @@ +using CollectionManager.Enums; +using LiveCharts.Helpers; +using PpCalculator; +using StreamCompanionTypes; +using StreamCompanionTypes.DataTypes; +using StreamCompanionTypes.Enums; +using StreamCompanionTypes.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace LiveVisualizer +{ + public class LiveVisualizerPlugin : LiveVisualizerPluginBase + { + private IWpfVisualizerData _visualizerData; + private SortedSet ChartCutoffsSet = new SortedSet(); + private MainWindow _visualizerWindow; + + private List> _liveTokens; + private TokenWithFormat _ppToken; + private TokenWithFormat _hit100Token; + private TokenWithFormat _hit50Token; + private TokenWithFormat _hitMissToken; + private TokenWithFormat _timeToken; + private List> LiveTokens + { + get => _liveTokens; + set + { + _ppToken = (TokenWithFormat)value.FirstOrDefault(t => t.Key == "PpIfMapEndsNow").Value; + _hit100Token = (TokenWithFormat)value.FirstOrDefault(t => t.Key == "100").Value; + _hit50Token = (TokenWithFormat)value.FirstOrDefault(t => t.Key == "50").Value; + _hitMissToken = (TokenWithFormat)value.FirstOrDefault(t => t.Key == "miss").Value; + _timeToken = (TokenWithFormat)value.FirstOrDefault(t => t.Key == "time").Value; + + _liveTokens = value; + } + } + + private string _lastMapLocation = string.Empty; + + + public override void Start(ILogger logger) + { + base.Start(logger); + _visualizerData = new VisualizerDataModel(); + + _visualizerData.ChartColor = "#" + ColorHelpers.GetArgbColor(Settings, ConfigEntrys.ChartColor); + _visualizerData.ChartProgressColor = "#" + ColorHelpers.GetArgbColor(Settings, ConfigEntrys.ChartProgressColor); + _visualizerData.AxisYSeparatorColor = "#" + ColorHelpers.GetArgbColor(Settings, ConfigEntrys.AxisYSeparatorColor); + + _visualizerData.ShowAxisYSeparator = Settings.Get(ConfigEntrys.ShowAxisYSeparator); + _visualizerData.Font = Settings.Get(ConfigEntrys.Font); + + foreach (var cutoff in GetManualAxisCutoffs()) + { + ChartCutoffsSet.Add(cutoff); + } + + EnableVisualizer(Settings.Get(ConfigEntrys.Enable)); + + Settings.SettingUpdated += SettingUpdated; + + Task.Run(() => { UpdateLiveTokens(); }); + } + + private void SettingUpdated(object sender, SettingUpdated e) + { + if (e.Name == ConfigEntrys.ChartProgressColor.Name) + _visualizerData.ChartProgressColor = "#" + ColorHelpers.GetArgbColor(Settings, ConfigEntrys.ChartProgressColor); + + else if (e.Name == ConfigEntrys.ChartColor.Name) + _visualizerData.ChartColor = "#" + ColorHelpers.GetArgbColor(Settings, ConfigEntrys.ChartColor); + + else if (e.Name == ConfigEntrys.Font.Name) + _visualizerData.Font = Settings.Get(ConfigEntrys.Font); + + else if (e.Name == ConfigEntrys.Enable.Name) + EnableVisualizer(Settings.Get(ConfigEntrys.Enable)); + + else if (e.Name == ConfigEntrys.ShowAxisYSeparator.Name) + _visualizerData.ShowAxisYSeparator = Settings.Get(ConfigEntrys.ShowAxisYSeparator); + + else if (e.Name == ConfigEntrys.AxisYSeparatorColor.Name) + _visualizerData.AxisYSeparatorColor = "#" + ColorHelpers.GetArgbColor(Settings, ConfigEntrys.AxisYSeparatorColor); + + else if (e.Name == ConfigEntrys.ManualAxisCutoffs.Name || e.Name == ConfigEntrys.AutoSizeAxisY.Name) + { + ChartCutoffsSet.Clear(); + + foreach (var cutoff in GetManualAxisCutoffs()) + { + ChartCutoffsSet.Add(cutoff); + } + + SetAxisValues(); + } + } + + private IEnumerable GetManualAxisCutoffs() + => Settings.Get(ConfigEntrys.ManualAxisCutoffs).Split(';').Select(v => int.TryParse(v, out int num) ? num : 0); + + private void EnableVisualizer(bool enable) + { + if (enable) + { + if (_visualizerWindow == null) + _visualizerWindow = new MainWindow(_visualizerData); + + _visualizerWindow.Show(); + } + else + { + _visualizerWindow?.Hide(); + } + } + + + protected override void ProcessNewMap(MapSearchResult mapSearchResult, CancellationToken token) + { + if (_visualizerData == null || + !mapSearchResult.FoundBeatmaps || + !mapSearchResult.BeatmapsFound[0].IsValidBeatmap(Settings, out var mapLocation) || + mapLocation == _lastMapLocation + ) + return; + + var workingBeatmap = new ProcessorWorkingBeatmap(mapLocation); + + var playMode = (PlayMode)PpCalculatorHelpers.GetRulesetId(workingBeatmap.RulesetID, + mapSearchResult.PlayMode.HasValue ? (int?)mapSearchResult.PlayMode : null); + + var ppCalculator = PpCalculatorHelpers.GetPpCalculator((int)playMode, mapLocation, null); + + var strains = new Dictionary(300); + + //Length refers to beatmap time, not song total time + var mapLength = workingBeatmap.Length; + + if (ppCalculator != null && (playMode == PlayMode.Osu || playMode == PlayMode.Taiko)) + { + var strainLength = 5000; + var interval = 1500; + int time = 0; + while (time + strainLength / 2 < mapLength) + { + if (token.IsCancellationRequested) + return; + var a = new Dictionary(); + var strain = ppCalculator.Calculate(time, time + strainLength, a); + + if (double.IsNaN(strain) || strain < 0) + strain = 0; + else if (strain > 2000) + strain = 2000;//lets not freeze everything with aspire/fancy 100* maps + + strains.Add(time, strain); + time += interval; + } + } + + _lastMapLocation = mapLocation; + + _visualizerData.TotalTime = mapLength; + + + + _visualizerData.Strains = strains.Select(s => s.Value).AsChartValues(); + + SetAxisValues(); + + var imageLocation = Path.Combine(mapSearchResult.BeatmapsFound[0] + .BeatmapDirectory(BeatmapHelpers.GetFullSongsLocation(Settings)), workingBeatmap.BackgroundFile ?? ""); + + _visualizerData.ImageLocation = File.Exists(imageLocation) ? imageLocation : null; + + } + + private void SetAxisValues() + { + if (_visualizerData.Strains.Any()) + { + var strainsMax = _visualizerData.Strains.Max(); + _visualizerData.MaxYValue = getMaxY(strainsMax); + _visualizerData.AxisYStep = GetAxisYStep(double.IsNaN(_visualizerData.MaxYValue) + ? strainsMax + : _visualizerData.MaxYValue); + } + } + + public override List GetFormatedPatterns(Tokens replacements, OsuStatus status) + { + //TODO: UHH... would be nice to have better way of sharing this data (instead of relying on Tokens with magic strings and blind value casts) + LiveTokens = replacements.Where(r => r.Value.Type == TokenType.Live).ToList(); + + if (_visualizerData == null) + return null; + + _visualizerData.Title = replacements.First(r => r.Key == "TitleRoman").Value.Value?.ToString(); + _visualizerData.Artist = replacements.First(r => r.Key == "ArtistRoman").Value.Value?.ToString(); + + return null; + } + + private void UpdateLiveTokens() + { + while (true) + { + try + { + if (LiveTokens != null) + { + //Blind casts :/ + _visualizerData.Pp = Math.Round((double)_ppToken.Value); + _visualizerData.Hit100 = (ushort)_hit100Token.Value; + _visualizerData.Hit50 = (ushort)_hit50Token.Value; + _visualizerData.HitMiss = (ushort)_hitMissToken.Value; + _visualizerData.CurrentTime = (double)_timeToken.Value * 1000; + + var normalizedCurrentTime = _visualizerData.CurrentTime < 0 ? 0 : _visualizerData.CurrentTime; + var progress = 700 * (normalizedCurrentTime / _visualizerData.TotalTime); + _visualizerData.PixelMapProgress = progress < 700 ? progress : 700; + } + } + catch + { + return; + } + + Thread.Sleep(22); + } + } + + private bool AutomaticAxisControlIsEnabled => Settings.Get(ConfigEntrys.AutoSizeAxisY); + private double getMaxY(double maxValue) + { + if (!AutomaticAxisControlIsEnabled) + foreach (var cutoff in ChartCutoffsSet) + { + if (maxValue < cutoff) + return cutoff; + } + + return Math.Ceiling(maxValue); + } + + private double GetAxisYStep(double maxYValue) + { + if (double.IsNaN(maxYValue) || maxYValue <= 0) + return 100; + + if (maxYValue < 10) + maxYValue += 10; + return RoundOff((int)(maxYValue / 3.1)); + + int RoundOff(int num) + { + return ((int)Math.Ceiling(num / 10.0)) * 10; + } + } + + public override void Dispose() + { + Settings.SettingUpdated -= SettingUpdated; + base.Dispose(); + } + } +} diff --git a/plugins/LiveVisualizer/LiveVisualizerPluginBase.cs b/plugins/LiveVisualizer/LiveVisualizerPluginBase.cs new file mode 100644 index 00000000..9194cbc0 --- /dev/null +++ b/plugins/LiveVisualizer/LiveVisualizerPluginBase.cs @@ -0,0 +1,78 @@ +using System; +using StreamCompanionTypes.DataTypes; +using StreamCompanionTypes.Enums; +using StreamCompanionTypes.Interfaces; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace LiveVisualizer +{ + public abstract class LiveVisualizerPluginBase : IPlugin, IMapDataGetter, ISettings, IMapDataParser, ISettingsProvider, IDisposable + { + protected ISettingsHandler Settings; + + private CancellationTokenSource _cts = new CancellationTokenSource(); + public string SettingGroup { get; } = "Visualizer"; + private LiveVisualizerSettings _liveVisualizerSettings = null; + + public bool Started { get; set; } + + public string Description { get; } = ""; + public string Name { get; } = "LiveVisualizer"; + public string Author { get; } = "Piotrekol"; + public string Url { get; } = ""; + public string UpdateUrl { get; } = ""; + + + public virtual void Start(ILogger logger) + { + Started = true; + } + + public async void SetNewMap(MapSearchResult mapSearchResult) + { + _cts.Cancel(); + + _cts = new CancellationTokenSource(); + var token = _cts.Token; + + await Task.Run(() => + { + ProcessNewMap(mapSearchResult, token); + }); + } + + public void SetSettingsHandle(ISettingsHandler settings) + { + Settings = settings; + } + + public abstract List GetFormatedPatterns(Tokens replacements, OsuStatus status); + + protected abstract void ProcessNewMap(MapSearchResult mapSearchResult, CancellationToken token); + + public void Free() + { + _liveVisualizerSettings?.Dispose(); + } + + public UserControl GetUiSettings() + { + if (_liveVisualizerSettings == null || _liveVisualizerSettings.IsDisposed) + { + _liveVisualizerSettings = new LiveVisualizerSettings(Settings); + } + + return _liveVisualizerSettings; + } + + public virtual void Dispose() + { + _cts?.Dispose(); + _liveVisualizerSettings?.Dispose(); + } + } +} \ No newline at end of file diff --git a/plugins/LiveVisualizer/LiveVisualizerSettings.Designer.cs b/plugins/LiveVisualizer/LiveVisualizerSettings.Designer.cs new file mode 100644 index 00000000..f6c14a20 --- /dev/null +++ b/plugins/LiveVisualizer/LiveVisualizerSettings.Designer.cs @@ -0,0 +1,270 @@ +namespace LiveVisualizer +{ + partial class LiveVisualizerSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.checkBox_enable = new System.Windows.Forms.CheckBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.color_horizontalLegend = new LiveVisualizer.ColorPickerWithPreview(); + this.checkBox_showAxisYSeparator = new System.Windows.Forms.CheckBox(); + this.linkLabel_UICredit2 = new System.Windows.Forms.LinkLabel(); + this.label4 = new System.Windows.Forms.Label(); + this.linkLabel_UICredit1 = new System.Windows.Forms.LinkLabel(); + this.label3 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.comboBox_font = new System.Windows.Forms.ComboBox(); + this.panel_manualChart = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.textBox_chartCutoffs = new System.Windows.Forms.TextBox(); + this.checkBox_autosizeChart = new System.Windows.Forms.CheckBox(); + this.color_chartProgress = new LiveVisualizer.ColorPickerWithPreview(); + this.color_chartPrimary = new LiveVisualizer.ColorPickerWithPreview(); + this.panel1.SuspendLayout(); + this.panel_manualChart.SuspendLayout(); + this.SuspendLayout(); + // + // checkBox_enable + // + this.checkBox_enable.AutoSize = true; + this.checkBox_enable.Location = new System.Drawing.Point(4, 4); + this.checkBox_enable.Name = "checkBox_enable"; + this.checkBox_enable.Size = new System.Drawing.Size(129, 17); + this.checkBox_enable.TabIndex = 0; + this.checkBox_enable.Text = "Enable Live Visualizer"; + this.checkBox_enable.UseVisualStyleBackColor = true; + // + // panel1 + // + this.panel1.Controls.Add(this.linkLabel_UICredit2); + this.panel1.Controls.Add(this.linkLabel_UICredit1); + this.panel1.Controls.Add(this.color_horizontalLegend); + this.panel1.Controls.Add(this.checkBox_showAxisYSeparator); + this.panel1.Controls.Add(this.label4); + this.panel1.Controls.Add(this.label3); + this.panel1.Controls.Add(this.label2); + this.panel1.Controls.Add(this.comboBox_font); + this.panel1.Controls.Add(this.panel_manualChart); + this.panel1.Controls.Add(this.checkBox_autosizeChart); + this.panel1.Controls.Add(this.color_chartProgress); + this.panel1.Controls.Add(this.color_chartPrimary); + this.panel1.Location = new System.Drawing.Point(0, 28); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(650, 254); + this.panel1.TabIndex = 1; + // + // color_horizontalLegend + // + this.color_horizontalLegend.Color = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(180)))), ((int)(((byte)(209))))); + // + // + // + this.color_horizontalLegend.LabelDesigner.AutoSize = true; + this.color_horizontalLegend.LabelDesigner.Location = new System.Drawing.Point(3, 6); + this.color_horizontalLegend.LabelDesigner.Name = "Label"; + this.color_horizontalLegend.LabelDesigner.Size = new System.Drawing.Size(115, 13); + this.color_horizontalLegend.LabelDesigner.TabIndex = 1; + this.color_horizontalLegend.LabelDesigner.Text = "Horizontal legend color"; + this.color_horizontalLegend.Location = new System.Drawing.Point(4, 68); + this.color_horizontalLegend.Name = "color_horizontalLegend"; + this.color_horizontalLegend.Size = new System.Drawing.Size(436, 26); + this.color_horizontalLegend.TabIndex = 33; + // + // checkBox_showAxisYSeparator + // + this.checkBox_showAxisYSeparator.AutoSize = true; + this.checkBox_showAxisYSeparator.Location = new System.Drawing.Point(8, 123); + this.checkBox_showAxisYSeparator.Name = "checkBox_showAxisYSeparator"; + this.checkBox_showAxisYSeparator.Size = new System.Drawing.Size(136, 17); + this.checkBox_showAxisYSeparator.TabIndex = 32; + this.checkBox_showAxisYSeparator.Text = "Show horizontal legend"; + this.checkBox_showAxisYSeparator.UseVisualStyleBackColor = true; + // + // linkLabel_UICredit2 + // + this.linkLabel_UICredit2.AutoSize = true; + this.linkLabel_UICredit2.Location = new System.Drawing.Point(217, 211); + this.linkLabel_UICredit2.Name = "linkLabel_UICredit2"; + this.linkLabel_UICredit2.Size = new System.Drawing.Size(48, 13); + this.linkLabel_UICredit2.TabIndex = 31; + this.linkLabel_UICredit2.TabStop = true; + this.linkLabel_UICredit2.Text = "Dartandr"; + this.linkLabel_UICredit2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_UICredit2_LinkClicked); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(191, 211); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(25, 13); + this.label4.TabIndex = 30; + this.label4.Text = "and"; + // + // linkLabel_UICredit1 + // + this.linkLabel_UICredit1.AutoSize = true; + this.linkLabel_UICredit1.Location = new System.Drawing.Point(128, 211); + this.linkLabel_UICredit1.Name = "linkLabel_UICredit1"; + this.linkLabel_UICredit1.Size = new System.Drawing.Size(62, 13); + this.linkLabel_UICredit1.TabIndex = 29; + this.linkLabel_UICredit1.TabStop = true; + this.linkLabel_UICredit1.Text = "BlackShark"; + this.linkLabel_UICredit1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_UICredit1_LinkClicked); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(5, 211); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(122, 13); + this.label3.TabIndex = 27; + this.label3.Text = "Initial UI design made by"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(8, 102); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(57, 13); + this.label2.TabIndex = 26; + this.label2.Text = "Font used:"; + // + // comboBox_font + // + this.comboBox_font.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox_font.FormattingEnabled = true; + this.comboBox_font.Location = new System.Drawing.Point(71, 99); + this.comboBox_font.Name = "comboBox_font"; + this.comboBox_font.Size = new System.Drawing.Size(139, 21); + this.comboBox_font.TabIndex = 25; + // + // panel_manualChart + // + this.panel_manualChart.Controls.Add(this.label1); + this.panel_manualChart.Controls.Add(this.textBox_chartCutoffs); + this.panel_manualChart.Location = new System.Drawing.Point(8, 169); + this.panel_manualChart.Name = "panel_manualChart"; + this.panel_manualChart.Size = new System.Drawing.Size(432, 37); + this.panel_manualChart.TabIndex = 4; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(4, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(248, 13); + this.label1.TabIndex = 4; + this.label1.Text = "Manual chart height cutoff points, separated by \";\":"; + // + // textBox_chartCutoffs + // + this.textBox_chartCutoffs.Location = new System.Drawing.Point(258, 6); + this.textBox_chartCutoffs.Name = "textBox_chartCutoffs"; + this.textBox_chartCutoffs.Size = new System.Drawing.Size(171, 20); + this.textBox_chartCutoffs.TabIndex = 3; + // + // checkBox_autosizeChart + // + this.checkBox_autosizeChart.AutoSize = true; + this.checkBox_autosizeChart.Location = new System.Drawing.Point(8, 146); + this.checkBox_autosizeChart.Name = "checkBox_autosizeChart"; + this.checkBox_autosizeChart.Size = new System.Drawing.Size(252, 17); + this.checkBox_autosizeChart.TabIndex = 2; + this.checkBox_autosizeChart.Text = "Autosize chart value height based on max value"; + this.checkBox_autosizeChart.UseVisualStyleBackColor = true; + // + // color_chartProgress + // + this.color_chartProgress.Color = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(180)))), ((int)(((byte)(209))))); + // + // + // + this.color_chartProgress.LabelDesigner.AutoSize = true; + this.color_chartProgress.LabelDesigner.Location = new System.Drawing.Point(3, 6); + this.color_chartProgress.LabelDesigner.Name = "Label"; + this.color_chartProgress.LabelDesigner.Size = new System.Drawing.Size(101, 13); + this.color_chartProgress.LabelDesigner.TabIndex = 1; + this.color_chartProgress.LabelDesigner.Text = "Chart progress color"; + this.color_chartProgress.Location = new System.Drawing.Point(4, 36); + this.color_chartProgress.Name = "color_chartProgress"; + this.color_chartProgress.Size = new System.Drawing.Size(436, 26); + this.color_chartProgress.TabIndex = 1; + // + // color_chartPrimary + // + this.color_chartPrimary.Color = System.Drawing.Color.FromArgb(((int)(((byte)(153)))), ((int)(((byte)(180)))), ((int)(((byte)(209))))); + // + // + // + this.color_chartPrimary.LabelDesigner.AutoSize = true; + this.color_chartPrimary.LabelDesigner.Location = new System.Drawing.Point(3, 6); + this.color_chartPrimary.LabelDesigner.Name = "Label"; + this.color_chartPrimary.LabelDesigner.Size = new System.Drawing.Size(94, 13); + this.color_chartPrimary.LabelDesigner.TabIndex = 1; + this.color_chartPrimary.LabelDesigner.Text = "Primary chart color"; + this.color_chartPrimary.Location = new System.Drawing.Point(4, 4); + this.color_chartPrimary.Name = "color_chartPrimary"; + this.color_chartPrimary.Size = new System.Drawing.Size(436, 26); + this.color_chartPrimary.TabIndex = 0; + // + // LiveVisualizerSettings + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.panel1); + this.Controls.Add(this.checkBox_enable); + this.Name = "LiveVisualizerSettings"; + this.Size = new System.Drawing.Size(650, 282); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.panel_manualChart.ResumeLayout(false); + this.panel_manualChart.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.CheckBox checkBox_enable; + private System.Windows.Forms.Panel panel1; + private ColorPickerWithPreview color_chartPrimary; + private ColorPickerWithPreview color_chartProgress; + private System.Windows.Forms.CheckBox checkBox_autosizeChart; + private System.Windows.Forms.Panel panel_manualChart; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox_chartCutoffs; + private System.Windows.Forms.ComboBox comboBox_font; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.LinkLabel linkLabel_UICredit1; + private System.Windows.Forms.LinkLabel linkLabel_UICredit2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.CheckBox checkBox_showAxisYSeparator; + private ColorPickerWithPreview color_horizontalLegend; + } +} diff --git a/plugins/LiveVisualizer/LiveVisualizerSettings.cs b/plugins/LiveVisualizer/LiveVisualizerSettings.cs new file mode 100644 index 00000000..d5ee93ee --- /dev/null +++ b/plugins/LiveVisualizer/LiveVisualizerSettings.cs @@ -0,0 +1,122 @@ +using StreamCompanionTypes.DataTypes; +using StreamCompanionTypes.Interfaces; +using System; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Text; +using System.Linq; +using System.Windows.Forms; + +namespace LiveVisualizer +{ + public partial class LiveVisualizerSettings : UserControl + { + private readonly ISettingsHandler _settings; + + public LiveVisualizerSettings(ISettingsHandler settings) + { + _settings = settings; + + InitializeComponent(); + + + using (InstalledFontCollection fontsCollection = new InstalledFontCollection()) + { + var fontNames = fontsCollection.Families.Select(f => f.Name).ToList(); + comboBox_font.DataSource = fontNames; + + var desiredFont = _settings.Get(ConfigEntrys.Font); + + var font = fontNames.Contains(desiredFont) + ? desiredFont + : "Arial"; + + comboBox_font.SelectedItem = font; + } + + + checkBox_enable.Checked = _settings.Get(ConfigEntrys.Enable); + panel1.Enabled = checkBox_enable.Checked; + + checkBox_autosizeChart.Checked = _settings.Get(ConfigEntrys.AutoSizeAxisY); + panel_manualChart.Enabled = !checkBox_autosizeChart.Checked; + + color_chartPrimary.Color = ColorHelpers.GetColor(_settings, ConfigEntrys.ChartColor); + color_chartProgress.Color = ColorHelpers.GetColor(_settings, ConfigEntrys.ChartProgressColor); + color_horizontalLegend.Color = ColorHelpers.GetColor(_settings, ConfigEntrys.AxisYSeparatorColor); + + textBox_chartCutoffs.Text = _settings.Get(ConfigEntrys.ManualAxisCutoffs); + + checkBox_showAxisYSeparator.Checked = _settings.Get(ConfigEntrys.ShowAxisYSeparator); + + + color_chartPrimary.ColorChanged += Color_chartPrimary_ColorChanged; + color_chartProgress.ColorChanged += ColorChartProgressOnColorChanged; + color_horizontalLegend.ColorChanged += ColorHorizontalLegendOnColorChanged; + + checkBox_enable.CheckedChanged += CheckBoxEnableOnCheckedChanged; + checkBox_autosizeChart.CheckedChanged += checkBox_autosizeChart_CheckedChanged; + comboBox_font.SelectedValueChanged += ComboBoxFontOnSelectedValueChanged; + textBox_chartCutoffs.TextChanged += textBox_chartCutoffs_TextChanged; + checkBox_showAxisYSeparator.CheckedChanged += checkBox_showAxisYSeparator_CheckedChanged; + } + + private void ColorHorizontalLegendOnColorChanged(object sender, Color e) + { + ColorHelpers.SaveColor(_settings, ConfigEntrys.AxisYSeparatorColor, e); + } + + private void ComboBoxFontOnSelectedValueChanged(object sender, EventArgs e) + { + _settings.Add(ConfigEntrys.Font.Name, (string)comboBox_font.SelectedValue, true); + } + + private void ColorChartProgressOnColorChanged(object sender, Color e) + { + ColorHelpers.SaveColor(_settings, ConfigEntrys.ChartProgressColor, e); + } + + private void Color_chartPrimary_ColorChanged(object sender, Color e) + { + ColorHelpers.SaveColor(_settings, ConfigEntrys.ChartColor, e); + } + + private void CheckBoxEnableOnCheckedChanged(object sender, EventArgs e) + { + var enabled = checkBox_enable.Checked; + _settings.Add(ConfigEntrys.Enable.Name, enabled, true); + + panel1.Enabled = enabled; + } + + private void checkBox_autosizeChart_CheckedChanged(object sender, EventArgs e) + { + var enabled = checkBox_autosizeChart.Checked; + _settings.Add(ConfigEntrys.AutoSizeAxisY.Name, enabled, true); + + panel_manualChart.Enabled = !enabled; + } + + private void textBox_chartCutoffs_TextChanged(object sender, EventArgs e) + { + _settings.Add(ConfigEntrys.ManualAxisCutoffs.Name, textBox_chartCutoffs.Text, true); + } + + private void checkBox_showAxisYSeparator_CheckedChanged(object sender, EventArgs e) + { + _settings.Add(ConfigEntrys.ShowAxisYSeparator.Name, checkBox_showAxisYSeparator.Checked, true); + } + + private void linkLabel_UICredit1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start("https://osu.ppy.sh/users/9173653"); + + } + + private void linkLabel_UICredit2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start("https://osu.ppy.sh/users/4944211"); + } + + } +} diff --git a/plugins/LiveVisualizer/LiveVisualizerSettings.resx b/plugins/LiveVisualizer/LiveVisualizerSettings.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/plugins/LiveVisualizer/LiveVisualizerSettings.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/plugins/LiveVisualizer/MainWindow.xaml b/plugins/LiveVisualizer/MainWindow.xaml new file mode 100644 index 00000000..8617dacb --- /dev/null +++ b/plugins/LiveVisualizer/MainWindow.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/LiveVisualizer/MainWindow.xaml.cs b/plugins/LiveVisualizer/MainWindow.xaml.cs new file mode 100644 index 00000000..587ecd0b --- /dev/null +++ b/plugins/LiveVisualizer/MainWindow.xaml.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace LiveVisualizer +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public MainWindow(IWpfVisualizerData data) + { + InitializeComponent(); + + this.DataContext = data; + + this.frameholder.Content = new Chart(data, nameof(IWpfVisualizerData.ChartColor), false); + this.frameholderTimer.Content = new Chart(data, nameof(IWpfVisualizerData.ChartProgressColor), true); + } + + private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + DragMove(); + } + } +} diff --git a/plugins/LiveVisualizer/Properties/Annotations.cs b/plugins/LiveVisualizer/Properties/Annotations.cs new file mode 100644 index 00000000..3e4991a4 --- /dev/null +++ b/plugins/LiveVisualizer/Properties/Annotations.cs @@ -0,0 +1,1065 @@ +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace LiveVisualizer.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for method output + /// means that the methos doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by R# analysis.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; private set; } + + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of method invocation must be used. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; private set; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; private set; } + } + + /// + /// An extension method marked with this attribute is processed by ReSharper code completion + /// as a 'Source Template'. When extension method is completed over some expression, it's source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// > + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation or property access + /// over collection type affects content of the collection. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; private set; } + + [NotNull] public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; private set; } + + [NotNull] public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; private set; } + [CanBeNull] public string PageName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } +} \ No newline at end of file diff --git a/plugins/LiveVisualizer/Properties/AssemblyInfo.cs b/plugins/LiveVisualizer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..461b1332 --- /dev/null +++ b/plugins/LiveVisualizer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("LiveVisualizer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("LiveVisualizer")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[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("6fb20d7d-f84a-41de-b086-da920c7b5e86")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/plugins/LiveVisualizer/Properties/Resources.Designer.cs b/plugins/LiveVisualizer/Properties/Resources.Designer.cs new file mode 100644 index 00000000..83946001 --- /dev/null +++ b/plugins/LiveVisualizer/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LiveVisualizer.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LiveVisualizer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/plugins/LiveVisualizer/Properties/Resources.resx b/plugins/LiveVisualizer/Properties/Resources.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/plugins/LiveVisualizer/Properties/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/plugins/LiveVisualizer/VisualizerDataModel.cs b/plugins/LiveVisualizer/VisualizerDataModel.cs new file mode 100644 index 00000000..6ca267dd --- /dev/null +++ b/plugins/LiveVisualizer/VisualizerDataModel.cs @@ -0,0 +1,49 @@ +using LiveCharts; +using LiveVisualizer.Annotations; +using PropertyChanged; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace LiveVisualizer +{ + public class VisualizerDataModel : IWpfVisualizerData, INotifyPropertyChanged + { + //private List _strains = new List(300); + private double _currentTime; + public event PropertyChangedEventHandler PropertyChanged; + + + + public ChartValues Strains { get; set; } + + public string Title { get; set; } + public string Artist { get; set; } + public string ImageLocation { get; set; } + public double Pp { get; set; } + public int Hit300 { get; set; } + public int Hit100 { get; set; } + public int Hit50 { get; set; } + public int HitMiss { get; set; } + + public double Acc { get; set; } + + public double CurrentTime { get; set; } + + public double TotalTime { get; set; } + + + public double FontsizeTitle { get; set; } = 15; + public double FontsizeArtist { get; set; } = 15; + public double PixelMapProgress { get; set; } + public string ChartColor { get; set; } = "#66FFB2E3"; + public string ChartProgressColor { get; set; } = "#B2FFB2E3"; + public double MaxYValue { get; set; } = 350; + public string FillColor { get; set; } = "#B2FFB2E3"; + public string Font { get; set; } + public bool ShowAxisYSeparator { get; set; } + public string AxisYSeparatorColor { get; set; } + public double AxisYStep { get; set; } = 100; + } +} \ No newline at end of file diff --git a/plugins/LiveVisualizer/packages.config b/plugins/LiveVisualizer/packages.config new file mode 100644 index 00000000..95001480 --- /dev/null +++ b/plugins/LiveVisualizer/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/OsuMemoryEventSource/MemoryDataProcessor.cs b/plugins/OsuMemoryEventSource/MemoryDataProcessor.cs index ed4f6bd5..80051190 100644 --- a/plugins/OsuMemoryEventSource/MemoryDataProcessor.cs +++ b/plugins/OsuMemoryEventSource/MemoryDataProcessor.cs @@ -1,6 +1,7 @@ using CollectionManager.DataTypes; using CollectionManager.Enums; using Newtonsoft.Json; +using osu.Framework.Extensions.IEnumerableExtensions; using OsuMemoryDataProvider; using PpCalculator; using StreamCompanionTypes.DataTypes; @@ -114,18 +115,25 @@ private void CopyPatterns(List patterns) } } + private bool _clearedLiveTokens = false; public void Tick(OsuStatus status, IOsuMemoryReader reader) { lock (_lockingObject) { + + PrepareTimeReplacement(reader.ReadPlayTime()); + if (status != OsuStatus.Playing) { - if (_clearLiveTokensAfterResultScreenExit && (status & OsuStatus.ResultsScreen) == 0) + if (_clearLiveTokensAfterResultScreenExit && !_clearedLiveTokens && (status & OsuStatus.ResultsScreen) == 0) {//we're not playing or we haven't just finished playing - clear ResetOutput(); + ResetTokens(); _lastStatus = status; + _clearedLiveTokens = true; } + return; } @@ -137,6 +145,8 @@ public void Tick(OsuStatus status, IOsuMemoryReader reader) //_rawData.SetPlayMode(playMode); } + _clearedLiveTokens = false; + _lastStatus = status; reader.GetPlayData(_rawData.Play); @@ -152,6 +162,10 @@ private void ResetOutput() SendData(true); } + private void ResetTokens() + { + replacements2.ForEach(r => r.Value.Reset()); + } private void SendData(bool emptyPatterns = false) { if (OutputPatterns != null && OutputPatterns.Count > 0) @@ -222,25 +236,32 @@ public Tokens Tokens private void InitReplacements() { - replacements2["acc"] = new TokenWithFormat(_rawData.Play.Acc, TokenType.Live, "{0:0.00}"); - replacements2["300"] = new TokenWithFormat(_rawData.Play.C300, TokenType.Live, "{0}"); - replacements2["100"] = new TokenWithFormat(_rawData.Play.C100, TokenType.Live, "{0}"); - replacements2["50"] = new TokenWithFormat(_rawData.Play.C50, TokenType.Live, "{0}"); - replacements2["miss"] = new TokenWithFormat(_rawData.Play.CMiss, TokenType.Live, "{0}"); - replacements2["time"] = new TokenWithFormat(0d, TokenType.Live, "{0:0.00}"); - replacements2["combo"] = new TokenWithFormat(_rawData.Play.Combo, TokenType.Live, "{0}"); - replacements2["CurrentMaxCombo"] = new TokenWithFormat(_rawData.Play.MaxCombo, TokenType.Live, "{0}"); - replacements2["PlayerHp"] = new TokenWithFormat(_rawData.Play.Hp, TokenType.Live, "{0:0.00}"); - - replacements2["PpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.PpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}"); - replacements2["AimPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.AimPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}"); - replacements2["SpeedPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.SpeedPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}"); - replacements2["AccPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.AccPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}"); - replacements2["StrainPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.StrainPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}"); - - replacements2["PpIfRestFced"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.PpIfRestFced].Current, TokenType.Live, "{0:0.00}"); + replacements2["acc"] = new TokenWithFormat(_rawData.Play.Acc, TokenType.Live, "{0:0.00}", 0d); + replacements2["300"] = new TokenWithFormat(_rawData.Play.C300, TokenType.Live, "{0}", (ushort)0); + replacements2["100"] = new TokenWithFormat(_rawData.Play.C100, TokenType.Live, "{0}", (ushort)0); + replacements2["50"] = new TokenWithFormat(_rawData.Play.C50, TokenType.Live, "{0}", (ushort)0); + replacements2["miss"] = new TokenWithFormat(_rawData.Play.CMiss, TokenType.Live, "{0}", (ushort)0); + replacements2["time"] = new TokenWithFormat(0d, TokenType.Live, "{0:0.00}", 0d); + replacements2["combo"] = new TokenWithFormat(_rawData.Play.Combo, TokenType.Live, "{0}", (ushort)0); + replacements2["CurrentMaxCombo"] = new TokenWithFormat(_rawData.Play.MaxCombo, TokenType.Live, "{0}", (ushort)0); + replacements2["PlayerHp"] = new TokenWithFormat(_rawData.Play.Hp, TokenType.Live, "{0:0.00}", 0d); + + replacements2["PpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.PpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}", 0d); + replacements2["AimPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.AimPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}", 0d); + replacements2["SpeedPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.SpeedPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}", 0d); + replacements2["AccPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.AccPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}", 0d); + replacements2["StrainPpIfMapEndsNow"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.StrainPpIfMapEndsNow].Current, TokenType.Live, "{0:0.00}", 0d); + + replacements2["PpIfRestFced"] = new TokenWithFormat(InterpolatedValues[InterpolatedValueName.PpIfRestFced].Current, TokenType.Live, "{0:0.00}", 0d); } + private void PrepareTimeReplacement(int readPlayTime) + { + double time = 0; + if (readPlayTime != 0) + time = readPlayTime / 1000d; + replacements2["time"].Value = time; + } private void PrepareReplacements() { @@ -249,10 +270,7 @@ private void PrepareReplacements() replacements2["100"].Value = _rawData.Play.C100; replacements2["50"].Value = _rawData.Play.C50; replacements2["miss"].Value = _rawData.Play.CMiss; - double time = 0; - if (_rawData.Play.Time != 0) - time = _rawData.Play.Time / 1000d; - replacements2["time"].Value = time; + replacements2["combo"].Value = _rawData.Play.Combo; replacements2["CurrentMaxCombo"].Value = _rawData.Play.MaxCombo; replacements2["PlayerHp"].Value = _rawData.Play.Hp;