Browse Source

Merge branch 'snozbot/master'

master
Zach Vinless 8 years ago
parent
commit
bc898d978f
  1. 39
      Assets/Fungus/Scripts/Components/Flowchart.cs
  2. 6
      Assets/Fungus/Scripts/Editor/BlockInspector.cs
  3. 337
      Assets/Fungus/Scripts/Editor/FlowchartWindow.cs
  4. 32
      Docs/fungus_docs/coding_standard.md

39
Assets/Fungus/Scripts/Components/Flowchart.cs

@ -41,8 +41,7 @@ namespace Fungus
[SerializeField] protected Rect scrollViewRect;
[HideInInspector]
[FormerlySerializedAs("selectedSequence")]
[SerializeField] protected Block selectedBlock;
[SerializeField] protected List<Block> selectedBlocks = new List<Block>();
[HideInInspector]
[SerializeField] protected List<Command> selectedCommands = new List<Command>();
@ -325,9 +324,22 @@ namespace Fungus
public virtual Rect ScrollViewRect { get { return scrollViewRect; } set { scrollViewRect = value; } }
/// <summary>
/// Currently selected block in the Flowchart editor.
/// Current actively selected block in the Flowchart editor.
/// </summary>
public virtual Block SelectedBlock { get { return selectedBlock; } set { selectedBlock = value; } }
public virtual Block SelectedBlock
{
get
{
return selectedBlocks.FirstOrDefault();
}
set
{
selectedBlocks.Clear();
selectedBlocks.Add(value);
}
}
public virtual List<Block> SelectedBlocks { get { return selectedBlocks; } set { selectedBlocks = value; } }
/// <summary>
/// Currently selected command in the Flowchart editor.
@ -1040,6 +1052,25 @@ namespace Fungus
}
}
/// <summary>
/// Clears the list of selected blocks.
/// </summary>
public virtual void ClearSelectedBlocks()
{
selectedBlocks.Clear();
}
/// <summary>
/// Adds a block to the list of selected blocks.
/// </summary>
public virtual void AddSelectedBlock(Block block)
{
if (!selectedBlocks.Contains(block))
{
selectedBlocks.Add(block);
}
}
/// <summary>
/// Reset the commands and variables in the Flowchart.
/// </summary>

6
Assets/Fungus/Scripts/Editor/BlockInspector.cs

