Skip to content

Commit

Permalink
Merge pull request #30098 from bdach/editor/setup-screen-palette
Browse files Browse the repository at this point in the history
Implement "form" colour palette control
  • Loading branch information
peppy authored Oct 3, 2024
2 parents 4f823a3 + 19356d0 commit 7f6f9e0
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 0 deletions.
11 changes: 11 additions & 0 deletions osu.Game.Tests/Visual/UserInterface/TestSceneFormControls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ public TestSceneFormControls()
Caption = "Audio file",
PlaceholderText = "Select an audio file",
},
new FormColourPalette
{
Caption = "Combo colours",
Colours =
{
Colour4.Red,
Colour4.Green,
Colour4.Blue,
Colour4.Yellow,
}
},
},
},
}
Expand Down
226 changes: 226 additions & 0 deletions osu.Game/Graphics/UserInterfaceV2/FormColourPalette.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Specialized;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Resources.Localisation.Web;
using osuTK;

namespace osu.Game.Graphics.UserInterfaceV2
{
public partial class FormColourPalette : CompositeDrawable
{
public BindableList<Colour4> Colours { get; } = new BindableList<Colour4>();

public LocalisableString Caption { get; init; }
public LocalisableString HintText { get; init; }

private Box background = null!;
private FormFieldCaption caption = null!;
private FillFlowContainer flow = null!;

[Resolved]
private OverlayColourProvider colourProvider { get; set; } = null!;

[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;

Masking = true;
CornerRadius = 5;

RoundedButton button;

InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background5,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(9),
Spacing = new Vector2(7),
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
caption = new FormFieldCaption
{
Caption = Caption,
TooltipText = HintText,
},
flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Spacing = new Vector2(5),
Child = button = new RoundedButton
{
Action = () => Colours.Add(Colour4.White),
Size = new Vector2(70, 25),
Text = "+",
}
}
},
},
};

flow.SetLayoutPosition(button, float.MaxValue);
}

protected override void LoadComplete()
{
base.LoadComplete();

Colours.BindCollectionChanged((_, args) =>
{
if (args.Action != NotifyCollectionChangedAction.Replace)
updateColours();
}, true);
updateState();
}

protected override bool OnHover(HoverEvent e)
{
updateState();
return true;
}

protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
updateState();
}

private void updateState()
{
background.Colour = colourProvider.Background5;
caption.Colour = colourProvider.Content2;

BorderThickness = IsHovered ? 2 : 0;

if (IsHovered)
BorderColour = colourProvider.Light4;
}

private void updateColours()
{
flow.RemoveAll(d => d is ColourButton, true);

for (int i = 0; i < Colours.Count; ++i)
{
// copy to avoid accesses to modified closure.
int colourIndex = i;
var colourButton = new ColourButton { Current = { Value = Colours[colourIndex] } };
colourButton.Current.BindValueChanged(colour => Colours[colourIndex] = colour.NewValue);
colourButton.DeleteRequested = () => Colours.RemoveAt(flow.IndexOf(colourButton));
flow.Add(colourButton);
}
}

private partial class ColourButton : OsuClickableContainer, IHasPopover, IHasContextMenu
{
public Bindable<Colour4> Current { get; } = new Bindable<Colour4>();
public Action? DeleteRequested { get; set; }

private Box background = null!;
private OsuSpriteText hexCode = null!;

[BackgroundDependencyLoader]
private void load()
{
Size = new Vector2(70, 25);

Masking = true;
CornerRadius = 12.5f;
Action = this.ShowPopover;

Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
hexCode = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
};
}

protected override void LoadComplete()
{
base.LoadComplete();

Current.BindValueChanged(_ => updateState(), true);
}

public Popover GetPopover() => new ColourPickerPopover
{
Current = { BindTarget = Current }
};

public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => DeleteRequested?.Invoke())
};

private void updateState()
{
background.Colour = Current.Value;
hexCode.Text = Current.Value.ToHex();
hexCode.Colour = OsuColour.ForegroundTextColourFor(Current.Value);
}
}

private partial class ColourPickerPopover : OsuPopover, IHasCurrentValue<Colour4>
{
public Bindable<Colour4> Current
{
get => current.Current;
set => current.Current = value;
}

private readonly BindableWithCurrent<Colour4> current = new BindableWithCurrent<Colour4>();

public ColourPickerPopover()
: base(false)
{
}

[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Child = new OsuColourPicker
{
Current = { BindTarget = Current }
};

Body.BorderThickness = 2;
Body.BorderColour = colourProvider.Highlight1;
Content.Padding = new MarginPadding(2);
}
}
}
}

0 comments on commit 7f6f9e0

Please sign in to comment.