1255 lines
44 KiB
C
1255 lines
44 KiB
C
|
#include "language_layer.h"
|
||
|
#include "app_memory.h"
|
||
|
#include "os.h"
|
||
|
#include "opengl.h"
|
||
|
#include "render.h"
|
||
|
#include "string_hash.h"
|
||
|
#include "ui.h"
|
||
|
#include "compute.h"
|
||
|
#include "token_buffer.h"
|
||
|
#include "definition.h"
|
||
|
#include "app.h"
|
||
|
#include "operations.h"
|
||
|
|
||
|
#include "language_layer.c"
|
||
|
#include "app_memory.c"
|
||
|
#include "os.c"
|
||
|
|
||
|
#include "app_core.c"
|
||
|
#include "string_hash.c"
|
||
|
#include "render.c"
|
||
|
#include "ui.c"
|
||
|
#include "compute.c"
|
||
|
#include "token_buffer.c"
|
||
|
#include "definition.c"
|
||
|
#include "operations.c"
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
internal b32
|
||
|
APP_FuzzyMatch(String8 name, String8_List *filter_pattern){
|
||
|
b32 result = 1;
|
||
|
for (String8_Node *node = filter_pattern->first;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
if (StringFindSubstringStart(name, node->string, StringMatchFlag_CaseInsensitive) == ~(u64)0){
|
||
|
result = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
APP_TopLevelNavigate(Dimension dimension, Side side){
|
||
|
if (dimension == Dimension_Y){
|
||
|
E_EditorState *new_editor = vars->neighbor_editors[side];
|
||
|
if (new_editor != 0){
|
||
|
f32 x = vars->active_editor?vars->active_editor->preferred_x:0;
|
||
|
E_EditorEnterFrom(new_editor, Dimension_Y, side^1, x);
|
||
|
vars->active_editor = new_editor;
|
||
|
MemoryZeroArray(vars->neighbor_editors);
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
NotImplemented;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
APP_ApplyViewChange(E_View *view){
|
||
|
if (vars->change_view != 0){
|
||
|
E_ViewDeriveFromSource(view);
|
||
|
vars->active_view = view;
|
||
|
}
|
||
|
vars->change_view = 0;
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
APP_SpaceHandleDeleteSignal(E_Space *space){
|
||
|
space->delete_signal = 0;
|
||
|
for (E_Definition *node = space->first_definition_ordered, *next = 0;
|
||
|
node != 0;
|
||
|
node = next){
|
||
|
next = node->ordered_next;
|
||
|
if (node->delete_me){
|
||
|
_E_DeleteDefinition(node);
|
||
|
space->dirty = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void
|
||
|
APP_CloseSpace(E_Space *space){
|
||
|
b32 deleted_active_view = 0;
|
||
|
E_View *active_view = vars->active_view;
|
||
|
for (E_View *view = vars->first_view, *next = 0;
|
||
|
view != 0;
|
||
|
view = next){
|
||
|
next = view->next;
|
||
|
if (view->space == space){
|
||
|
if (view == active_view){
|
||
|
deleted_active_view = 1;
|
||
|
}
|
||
|
E_DeleteView(view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
E_DeleteSpace(space);
|
||
|
|
||
|
if (deleted_active_view){
|
||
|
vars->active_view = 0;
|
||
|
if (vars->first_view != 0){
|
||
|
APP_SignalViewChange(vars->first_view);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal E_Space*
|
||
|
APP_ReloadSpace(E_Space *space, String8 serialized){
|
||
|
E_Space *result = E_DeserializeSpace(serialized);
|
||
|
|
||
|
if (result == 0){
|
||
|
result = space;
|
||
|
}
|
||
|
else{
|
||
|
E_View *active_view = vars->active_view;
|
||
|
b32 deleted_active_view = 0;
|
||
|
b32 effected_active_view = 0;
|
||
|
|
||
|
for (E_View *view = vars->first_view, *next = 0;
|
||
|
view != 0;
|
||
|
view = next){
|
||
|
next = view->next;
|
||
|
if (view->space == space){
|
||
|
E_Definition *definition = view->definition;
|
||
|
E_Definition *replacement_definition = 0;
|
||
|
if (definition != 0){
|
||
|
replacement_definition = E_GetDefinitionByID(result, definition->id, 0);
|
||
|
}
|
||
|
|
||
|
if (definition != 0 && replacement_definition == 0){
|
||
|
if (view == active_view){
|
||
|
deleted_active_view = 1;
|
||
|
effected_active_view = 1;
|
||
|
}
|
||
|
E_DeleteView(view);
|
||
|
}
|
||
|
else{
|
||
|
if (view == active_view){
|
||
|
effected_active_view = 1;
|
||
|
}
|
||
|
view->space = result;
|
||
|
view->definition = replacement_definition;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
E_View *global_view = APP_GetGlobalView(result);
|
||
|
if (global_view == 0){
|
||
|
global_view = E_NewView();
|
||
|
E_InitGlobalView(global_view, &vars->font, result);
|
||
|
}
|
||
|
if (deleted_active_view){
|
||
|
active_view = global_view;
|
||
|
}
|
||
|
if (effected_active_view){
|
||
|
vars->active_view = 0;
|
||
|
APP_SignalViewChange(active_view);
|
||
|
}
|
||
|
|
||
|
E_DeleteSpace(space);
|
||
|
}
|
||
|
|
||
|
return(result);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
APP_PERMANENT_LOAD
|
||
|
{
|
||
|
os = os_;
|
||
|
|
||
|
#if 1
|
||
|
_STR_Test();
|
||
|
_E_TokenBuffer_Test();
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
M_Arena arena_ = M_ArenaInitialize();
|
||
|
vars = PushArray(&arena_, APP_Variables, 1);
|
||
|
MemoryCopyStruct(&vars->arena_, &arena_);
|
||
|
}
|
||
|
M_Arena *arena = vars->arena = &vars->arena_;
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Init rendering
|
||
|
|
||
|
R_Init(arena);
|
||
|
R_InitFont(&vars->font, S8Lit("liberation-mono.ttf"), 20);
|
||
|
R_SelectFont(&vars->font);
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Init splink engine
|
||
|
|
||
|
for (u64 i = 0; i < ArrayCount(vars->frame_arena); i += 1){
|
||
|
vars->frame_arena[i] = M_ArenaInitialize();
|
||
|
}
|
||
|
vars->string_hash = STR_InitHash();
|
||
|
|
||
|
vars->cells = C_InitCellMemory();
|
||
|
vars->static_bucket = C_InitCellBucket(&vars->cells);
|
||
|
vars->statics = C_InitStatics(&vars->static_bucket);
|
||
|
vars->global_defines_bucket = C_InitCellBucket(&vars->cells);
|
||
|
vars->eval_bucket = C_InitCellBucket(&vars->cells);
|
||
|
|
||
|
for (u64 i = 0; i < C_BuiltInIndex_COUNT; i += 1){
|
||
|
vars->keyword_table[i] = STR_Save(&vars->string_hash, C_GetBuiltInKeyword(i));
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): UI State
|
||
|
|
||
|
vars->lister_flags = ~0;
|
||
|
vars->panel_filter_buffer = E_InitTextFieldTokenBuffer(&vars->panel_filter_memory);
|
||
|
vars->panel_filter = E_InitEditorState(&vars->panel_filter_buffer, &vars->font);
|
||
|
}
|
||
|
|
||
|
APP_HOT_LOAD
|
||
|
{
|
||
|
os = os_;
|
||
|
}
|
||
|
|
||
|
APP_HOT_UNLOAD {}
|
||
|
|
||
|
////////////////////////////////
|
||
|
|
||
|
APP_UPDATE
|
||
|
{
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Frame setup
|
||
|
|
||
|
vars->frame_time = os->GetTime();
|
||
|
vars->window_dim = v2(os->window_size.x, os->window_size.y);
|
||
|
R_Begin(vars->window_dim, cl_black);
|
||
|
|
||
|
M_ArenaClear(APP_GetFrameArena());
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Cleanup deleted definitions
|
||
|
|
||
|
if (vars->active_view != 0){
|
||
|
E_View *view = vars->active_view;
|
||
|
E_Space *space = view->space;
|
||
|
if (space->delete_signal){
|
||
|
APP_SpaceHandleDeleteSignal(space);
|
||
|
vars->active_view = 0;
|
||
|
APP_SignalViewChange(view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (E_Space *space = vars->first_space;
|
||
|
space != 0;
|
||
|
space = space->next){
|
||
|
if (space->delete_signal){
|
||
|
APP_SpaceHandleDeleteSignal(space);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Cleanup closed tiles
|
||
|
|
||
|
for (E_View *view = vars->first_view;
|
||
|
view != 0;
|
||
|
view = view->next){
|
||
|
E_ViewCleanup(view);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Auto-Save
|
||
|
|
||
|
{
|
||
|
M_Arena *scratch = OS_GetScratch();
|
||
|
M_Temp restore = M_BeginTemp(scratch);
|
||
|
for (E_Space *space = vars->first_space_ordered;
|
||
|
space != 0;
|
||
|
space = space->ordered_next){
|
||
|
if (space->dirty && space->save_path != 0){
|
||
|
if (space->last_save_time + 3.f <= vars->frame_time){
|
||
|
space->last_save_time = vars->frame_time;
|
||
|
String8 path = STR_Read(APP_GetStringHash(), space->save_path);
|
||
|
String8 serialized = E_SerializeSpace(scratch, space);
|
||
|
space->dirty = os->SaveToFile(path, serialized.str, serialized.size);
|
||
|
M_EndTemp(restore);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
OS_ReleaseScratch(scratch);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Set Mouse Position
|
||
|
|
||
|
UI_Id main_mouse_layer = UI_IdV(APP_MouseLayer_Main);
|
||
|
UI_Id floating_window_mouse_layer = UI_IdV(APP_MouseLayer_FloatingWindow);
|
||
|
vars->active_mouse_layer = main_mouse_layer;
|
||
|
|
||
|
MemoryZeroStruct(&vars->tool_tip_string);
|
||
|
|
||
|
{
|
||
|
OS_Event *event = 0;
|
||
|
for (;OS_GetNextEvent(&event);){
|
||
|
switch (event->type){
|
||
|
case OS_EventType_MouseMove:
|
||
|
{
|
||
|
vars->mouse_p = event->position;
|
||
|
OS_EatEvent(event);
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Floating Window Pre-Update
|
||
|
|
||
|
vars->last_frame_owner_of_floating_window = vars->owner_of_floating_window;
|
||
|
vars->last_frame_floating_window_ptr = vars->floating_window_ptr;
|
||
|
vars->floating_window_ptr = 0;
|
||
|
vars->floating_window_callback = 0;
|
||
|
|
||
|
if (!UI_IdEq(vars->owner_of_floating_window, UI_IdZero())){
|
||
|
APP_SetMouseLayer(floating_window_mouse_layer);
|
||
|
vars->active_mouse_layer = floating_window_mouse_layer;
|
||
|
if (UI_TryEatKeyPress(Key_Esc)){
|
||
|
MemoryZeroStruct(&vars->owner_of_floating_window);
|
||
|
}
|
||
|
if (!UI_MouseInRect(vars->floating_window_last_frame_rect)){
|
||
|
if (UI_TryEatLeftClick()){
|
||
|
MemoryZeroStruct(&vars->owner_of_floating_window);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (UI_IdEq(vars->owner_of_floating_window, UI_IdZero())){
|
||
|
vars->active_mouse_layer = main_mouse_layer;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Top Level UI Layout
|
||
|
|
||
|
APP_SetMouseLayer(main_mouse_layer);
|
||
|
|
||
|
v2 space_dim = R_StringDimWithFont(&vars->font, 1.f, S8Lit(" "));
|
||
|
f32 outline_t = 2.f;
|
||
|
f32 bar_h = space_dim.y + outline_t*2.f;
|
||
|
v2 btn_dim = {space_dim.x*3.f};
|
||
|
btn_dim.y = btn_dim.x;
|
||
|
|
||
|
Range y_free = MakeRange(0, vars->window_dim.y);
|
||
|
Range x_free = MakeRange(0, vars->window_dim.x);
|
||
|
|
||
|
Range panel_x = RangeSplit(&x_free, Side_Min, space_dim.x*21.f);
|
||
|
x_free.min += outline_t;
|
||
|
|
||
|
Range y_free_panel = y_free;
|
||
|
Range tool_box_y = RangeSplit(&y_free_panel, Side_Min, btn_dim.y);
|
||
|
Range lister_toggles_y = RangeSplit(&y_free_panel, Side_Min, btn_dim.y);
|
||
|
y_free_panel.min += outline_t;
|
||
|
Range filter_field_y = RangeSplit(&y_free_panel, Side_Min, bar_h);
|
||
|
y_free_panel.min += outline_t;
|
||
|
Range lister_y = y_free_panel;
|
||
|
|
||
|
|
||
|
Range code_x = x_free;
|
||
|
Range view_buttons_y = RangeSplit(&y_free, Side_Min, btn_dim.y);
|
||
|
y_free_panel.min += outline_t;
|
||
|
Range tabs_y = RangeSplit(&y_free, Side_Min, bar_h);
|
||
|
Range code_y = y_free;
|
||
|
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Tool Box
|
||
|
|
||
|
{
|
||
|
Rect rect = MakeRectRanges(panel_x, tool_box_y);
|
||
|
R_Rect(rect, cl_back_unimportant, 1.f);
|
||
|
|
||
|
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(rect, btn_dim, &vars->font, UI_IdV(APP_BtnCtx_ToolBox));
|
||
|
btn_ctx.outline_t = outline_t;
|
||
|
btn_ctx.enable_drop_down = 1;
|
||
|
btn_ctx.enable_hot_keys = 1;
|
||
|
|
||
|
UI_NextHotkey(&btn_ctx, Key_N, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("new definition"));
|
||
|
if (UI_Button(&btn_ctx, '+', 'n')){
|
||
|
if (vars->active_view != 0){
|
||
|
E_ViewCreateNewDefinition(vars->active_view);
|
||
|
}
|
||
|
else{
|
||
|
E_Space *empty_space = E_NewSpace();
|
||
|
E_View *global_view = E_NewView();
|
||
|
E_InitGlobalView(global_view, &vars->font, empty_space);
|
||
|
APP_SetActiveView(global_view);
|
||
|
E_ViewCreateNewDefinition(global_view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UI_NextHotkey(&btn_ctx, Key_T, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("new page"));
|
||
|
if (UI_Button(&btn_ctx, '+', 't')){
|
||
|
if (vars->active_view != 0){
|
||
|
E_Definition *definition = E_ViewCreateNewDefinition(vars->active_view);
|
||
|
E_View *view = E_NewView();
|
||
|
E_InitPageView(view, vars->active_view->font, definition);
|
||
|
APP_SignalViewChange(view);
|
||
|
}
|
||
|
else{
|
||
|
E_Space *empty_space = E_NewSpace();
|
||
|
E_View *global_view = E_NewView();
|
||
|
E_InitGlobalView(global_view, &vars->font, empty_space);
|
||
|
APP_SetActiveView(global_view);
|
||
|
E_ViewCreateNewDefinition(global_view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String8 ext[2];
|
||
|
ext[0] = S8Lit("Splink");
|
||
|
ext[1] = S8Lit("splink");
|
||
|
|
||
|
E_Space *space = APP_GetActiveSpace();
|
||
|
UI_NextCondition(&btn_ctx, space != 0);
|
||
|
UI_NextHotkey(&btn_ctx, Key_S, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("set save path"));
|
||
|
if (UI_Button(&btn_ctx, 'v', 'S')){
|
||
|
M_Arena *scratch = OS_GetScratch();
|
||
|
Assert(space != 0);
|
||
|
String8 string = os->DialogueSavePath(scratch, ext);
|
||
|
if (string.size > 0){
|
||
|
if (!StringMatch(StringPostfix(string, 7),
|
||
|
S8Lit(".splink"))){
|
||
|
string = PushStringCat(scratch, string, S8Lit(".splink"));
|
||
|
}
|
||
|
space->dirty = 1;
|
||
|
space->last_save_time = 0.f;
|
||
|
space->save_path = STR_Save(APP_GetStringHash(), string);
|
||
|
E_SpaceUpdateValidation(space);
|
||
|
}
|
||
|
OS_ReleaseScratch(scratch);
|
||
|
}
|
||
|
|
||
|
UI_NextHotkey(&btn_ctx, Key_O, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("open space"));
|
||
|
if (UI_Button(&btn_ctx, '^', 'O')){
|
||
|
M_Arena *scratch = OS_GetScratch();
|
||
|
String8 string = os->DialogueLoadPath(scratch, ext);
|
||
|
if (string.size > 0){
|
||
|
String8 serialized = {0};
|
||
|
os->LoadEntireFile(scratch, string, (void**)&serialized.str, &serialized.size);
|
||
|
|
||
|
STR_Index path = STR_Save(APP_GetStringHash(), string);
|
||
|
E_Space *new_space = E_GetSpaceByPath(path, 0);
|
||
|
if (new_space != 0){
|
||
|
new_space = APP_ReloadSpace(new_space, serialized);
|
||
|
}
|
||
|
else{
|
||
|
new_space = E_DeserializeSpace(serialized);
|
||
|
E_View *global_view = E_NewView();
|
||
|
E_InitGlobalView(global_view, &vars->font, new_space);
|
||
|
APP_SetActiveView(global_view);
|
||
|
}
|
||
|
if (new_space->init_error){
|
||
|
new_space->save_path = 0;
|
||
|
}
|
||
|
else{
|
||
|
new_space->save_path = path;
|
||
|
}
|
||
|
E_SpaceUpdateValidation(new_space);
|
||
|
}
|
||
|
OS_ReleaseScratch(scratch);
|
||
|
}
|
||
|
|
||
|
UI_NextHotkey(&btn_ctx, Key_N, KeyModifier_Ctrl|KeyModifier_Shift);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("new space"));
|
||
|
if (UI_Button(&btn_ctx, '^', 'N')){
|
||
|
E_Space *empty_space = E_NewSpace();
|
||
|
E_View *global_view = E_NewView();
|
||
|
E_InitGlobalView(global_view, &vars->font, empty_space);
|
||
|
APP_SetActiveView(global_view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Panel Buttons
|
||
|
|
||
|
{
|
||
|
Rect rect = MakeRectRanges(panel_x, lister_toggles_y);
|
||
|
R_Rect(rect, cl_back_unimportant, 1.f);
|
||
|
|
||
|
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(rect, btn_dim, &vars->font, UI_IdV(APP_BtnCtx_ListerOptions));
|
||
|
btn_ctx.outline_t = outline_t;
|
||
|
btn_ctx.enable_drop_down = 1;
|
||
|
btn_ctx.enable_hot_keys = 1;
|
||
|
|
||
|
UI_NextActive(&btn_ctx, vars->lister_flags & APP_ListerFlag_Spaces);
|
||
|
UI_NextHotkey(&btn_ctx, Key_1, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("list spaces"));
|
||
|
if (UI_Button(&btn_ctx, 'L', 's')){
|
||
|
vars->lister_flags ^= APP_ListerFlag_Spaces;
|
||
|
}
|
||
|
|
||
|
UI_NextActive(&btn_ctx, vars->lister_flags & APP_ListerFlag_Pages);
|
||
|
UI_NextHotkey(&btn_ctx, Key_2, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("list pages"));
|
||
|
if (UI_Button(&btn_ctx, 'L', 'p')){
|
||
|
vars->lister_flags ^= APP_ListerFlag_Pages;
|
||
|
}
|
||
|
|
||
|
UI_NextActive(&btn_ctx, vars->lister_flags & APP_ListerFlag_Invalids);
|
||
|
UI_NextHotkey(&btn_ctx, Key_3, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("list invalid definitions"));
|
||
|
if (UI_Button(&btn_ctx, 'L', 'e')){
|
||
|
vars->lister_flags ^= APP_ListerFlag_Invalids;
|
||
|
}
|
||
|
|
||
|
UI_NextActive(&btn_ctx, vars->lister_flags & APP_ListerFlag_Definitions);
|
||
|
UI_NextHotkey(&btn_ctx, Key_4, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("list definitions"));
|
||
|
if (UI_Button(&btn_ctx, 'L', 'd')){
|
||
|
vars->lister_flags ^= APP_ListerFlag_Definitions;
|
||
|
}
|
||
|
|
||
|
UI_NextActive(&btn_ctx, vars->lister_flags & APP_ListerFlag_Tests);
|
||
|
UI_NextHotkey(&btn_ctx, Key_5, KeyModifier_Ctrl);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("list tests"));
|
||
|
if (UI_Button(&btn_ctx, 'L', 't')){
|
||
|
vars->lister_flags ^= APP_ListerFlag_Tests;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Panel Text Field
|
||
|
|
||
|
{
|
||
|
Rect rect = MakeRectRanges(panel_x, filter_field_y);
|
||
|
Rect inner = RectShrink(rect, outline_t);
|
||
|
|
||
|
UI_ColorProfile *cl = UI_ColorsDefault();
|
||
|
UI_ActionLevel actlvl = UI_ActionLevel_None;
|
||
|
if (UI_MouseInRect(rect)){
|
||
|
actlvl = UI_ActionLevel_Hover;
|
||
|
OS_Event *event = 0;
|
||
|
if (UI_TryGetLeftClick(&event)){
|
||
|
APP_SetActiveEditor(&vars->panel_filter);
|
||
|
E_EditorHandleMousePressEvent(&vars->panel_filter, event, inner.p0);
|
||
|
}
|
||
|
}
|
||
|
else if (vars->active_editor == &vars->panel_filter){
|
||
|
actlvl = UI_ActionLevel_Active;
|
||
|
}
|
||
|
|
||
|
R_Rect(rect, cl->back[actlvl], 1.f);
|
||
|
R_RectOutline(rect, outline_t, cl->outline[actlvl], 1.f);
|
||
|
|
||
|
Rect clip_restore = R_PushClip(inner);
|
||
|
|
||
|
E_EditorUpdateLayout(&vars->panel_filter, &vars->font);
|
||
|
E_EditorRender(&vars->panel_filter, &vars->font, inner.p0);
|
||
|
|
||
|
if (vars->active_editor != &vars->panel_filter && vars->panel_filter.buffer->count == 0){
|
||
|
R_String(inner.p0, 1.f, S8Lit("filter"), cl->front[0], 0.15f);
|
||
|
}
|
||
|
|
||
|
R_SetClip(clip_restore);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Panel Definitions List
|
||
|
|
||
|
{
|
||
|
M_Arena *scratch = OS_GetScratch();
|
||
|
|
||
|
STR_Hash *string_hash = APP_GetStringHash();
|
||
|
Rect rect = MakeRectRanges(panel_x, lister_y);
|
||
|
R_Rect(rect, cl_back_unimportant, 1.f);
|
||
|
|
||
|
Rect clip_restore = R_PushClip(rect);
|
||
|
Rect scrolled_rect = rect;
|
||
|
scrolled_rect.y0 -= vars->panel_scroll_y;
|
||
|
|
||
|
char split_chars[] = " *";
|
||
|
String8 filter_key = {0};
|
||
|
if (vars->panel_filter_buffer.count > 0){
|
||
|
filter_key = STR_Read(string_hash, vars->panel_filter_memory.string);
|
||
|
}
|
||
|
String8_List filter_pattern = StringSplit(scratch, filter_key, (u8*)split_chars, ArrayCount(split_chars));
|
||
|
StringListRemoveEmpties(&filter_pattern);
|
||
|
|
||
|
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(scrolled_rect, v2(RangeSize(panel_x), bar_h), &vars->font, UI_IdV(APP_BtnCtx_Lister));
|
||
|
btn_ctx.outline_t = outline_t;
|
||
|
|
||
|
UI_ColorProfile *cl_prof[2];
|
||
|
cl_prof[0] = UI_ColorsProblem();
|
||
|
cl_prof[1] = UI_ColorsDefault();
|
||
|
|
||
|
// NOTE(allen): Spaces
|
||
|
if (vars->lister_flags & APP_ListerFlag_Spaces){
|
||
|
UI_NextCondition(&btn_ctx, 0);
|
||
|
UI_ButtonLabel(&btn_ctx, S8Lit("<Spaces>"));
|
||
|
|
||
|
E_Space *first[2];
|
||
|
first[0] = vars->first_invalid_space;
|
||
|
first[1] = vars->first_space;
|
||
|
|
||
|
for (u64 i = 0; i < 2; i += 1){
|
||
|
UI_SetColorProfile(&btn_ctx, cl_prof[i]);
|
||
|
for (E_Space *space = first[i];
|
||
|
space != 0;
|
||
|
space = space->next){
|
||
|
String8 path = STR_Read(string_hash, space->save_path);
|
||
|
String8 name = STR_NameFromPath(path);
|
||
|
if (APP_FuzzyMatch(name, &filter_pattern)){
|
||
|
if (UI_ButtonLabel(&btn_ctx, name)){
|
||
|
E_View *view = APP_GetGlobalView(space);
|
||
|
if (view == 0){
|
||
|
view = E_NewView();
|
||
|
E_InitGlobalView(view, &vars->font, space);
|
||
|
}
|
||
|
APP_SignalViewChange(view);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NOTE(allen): Various types of definitions
|
||
|
E_Space *space = APP_GetActiveSpace();
|
||
|
if (space != 0){
|
||
|
typedef struct APP_DefinitionGroup APP_DefinitionGroup;
|
||
|
struct APP_DefinitionGroup{
|
||
|
char *label;
|
||
|
APP_ListerFlags lister_flag;
|
||
|
E_DefinitionFlags filter_positive;
|
||
|
E_DefinitionFlags filter_negative;
|
||
|
UI_ColorProfile *cl;
|
||
|
};
|
||
|
|
||
|
APP_DefinitionGroup groups[4] = {
|
||
|
{"<Pages>" , APP_ListerFlag_Pages ,
|
||
|
E_DefinitionFlag_Page, 0,
|
||
|
UI_ColorsPage(), },
|
||
|
|
||
|
{"<Invalids>" , APP_ListerFlag_Invalids ,
|
||
|
E_DefinitionFlag_Invalid, E_DefinitionFlag_Page|E_DefinitionFlag_Test,
|
||
|
UI_ColorsProblem(), },
|
||
|
|
||
|
{"<Definitions>", APP_ListerFlag_Definitions,
|
||
|
0, E_DefinitionFlag_Page|E_DefinitionFlag_Invalid|E_DefinitionFlag_Test,
|
||
|
UI_ColorsDefault(), },
|
||
|
|
||
|
{"<Tests>" , APP_ListerFlag_Tests ,
|
||
|
E_DefinitionFlag_Test, E_DefinitionFlag_Page,
|
||
|
UI_ColorsTest(), },
|
||
|
};
|
||
|
|
||
|
APP_DefinitionGroup *group = groups;
|
||
|
for (u64 j = 0; j < ArrayCount(groups); j += 1, group += 1){
|
||
|
if (vars->lister_flags & group->lister_flag){
|
||
|
Assert((group->filter_positive & group->filter_negative) == 0);
|
||
|
|
||
|
E_DefinitionFlags filter_positive = group->filter_positive;
|
||
|
E_DefinitionFlags filter_negative = group->filter_negative;
|
||
|
|
||
|
UI_SetColorProfile(&btn_ctx, group->cl);
|
||
|
UI_NextCondition(&btn_ctx, 0);
|
||
|
UI_ButtonLabel(&btn_ctx, String8FromCString(group->label));
|
||
|
|
||
|
for (E_Definition *node = space->first_definition_ordered;
|
||
|
node != 0;
|
||
|
node = node->ordered_next){
|
||
|
if ((node->flags & filter_positive) != filter_positive){
|
||
|
continue;
|
||
|
}
|
||
|
if ((node->flags & filter_negative) != 0){
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
String8 name = E_DefinitionName(node);
|
||
|
if (!APP_FuzzyMatch(name, &filter_pattern)){
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (name.size == 0){
|
||
|
name = S8Lit("<unnamed>");
|
||
|
}
|
||
|
if (UI_ButtonLabel(&btn_ctx, name)){
|
||
|
switch (group->lister_flag){
|
||
|
case APP_ListerFlag_Pages:
|
||
|
{
|
||
|
OP_ViewPageFromDefinition(node);
|
||
|
}break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
OP_LookAtDefinition(node);
|
||
|
}break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NOTE(allen): Scrolling
|
||
|
if (UI_MouseInRect(rect)){
|
||
|
v2 delta = {0};
|
||
|
if (UI_TryEatScroll(&delta)){
|
||
|
vars->panel_scroll_y -= delta.y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
f32 h = UI_ButtonCtxGetTraversedY(&btn_ctx);;
|
||
|
f32 max_scroll = h - RangeSize(lister_y)*0.5f;
|
||
|
max_scroll = ClampBot(0.f, max_scroll);
|
||
|
vars->panel_scroll_y = Clamp(0.f, vars->panel_scroll_y, max_scroll);
|
||
|
|
||
|
}
|
||
|
|
||
|
R_SetClip(clip_restore);
|
||
|
OS_ReleaseScratch(scratch);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): View Buttons
|
||
|
|
||
|
{
|
||
|
Rect rect = MakeRectRanges(code_x, view_buttons_y);
|
||
|
R_Rect(rect, cl_back_unimportant, 1.f);
|
||
|
|
||
|
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(rect, btn_dim, &vars->font, UI_IdV(APP_BtnCtx_ViewButtons));
|
||
|
btn_ctx.outline_t = outline_t;
|
||
|
btn_ctx.enable_drop_down = 1;
|
||
|
btn_ctx.enable_hot_keys = 1;
|
||
|
|
||
|
UI_SetColorProfile(&btn_ctx, UI_ColorsProblem());
|
||
|
UI_SetRedText(&btn_ctx.cl);
|
||
|
|
||
|
UI_NextActive(&btn_ctx, vars->active_view != 0);
|
||
|
UI_NextHotkey(&btn_ctx, Key_K, KeyModifier_Ctrl|KeyModifier_Shift);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("close current tab"));
|
||
|
if (UI_Button(&btn_ctx, 'X', 't')){
|
||
|
E_View *view = vars->active_view;
|
||
|
E_View *switch_to_view = view->next;
|
||
|
if (switch_to_view == 0){
|
||
|
switch_to_view = view->prev;
|
||
|
}
|
||
|
E_DeleteView(view);
|
||
|
|
||
|
vars->active_view = 0;
|
||
|
if (switch_to_view != 0){
|
||
|
APP_SignalViewChange(switch_to_view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UI_NextActive(&btn_ctx, APP_GetActiveSpace() != 0);
|
||
|
UI_NextTooltip(&btn_ctx, S8Lit("close current splink file"));
|
||
|
if (UI_Button(&btn_ctx, 'X', 'f')){
|
||
|
E_Space *space = APP_GetActiveSpace();
|
||
|
APP_CloseSpace(space);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Page Tabs
|
||
|
|
||
|
{
|
||
|
E_View *view = vars->active_view;
|
||
|
if (UI_TryEatKeyPressModified(Key_Tab, KeyModifier_Ctrl)){
|
||
|
APP_SignalViewChange((view != 0 && view->next != 0)?
|
||
|
view->next:vars->first_view);
|
||
|
}
|
||
|
if (UI_TryEatKeyPressModified(Key_Tab, KeyModifier_Ctrl|KeyModifier_Shift)){
|
||
|
APP_SignalViewChange((view != 0 && view->prev != 0)?
|
||
|
view->prev:vars->last_view);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
Rect rect = MakeRectRanges(code_x, tabs_y);
|
||
|
R_Rect(rect, cl_back_unimportant, 1.f);
|
||
|
|
||
|
f32 w = R_StringDimWithFont(&vars->font, 1.f, S8Lit(" ")).x*10.f;
|
||
|
UI_ButtonCtx btn_ctx = UI_InitButtonCtx(rect, v2(w, RangeSize(tabs_y)), &vars->font, UI_IdV(APP_BtnCtx_Tabs));
|
||
|
btn_ctx.outline_t = outline_t;
|
||
|
btn_ctx.text_scale = 0.70f;
|
||
|
btn_ctx.enable_flexible_x_advance = 1;
|
||
|
|
||
|
UI_SetColorProfile(&btn_ctx, UI_ColorsTabs());
|
||
|
|
||
|
for (E_View *view = vars->first_view;
|
||
|
view != 0;
|
||
|
view = view->next){
|
||
|
if (view == vars->active_view){
|
||
|
UI_NextActive(&btn_ctx, 1);
|
||
|
}
|
||
|
String8 name = E_ViewGetName(view);
|
||
|
if (UI_ButtonLabel(&btn_ctx, name)){
|
||
|
APP_SignalViewChange(view);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Update token editor layouts
|
||
|
|
||
|
if (vars->active_view != 0){
|
||
|
MemoryZeroArray(vars->neighbor_editors);
|
||
|
E_EditorState *prev_editor = 0;
|
||
|
b32 save_next = 0;
|
||
|
E_EditorState *active_editor = vars->active_editor;
|
||
|
|
||
|
for (E_Tile *tile = vars->active_view->first_tile;
|
||
|
tile != 0;
|
||
|
tile = tile->next){
|
||
|
E_TileUpdateLayout(tile, &vars->font);
|
||
|
|
||
|
if (active_editor != 0){
|
||
|
E_EditorState *editor = tile->editors;
|
||
|
for (u64 i = 0; i < ArrayCount(tile->editors); i += 1, editor += 1){
|
||
|
if (save_next){
|
||
|
vars->neighbor_editors[Side_Max] = editor;
|
||
|
save_next = 0;
|
||
|
active_editor = 0;
|
||
|
break;
|
||
|
}
|
||
|
if (active_editor == editor){
|
||
|
save_next = 1;
|
||
|
vars->neighbor_editors[Side_Min] = prev_editor;
|
||
|
}
|
||
|
prev_editor = editor;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Handle events
|
||
|
|
||
|
{
|
||
|
OS_Event *event = 0;
|
||
|
for (;OS_GetNextEvent(&event);){
|
||
|
switch (event->type){
|
||
|
case OS_EventType_CharacterInput:
|
||
|
{
|
||
|
// NOTE(allen): Does the active editor use this key?
|
||
|
if (vars->active_editor != 0 && E_EditorHandleCharacterEvent(vars->active_editor, event)){
|
||
|
goto finished_event;
|
||
|
}
|
||
|
}break;
|
||
|
|
||
|
case OS_EventType_KeyPress:
|
||
|
{
|
||
|
// NOTE(allen): Universal rules
|
||
|
if (event->key == Key_Esc){
|
||
|
vars->active_editor = 0;
|
||
|
OS_EatEvent(event);
|
||
|
goto finished_event;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (vars->active_editor != 0){
|
||
|
// NOTE(allen): Editor override rules
|
||
|
if (((event->modifiers & KeyModifier_Shift) != 0)){
|
||
|
if (event->key == Key_Enter){
|
||
|
APP_TopLevelNavigate(Dimension_Y, Side_Max);
|
||
|
OS_EatEvent(event);
|
||
|
goto finished_event;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NOTE(allen): Does the active editor use this key?
|
||
|
if (E_EditorHandleKeyPressEvent(vars->active_editor, event)){
|
||
|
OS_EatEvent(event);
|
||
|
goto finished_event;
|
||
|
}
|
||
|
|
||
|
// NOTE(allen): Navigate between active editors
|
||
|
if (event->key == Key_Up || event->key == Key_Down){
|
||
|
Side side = Side_Min;
|
||
|
if (event->key == Key_Down){
|
||
|
side = Side_Max;
|
||
|
}
|
||
|
APP_TopLevelNavigate(Dimension_Y, side);
|
||
|
|
||
|
OS_EatEvent(event);
|
||
|
goto finished_event;
|
||
|
}
|
||
|
}
|
||
|
}break;
|
||
|
|
||
|
case OS_EventType_MouseMove:
|
||
|
{
|
||
|
vars->mouse_p = event->position;
|
||
|
}break;
|
||
|
}
|
||
|
|
||
|
finished_event:;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Check for change notifications
|
||
|
|
||
|
if (vars->active_view != 0){
|
||
|
M_Arena *scratch = OS_GetScratch();
|
||
|
M_Temp restore = M_BeginTemp(scratch);
|
||
|
E_View *view = vars->active_view;
|
||
|
|
||
|
for (E_Space *space = vars->first_space_ordered;
|
||
|
space != 0;
|
||
|
space = space->ordered_next){
|
||
|
for (E_Definition *node = space->first_definition_ordered;
|
||
|
node != 0;
|
||
|
node = node->ordered_next){
|
||
|
// NOTE(allen): Check for a change
|
||
|
b32 has_change = 0;
|
||
|
{
|
||
|
E_TokenBuffer *buffer = node->buffers;
|
||
|
for (u64 i = 0; i < ArrayCount(node->buffers); i += 1, buffer += 1){
|
||
|
if (E_TokenBufferHasChange(buffer)){
|
||
|
has_change = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (has_change){
|
||
|
// NOTE(allen): State Update
|
||
|
if (E_TokenBufferHasChange(&node->name)){
|
||
|
E_DefinitionUpdateValidation(node);
|
||
|
}
|
||
|
|
||
|
// NOTE(allen): Collect tiles that view this definition
|
||
|
E_Tile **tile_ptrs = PushArray(scratch, E_Tile*, 0);
|
||
|
for (E_Tile *tile = view->first_tile;
|
||
|
tile != 0;
|
||
|
tile = tile->next){
|
||
|
if (tile->definition == node){
|
||
|
E_Tile **new_tile_ptr = PushArray(scratch, E_Tile*, 1);
|
||
|
*new_tile_ptr = tile;
|
||
|
}
|
||
|
}
|
||
|
u64 tile_count = (PushArray(scratch, E_Tile*, 0) - tile_ptrs);
|
||
|
|
||
|
// NOTE(allen): Update Buffers
|
||
|
u64 *cursor_array = PushArray(scratch, u64, tile_count);
|
||
|
E_TokenBuffer *buffer = node->buffers;
|
||
|
for (u64 i = 0; i < ArrayCount(node->buffers); i += 1, buffer += 1){
|
||
|
if (E_TokenBufferHasChange(buffer)){
|
||
|
for (u64 j = 0; j < tile_count; j += 1){
|
||
|
cursor_array[j] = tile_ptrs[j]->editors[i].cursor.pos;
|
||
|
}
|
||
|
E_TokenBufferScrub(buffer, cursor_array, tile_count);
|
||
|
E_TokenBufferChangeHandled(buffer);
|
||
|
|
||
|
// NOTE(allen): Update Editors
|
||
|
for (u64 j = 0; j < tile_count; j += 1){
|
||
|
E_EditorState *editor = &tile_ptrs[j]->editors[i];
|
||
|
E_EditorUpdateLayout(editor, tile_ptrs[j]->font);
|
||
|
editor->mark = editor->cursor;
|
||
|
E_EditorUpdatePreferredXToCursor(editor);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NOTE(allen): Compute Update
|
||
|
E_DefinitionUpdateCompute(node);
|
||
|
|
||
|
// NOTE(allen): Space Dirty
|
||
|
space->dirty = 1;
|
||
|
M_EndTemp(restore);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
OS_ReleaseScratch(scratch);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Update space validations from available identifiers
|
||
|
|
||
|
for (u64 lim = 0; vars->identifier_available_count > 0 && lim < 2; lim += 1){
|
||
|
u64 count = vars->identifier_available_count;
|
||
|
vars->identifier_available_count = 0;
|
||
|
|
||
|
if (count <= ArrayCount(vars->identifier_available)){
|
||
|
// NOTE(allen): Grab a copy of available so that E_SpaceUpdateValidation can
|
||
|
// signal more available identifiers.
|
||
|
STR_Index available[ArrayCount(vars->identifier_available)];
|
||
|
MemoryCopy(available, vars->identifier_available, sizeof(*available)*count);
|
||
|
|
||
|
for (u64 i = 0; i < count; i += 1){
|
||
|
STR_Index check = available[i];
|
||
|
E_Space *space = E_GetInvalidSpace(check, 0);
|
||
|
if (space != 0){
|
||
|
E_SpaceUpdateValidation(space);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
for (E_Space *space = vars->first_invalid_space;
|
||
|
space != 0;
|
||
|
space = space->next){
|
||
|
E_SpaceUpdateValidation(space);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Update definition validations from available identifiers
|
||
|
|
||
|
for (E_Space *space = vars->first_space_ordered;
|
||
|
space != 0;
|
||
|
space = space->ordered_next){
|
||
|
for (u64 lim = 0; space->identifier_available_count > 0 && lim < 2; lim += 1){
|
||
|
u64 count = space->identifier_available_count;
|
||
|
space->identifier_available_count = 0;
|
||
|
|
||
|
if (count <= ArrayCount(space->identifier_available)){
|
||
|
// NOTE(allen): Grab a copy of available so that E_DefinitionUpdateValidation can
|
||
|
// signal more available identifiers.
|
||
|
STR_Index available[ArrayCount(space->identifier_available)];
|
||
|
MemoryCopy(available, space->identifier_available, sizeof(*available)*count);
|
||
|
|
||
|
for (u64 i = 0; i < count; i += 1){
|
||
|
STR_Index check = available[i];
|
||
|
E_Definition *definition = E_GetInvalidDefinition(space, check, 0);
|
||
|
if (definition != 0){
|
||
|
E_DefinitionUpdateValidation(definition);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
for (E_Definition *node = space->first_invalid_definition;
|
||
|
node != 0;
|
||
|
node = node->next){
|
||
|
E_DefinitionUpdateValidation(node);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Init defers & Populate global defines
|
||
|
|
||
|
{
|
||
|
STR_Hash *string_hash = APP_GetStringHash();
|
||
|
M_Arena *scratch = OS_GetScratch();
|
||
|
C_CellBucket *bucket = &vars->global_defines_bucket;
|
||
|
C_ClearBucket(bucket);
|
||
|
|
||
|
C_Cell *spaces_env = vars->statics.env;
|
||
|
for (E_Space *space = vars->first_space_ordered;
|
||
|
space != 0;
|
||
|
space = space->ordered_next){
|
||
|
if (!space->invalid){
|
||
|
String8 name = E_SpaceName(space);
|
||
|
String8 space_identifier = PushStringCat(scratch, S8Lit("$"), name);
|
||
|
STR_Index space_name = STR_Save(string_hash, space_identifier);
|
||
|
C_Cell *space_cell = C_NewSpaceCell(bucket, space);
|
||
|
spaces_env = C_ExtendEnvironment(bucket, space_name, space_cell, spaces_env);
|
||
|
}
|
||
|
}
|
||
|
vars->spaces_env = spaces_env;
|
||
|
|
||
|
for (E_Space *space = vars->first_space_ordered;
|
||
|
space != 0;
|
||
|
space = space->ordered_next){
|
||
|
C_Cell *env = spaces_env;
|
||
|
C_Cell *id_env = vars->statics.Nil;
|
||
|
C_Cell *id_list = 0;
|
||
|
C_ListBuilder builder = C_InitListBuilder(id_list);
|
||
|
for (E_Definition *node = space->first_definition_ordered;
|
||
|
node != 0;
|
||
|
node = node->ordered_next){
|
||
|
MemoryZeroStruct(&node->deferred);
|
||
|
node->deferred.user_ptr = node;
|
||
|
C_Cell *defer_cell = C_NewDeferredCell(bucket, &node->deferred, node->body_cell);
|
||
|
C_Cell *id_cell = C_NewIdCell(bucket, node->id);
|
||
|
|
||
|
if (!(node->flags & E_DefinitionFlag_Invalid) && E_DefinitionCanEval(node)){
|
||
|
STR_Index name = C_GetIdentifierFromCell(node->name_cell);
|
||
|
env = C_ExtendEnvironment(bucket, name, defer_cell, env);
|
||
|
}
|
||
|
id_env = C_ExtendEnvironment(bucket, node->id, defer_cell, id_env);
|
||
|
C_ListBuilderPush(bucket, builder, id_cell);
|
||
|
}
|
||
|
C_ListBuilderTerminate(&vars->statics, builder);
|
||
|
|
||
|
space->defines_env = env;
|
||
|
space->id_env = id_env;
|
||
|
space->id_list = id_list;
|
||
|
|
||
|
for (E_Definition *node = space->first_definition_ordered;
|
||
|
node != 0;
|
||
|
node = node->ordered_next){
|
||
|
node->deferred.env = env;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
OS_ReleaseScratch(scratch);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Render and update editor
|
||
|
|
||
|
{
|
||
|
C_ClearBucket(&vars->eval_bucket);
|
||
|
|
||
|
Rect rect = MakeRectRanges(code_x, code_y);
|
||
|
R_Rect(rect, cl_back_editor, 1.f);
|
||
|
|
||
|
E_Tile *snap_to_tile = vars->snap_to_tile;
|
||
|
b32 found_snap_y = 0;
|
||
|
f32 snap_y = 0;
|
||
|
|
||
|
E_View *view = vars->active_view;
|
||
|
if (view != 0){
|
||
|
Rect restore_clip = R_PushClip(rect);
|
||
|
|
||
|
// NOTE(allen): Height pass
|
||
|
E_Tile *first_tile_crossover = 0;
|
||
|
f32 first_tile_crossover_y = 0;
|
||
|
|
||
|
f32 top = rect.y0 - view->scroll_y;
|
||
|
f32 y = top;
|
||
|
for (E_Tile *tile = view->first_tile;
|
||
|
tile != 0;
|
||
|
tile = tile->next){
|
||
|
f32 h = E_TileUIGetHeight(view, tile, y, code_x);
|
||
|
if (tile == snap_to_tile){
|
||
|
found_snap_y = 1;
|
||
|
snap_y = y + h*0.5f;
|
||
|
}
|
||
|
y += h;
|
||
|
if (first_tile_crossover == 0 && y > rect.y0){
|
||
|
first_tile_crossover = tile;
|
||
|
first_tile_crossover_y = y - h;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
f32 h = y - top;
|
||
|
|
||
|
// NOTE(allen): Scrolling
|
||
|
if (UI_MouseInRect(rect)){
|
||
|
v2 delta = {0};
|
||
|
if (UI_TryEatScroll(&delta)){
|
||
|
view->scroll_y -= delta.y;
|
||
|
}
|
||
|
}
|
||
|
if (found_snap_y){
|
||
|
f32 snap_y_document_space = snap_y - top;
|
||
|
|
||
|
// align the snap with 33% of the way down the view
|
||
|
// solve for scroll_y in: scroll_y + 0.33*view_h = snap_y
|
||
|
view->scroll_y = snap_y_document_space - 0.33f*RangeSize(code_y);
|
||
|
}
|
||
|
|
||
|
f32 max_scroll = h - RangeSize(code_y)*0.5f;
|
||
|
max_scroll = ClampBot(0.f, max_scroll);
|
||
|
view->scroll_y = Clamp(0.f, view->scroll_y, max_scroll);
|
||
|
|
||
|
// NOTE(allen): Action pass
|
||
|
y = first_tile_crossover_y;
|
||
|
for (E_Tile *tile = first_tile_crossover;
|
||
|
tile != 0;
|
||
|
tile = tile->next){
|
||
|
f32 h = E_TileUIUpdateAndRender(view, tile, y, code_x);
|
||
|
y += h;
|
||
|
if (y >= rect.y1 + 500.f){
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
R_SetClip(restore_clip);
|
||
|
}
|
||
|
|
||
|
vars->snap_to_tile = 0;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Delayed view change
|
||
|
|
||
|
APP_ApplyViewChange(vars->change_view);
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Floating Window Post-Update
|
||
|
|
||
|
if (!UI_IdEq(vars->owner_of_floating_window, UI_IdZero())){
|
||
|
APP_SetMouseLayer(floating_window_mouse_layer);
|
||
|
if (vars->floating_window_callback != 0){
|
||
|
R_Rect(MakeRect(0, 0, V2Expand(vars->window_dim)), cl_gray(0.1f), 0.25f);
|
||
|
APP_FloatingWindowResult result = {0};
|
||
|
vars->floating_window_callback(vars->floating_window_ptr, &result);
|
||
|
vars->floating_window_last_frame_rect = result.rect;
|
||
|
}
|
||
|
else{
|
||
|
MemoryZeroStruct(&vars->owner_of_floating_window);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): Tool Tip
|
||
|
|
||
|
if (vars->tool_tip_string.size > 0){
|
||
|
f32 padding = 2.f;
|
||
|
f32 y_skirt[2] = {6.f, 18.f};
|
||
|
f32 dbl_padding = padding*2.f;
|
||
|
|
||
|
String8 str = vars->tool_tip_string;
|
||
|
|
||
|
R_SelectFont(&vars->font);
|
||
|
v2 str_dim = R_StringDim(1.f, str);
|
||
|
v2 str_dim_padded = V2Add(str_dim, v2(dbl_padding, dbl_padding));
|
||
|
|
||
|
f32 y_base = vars->mouse_p.y + y_skirt[Side_Max];
|
||
|
Range y = MakeRange(y_base, y_base + str_dim_padded.y);
|
||
|
if (y.max > vars->window_dim.y){
|
||
|
y_base = vars->mouse_p.y - y_skirt[Side_Min];
|
||
|
y = MakeRange(y_base, y_base - str_dim_padded.y);
|
||
|
}
|
||
|
|
||
|
Range x;
|
||
|
// solve for x in: (x + 0.2*w = mx)
|
||
|
x.min = vars->mouse_p.x - 0.2f*str_dim_padded.x;
|
||
|
x.min = ClampBot(0.f, x.min);
|
||
|
x.max = x.min + str_dim_padded.x;
|
||
|
|
||
|
Rect rect = MakeRectRanges(x, y);
|
||
|
R_Rect(rect, cl_gray(0.1f), 0.66f);
|
||
|
|
||
|
v2 p = V2Add(rect.p0, v2(padding, padding));
|
||
|
R_String(p, 1.f, str, cl_white, 0.66f);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////
|
||
|
// NOTE(allen): End the frame
|
||
|
|
||
|
vars->frame_indx += 1;
|
||
|
|
||
|
{
|
||
|
// NOTE(allen): Why does the layer even keep events from previous frames?
|
||
|
// Anyway, just clear those out.
|
||
|
OS_Event *event = 0;
|
||
|
for (;OS_GetNextEvent(&event);){
|
||
|
OS_EatEvent(event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
R_End();
|
||
|
|
||
|
AssertIff(vars->active_view == 0, vars->first_view == 0);
|
||
|
_E_AssertGlobalEngineInvariants();
|
||
|
}
|
||
|
|