Skip to content

Instantly share code, notes, and snippets.

@SolarianZ
Last active December 1, 2024 05:31
Show Gist options
  • Select an option

  • Save SolarianZ/37636f213723c1a2079abe1d7769827e to your computer and use it in GitHub Desktop.

Select an option

Save SolarianZ/37636f213723c1a2079abe1d7769827e to your computer and use it in GitHub Desktop.
{"category": "Unity Engine/Editor/Extensions", "keywords": "Unity, Editor, DisplayDialog, Modal, Checkbox, Button"} Display a modal dialog window with multiple checkboxes and buttons in the Unity Editor.
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
/// <summary>
/// Display a modal dialog window with multiple checkboxes and buttons in the Unity Editor.
/// </summary>
public static class ModalDialog
{
[Flags]
public enum CheckboxModes
{
None = 0,
AtLeastOne = 1,
AtMostOne = 2,
ToggleGroup = AtLeastOne | AtMostOne,
}
public static void ShowMessage(string title, string message, string positiveButton)
{
EditorUtility.DisplayDialog(title, message, positiveButton);
}
public static bool ShowMessage(string title, string message, string positiveButton, string negativeButton)
{
return EditorUtility.DisplayDialog(title, message, positiveButton, negativeButton);
}
public static int ShowMessage(string title, string message, params string[] buttons)
{
if (buttons == null || buttons.Length == 0)
throw new ArgumentNullException(nameof(buttons));
return ShowMessage(DialogWindow.CalcPosition(), MessageType.None, title, message, buttons);
}
public static int ShowMessage(MessageType messageType, string title, string message, params string[] buttons)
{
if (buttons == null || buttons.Length == 0)
throw new ArgumentNullException(nameof(buttons));
return ShowMessage(DialogWindow.CalcPosition(), messageType, title, message, buttons);
}
public static int ShowMessage(Rect position, MessageType messageType, string title, string message, params string[] buttons)
{
if (buttons == null || buttons.Length == 0)
throw new ArgumentNullException(nameof(buttons));
return DialogWindow.ShowModal(position, messageType, title, message, null, CheckboxModes.None, buttons);
}
public static int ShowMessageWithCheckboxes(MessageType messageType, string title, string message,
KeyValuePair<string, bool>[] checkboxes, CheckboxModes checkboxModes, params string[] buttons)
{
if (checkboxes == null || checkboxes.Length == 0)
throw new ArgumentNullException(nameof(checkboxes));
if (buttons == null || buttons.Length == 0)
throw new ArgumentNullException(nameof(buttons));
return DialogWindow.ShowModal(DialogWindow.CalcPosition(), messageType, title, message, checkboxes, checkboxModes, buttons);
}
public static int ShowMessageWithCheckboxes(Rect position, MessageType messageType, string title, string message,
KeyValuePair<string, bool>[] checkboxes, CheckboxModes checkboxModes, params string[] buttons)
{
if (checkboxes == null || checkboxes.Length == 0)
throw new ArgumentNullException(nameof(checkboxes));
if (buttons == null || buttons.Length == 0)
throw new ArgumentNullException(nameof(buttons));
return DialogWindow.ShowModal(position, messageType, title, message, checkboxes, checkboxModes, buttons);
}
private class DialogWindow : EditorWindow
{
public static int ShowModal(Rect position, MessageType messageType, string title, string message,
KeyValuePair<string, bool>[] checkboxes, CheckboxModes checkboxModes, string[] buttons)
{
DialogWindow dialog = CreateInstance<DialogWindow>();
dialog.position = position;
dialog.titleContent = new GUIContent(title);
dialog.MessageType = messageType;
dialog.Message = message;
dialog.Checkboxes = checkboxes;
dialog.CheckboxModes = checkboxModes;
dialog.ButtonTexts = buttons;
dialog.ShowModalUtility();
return dialog.SelectedOptionIndex;
}
public static Rect CalcPosition() => CalcPosition(DefaultSize);
public static Rect CalcPosition(Vector2 size)
{
return new Rect(Screen.currentResolution.width / 2f - size.x / 2f,
Screen.currentResolution.height / 2f - size.y / 2f, size.x, size.y);
}
public static Vector2 DefaultSize => new Vector2(500, 300);
public MessageType MessageType { get; set; }
public string Message { get; set; }
public KeyValuePair<string, bool>[] Checkboxes { get; set; }
public CheckboxModes CheckboxModes { get; set; }
public string[] ButtonTexts { get; set; }
public int SelectedOptionIndex { get; private set; } = -1;
private void OnEnable()
{
minSize = DefaultSize;
}
private void CreateGUI()
{
CreateMessageGUI();
CreateCheckboxGUI();
CreateOptionButtonGUI();
}
private void CreateMessageGUI()
{
// Message Container
VisualElement messageContainer = new VisualElement
{
style =
{
flexDirection = FlexDirection.Row,
flexGrow = 1,
flexShrink = 1,
minHeight = 100,
},
};
rootVisualElement.Add(messageContainer);
// Message Icon
if (MessageType != MessageType.None)
{
Image messageIcon = new Image
{
style =
{
width = 32,
height = 32,
marginLeft = 4,
marginTop = 4,
flexShrink = 0,
},
};
switch (MessageType)
{
case MessageType.None: messageIcon.image = null; break;
case MessageType.Info: messageIcon.image = EditorGUIUtility.IconContent("info@2x").image; break;
case MessageType.Warning: messageIcon.image = EditorGUIUtility.IconContent("warning@2x").image; break;
case MessageType.Error: messageIcon.image = EditorGUIUtility.IconContent("error@2x").image; break;
default: throw new ArgumentOutOfRangeException(nameof(MessageType), MessageType, null);
}
messageContainer.Add(messageIcon);
}
// Message ScrollView
ScrollView messageScrollView = new ScrollView(ScrollViewMode.Vertical)
{
style =
{
flexGrow = 1,
}
};
messageContainer.Add(messageScrollView);
// Message Text
Label messageLabel = new Label(Message)
{
#if UNITY_2021_3_OR_NEWER
enableRichText = true,
#endif
#if UNITY_2022_3_OR_NEWER
selection =
{
isSelectable = true,
},
#endif
style =
{
flexGrow = 1,
flexShrink = 1,
marginRight = 4,
marginLeft = 4,
marginTop = 4,
marginBottom = 4,
overflow = Overflow.Hidden,
#if UNITY_2021_3_OR_NEWER
textOverflow = TextOverflow.Ellipsis,
#endif
whiteSpace = WhiteSpace.Normal,
}
};
messageScrollView.Add(messageLabel);
}
private void CreateCheckboxGUI()
{
if (Checkboxes == null || Checkboxes.Length == 0)
return;
// Checkbox Container
VisualElement checkboxContainer = new VisualElement
{
style =
{
justifyContent = Justify.FlexEnd,
flexGrow = 0,
flexShrink = 0,
marginTop = 8,
maxHeight = 120,
}
};
rootVisualElement.Add(checkboxContainer);
// Checkboxes ScrollView
ScrollView checkboxScrollView = new ScrollView(ScrollViewMode.Vertical)
{
style =
{
flexGrow = 1,
}
};
checkboxContainer.Add(checkboxScrollView);
// Checkboxes
string toggleNamePrefix = "modal-dialog__checkbox";
for (int i = 0; i < Checkboxes.Length; i++)
{
int index = i;
Toggle toggle = new Toggle(Checkboxes[i].Key)
{
name = $"{toggleNamePrefix}-{index}",
value = Checkboxes[i].Value,
style =
{
marginLeft = 8,
marginRight = 8,
}
};
DecorateToggle(toggle);
checkboxScrollView.Add(toggle);
// Checkbox mode
toggle.RegisterValueChangedCallback(evt =>
{
Checkboxes[index] = new KeyValuePair<string, bool>(Checkboxes[index].Key, evt.newValue);
if (CheckboxModes == CheckboxModes.None)
return;
if (CheckboxModes.HasFlag(CheckboxModes.AtLeastOne) && !evt.newValue)
{
bool hasOtherChecked = false;
for (int j = 0; j < Checkboxes.Length; j++)
{
if (Checkboxes[j].Value && j != index)
{
hasOtherChecked = true;
break;
}
}
if (!hasOtherChecked)
{
Checkboxes[index] = new KeyValuePair<string, bool>(Checkboxes[index].Key, true);
checkboxScrollView.Q<Toggle>($"{toggleNamePrefix}-{index}").SetValueWithoutNotify(true);
}
}
if (CheckboxModes.HasFlag(CheckboxModes.AtMostOne) && evt.newValue)
{
for (int j = 0; j < Checkboxes.Length; j++)
{
if (j == index)
continue;
Checkboxes[j] = new KeyValuePair<string, bool>(Checkboxes[j].Key, false);
checkboxScrollView.Q<Toggle>($"{toggleNamePrefix}-{j}").SetValueWithoutNotify(false);
}
}
});
}
}
private void CreateOptionButtonGUI()
{
// Option Container
VisualElement optionContainer = new VisualElement
{
style =
{
flexDirection = FlexDirection.Row,
flexGrow = 0,
flexShrink = 0,
justifyContent = Justify.FlexEnd,
marginRight = 8,
marginLeft = 8,
marginTop = 8,
marginBottom = 8,
flexWrap = Wrap.Wrap,
maxHeight = 44,
}
};
rootVisualElement.Add(optionContainer);
// Option Buttons
for (int i = 0; i < ButtonTexts.Length; i++)
{
int index = i;
Button optionButton = new Button(() => OnClickOptionButton(index))
{
text = ButtonTexts[i],
style =
{
minWidth = 50,
}
};
optionContainer.Add(optionButton);
if (i == 0)
optionButton.Focus();
}
}
private void OnClickOptionButton(int index)
{
SelectedOptionIndex = index;
Close();
}
private static void DecorateToggle(Toggle toggle)
{
VisualElement toggleInput = toggle.Q<VisualElement>(className: Toggle.inputUssClassName);
toggleInput.style.flexGrow = 0;
Label toggleLabel = toggle.Q<Label>(className: Toggle.labelUssClassName);
#if UNITY_2021_3_OR_NEWER
toggleLabel.enableRichText = true;
toggleLabel.style.textOverflow = TextOverflow.Ellipsis;
#endif
toggleLabel.style.flexGrow = 1;
toggleLabel.style.marginLeft = 2;
toggleLabel.style.marginRight = 0;
toggleLabel.style.paddingLeft = 2;
toggleLabel.style.paddingRight = 1;
toggleLabel.style.overflow = Overflow.Hidden;
toggleLabel.style.whiteSpace = WhiteSpace.Normal;
toggle.Remove(toggleLabel);
toggleInput.Add(toggleLabel);
}
}
}
@SolarianZ
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment