#nullable enable using Cysharp.Threading.Tasks; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using UnityEngine; using UnityEngine.UIElements; namespace SHI.Modal.ISOP { /// /// ISOP(조선소 공정 계획) 모달 창을 관리하는 메인 컨트롤러입니다. /// /// 개요: /// /// TreeList, ISOPModelView, ISOPChart 세 가지 뷰를 통합 관리하며, /// 뷰 간의 선택 동기화, 드래그를 통한 뷰 크기 조절, 확장/축소 기능을 제공합니다. /// UIDocument를 통해 UXML 기반 UI와 연결됩니다. /// /// /// 주요 기능: /// /// glTF 모델과 간트 데이터 동시 로드 /// TreeList ↔ ModelView ↔ Chart 선택 동기화 /// 드래그 버튼으로 ModelView/Chart 크기 비율 조절 /// 개별 뷰 전체화면 확장/복원 /// TreeList 가시성 토글 시 3D 모델 가시성 동기화 /// /// /// UI 구조: /// /// ISOPModal (UXML) /// ├── TreeList (왼쪽 패널) - 모델 계층 구조 /// ├── ISOPModelView (중앙) - 3D 모델 뷰어 /// ├── drag-btn (구분선) - 수평 드래그로 크기 조절 /// └── ISOPChart (오른쪽) - 간트 차트 /// /// /// 사용 예시: /// /// var modal = Instantiate(isopModalPrefab); /// await modal.GetComponent<ISOPModal>().LoadData(gltfPath, ganttPath); /// /// public class ISOPModal : MonoBehaviour { [SerializeField] public UIDocument uiDocument; private VisualElement? content; private TreeList? listView; private ISOPModelView? modelView; private ISOPChart? chartView; private Button? closeBtn; private Button? showTreeBtn; private Button? dragBtn; private CancellationTokenSource? _cts; private bool _suppressSelection = false; // key<->id 매핑(차트-리스트/모델 동기화) private readonly Dictionary _keyToId = new Dictionary(); private readonly Dictionary _idToKey = new Dictionary(); private int selectedItemId = -1; private enum ExpandedSide { None, Model, Chart } private ExpandedSide _expanded = ExpandedSide.None; // 드래그 상태 저장 private bool _isDragging = false; private int _activePointerId = -1; private float _dragOffset = 0f; // 포인터와 drag 버튼 중심 간 오프셋 // 확장 전 비율 저장 private float _lastModelFlexGrow = 1f; private float _lastChartFlexGrow = 1f; // GeometryChangedEvent 콜백 (해제용) private EventCallback? _contentGeometryChangedCallback; // 로딩 UI private VisualElement? _loadingOverlay; private VisualElement? _loadingSpinner; private IVisualElementScheduledItem? _spinnerAnimation; private void OnEnable() { OnDestroy(); // 중복 초기화 방지 var root = uiDocument.rootVisualElement; content = root.Q("content"); // UXML에서 태그로 추가했다면 Query로 찾음 listView = root.Q(); if (listView != null) { listView.OnSelectionChanged += OnListItemSelectionChanged; listView.OnClosed += OnListClosed; listView.OnVisibilityChanged += OnListVisibilityChanged; // listView가 다른 요소 위에 표시되도록 설정 listView.style.unityOverflowClipBox = OverflowClipBox.ContentBox; // 더미 데이터 생성 // var data = GenerateDummyData(); // listView.SetData(data); } modelView = root.Q(); if (modelView != null) { // 선택 동기화: 모델 -> 리스트/차트 modelView.OnItemSelected += OnModelItemSelected; modelView.OnExpand += ToggleExpandModel; // modelView의 내용이 범위를 벗어나지 않도록 overflow 설정 modelView.style.overflow = Overflow.Hidden; } chartView = root.Q(); if (chartView != null) { chartView.OnExpand += ToggleExpandChart; } showTreeBtn = root.Q