From e831c46f13e45a3f619d819c9e709febfa09fd2f Mon Sep 17 00:00:00 2001 From: Krzysztof Gutkowski Date: Mon, 25 May 2026 08:24:33 +0200 Subject: [PATCH] Replace new combo button icons with ruleset-specifc ones (#37848) - Depends on https://github.com/ppy/osu-resources/pull/425. - Closes https://github.com/ppy/osu/issues/37874 This makes the new combo button use the new icons added in https://github.com/ppy/osu/pull/37804. Instead of having four separate icons per ruleset, the "sparkle" texture is overlaid on top of the appropriate icon. I'm not sure if I've overdone it with how every ruleset copypastes the same code for the icon (in `BlueprintContainer`), so that can be scaled down if necessary. | osu | taiko | catch | mania | |--------|--------|--------|--------| | image | image | image | image | Co-authored-by: Dean Herbert --- .../Edit/CatchBlueprintContainer.cs | 28 +++++++++++++ .../Edit/ManiaBlueprintContainer.cs | 28 +++++++++++++ .../Edit/TaikoBlueprintContainer.cs | 28 +++++++++++++ osu.Game/Graphics/OsuIcon.cs | 6 +-- .../TernaryButtons/NewComboTernaryButton.cs | 4 +- .../Components/ComposeBlueprintContainer.cs | 39 ++++++++++++++++++- 6 files changed, 128 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs b/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs index 47035b0227..79f24870ed 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchBlueprintContainer.cs @@ -3,11 +3,16 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Edit.Blueprints; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -22,6 +27,29 @@ namespace osu.Game.Rulesets.Catch.Edit { } + protected override Drawable CreateNewComboButton() => new NewComboTernaryButton + { + Current = NewCombo, + CreateIcon = () => new Container + { + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Icon = OsuIcon.EditorFruit, + Size = new Vector2(15), + }, + new SpriteIcon + { + Icon = OsuIcon.EditorNewComboSparkles, + Size = new Vector2(20), + } + }, + }, + }; + protected override SelectionHandler CreateSelectionHandler() => new CatchSelectionHandler(); public override HitObjectSelectionBlueprint? CreateHitObjectBlueprintFor(HitObject hitObject) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs index 4eb54e6366..1c62b74f18 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaBlueprintContainer.cs @@ -3,11 +3,16 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -22,6 +27,29 @@ namespace osu.Game.Rulesets.Mania.Edit { } + protected override Drawable CreateNewComboButton() => new NewComboTernaryButton + { + Current = NewCombo, + CreateIcon = () => new Container + { + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Icon = OsuIcon.EditorNote, + Size = new Vector2(15), + }, + new SpriteIcon + { + Icon = OsuIcon.EditorNewComboSparkles, + Size = new Vector2(20), + } + }, + }, + }; + public override HitObjectSelectionBlueprint? CreateHitObjectBlueprintFor(HitObject hitObject) { switch (hitObject) diff --git a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs index f0c3eec044..417463b0d3 100644 --- a/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs +++ b/osu.Game.Rulesets.Taiko/Edit/TaikoBlueprintContainer.cs @@ -3,10 +3,15 @@ using System.Collections.Generic; using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Taiko.Edit.Blueprints; +using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -21,6 +26,29 @@ namespace osu.Game.Rulesets.Taiko.Edit { } + protected override Drawable CreateNewComboButton() => new NewComboTernaryButton + { + Current = NewCombo, + CreateIcon = () => new Container + { + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Icon = OsuIcon.EditorHit, + Size = new Vector2(15), + }, + new SpriteIcon + { + Icon = OsuIcon.EditorNewComboSparkles, + Size = new Vector2(20), + } + }, + }, + }; + protected override SelectionHandler CreateSelectionHandler() => new TaikoSelectionHandler(); public override HitObjectSelectionBlueprint CreateHitObjectBlueprintFor(HitObject hitObject) => diff --git a/osu.Game/Graphics/OsuIcon.cs b/osu.Game/Graphics/OsuIcon.cs index acebccc946..138ae45cfb 100644 --- a/osu.Game/Graphics/OsuIcon.cs +++ b/osu.Game/Graphics/OsuIcon.cs @@ -107,7 +107,7 @@ namespace osu.Game.Graphics public static IconUsage EditorDistanceSnap => get(OsuIconMapping.EditorDistanceSnap); public static IconUsage EditorFinish => get(OsuIconMapping.EditorFinish); public static IconUsage EditorGridSnap => get(OsuIconMapping.EditorGridSnap); - public static IconUsage EditorNewCombo => get(OsuIconMapping.EditorNewCombo); + public static IconUsage EditorNewComboSparkles => get(OsuIconMapping.EditorNewComboSparkles); public static IconUsage EditorSelect => get(OsuIconMapping.EditorSelect); public static IconUsage EditorSound => get(OsuIconMapping.EditorSound); public static IconUsage EditorWhistle => get(OsuIconMapping.EditorWhistle); @@ -459,8 +459,8 @@ namespace osu.Game.Graphics [Description(@"Editor/grid-snap")] EditorGridSnap, - [Description(@"Editor/new-combo")] - EditorNewCombo, + [Description(@"Editor/new-combo-sparkles")] + EditorNewComboSparkles, [Description(@"Editor/select")] EditorSelect, diff --git a/osu.Game/Screens/Edit/Components/TernaryButtons/NewComboTernaryButton.cs b/osu.Game/Screens/Edit/Components/TernaryButtons/NewComboTernaryButton.cs index 5226816043..a6977aca85 100644 --- a/osu.Game/Screens/Edit/Components/TernaryButtons/NewComboTernaryButton.cs +++ b/osu.Game/Screens/Edit/Components/TernaryButtons/NewComboTernaryButton.cs @@ -29,6 +29,8 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons { public partial class NewComboTernaryButton : CompositeDrawable, IHasCurrentValue { + public Func? CreateIcon { get; init; } + public Bindable Current { get => current.Current; @@ -61,7 +63,7 @@ namespace osu.Game.Screens.Edit.Components.TernaryButtons { Current = Current, Description = "New combo", - CreateIcon = () => new SpriteIcon { Icon = OsuIcon.EditorNewCombo }, + CreateIcon = CreateIcon, }, }, pickerButton = new ColourPickerButton diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index eff74bd556..dd2ddf6ee4 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -181,13 +181,50 @@ namespace osu.Game.Screens.Edit.Compose.Components public SampleBankTernaryButton[] SampleBankTernaryStates { get; private set; } + /// + /// Create the new combo ternary button. Mainly used to customize the displayed icon + /// depending on the ruleset. Can be overriden to return null if a ruleset does not + /// provide combo-supporting HitObjects. + /// + /// + [CanBeNull] + protected virtual Drawable CreateNewComboButton() => new NewComboTernaryButton + { + Current = NewCombo, + CreateIcon = () => new Container + { + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + // This is currently using the osu! hitcircle icon as a default in order + // not to break any custom rulesets that depend on there being a defined + // new combo button. + // Could consider removing it and let rulesets specify their own buttons/icons. + Icon = OsuIcon.EditorHitCircle, + Size = new Vector2(15), + }, + new SpriteIcon + { + Icon = OsuIcon.EditorNewComboSparkles, + Size = new Vector2(20), + } + }, + }, + }; + /// /// Create all ternary states required to be displayed to the user. /// protected virtual IEnumerable CreateTernaryButtons() { //TODO: this should only be enabled (visible?) for rulesets that provide combo-supporting HitObjects. - yield return new NewComboTernaryButton { Current = NewCombo }; + var newComboButton = CreateNewComboButton(); + + if (newComboButton != null) + yield return newComboButton; foreach (var kvp in SelectionHandler.SelectionSampleStates) {