Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: generic dynamic context menu + reel gallery integration #3071

Merged
merged 45 commits into from
Jan 20, 2025
Merged
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8523506
wip
lorenzo-ranciaffi Jan 3, 2025
e3ab563
wip
lorenzo-ranciaffi Jan 3, 2025
5943bbd
wip
lorenzo-ranciaffi Jan 3, 2025
e1bc7fc
fix scriptable objects
lorenzo-ranciaffi Jan 3, 2025
b52e8d2
improved SO
lorenzo-ranciaffi Jan 3, 2025
ac6c09d
fix and integration into reel gallery
lorenzo-ranciaffi Jan 3, 2025
5d0e5ad
extension and initial values
lorenzo-ranciaffi Jan 6, 2025
06caf8c
moved all SO in own classes
lorenzo-ranciaffi Jan 6, 2025
abfb65f
fix toggle graphics
lorenzo-ranciaffi Jan 6, 2025
278bc57
added readonly serialized attribute drawer
lorenzo-ranciaffi Jan 6, 2025
aae1e1e
context menu conversion test
lorenzo-ranciaffi Jan 6, 2025
e0b2e48
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 6, 2025
2039858
fix custom property naming
lorenzo-ranciaffi Jan 6, 2025
2fc3279
more test conversion
lorenzo-ranciaffi Jan 6, 2025
682984a
fix context menu position
lorenzo-ranciaffi Jan 6, 2025
22ae88b
avoid layout rebuild for height calculation
lorenzo-ranciaffi Jan 6, 2025
18aed20
restructured components
lorenzo-ranciaffi Jan 6, 2025
939556f
more generic management of components
lorenzo-ranciaffi Jan 7, 2025
d5484f3
reference lambdas and values by config instead of indexes
lorenzo-ranciaffi Jan 7, 2025
e25937d
more generic pool manager
lorenzo-ranciaffi Jan 7, 2025
41121a5
expose elements spacing in context menu config
lorenzo-ranciaffi Jan 7, 2025
c03f7d0
exposed layout settings
lorenzo-ranciaffi Jan 7, 2025
d73b1cc
fix reel gallery context menu behavior
lorenzo-ranciaffi Jan 7, 2025
2bb9953
removed unused import
lorenzo-ranciaffi Jan 7, 2025
660b442
ported camera reel gallery context menu to new generic architecture
lorenzo-ranciaffi Jan 8, 2025
f694ec9
fix asmdef pointers
lorenzo-ranciaffi Jan 8, 2025
8cfd7d3
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 8, 2025
cf7e072
add required component
lorenzo-ranciaffi Jan 8, 2025
7b56cb9
changed class names
lorenzo-ranciaffi Jan 9, 2025
9d76599
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 9, 2025
18c2b99
use asset references instead of indexes
lorenzo-ranciaffi Jan 9, 2025
6c262a0
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 9, 2025
8b49855
added generic types for component configure method
lorenzo-ranciaffi Jan 10, 2025
06dbf24
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 10, 2025
dfae86e
moved scriptable object logic to code builder
lorenzo-ranciaffi Jan 14, 2025
53a71a1
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 14, 2025
07ce827
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 16, 2025
b3e145a
suggested modifications
lorenzo-ranciaffi Jan 16, 2025
98829f0
removed unnecessary abstraction layer in components
lorenzo-ranciaffi Jan 16, 2025
f5ad975
refactored context menu closing task
lorenzo-ranciaffi Jan 16, 2025
2e59aee
removed background button click event and used onClickAsync directly …
lorenzo-ranciaffi Jan 16, 2025
a60cde0
prevent double Hide invocation
lorenzo-ranciaffi Jan 16, 2025
b3a0421
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 16, 2025
c6bdcf7
Merge branch 'dev' into feat/dynamic-context-menu
lorenzo-ranciaffi Jan 20, 2025
c71845d
removed generic Delegate and initial value. Moved everything as typed…
lorenzo-ranciaffi Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip
lorenzo-ranciaffi committed Jan 3, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit e3ab56367bd2494cb88014a090e68d7b7eda7afb
Original file line number Diff line number Diff line change
@@ -8,14 +8,32 @@ namespace DCL.UI.GenericContextMenu.Controls
[Serializable]
public class GenericContextMenuConfig : ScriptableObject
{
[SerializeField] private List<ContextMenuControlSettings> contextMenuSettings = new ();
public List<ContextMenuControlSettings> ContextMenuSettings { get; private set; } = new ();
public Vector2 OffsetFromTarget { get; private set; } = Vector2.zero;
}

