protected static bool IsUtilityWindow <T>(bool defaultIsUtility = false) where T : ConfigurableWindow
 {
     return(ProBuilderSettings.Get <bool>(typeof(T).ToString() + "-isUtilityWindow", SettingsScope.Project, defaultIsUtility));
 }
        void OnGUI()
        {
            DoContextMenu();

            GUILayout.BeginHorizontal(EditorStyles.toolbar);

            EditorGUI.BeginChangeCheck();
            s_ShowSettings.value = GUILayout.Toggle(s_ShowSettings.value, "Settings", EditorStyles.toolbarButton);
            s_ShowPreview.value  = GUILayout.Toggle(s_ShowPreview.value, "Preview", EditorStyles.toolbarButton);
            s_ShowNormals.value  = GUILayout.Toggle(s_ShowNormals.value, "Normals", EditorStyles.toolbarButton);
            if (EditorGUI.EndChangeCheck())
            {
                ProBuilderSettings.Save();
            }

            if (s_ShowNormals)
            {
                EditorGUI.BeginChangeCheck();

                s_NormalsSize.value = GUILayout.HorizontalSlider(
                    s_NormalsSize,
                    .001f,
                    1f,
                    GUILayout.MinWidth(30f),
                    GUILayout.MaxWidth(100f));

                if (EditorGUI.EndChangeCheck())
                {
                    ProBuilderSettings.Save();

                    foreach (var kvp in m_SmoothGroups)
                    {
                        kvp.Value.RebuildNormalsMesh(kvp.Key);
                    }
                    SceneView.RepaintAll();
                }
            }

            GUILayout.FlexibleSpace();

            if (GUILayout.Button(m_HelpIcon, UI.EditorStyles.toolbarHelpIcon))
            {
                s_ShowHelp.SetValue(!s_ShowHelp, true);
            }
            GUILayout.EndHorizontal();

            if (s_ShowSettings)
            {
                GUILayout.BeginVertical(UI.EditorStyles.settingsGroup);

                EditorGUIUtility.labelWidth = 100;

                EditorGUI.BeginChangeCheck();

                s_PreviewOpacity.value = EditorGUILayout.Slider("Preview Opacity", s_PreviewOpacity, .001f, 1f);
                s_PreviewDither.value  = EditorGUILayout.Toggle("Preview Dither", s_PreviewDither);

                if (EditorGUI.EndChangeCheck())
                {
                    ProBuilderSettings.Save();
                    smoothPreviewMaterial.SetFloat("_Opacity", s_PreviewOpacity);
                    smoothPreviewMaterial.SetFloat("_Dither", s_PreviewDither ? 1f : 0f);
                    SceneView.RepaintAll();
                }

                EditorGUIUtility.labelWidth = 0;

                GUILayout.EndVertical();
            }

            m_Scroll = EditorGUILayout.BeginScrollView(m_Scroll);

            if (s_ShowHelp)
            {
                GUILayout.BeginVertical(UI.EditorStyles.settingsGroup);

                GUILayout.Label("Create and Clear Smoothing Groups", EditorStyles.boldLabel);

                GUILayout.Label("Adjacent faces with the same smoothing group will appear to have a soft adjoining edge.", wordWrappedRichText);
                GUILayout.Space(2);
                GUILayout.Label("<b>To smooth</b> a selected group of faces, click one of the Smooth Group buttons.", wordWrappedRichText);
                GUILayout.Label("<b>To clear</b> selected faces of their smooth group, click the [Break] icon.", wordWrappedRichText);
                GUILayout.Label("<b>To select</b> all faces in a group, Right+Click or Alt+Click a smooth group button.", wordWrappedRichText);
                GUILayout.Space(2);

                global::UnityEditor.ProBuilder.UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonStyle);
                GUILayout.Label("An unused smooth group", wordWrappedRichText);
                global::UnityEditor.ProBuilder.UI.EditorGUILayout.EndRow();

                global::UnityEditor.ProBuilder.UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonInUseStyle);
                GUILayout.Label("A smooth group that is in use, but not in the current selection", wordWrappedRichText);
                global::UnityEditor.ProBuilder.UI.EditorGUILayout.EndRow();

                global::UnityEditor.ProBuilder.UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonSelectedStyle);
                GUILayout.Label("A smooth group that is currently selected", wordWrappedRichText);
                global::UnityEditor.ProBuilder.UI.EditorGUILayout.EndRow();

                global::UnityEditor.ProBuilder.UI.EditorGUILayout.BeginRow();
                GUILayout.Button("1", groupButtonMixedSelectionStyle);
                GUI.backgroundColor = Color.white;
                GUILayout.Label("A smooth group is selected, but the selection also contains non-grouped faces", wordWrappedRichText);
                global::UnityEditor.ProBuilder.UI.EditorGUILayout.EndRow();

                if (GUILayout.Button("Open Documentation"))
                {
                    Application.OpenURL("http://procore3d.github.io/probuilder2/toolbar/tool-panels/#smoothing-groups");
                }

                GUILayout.EndVertical();
            }

            // border style is 4 margin, 4 pad, 1px content. inner is accounted for by btn size + btn margin.
            float area    = (position.width - 10);
            float margin  = Mathf.Max(groupButtonStyle.margin.left, groupButtonStyle.margin.right);
            int   columns = (int)(area / (groupButtonStyle.CalcSize(m_GroupKeyContent).x + margin)) - 1;

            if (m_SmoothGroups.Count < 1)
            {
                GUILayout.BeginVertical();
                GUILayout.FlexibleSpace();
                GUILayout.Label("Select a ProBuilder Mesh", UI.EditorGUIUtility.CenteredGreyMiniLabel);
                GUILayout.FlexibleSpace();
                GUILayout.EndVertical();
            }
            else
            {
                foreach (var mesh in m_SmoothGroups)
                {
                    ProBuilderMesh  pb   = mesh.Key;
                    SmoothGroupData data = mesh.Value;

                    GUILayout.BeginVertical(UI.EditorStyles.settingsGroup);

                    GUILayout.BeginHorizontal();

                    if (GUILayout.Button(pb.name, UI.EditorStyles.headerLabel))
                    {
                        data.isVisible = !data.isVisible;
                    }

                    GUILayout.FlexibleSpace();

                    if (GUILayout.Button(m_SelectFacesWithSmoothGroupSelectionContent,
                                         UI.EditorStyles.buttonStyle))
                    {
                        SelectGroups(pb, new HashSet <int>(pb.selectedFacesInternal.Select(x => x.smoothingGroup)));
                    }

                    if (GUILayout.Button(m_BreakSmoothingContent,
                                         UI.EditorStyles.buttonStyle))
                    {
                        SetGroup(pb, Smoothing.smoothingGroupNone);
                    }

                    GUILayout.EndHorizontal();

                    bool isMixedSelection = data.selected.Contains(Smoothing.smoothingGroupNone);

                    if (data.isVisible)
                    {
                        int  column          = 0;
                        bool anySmoothGroups = data.groups.Any(x => x.Key > Smoothing.smoothingGroupNone);

                        GUILayout.BeginHorizontal();

                        for (int i = 1; i < Smoothing.smoothRangeMax; i++)
                        {
                            bool isSelected = data.selected.Contains(i);

                            GUIStyle stateStyle = isSelected ?
                                                  (isMixedSelection ? groupButtonMixedSelectionStyle : groupButtonSelectedStyle) :
                                                  data.groups.ContainsKey(i) ? groupButtonInUseStyle : groupButtonStyle;

                            if (s_ShowPreview && anySmoothGroups)
                            {
                                GUILayout.BeginVertical(GUILayout.MaxWidth(IconWidth));
                            }

                            m_GroupKeyContent.text = i.ToString();

                            if (GUILayout.Button(m_GroupKeyContent, stateStyle))
                            {
                                // if right click or alt click select the faces instead of setting a group
                                if ((Event.current.modifiers & EventModifiers.Alt) == EventModifiers.Alt ||
                                    Event.current.button != 0)
                                {
                                    SelectGroups(pb, new HashSet <int>()
                                    {
                                        i
                                    });
                                }
                                else
                                {
                                    SetGroup(pb, i);
                                }
                            }

                            if (s_ShowPreview && anySmoothGroups)
                            {
                                GUI.backgroundColor = data.groupColors.ContainsKey(i) ? data.groupColors[i] : Color.clear;
                                GUILayout.Label("", colorKeyStyle);
                                GUILayout.EndVertical();
                                GUI.backgroundColor = Color.white;
                            }

                            if (++column > columns)
                            {
                                column = 0;
                                GUILayout.EndHorizontal();
                                GUILayout.BeginHorizontal();
                            }
                        }

                        GUILayout.EndHorizontal();
                    }

                    GUILayout.EndVertical();
                }
            }

            EditorGUILayout.EndScrollView();

            // This isn't great, but we need hover previews to work
            if (mouseOverWindow == this)
            {
                Repaint();
            }
        }
        void OnGUI()
        {
            GUILayout.Label("Log Output", EditorStyles.boldLabel);

            EditorGUI.BeginChangeCheck();
            s_LogOutput.value = (LogOutput)global::UnityEditor.ProBuilder.UI.EditorGUILayout.FlagToolbar((int)s_LogOutput.value, gc_output);
            if (EditorGUI.EndChangeCheck())
            {
                ProBuilderSettings.Save();
                LogPreferences.SetLogPreferences();
            }

            GUI.enabled = (s_LogOutput & LogOutput.File) > 0;

            GUILayout.BeginHorizontal();
            GUILayout.Label("Log Path");
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("...", EditorStyles.miniButton))
            {
                string dir = UnityEditor.EditorUtility.OpenFolderPanel("ProBuilder Log Directory", "", "");

                if (!string.IsNullOrEmpty(dir) && Directory.Exists(dir))
                {
                    s_LogPath.value = string.Format("{0}/{1}", dir, Log.k_ProBuilderLogFileName);

                    try
                    {
                        Uri    directoryUri = new Uri(dir);
                        Uri    fileUri      = new Uri(s_LogPath);
                        string relativePath = directoryUri.MakeRelativeUri(fileUri).ToString();
                        if (!string.IsNullOrEmpty(relativePath))
                        {
                            s_LogPath.value = relativePath;
                        }
                    }
                    catch {}

                    ProBuilderSettings.Save();
                    Log.SetLogFile(s_LogPath);
                }
            }
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
            GUI.enabled = false;
            EditorGUILayout.LabelField(s_LogPath);
            GUI.enabled = true;
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("open", EditorStyles.miniButton))
            {
                UnityEditor.EditorUtility.OpenWithDefaultApp(s_LogPath);
            }
            GUILayout.EndHorizontal();

            GUILayout.Label("Chatty-ness", EditorStyles.boldLabel);

            EditorGUI.BeginChangeCheck();
            s_LogLevel.value = (LogLevel)global::UnityEditor.ProBuilder.UI.EditorGUILayout.FlagToolbar((int)s_LogLevel.value, gc_level, false, true);
            if (EditorGUI.EndChangeCheck())
            {
                ProBuilderSettings.Save();
                Log.SetLogLevel(s_LogLevel);
            }

            GUILayout.FlexibleSpace();
            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Clear Log File", EditorStyles.miniButton))
            {
                Log.ClearLogFile();
            }
            GUILayout.EndHorizontal();
        }