Files
XRLib/Assets/Scripts/SHI/modal/BlockDetailModal.cs

259 lines
9.6 KiB
C#
Raw Normal View History

2025-11-12 16:48:34 +09:00
using Cysharp.Threading.Tasks;
2025-11-13 20:16:25 +09:00
using System;
using System.Collections.Generic;
using System.Threading;
2025-11-12 12:28:17 +09:00
using UnityEngine;
using UnityEngine.UI;
namespace SHI.modal
{
2025-11-12 16:48:34 +09:00
public class BlockDetailModal : MonoBehaviour
2025-11-12 12:28:17 +09:00
{
2025-11-12 16:48:34 +09:00
[Header("References")]
2025-11-13 20:16:25 +09:00
[SerializeField] private Button closeButton;
[SerializeField] private ModelDetailListView listView;
[SerializeField] private ModelDetailView modelView;
[SerializeField] private ModelDetailChartView chartView;
2025-11-12 12:28:17 +09:00
2025-11-12 16:48:34 +09:00
[Header("UI Controls")]
2025-11-13 20:16:25 +09:00
[SerializeField] private Button modelViewExpandButton;
[SerializeField] private Button chartViewExpandButton;
[SerializeField] private Button dragButton;
[SerializeField] private Button showListButton;
2025-11-12 16:48:34 +09:00
// cached layout elements for split control
private LayoutElement _modelLayout;
private LayoutElement _chartLayout;
private enum ExpandedSide { None, Model, Chart }
private ExpandedSide _expanded = ExpandedSide.None;
private RectTransform ModelRect => modelView != null ? modelView.GetComponent<RectTransform>() : null;
private RectTransform ChartRect => chartView != null ? chartView.GetComponent<RectTransform>() : null;
private HorizontalSplitDrag _splitter;
2025-11-12 12:28:17 +09:00
2025-11-13 20:16:25 +09:00
// lifecycle
private CancellationTokenSource _cts;
private bool _suppressSelection;
// key<->id 매핑(차트-리스트/모델 동기화를 위해 유지)
private readonly Dictionary<string, Guid> _keyToId = new Dictionary<string, Guid>();
private readonly Dictionary<Guid, string> _idToKey = new Dictionary<Guid, string>();
2025-11-12 12:28:17 +09:00
public void Start()
{
2025-11-12 16:48:34 +09:00
// Close
if (closeButton != null)
{
closeButton.onClick.AddListener(() => gameObject.SetActive(false));
}
// list show 버튼
if (showListButton != null && listView != null)
showListButton.onClick.AddListener(() =>
{
Debug.Log("BlockDetailModal: Show List View");
listView.gameObject.SetActive(true);
showListButton.gameObject.SetActive(false);
if (_splitter != null) _splitter.RefreshPosition();
});
2025-11-13 20:16:25 +09:00
if (showListButton != null) showListButton.gameObject.SetActive(false);
2025-11-12 16:48:34 +09:00
// Selection wiring: list -> model/chart
if (listView != null)
{
listView.OnItemSelected += data =>
{
2025-11-13 20:16:25 +09:00
if (data == null) return;
HandleSelection(data.Id, "ListView");
};
listView.OnItemDeselected += data =>
{
HandleDeselection(data.Id, "ListView");
2025-11-12 16:48:34 +09:00
};
2025-11-13 20:16:25 +09:00
2025-11-12 16:48:34 +09:00
listView.OnClosed += () =>
{
if (showListButton != null) showListButton.gameObject.SetActive(true);
if (_splitter != null) _splitter.RefreshPosition();
};
2025-11-13 20:16:25 +09:00
listView.OnVisibilityChanged += (id, vis) =>
{
if (modelView != null) modelView.SetVisibility(id, vis);
};
2025-11-12 16:48:34 +09:00
}
// Selection wiring: model -> list/chart
if (modelView != null)
{
modelView.OnItemSelected += data =>
{
2025-11-13 20:16:25 +09:00
if (data == null) return;
HandleSelection(data.Id, "ModelView");
2025-11-12 16:48:34 +09:00
};
}
// Chart -> list/model
if (chartView != null)
{
2025-11-13 20:16:25 +09:00
// key 기반 클릭 우선 사용
chartView.OnRowClickedByKey += key =>
2025-11-12 16:48:34 +09:00
{
2025-11-13 20:16:25 +09:00
if (string.IsNullOrEmpty(key)) return;
if (_keyToId.TryGetValue(key, out var id)) HandleSelection(id, "ChartView");
};
// 호환: Guid 이벤트도 유지
chartView.OnRowClicked += id =>
{
HandleSelection(id, "ChartView");
2025-11-12 16:48:34 +09:00
};
}
// Expand buttons
if (modelViewExpandButton != null)
modelViewExpandButton.onClick.AddListener(ToggleExpandModel);
if (chartViewExpandButton != null)
chartViewExpandButton.onClick.AddListener(ToggleExpandChart);
// Drag splitter
SetupSplitControls();
}
2025-11-13 20:16:25 +09:00
public async UniTask LoadData(string gltfPath, GanttChartData gantt, CancellationToken externalCt = default)
{
Debug.Log($"BlockDetailModal: LoadData {gltfPath}");
// cancel previous
if (_cts != null)
{
try { _cts.Cancel(); } catch { }
_cts.Dispose();
}
_cts = CancellationTokenSource.CreateLinkedTokenSource(externalCt);
var ct = _cts.Token;
// load model and list
IEnumerable<ModelDetailListItemData> items = Array.Empty<ModelDetailListItemData>();
if (modelView != null)
{
try
{
items = await modelView.LoadModelAsync(gltfPath, ct);
}
catch (OperationCanceledException) { }
}
// 매핑 초기화
_keyToId.Clear();
_idToKey.Clear();
foreach (var it in items)
{
if (!string.IsNullOrEmpty(it.ExternalKey))
{
_keyToId[it.ExternalKey] = it.Id;
_idToKey[it.Id] = it.ExternalKey;
}
}
2025-11-14 17:02:38 +09:00
if (listView != null) listView.SetupData(items);
2025-11-13 20:16:25 +09:00
if (chartView != null) chartView.LoadData(gantt);
}
2025-11-12 16:48:34 +09:00
private void SetupSplitControls()
{
var modelRect = ModelRect;
var chartRect = ChartRect;
if (modelRect == null || chartRect == null || dragButton == null) return;
_modelLayout = modelRect.GetComponent<LayoutElement>();
if (_modelLayout == null) _modelLayout = modelRect.gameObject.AddComponent<LayoutElement>();
_chartLayout = chartRect.GetComponent<LayoutElement>();
if (_chartLayout == null) _chartLayout = chartView.gameObject.AddComponent<LayoutElement>();
// initial split50/50
2025-11-13 20:16:25 +09:00
_modelLayout.flexibleWidth =1f;
_chartLayout.flexibleWidth =1f;
2025-11-12 16:48:34 +09:00
// attach drag handler
_splitter = dragButton.gameObject.GetComponent<HorizontalSplitDrag>();
if (_splitter == null) _splitter = dragButton.gameObject.AddComponent<HorizontalSplitDrag>();
var leftFixed = listView != null ? listView.GetComponent<RectTransform>() : null;
_splitter.Initialize(modelRect, chartRect, leftFixed);
//시간이 좀 필요 함
UniTask.DelayFrame(1).ContinueWith(() => _splitter.RefreshPosition());
}
2025-11-13 20:16:25 +09:00
private void HandleSelection(Guid itemId, string source)
{
if (_suppressSelection) return;
_suppressSelection = true;
try
{
if (source != "ListView" && listView != null) listView.SelectByItemId(itemId);
if (source != "ModelView" && modelView != null) modelView.FocusItemById(itemId);
if (source != "ChartView" && chartView != null)
{
if (_idToKey.TryGetValue(itemId, out var key)) chartView.SelectByItemKey(key);
else chartView.SelectByItemId(itemId);
}
}
finally
{
_suppressSelection = false;
}
}
private void HandleDeselection(Guid itemId, string source)
{
if (_suppressSelection) return;
if (source != "ModelView" && modelView != null) modelView.UnfocusItem();
}
2025-11-12 16:48:34 +09:00
private void ToggleExpandModel()
{
if (ModelRect == null || chartView == null) return;
if (_expanded == ExpandedSide.Model) { ResetSplit(); return; }
_expanded = ExpandedSide.Model;
ModelRect.gameObject.SetActive(true);
chartView.gameObject.SetActive(false);
if (_splitter != null) _splitter.gameObject.SetActive(false);
}
private void ToggleExpandChart()
{
if (ModelRect == null || chartView == null) return;
if (_expanded == ExpandedSide.Chart) { ResetSplit(); return; }
_expanded = ExpandedSide.Chart;
ModelRect.gameObject.SetActive(false);
chartView.gameObject.SetActive(true);
if (_splitter != null) _splitter.gameObject.SetActive(false);
}
private void ResetSplit()
{
_expanded = ExpandedSide.None;
if (ModelRect != null) ModelRect.gameObject.SetActive(true);
if (chartView != null) chartView.gameObject.SetActive(true);
2025-11-13 20:16:25 +09:00
if (_modelLayout != null) _modelLayout.flexibleWidth =1f;
if (_chartLayout != null) _chartLayout.flexibleWidth =1f;
2025-11-12 16:48:34 +09:00
if (_splitter != null)
{
_splitter.gameObject.SetActive(true);
_splitter.RefreshPosition();
}
2025-11-12 12:28:17 +09:00
}
2025-11-13 20:16:25 +09:00
private void OnDisable()
{
if (_cts != null)
{
try { _cts.Cancel(); } catch { }
_cts.Dispose();
_cts = null;
}
if (chartView != null) chartView.Dispose();
if (modelView != null) modelView.Dispose();
}
2025-11-12 12:28:17 +09:00
}
}