@ -83,6 +83,12 @@ namespace Fungus.EditorUtils
var flowchart = (Flowchart)block.GetFlowchart();
if (flowchart.SelectedBlocks.Count > 1)
{
GUILayout.Label("Multiple blocks selected");
return;
}
if (activeBlockEditor == null ||
!block.Equals(activeBlockEditor.target))
{

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

@ -33,7 +33,11 @@ namespace Fungus.EditorUtils
protected int forceRepaintCount;
protected Texture2D addTexture;
protected Rect selectionBox;
protected Vector2 startSelectionBoxPosition = new Vector2(-1.0f, -1.0f);
protected List<Block> mouseDownSelectionState = new List<Block>();
[MenuItem("Tools/Fungus/Flowchart Window")]
static void Init()
{
@ -120,7 +124,7 @@ namespace Fungus.EditorUtils
// Delete any scheduled objects
foreach (var deleteBlock in deleteList)
{
bool isSelected = (flowchart.SelectedBlock == deleteBlock);
bool isSelected = (flowchart.SelectedBlocks.Contains(deleteBlock));
var commandList = deleteBlock.CommandList;
foreach (var command in commandList)
@ -142,6 +146,9 @@ namespace Fungus.EditorUtils
DrawFlowchartView(flowchart);
DrawOverlay(flowchart);
// Handle selection box events after block and overlay events
HandleSelectionBox(flowchart);
if (forceRepaintCount > 0)
{
// Redraw on next frame to get crisp refresh rate
@ -166,7 +173,8 @@ namespace Fungus.EditorUtils
GUILayout.Space(8);
flowchart.Zoom = GUILayout.HorizontalSlider(flowchart.Zoom, minZoomValue, maxZoomValue, GUILayout.Width(100));
var newZoom = GUILayout.HorizontalSlider(flowchart.Zoom, minZoomValue, maxZoomValue, GUILayout.Width(100));
DoZoom(flowchart, newZoom - flowchart.Zoom, Vector2.one * 0.5f);
GUILayout.FlexibleSpace();
@ -248,18 +256,6 @@ namespace Fungus.EditorUtils
GLDraw.BeginGroup(scriptViewRect);
if (Event.current.button == 0 &&
Event.current.type == EventType.MouseDown &&
!mouseOverVariables)
{
flowchart.SelectedBlock = null;
if (!EditorGUI.actionKey)
{
flowchart.ClearSelectedCommands();
}
Selection.activeGameObject = flowchart.gameObject;
}
// The center of the Flowchart depends on the block positions and window dimensions, so we calculate it
// here in the FlowchartWindow class and store it on the Flowchart object for use later.
CalcFlowchartCenter(flowchart, blocks);
@ -280,6 +276,8 @@ namespace Fungus.EditorUtils
BeginWindows();
windowBlockMap.Clear();
bool useEvent = false;
bool endDrag = false;
for (int i = 0; i < blocks.Length; ++i)
{
var block = blocks[i];
@ -297,28 +295,33 @@ namespace Fungus.EditorUtils
tempRect.width = Mathf.Max(Mathf.Max(nodeWidthA, nodeWidthB), 120);
tempRect.height = 40;
if (Event.current.type == EventType.MouseDrag && dragWindowId == i)
if (dragWindowId > -1 && flowchart.SelectedBlocks.Contains(block))
{
tempRect.x += Event.current.delta.x;
tempRect.y += Event.current.delta.y;
if (Event.current.type == EventType.MouseDrag)
{
tempRect.x += Event.current.delta.x;
tempRect.y += Event.current.delta.y;
forceRepaintCount = 6;
}
else if (Event.current.type == EventType.MouseUp &&
dragWindowId == i)
{
Vector2 newPos = new Vector2(tempRect.x, tempRect.y);
tempRect.x = startDragPosition.x;
tempRect.y = startDragPosition.y;
Undo.RecordObject((Block)block, "Node Position");
tempRect.x = newPos.x;
tempRect.y = newPos.y;
forceRepaintCount = 6;
useEvent = true;
}
else if (Event.current.rawType == EventType.MouseUp)
{
Vector2 newPos = new Vector2(tempRect.x, tempRect.y);
tempRect.x = startDragPosition.x + (newPos.x - blocks[dragWindowId]._NodeRect.position.x);
tempRect.y = startDragPosition.y + (newPos.y - blocks[dragWindowId]._NodeRect.position.y);
dragWindowId = -1;
forceRepaintCount = 6;
block._NodeRect = tempRect;
Undo.RecordObject(block, "Node Position");
tempRect.x = newPos.x;
tempRect.y = newPos.y;
forceRepaintCount = 6;
useEvent = true;
endDrag = true;
}
}
block._NodeRect = tempRect;
@ -335,6 +338,13 @@ namespace Fungus.EditorUtils
windowBlockMap.Add(block);
}
dragWindowId = endDrag ? -1 : dragWindowId;
if (useEvent)
{
Event.current.Use();
}
EndWindows();
// Draw Event Handler labels
@ -407,6 +417,23 @@ namespace Fungus.EditorUtils
GLDraw.EndGroup();
EditorZoomArea.End();
// If event has yet to be used and user isn't multiselecting or panning, clear selection
bool validModifier = Event.current.alt || GetAppendModifierDown();
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && !validModifier)
{
Undo.RecordObject(flowchart, "Deselect");
flowchart.ClearSelectedCommands();
flowchart.ClearSelectedBlocks();
Selection.activeGameObject = flowchart.gameObject;
}
// Draw selection box
if (startSelectionBoxPosition.x >= 0 && startSelectionBoxPosition.y >= 0)
{
GUI.Box(selectionBox, "", (GUIStyle) "SelectionRect");
forceRepaintCount = 6;
}
}
public virtual void CalcFlowchartCenter(Flowchart flowchart, Block[] blocks)
@ -436,6 +463,77 @@ namespace Fungus.EditorUtils
flowchart.CenterPosition = center;
}
protected virtual void HandleSelectionBox(Flowchart flowchart)
{
if (Event.current.button == 0 && Event.current.modifiers != EventModifiers.Alt &&
!(UnityEditor.Tools.current == Tool.View && UnityEditor.Tools.viewTool == ViewTool.Pan))
{
switch (Event.current.type)
{
case EventType.MouseDown:
startSelectionBoxPosition = Event.current.mousePosition;
mouseDownSelectionState = new List<Block>(flowchart.SelectedBlocks);
Event.current.Use();
break;
case EventType.MouseDrag:
if (startSelectionBoxPosition.x >= 0 && startSelectionBoxPosition.y >= 0)
{
var topLeft = Vector2.Min(startSelectionBoxPosition, Event.current.mousePosition);
var bottomRight = Vector2.Max(startSelectionBoxPosition, Event.current.mousePosition);
selectionBox = Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
Rect zoomSelectionBox = selectionBox;
zoomSelectionBox.position -= flowchart.ScrollPos * flowchart.Zoom;
zoomSelectionBox.position /= flowchart.Zoom;
zoomSelectionBox.size /= flowchart.Zoom;
foreach (var block in flowchart.GetComponents<Block>())
{
if (zoomSelectionBox.Overlaps(block._NodeRect))
{
if (mouseDownSelectionState.Contains(block))
{
flowchart.SelectedBlocks.Remove(block);
}
else
{
flowchart.AddSelectedBlock(block);
}
}
else if (mouseDownSelectionState.Contains(block))
{
flowchart.AddSelectedBlock(block);
}
else
{
flowchart.SelectedBlocks.Remove(block);
}
}
}
Event.current.Use();
break;
}
if (Event.current.rawType == EventType.MouseUp)
{
selectionBox.size = Vector2.zero;
selectionBox.position = Vector2.one * -1;
startSelectionBoxPosition = selectionBox.position;
var tempList = new List<Block>(flowchart.SelectedBlocks);
flowchart.SelectedBlocks = mouseDownSelectionState;
Undo.RecordObject(flowchart, "Select");
flowchart.SelectedBlocks = tempList;
if (flowchart.SelectedBlock != null)
{
SetBlockForInspector(flowchart, flowchart.SelectedBlock);
}
}
}
}
protected virtual void PanAndZoom(Flowchart flowchart)
{
// Right click to drag view
@ -482,14 +580,28 @@ namespace Fungus.EditorUtils
zoom = true;
}
if (zoom)
if (zoom && selectionBox.size == Vector2.zero)
{
flowchart.Zoom -= Event.current.delta.y * 0.01f;
flowchart.Zoom = Mathf.Clamp(flowchart.Zoom, minZoomValue, maxZoomValue);
forceRepaintCount = 6;
Vector2 zoomCenter;
zoomCenter.x = Event.current.mousePosition.x / position.width;
zoomCenter.y = Event.current.mousePosition.y / position.height;
zoomCenter *= flowchart.Zoom;
DoZoom(flowchart, -Event.current.delta.y * 0.01f, zoomCenter);
}
}
protected virtual void DoZoom(Flowchart flowchart, float delta, Vector2 center)
{
var prevZoom = flowchart.Zoom;
flowchart.Zoom += delta;
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 = 6;
}
protected virtual void DrawGrid(Flowchart flowchart)
{
float width = this.position.width / flowchart.Zoom;
@ -532,22 +644,18 @@ namespace Fungus.EditorUtils
protected virtual void SelectBlock(Flowchart flowchart, Block block)
{
// Select the block and also select currently executing command
ShowBlockInspector(flowchart);
flowchart.SelectedBlock = block;
flowchart.ClearSelectedCommands();
if (block.ActiveCommand != null)
{
flowchart.AddSelectedCommand(block.ActiveCommand);
}
SetBlockForInspector(flowchart, block);
}
public static Block CreateBlock(Flowchart flowchart, Vector2 position)
{
Block newBlock = flowchart.CreateBlock(position);
Undo.RegisterCreatedObjectUndo(newBlock, "New Block");
ShowBlockInspector(flowchart);
flowchart.SelectedBlock = newBlock;
flowchart.ClearSelectedCommands();
// Use AddSelected instead of Select for when multiple blocks are duplicated
flowchart.AddSelectedBlock(newBlock);
SetBlockForInspector(flowchart, newBlock);
return newBlock;
}
@ -583,25 +691,50 @@ namespace Fungus.EditorUtils
if (Event.current.button == 0 &&
Event.current.alt == false)
{
dragWindowId = windowId;
if (!GetAppendModifierDown())
{
dragWindowId = windowId;
startDragPosition.x = block._NodeRect.x;
startDragPosition.y = block._NodeRect.y;
startDragPosition.x = block._NodeRect.x;
startDragPosition.y = block._NodeRect.y;
}
Event.current.Use();
}
if (windowId < windowBlockMap.Count)
{
Undo.RecordObject(flowchart, "Select");
SelectBlock(flowchart, block);
if (GetAppendModifierDown())
{
if (flowchart.SelectedBlocks.Contains(block))
{
flowchart.SelectedBlocks.Remove(block);
}
else
{
flowchart.AddSelectedBlock(block);
}
}
else
{
if (flowchart.SelectedBlocks.Contains(block))
{
SetBlockForInspector(flowchart, block);
}
else
{
SelectBlock(flowchart, block);
}
}
GUIUtility.keyboardControl = 0; // Fix for textarea not refeshing (change focus)
}
}
bool selected = false;
if (flowchart.SelectedBlock != null &&
flowchart.SelectedBlock.Equals(block))
if (flowchart.SelectedBlocks.Contains(block))
{
selected = true;
}
@ -670,7 +803,7 @@ namespace Fungus.EditorUtils
nodeStyleCopy.normal.background = offTex;
GUI.backgroundColor = tintColor;
GUI.Box(GUILayoutUtility.GetLastRect(), block.BlockName, nodeStyleCopy);
GUI.Box(boxRect, block.BlockName, nodeStyleCopy);
GUI.backgroundColor = Color.white;
@ -683,10 +816,14 @@ namespace Fungus.EditorUtils
if (Event.current.type == EventType.ContextClick)
{
flowchart.AddSelectedBlock(block);
GenericMenu menu = new GenericMenu ();
menu.AddItem(new GUIContent ("Duplicate"), false, DuplicateBlock, block);
menu.AddItem(new GUIContent ("Delete"), false, DeleteBlock, block);
// Use a copy because flowchart.SelectedBlocks gets modified
var blockList = new List<Block>(flowchart.SelectedBlocks);
menu.AddItem(new GUIContent ("Duplicate"), false, DuplicateBlocks, blockList);
menu.AddItem(new GUIContent ("Delete"), false, DeleteBlocks, blockList);
menu.ShowAsContext();
}
@ -804,61 +941,68 @@ namespace Fungus.EditorUtils
GUI.Label(dotBRect, "", new GUIStyle("U2D.dragDotActive"));
}
public static void DeleteBlock(object obj)
public static void DeleteBlocks(object obj)
{
var block = obj as Block;
FlowchartWindow.deleteList.Add(block);
var blocks = obj as List<Block>;
blocks.ForEach(block => FlowchartWindow.deleteList.Add(block));
}
protected static void DuplicateBlock(object obj)
protected static void DuplicateBlocks(object obj)
{
var flowchart = GetFlowchart();
Block block = obj as Block;
Vector2 newPosition = new Vector2(block._NodeRect.position.x +
Undo.RecordObject(flowchart, "Select");
flowchart.ClearSelectedBlocks();
var blocks = obj as List<Block>;
foreach (var block in blocks)
{
Vector2 newPosition = new Vector2(block._NodeRect.position.x +
block._NodeRect.width + 20,
block._NodeRect.y);
Block oldBlock = block;
Block oldBlock = block;
Block newBlock = FlowchartWindow.CreateBlock(flowchart, newPosition);
newBlock.BlockName = flowchart.GetUniqueBlockKey(oldBlock.BlockName + " (Copy)");
Block newBlock = FlowchartWindow.CreateBlock(flowchart, newPosition);
newBlock.BlockName = flowchart.GetUniqueBlockKey(oldBlock.BlockName + " (Copy)");
Undo.RecordObject(newBlock, "Duplicate Block");
Undo.RecordObject(newBlock, "Duplicate Block");
var commandList = oldBlock.CommandList;
foreach (var command in commandList)
{
if (ComponentUtility.CopyComponent(command))
var commandList = oldBlock.CommandList;
foreach (var command in commandList)
{
if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject))
if (ComponentUtility.CopyComponent(command))
{
Command[] commands = flowchart.GetComponents<Command>();
Command pastedCommand = commands.Last<Command>();
if (pastedCommand != null)
if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject))
{
pastedCommand.ItemId = flowchart.NextItemId();
newBlock.CommandList.Add(pastedCommand);
Command[] commands = flowchart.GetComponents<Command>();
Command pastedCommand = commands.Last<Command>();
if (pastedCommand != null)
{
pastedCommand.ItemId = flowchart.NextItemId();
newBlock.CommandList.Add(pastedCommand);
}
}
// This stops the user pasting the command manually into another game object.
ComponentUtility.CopyComponent(flowchart.transform);
}
// This stops the user pasting the command manually into another game object.
ComponentUtility.CopyComponent(flowchart.transform);
}
}
if (oldBlock._EventHandler != null)
{
if (ComponentUtility.CopyComponent(oldBlock._EventHandler))
if (oldBlock._EventHandler != null)
{
if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject))
if (ComponentUtility.CopyComponent(oldBlock._EventHandler))
{
EventHandler[] eventHandlers = flowchart.GetComponents<EventHandler>();
EventHandler pastedEventHandler = eventHandlers.Last<EventHandler>();
if (pastedEventHandler != null)
if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject))
{
pastedEventHandler.ParentBlock = newBlock;
newBlock._EventHandler = pastedEventHandler;
EventHandler[] eventHandlers = flowchart.GetComponents<EventHandler>();
EventHandler pastedEventHandler = eventHandlers.Last<EventHandler>();
if (pastedEventHandler != null)
{
pastedEventHandler.ParentBlock = newBlock;
newBlock._EventHandler = pastedEventHandler;
}
}
}
}
@ -880,6 +1024,16 @@ namespace Fungus.EditorUtils
EditorUtility.SetDirty(blockInspector);
}
protected static void SetBlockForInspector(Flowchart flowchart, Block block)
{
ShowBlockInspector(flowchart);
flowchart.ClearSelectedCommands();
if (block.ActiveCommand != null)
{
flowchart.AddSelectedCommand(block.ActiveCommand);
}
}
/// <summary>
/// Displays a temporary text alert in the center of the Flowchart window.
/// </summary>
@ -891,5 +1045,10 @@ namespace Fungus.EditorUtils
window.ShowNotification(new GUIContent(notificationText));
}
}
protected virtual bool GetAppendModifierDown()
{
return Event.current.shift || EditorGUI.actionKey;
}
}
}

