@@ -25,6 +25,9 @@ internal partial class ContainerWindow : ScriptableObject
2525 [ SerializeField ] bool m_Maximized ;
2626
2727 internal bool m_DontSaveToLayout = false ;
28+ private bool m_HasUnsavedChanges = false ;
29+ private List < EditorWindow > m_UnsavedEditorWindows ;
30+
2831 private int m_ButtonCount ;
2932 private float m_TitleBarWidth ;
3033
@@ -40,7 +43,6 @@ private static class Styles
4043 {
4144 // Title Bar Buttons (Non)
4245 public static GUIStyle buttonMin = "WinBtnMinMac" ;
43- public static GUIStyle buttonInactive = "WinBtnInactiveMac" ;
4446 public static GUIStyle buttonClose = macEditor ? "WinBtnCloseMac" : "WinBtnClose" ;
4547 public static GUIStyle buttonMax = macEditor ? "WinBtnMaxMac" : "WinBtnMax" ;
4648 public static GUIStyle buttonRestore = macEditor ? "WinBtnRestoreMac" : "WinBtnRestore" ;
@@ -64,6 +66,7 @@ private static class Styles
6466 public ContainerWindow ( )
6567 {
6668 m_PixelRect = new Rect ( 0 , 0 , 400 , 300 ) ;
69+ m_UnsavedEditorWindows = new List < EditorWindow > ( ) ;
6770 }
6871
6972 internal void __internalAwake ( )
@@ -196,6 +199,73 @@ public void SetMinMaxSizes(Vector2 min, Vector2 max)
196199 Internal_SetMinMaxSizes ( min , max ) ;
197200 }
198201
202+ internal bool InternalRequestClose ( )
203+ {
204+ if ( hasUnsavedChanges )
205+ {
206+ return PrivateRequestClose ( m_UnsavedEditorWindows ) ;
207+ }
208+
209+ return true ;
210+ }
211+
212+ internal bool InternalRequestClose ( EditorWindow dockedTab )
213+ {
214+ if ( dockedTab . hasUnsavedChanges )
215+ {
216+ var unsaved = new List < EditorWindow > ( ) { dockedTab } ;
217+ return PrivateRequestClose ( unsaved ) ;
218+ }
219+
220+ return true ;
221+ }
222+
223+ private bool PrivateRequestClose ( List < EditorWindow > allUnsaved )
224+ {
225+ Debug . Assert ( allUnsaved . Count > 0 ) ;
226+
227+ const int kSave = 0 ;
228+ const int kCancel = 1 ;
229+ const int kDiscard = 2 ;
230+
231+ int option = 1 ; // Cancel
232+
233+ if ( allUnsaved . Count == 1 )
234+ {
235+ option = EditorUtility . DisplayDialogComplex ( L10n . Tr ( "Unsaved Changes Detected" ) ,
236+ allUnsaved . First ( ) . saveChangesMessage ,
237+ L10n . Tr ( "Save" ) ,
238+ L10n . Tr ( "Cancel" ) ,
239+ L10n . Tr ( "Discard" ) ) ;
240+ }
241+ else
242+ {
243+ string unsavedChangesMessage = string . Join ( "\n " , allUnsaved . Select ( v => v . saveChangesMessage ) . ToArray ( ) ) ;
244+
245+ option = EditorUtility . DisplayDialogComplex ( L10n . Tr ( "Unsaved Changes Detected" ) ,
246+ unsavedChangesMessage ,
247+ L10n . Tr ( "Save All" ) ,
248+ L10n . Tr ( "Cancel" ) ,
249+ L10n . Tr ( "Discard All" ) ) ;
250+ }
251+
252+ switch ( option )
253+ {
254+ case kSave :
255+ foreach ( var w in allUnsaved )
256+ w . SaveChanges ( ) ;
257+ break ;
258+ case kCancel :
259+ case kDiscard :
260+ break ;
261+ default :
262+ Debug . LogError ( "Unrecognized option." ) ;
263+ break ;
264+ }
265+
266+ return option != kCancel ;
267+ }
268+
199269 internal void InternalCloseWindow ( )
200270 {
201271 Save ( ) ;
@@ -208,6 +278,37 @@ internal void InternalCloseWindow()
208278 DestroyImmediate ( this , true ) ;
209279 }
210280
281+ private static List < EditorWindow > FindUnsavedChanges ( View view )
282+ {
283+ var unsavedChanges = new List < EditorWindow > ( ) ;
284+
285+ foreach ( View v in view . allChildren )
286+ {
287+ switch ( v )
288+ {
289+ case DockArea dockArea :
290+ foreach ( var windowClose in dockArea . m_Panes . OfType < EditorWindow > ( ) )
291+ if ( windowClose . hasUnsavedChanges )
292+ unsavedChanges . Add ( windowClose ) ;
293+ break ;
294+ case HostView hostView :
295+ if ( hostView . actualView ? . hasUnsavedChanges ?? false )
296+ unsavedChanges . Add ( hostView . actualView ) ;
297+ break ;
298+ default :
299+ break ;
300+ }
301+ }
302+
303+ return unsavedChanges ;
304+ }
305+
306+ public void UnsavedStateChanged ( )
307+ {
308+ m_UnsavedEditorWindows = FindUnsavedChanges ( rootView ) ;
309+ hasUnsavedChanges = m_UnsavedEditorWindows . Count > 0 ;
310+ }
311+
211312 public void Close ( )
212313 {
213314 Save ( ) ;
@@ -256,7 +357,7 @@ internal string GetWindowID()
256357
257358 public bool IsMainWindow ( )
258359 {
259- if ( Unity . MPE . ProcessService . level == Unity . MPE . ProcessLevel . UMP_MASTER )
360+ if ( UnityEditor . MPE . ProcessService . level == UnityEditor . MPE . ProcessLevel . Master )
260361 return m_ShowMode == ( int ) ShowMode . MainWindow && m_DontSaveToLayout == false ;
261362 return false ;
262363 }
@@ -346,11 +447,47 @@ internal void OnMove()
346447 }
347448 }
348449
450+ // The title of the window, including unsaved changes markings, if any.
451+ public string displayedTitle { get ; private set ; }
452+
453+ private void UpdateTitle ( )
454+ {
455+ displayedTitle = hasUnsavedChanges ? m_Title + "*" : m_Title ;
456+ Internal_SetTitle ( displayedTitle ) ;
457+ }
458+
349459 // The title of the window
350460 public string title
351461 {
352- get { return m_Title ; }
353- set { m_Title = value ; Internal_SetTitle ( value ) ; }
462+ get
463+ {
464+ return m_Title ;
465+ }
466+ set
467+ {
468+ if ( m_Title != value )
469+ {
470+ m_Title = value ;
471+ UpdateTitle ( ) ;
472+ }
473+ }
474+ }
475+
476+ public bool hasUnsavedChanges
477+ {
478+ get
479+ {
480+ return m_HasUnsavedChanges ;
481+ }
482+ private set
483+ {
484+ if ( m_HasUnsavedChanges != value )
485+ {
486+ m_HasUnsavedChanges = value ;
487+ Internal_SetHasUnsavedChanges ( value ) ;
488+ UpdateTitle ( ) ;
489+ }
490+ }
354491 }
355492
356493 // Array of all visible ContainerWindows, from frontmost to last
@@ -449,8 +586,11 @@ public void HandleWindowDecorationStart(Rect windowPosition)
449586 BeginTitleBarButtons ( windowPosition ) ;
450587 if ( TitleBarButton ( close ) )
451588 {
452- Close ( ) ;
453- GUIUtility . ExitGUI ( ) ;
589+ if ( InternalRequestClose ( ) )
590+ {
591+ Close ( ) ;
592+ GUIUtility . ExitGUI ( ) ;
593+ }
454594 }
455595
456596 var canMaximize = m_MaxSize . x == 0 || m_MaxSize . y == 0 || m_MaxSize . x >= Screen . currentResolution . width || m_MaxSize . y >= Screen . currentResolution . height ;
0 commit comments