riversong code showcase

This commit is contained in:
Daniele Marotta
2026-05-21 15:52:18 +02:00
commit 4c9eea1c02
462 changed files with 23406 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
using Unity.Properties;
using UnityEngine;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class BuildingPanelModel : UIModel
{
private Building _building;
private string _buildingName;
private string _buildingDescription;
private Sprite _buildingIcon;
private bool _showNoHouseNearbyWarning;
public Building Building
{
get => _building;
private set => SetProperty(ref _building, value);
}
[CreateProperty]
public string BuildingDescription
{
get => _buildingDescription;
private set => SetProperty(ref _buildingDescription, value);
}
[CreateProperty]
public string BuildingName
{
get => _buildingName;
private set => SetProperty(ref _buildingName, value);
}
[CreateProperty]
public Sprite BuildingIcon
{
get => _buildingIcon;
private set => SetProperty(ref _buildingIcon, value);
}
[CreateProperty]
public bool ShowNoHouseNearbyWarning
{
get => _showNoHouseNearbyWarning;
set => SetProperty(ref _showNoHouseNearbyWarning, value);
}
public HousePanelModel HouseModel { get; } = new();
public StoragePanelModel StorageModel { get; } = new();
public void SetBuilding(Building building)
{
Building = building;
BuildingName = Building?.Definition.BuildingName;
BuildingDescription = Building?.Definition.BuildingDescription;
BuildingIcon = Building?.Definition.Icon;
}
}
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class BuildingPanelUIController : UIControllerSystem<BuildingPanelUIView>, IDisposable, IUpdatable
{
[InjectService]
private ISignalBus _signalBus;
[InjectService]
private IProductCatalog _productCatalog;
[InjectService]
private UIState _uiState;
[InjectService]
private World _world;
private BuildingPanelModel _model;
private List<IBuildingPanelSectionPresenter> _sectionPresenters;
public BuildingPanelUIController(IServiceLocator serviceLocator) : base(serviceLocator)
{
}
protected override BuildingPanelUIView View => UIRoot.GetView<BuildingPanelUIView>();
public override async UniTask InitializeAsync()
{
await base.InitializeAsync();
_model = new BuildingPanelModel();
_sectionPresenters = new List<IBuildingPanelSectionPresenter>
{
new HouseBuildingPanelSectionPresenter(_productCatalog),
new StorageBuildingPanelSectionPresenter(_productCatalog)
};
View.SetModel(_model);
View.Show(false);
_signalBus.Subscribe<SelectedBuildingChangedSignal>(OnSelectedBuildingChanged);
_signalBus.Subscribe<BuildingUpgradedSignal>(OnBuildingUpgraded);
}
public void Dispose()
{
_signalBus.Unsubscribe<SelectedBuildingChangedSignal>(OnSelectedBuildingChanged);
_signalBus.Unsubscribe<BuildingUpgradedSignal>(OnBuildingUpgraded);
}
private void OnSelectedBuildingChanged(SelectedBuildingChangedSignal signal)
{
var selectedBuilding = signal.NewSelection;
if (selectedBuilding != null)
{
_model.SetBuilding(selectedBuilding);
InitializeSections(selectedBuilding);
UpdateWorkerHousingWarning(selectedBuilding);
}
View.Show(selectedBuilding != null, true);
}
private void InitializeSections(Building building)
{
foreach (var sectionPresenter in _sectionPresenters)
{
if (!sectionPresenter.IsSectionRelevant(building)) continue;
sectionPresenter.InitializeSection(_model, building);
}
}
public void Update()
{
var building = _model.Building;
if (building == null) return;
foreach (var sectionPresenter in _sectionPresenters)
{
if (!sectionPresenter.IsSectionRelevant(building)) continue;
sectionPresenter.UpdateSection(_model, building);
}
UpdateWorkerHousingWarning(building);
}
private void OnBuildingUpgraded(BuildingUpgradedSignal signal)
{
if (signal.Building != _uiState.SelectedBuilding) return;
InitializeSections(signal.Building);
}
private void UpdateWorkerHousingWarning(Building building)
{
_model.ShowNoHouseNearbyWarning = false;
if (building == null || _world.TimeState.DayNightCycleStep != DayNightCycleStep.Day) return;
ref var sleepState = ref building.GetSleepStateRW();
_model.ShowNoHouseNearbyWarning = sleepState.HasHomelessWorkers;
}
}
}

View File

@@ -0,0 +1,57 @@
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
[UIView("building-panel")]
public class BuildingPanelUIView : UIView<BuildingPanelModel>
{
private VisualElement _noHouseNearbyWarning;
private HousePanelUIView _housePanel;
private StoragePanelUIView _storagePanel;
public override async UniTask InitializeAsync(UIService uiService, VisualElement rootElement)
{
await base.InitializeAsync(uiService, rootElement);
_noHouseNearbyWarning = rootElement.Q<VisualElement>(className: "building-panel__no-house-nearby-warning");
_housePanel = (HousePanelUIView)await uiService.CreateView(typeof(HousePanelUIView), rootElement.Q(className: "building-panel__house-panel"));
_storagePanel = (StoragePanelUIView)await uiService.CreateView(typeof(StoragePanelUIView), rootElement.Q(className: "building-panel__storage-panel"));
}
protected override void OnNewModel(BuildingPanelModel model)
{
base.OnNewModel(model);
_housePanel.SetModel(model.HouseModel);
_storagePanel.SetModel(model.StorageModel);
}
protected override void OnModelPropertyChanged(object sender, BindablePropertyChangedEventArgs e)
{
base.OnModelPropertyChanged(sender, e);
switch (e.propertyName)
{
case nameof(BuildingPanelModel.Building):
_housePanel.Show(Model.Building != null && Model.Building.Definition.IsHouse);
_storagePanel.Show(Model.Building != null && Model.Building.Definition.IsStorage);
UpdateNoHouseNearbyWarning();
break;
case nameof(BuildingPanelModel.ShowNoHouseNearbyWarning):
UpdateNoHouseNearbyWarning();
break;
}
}
private void UpdateNoHouseNearbyWarning()
{
_noHouseNearbyWarning.style.display = Model.Building != null && Model.ShowNoHouseNearbyWarning ? DisplayStyle.Flex : DisplayStyle.None;
}
}
}

View File

