Search lister improvements.

This commit is contained in:
Peter Slattery 2019-09-21 13:19:02 -07:00
parent 594e4257e0
commit eb22ebf1ea
10 changed files with 168 additions and 111 deletions

View File

@ -351,7 +351,13 @@ RELOAD_STATIC_DATA(ReloadStaticData)
RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_A, false, KeyCode_Invalid, OpenNodeLister); RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_A, false, KeyCode_Invalid, OpenNodeLister);
RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_Tab, false, KeyCode_Invalid, ToggleNodeDisplay); RegisterKeyPressCommand(&State->InputCommandRegistry, KeyCode_Tab, false, KeyCode_Invalid, ToggleNodeDisplay);
InitializeTextInputCommands(&State->TextEntryCommandRegistry, State->Permanent); // Node Lister
RegisterKeyPressCommand(&State->NodeListerCommandRegistry, KeyCode_DownArrow, false, KeyCode_Invalid, SearchListerNextItem);
RegisterKeyPressCommand(&State->NodeListerCommandRegistry, KeyCode_UpArrow, false, KeyCode_Invalid, SearchListerPrevItem);
RegisterKeyPressCommand(&State->NodeListerCommandRegistry, KeyCode_Enter, false, KeyCode_Invalid, SelectAndCloseSearchLister);
RegisterKeyPressCommand(&State->NodeListerCommandRegistry, KeyCode_MouseLeftButton, false, KeyCode_Invalid, CloseSearchLister);
RegisterKeyPressCommand(&State->NodeListerCommandRegistry, KeyCode_Esc, false, KeyCode_Invalid, CloseSearchLister);
InitializeTextInputCommands(&State->NodeListerCommandRegistry, State->Permanent);
} }
} }
@ -367,7 +373,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
InitMemoryArena(&State->SACNMemory, 0, 0, Context.PlatformAlloc); InitMemoryArena(&State->SACNMemory, 0, 0, Context.PlatformAlloc);
InitializeInputCommandRegistry(&State->InputCommandRegistry, 32, State->Permanent); InitializeInputCommandRegistry(&State->InputCommandRegistry, 32, State->Permanent);
InitializeInputCommandRegistry(&State->TextEntryCommandRegistry, 32, State->Permanent); InitializeInputCommandRegistry(&State->NodeListerCommandRegistry, 32, State->Permanent);
State->ActiveCommands = &State->InputCommandRegistry; State->ActiveCommands = &State->InputCommandRegistry;
State->ActiveTextEntry.Buffer = MakeString(PushArray(State->Permanent, char, 256), 0, 256); State->ActiveTextEntry.Buffer = MakeString(PushArray(State->Permanent, char, 256), 0, 256);
@ -447,15 +453,14 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->Camera.Far = 100.0f; State->Camera.Far = 100.0f;
State->Camera.Position = v3{0, 0, -250}; State->Camera.Position = v3{0, 0, -250};
State->Camera.LookAt = v3{0, 0, 0}; State->Camera.LookAt = v3{0, 0, 0};
State->Camera_StartDragPos = V4(State->Camera.Position, 1);
#if 1 #if 1
char Path[] = "radialumia.fold"; char Path[] = "radialumia.fold";
LoadAssembly(State, Context, Path); LoadAssembly(State, Context, Path);
#endif #endif
State->InterfaceYMax = 200;
State->PixelsToWorldScale = .01f; State->PixelsToWorldScale = .01f;
State->Camera_StartDragPos = {};
State->UniverseOutputDisplayOffset = v2{0, 0}; State->UniverseOutputDisplayOffset = v2{0, 0};
State->UniverseOutputDisplayZoom = 1.0f; State->UniverseOutputDisplayZoom = 1.0f;
@ -489,7 +494,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
// incorrect to clear the arena, and then access the memory later. // incorrect to clear the arena, and then access the memory later.
ClearArena(State->Transient); ClearArena(State->Transient);
if (State->ActiveCommands == &State->TextEntryCommandRegistry) if (State->ActiveCommands == &State->NodeListerCommandRegistry)
{ {
AppendInputToEntryString(&State->ActiveTextEntry, Input.New->StringInput, Input.New->StringInputUsed); AppendInputToEntryString(&State->ActiveTextEntry, Input.New->StringInput, Input.New->StringInputUsed);
} }
@ -504,7 +509,6 @@ UPDATE_AND_RENDER(UpdateAndRender)
State->LEDBufferList->Count, State->LEDBufferList->Count,
Context.DeltaTime); Context.DeltaTime);
} }
ClearTransientNodeColorBuffers(State->NodeList); ClearTransientNodeColorBuffers(State->NodeList);
{ {
@ -768,7 +772,9 @@ UPDATE_AND_RENDER(UpdateAndRender)
SetTextInputDestinationToFloat(&State->ActiveTextEntry, &Connection->R32Value); SetTextInputDestinationToFloat(&State->ActiveTextEntry, &Connection->R32Value);
} }
State->NodeInteraction = NewEmptyNodeInteraction(); State->NodeInteraction = NewEmptyNodeInteraction();
State->ActiveCommands = &State->TextEntryCommandRegistry;
// TODO(Peter): This is wrong, should be something to do with capturing text input
State->ActiveCommands = &State->NodeListerCommandRegistry;
} }
else // This is the case where you dragged the value else // This is the case where you dragged the value
{ {
@ -796,15 +802,6 @@ UPDATE_AND_RENDER(UpdateAndRender)
if (State->InterfaceShowNodeList) if (State->InterfaceShowNodeList)
{ {
if (KeyTransitionedDown(Input, KeyCode_DownArrow))
{
SearchListerNextItem(State, Input);
}
if (KeyTransitionedDown(Input, KeyCode_UpArrow))
{
SearchListerPrevItem(State, Input);
}
v2 TopLeft = State->NodeListMenuPosition; v2 TopLeft = State->NodeListMenuPosition;
v2 Dimension = v2{300, 30}; v2 Dimension = v2{300, 30};
@ -817,23 +814,6 @@ UPDATE_AND_RENDER(UpdateAndRender)
State->ActiveTextEntry.CursorPosition, State->ActiveTextEntry.CursorPosition,
State->Font, State->Interface, Input); State->Font, State->Interface, Input);
State->GeneralPurposeSearchHotItem = NodeListResult.HotItem; State->GeneralPurposeSearchHotItem = NodeListResult.HotItem;
if (KeyTransitionedDown(Input, KeyCode_Enter))
{
NodeListResult.SelectedItem = NodeListResult.HotItem;
}
if (NodeListResult.SelectedItem >= 0)
{
PushNodeOnListFromSpecification(State->NodeList, NodeSpecifications[NodeListResult.SelectedItem],
MousePos, State->NodeRenderSettings, State->Permanent);
CloseSearchLister(State, Input);
}
else if (KeyTransitionedDown(Input, KeyCode_MouseLeftButton) ||
KeyTransitionedDown(Input, KeyCode_Esc))
{
CloseSearchLister(State, Input);
}
} }
} }
@ -851,7 +831,7 @@ UPDATE_AND_RENDER(UpdateAndRender)
DrawDebugInterface(RenderBuffer, 25, DrawDebugInterface(RenderBuffer, 25,
State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight, State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight,
Context.DeltaTime, State->Camera, Input, State->Transient); Context.DeltaTime, State, State->Camera, Input, State->Transient);
} }
EndDebugFrame(GlobalDebugServices); EndDebugFrame(GlobalDebugServices);

