[C#] xGUI, petite extension pour le GUI built-in

Cette section est destinée aux scripts partagés par la communauté. Chaque post est destiné à un script. Suivez bien les recommandations.
Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

[C#] xGUI, petite extension pour le GUI built-in

Message par artemisart » 18 Avr 2013 00:19

-NOM DU SCRIPT : xGUI

-AUTEUR: artemisart

-DESCRIPTION :
Cette classe est un portage presque complet de la classe EditorGUI (toutes les fonctions ont été recodées à part DisabledGroup et ChangeCheck (merci ILSpy :mrgreen: )).

- Fonctions utilisables : L'utilisation est extrêmement similaire à EditorGUI.
  • Créer un groupe désactivable :
    - void BeginDisabledGroup (bool disabled)
    - void EndDisabledGroup ()
     
  • Vérifie si un contrôle a été modifié à l'intérieur d'un bloc de code.
    - void BeginChangeCheck ()
    - bool EndChangeCheck ()
     
  • édition d'int
    - int IntField (Rect rect, int value)
     
  • édition de float, peut accepter les valeurs infinies (par défaut non), écriture normale ou scientifique
    - float FloatField (Rect rect, float value)
    - float FloatField (Rect rect, float value, bool allowInfinity)
     
  • édition de Vector, yOffset est l'offset en pixels entre les différents FloatFields (x, y, z)
    - Vector2/3/4 VectorField (Rect rect, Vector2/3/4 value)
    - Vector2/3/4 VectorField (Rect rect, Vector2/3/4 value, float yOffset)
    doc : http://docs.unity3d.com/Documentation/S ... Field.html, http://docs.unity3d.com/Documentation/S ... Field.html, http://docs.unity3d.com/Documentation/S ... Field.html
     
  • édition de Rect, idem pour yOffset
    - Rect RectField (Rect rect, Rect value)
    - Rect RectField (Rect rect, Rect value, float yOffset)
     
  • édition de Bounds, idem pour yOffset
    - Bounds BoundsField (Rect rect, Bounds value)
    - Bounds BoundsField (Rect rect, Bounds value, float yOffset)
     
  • sélection dans une liste, selectedIndex est l'option actuellement sélectionné et displayedOptions est la liste des options. selectionHighlight est le caractère utilisé pour indiquer la sélection (par défaut ►).
    - int Popup (Rect rect, int selectedIndex, string[] displayedOptions)
    - int Popup (Rect rect, int selectedIndex, string[] displayedOptions, char selectionHighlight)
     
  • sélection dans un enum, T doit être un enum, la fonction est générique seulement pour éviter à l'utilisateur de convertir le retour de la fonction.
    - T EnumPopup<T> (Rect rect, T selected)
     
  • multisélection dans une liste (bitfield), mask est le bitfield displayedOptions est la liste des options. selectionHighlight est le caractère utilisé pour indiquer la sélection (par défaut ►).
    - int MaskField (Rect rect, int mask, string[] displayedOptions)
    - int MaskField (Rect rect, int mask, string[] displayedOptions, char selectionHighlight)
     
  • multisélection dans un enum, T doit être un enum, la fonction est générique seulement pour éviter à l'utilisateur de convertir le retour de la fonction.
    - T EnumMaskField<T> (Rect rect, T mask)
- Quelques précisions :
Dans le cas où plusieurs champs sont affichés, (toutes les fonctions sauf IntField et FloatField), le paramètre rect est le rectangle utilisé pour le premier champ, et non l'ensemble.

Dans le IntField, on peut rentrer juste des chiffres, et l'appui sur la touche "-" inverse le signe du nombre.
Exemple : si on entre "54-", le nombre sera "-54", et si on ré-appui sur "-" (ce qui donne "-54-") le nombre sera 54.

Dans le FloatField, c'est pareil, la touche - inverse le signe du nombre, + les virgules sont transformés en points et on peut autoriser l'utilisateur à entrer l'infini (bool allowInfinity) (et la casse est ignorée).
Quelques exemples :
  • "0.1", ".1", "0,1" et ",1" donnent 0.1f
  • "inf" et "infinity" donnent float.PositiveInfinity / Mathf.Infinity
  • "-inf" et "-infinity" donnent float.NegativeInfinity / Mathf.NegativeInfinity
  • "1e3" et "1e+3" donnent 1000
  • "1e-3" donne 0.001f
Si le nombre entrée ne peut pas être converti, la dernière valeur valide est utilisée et cette image Image apparait dans le field (le lien est à la fin du post).

-UTILISATION :
Exemple :

Code : Tout sélectionner

using UnityEngine;

public class xGUItest : MonoBehaviour
{
	[System.Flags]
	public enum MyEnum 
	{
		first,
		second,
		third,
		last
	}
	
	public bool disabled = false;
	public int myInt = 0;
	public float myFloat = 0;
	public float myFloatAllowInfinity = 0;
	public Vector4 myVector;
	public Rect myRect;
	public Bounds myBounds;
	public int mySelected;
	public int myFlags;
	public string[] myOptions = { "haha", "truc", "bidule" };
	public MyEnum myEnum;
	public MyEnum myEnumFlags;
	
	void OnGUI ()
	{
		xGUI.BeginChangeCheck ();
		
		xGUI.BeginDisabledGroup (disabled);
		DrawGUI ();
		xGUI.EndDisabledGroup ();
		
		if (xGUI.EndChangeCheck ())
			print ("changed");
	}
	
	void DrawGUI ()
	{
		myInt = xGUI.IntField (new Rect (10, 10, 100, 20), myInt);
		
		myFloat = xGUI.FloatField (new Rect (10, 40, 100, 20), myFloat);
		
		myFloatAllowInfinity = xGUI.FloatField (new Rect (10, 70, 100, 20), myFloatAllowInfinity, true);
		
		myVector = xGUI.VectorField (new Rect (10, 100, 100, 20), myVector);
		
		myRect = xGUI.RectField (new Rect (10, 190, 100, 20), myRect);
		
		myBounds = xGUI.BoundsField (new Rect (120, 10, 100, 20), myBounds);
		
		mySelected = xGUI.Popup (new Rect (230, 10, 100, 20), mySelected, myOptions);
		
		myEnum = xGUI.EnumPopup (new Rect (230, 100, 100, 20), myEnum);
		
		myFlags = xGUI.MaskField (new Rect (230, 200, 100, 20), myFlags, myOptions);
		
		myEnumFlags = xGUI.EnumMaskField (new Rect (230, 330, 100, 20), myEnumFlags);
	}
}
-SCRIPT :

Code : Tout sélectionner

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public static class xGUI
{
	private static Texture error_tex = null;
	private static Stack<bool> enabledStack = new Stack<bool> ();
	private static Stack<bool> changedStack = new Stack<bool> ();
	private static string currentEditingString = "empty";
	private static string lastFocusedControl;
	
	private static void Load ()
	{
		if (error_tex == null)
			error_tex = Resources.Load ("Textures/error") as Texture;
	}
	
	private static string GetHash (string a, Rect b)
	{
		return string.Format ("xGUI{0}{1}{2}{3}{4}", a, b.x, b.y, b.width, b.height);
	}
	
	private static void SetCES (object value)
	{
		if (currentEditingString == "empty")
			currentEditingString = value.ToString ();
	}
	
	private static bool HasFocus (string hash)
	{
		if ((hash.StartsWith ("xGUIPopup") || hash.StartsWith ("xGUIMaskField")) && lastFocusedControl == hash)
			return true;
		if (GUI.GetNameOfFocusedControl () == hash)
		{
			if (lastFocusedControl != hash)
				currentEditingString = "empty";
			lastFocusedControl = hash;
			return true;
		}
		return false;
	}
	
	private static void NumberTextField (Rect rect, bool isFloat)
	{
		currentEditingString = GUI.TextField (rect, currentEditingString).ToLower ();
		if (isFloat)
			currentEditingString = currentEditingString.Replace (',', '.');
		currentEditingString = new string (currentEditingString
			.Where (c => (isFloat ? "inftye0123456789.+-" : "0123456789-").Contains (c))
			.ToArray ()
			);
		if (currentEditingString.Length > 1 && currentEditingString.EndsWith ("-") && !currentEditingString.EndsWith ("e-"))
		{
			if (currentEditingString[0] == '-')
				currentEditingString = currentEditingString.Substring (1, currentEditingString.Length - 2);
			else
				currentEditingString = "-" + currentEditingString.Substring (0, currentEditingString.Length - 1);
		}
	}
	
	public static void BeginDisabledGroup (bool disabled)
	{
		enabledStack.Push (GUI.enabled);
		GUI.enabled &= !disabled;
	}
	
	public static void EndDisabledGroup ()
	{
		GUI.enabled = enabledStack.Pop ();
	}
	
	public static void BeginChangeCheck ()
	{
		changedStack.Push (GUI.changed);
		GUI.changed = false;
	}
	
	public static bool EndChangeCheck ()
	{
		bool changed = GUI.changed;
		GUI.changed |= changedStack.Pop ();
		return changed;
	}
	
	public static int IntField (Rect rect, int value)
	{
		string hash = GetHash ("IntField", rect);
		GUI.SetNextControlName (hash);
		if (HasFocus (hash))
		{
			SetCES (value);
			NumberTextField (rect, false);
			int temp;
			if (int.TryParse (currentEditingString, out temp))
				value = temp;
		}
		else
			GUI.TextField (rect, value.ToString ());
		return value;
	}
	
	public static float FloatField (Rect rect, float value)
	{
		return FloatField (rect, value, false);
	}
	
	public static float FloatField (Rect rect, float value, bool allowInfinity)
	{
		Load ();
		string hash = GetHash ("FloatField", rect);
		GUI.SetNextControlName (hash);
		if (HasFocus (hash))
		{
			SetCES (value);
			NumberTextField (rect, true);
			if (allowInfinity)
			{
				if (currentEditingString == "inf" || currentEditingString == "infinity")
					return float.PositiveInfinity;
				if (currentEditingString == "-inf" || currentEditingString == "-infinity")
					return float.NegativeInfinity;
			}
			float temp;
			if (float.TryParse (currentEditingString, out temp))
				value = temp;
			else
				GUI.DrawTexture (new Rect (rect.x + rect.width - 20, rect.y + 2, 16, 16), error_tex);
		}
		else
			GUI.TextField (rect, value.ToString ());
		return value;
	}
	
	public static Vector2 VectorField (Rect rect, Vector2 value) { return VectorField (rect, value, 20); }
	public static Vector2 VectorField (Rect rect, Vector2 value, float yOffset)
	{
		for (int i = 0; i < 2; i++)
		{
			value[i] = FloatField (rect, value[i]);
			rect.y += yOffset;
		}
		return value;
	}
	
	public static Vector3 VectorField (Rect rect, Vector3 value) { return VectorField (rect, value, 20); }
	public static Vector3 VectorField (Rect rect, Vector3 value, float yOffset)
	{
		for (int i = 0; i < 3; i++)
		{
			value[i] = FloatField (rect, value[i]);
			rect.y += yOffset;
		}
		return value;
	}
	
	public static Vector4 VectorField (Rect rect, Vector4 value) { return VectorField (rect, value, 20); }
	public static Vector4 VectorField (Rect rect, Vector4 value, float yOffset)
	{
		for (int i = 0; i < 4; i++)
		{
			value[i] = FloatField (rect, value[i]);
			rect.y += yOffset;
		}
		return value;
	}
	
	public static Rect RectField (Rect rect, Rect value) { return RectField (rect, value, 20); }
	public static Rect RectField (Rect rect, Rect value, float yOffset)
	{
		value.x = FloatField (rect, value.x);
		rect.y += yOffset;
		value.y = FloatField (rect, value.y);
		rect.y += yOffset;
		value.width = FloatField (rect, value.width);
		rect.y += yOffset;
		value.height = FloatField (rect, value.height);
		return value;
	}
	
	public static Bounds BoundsField (Rect rect, Bounds value) { return BoundsField (rect, value, 20); }
	public static Bounds BoundsField (Rect rect, Bounds value, float yOffset)
	{
		GUI.Label (rect, "Center");
		rect.y += yOffset;
		value.center = VectorField (rect, value.center, yOffset);
		rect.y += yOffset * 3;
		GUI.Label (rect, "Extents");
		rect.y += yOffset;
		value.extents = VectorField (rect, value.extents, yOffset);
		return value;
	}
	
	public static int Popup (Rect rect, int selectedIndex, string[] displayedOptions) { return Popup (rect, selectedIndex, displayedOptions, '►'); }
	public static int Popup (Rect rect, int selectedIndex, string[] displayedOptions, char selectionHighlight)
	{
		string hash = GetHash ("Popup", rect);
		GUI.SetNextControlName (hash);
		if (HasFocus (hash))
		{
			GUI.Button (rect, displayedOptions[selectedIndex]);
			int height = 18 * displayedOptions.Length + 2;
			if (Event.current.type == EventType.MouseDown &&
				!new Rect (rect.x, rect.y, rect.width, rect.height + height).Contains (Event.current.mousePosition))
			{
				lastFocusedControl = "";
				return selectedIndex;
			}
			rect.y += rect.height;
			GUI.Box (new Rect (rect.x, rect.y, rect.width, height), "");
			rect.x += 5;
			rect.width -= 5;
			for (int i = 0; i < displayedOptions.Length; i++)
			{
				if (selectedIndex == i)
					GUI.Label (rect, selectionHighlight + "\t" + displayedOptions[i]);
				else if (GUI.Button (rect, "\t" + displayedOptions[i], GUI.skin.label))
					selectedIndex = i;
				rect.y += 18;
			}
		}
		else if (GUI.Button (rect, displayedOptions[selectedIndex]))
			lastFocusedControl = hash;
		return selectedIndex;
	}
	
	public static T EnumPopup<T> (Rect rect, T selected) where T : struct, IComparable, IFormattable, IConvertible
	{
		Type t = typeof (T);
		if (!t.IsEnum)
			throw new Exception ("parameter T selected must be of type System.Enum");
		return (T)Enum.Parse (t, Popup (rect, Convert.ToInt32 (selected), Enum.GetNames (t)).ToString ());
	}
	
	public static int MaskField (Rect rect, int mask, string[] displayedOptions) { return MaskField (rect, mask, displayedOptions, '►'); }
	public static int MaskField (Rect rect, int mask, string[] displayedOptions, char selectionHighlight)
	{
		int i;
		string label;
		List<int> selectedIndexes = new List<int> ();
		List<string> options = new List<string> { "Nothing", "Everything" };
		options.AddRange (displayedOptions);
		for (i = 0; i < displayedOptions.Length; i++)
			if ((mask & 1 << i) > 0)
				selectedIndexes.Add (i + 2);
		
		if (selectedIndexes.Count == 0)
			label = "Nothing";
		else if (selectedIndexes.Count == 1)
			label = options[selectedIndexes[0]];
		else if (selectedIndexes.Count < displayedOptions.Length)
			label = "Mixed ...";
		else
		{
			label = "Everything";
			selectedIndexes.Add (1);
		}
		
		i = 0;
		int count = displayedOptions.Count (useless => (mask & 1 << i) == 1 << i++);
		i = 0;
		if (count == 0)
			label = "Nothing";
		else if (count == 1)
			label = displayedOptions.First (useless => (mask & 1 << i) == 1 << i++);
		else if (count < displayedOptions.Length)
			label = "Mixed ...";
		else
			label = "Everything";
		
		string hash = GetHash ("MaskField", rect);
		GUI.SetNextControlName (hash);
		if (HasFocus (hash))
		{
			GUI.Button (rect, label);
			int height = 18 * options.Count + 2;
			if (Event.current.type == EventType.MouseDown &&
				!new Rect (rect.x, rect.y, rect.width, rect.height + height).Contains (Event.current.mousePosition))
			{
				lastFocusedControl = "";
				return mask;
			}
			rect.y += rect.height;
			GUI.Box (new Rect (rect.x, rect.y, rect.width, height), "");
			rect.x += 5;
			rect.width -= 5;
			for (i = -2; i < displayedOptions.Length; i++)
			{
				if (i == -2)
				{
					if (count == displayedOptions.Length)
						GUI.Label (rect, selectionHighlight + "\tEverything");
					else if (GUI.Button (rect, "\tEverything", GUI.skin.label))
						mask = -1;
				}
				else if (i == -1)
				{
					if (count == 0)
						GUI.Label (rect, selectionHighlight + "\tNothing");
					else if (GUI.Button (rect, "\tNothing", GUI.skin.label))
						mask = 0;
				}
				else if (GUI.Button (rect, ((mask & 1 << i) == 1 << i ? selectionHighlight + "\t" : "\t") + displayedOptions[i], GUI.skin.label))
					mask ^= 1 << i;
				rect.y += 18;
			}
		}
		else if (GUI.Button (rect, label))
			lastFocusedControl = hash;
		return mask;
	}
	
	public static T EnumMaskField<T> (Rect rect, T mask) where T : struct, IComparable, IFormattable, IConvertible
	{
		Type t = typeof (T);
		if (!t.IsEnum)
			throw new Exception ("parameter T mask must be of type System.Enum");
		return (T)Enum.Parse (t, MaskField (rect, Convert.ToInt32 (mask), Enum.GetNames (t)).ToString ());
	}
}
+ Image à télécharger et à placer ici : "Assets/Resources/Textures/error.png" : https://dl.dropboxusercontent.com/u/130416610/error.png (clic droit > enregistrer la cible du lien sous...).
Dernière édition par artemisart le 19 Avr 2013 16:16, édité 8 fois.

Avatar de l’utilisateur
cayou66
Codeur
Codeur
Messages : 6450
Inscription : 30 Juin 2011 14:45
Localisation : Montréal

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par cayou66 » 18 Avr 2013 03:19

Cool, ça a l'air intéressant :)

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par artemisart » 18 Avr 2013 12:26

Up !
Ajout d'une vraie description et d'exemples + correction d'un conflit entre "e-" et le "-" de signe.
cayou66 a écrit :Cool, ça a l'air intéressant :)
Merki ! :)

Avatar de l’utilisateur
Max
Messages : 8773
Inscription : 30 Juil 2011 13:57
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par Max » 18 Avr 2013 16:17

pas mal ;)
tu as pondu ça au départ suite à un besoin perso , ou juste pour le fun et le partage ?
Image
Pas d'aide par MP, le forum est là pour ça.
En cas de doute sur les bonnes pratiques à adopter sur le forum, consulter la Charte et sa FAQ

Avatar de l’utilisateur
yoyoyaya
Messages : 1656
Inscription : 30 Mai 2011 13:14
Localisation : PAAAAARTOUUUU
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par yoyoyaya » 18 Avr 2013 17:03

Max a écrit :ou juste pour le fun et le partage ?
Masochisme ? :mrgreen:

Intéressent tout ça en effet.
ImageImage

Avatar de l’utilisateur
Titan
Messages : 582
Inscription : 12 Sep 2011 13:54
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par Titan » 18 Avr 2013 17:24

tu gére infinty... mais est ce que tu gère NaN ???
sinon un x.tostring() et un Convert.ToInt32(x) avec la textbox traditionnel, ça suffit pas ?
____________________________________________
Hop Boy

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par artemisart » 18 Avr 2013 18:10

Max a écrit :pas mal ;)
tu as pondu ça au départ suite à un besoin perso , ou juste pour le fun et le partage ?
Les 2
Titan a écrit :tu gére infinty... mais est ce que tu gère NaN ???
La fonction ne doit normalement jamais retourner NaN (ou alors y a un bug).
Si le texte entré par l'utilisateur n'est pas valide, la valeur n'est pas modifié.
Titan a écrit :sinon un x.tostring() et un Convert.ToInt32(x) avec la textbox traditionnel, ça suffit pas ?
Pour afficher un nombre, ça suffit, mais pour entrer un nombre non.

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par artemisart » 19 Avr 2013 11:59

Big up !

Au menu : portage presque complet de EditorGUI, donc en plus de IntField et FloatField, on peut maintenant utiliser :
Begin/EndDisabledGroup, Begin/EndChangeCheck, VectorField, RectField, Bounds, Popup, EnumPopup, MaskField et EnumMaskField !

J'ai mis à jour le premier post, tout est décrit + l'exemple fait le tour de toutes les fonctions mais si vous avez des questions n'hésitez pas ;) .

Avatar de l’utilisateur
artemisart
Messages : 1893
Inscription : 21 Juin 2011 19:51
Localisation : Centre
Contact :

Re: [C#] xGUI, petite extension pour le GUI built-in

Message par artemisart » 19 Avr 2013 16:18

Petite correction d'un bug de focus sur les IntField/FloatField (script mis à jour).

Répondre

Revenir vers « Scripts »