add EsPanelTableAddBandDecorator; script console array of struct viewer

This commit is contained in:
nakst 2022-02-06 14:18:04 +00:00
parent 73a6457025
commit 67b268db0a
5 changed files with 138 additions and 23 deletions

View File

@ -2,6 +2,8 @@
#include <essence.h>
// TODO Resizing the window after calling DirectoryEnumerateChildrenRecursively() is slow.
// TODO UTF-8 validation of outputted strings.
// TODO Check for heap allocation leaks.
struct Instance : EsInstance {
EsThreadInformation scriptThread;
@ -348,6 +350,7 @@ EXTERNAL_STUB(ExternalSystemSetEnvironmentVariable);
#define COLOR_BACKGROUND (0xFFFDFDFD)
#define COLOR_ROW_ODD (0xFFF4F4F4)
#define COLOR_ROW_HEADER_LINE (0xFFCCCCCC)
#define COLOR_OUTPUT_DECORATION_IN_PROGRESS (0xFFFF7F00)
#define COLOR_OUTPUT_DECORATION_SUCCESS (0xFF3070FF)
#define COLOR_OUTPUT_DECORATION_FAILURE (0xFFFF3040)
@ -523,16 +526,30 @@ const EsStyle styleOutputDecorationFailure = {
},
};
const EsThemeAppearance styleAppearanceRowHeader = {
.enabled = true,
.backgroundColor = COLOR_BACKGROUND,
.borderColor = COLOR_ROW_HEADER_LINE,
.borderSize = ES_RECT_4(0, 0, 0, 1),
};
const EsThemeAppearance styleAppearanceRowEven = {
.enabled = true,
.backgroundColor = COLOR_BACKGROUND,
};
const EsThemeAppearance styleAppearanceRowOdd = {
.enabled = true,
.backgroundColor = COLOR_ROW_ODD,
};
const EsStyle styleListRowEven = {
.metrics = {
.mask = ES_THEME_METRICS_INSETS,
.insets = ES_RECT_1(3),
},
.appearance = {
.enabled = true,
.backgroundColor = COLOR_BACKGROUND,
},
.appearance = styleAppearanceRowEven,
};
const EsStyle styleListRowOdd = {
@ -541,10 +558,7 @@ const EsStyle styleListRowOdd = {
.insets = ES_RECT_1(3),
},
.appearance = {
.enabled = true,
.backgroundColor = COLOR_ROW_ODD,
},
.appearance = styleAppearanceRowOdd,
};
void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Value value) {
@ -554,13 +568,16 @@ void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Val
size_t bytes;
if (type->type == T_INT) {
char *buffer = EsStringAllocateAndFormat(&bytes, "(int) %d", value.i);
char *buffer = EsStringAllocateAndFormat(&bytes, "%d", value.i);
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes);
} else if (type->type == T_BOOL) {
char *buffer = EsStringAllocateAndFormat(&bytes, "%z", value.i ? "true" : "false");
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes);
} else if (type->type == T_NULL) {
char *buffer = EsStringAllocateAndFormat(&bytes, "%z", "null");
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes);
} else if (type->type == T_FLOAT) {
char *buffer = EsStringAllocateAndFormat(&bytes, "(float) %F", value.f);
char *buffer = EsStringAllocateAndFormat(&bytes, "%F", value.f);
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes);
} else if (type->type == T_STR) {
EsAssert(context->heapEntriesAllocated > (uint64_t) value.i);
@ -570,7 +587,48 @@ void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Val
ScriptHeapEntryToString(context, entry, &valueText, &valueBytes);
char *buffer = EsStringAllocateAndFormat(&bytes, "\"%s\"", valueBytes, valueText);
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes);
} else if (type->type == T_LIST && type->firstChild->type == T_STRUCT) {
// TODO Row styling.
EsAssert(context->heapEntriesAllocated > (uint64_t) value.i);
HeapEntry *listEntry = &context->heap[value.i];
EsAssert(listEntry->type == T_LIST);
if (!listEntry->length) goto normalList;
EsAssert(context->heapEntriesAllocated > (uint64_t) listEntry->list[0].i);
EsAssert(context->heap[listEntry->list[0].i].type == T_STRUCT);
EsPanel *table = EsPanelCreate(parent, ES_CELL_H_FILL | ES_PANEL_HORIZONTAL | ES_PANEL_TABLE);
EsPanelSetBands(table, context->heap[listEntry->list[0].i].fieldCount);
EsPanelTableAddBandDecorator(table, { .index = 0, .repeatEvery = 0, .axis = 1, .appearance = &styleAppearanceRowHeader });
EsPanelTableAddBandDecorator(table, { .index = 1, .repeatEvery = 2, .axis = 1, .appearance = &styleAppearanceRowEven });
EsPanelTableAddBandDecorator(table, { .index = 2, .repeatEvery = 2, .axis = 1, .appearance = &styleAppearanceRowOdd });
{
Node *field = type->firstChild->firstChild;
while (field) {
EsTextDisplayCreate(table, ES_CELL_H_FILL, &styleOutputParagraph, field->token.text, field->token.textBytes);
field = field->sibling;
}
}
for (uintptr_t i = 0; i < listEntry->length; i++) {
EsAssert(context->heapEntriesAllocated > (uint64_t) value.i);
HeapEntry *itemEntry = &context->heap[listEntry->list[i].i];
EsAssert(itemEntry->type == T_STRUCT);
EsAssert(itemEntry->fieldCount == context->heap[listEntry->list[0].i].fieldCount);
Node *field = type->firstChild->firstChild;
uintptr_t j = 0;
while (field) {
EsAssert(j != itemEntry->fieldCount);
AddREPLResult(context, table, field->firstChild, itemEntry->fields[j]);
field = field->sibling;
j++;
}
}
} else if (type->type == T_LIST) {
normalList:;
EsPanel *panel = EsPanelCreate(parent, ES_CELL_H_FILL | ES_PANEL_VERTICAL | ES_PANEL_STACK);
EsAssert(context->heapEntriesAllocated > (uint64_t) value.i);
HeapEntry *entry = &context->heap[value.i];
@ -585,13 +643,8 @@ void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Val
EsPanel *item = EsPanelCreate(panel, ES_CELL_H_FILL, (i % 2) ? &styleListRowOdd : &styleListRowEven);
AddREPLResult(context, item, type->firstChild, entry->list[i]);
}
} else if (type->type == T_FUNCPTR) {
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphItalic,
EsLiteral("Function pointer.\n"));
} else if (type->type == T_STRUCT) {
EsPanel *panel = EsPanelCreate(parent, ES_CELL_H_FILL | ES_PANEL_VERTICAL | ES_PANEL_STACK);
char *buffer = EsStringAllocateAndFormat(&bytes, "%s:", type->token.textBytes, type->token.text);
EsTextDisplayCreate(panel, ES_CELL_H_FILL, &styleOutputParagraph, buffer, bytes);
EsAssert(context->heapEntriesAllocated > (uint64_t) value.i);
HeapEntry *entry = &context->heap[value.i];
EsAssert(entry->type == T_STRUCT);
@ -601,14 +654,14 @@ void AddREPLResult(ExecutionContext *context, EsElement *parent, Node *type, Val
while (field) {
EsAssert(i != entry->fieldCount);
EsPanel *item = EsPanelCreate(panel, ES_CELL_H_FILL, (i % 2) ? &styleListRowEven : &styleListRowOdd);
EsPanel *item = EsPanelCreate(panel, ES_CELL_H_FILL, (i % 2) ? &styleListRowOdd : &styleListRowEven);
AddREPLResult(context, item, field->firstChild, entry->fields[i]);
field = field->sibling;
i++;
}
} else if (type->type == T_NULL) {
char *buffer = EsStringAllocateAndFormat(&bytes, "%z", "null");
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphStrong, buffer, bytes);
} else if (type->type == T_FUNCPTR) {
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphItalic,
EsLiteral("Function pointer.\n"));
} else {
EsTextDisplayCreate(parent, ES_CELL_H_FILL, &styleOutputParagraphItalic,
EsLiteral("The type of the result was not recognized.\n"));

View File

@ -382,6 +382,8 @@ struct EsPanel : EsElement {
uint16_t bandCount[2];
EsPanelBand *bands[2];
uintptr_t tableIndex;
uint8_t *tableMemoryBase;
Array<EsPanelBandDecorator> bandDecorators;
// TODO This names overlap with fields in EsElement, they should probably be renamed.
uint16_t transitionType;
@ -1985,18 +1987,23 @@ void LayoutTable(EsPanel *panel, EsMessage *message) {
size_t childCount = panel->GetChildCount();
EsHeapFree(panel->tableMemoryBase);
panel->tableMemoryBase = nullptr;
uint8_t *memoryBase = (uint8_t *) EsHeapAllocate(sizeof(int) * childCount * 2 + sizeof(EsPanelBand) * (panel->bandCount[0] + panel->bandCount[1]), true), *memory = memoryBase;
if (!memoryBase) {
return;
}
// NOTE These must be contiguous at come at the start of memoryBase, so that the band decorators can look up band positions.
EsPanelBand *calculatedProperties[2];
calculatedProperties[0] = (EsPanelBand *) memory; memory += sizeof(EsPanelBand) * panel->bandCount[0];
calculatedProperties[1] = (EsPanelBand *) memory; memory += sizeof(EsPanelBand) * panel->bandCount[1];
int *calculatedSize[2];
calculatedSize[0] = (int *) memory; memory += sizeof(int) * childCount;
calculatedSize[1] = (int *) memory; memory += sizeof(int) * childCount;
EsPanelBand *calculatedProperties[2];
calculatedProperties[0] = (EsPanelBand *) memory; memory += sizeof(EsPanelBand) * panel->bandCount[0];
calculatedProperties[1] = (EsPanelBand *) memory; memory += sizeof(EsPanelBand) * panel->bandCount[1];
for (int axis = 0; axis < 2; axis++) {
if (panel->bands[axis]) {
@ -2243,7 +2250,7 @@ void LayoutTable(EsPanel *panel, EsMessage *message) {
EsPrint("\t%d/%d\n", out[0], out[1]);
}
EsHeapFree(memoryBase);
panel->tableMemoryBase = memoryBase;
}
int LayoutStackDeterminePerPush(EsPanel *panel, int available, int secondary) {
@ -3128,6 +3135,9 @@ void PanelTableSetChildCell(EsPanel *panel, EsElement *child) {
}
child->tableCell = cell;
EsHeapFree(panel->tableMemoryBase);
panel->tableMemoryBase = nullptr;
}
int ProcessPanelMessage(EsElement *element, EsMessage *message) {
@ -3165,6 +3175,28 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) {
} else {
LayoutStack(panel, message);
}
} else if (message->type == ES_MSG_PAINT) {
uint8_t *memory = panel->tableMemoryBase;
EsRectangle client = EsPainterBoundsClient(message->painter);
if (memory) {
EsPanelBand *calculatedProperties[2];
calculatedProperties[0] = (EsPanelBand *) memory; memory += sizeof(EsPanelBand) * panel->bandCount[0];
calculatedProperties[1] = (EsPanelBand *) memory; memory += sizeof(EsPanelBand) * panel->bandCount[1];
for (uintptr_t i = 0; i < panel->bandDecorators.Length(); i++) {
EsPanelBandDecorator decorator = panel->bandDecorators[i];
for (uintptr_t j = decorator.index; j < panel->bandCount[decorator.axis]; j += decorator.repeatEvery) {
EsRectangle bounds;
if (decorator.axis) bounds = ES_RECT_4(client.l, client.r, client.t + calculatedProperties[1][j].maximumSize, client.t + calculatedProperties[1][j].maximumSize + calculatedProperties[1][j].preferredSize);
else bounds = ES_RECT_4(client.l + calculatedProperties[0][j].maximumSize, client.l + calculatedProperties[0][j].maximumSize + calculatedProperties[0][j].preferredSize, client.t, client.b);
EsDrawRectangle(message->painter, bounds, decorator.appearance->backgroundColor,
decorator.appearance->borderColor, decorator.appearance->borderSize);
if (!decorator.repeatEvery) break;
}
}
}
} else if (message->type == ES_MSG_PAINT_CHILDREN) {
if ((panel->flags & ES_PANEL_SWITCHER) && panel->transitionType != ES_TRANSITION_NONE) {
double progress = SmoothAnimationTimeSharp((double) panel->transitionTimeMs / (double) panel->transitionLengthMs);
@ -3316,7 +3348,11 @@ int ProcessPanelMessage(EsElement *element, EsMessage *message) {
if ((panel->flags & ES_PANEL_TABLE)) {
EsHeapFree(panel->bands[0]);
EsHeapFree(panel->bands[1]);
EsHeapFree(panel->tableMemoryBase);
}
panel->bandDecorators.Free();
panel->movementItems.Free();
} else if (message->type == ES_MSG_KEY_DOWN) {
if (!(panel->flags & (ES_PANEL_TABLE | ES_PANEL_SWITCHER))
&& panel->window->focused && panel->window->focused->parent == panel) {
@ -3567,6 +3603,8 @@ void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount, const
panel->bands[1] = rows ? (EsPanelBand *) EsHeapAllocate(rowCount * sizeof(EsPanelBand), false) : nullptr;
if (columns && panel->bands[0]) EsMemoryCopy(panel->bands[0], columns, columnCount * sizeof(EsPanelBand));
if (rows && panel->bands[1]) EsMemoryCopy(panel->bands[1], rows, rowCount * sizeof(EsPanelBand));
EsHeapFree(panel->tableMemoryBase);
panel->tableMemoryBase = nullptr;
}
void EsPanelSetBandsAll(EsPanel *panel, const EsPanelBand *column, const EsPanelBand *row) {
@ -3591,6 +3629,9 @@ void EsPanelSetBandsAll(EsPanel *panel, const EsPanelBand *column, const EsPanel
}
void EsPanelTableSetChildCells(EsPanel *panel) {
EsMessageMutexCheck();
EsAssert(panel->flags & ES_PANEL_TABLE);
// The number of columns/rows should have been set by the time this function is called.
EsAssert(panel->bandCount[0] || panel->bandCount[1]);
@ -3602,6 +3643,16 @@ void EsPanelTableSetChildCells(EsPanel *panel) {
if (child->flags & ES_ELEMENT_NON_CLIENT) continue;
PanelTableSetChildCell(panel, child);
}
EsHeapFree(panel->tableMemoryBase);
panel->tableMemoryBase = nullptr;
}
void EsPanelTableAddBandDecorator(EsPanel *panel, EsPanelBandDecorator decorator) {
EsMessageMutexCheck();
EsAssert(panel->flags & ES_PANEL_TABLE);
EsAssert(decorator.axis == 0 || decorator.axis == 1);
panel->bandDecorators.Add(decorator);
}
void EsPanelSwitchTo(EsPanel *panel, EsElement *targetChild, EsTransitionType transitionType, uint32_t flags, float timeMultiplier) {

View File

@ -1965,6 +1965,12 @@ private struct EsMountPoint {
bool addedByApplication;
};
struct EsPanelBandDecorator {
uintptr_t index, repeatEvery;
uint8_t axis; // 0 for columns, 1 for rows.
const EsThemeAppearance *appearance; // Pointer must remain valid.
};
// Function pointer types.
function_pointer void EsThreadEntryCallback(EsGeneric argument);
@ -2541,6 +2547,7 @@ function EsCanvasPane *EsCanvasPaneCreate(EsElement *parent, uint64_t flags = ES
function void EsPanelSetBands(EsPanel *panel, size_t columnCount, size_t rowCount = 0, const EsPanelBand *columns = ES_NULL, const EsPanelBand *rows = ES_NULL);
function void EsPanelSetBandsAll(EsPanel *panel, const EsPanelBand *column = ES_NULL, const EsPanelBand *row = ES_NULL); // Set all the columns/rows to have the same properties. This must be called after the final number of bands has been determined/set!
function void EsPanelTableSetChildCells(EsPanel *panel); // Automatically set the child cells for items in a table. This is only necessary if the number of columns/rows is changed after adding items to a table.
function void EsPanelTableAddBandDecorator(EsPanel *panel, EsPanelBandDecorator decorator);
function void EsPanelSwitchTo(EsPanel *panel, EsElement *targetChild, EsTransitionType transitionType, uint32_t flags = ES_FLAGS_DEFAULT, float timeMultiplier = 1); // TODO More customization of transitions?
function void EsPanelStartMovementAnimation(EsPanel *panel, float timeMultiplier = 1); // TODO More customization.

View File

@ -374,6 +374,7 @@ EsElementRepaintForScroll=372
EsMemoryFaultRange=373
EsDrawBitmapScaled=374
EsRectangleCenter=375
EsPanelTableAddBandDecorator=376
EsMountPointGetVolumeInformation=377
EsListViewInvalidateAll=378
EsListViewGetFocusedItem=379

View File

@ -5,6 +5,7 @@
// - Other operators: remainder, bitwise shifts, bitwise AND/OR/XOR/NOT, ternary.
// - Enums, bitsets.
// - Named optional arguments with default values.
// - Accessing structs and functypes from inline modules.
// TODO Larger missing features:
// - Serialization.
@ -1680,6 +1681,8 @@ Node *ParseGlobalVariableOrFunctionDefinition(Tokenizer *tokenizer, bool allowGl
}
Node *ParseRootREPL(Tokenizer *tokenizer) {
// TODO Importing modules.
Node *root = (Node *) AllocateFixed(sizeof(Node));
root->type = T_ROOT;
Node **link = &root->firstChild;