Files
Ez2Lazer/osu.Game/LAsEzExtensions/UserInterface/EzKeyModeSelector.cs
LA 0b9f9f70d6 主要为代码质量更新
1. 匹配新版按钮控件的自动宽度写法

2. 统一Ez日志写入方向

3.移除历史修改:缓存启用mod列表,切换mod时保持通用mod开启状态

4.代码格式化、

5.修改文件名称表意,更直观
2026-03-12 19:29:55 +08:00

424 lines
15 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.LAsEzExtensions.Configuration;
using osu.Game.LAsEzExtensions.Localization;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osuTK;
namespace osu.Game.LAsEzExtensions.UserInterface
{
public partial class EzKeyModeSelector : CompositeDrawable
{
private Bindable<string> keyModeId = new Bindable<string>();
private readonly BindableBool isMultiSelectMode = new BindableBool();
private readonly Dictionary<int, HashSet<string>> modeSelections = new Dictionary<int, HashSet<string>>();
private int currentRulesetId = -1;
private ShearedButton labelButton = null!;
private ShearedCsModeTabControl tabControl = null!;
private ShearedToggleButton multiSelectButton = null!;
[Resolved]
private Ez2ConfigManager ezConfig { get; set; } = null!;
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
public IBindable<string> Current => tabControl.Current;
public HashSet<string> SelectedModeIds { get; } = new HashSet<string>();
public void SetSelection(HashSet<string> modeIds)
{
SelectedModeIds.Clear();
SelectedModeIds.UnionWith(modeIds);
}
public EzKeyModeSelector()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
CornerRadius = 8;
Masking = true;
Shear = OsuGame.SHEAR;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
{
new Drawable[]
{
labelButton = new ShearedButton()
{
Text = "Keys",
TextSize = 16,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.X,
Height = 30f,
Shear = new Vector2(0),
TooltipText = EzSongSelectStrings.CLEAR_SELECTION,
},
tabControl = new ShearedCsModeTabControl
{
RelativeSizeAxes = Axes.X,
Shear = new Vector2(0),
},
multiSelectButton = new ShearedToggleButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shear = new Vector2(0),
Text = "K +",
Height = 30f,
TooltipText = EzSongSelectStrings.MULTI_SELECT_BUTTON_TOOLTIP,
}
}
}
}
};
multiSelectButton.Active.BindTo(isMultiSelectMode);
labelButton.Action = () =>
{
SelectedModeIds.Clear();
updateValue();
};
keyModeId = ezConfig.GetBindable<string>(Ez2Setting.EzSelectCsMode);
keyModeId.BindValueChanged(onSelectorChanged, true);
isMultiSelectMode.BindValueChanged(_ => updateValue(), true);
ruleset.BindValueChanged(onRulesetChanged, true);
tabControl.Current.BindTarget = keyModeId;
}
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> e)
{
if (currentRulesetId >= 0)
modeSelections[currentRulesetId] = new HashSet<string>(SelectedModeIds);
int id = e.NewValue.OnlineID;
currentRulesetId = id;
var validIds = CsItemIds.GetModesForRuleset(id)
.Select(m => m.Id)
.ToHashSet();
if (!modeSelections.TryGetValue(id, out var selectionForRuleset))
selectionForRuleset = parseModeIds(keyModeId.Value);
selectionForRuleset.IntersectWith(validIds);
SetSelection(selectionForRuleset);
modeSelections[id] = new HashSet<string>(selectionForRuleset);
if (id == 1) // Taiko
{
Hide();
SelectedModeIds.Clear();
}
else
{
Show();
}
tabControl.UpdateForRuleset(id);
labelButton.Text = id == 3 ? "Keys" : "CS";
updateValue();
}
private void onSelectorChanged(ValueChangedEvent<string> e)
{
var modes = parseModeIds(e.NewValue);
SetSelection(modes);
tabControl.UpdateTabItemUI(modes);
}
private void updateValue()
{
int activeRulesetId = ruleset.Value.OnlineID;
if (!modeSelections.ContainsKey(activeRulesetId))
modeSelections[activeRulesetId] = new HashSet<string>();
HashSet<string> selectedModes = SelectedModeIds;
if (selectedModes.Count == 0)
{
keyModeId.Value = "";
}
else
{
if (isMultiSelectMode.Value)
{
keyModeId.Value = string.Join(",", selectedModes.OrderBy(x => x));
}
else
{
keyModeId.Value = selectedModes.First();
}
}
modeSelections[activeRulesetId] = new HashSet<string>(selectedModes);
tabControl.UpdateForRuleset(activeRulesetId);
tabControl.UpdateTabItemUI(selectedModes);
tabControl.IsMultiSelectMode = isMultiSelectMode.Value;
}
private HashSet<string> parseModeIds(string value)
{
if (string.IsNullOrEmpty(value))
return new HashSet<string>();
return new HashSet<string>(value.Split(','));
}
public partial class ShearedCsModeTabControl : OsuTabControl<string>
{
private HashSet<string> currentSelection = new HashSet<string>();
private int currentRulesetId = -1;
public bool IsMultiSelectMode { get; set; }
public Action<HashSet<string>>? SetCurrentSelections;
//
// [Resolved]
// private OverlayColourProvider colourProvider { get; set; } = null!;
public ShearedCsModeTabControl()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Shear = OsuGame.SHEAR;
Masking = true;
}
[BackgroundDependencyLoader]
private void load()
{
TabContainer.Anchor = Anchor.CentreLeft;
TabContainer.Origin = Anchor.CentreLeft;
// TabContainer.Shear = OsuGame.SHEAR;
TabContainer.RelativeSizeAxes = Axes.X;
TabContainer.AutoSizeAxes = Axes.Y;
TabContainer.Spacing = new Vector2(0f);
}
public void UpdateForRuleset(int rulesetId)
{
if (currentRulesetId == rulesetId && Items.Any())
return;
currentRulesetId = rulesetId;
var keyModes = CsItemIds.GetModesForRuleset(rulesetId)
.Select(m => m.Id)
.ToList();
TabContainer.Clear();
Items = keyModes;
Schedule(() =>
{
int count = keyModes.Count;
if (count > 0)
{
float totalWidth = DrawWidth;
float itemWidth = (totalWidth - (count * 2f)) / count;
foreach (var tab in TabContainer.Children.Cast<ShearedCsModeTabItem>())
tab.Width = itemWidth;
}
});
UpdateTabItemUI(currentSelection);
}
public void UpdateTabItemUI(HashSet<string> selectedModes)
{
currentSelection = new HashSet<string>(selectedModes);
foreach (var tabItem in TabContainer.Children.Cast<ShearedCsModeTabItem>())
{
bool isSelected = selectedModes.Contains(tabItem.Value);
tabItem.UpdateButton(isSelected);
}
}
protected override Dropdown<string> CreateDropdown() => null!;
// protected override bool AddEnumEntriesAutomatically => false;
protected override TabItem<string> CreateTabItem(string value)
{
var tabItem = new ShearedCsModeTabItem(value);
tabItem.Clicked += onTabItemClicked;
return tabItem;
}
private void onTabItemClicked(string mode)
{
var newSelection = new HashSet<string>(currentSelection);
if (!newSelection.Remove(mode))
{
if (IsMultiSelectMode)
{
newSelection.Add(mode);
}
else
{
newSelection.Clear();
newSelection.Add(mode);
}
}
currentSelection = newSelection;
Current.Value = newSelection.Count == 0 ? "" : string.Join(",", newSelection.OrderBy(x => x));
UpdateTabItemUI(newSelection);
SetCurrentSelections?.Invoke(newSelection);
}
public partial class ShearedCsModeTabItem : TabItem<string>
{
private readonly OsuSpriteText text;
private readonly Box background;
private OverlayColourProvider colourProvider = null!;
public event Action<string>? Clicked;
public ShearedCsModeTabItem(string value)
: base(value)
{
// Shear = OsuGame.SHEAR;
CornerRadius = ShearedButton.CORNER_RADIUS;
Masking = true;
// Width = 40;
AutoSizeAxes = Axes.Y;
Margin = new MarginPadding { Left = 4 };
var modeInfo = CsItemIds.GetById(value);
InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
text = new OsuSpriteText
{
Text = modeInfo?.DisplayName ?? value,
Margin = new MarginPadding
{ Horizontal = 10f, Vertical = 7f },
Font = OsuFont.Style.Body.With(weight: FontWeight.SemiBold),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shear = -OsuGame.SHEAR,
Colour = Colour4.White,
},
};
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
this.colourProvider = colourProvider;
background.Colour = colourProvider.Background5;
}
protected override void LoadComplete()
{
base.LoadComplete();
if (Width > 40) Width = 40;
// if (Width < 30) Width = 30;
}
public void UpdateButton(bool isSelected)
{
if (Active.Value != isSelected)
{
Active.Value = isSelected;
Schedule(updateColours);
}
}
private void updateColours()
{
using (BeginDelayedSequence(0))
{
if (Active.Value)
{
background.FadeColour(colourProvider.Light4, 150, Easing.OutQuint);
text.FadeColour(Colour4.Black, 150, Easing.OutQuint);
}
else if (IsHovered)
{
background.FadeColour(colourProvider.Background4, 150, Easing.OutQuint);
text.FadeColour(Colour4.White, 150, Easing.OutQuint);
}
else
{
background.FadeColour(colourProvider.Background5, 150, Easing.OutQuint);
text.FadeColour(Colour4.White, 150, Easing.OutQuint);
}
}
}
protected override void OnActivated() => Schedule(updateColours);
protected override void OnDeactivated() => Schedule(updateColours);
protected override bool OnHover(HoverEvent e)
{
Schedule(updateColours);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
Schedule(updateColours);
base.OnHoverLost(e);
}
protected override bool OnClick(ClickEvent e)
{
Clicked?.Invoke(Value);
return true;
}
}
}
}
}