32
Docs/fungus_docs/coding_standard.md

@ -51,7 +51,7 @@ namespace Fungus
Things to note:
- using declarations all go together at the top of the file.
- You should remove any unused using declarations (can spot these easily with static code analysis - see below).
- Remove any unused using declarations (can spot these easily with static code analysis - see below).
- Runtime code goes in the Fungus namespace.
- Editor code goes in the Fungus.EditorUtils namespace.
- All public classes, structs, enums and class members should be documented using xml comments.
@ -78,4 +78,32 @@ These are some general best practices when writing code for %Fungus. Where these
- Use Mathf.Approximately when comparing float variables to constants.
- Treat compiler warnings as errors. There should be zero warnings at build or runtime in normal operation.
- Add global constants to FungusConstants.cs
- Always try to maintain backwards compatibility when introducing changes. We support Unity 5.0+ so beware of API differences in newer versions.
# Backwards compatibility # {#backwards_compatibility}
We aim to maintain backwards compatibility with each new release (to a reasonable extent).
- Projects should work correctly after upgrading to a newer %Fungus version. Minor behavior changes are acceptable.
- Custom code which uses the %Fungus API should compile without error after upgrading. Minor compile errors that are trivial to fix are sometimes acceptable.
- There are loads of %Fungus tutorial videos and articles on the Internet, so avoid changing the UI too dramatically. Small UI tweaks and adding new controls is acceptable.
- We support Unity 5.0+ so beware of API differences in newer versions. If in doubt, install Unity 5.0 and test your changes.
# Contributing # {#contributing}
We welcome pull requests from everyone. By contributing to this project, you agree to abide by the @ref code_of_conduct. You also agree that by submitting a pull request for this project, your contribution will be licensed under the [Open Source license] for this project.
- Fork and clone the %Fungus repo.
- Make sure the tests pass locally (see the project readme for instructions).
- Make your change. Add tests for your change. Make the tests pass locally.
- Push to your fork and submit a pull request.
Your pull request will have a better chance of being accepted if you do the following:
- Send one pull request for each new feature / bug fix. It's time consuming to review multi-feature changes and we won't merge a change unless we know exactly what it does.
- Write tests for each change / new feature (not always possible)
- Follow our coding standard (see above)
- Write a [good commit message][commit].
[commit]: http://chris.beams.io/posts/git-commit/
[fork a repo]: https://help.github.com/articles/fork-a-repo/
[Open Source license]: https://github.com/snozbot/Fungus/blob/master/LICENSE

Loading…
Cancel
Save