514 lines
20 KiB
C++
514 lines
20 KiB
C++
//
|
|
// File: foldhaus_node_interface.cpp
|
|
// Author: Peter Slattery
|
|
// Creation Date: 2020-01-01
|
|
//
|
|
#ifndef FOLHAUS_NODE_INTERFACE_CPP
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node Lister
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
struct node_lister_operation_state
|
|
{
|
|
search_lister SearchLister;
|
|
v2 ListPosition;
|
|
};
|
|
|
|
internal void
|
|
RenderNodeLister(panel Panel, rect PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
|
{
|
|
node_lister_operation_state* OpState = (node_lister_operation_state*)Operation.OpStateMemory;
|
|
|
|
v2 TopLeft = OpState->ListPosition;
|
|
v2 Dimension = v2{300, 30};
|
|
|
|
// Filter the lister
|
|
OpState->SearchLister.Filter = State->ActiveTextEntry.Buffer;
|
|
FilterSearchLister(&OpState->SearchLister);
|
|
|
|
// Display Search Lister
|
|
search_lister_result NodeListerResult = EvaluateSearchLister (&State->Interface_, TopLeft, Dimension,
|
|
MakeStringLiteral("Nodes List"),
|
|
OpState->SearchLister.SourceList,
|
|
OpState->SearchLister.FilteredIndexLUT,
|
|
OpState->SearchLister.FilteredListCount,
|
|
OpState->SearchLister.HotItem,
|
|
&State->ActiveTextEntry.Buffer,
|
|
State->ActiveTextEntry.CursorPosition);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerNextItem)
|
|
{
|
|
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
|
|
OpState->SearchLister.HotItem = GetNextFilteredItem(OpState->SearchLister);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(NodeListerPrevItem)
|
|
{
|
|
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
|
|
OpState->SearchLister.HotItem = GetPrevFilteredItem(OpState->SearchLister);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeLister)
|
|
{
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(SelectAndCloseNodeLister)
|
|
{
|
|
node_lister_operation_state* OpState = GetCurrentOperationState(State->Modes, node_lister_operation_state);
|
|
s32 FilteredNodeIndex = OpState->SearchLister.HotItem;
|
|
if (FilteredNodeIndex >= 0)
|
|
{
|
|
s32 NodeIndex = OpState->SearchLister.FilteredIndexLUT[FilteredNodeIndex];
|
|
PushNodeOnListFromSpecification(State->NodeList, (node_type)NodeIndex,
|
|
Mouse.Pos, State->Permanent);
|
|
}
|
|
CloseNodeLister(State, Event, Mouse);
|
|
}
|
|
|
|
input_command UniverseViewCommads [] = {
|
|
{ KeyCode_DownArrow, KeyCode_Invalid, Command_Began, NodeListerNextItem },
|
|
{ KeyCode_UpArrow, KeyCode_Invalid, Command_Began, NodeListerPrevItem },
|
|
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, SelectAndCloseNodeLister },
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, CloseNodeLister },
|
|
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseNodeLister },
|
|
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
|
|
};
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeLister)
|
|
{
|
|
operation_mode* AddNodeOperation = ActivateOperationModeWithCommands(&State->Modes, UniverseViewCommads);
|
|
|
|
AddNodeOperation->Render = RenderNodeLister;
|
|
|
|
node_lister_operation_state* OpState = CreateOperationState(AddNodeOperation,
|
|
&State->Modes,
|
|
node_lister_operation_state);
|
|
{
|
|
OpState->SearchLister.SourceListCount = NodeSpecificationsCount;
|
|
OpState->SearchLister.SourceList = PushArray(&State->Modes.Arena, string, OpState->SearchLister.SourceListCount);
|
|
{
|
|
for (s32 i = 0; i < OpState->SearchLister.SourceListCount; i++)
|
|
{
|
|
OpState->SearchLister.SourceList[i] = MakeString(
|
|
NodeSpecifications[i].Name,
|
|
NodeSpecifications[i].NameLength);
|
|
}
|
|
}
|
|
OpState->SearchLister.Filter = MakeString(PushArray(&State->Modes.Arena, char, 64), 0, 64);
|
|
|
|
OpState->SearchLister.FilteredListMax = OpState->SearchLister.SourceListCount;
|
|
OpState->SearchLister.FilteredListCount = 0;
|
|
OpState->SearchLister.FilteredIndexLUT = PushArray(&State->Modes.Arena, s32, OpState->SearchLister.SourceListCount);
|
|
}
|
|
|
|
OpState->ListPosition = Mouse.Pos;
|
|
SetTextInputDestinationToString(&State->ActiveTextEntry, &OpState->SearchLister.Filter);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node Color Picker
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
struct color_picker_operation_state
|
|
{
|
|
v4* ValueAddr;
|
|
};
|
|
|
|
internal void
|
|
CloseColorPicker(app_state* State)
|
|
{
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(CloseColorPickerCommand)
|
|
{
|
|
CloseColorPicker(State);
|
|
}
|
|
|
|
internal void
|
|
RenderColorPicker(panel Panel, rect PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
|
{
|
|
color_picker_operation_state* OpState = (color_picker_operation_state*)Operation.OpStateMemory;
|
|
|
|
|
|
b32 ShouldClose = EvaluateColorPicker(RenderBuffer, OpState->ValueAddr,
|
|
v2{200, 200}, State->Interface, Mouse);
|
|
|
|
if (ShouldClose)
|
|
{
|
|
CloseColorPicker(State);
|
|
}
|
|
}
|
|
|
|
input_command ColorPickerCommands [] = {
|
|
{ KeyCode_Esc, KeyCode_Invalid, Command_Began, CloseColorPickerCommand },
|
|
};
|
|
|
|
internal void
|
|
OpenColorPicker(app_state* State, node_connection* Connection)
|
|
{
|
|
operation_mode* ColorPickerMode = ActivateOperationModeWithCommands(&State->Modes, ColorPickerCommands);
|
|
ColorPickerMode->Render = RenderColorPicker;
|
|
|
|
color_picker_operation_state* OpState = CreateOperationState(ColorPickerMode,
|
|
&State->Modes,
|
|
color_picker_operation_state);
|
|
OpState->ValueAddr = Connection->V4ValuePtr;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node Field Text Edit
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(EndNodeFieldTextEdit)
|
|
{
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
}
|
|
|
|
input_command NodeFieldTextEditCommands [] = {
|
|
{ KeyCode_Enter, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, EndNodeFieldTextEdit },
|
|
DEFAULT_TEXT_ENTRY_INPUT_COMMANDS_ARRAY_ENTRY,
|
|
};
|
|
|
|
internal void
|
|
BeginNodeFieldTextEdit(app_state* State, node_connection* Connection)
|
|
{
|
|
operation_mode* NodeFieldTextEditMode = ActivateOperationModeWithCommands(&State->Modes,
|
|
NodeFieldTextEditCommands);
|
|
|
|
SetTextInputDestinationToFloat(&State->ActiveTextEntry, Connection->R32ValuePtr);
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node Port Mouse Drag
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
struct drag_node_port_operation_state
|
|
{
|
|
node_interaction Interaction;
|
|
};
|
|
|
|
internal void
|
|
RenderDraggingNodePort(panel Panel, rect PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
|
{
|
|
drag_node_port_operation_state* OpState = (drag_node_port_operation_state*)Operation.OpStateMemory;
|
|
UpdateDraggingNodePort(Mouse.Pos, OpState->Interaction, State->NodeList,
|
|
State->NodeRenderSettings, RenderBuffer);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNodePort)
|
|
{
|
|
drag_node_port_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_port_operation_state);
|
|
|
|
TryConnectNodes(OpState->Interaction, Mouse.Pos, State->NodeList, State->NodeRenderSettings);
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
}
|
|
|
|
input_command DragNodePortInputCommands[] = {
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNodePort },
|
|
};
|
|
|
|
internal void
|
|
BeginDraggingNodePort(app_state* State, node_interaction Interaction)
|
|
{
|
|
operation_mode* DragNodePortMode = ActivateOperationModeWithCommands(
|
|
&State->Modes,
|
|
DragNodePortInputCommands);
|
|
DragNodePortMode->Render = RenderDraggingNodePort;
|
|
|
|
drag_node_port_operation_state* OpState = CreateOperationState(DragNodePortMode,
|
|
&State->Modes,
|
|
drag_node_port_operation_state);
|
|
OpState->Interaction = Interaction;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node Field Mouse Drag
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
internal void
|
|
RenderDragNodeField(panel Panel, rect PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
|
{
|
|
// TODO(Peter):
|
|
//UpdateDraggingNodeValue(Mouse.Pos, Mouse.OldPos, OpState->Interaction, State->NodeList, State->NodeRenderSettings, State);
|
|
}
|
|
|
|
internal void
|
|
BeginInteractWithNodeField(app_state* State, node_interaction Interaction)
|
|
{
|
|
// TODO(Peter):
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node Mouse Drag
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
struct drag_node_operation_state
|
|
{
|
|
node_interaction Interaction;
|
|
};
|
|
|
|
internal void
|
|
RenderDraggingNode(panel Panel, rect PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
|
{
|
|
drag_node_operation_state* OpState = GetCurrentOperationState(State->Modes, drag_node_operation_state);
|
|
UpdateDraggingNode(Mouse.Pos, OpState->Interaction, State->NodeList,
|
|
State->NodeRenderSettings);
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(EndDraggingNode)
|
|
{
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
}
|
|
|
|
input_command DragNodeInputCommands[] = {
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, EndDraggingNode },
|
|
};
|
|
|
|
internal void
|
|
BeginDraggingNode(app_state* State, node_interaction Interaction)
|
|
{
|
|
operation_mode* DragNodeMode = ActivateOperationModeWithCommands(
|
|
&State->Modes,
|
|
DragNodeInputCommands);
|
|
DragNodeMode->Render = RenderDraggingNode;
|
|
|
|
drag_node_operation_state* OpState = CreateOperationState(DragNodeMode,
|
|
&State->Modes,
|
|
drag_node_operation_state);
|
|
OpState->Interaction = Interaction;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// Node View
|
|
//
|
|
///////////////////////////////////////
|
|
|
|
struct node_view_operation_state
|
|
{
|
|
s32 SelectedNodeHandle;
|
|
};
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseDragInteraction)
|
|
{
|
|
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
|
|
|
|
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.DownPos, State->NodeRenderSettings);
|
|
if (Node)
|
|
{
|
|
node_interaction NewInteraction = GetNodeInteractionType(Node,
|
|
Mouse.Pos,
|
|
State->NodeRenderSettings);
|
|
if (IsDraggingNodePort(NewInteraction))
|
|
{
|
|
BeginDraggingNodePort(State, NewInteraction);
|
|
}
|
|
else if(IsDraggingNodeValue(NewInteraction))
|
|
{
|
|
// TODO(Peter): This probably wants to live in a mouse held action
|
|
// the first frame we realize we're held over a field, just transition to
|
|
// drag node field
|
|
//BeginInteractWithNodeField(State, NewInteraction, State->NodeRenderSettings);
|
|
}
|
|
else // IsDraggingNode
|
|
{
|
|
OpState->SelectedNodeHandle = Node->Handle;
|
|
BeginDraggingNode(State, NewInteraction);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OpState->SelectedNodeHandle = 0;
|
|
}
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewBeginMouseSelectInteraction)
|
|
{
|
|
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
|
|
|
|
node_header* Node = GetNodeUnderPoint(State->NodeList, Mouse.Pos, State->NodeRenderSettings);
|
|
if (Node)
|
|
{
|
|
node_interaction NewInteraction = GetNodeInteractionType(Node,
|
|
Mouse.Pos,
|
|
State->NodeRenderSettings);
|
|
if(IsDraggingNodeValue(NewInteraction))
|
|
{
|
|
node_connection* Connection = Node->Connections + NewInteraction.InputValue;
|
|
struct_member_type InputType = Connection->Type;
|
|
|
|
if (InputType == MemberType_r32)
|
|
{
|
|
BeginNodeFieldTextEdit(State, Connection);
|
|
}
|
|
else if (InputType == MemberType_v4)
|
|
{
|
|
OpenColorPicker(State, Connection);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void
|
|
RenderNodeView(panel Panel, rect PanelBounds, render_command_buffer* RenderBufer, app_state* State, context Context, mouse_state Mouse)
|
|
{
|
|
node_view_operation_state* OpState = (node_view_operation_state*)Operation.OpStateMemory;
|
|
|
|
DEBUG_TRACK_FUNCTION;
|
|
|
|
MakeStringBuffer(NodeHeaderBuffer, 128);
|
|
|
|
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
|
|
|
|
node_list_iterator NodeIter = GetNodeListIterator(*State->NodeList);
|
|
while (NodeIteratorIsValid(NodeIter))
|
|
{
|
|
node_header* Node = NodeIter.At;
|
|
|
|
rect NodeBounds = CalculateNodeBounds(Node, State->NodeRenderSettings);
|
|
b32 DrawFields = PointIsInRect(Mouse.Pos, NodeBounds);
|
|
|
|
if (Node == SelectedNode)
|
|
{
|
|
PushRenderQuad2D(RenderBuffer, NodeBounds.Min - v2{2, 2}, NodeBounds.Max + v2{2, 2}, WhiteV4);
|
|
}
|
|
|
|
PushRenderQuad2D(RenderBuffer, NodeBounds.Min, NodeBounds.Max, v4{.5f, .5f, .5f, 1.f});
|
|
|
|
// TODO(Peter): This is just for debug purposes. We can remove and go back to just having
|
|
// Node->Name in DrawString
|
|
string NodeName = GetNodeName(*Node);
|
|
PrintF(&NodeHeaderBuffer, "%.*s: %d", NodeName.Length, NodeName.Memory, Node->Handle);
|
|
DrawString(RenderBuffer, NodeHeaderBuffer, State->NodeRenderSettings.Font,
|
|
v2{NodeBounds.Min.x + 5, NodeBounds.Max.y - (State->NodeRenderSettings.Font->PixelHeight + NODE_HEADER_HEIGHT + 5)},
|
|
WhiteV4);
|
|
|
|
for (s32 Connection = 0; Connection < Node->ConnectionsCount; Connection++)
|
|
{
|
|
v4 PortColor = State->NodeRenderSettings.PortColors[Node->Connections[Connection].Type];
|
|
|
|
// Inputs
|
|
if (ConnectionIsInput(Node, Connection))
|
|
{
|
|
rect PortBounds = CalculateNodeInputPortBounds(Node, Connection, State->NodeRenderSettings);
|
|
DrawPort(RenderBuffer, PortBounds, PortColor);
|
|
|
|
//
|
|
// TODO(Peter): I don't like excluding OutputNode, feels too much like a special case
|
|
// but I don't want to get in to the meta programming right now.
|
|
// We should just generate a spec and struct member types for NodeType_OutputNode
|
|
//
|
|
// :ExcludingOutputNodeSpecialCase
|
|
//
|
|
if (Node->Type != NodeType_OutputNode && DrawFields)
|
|
{
|
|
node_specification Spec = NodeSpecifications[Node->Type];
|
|
node_struct_member Member = Spec.MemberList[Connection];
|
|
DrawString(RenderBuffer, MakeString(Member.Name),
|
|
State->NodeRenderSettings.Font,
|
|
v2{PortBounds.Min.x - 8, PortBounds.Min.y}, WhiteV4, Align_Right);
|
|
}
|
|
|
|
rect ValueBounds = CalculateNodeInputValueBounds(Node, Connection, State->NodeRenderSettings);
|
|
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
|
|
|
|
// NOTE(Peter): its way easier to draw the connection on the input port b/c its a 1:1 relationship,
|
|
// whereas output ports might have many connections, they really only know about the most recent one
|
|
// Not sure if this is a problem. We mostly do everything backwards here, starting at the
|
|
// most downstream node and working back up to find dependencies.
|
|
if (ConnectionHasUpstreamConnection(Node, Connection))
|
|
{
|
|
rect ConnectedPortBounds = GetBoundsOfPortConnectedToInput(Node, Connection, State->NodeList, State->NodeRenderSettings);
|
|
v2 InputCenter = CalculateRectCenter(PortBounds);
|
|
v2 OutputCenter = CalculateRectCenter(ConnectedPortBounds);
|
|
PushRenderLine2D(RenderBuffer, OutputCenter, InputCenter, 1, WhiteV4);
|
|
}
|
|
}
|
|
|
|
// Outputs
|
|
if (ConnectionIsOutput(Node, Connection))
|
|
{
|
|
rect PortBounds = CalculateNodeOutputPortBounds(Node, Connection, State->NodeRenderSettings);
|
|
DrawPort(RenderBuffer, PortBounds, PortColor);
|
|
|
|
if (DrawFields)
|
|
{
|
|
node_specification Spec = NodeSpecifications[Node->Type];
|
|
node_struct_member Member = Spec.MemberList[Connection];
|
|
DrawString(RenderBuffer, MakeString(Member.Name),
|
|
State->NodeRenderSettings.Font,
|
|
v2{PortBounds.Max.x + 8, PortBounds.Min.y}, WhiteV4);
|
|
}
|
|
|
|
rect ValueBounds = CalculateNodeOutputValueBounds(Node, Connection, State->NodeRenderSettings);
|
|
DrawValueDisplay(RenderBuffer, ValueBounds, Node->Connections[Connection], State->NodeRenderSettings.Font);
|
|
}
|
|
|
|
for (s32 Button = 0; Button < 3; Button++)
|
|
{
|
|
rect ButtonRect = CalculateNodeDragHandleBounds(NodeBounds, Button, State->NodeRenderSettings);
|
|
PushRenderQuad2D(RenderBuffer, ButtonRect.Min, ButtonRect.Max, DragButtonColors[Button]);
|
|
}
|
|
}
|
|
|
|
Next(&NodeIter);
|
|
}
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(NodeViewDeleteNode)
|
|
{
|
|
node_view_operation_state* OpState = GetCurrentOperationState(State->Modes, node_view_operation_state);
|
|
if (OpState->SelectedNodeHandle > 0)
|
|
{
|
|
node_header* SelectedNode = GetNodeWithHandle(State->NodeList, OpState->SelectedNodeHandle);
|
|
FreeNodeOnList(State->NodeList, SelectedNode);
|
|
}
|
|
}
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(CloseNodeView)
|
|
{
|
|
DeactivateCurrentOperationMode(&State->Modes);
|
|
}
|
|
|
|
input_command NodeViewCommands [] = {
|
|
{ KeyCode_Tab, KeyCode_Invalid, Command_Began, CloseNodeView},
|
|
{ KeyCode_A, KeyCode_Invalid, Command_Began, OpenNodeLister},
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Began, NodeViewBeginMouseDragInteraction},
|
|
{ KeyCode_MouseLeftButton, KeyCode_Invalid, Command_Ended, NodeViewBeginMouseSelectInteraction},
|
|
{ KeyCode_X, KeyCode_Invalid, Command_Began, NodeViewDeleteNode},
|
|
};
|
|
|
|
FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeView)
|
|
{
|
|
operation_mode* NodeViewMode = ActivateOperationModeWithCommands(&State->Modes, NodeViewCommands);
|
|
NodeViewMode->Render = RenderNodeView;
|
|
|
|
node_view_operation_state* OpState = CreateOperationState(NodeViewMode,
|
|
&State->Modes,
|
|
node_view_operation_state);
|
|
|
|
OpState->SelectedNodeHandle = 0;
|
|
}
|
|
|
|
|
|
#define FOLHAUS_NODE_INTERFACE_CPP
|
|
#endif // FOLHAUS_NODE_INTERFACE_CPP
|