Browse Source

Merge pull request #798 from snozbot/feature/FlowchartWindowGridSnap

Add option to Grid Snap blocks in flowchart window
master
Steve Halliwell 5 years ago committed by GitHub
parent
commit
5dbf353bb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      Assets/Fungus/Scripts/Components/Block.cs
  2. 16
      Assets/Fungus/Scripts/Editor/EditorZoomArea.cs
  3. 203
      Assets/Fungus/Scripts/Editor/FlowchartWindow.cs
  4. 5
      Assets/Fungus/Scripts/Editor/FungusEditorPreferences.cs

7
Assets/Fungus/Scripts/Components/Block.cs

@ -375,6 +375,12 @@ namespace Fungus
public virtual List<Block> GetConnectedBlocks()
{
var connectedBlocks = new List<Block>();
GetConnectedBlocks(ref connectedBlocks);
return connectedBlocks;
}
public virtual void GetConnectedBlocks(ref List<Block> connectedBlocks)
{
for (int i = 0; i < commandList.Count; i++)
{
var command = commandList[i];
@ -383,7 +389,6 @@ namespace Fungus
command.GetConnectedBlocks(ref connectedBlocks);
}
}
return connectedBlocks;
}
/// <summary>

16
Assets/Fungus/Scripts/Editor/EditorZoomArea.cs

@ -52,6 +52,22 @@ namespace Fungus.EditorUtils
result.y += pivotPoint.y;
return result;
}
public static Rect SnapPosition(this Rect rect, float snapInterval)
{
var tmp = rect;
var x = tmp.position.x;
var y = tmp.position.y;
tmp.position = new Vector2(Mathf.RoundToInt(x / snapInterval) * snapInterval, Mathf.RoundToInt(y / snapInterval) * snapInterval);
return tmp;
}
public static Rect SnapWidth(this Rect rect, float snapInterval)
{
var tmp = rect;
tmp.width = Mathf.RoundToInt(tmp.width / snapInterval) * snapInterval;
return tmp;
}
}
public class EditorZoomArea

203
Assets/Fungus/Scripts/Editor/FlowchartWindow.cs