@@ -0,0 +1,92 @@
using Unity.Mathematics;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HouseBuildingPanelSectionPresenter : IBuildingPanelSectionPresenter
{
private readonly IProductCatalog _productCatalog;
public HouseBuildingPanelSectionPresenter(IProductCatalog productCatalog)
{
_productCatalog = productCatalog;
}
public bool IsSectionRelevant(Building building)
{
return building.Definition.IsHouse;
}
public void InitializeSection(BuildingPanelModel model, Building building)
{
var tiers = building.Definition.HouseTiers;
var tierIndex = building.TierIndex;
var maxTier = math.min(tierIndex, tiers.Count - 1);
ref var needsState = ref building.GetNeedsStateRW();
model.HouseModel.Needs.Clear();
var rowIndex = 0;
for (var i = 0; i <= maxTier; i++)
{
var tier = tiers[i];
foreach (var need in tier.Needs)
{
var needModel = new HousePanelNeedModel();
switch (need.Type)
{
case PopulationNeedType.Product:
needModel.Initialize(rowIndex++, need.Product.ProductName, need.Product.Icon, 0, need.YieldOnFetch);
break;
}
model.HouseModel.Needs.Add(needModel);
}
}
model.HouseModel.AllNeedsMet = needsState.AllNeedsMet;
model.HouseModel.UpgradeState = needsState.UpgradeState;
model.HouseModel.UpgradeMaterials.Clear();
if (tierIndex < tiers.Count)
foreach (var productAmount in tiers[tierIndex].UpgradeMaterials)
model.HouseModel.UpgradeMaterials.Add(new HousePanelUpgradeMaterialModel(productAmount));
model.HouseModel.NotifyChanged();
}
public void UpdateSection(BuildingPanelModel model, Building building)
{
ref var needsState = ref building.GetNeedsStateRW();
ref var storage = ref building.GetStorageRW();
for (var i = 0; i < needsState.Needs.Length; i++)
{
var need = needsState.Needs[i];
if (need.TierIndex > building.TierIndex) break;
var needModel = model.HouseModel.Needs[i];
switch (need.Type)
{
case PopulationNeedType.Product:
needModel.UpdateValue(need.Current);
break;
}
}
model.HouseModel.AllNeedsMet = needsState.AllNeedsMet;
model.HouseModel.UpgradeState = needsState.UpgradeState;
foreach (var upgradeMaterialModel in model.HouseModel.UpgradeMaterials)
{
var productHandle = _productCatalog.GetHandle(upgradeMaterialModel.Product);
upgradeMaterialModel.Remaining = math.max(upgradeMaterialModel.Needed - storage.AvailableNow(productHandle), 0);
upgradeMaterialModel.Done &= needsState.UpgradeState != TierUpgradeState.NotReady;
upgradeMaterialModel.Done |= upgradeMaterialModel.Remaining <= 0;
}
}
}
}

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HousePanelModel : UIModel
{
private bool _allNeedsMet;
private TierUpgradeState _upgradeState;
public List<HousePanelNeedModel> Needs { get; } = new();
public bool AllNeedsMet
{
get => _allNeedsMet;
set => SetProperty(ref _allNeedsMet, value);
}
public TierUpgradeState UpgradeState
{
get => _upgradeState;
set => SetProperty(ref _upgradeState, value);
}
public List<HousePanelUpgradeMaterialModel> UpgradeMaterials { get; } = new();
}
}

View File

@@ -0,0 +1,43 @@
using Unity.Properties;
using UnityEngine;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HousePanelNeedModel : UIModel
{
private int _value;
private int _max;
public int RowIndex { get; set; }
[CreateProperty] public string Name { get; set; }
[CreateProperty] public Sprite Icon { get; set; }
[CreateProperty] public int Value { get; set; }
[CreateProperty] public int Max { get; set; }
[CreateProperty] public string ValueString => $"{Mathf.RoundToInt(100 * Mathf.Clamp01(Value / (float)Max))}%";
public void Initialize(int rowIndex, string name, Sprite icon, int value, int max)
{
RowIndex = rowIndex;
Name = name;
Icon = icon;
Value = value;
Max = max;
}
public void UpdateValue(int value)
{
if (Value == value) return;
Value = value;
NotifyPropertyChanged(nameof(Value));
NotifyPropertyChanged(nameof(ValueString));
}
}
}

View File

@@ -0,0 +1,47 @@
using Cysharp.Threading.Tasks;
using Unity.Mathematics;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HousePanelNeedUIView : UIView<HousePanelNeedModel>
{
private VisualElement _valueBarFill;
public override UniTask InitializeAsync(UIService uiService, VisualElement rootElement)
{
base.InitializeAsync(uiService, rootElement);
_valueBarFill = rootElement.Q<VisualElement>(className: "house-panel__need-value-bar-fill");
return UniTask.CompletedTask;
}
protected override void OnNewModel(HousePanelNeedModel model)
{
base.OnNewModel(model);
if (model.RowIndex % 2 == 0) RootElement.Q(className: "house-panel__need").AddToClassList("alt");
UpdateValueBar();
}
protected override void OnModelPropertyChanged(object sender, BindablePropertyChangedEventArgs e)
{
base.OnModelPropertyChanged(sender, e);
switch (e.propertyName)
{
case nameof(HousePanelNeedModel.Value):
UpdateValueBar();
break;
}
}
private void UpdateValueBar()
{
var fillPercent = math.clamp((float)Model.Value / Model.Max, 0, 1) * 100;
_valueBarFill.style.width = Length.Percent(fillPercent);
}
}
}