View File

@ -48,7 +48,7 @@ struct assembly
typedef struct app_state app_state; typedef struct app_state app_state;
#include "foldhaus_debug_visuals.h"
#include "foldhaus_command_dispatch.h" #include "foldhaus_command_dispatch.h"
@ -56,6 +56,7 @@ typedef struct app_state app_state;
#include "foldhaus_default_nodes.h" #include "foldhaus_default_nodes.h"
#include "generated/foldhaus_nodes_generated.cpp" #include "generated/foldhaus_nodes_generated.cpp"
#include "foldhaus_search_lister.h"
struct app_state struct app_state
{ {
@ -66,9 +67,9 @@ struct app_state
camera Camera; camera Camera;
input_command_registry InputCommandRegistry; input_command_registry InputCommandRegistry;
input_command_registry TextEntryCommandRegistry; input_command_registry NodeListerCommandRegistry;
input_command_registry* ActiveCommands; input_command_registry* ActiveCommands;
text_input ActiveTextEntry; text_entry ActiveTextEntry;
streaming_acn SACN; streaming_acn SACN;
s32 TotalLEDsCount; s32 TotalLEDsCount;
@ -84,7 +85,6 @@ struct app_state
bitmap_font* Font; bitmap_font* Font;
interface_config Interface; interface_config Interface;
r32 InterfaceYMax;
r32 PixelsToWorldScale; r32 PixelsToWorldScale;
v4 Camera_StartDragPos; v4 Camera_StartDragPos;
@ -107,9 +107,10 @@ struct app_state
}; };
#include "foldhaus_search_lister.h" #include "foldhaus_debug_visuals.h"
#include "foldhaus_sacn_view.cpp" #include "foldhaus_sacn_view.cpp"
#include "foldhaus_command_dispatch.cpp" #include "foldhaus_command_dispatch.cpp"
#include "foldhaus_node.cpp" #include "foldhaus_node.cpp"
#include "foldhaus_text_entry.cpp" #include "foldhaus_text_entry.cpp"
#include "foldhaus_interface.cpp" #include "foldhaus_interface.cpp"
#include "foldhaus_search_lister.cpp"