@ -172,12 +172,29 @@ namespace Fungus.EditorUtils
}
}
public const float GridLineSpacingSize = 120;
public const float GridObjectSnap = 20;
public const float DefaultBlockHeight = 40;
public const float BlockMinWidth = 60;
public const float BlockMaxWidth = 240;
public const float MinZoomValue = 0.25f;
public const float MaxZoomValue = 1f;
public const int HorizontalPad = 20;
public const int VerticalPad = 5;
//defines the distance between a down and up for a right click to be a click rather than a drag
public const float RightClickTolerance = 5f;
public const string SearchFieldName = "search";
protected readonly Color connectionColor = new Color(0.65f, 0.65f, 0.65f, 1.0f);
protected List<BlockCopy> copyList = new List<BlockCopy>();
public static List<Block> deleteList = new List<Block>();
protected Vector2 startDragPosition;
public const float minZoomValue = 0.25f;
public const float maxZoomValue = 1f;
protected GUIStyle nodeStyle = new GUIStyle();
protected GUIStyle nodeStyle, descriptionStyle, handlerStyle;
protected static BlockInspector blockInspector;
protected int forceRepaintCount;
protected Texture2D addTexture;
@ -187,12 +204,9 @@ namespace Fungus.EditorUtils
protected Vector2 startSelectionBoxPosition = -Vector2.one;
protected List<Block> mouseDownSelectionState = new List<Block>();
protected Color gridLineColor = Color.black;
protected readonly Color connectionColor = new Color(0.65f, 0.65f, 0.65f, 1.0f);
// Context Click occurs on MouseDown which interferes with panning
// Track right click positions manually to show menus on MouseUp
protected Vector2 rightClickDown = -Vector2.one;
protected const float rightClickTolerance = 5f;
protected const string searchFieldName = "search";
private string searchString = string.Empty;
protected Rect searchRect;
protected Rect popupRect;
@ -245,13 +259,6 @@ namespace Fungus.EditorUtils
protected virtual void OnEnable()
{
// All block nodes use the same GUIStyle, but with a different background
nodeStyle.border = new RectOffset(20, 20, 5, 5);
nodeStyle.padding = nodeStyle.border;
nodeStyle.contentOffset = Vector2.zero;
nodeStyle.alignment = TextAnchor.MiddleCenter;
nodeStyle.wordWrap = true;
addTexture = FungusEditorResources.AddSmall;
addButtonContent = new GUIContent(addTexture, "Add a new block");
connectionPointTexture = FungusEditorResources.ConnectionPoint;
@ -272,6 +279,37 @@ namespace Fungus.EditorUtils
#endif
}
//cache styles here, rather than duping them for every block we may ever draw,
// does mean any modifications made to the style when drawing must be undone as you go
protected void InitStyles()
{
if (nodeStyle == null)
{
nodeStyle = new GUIStyle();
// All block nodes use the same GUIStyle, but with a different background
nodeStyle.border = new RectOffset(HorizontalPad, HorizontalPad, VerticalPad, VerticalPad);
nodeStyle.padding = nodeStyle.border;
nodeStyle.contentOffset = Vector2.zero;
nodeStyle.alignment = TextAnchor.MiddleCenter;
nodeStyle.wordWrap = true;
}
if (EditorStyles.helpBox != null && descriptionStyle == null)
{
descriptionStyle = new GUIStyle(EditorStyles.helpBox);
descriptionStyle.wordWrap = true;
}
if (EditorStyles.whiteLabel != null && handlerStyle == null)
{
handlerStyle = new GUIStyle(EditorStyles.whiteLabel);
handlerStyle.wordWrap = true;
handlerStyle.margin.top = 0;
handlerStyle.margin.bottom = 0;
handlerStyle.alignment = TextAnchor.MiddleCenter;
}
}
protected virtual void OnDisable()
{
EditorApplication.update -= OnEditorUpdate;
@ -530,7 +568,7 @@ namespace Fungus.EditorUtils
break;
case EventType.KeyDown:
if (GUI.GetNameOfFocusedControl() == searchFieldName)
if (GUI.GetNameOfFocusedControl() == SearchFieldName)
{
var centerBlock = false;
var selectBlock = false;
@ -715,6 +753,8 @@ namespace Fungus.EditorUtils
return;
}
InitStyles();
DeleteBlocks();
UpdateFilteredBlocks();
@ -787,7 +827,7 @@ namespace Fungus.EditorUtils
// Draw scale bar and labels
GUILayout.Label("Scale", EditorStyles.miniLabel);
var newZoom = GUILayout.HorizontalSlider(
flowchart.Zoom, minZoomValue, maxZoomValue, GUILayout.MinWidth(40), GUILayout.MaxWidth(100)
flowchart.Zoom, MinZoomValue, MaxZoomValue, GUILayout.MinWidth(40), GUILayout.MaxWidth(100)
);
GUILayout.Label(flowchart.Zoom.ToString("0.0#x"), EditorStyles.miniLabel, GUILayout.Width(30));
@ -805,7 +845,7 @@ namespace Fungus.EditorUtils
GUILayout.FlexibleSpace();
// Draw search bar
GUI.SetNextControlName(searchFieldName);
GUI.SetNextControlName(SearchFieldName);
var newString = EditorGUILayout.TextField(searchString, ToolbarSeachTextFieldStyle, GUILayout.Width(150));
if (newString != searchString)
{
@ -861,7 +901,7 @@ namespace Fungus.EditorUtils
// Draw block search popup on top of other controls
if (GUI.GetNameOfFocusedControl() == searchFieldName && filteredBlocks.Length > 0)
if (GUI.GetNameOfFocusedControl() == SearchFieldName && filteredBlocks.Length > 0)
{
DrawBlockPopup(e);
}
@ -1152,7 +1192,7 @@ namespace Fungus.EditorUtils
break;
case MouseButton.Right:
if (Vector2.Distance(rightClickDown, e.mousePosition) > rightClickTolerance)
if (Vector2.Distance(rightClickDown, e.mousePosition) > RightClickTolerance)
{
rightClickDown = -Vector2.one;
}
@ -1207,6 +1247,11 @@ namespace Fungus.EditorUtils
Undo.RecordObject(block, "Block Position");
tempRect.position += distance;
block._NodeRect = tempRect;
if (FungusEditorPreferences.useGridSnap)
{
block._NodeRect = block._NodeRect.SnapPosition(GridObjectSnap);
}
Repaint();
}
dragBlock = null;
@ -1345,12 +1390,6 @@ namespace Fungus.EditorUtils
{
DrawGrid();
// Draw connections
foreach (var block in blocks)
{
DrawConnections(block);
}
//draw all non selected
for (int i = 0; i < blocks.Length; ++i)
{
@ -1469,13 +1508,14 @@ namespace Fungus.EditorUtils
{
var prevZoom = flowchart.Zoom;
flowchart.Zoom += delta;
flowchart.Zoom = Mathf.Clamp(flowchart.Zoom, minZoomValue, maxZoomValue);
flowchart.Zoom = Mathf.Clamp(flowchart.Zoom, MinZoomValue, MaxZoomValue);
var deltaSize = position.size / prevZoom - position.size / flowchart.Zoom;
var offset = -Vector2.Scale(deltaSize, center);
flowchart.ScrollPos += offset;
forceRepaintCount = 1;
}
//Potentially could be faster using https://forum.unity.com/threads/how-do-i-access-the-background-image-used-for-the-animator.501876/
protected virtual void DrawGrid()
{
float width = this.position.width / flowchart.Zoom;
@ -1483,23 +1523,22 @@ namespace Fungus.EditorUtils
Handles.color = gridLineColor;
float gridSize = 128f;
float x = flowchart.ScrollPos.x % gridSize;
float x = flowchart.ScrollPos.x % GridLineSpacingSize;
while (x < width)
{
Handles.DrawLine(new Vector2(x, 0), new Vector2(x, height));
x += gridSize;
x += GridLineSpacingSize;
}
float y = (flowchart.ScrollPos.y % gridSize);
float y = (flowchart.ScrollPos.y % GridLineSpacingSize);
while (y < height)
{
if (y >= 0)
{
Handles.DrawLine(new Vector2(0, y), new Vector2(width, y));
}
y += gridSize;
y += GridLineSpacingSize;
}
Handles.color = Color.white;
@ -1543,6 +1582,9 @@ namespace Fungus.EditorUtils
return newBlock;
}
//prevent every DrawConnections from allocating a new list for all of its connections
protected List<Block> connectedBlocksWorkSpace = new List<Block>();
protected virtual void DrawConnections(Block block)
{
if (block == null)
@ -1550,7 +1592,6 @@ namespace Fungus.EditorUtils
return;
}
var connectedBlocks = new List<Block>();
bool blockIsSelected = flowchart.SelectedBlock == block;
@ -1578,10 +1619,10 @@ namespace Fungus.EditorUtils
bool highlight = command.IsExecuting || (blockIsSelected && commandIsSelected);
connectedBlocks.Clear();
command.GetConnectedBlocks(ref connectedBlocks);
connectedBlocksWorkSpace.Clear();
command.GetConnectedBlocks(ref connectedBlocksWorkSpace);
foreach (var blockB in connectedBlocks)
foreach (var blockB in connectedBlocksWorkSpace)
{
if (blockB == null ||
block == blockB ||
@ -1945,7 +1986,7 @@ namespace Fungus.EditorUtils
case "Find":
blockPopupSelection = 0;
popupScroll = Vector2.zero;
EditorGUI.FocusTextInControl(searchFieldName);
EditorGUI.FocusTextInControl(SearchFieldName);
e.Use();
break;
}
@ -1967,10 +2008,14 @@ namespace Fungus.EditorUtils
searchString = string.Empty;
}
static protected List<Block> blockGraphicsUniqueListWorkSpace = new List<Block>();
static protected List<Block> blockGraphicsConnectedWorkSpace = new List<Block>();
protected virtual BlockGraphics GetBlockGraphics(Block block)
{
var graphics = new BlockGraphics();
blockGraphicsUniqueListWorkSpace.Clear();
blockGraphicsConnectedWorkSpace.Clear();
Color defaultTint;
if (block._EventHandler != null)
{
@ -1981,19 +2026,18 @@ namespace Fungus.EditorUtils
else
{
// Count the number of unique connections (excluding self references)
var uniqueList = new List<Block>();
var connectedBlocks = block.GetConnectedBlocks();
foreach (var connectedBlock in connectedBlocks)
block.GetConnectedBlocks(ref blockGraphicsConnectedWorkSpace);
foreach (var connectedBlock in blockGraphicsConnectedWorkSpace)
{
if (connectedBlock == block ||
uniqueList.Contains(connectedBlock))
blockGraphicsUniqueListWorkSpace.Contains(connectedBlock))
{
continue;
}
uniqueList.Add(connectedBlock);
blockGraphicsUniqueListWorkSpace.Add(connectedBlock);
}
if (uniqueList.Count > 1)
if (blockGraphicsUniqueListWorkSpace.Count > 1)
{
graphics.offTexture = FungusEditorResources.ChoiceNodeOff;
graphics.onTexture = FungusEditorResources.ChoiceNodeOn;
@ -2015,79 +2059,81 @@ namespace Fungus.EditorUtils
private void DrawBlock(Block block, Rect scriptViewRect)
{
float nodeWidthA = nodeStyle.CalcSize(new GUIContent(block.BlockName)).x + 10;
float nodeWidthB = 0f;
if (block._EventHandler != null)
Rect tempRect = block._NodeRect;
tempRect.width = Mathf.Clamp(nodeWidthA, BlockMinWidth, BlockMaxWidth);
tempRect.height = DefaultBlockHeight;
if (FungusEditorPreferences.useGridSnap)
{
nodeWidthB = nodeStyle.CalcSize(new GUIContent(block._EventHandler.GetSummary())).x + 10;
tempRect = tempRect.SnapWidth(GridObjectSnap);
}
Rect tempRect = block._NodeRect;
tempRect.width = Mathf.Max(Mathf.Max(nodeWidthA, nodeWidthB), 120);
tempRect.height = 40;
block._NodeRect = tempRect;
Rect windowRect = new Rect(block._NodeRect);
windowRect.position += flowchart.ScrollPos;
// Draw blocks
var graphics = GetBlockGraphics(block);
Rect windowRelativeRect = new Rect(block._NodeRect);
if (FungusEditorPreferences.useGridSnap)
{
windowRelativeRect = windowRelativeRect.SnapPosition(GridObjectSnap);
}
windowRelativeRect.position += flowchart.ScrollPos;
//skip if outside of view
if (!scriptViewRect.Overlaps(windowRect))
if (!scriptViewRect.Overlaps(windowRelativeRect))
return;
// Draw blocks
GUIStyle nodeStyleCopy = new GUIStyle(nodeStyle);
var graphics = GetBlockGraphics(block);
// Make sure node is wide enough to fit the node name text
float width = nodeStyleCopy.CalcSize(new GUIContent(block.BlockName)).x;
tempRect = block._NodeRect;
tempRect.width = Mathf.Max(block._NodeRect.width, width);
block._NodeRect = tempRect;
var tmpNormBg = nodeStyle.normal.background;
// Draw untinted highlight
if (block.IsSelected && !block.IsControlSelected)
{
GUI.backgroundColor = Color.white;
nodeStyleCopy.normal.background = graphics.onTexture;
GUI.Box(windowRect, "", nodeStyleCopy);
nodeStyle.normal.background = graphics.onTexture;
GUI.Box(windowRelativeRect, "", nodeStyle);
nodeStyle.normal.background = tmpNormBg;
}
if (block.IsControlSelected && !block.IsSelected)
{
GUI.backgroundColor = Color.white;
nodeStyleCopy.normal.background = graphics.onTexture;
nodeStyle.normal.background = graphics.onTexture;
var c = GUI.backgroundColor;
c.a = 0.5f;
GUI.backgroundColor = c;
GUI.Box(windowRect, "", nodeStyleCopy);
GUI.Box(windowRelativeRect, "", nodeStyle);
nodeStyle.normal.background = tmpNormBg;
}
// Draw tinted block; ensure text is readable
var brightness = graphics.tint.r * 0.3 + graphics.tint.g * 0.59 + graphics.tint.b * 0.11;
nodeStyleCopy.normal.textColor = brightness >= 0.5 ? Color.black : Color.white;
var tmpNormTxtCol = nodeStyle.normal.textColor;
nodeStyle.normal.textColor = brightness >= 0.5 ? Color.black : Color.white;
if (GUI.GetNameOfFocusedControl() == searchFieldName && !block.IsFiltered)
if (GUI.GetNameOfFocusedControl() == SearchFieldName && !block.IsFiltered)
{
graphics.tint.a *= 0.2f;
}
nodeStyleCopy.normal.background = graphics.offTexture;
nodeStyle.normal.background = graphics.offTexture;
GUI.backgroundColor = graphics.tint;
GUI.Box(windowRect, block.BlockName, nodeStyleCopy);
GUI.Box(windowRelativeRect, block.BlockName, nodeStyle);
GUI.backgroundColor = Color.white;
if (block.Description.Length > 0)
{
GUIStyle descriptionStyle = new GUIStyle(EditorStyles.helpBox);
descriptionStyle.wordWrap = true;
var content = new GUIContent(block.Description);
windowRect.y += windowRect.height;
windowRect.height = descriptionStyle.CalcHeight(content, windowRect.width);
GUI.Label(windowRect, content, descriptionStyle);
windowRelativeRect.y += windowRelativeRect.height;
windowRelativeRect.height = descriptionStyle.CalcHeight(content, windowRelativeRect.width);
GUI.Label(windowRelativeRect, content, descriptionStyle);
}
GUI.backgroundColor = Color.white;
nodeStyle.normal.textColor = tmpNormTxtCol;
nodeStyle.normal.background = tmpNormBg;
// Draw Event Handler labels
if (block._EventHandler != null)
{
@ -2097,13 +2143,7 @@ namespace Fungus.EditorUtils
{
handlerLabel = "<" + info.EventHandlerName + "> ";
}
GUIStyle handlerStyle = new GUIStyle(EditorStyles.whiteLabel);
handlerStyle.wordWrap = true;
handlerStyle.margin.top = 0;
handlerStyle.margin.bottom = 0;
handlerStyle.alignment = TextAnchor.MiddleCenter;
Rect rect = new Rect(block._NodeRect);
rect.height = handlerStyle.CalcHeight(new GUIContent(handlerLabel), block._NodeRect.width);
rect.x += flowchart.ScrollPos.x;
@ -2111,6 +2151,9 @@ namespace Fungus.EditorUtils
GUI.Label(rect, handlerLabel, handlerStyle);
}
DrawConnections(block);
}
}
}

5
Assets/Fungus/Scripts/Editor/FungusEditorPreferences.cs

@ -21,9 +21,11 @@ namespace Fungus
private static bool prefsLoaded = false;
private const string HIDE_MUSH_KEY = "hideMushroomInHierarchy";
private const string USE_LEGACY_MENUS = "useLegacyMenus";
private const string USE_GRID_SNAP = "useGridSnap";
public static bool hideMushroomInHierarchy;
public static bool useLegacyMenus;
public static bool useGridSnap;
static FungusEditorPreferences()
{
@ -63,6 +65,7 @@ namespace Fungus
// Preferences GUI
hideMushroomInHierarchy = EditorGUILayout.Toggle("Hide Mushroom Flowchart Icon", hideMushroomInHierarchy);
useLegacyMenus = EditorGUILayout.Toggle(new GUIContent("Legacy Menus", "Force Legacy menus for Event, Add Variable and Add Command menus"), useLegacyMenus);
useGridSnap = EditorGUILayout.Toggle(new GUIContent("Grid Snap", "Align and Snap block positions and widths in the flowchart window to the grid"), useGridSnap);
EditorGUILayout.Space();
//ideally if any are null, but typically it is all or nothing that have broken links due to version changes or moving files external to Unity
@ -113,6 +116,7 @@ namespace Fungus
{
EditorPrefs.SetBool(HIDE_MUSH_KEY, hideMushroomInHierarchy);
EditorPrefs.SetBool(USE_LEGACY_MENUS, useLegacyMenus);
EditorPrefs.SetBool(USE_GRID_SNAP, useGridSnap);
}
}
@ -120,6 +124,7 @@ namespace Fungus
{
hideMushroomInHierarchy = EditorPrefs.GetBool(HIDE_MUSH_KEY, false);
useLegacyMenus = EditorPrefs.GetBool(USE_LEGACY_MENUS, false);
useGridSnap = EditorPrefs.GetBool(USE_GRID_SNAP, false);
prefsLoaded = true;
}
}

Loading…
Cancel
Save