using Cysharp.Threading.Tasks; using System; using System.Collections.Generic; using System.Threading; using UnityEngine; using UnityEngine.UI; namespace SHI.modal { public class BlockDetailModal : MonoBehaviour { [Header("References")] [SerializeField] private Button closeButton; [SerializeField] private ModelDetailListView listView; [SerializeField] private ModelDetailView modelView; [SerializeField] private ModelDetailChartView chartView; [Header("UI Controls")] [SerializeField] private Button modelViewExpandButton; [SerializeField] private Button chartViewExpandButton; [SerializeField] private Button dragButton; [SerializeField] private Button showListButton; // 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() : null; private RectTransform ChartRect => chartView != null ? chartView.GetComponent() : null; private HorizontalSplitDrag _splitter; // lifecycle private CancellationTokenSource _cts; private bool _suppressSelection; // key<->id 매핑(차트-리스트/모델 동기화를 위해 유지) private readonly Dictionary _keyToId = new Dictionary(); private readonly Dictionary _idToKey = new Dictionary(); public void Start() { // 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(); }); if (showListButton != null) showListButton.gameObject.SetActive(false); // Selection wiring: list -> model/chart if (listView != null) { listView.OnItemSelected += data => { if (data == null) return; HandleSelection(data.Id, "ListView"); }; listView.OnItemDeselected += data => { HandleDeselection(data.Id, "ListView"); }; listView.OnClosed += () => { if (showListButton != null) showListButton.gameObject.SetActive(true); if (_splitter != null) _splitter.RefreshPosition(); }; listView.OnVisibilityChanged += (id, vis) => { if (modelView != null) modelView.SetVisibility(id, vis); }; } // Selection wiring: model -> list/chart if (modelView != null) { modelView.OnItemSelected += data => { if (data == null) return; HandleSelection(data.Id, "ModelView"); }; } // Chart -> list/model if (chartView != null) { // key 기반 클릭 우선 사용 chartView.OnRowClickedByKey += key => { if (string.IsNullOrEmpty(key)) return; if (_keyToId.TryGetValue(key, out var id)) HandleSelection(id, "ChartView"); }; // 호환: Guid 이벤트도 유지 chartView.OnRowClicked += id => { HandleSelection(id, "ChartView"); }; } // Expand buttons if (modelViewExpandButton != null) modelViewExpandButton.onClick.AddListener(ToggleExpandModel); if (chartViewExpandButton != null) chartViewExpandButton.onClick.AddListener(ToggleExpandChart); // Drag splitter SetupSplitControls(); } 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 items = Array.Empty(); 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; } } if (listView != null) listView.SetupData(items); if (chartView != null) chartView.LoadData(gantt); } private void SetupSplitControls() { var modelRect = ModelRect; var chartRect = ChartRect; if (modelRect == null || chartRect == null || dragButton == null) return; _modelLayout = modelRect.GetComponent(); if (_modelLayout == null) _modelLayout = modelRect.gameObject.AddComponent(); _chartLayout = chartRect.GetComponent(); if (_chartLayout == null) _chartLayout = chartView.gameObject.AddComponent(); // initial split50/50 _modelLayout.flexibleWidth =1f; _chartLayout.flexibleWidth =1f; // attach drag handler _splitter = dragButton.gameObject.GetComponent(); if (_splitter == null) _splitter = dragButton.gameObject.AddComponent(); var leftFixed = listView != null ? listView.GetComponent() : null; _splitter.Initialize(modelRect, chartRect, leftFixed); //시간이 좀 필요 함 UniTask.DelayFrame(1).ContinueWith(() => _splitter.RefreshPosition()); } 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(); } 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); if (_modelLayout != null) _modelLayout.flexibleWidth =1f; if (_chartLayout != null) _chartLayout.flexibleWidth =1f; if (_splitter != null) { _splitter.gameObject.SetActive(true); _splitter.RefreshPosition(); } } private void OnDisable() { if (_cts != null) { try { _cts.Cancel(); } catch { } _cts.Dispose(); _cts = null; } if (chartView != null) chartView.Dispose(); if (modelView != null) modelView.Dispose(); } } }