View File

@ -1,5 +1,5 @@
internal void internal void
DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_config Interface, r32 WindowWidth, r32 WindowHeight, r32 DeltaTime, camera Camera, input Input, memory_arena* Transient) DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_config Interface, r32 WindowWidth, r32 WindowHeight, r32 DeltaTime, app_state* State, camera Camera, input Input, memory_arena* Transient)
{ {
DEBUG_TRACK_SCOPE(DrawDebugInterface); DEBUG_TRACK_SCOPE(DrawDebugInterface);
@ -19,9 +19,22 @@ DrawDebugInterface (render_command_buffer* RenderBuffer, r32 StartX, interface_c
} }
r32 FramesPerSecond = 1.0f / DeltaTime; r32 FramesPerSecond = 1.0f / DeltaTime;
PrintF(&DebugString, "Framerate: %.*f s %d fps",
string InputCommands = MakeStringLiteral("Default Input Registry - HI KALAN");
string NodeListerCommands = MakeStringLiteral("Node Lister Input Registry - HI KALAN");
string ActiveInputRegistry = {};
if (State->ActiveCommands == &State->InputCommandRegistry)
{
ActiveInputRegistry = InputCommands;
}
else if (State->ActiveCommands == &State->NodeListerCommandRegistry)
{
ActiveInputRegistry = NodeListerCommands;
}
PrintF(&DebugString, "Framerate: %.*f s %d fps | Input: %s",
5, DeltaTime, 5, DeltaTime,
(u32)FramesPerSecond); (u32)FramesPerSecond,
ActiveInputRegistry.Memory);
DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize, TopOfScreenLinePos, WhiteV4); DrawString(RenderBuffer, DebugString, Interface.Font, Interface.FontSize, TopOfScreenLinePos, WhiteV4);
v2 ButtonDim = v2{200, (r32)NewLineYOffset(*Interface.Font) + 10}; v2 ButtonDim = v2{200, (r32)NewLineYOffset(*Interface.Font) + 10};

View File

@ -7,7 +7,11 @@ FOLDHAUS_INPUT_COMMAND_PROC(CameraMouseControl)
State->Camera_StartDragPos = V4(State->Camera.Position, 1); State->Camera_StartDragPos = V4(State->Camera.Position, 1);
} }
if (Input.MouseDownY > State->InterfaceYMax) if (KeyTransitionedUp(Input, KeyCode_MouseLeftButton))
{
State->Camera_StartDragPos = V4(State->Camera.Position, 1);
}
else
{ {
if (!State->DrawUniverseOutputDisplay) if (!State->DrawUniverseOutputDisplay)
{ {
@ -55,7 +59,7 @@ FOLDHAUS_INPUT_COMMAND_PROC(OpenNodeLister)
State->InterfaceShowNodeList = true; State->InterfaceShowNodeList = true;
State->NodeListMenuPosition = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY}; State->NodeListMenuPosition = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY};
SetTextInputDestinationToString(&State->ActiveTextEntry, &State->GeneralPurposeSearchString); SetTextInputDestinationToString(&State->ActiveTextEntry, &State->GeneralPurposeSearchString);
State->ActiveCommands = &State->TextEntryCommandRegistry; State->ActiveCommands = &State->NodeListerCommandRegistry;
} }
FOLDHAUS_INPUT_COMMAND_PROC(ToggleNodeDisplay) FOLDHAUS_INPUT_COMMAND_PROC(ToggleNodeDisplay)