View File

@@ -0,0 +1,116 @@
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HousePanelUIView : UIView<HousePanelModel>
{
private VisualElement _needs;
private Label _notReadyNeedsNotMetLabel;
private Label _notReadyNeedsMetLabel;
private Label _fetchingMaterialsLabel;
private Label _maxedOutLabel;
private VisualElement _upgradeMaterials;
public override UniTask InitializeAsync(UIService uiService, VisualElement rootElement)
{
base.InitializeAsync(uiService, rootElement);
_needs = rootElement.Q<VisualElement>(className: "house-panel__needs");
_notReadyNeedsNotMetLabel = rootElement.Q<Label>(className: "house-panel__not-ready-needs-not-met");
_notReadyNeedsMetLabel = rootElement.Q<Label>(className: "house-panel__not-ready-needs-met");
_fetchingMaterialsLabel = rootElement.Q<Label>(className: "house-panel__fetching-materials");
_maxedOutLabel = rootElement.Q<Label>(className: "house-panel__maxed-out");
_upgradeMaterials = rootElement.Q<VisualElement>(className: "house-panel__upgrade-materials");
_ = AnimateHouseUpgradeStatusAsync();
return UniTask.CompletedTask;
}
protected override void OnModelChanged()
{
base.OnModelChanged();
_ = CreateNeedViewsAsync();
_ = CreateUpgradeMaterialViewsAsync();
UpdateUpgradeState();
}
private async UniTask CreateNeedViewsAsync()
{
var template = UIService.TemplateLibrary.BuildingPanel.Need;
await UIService.CreateViews<HousePanelNeedModel, HousePanelNeedUIView>(Model.Needs, _needs, template);
}
private async UniTask CreateUpgradeMaterialViewsAsync()
{
var template = UIService.TemplateLibrary.BuildingPanel.UpgradeMaterial;
await UIService.CreateViews<HousePanelUpgradeMaterialModel, HousePanelUpgradeMaterialUIView>(Model.UpgradeMaterials, _upgradeMaterials, template);
}
private void UpdateUpgradeState()
{
_notReadyNeedsNotMetLabel.style.display = DisplayStyle.None;
_notReadyNeedsMetLabel.style.display = DisplayStyle.None;
_fetchingMaterialsLabel.style.display = DisplayStyle.None;
_maxedOutLabel.style.display = DisplayStyle.None;
switch (Model.UpgradeState)
{
case TierUpgradeState.NotReady:
_notReadyNeedsNotMetLabel.style.display = Model.AllNeedsMet ? DisplayStyle.None : DisplayStyle.Flex;
_notReadyNeedsMetLabel.style.display = Model.AllNeedsMet ? DisplayStyle.Flex : DisplayStyle.None;
break;
case TierUpgradeState.FetchingMaterials:
case TierUpgradeState.AllMaterialsFetched:
_fetchingMaterialsLabel.style.display = DisplayStyle.Flex;
break;
case TierUpgradeState.MaxedOut:
_maxedOutLabel.style.display = DisplayStyle.Flex;
break;
}
}
protected override void OnModelPropertyChanged(object sender, BindablePropertyChangedEventArgs e)
{
base.OnModelPropertyChanged(sender, e);
switch (e.propertyName)
{
case nameof(HousePanelModel.AllNeedsMet):
case nameof(HousePanelModel.UpgradeState):
UpdateUpgradeState();
break;
}
}
private async UniTask AnimateHouseUpgradeStatusAsync()
{
_fetchingMaterialsLabel.text = _fetchingMaterialsLabel.text.Replace(".", string.Empty);
var i = 0;
while (_fetchingMaterialsLabel.panel != null)
{
await UniTask.WaitForSeconds(1, true);
var text = _fetchingMaterialsLabel.text;
text = text.Substring(0, text.Length - i);
i = (i + 1) % 4;
if (i > 0) text += new string('.', i);
_fetchingMaterialsLabel.text = text;
}
}
}
}