[Serializable]
public class ContextMenuControlSettings
public class ContextMenuControlSettings : ScriptableObject
{
public ContextMenuControlTypes controlTypeType;
public string text;
public Sprite icon;
}

[Serializable]
public class ButtonContextMenuControlSettings : ContextMenuControlSettings
{
public string buttonText;
public Sprite buttonIcon;
}

[Serializable]
public class ToggleContextMenuControlSettings : ContextMenuControlSettings
{
public string buttonText;
public Sprite buttonIcon;
}

[Serializable]
public class SeparatorContextMenuControlSettings : ContextMenuControlSettings
{
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using DCL.UI.GenericContextMenu.Controls;
using System;
using UnityEngine;
using UnityEngine.UI;

namespace DCL.UI.GenericContextMenu
{
@@ -10,11 +13,29 @@ public ControlsPoolManager()

}

public GameObject GetSeparator()
{
throw new NotImplementedException();
}

public Button GetButton(ButtonContextMenuControlSettings settings)
{
throw new NotImplementedException();
}

public Toggle GetToggle(ToggleContextMenuControlSettings settings)
{
throw new NotImplementedException();
}

public void Dispose()
{

}

public void ReleaseAllCurrentControls()
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,156 @@
using Cysharp.Threading.Tasks;
using DCL.UI.GenericContextMenu.Controls;
using MVC;
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;

namespace DCL.UI.GenericContextMenu
{
public class GenericContextMenuController : ControllerBase<GenericContextMenuView, GenericContextMenuParameter>
{
private enum ContextMenuOpenDirection
{
BOTTOM_RIGHT,
BOTTOM_LEFT,
TOP_LEFT,
TOP_RIGHT
}

public override CanvasOrdering.SortingLayer Layer => CanvasOrdering.SortingLayer.Popup;

private readonly ControlsPoolManager controlsPoolManager;

private RectTransform viewRectTransform;
private bool isClosing;

public GenericContextMenuController(ViewFactoryMethod viewFactory) : base(viewFactory)
{
controlsPoolManager = new ControlsPoolManager();
}

protected override void OnViewInstantiated()
{
viewInstance!.BackgroundCloseButtonClicked += BackgroundCloseButtonClicked;
viewRectTransform = viewInstance!.GetComponent<RectTransform>();

viewInstance!.BackgroundCloseButtonClicked += TriggerContextMenuClose;
}

protected override void OnViewShow()
protected override void OnBeforeViewShow()
{
isClosing = false;

ConfigureContextMenu();
}

private void BackgroundCloseButtonClicked() => isClosing = true;
private void ConfigureContextMenu()
{
for (var i = 0; i < inputData.Config.ContextMenuSettings.Count; i++)
{
ContextMenuControlSettings config = inputData.Config.ContextMenuSettings[i];

switch (config.controlTypeType)
{
case ContextMenuControlTypes.SEPARATOR:
controlsPoolManager.GetSeparator();
break;
case ContextMenuControlTypes.BUTTON_WITH_TEXT_AND_ICON:
Button button = controlsPoolManager.GetButton(config as ButtonContextMenuControlSettings);
button.onClick.AddListener(inputData.ControlsActions[i] as UnityEngine.Events.UnityAction);
button.onClick.AddListener(TriggerContextMenuClose);
break;
case ContextMenuControlTypes.TOGGLE_WITH_TEXT:
Toggle toggle = controlsPoolManager.GetToggle(config as ToggleContextMenuControlSettings);
toggle.onValueChanged.AddListener(inputData.ControlsActions[i] as UnityEngine.Events.UnityAction<bool>);
toggle.onValueChanged.AddListener((toggleValue) => TriggerContextMenuClose());
break;
}
}

viewInstance!.ControlsContainer.localPosition = GetControlsPosition(inputData.AnchorPosition, inputData.Config.OffsetFromTarget);
}

private Vector2 GetOffsetByDirection(ContextMenuOpenDirection direction, Vector2 offsetFromTarget)
{
return direction switch
{
ContextMenuOpenDirection.BOTTOM_RIGHT => offsetFromTarget,
ContextMenuOpenDirection.BOTTOM_LEFT => new Vector2(-offsetFromTarget.x - viewInstance!.ControlsContainer.rect.width, offsetFromTarget.y),
ContextMenuOpenDirection.TOP_RIGHT => new Vector2(offsetFromTarget.x, -offsetFromTarget.y + viewInstance!.ControlsContainer.rect.height),
ContextMenuOpenDirection.TOP_LEFT => new Vector2(-offsetFromTarget.x - viewInstance!.ControlsContainer.rect.width, -offsetFromTarget.y + viewInstance!.ControlsContainer.rect.height),
_ => Vector3.zero
};
}

private Vector3 GetControlsPosition(Vector2 anchorPosition, Vector2 offsetFromTarget)
{
Vector3 position = viewRectTransform.InverseTransformPoint(anchorPosition);
position.x += viewInstance!.ControlsContainer.rect.width / 2;
position.y -= viewInstance!.ControlsContainer.rect.height / 2;

Vector3 newPosition = Vector3.zero;
float minNonOverlappingArea = float.MaxValue;
foreach (ContextMenuOpenDirection enumVal in Enum.GetValues(typeof(ContextMenuOpenDirection)))
{
Vector2 offsetByDirection = GetOffsetByDirection(enumVal, offsetFromTarget);
Vector3 currentPosition = position + new Vector3(offsetByDirection.x, offsetByDirection.y, 0);
float nonOverlappingArea = CalculateNonOverlappingArea(viewRectTransform.rect, GetProjectedRect(currentPosition));
if (nonOverlappingArea < minNonOverlappingArea)
{
newPosition = currentPosition;
minNonOverlappingArea = nonOverlappingArea;
}
}

return newPosition;
}

private float CalculateNonOverlappingArea(Rect rect1, Rect rect2)
{
float area1 = rect1.width * rect1.height;
float area2 = rect2.width * rect2.height;

Rect intersection = Rect.MinMaxRect(
Mathf.Max(rect1.xMin, rect2.xMin),
Mathf.Max(rect1.yMin, rect2.yMin),
Mathf.Min(rect1.xMax, rect2.xMax),
Mathf.Min(rect1.yMax, rect2.yMax)
);

float intersectionArea = 0;

if (intersection is { width: > 0, height: > 0 })
intersectionArea = intersection.width * intersection.height;

return area1 + area2 - intersectionArea;
}

private Rect GetProjectedRect(Vector3 newPosition)
{
Vector3 originalPosition = viewInstance!.ControlsContainer.localPosition;
viewInstance!.ControlsContainer.localPosition = newPosition;
Rect rect = GetWorldRect(viewInstance!.ControlsContainer);
viewInstance!.ControlsContainer.localPosition = originalPosition;

return rect;
}

private static Rect GetWorldRect(RectTransform rectTransform)
{
var corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
Vector2 min = corners[0];
Vector2 max = corners[2];
Vector2 size = max - min;
return new Rect(min, size);
}

protected override void OnViewClose() =>
controlsPoolManager.ReleaseAllCurrentControls();

private void TriggerContextMenuClose() => isClosing = true;

protected override UniTask WaitForCloseIntentAsync(CancellationToken ct) =>
UniTask.WhenAny(UniTask.WaitUntil(() => isClosing, cancellationToken: ct));
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
using DCL.UI.GenericContextMenu.Controls;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace DCL.UI.GenericContextMenu
{
public struct GenericContextMenuParameter
{
public readonly GenericContextMenuConfig config;
public readonly GenericContextMenuConfig Config;
public readonly Dictionary<int, Delegate> ControlsActions;
public readonly Vector2 AnchorPosition;

public GenericContextMenuParameter(GenericContextMenuConfig config)
public GenericContextMenuParameter(GenericContextMenuConfig config, Dictionary<int, Delegate> controlsActions, Vector2 anchorPosition)
{
this.config = config;
this.Config = config;
this.ControlsActions = controlsActions;
this.AnchorPosition = anchorPosition;
}
}
}