View File

@ -0,0 +1,74 @@
internal string*
GetStringAtIndex (search_lister Lister, s32 Index)
{
string* Result = 0;
Assert(Index >= 0 && Index < Lister.ListLength);
u8* Element = Lister.ListMemory + (Lister.ElementStride * Index);
Result = (string*)(Element + Lister.OffsetToStringInElement);
return Result;
}
#define InitializeSearchLister(SearchLister, List, ListLength, StringFieldName) \
s32 ElementStride = (u8*)(List + 1) - (u8*)(List); \
s32 OffsetToString = (u8*)(List->#StringFieldName) - (u8*)(List); \
InitializeSearchLister_((u8*)List, ListLength, ElementStride, OffsetToString); \
internal void
InitializeSearchLister_(search_lister* Lister,
u8* ListMemory,
s32 ListLength,
s32 ElementStride,
s32 OffsetToString)
{
Lister->ListLength = ListLength;
Lister->ListMemory = ListMemory;
Lister->OffsetToStringInElement = OffsetToString;
Lister->ElementStride = ElementStride;
Lister->Filter.Length = 0;
}
internal s32
GetNextHotItemIndex (s32 CurrentHotItem, s32 ListLength)
{
s32 Result = GSMin(CurrentHotItem + 1, ListLength - 1);
return Result;
}
internal s32
GetPrevHotItemIndex (s32 CurrentHotItem)
{
s32 Result = GSMax(0, CurrentHotItem - 1);
return Result;
}
FOLDHAUS_INPUT_COMMAND_PROC(SearchListerNextItem)
{
State->GeneralPurposeSearchHotItem = GetNextHotItemIndex(State->GeneralPurposeSearchHotItem, NodeSpecificationsCount);
}
FOLDHAUS_INPUT_COMMAND_PROC(SearchListerPrevItem)
{
State->GeneralPurposeSearchHotItem = GetPrevHotItemIndex(State->GeneralPurposeSearchHotItem);
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseSearchLister)
{
// TODO(Peter): This is to show the node list. Generalize to just a lister
State->InterfaceShowNodeList = false;
// TODO(Peter): This also assumes we know where we came from. Probably need to implement
// push/pop functionality for the activecommands.
State->ActiveCommands = &State->InputCommandRegistry;
State->GeneralPurposeSearchHotItem = -1;
}
FOLDHAUS_INPUT_COMMAND_PROC(SelectAndCloseSearchLister)
{
s32 HotItem = State->GeneralPurposeSearchHotItem;
v2 MousePos = v2{(r32)Input.New->MouseX, (r32)Input.New->MouseY};
PushNodeOnListFromSpecification(State->NodeList, NodeSpecifications[HotItem],
MousePos, State->NodeRenderSettings, State->Permanent);
CloseSearchLister(State, Input);
}

View File

@ -1,33 +1,9 @@
internal s32 struct search_lister
GetNextHotItem (s32 CurrentHotItem, s32 ListLength)
{ {
s32 Result = GSMin(CurrentHotItem + 1, ListLength - 1); s32 ListLength;
return Result; u8* ListMemory;
} s32 OffsetToStringInElement;
s32 ElementStride;
internal s32
GetPrevHotItem (s32 CurrentHotItem) string Filter;
{ };
s32 Result = GSMax(0, CurrentHotItem - 1);
return Result;
}
FOLDHAUS_INPUT_COMMAND_PROC(SearchListerNextItem)
{
State->GeneralPurposeSearchHotItem = GetNextHotItem(State->GeneralPurposeSearchHotItem, NodeSpecificationsCount);
}
FOLDHAUS_INPUT_COMMAND_PROC(SearchListerPrevItem)
{
State->GeneralPurposeSearchHotItem = GetPrevHotItem(State->GeneralPurposeSearchHotItem);
}
FOLDHAUS_INPUT_COMMAND_PROC(CloseSearchLister)
{
// TODO(Peter): This is to show the node list. Generalize to just a lister
State->InterfaceShowNodeList = false;
// TODO(Peter): This also assumes we know where we came from. Probably need to implement
// push/pop functionality for the activecommands.
State->ActiveCommands = &State->InputCommandRegistry;
State->GeneralPurposeSearchHotItem = -1;
}

View File

@ -1,12 +1,12 @@
internal void internal void
ResetTextInput (text_input* Input) ResetTextInput (text_entry* Input)
{ {
Input->CursorPosition = 0; Input->CursorPosition = 0;
Input->Buffer.Length = 0; Input->Buffer.Length = 0;
} }
internal void internal void
PipeSearchStringToDestination (text_input* Input) PipeSearchStringToDestination (text_entry* Input)
{ {
switch (Input->Destination.Type) switch (Input->Destination.Type)
{ {
@ -25,18 +25,19 @@ PipeSearchStringToDestination (text_input* Input)
} }
} }
FOLDHAUS_INPUT_COMMAND_PROC(RemoveCharacterFromEntryString) internal void
RemoveCharacterAtCursor (text_entry* TextEntry)
{ {
if (State->ActiveTextEntry.CursorPosition > 0) if (TextEntry->CursorPosition > 0)
{ {
RemoveCharAt(&State->ActiveTextEntry.Buffer, RemoveCharAt(&TextEntry->Buffer,
State->ActiveTextEntry.CursorPosition - 1); TextEntry->CursorPosition - 1);
State->ActiveTextEntry.CursorPosition--; TextEntry->CursorPosition--;
} }
} }
internal void internal void
SetTextInputDestinationToString (text_input* TextInput, string* DestinationString) SetTextInputDestinationToString (text_entry* TextInput, string* DestinationString)
{ {
ResetTextInput(TextInput); ResetTextInput(TextInput);
TextInput->Destination.Type = TextTranslateTo_String; TextInput->Destination.Type = TextTranslateTo_String;
@ -45,7 +46,7 @@ SetTextInputDestinationToString (text_input* TextInput, string* DestinationStrin
} }
internal void internal void
SetTextInputDestinationToFloat (text_input* TextInput, r32* DestinationFloat) SetTextInputDestinationToFloat (text_entry* TextInput, r32* DestinationFloat)
{ {
ResetTextInput(TextInput); ResetTextInput(TextInput);
TextInput->Destination.Type = TextTranslateTo_R32; TextInput->Destination.Type = TextTranslateTo_R32;
@ -59,7 +60,7 @@ SetTextInputDestinationToFloat (text_input* TextInput, r32* DestinationFloat)
} }
internal void internal void
AppendInputToEntryString (text_input* EntryString, char* InputString, s32 InputStringLength) AppendInputToEntryString (text_entry* EntryString, char* InputString, s32 InputStringLength)
{ {
if (InputStringLength > 0) if (InputStringLength > 0)
{ {
@ -72,33 +73,41 @@ AppendInputToEntryString (text_input* EntryString, char* InputString, s32 InputS
PipeSearchStringToDestination(EntryString); PipeSearchStringToDestination(EntryString);
} }
internal void
MoveCursorRight (text_entry* TextEntry)
{
TextEntry->CursorPosition = GSMin(TextEntry->Buffer.Length,
TextEntry->CursorPosition + 1);
}
internal void
MoveCursorLeft (text_entry* TextEntry)
{
TextEntry->CursorPosition = GSMax(0, TextEntry->CursorPosition - 1);
}
FOLDHAUS_INPUT_COMMAND_PROC(RemoveCharacterFromEntryString)
{
RemoveCharacterAtCursor(&State->ActiveTextEntry);
}
FOLDHAUS_INPUT_COMMAND_PROC(TextEntryMoveCursorRight) FOLDHAUS_INPUT_COMMAND_PROC(TextEntryMoveCursorRight)
{ {
State->ActiveTextEntry.CursorPosition = GSMin(State->ActiveTextEntry.Buffer.Length, MoveCursorRight(&State->ActiveTextEntry);
State->ActiveTextEntry.CursorPosition + 1);
} }
FOLDHAUS_INPUT_COMMAND_PROC(TextEntryMoveCursorLeft) FOLDHAUS_INPUT_COMMAND_PROC(TextEntryMoveCursorLeft)
{ {
State->ActiveTextEntry.CursorPosition = GSMax(0, MoveCursorLeft(&State->ActiveTextEntry);
State->ActiveTextEntry.CursorPosition - 1);
}
FOLDHAUS_INPUT_COMMAND_PROC(LeaveTextEntryMode)
{
// TODO(Peter): Make this more flexible. Should return to whatever came before
State->ActiveCommands = &State->InputCommandRegistry;
} }
internal void internal void
InitializeTextInputCommands (input_command_registry* SearchCommands, memory_arena* PermanentStorage) InitializeTextInputCommands (input_command_registry* Commands, memory_arena* PermanentStorage)
{ {
if (SearchCommands->Size > 0) if (Commands->Size > 0)
{ {
RegisterKeyPressCommand(SearchCommands, KeyCode_Backspace, false, KeyCode_Invalid, RemoveCharacterFromEntryString); RegisterKeyPressCommand(Commands, KeyCode_Backspace, false, KeyCode_Invalid, RemoveCharacterFromEntryString);
RegisterKeyPressCommand(SearchCommands, KeyCode_LeftArrow, false, KeyCode_Invalid, TextEntryMoveCursorLeft); RegisterKeyPressCommand(Commands, KeyCode_LeftArrow, false, KeyCode_Invalid, TextEntryMoveCursorLeft);
RegisterKeyPressCommand(SearchCommands, KeyCode_RightArrow, false, KeyCode_Invalid, TextEntryMoveCursorRight); RegisterKeyPressCommand(Commands, KeyCode_RightArrow, false, KeyCode_Invalid, TextEntryMoveCursorRight);
RegisterKeyPressCommand(SearchCommands, KeyCode_Enter, false, KeyCode_Invalid,
LeaveTextEntryMode);
} }
} }

View File

@ -6,7 +6,7 @@ enum text_translation_type
TextTranslateTo_U32, TextTranslateTo_U32,
}; };
struct text_input_destination struct text_entry_destination
{ {
text_translation_type Type; text_translation_type Type;
union { union {
@ -17,10 +17,10 @@ struct text_input_destination
}; };
}; };
struct text_input struct text_entry
{ {
string Buffer; string Buffer;
s32 CursorPosition; s32 CursorPosition;
text_input_destination Destination; text_entry_destination Destination;
}; };

View File

@ -579,7 +579,6 @@ EvaluateSearchLister (render_command_buffer* RenderBuffer, v2 TopLeft, v2 Dimens
search_lister_result Result = {}; search_lister_result Result = {};
Result.ShouldRemainOpen = true; Result.ShouldRemainOpen = true;
Result.HotItem = HotItem; Result.HotItem = HotItem;
Result.SelectedItem = -1;
// Title Bar // Title Bar
PushRenderQuad2D(RenderBuffer, v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y}, v4{.3f, .3f, .3f, 1.f}); PushRenderQuad2D(RenderBuffer, v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y}, v4{.3f, .3f, .3f, 1.f});

View File

@ -1,13 +1,14 @@
TODO FOLDHAUS TODO FOLDHAUS
Hardening Hardening
- more robust windows input layer
- memory visualization
- separate rendering thread
- cache led positions. Only update if they are moving
- input context changes - input context changes
- x don't type into the search bar when its not open
- x don't type into the search bar when you press a to open it
- x type into text boxes
- - shift drag to 10x drag speed - - shift drag to 10x drag speed
- select nodes -> delete nodes - select nodes -> delete nodes
- open add node menu, click off to the side, it still adds a node. - remove node connections
Name Name
- Splash screen (like blender) (thisll be fun) - Splash screen (like blender) (thisll be fun)