// 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.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { public partial class ModPresetPanel : ModSelectPanel, IHasCustomTooltip, IHasContextMenu, IHasPopover { public readonly Live Preset; public override BindableBool Active { get; } = new BindableBool(); [Resolved] private IDialogOverlay? dialogOverlay { get; set; } [Resolved] private Bindable> selectedMods { get; set; } = null!; private ModSettingChangeTracker? settingChangeTracker; public ModPresetPanel(Live preset) { Preset = preset; Title = preset.Value.Name; Description = preset.Value.Description; } [BackgroundDependencyLoader] private void load(OsuColour colours) { AccentColour = colours.Orange1; } protected override void LoadComplete() { base.LoadComplete(); selectedMods.BindValueChanged(_ => selectedModsChanged(), true); } protected override void Select() { // if the preset is not active at the point of the user click, then set the mods using the preset directly, discarding any previous selections, // which will also have the side effect of activating the preset (see `updateActiveState()`). selectedMods.Value = Preset.Value.Mods.ToArray(); } protected override void Deselect() { // if the preset is active when the user has clicked it, then it means that the set of active mods is exactly equal to the set of mods in the preset // (there are no other active mods than what the preset specifies, and the mod settings match exactly). // therefore it's safe to just clear selected mods, since it will have the effect of toggling the preset off. selectedMods.Value = Array.Empty(); } private void selectedModsChanged() { settingChangeTracker?.Dispose(); settingChangeTracker = new ModSettingChangeTracker(selectedMods.Value); settingChangeTracker.SettingChanged = _ => updateActiveState(); updateActiveState(); } private void updateActiveState() { Active.Value = new HashSet(Preset.Value.Mods).SetEquals(selectedMods.Value); } #region IHasCustomTooltip public ModPreset TooltipContent => Preset.Value; public ITooltip GetCustomTooltip() => new ModPresetTooltip(ColourProvider); #endregion #region IHasContextMenu public MenuItem[] ContextMenuItems { get { var menu = new List { new OsuMenuItem(CommonStrings.ButtonsEdit, MenuItemType.Highlighted, this.ShowPopover), new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new DeleteModPresetDialog(Preset))), }; if (checkCurrentModCanBeSave()) { menu.Insert(1, new OsuMenuItem("Use Current Mods", MenuItemType.Destructive, () => SaveCurrentMod())); } return menu.ToArray(); } } #endregion public bool SaveCurrentMod() { if (!checkCurrentModCanBeSave()) return false; Preset.PerformWrite(s => { s.Mods = selectedMods.Value.ToArray(); }); return true; } private bool checkCurrentModCanBeSave() => (!Active.Value && selectedMods.Value.Any()); protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); settingChangeTracker?.Dispose(); } public Popover GetPopover() => new EditPresetPopover(this); } }