Search Unity

Question Issue setting the position of an EditorWindow opened with ShowUtility

Discussion in 'Editor & General Support' started by tempgogal54084, Mar 5, 2021.

  1. tempgogal54084

    tempgogal54084

    Joined:
    Jan 26, 2021
    Posts:
    30
    Hi all,

    I am currently putting together some editor tools, and have come across a minor issue.

    After creating an instance of a class that inherits from EditorWindow, I set the position and size of the window by creating a Rect that would place the window in the center of the screen.

    Code (CSharp):
    1.  
    2.         public void SetWindowSizeAndPosition()
    3.         {
    4.             var desired_window_size = kDesiredWindowSize; // Vector2Int(768, 512)
    5.            
    6.             // Create a Rect of the above size, centered on the screen
    7.             var window_position = EditorHelper.GetCenteredWindowRect(
    8.                 desired_window_size.x,
    9.                 desired_window_size.y
    10.             );
    11.  
    12.             position = window_position;
    13.  
    14.             // Set min/max size so window cannot be resized
    15.             var fixed_size = new Vector2(window_position.width, window_position.height);
    16.             minSize = maxSize = fixed_size;
    17.         }
    18.  
    Code (CSharp):
    1.  
    2.     public static Rect GetCenteredWindowRect(int window_width, int window_height)
    3.         {
    4.             var nullable_out_rect = RectFactory.Make(0, 0, 0, 0);
    5.             if (!nullable_out_rect.HasValue)
    6.             {
    7.                 // OOM error or other fatal error.
    8.                 return new Rect();
    9.             }
    10.  
    11.             var out_rect = nullable_out_rect.Value;
    12.  
    13.             // Gets the resolution of the monitor on which the editor is open with
    14.             // Screen.currentResolution
    15.             var screen_size = GetScreenResolution();
    16.  
    17.             // Scales the screen size down based on the Windows display scaling
    18.             // For example, a 4K monitor with a 150% scaling setting will result in
    19.             // a resolution of 2560 x 1440, which is the resolution at which the Unity
    20.             // editor is actually rendered at
    21.             screen_size.x = RawToTrueSize(screen_size.x);
    22.             screen_size.y = RawToTrueSize(screen_size.y);
    23.  
    24.             // TODO: Handle the case when Unity is not open on the main monitor
    25.             //
    26.  
    27.             // Set the width and height of the window, clamping the values to ensure
    28.             // that the window is not wider than the screen
    29.             out_rect.width = Mathf.Clamp(window_width, 0, screen_size.x);
    30.             out_rect.height = Mathf.Clamp(window_height, 0, screen_size.y);
    31.  
    32.             // Set the x and y position of the window, clamping the value to ensure
    33.             // that the window is placed within the bounds of the screen
    34.             out_rect.x = Mathf.Clamp((screen_size.x / 2f) - (window_width / 2f), 0, screen_size.x);
    35.             out_rect.y = Mathf.Clamp((screen_size.y / 2f) - (window_height / 2f), 0, screen_size.y);
    36.            
    37.             return out_rect;
    38.         }
    39.  
    EditorHelper.GetCenteredWindowRect returns exactly that. In the example above, I am creating a window of size [ 768, 512 ] pixels, taking into account the scaling of my 4k monitor.

    With the above code, and when I have Unity open on my main monitor, opening the aforementioned EditorWindow works as expected.

    However, when I move Unity to my second monitor, and attempt to open the window, it opens on my main monitor - sized and positioned as if it was placed on my second monitor.

    It looks like the position value needs to be relative to the main monitor, or perhaps the left-most monitor? For example, when Unity is on my second monitor (1080p), and value of "x" for the Rect that is applied to "position" is 320, it looks like it would in fact have to be <width of main/left-most monitor> + 320.

    If my assumptions are correct, in order to correctly calculate the x and y component of the Rect used to set the window position I would need to first determine:
    1. The position of the monitor on which Unity is currently open (even if its overlapping two screens, it looks like one of them is considered the "current") relative to the main monitor.
    2. The size of the main\left-most monitor, and any other monitors between the that monitor and the one on which Unity is currently open.
    Is there a way to get this information while in the editor? I have checked the Display and Screen classes but there doesn't seem to be anything of the sort.

    Or am I barking up the wrong tree, and there is some sort of handy Editor tool that can handle this for me?

    Kind regards,
    James
     
  2. tempgogal54084

    tempgogal54084

    Joined:
    Jan 26, 2021
    Posts:
    30
    Are we allowed to bump?
     
  3. tempgogal54084

    tempgogal54084

    Joined:
    Jan 26, 2021
    Posts:
    30
  4. RodrigoAbreu

    RodrigoAbreu

    Joined:
    Jan 29, 2013
    Posts:
    12
    You'll need to dig the ContainerWindow with reflection to get the proper thing.


    Code (CSharp):
    1. public static void CentralizeOnUnityCurrentWindow (this EditorWindow window) {
    2.     Rect mainWindow = GetUnityCurrentMonitorRect();
    3.     Rect pos = window.position;
    4.     pos.x = mainWindow.center.x - pos.width * 0.5f;
    5.     pos.y = mainWindow.center.y - pos.height * 0.5f;
    6.     window.position = pos;
    7. }
    8.  
    9. private static Rect GetUnityCurrentMonitorRect() {
    10.     IEnumerable<Type> derivedTypes = AppDomain.CurrentDomain.GetAssemblies()
    11.         .SelectMany(assembly => assembly.GetTypes())
    12.         .Where(type => type.IsSubclassOf(typeof(ScriptableObject)));
    13.     Type containerWinType = derivedTypes.FirstOrDefault(t => "ContainerWindow".Equals(t.Name));
    14.     if (containerWinType == null) {
    15.         Debug.LogError("Can't find ContainerWindow");
    16.     }
    17.     FieldInfo showModeField = containerWinType.GetField("m_ShowMode",
    18.         System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    19.     PropertyInfo rootViewProperty = containerWinType.GetProperty("rootView",
    20.         System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
    21.     if (showModeField == null || rootViewProperty == null) {
    22.         Debug.LogError("Can't find 'm_ShowMode' or 'position'");
    23.     }
    24.     Object[] windows = Resources.FindObjectsOfTypeAll(containerWinType);
    25.     foreach (Object win in windows) {
    26.         var showMode = (int) showModeField.GetValue(win);
    27.         if (showMode != 4) {
    28.             continue;
    29.         }
    30.         object view = rootViewProperty.GetValue(win, null);
    31.         PropertyInfo screenPosProperty = view.GetType().GetProperty("screenPosition");
    32.         if (screenPosProperty == null) {
    33.             Debug.LogError("Can't find 'screenPosition'.");
    34.         }
    35.         var screenPos = (Rect) screenPosProperty.GetValue(view, null);
    36.         return screenPos;
    37.     }
    38. }
    To use the snippet above, you'll need to create an extension class for EditorWindow, then use the EditorWindow instance to call CentralizeOnUnityCurrentWindow. It will detect the monitor where the UnityEditor is running and recalculate the position using that.
    A caveat here though, is that you'll need to create your EditorWindow using CreateInstance instead of GetWindow.