259 lines
9.6 KiB
C#
259 lines
9.6 KiB
C#
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<RectTransform>() : null;
|
|
private RectTransform ChartRect => chartView != null ? chartView.GetComponent<RectTransform>() : null;
|
|
private HorizontalSplitDrag _splitter;
|
|
|
|
// 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>();
|
|
|
|
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<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;
|
|
}
|
|
}
|
|
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<LayoutElement>();
|
|
if (_modelLayout == null) _modelLayout = modelRect.gameObject.AddComponent<LayoutElement>();
|
|
|
|
_chartLayout = chartRect.GetComponent<LayoutElement>();
|
|
if (_chartLayout == null) _chartLayout = chartView.gameObject.AddComponent<LayoutElement>();
|
|
|
|
// initial split50/50
|
|
_modelLayout.flexibleWidth =1f;
|
|
_chartLayout.flexibleWidth =1f;
|
|
|
|
// 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());
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|