View File

@@ -0,0 +1,42 @@
using Unity.Properties;
using UnityEngine;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HousePanelUpgradeMaterialModel : UIModel
{
private int _remaining;
private bool _done;
public HousePanelUpgradeMaterialModel(ProductDefinition product, int needed)
{
Product = product;
Needed = needed;
}
public HousePanelUpgradeMaterialModel(IProductAmount productAmount) : this(productAmount.Product, productAmount.Amount)
{
}
public ProductDefinition Product { get; }
[CreateProperty] public Sprite Icon => Product.Icon;
[CreateProperty]
public int Remaining
{
get => _remaining;
set => SetProperty(ref _remaining, value);
}
[CreateProperty] public int Needed { get; }
[CreateProperty]
public bool Done
{
get => _done;
set => SetProperty(ref _done, value);
}
}
}

View File

@@ -0,0 +1,37 @@
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class HousePanelUpgradeMaterialUIView : UIView<HousePanelUpgradeMaterialModel>
{
private VisualElement _remaining;
private VisualElement _checkIcon;
public override UniTask InitializeAsync(UIService uiService, VisualElement rootElement)
{
base.InitializeAsync(uiService, rootElement);
_remaining = rootElement.Q<VisualElement>(className: "house-panel__remaining");
_checkIcon = rootElement.Q<VisualElement>(className: "house-panel__upgrade-material-check");
_checkIcon.style.display = DisplayStyle.None;
return UniTask.CompletedTask;
}
protected override void OnModelPropertyChanged(object sender, BindablePropertyChangedEventArgs e)
{
base.OnModelPropertyChanged(sender, e);
switch (e.propertyName)
{
case nameof(HousePanelUpgradeMaterialModel.Done):
_remaining.style.display = Model.Done ? DisplayStyle.None : DisplayStyle.Flex;
_checkIcon.style.display = Model.Done ? DisplayStyle.Flex : DisplayStyle.None;
break;
}
}
}
}

View File

@@ -0,0 +1,11 @@
namespace DanieleMarotta.RiversongCodeShowcase
{
public interface IBuildingPanelSectionPresenter
{
bool IsSectionRelevant(Building building);
void InitializeSection(BuildingPanelModel model, Building building);
void UpdateSection(BuildingPanelModel model, Building building);
}
}

View File

