[Add] Menu with configs and size fix
This commit is contained in:
@@ -12,9 +12,11 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
private const string ContentImagePath = "Content/Image";
|
||||
private const string ContentLabelPath = "Content/Text (TMP)";
|
||||
private const string ContentPath = "Content";
|
||||
|
||||
[SerializeField] private Button button;
|
||||
[SerializeField] private Image backgroundImage;
|
||||
[SerializeField] private RectTransform contentRoot;
|
||||
[SerializeField] private Image contentImage;
|
||||
[SerializeField] private TMP_Text label;
|
||||
|
||||
@@ -28,10 +30,11 @@ namespace Minesweeper.Presentation.Views
|
||||
public event Action PressStarted;
|
||||
public event Action PressEnded;
|
||||
|
||||
public void Bind(Button button, Image backgroundImage, Image contentImage, TMP_Text label)
|
||||
public void Bind(Button button, Image backgroundImage, RectTransform contentRoot, Image contentImage, TMP_Text label)
|
||||
{
|
||||
this.button = button;
|
||||
this.backgroundImage = backgroundImage;
|
||||
this.contentRoot = contentRoot;
|
||||
this.contentImage = contentImage;
|
||||
this.label = label;
|
||||
}
|
||||
@@ -48,6 +51,15 @@ namespace Minesweeper.Presentation.Views
|
||||
backgroundImage = GetComponent<Image>();
|
||||
}
|
||||
|
||||
if (contentRoot == null)
|
||||
{
|
||||
var contentTransform = transform.Find(ContentPath);
|
||||
if (contentTransform != null)
|
||||
{
|
||||
contentRoot = contentTransform.GetComponent<RectTransform>();
|
||||
}
|
||||
}
|
||||
|
||||
if (contentImage == null)
|
||||
{
|
||||
var contentImageTransform = transform.Find(ContentImagePath);
|
||||
@@ -82,12 +94,18 @@ namespace Minesweeper.Presentation.Views
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, bool revealUnflaggedMines)
|
||||
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, float contentPadding, bool revealUnflaggedMines)
|
||||
{
|
||||
gameObject.name = $"bt_{cell.X}_{cell.Y}_{cell.DisplayValue}";
|
||||
isOpened = cell.IsOpened;
|
||||
var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;
|
||||
|
||||
if (contentRoot != null)
|
||||
{
|
||||
contentRoot.offsetMin = new Vector2(contentPadding, contentPadding);
|
||||
contentRoot.offsetMax = new Vector2(-contentPadding, -contentPadding);
|
||||
}
|
||||
|
||||
if (backgroundImage != null)
|
||||
{
|
||||
backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
private const float ResizeRefreshDelaySeconds = 0.5f;
|
||||
private const float ResizeSizeEpsilon = 0.5f;
|
||||
private const float MaximumCellPixelsPerUnitMultiplier = 1f;
|
||||
private const float ContentPaddingReferenceCellSize = 202f;
|
||||
private const float ContentPaddingReferencePadding = 15f;
|
||||
private const float MinimumContentPadding = 1f;
|
||||
|
||||
[SerializeField] private GameObject gameRoot;
|
||||
[SerializeField] private GameObject pauseRoot;
|
||||
@@ -37,6 +41,7 @@ namespace Minesweeper.Presentation.Views
|
||||
private bool resizeRefreshPending;
|
||||
private int currentBoardWidth;
|
||||
private int currentBoardHeight;
|
||||
private float currentContentPadding = MinimumContentPadding;
|
||||
private float currentPixelsPerUnitMultiplier = 1f;
|
||||
private float resizeStableAt;
|
||||
private Vector2 lastObservedLayoutSize;
|
||||
@@ -195,7 +200,7 @@ namespace Minesweeper.Presentation.Views
|
||||
var cell = cells[i];
|
||||
if (cellsByCoordinate.TryGetValue(ToKey(cell.X, cell.Y), out var view))
|
||||
{
|
||||
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, revealUnflaggedMines);
|
||||
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier, currentContentPadding, revealUnflaggedMines);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,10 +353,16 @@ namespace Minesweeper.Presentation.Views
|
||||
gridLayoutGroup.padding = new RectOffset();
|
||||
gridLayoutGroup.spacing = new Vector2(spacing, spacing);
|
||||
gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
|
||||
currentPixelsPerUnitMultiplier = uiConfig.ReferenceCellSize / cellSize;
|
||||
currentPixelsPerUnitMultiplier = Mathf.Min(MaximumCellPixelsPerUnitMultiplier, uiConfig.ReferenceCellSize / cellSize);
|
||||
currentContentPadding = CalculateContentPadding(cellSize);
|
||||
lastObservedLayoutSize = layoutSize;
|
||||
}
|
||||
|
||||
private static float CalculateContentPadding(float cellSize)
|
||||
{
|
||||
return Mathf.Max(MinimumContentPadding, cellSize * ContentPaddingReferencePadding / ContentPaddingReferenceCellSize);
|
||||
}
|
||||
|
||||
private Vector2 GetLayoutSourceSize()
|
||||
{
|
||||
var parentRect = boardPanel.parent as RectTransform;
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
using System;
|
||||
using Minesweeper.Core;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public interface IMainMenuView : IView
|
||||
{
|
||||
event Action StartClicked;
|
||||
event Action SizeChanged;
|
||||
|
||||
GameSettingsValue SelectedSettings { get; }
|
||||
|
||||
void Show();
|
||||
void Hide();
|
||||
void ConfigureSizeX(int min, int max, int value);
|
||||
void ConfigureSizeY(int min, int max, int value);
|
||||
void ConfigureMines(int min, int max, int value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Minesweeper.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -8,8 +9,14 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
[SerializeField] private GameObject root;
|
||||
[SerializeField] private Button startButton;
|
||||
[SerializeField] private MenuSliderView sizeXSlider = new MenuSliderView();
|
||||
[SerializeField] private MenuSliderView sizeYSlider = new MenuSliderView();
|
||||
[SerializeField] private MenuSliderView minesSlider = new MenuSliderView();
|
||||
|
||||
public event Action StartClicked;
|
||||
public event Action SizeChanged;
|
||||
|
||||
public GameSettingsValue SelectedSettings => new GameSettingsValue(sizeXSlider.Value, sizeYSlider.Value, minesSlider.Value);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -17,8 +24,6 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
root = gameObject;
|
||||
}
|
||||
|
||||
AutoBind();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
@@ -27,6 +32,12 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
startButton.onClick.AddListener(OnStartClicked);
|
||||
}
|
||||
|
||||
sizeXSlider.ValueChanged += OnSizeSliderChanged;
|
||||
sizeYSlider.ValueChanged += OnSizeSliderChanged;
|
||||
sizeXSlider.AddListeners();
|
||||
sizeYSlider.AddListeners();
|
||||
minesSlider.AddListeners();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@@ -35,6 +46,12 @@ namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
startButton.onClick.RemoveListener(OnStartClicked);
|
||||
}
|
||||
|
||||
sizeXSlider.RemoveListeners();
|
||||
sizeYSlider.RemoveListeners();
|
||||
minesSlider.RemoveListeners();
|
||||
sizeXSlider.ValueChanged -= OnSizeSliderChanged;
|
||||
sizeYSlider.ValueChanged -= OnSizeSliderChanged;
|
||||
}
|
||||
|
||||
public void Show()
|
||||
@@ -47,27 +64,34 @@ namespace Minesweeper.Presentation.Views
|
||||
root.SetActive(false);
|
||||
}
|
||||
|
||||
public void ConfigureSizeX(int min, int max, int value)
|
||||
{
|
||||
sizeXSlider.Configure(min, max, value, "Size X");
|
||||
}
|
||||
|
||||
public void ConfigureSizeY(int min, int max, int value)
|
||||
{
|
||||
sizeYSlider.Configure(min, max, value, "Size Y");
|
||||
}
|
||||
|
||||
public void ConfigureMines(int min, int max, int value)
|
||||
{
|
||||
minesSlider.Configure(min, max, value, "Mines Count");
|
||||
}
|
||||
|
||||
private void OnStartClicked()
|
||||
{
|
||||
StartClicked?.Invoke();
|
||||
}
|
||||
|
||||
public void Bind(GameObject root, Button startButton)
|
||||
public void BindRoot(GameObject root)
|
||||
{
|
||||
this.root = root != null ? root : gameObject;
|
||||
this.startButton = startButton;
|
||||
}
|
||||
|
||||
private void AutoBind()
|
||||
private void OnSizeSliderChanged(int value)
|
||||
{
|
||||
if (startButton == null)
|
||||
{
|
||||
var startButtonTransform = transform.Find("StartButton");
|
||||
if (startButtonTransform != null)
|
||||
{
|
||||
startButton = startButtonTransform.GetComponent<Button>();
|
||||
}
|
||||
}
|
||||
SizeChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class MenuSliderView
|
||||
{
|
||||
[SerializeField] private Slider slider;
|
||||
[SerializeField] private TMP_Text minText;
|
||||
[SerializeField] private TMP_Text maxText;
|
||||
[SerializeField] private TMP_Text valueText;
|
||||
[SerializeField] private string valueLabel;
|
||||
|
||||
public event Action<int> ValueChanged;
|
||||
|
||||
public int Value => slider != null ? Mathf.RoundToInt(slider.value) : 0;
|
||||
|
||||
public void Bind(Slider slider, TMP_Text minText, TMP_Text maxText, TMP_Text valueText)
|
||||
{
|
||||
RemoveListeners();
|
||||
this.slider = slider;
|
||||
this.minText = minText;
|
||||
this.maxText = maxText;
|
||||
this.valueText = valueText;
|
||||
AddListeners();
|
||||
}
|
||||
|
||||
public void AddListeners()
|
||||
{
|
||||
if (slider != null)
|
||||
{
|
||||
slider.onValueChanged.AddListener(OnValueChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveListeners()
|
||||
{
|
||||
if (slider != null)
|
||||
{
|
||||
slider.onValueChanged.RemoveListener(OnValueChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(int min, int max, int value, string label)
|
||||
{
|
||||
if (slider == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
valueLabel = label;
|
||||
var clampedMax = Mathf.Max(min, max);
|
||||
var clampedValue = Mathf.Clamp(value, min, clampedMax);
|
||||
slider.wholeNumbers = true;
|
||||
slider.minValue = min;
|
||||
slider.maxValue = clampedMax;
|
||||
slider.SetValueWithoutNotify(clampedValue);
|
||||
SetText(minText, min);
|
||||
SetText(maxText, clampedMax);
|
||||
SetValueText(clampedValue);
|
||||
}
|
||||
|
||||
private void OnValueChanged(float value)
|
||||
{
|
||||
var intValue = Mathf.RoundToInt(value);
|
||||
SetValueText(intValue);
|
||||
ValueChanged?.Invoke(intValue);
|
||||
}
|
||||
|
||||
private void SetValueText(int value)
|
||||
{
|
||||
if (valueText != null)
|
||||
{
|
||||
valueText.text = string.IsNullOrEmpty(valueLabel) ? value.ToString() : $"{valueLabel}: {value}";
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetText(TMP_Text text, int value)
|
||||
{
|
||||
if (text != null)
|
||||
{
|
||||
text.text = value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15be53f58e067f944a33854111083046
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Minesweeper.Core;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
@@ -10,6 +11,14 @@ namespace Minesweeper.Presentation.Views
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action SizeChanged
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public GameSettingsValue SelectedSettings => default;
|
||||
|
||||
public void Show()
|
||||
{
|
||||
}
|
||||
@@ -17,5 +26,17 @@ namespace Minesweeper.Presentation.Views
|
||||
public void Hide()
|
||||
{
|
||||
}
|
||||
|
||||
public void ConfigureSizeX(int min, int max, int value)
|
||||
{
|
||||
}
|
||||
|
||||
public void ConfigureSizeY(int min, int max, int value)
|
||||
{
|
||||
}
|
||||
|
||||
public void ConfigureMines(int min, int max, int value)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user