@@ -0,0 +1,95 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class StorageBuildingPanelSectionPresenter : IBuildingPanelSectionPresenter
{
private const int RequestedAmountLargeDelta = 5;
private readonly IProductCatalog _productCatalog;
public StorageBuildingPanelSectionPresenter(IProductCatalog productCatalog)
{
_productCatalog = productCatalog;
}
public bool IsSectionRelevant(Building building)
{
return building.Definition.IsStorage;
}
public void InitializeSection(BuildingPanelModel model, Building building)
{
model.StorageModel.Products.Clear();
for (var productHandle = 0; productHandle < _productCatalog.ProductTypeCount; productHandle++)
{
var productModel = new StoragePanelProductModel();
productModel.Initialize(
productHandle,
productHandle,
_productCatalog.GetProduct(productHandle),
() => ToggleAllowTaking(building, productModel),
() => ToggleCanFulfillRequests(building, productModel),
() => ChangeRequestedAmount(building, productModel, Keyboard.current.shiftKey.isPressed ? -RequestedAmountLargeDelta : -1),
() => ChangeRequestedAmount(building, productModel, Keyboard.current.shiftKey.isPressed ? RequestedAmountLargeDelta : 1));
model.StorageModel.Products.Add(productModel);
}
UpdateSection(model, building);
model.StorageModel.NotifyChanged();
}
public void UpdateSection(BuildingPanelModel model, Building building)
{
ref var storage = ref building.GetStorageRW();
ref var storagePolicy = ref building.GetProductStoragePolicyRW();
foreach (var productModel in model.StorageModel.Products)
{
var productHandle = productModel.ProductHandle;
productModel.AvailableNow = storage.AvailableNow(productHandle);
productModel.InputBlocked = !storagePolicy.IsTakingAllowed(productHandle);
productModel.CanFulfillRequests = storagePolicy.CanFulfillRequests(productHandle);
productModel.RequestedAmount = storagePolicy.GetRequestedAmount(productHandle);
}
}
private static void ToggleAllowTaking(Building building, StoragePanelProductModel productModel)
{
ref var storagePolicy = ref building.GetProductStoragePolicyRW();
var isTakingAllowed = storagePolicy.IsTakingAllowed(productModel.ProductHandle);
storagePolicy.SetAllowTaking(productModel.ProductHandle, !isTakingAllowed);
productModel.InputBlocked = isTakingAllowed;
}
private static void ToggleCanFulfillRequests(Building building, StoragePanelProductModel productModel)
{
ref var storagePolicy = ref building.GetProductStoragePolicyRW();
var canFulfillRequests = storagePolicy.CanFulfillRequests(productModel.ProductHandle);
storagePolicy.SetCanFulfillRequests(productModel.ProductHandle, !canFulfillRequests);
productModel.CanFulfillRequests = !canFulfillRequests;
}
private static void ChangeRequestedAmount(Building building, StoragePanelProductModel productModel, int delta)
{
ref var storage = ref building.GetStorageRW();
ref var storagePolicy = ref building.GetProductStoragePolicyRW();
var requestedAmount = storagePolicy.GetRequestedAmount(productModel.ProductHandle) + delta;
requestedAmount = Mathf.Clamp(requestedAmount, 0, storage.Capacity);
storagePolicy.SetRequestedAmount(productModel.ProductHandle, requestedAmount);
productModel.RequestedAmount = requestedAmount;
}
}
}

View File

@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class StoragePanelModel : UIModel
{
public List<StoragePanelProductModel> Products { get; } = new();
}
}

View File

@@ -0,0 +1,77 @@
using System;
using Unity.Properties;
using UnityEngine;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class StoragePanelProductModel : UIModel
{
private int _availableNow;
private bool _inputBlocked;
private bool _canFulfillRequests;
private int _requestedAmount;
public int RowIndex { get; set; }
public int ProductHandle { get; private set; }
[CreateProperty] public string Name { get; private set; }
[CreateProperty] public Sprite Icon { get; private set; }
[CreateProperty]
public int AvailableNow
{
get => _availableNow;
set => SetProperty(ref _availableNow, value);
}
public bool InputBlocked
{
get => _inputBlocked;
set => SetProperty(ref _inputBlocked, value);
}
public bool CanFulfillRequests
{
get => _canFulfillRequests;
set => SetProperty(ref _canFulfillRequests, value);
}
[CreateProperty]
public int RequestedAmount
{
get => _requestedAmount;
set => SetProperty(ref _requestedAmount, value);
}
public Action ToggleInputBlockedCallback { get; private set; }
public Action ToggleCanFulfillRequestsCallback { get; private set; }
public Action DecreaseRequestedAmountCallback { get; private set; }
public Action IncreaseRequestedAmountCallback { get; private set; }
public void Initialize(int rowIndex,
int productHandle,
ProductDefinition product,
Action toggleAllowTakingCallback,
Action toggleCanFulfillRequestsCallback,
Action decreaseRequestedAmountCallback,
Action increaseRequestedAmountCallback)
{
RowIndex = rowIndex;
ProductHandle = productHandle;
Name = product.ProductName;
Icon = product.Icon;
ToggleInputBlockedCallback = toggleAllowTakingCallback;
ToggleCanFulfillRequestsCallback = toggleCanFulfillRequestsCallback;
DecreaseRequestedAmountCallback = decreaseRequestedAmountCallback;
IncreaseRequestedAmountCallback = increaseRequestedAmountCallback;
}
}
}

View File

@@ -0,0 +1,89 @@
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class StoragePanelProductUIView : UIView<StoragePanelProductModel>
{
private VisualElement _inputBlockedButton;
private VisualElement _fulfillRequestsButton;
private Button _decreaseRequestedAmountButton;
private Button _increaseRequestedAmountButton;
public override UniTask InitializeAsync(UIService uiService, VisualElement rootElement)
{
base.InitializeAsync(uiService, rootElement);
_inputBlockedButton = rootElement.Q(className: "storage-panel__product-input-block-button");
_fulfillRequestsButton = rootElement.Q(className: "storage-panel__product-fulfill-requests-button");
_decreaseRequestedAmountButton = rootElement.Q<Button>(className: "storage-panel__product-requested-amount-button-minus");
_increaseRequestedAmountButton = rootElement.Q<Button>(className: "storage-panel__product-requested-amount-button-plus");
_inputBlockedButton.RegisterCallback<ClickEvent>(OnInputBlockedClick);
_fulfillRequestsButton.RegisterCallback<ClickEvent>(OnFulfillRequestsClick);
_decreaseRequestedAmountButton.RegisterCallback<ClickEvent>(OnDecreaseRequestedAmountClick);
_increaseRequestedAmountButton.RegisterCallback<ClickEvent>(OnIncreaseRequestedAmountClick);
return UniTask.CompletedTask;
}
protected override void OnNewModel(StoragePanelProductModel model)
{
base.OnNewModel(model);
if (model.RowIndex % 2 == 0) RootElement.Q(className: "storage-panel__product").AddToClassList("alt");
UpdateSelectedState();
}
protected override void OnModelPropertyChanged(object sender, BindablePropertyChangedEventArgs e)
{
base.OnModelPropertyChanged(sender, e);
switch (e.propertyName)
{
case nameof(StoragePanelProductModel.InputBlocked):
case nameof(StoragePanelProductModel.CanFulfillRequests):
UpdateSelectedState();
break;
}
}
private void OnInputBlockedClick(ClickEvent evt)
{
Model.ToggleInputBlockedCallback?.Invoke();
}
private void OnFulfillRequestsClick(ClickEvent evt)
{
Model.ToggleCanFulfillRequestsCallback?.Invoke();
}
private void OnDecreaseRequestedAmountClick(ClickEvent evt)
{
Model.DecreaseRequestedAmountCallback?.Invoke();
}
private void OnIncreaseRequestedAmountClick(ClickEvent evt)
{
Model.IncreaseRequestedAmountCallback?.Invoke();
}
private void UpdateSelectedState()
{
UpdateSelected(_inputBlockedButton, Model.InputBlocked);
UpdateSelected(_fulfillRequestsButton, Model.CanFulfillRequests);
}
private static void UpdateSelected(VisualElement element, bool isSelected)
{
if (isSelected)
element.AddToClassList("selected");
else
element.RemoveFromClassList("selected");
}
}
}

View File

@@ -0,0 +1,27 @@
using Cysharp.Threading.Tasks;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class StoragePanelUIView : UIView<StoragePanelModel>
{
private VisualElement _products;
public override UniTask InitializeAsync(UIService uiService, VisualElement rootElement)
{
base.InitializeAsync(uiService, rootElement);
_products = rootElement.Q<VisualElement>(className: "storage-panel__products");
return UniTask.CompletedTask;
}
protected override void OnModelChanged()
{
base.OnModelChanged();
var template = UIService.TemplateLibrary.BuildingPanel.StorageProduct;
_ = UIService.CreateViews<StoragePanelProductModel, StoragePanelProductUIView>(Model.Products, _products, template);
}
}
}