diff --git a/desktop/theme.cpp b/desktop/theme.cpp index 300057b..6a0b442 100644 --- a/desktop/theme.cpp +++ b/desktop/theme.cpp @@ -853,7 +853,7 @@ void ThemeDrawBox(EsPainter *painter, EsRectangle rect, EsBuffer *data, float sc if (borders.l + borders.r > width) { float p = (float) borders.l / (borders.l + borders.r); borders.l = p * width; borders.r = (1 - p) * width; } if (borders.t + borders.b > height) { float p = (float) borders.t / (borders.t + borders.b); borders.t = p * height; borders.b = (1 - p) * height; } - if (isBlurred && !borders.l) { + if (isBlurred && (!borders.l || !borders.r || !borders.t || !borders.b)) { return; } diff --git a/res/Theme Source.dat b/res/Theme Source.dat index dfae8e7..4839f2c 100644 Binary files a/res/Theme Source.dat and b/res/Theme Source.dat differ diff --git a/res/Themes/Theme.dat b/res/Themes/Theme.dat index f084e0c..1d4a958 100644 Binary files a/res/Themes/Theme.dat and b/res/Themes/Theme.dat differ diff --git a/shared/hash_table.cpp b/shared/hash_table.cpp index f776c91..62b6ec1 100644 --- a/shared/hash_table.cpp +++ b/shared/hash_table.cpp @@ -1,20 +1,5 @@ // TODO Test deleting values in hash stores with variable length keys. -#include -#include - -#ifndef OS_ESSENCE -#include -#include -#include -#define EsHeapAllocate(a, b) ((b) ? calloc(1, (a)) : malloc((a))) -#define EsHeapReallocate(a, b, c) ((c) ? (assert(false), nullptr) : realloc((a), (b))) -#define EsHeapFree free -#define EsMemoryCopy memcpy -#define EsMemoryCompare memcmp -#define EsAssert assert -#endif - ////////////////////////////////////////// struct HashTableKey { @@ -221,6 +206,7 @@ void HashTableFree(HashTable *table, bool freeLongKeys) { table->itemCount = 0; table->slotCount = 0; table->slots = nullptr; + table->storage = nullptr; } ////////////////////////////////////////// @@ -288,10 +274,12 @@ void *HashStorePut(HashTable *table, HashStoreOptions *options, const void *key, size_t storageAllocated = ((size_t *) table->storage)[-1]; if (table->itemCount == storageAllocated) { - uint8_t *newStorage = (uint8_t *) EsHeapReallocate(table->storage - sizeof(size_t), - (options->keyBytes + options->valueBytes) * storageAllocated * 2 + sizeof(size_t), true); + size_t oldSize = (options->keyBytes + options->valueBytes) * storageAllocated; + size_t newSize = oldSize * 2; + uint8_t *newStorage = (uint8_t *) EsHeapReallocate(table->storage - sizeof(size_t), newSize + sizeof(size_t), false, nullptr); if (!newStorage) return nullptr; newStorage += sizeof(size_t); + EsMemoryZero(newStorage + oldSize, newSize - oldSize); if (newStorage != table->storage) { for (uintptr_t i = 0; i < table->slotCount; i++) { @@ -400,7 +388,7 @@ struct HashStore { ////////////////////////////////////////// -#ifndef OS_ESSENCE +#if 0 #include #include "stb_ds.h" diff --git a/util/designer/designer.c b/util/designer/designer.c index e63adb6..590c7d0 100644 --- a/util/designer/designer.c +++ b/util/designer/designer.c @@ -1632,6 +1632,480 @@ void ActionExport(void *_unused) { } } +// ------------------- Exporting to Designer2 ------------------- + +enum PropertyType { + PROP_NONE, + PROP_COLOR, + PROP_INT, + PROP_OBJECT, + PROP_FLOAT, +}; + +typedef struct Property2 { + uint8_t type; +#define PROPERTY_NAME_SIZE (31) + char cName[PROPERTY_NAME_SIZE]; + + union { + int32_t integer; + uint64_t object; + float floating; + }; +} Property2; + +enum ObjectType { + OBJ_NONE, + + OBJ_STYLE, + OBJ_COMMENT, + OBJ_INSTANCE, + + OBJ_VAR_COLOR = 0x40, + OBJ_VAR_INT, + OBJ_VAR_TEXT_STYLE, + OBJ_VAR_ICON_STYLE, + OBJ_VAR_CONTOUR_STYLE, + + OBJ_PAINT_OVERWRITE = 0x60, + OBJ_PAINT_LINEAR_GRADIENT, + OBJ_PAINT_RADIAL_GRADIENT, + + OBJ_LAYER_BOX = 0x80, + OBJ_LAYER_METRICS, + OBJ_LAYER_TEXT, + OBJ_LAYER_GROUP, + OBJ_LAYER_PATH, + + OBJ_MOD_COLOR = 0xC0, + OBJ_MOD_MULTIPLY, +}; + +typedef struct Object2 { + uint8_t type; +#define OBJECT_NAME_SIZE (46) + char cName[OBJECT_NAME_SIZE]; +#define OBJECT_IS_SELECTED (1 << 0) +#define OBJECT_IN_PROTOTYPE (1 << 1) + uint8_t flags; + uint64_t id; + Property2 *properties; +} Object2; + +void ObjectAddIntegerProperty(Object2 *object, const char *cName, int32_t value) { + Property2 property = { 0 }; + property.type = PROP_INT; + strcpy(property.cName, cName); + property.integer = value; + arrput(object->properties, property); +} + +void ObjectAddColorProperty(Object2 *object, const char *cName, uint32_t value) { + Property2 property = { 0 }; + property.type = PROP_COLOR; + strcpy(property.cName, cName); + property.integer = value; + arrput(object->properties, property); +} + +void ObjectAddFloatProperty(Object2 *object, const char *cName, float value) { + Property2 property = { 0 }; + property.type = PROP_FLOAT; + strcpy(property.cName, cName); + property.floating = value; + arrput(object->properties, property); +} + +void ObjectAddObjectProperty(Object2 *object, const char *cName, uint64_t value) { + Property2 property = { 0 }; + property.type = PROP_OBJECT; + strcpy(property.cName, cName); + property.object = value; + arrput(object->properties, property); +} + +uint64_t ExportPaint2(Paint *paint, int *x, int y, Object2 **objects, uint64_t *idAllocator) { + char cPropertyName[PROPERTY_NAME_SIZE]; + + if (!paint->tag) { + return 0; + } else if (paint->tag == Paint_solid + 1) { + return ColorLookupPointer(paint->solid.color)->object2ID; + } else if (paint->tag == Paint_linearGradient + 1) { + bool constantColor = true; + + for (uintptr_t i = 1; i < arrlenu(paint->linearGradient.stops); i++) { + if (paint->linearGradient.stops[i].color != paint->linearGradient.stops[0].color) { + constantColor = false; + break; + } + } + + if (constantColor) { + return ColorLookupPointer(paint->linearGradient.stops[0].color)->object2ID; + } + + Object2 object = { .type = OBJ_PAINT_LINEAR_GRADIENT, .id = ++(*idAllocator) }; + + ObjectAddIntegerProperty(&object, "_graphX", *x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddFloatProperty(&object, "transformX", paint->linearGradient.transformX); + ObjectAddFloatProperty(&object, "transformY", paint->linearGradient.transformY); + ObjectAddFloatProperty(&object, "transformStart", paint->linearGradient.transformStart); + ObjectAddIntegerProperty(&object, "repeatMode", paint->linearGradient.repeat); + ObjectAddIntegerProperty(&object, "useGammaInterpolation", paint->linearGradient.useGammaInterpolation); + ObjectAddIntegerProperty(&object, "useSystemColor", paint->linearGradient.useSystemHue); + ObjectAddIntegerProperty(&object, "stops_count", arrlenu(paint->linearGradient.stops)); + + for (uintptr_t i = 0; i < arrlenu(paint->linearGradient.stops); i++) { + sprintf(cPropertyName, "stops_%d_position", (int) i); + ObjectAddIntegerProperty(&object, cPropertyName, paint->linearGradient.stops[i].position); + sprintf(cPropertyName, "stops_%d_color", (int) i); + ObjectAddObjectProperty(&object, cPropertyName, ColorLookupPointer(paint->linearGradient.stops[i].color)->object2ID); + } + + *x += 100; + arrput(*objects, object); + return object.id; + } else if (paint->tag == Paint_radialGradient + 1) { + Object2 object = { .type = OBJ_PAINT_RADIAL_GRADIENT, .id = ++(*idAllocator) }; + + ObjectAddIntegerProperty(&object, "_graphX", *x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddFloatProperty(&object, "transform0", paint->radialGradient.transform0); + ObjectAddFloatProperty(&object, "transform1", paint->radialGradient.transform1); + ObjectAddFloatProperty(&object, "transform2", paint->radialGradient.transform2); + ObjectAddFloatProperty(&object, "transform3", paint->radialGradient.transform3); + ObjectAddFloatProperty(&object, "transform4", paint->radialGradient.transform4); + ObjectAddFloatProperty(&object, "transform5", paint->radialGradient.transform5); + ObjectAddIntegerProperty(&object, "repeatMode", paint->radialGradient.repeat); + ObjectAddIntegerProperty(&object, "useGammaInterpolation", paint->radialGradient.useGammaInterpolation); + ObjectAddIntegerProperty(&object, "stops_count", arrlenu(paint->radialGradient.stops)); + + for (uintptr_t i = 0; i < arrlenu(paint->radialGradient.stops); i++) { + sprintf(cPropertyName, "stops_%d_position", (int) i); + ObjectAddIntegerProperty(&object, cPropertyName, paint->radialGradient.stops[i].position); + sprintf(cPropertyName, "stops_%d_color", (int) i); + ObjectAddObjectProperty(&object, cPropertyName, ColorLookupPointer(paint->radialGradient.stops[i].color)->object2ID); + } + + *x += 100; + arrput(*objects, object); + return object.id; + } else if (paint->tag == Paint_overwrite + 1) { + Object2 object = { .type = OBJ_PAINT_OVERWRITE, .id = ++(*idAllocator) }; + ObjectAddIntegerProperty(&object, "_graphX", *x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddObjectProperty(&object, "color", ColorLookupPointer(paint->overwrite.color)->object2ID); + *x += 100; + arrput(*objects, object); + return object.id; + } else { + assert(false); + return 0; + } +} + +uint64_t ExportFillMode2(PathFillMode *fill, int *x, int y, Object2 **objects, uint64_t *idAllocator) { + if (fill->tag == PathFillMode_solid + 1) { + return 0; + } else if (fill->tag == PathFillMode_contour + 1) { + Object2 object = { .type = OBJ_VAR_CONTOUR_STYLE, .id = ++(*idAllocator) }; + ObjectAddIntegerProperty(&object, "_graphX", *x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddIntegerProperty(&object, "internalWidth", fill->contour.internalWidth); + ObjectAddIntegerProperty(&object, "externalWidth", fill->contour.externalWidth); + ObjectAddIntegerProperty(&object, "integerWidthsOnly", fill->contour.integerWidthsOnly); + ObjectAddIntegerProperty(&object, "joinMode", fill->contour.joinMode == JOIN_MODE_ROUND ? RAST_LINE_JOIN_ROUND : RAST_LINE_JOIN_MITER); + ObjectAddIntegerProperty(&object, "capMode", fill->contour.capMode == CAP_MODE_FLAT ? RAST_LINE_CAP_FLAT + : fill->contour.capMode == CAP_MODE_ROUND ? RAST_LINE_CAP_ROUND : RAST_LINE_CAP_SQUARE); + ObjectAddFloatProperty(&object, "miterLimit", fill->contour.joinMode == JOIN_MODE_BEVEL ? 0.0f : fill->contour.miterLimit); + *x += 100; + arrput(*objects, object); + return object.id; + } else { + assert(false); + return 0; + } +} + +void ActionExportDesigner2(void *cp) { + // TODO Exporting sequences. + // TODO Inherited text styles. + // TODO Merging identical layers and styles. + + Object2 *objects = NULL; + uint64_t objectIDAllocator = 0; + char cPropertyName[PROPERTY_NAME_SIZE]; + + int y = 0; + + // Colors. + + for (uintptr_t i = 0; i < arrlenu(styleSet.colors); i++) { + Object2 object = { .type = OBJ_VAR_COLOR, .id = ++objectIDAllocator }; + snprintf(object.cName, sizeof(object.cName), "%.*s", (int) styleSet.colors[i]->key.byteCount, (const char *) styleSet.colors[i]->key.buffer); + ObjectAddIntegerProperty(&object, "_graphX", (i % 10) * 180); + ObjectAddIntegerProperty(&object, "_graphY", (i / 10) * 100 + y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddColorProperty(&object, "color", styleSet.colors[i]->value); + ObjectAddIntegerProperty(&object, "isExported", 0); + arrput(objects, object); + styleSet.colors[i]->object2ID = object.id; + } + + y += (arrlenu(styleSet.colors) / 10) * 100 + 200; + + // Constants. + + for (uintptr_t i = 0; i < arrlenu(styleSet.constants); i++) { + char value[64]; + snprintf(value, sizeof(value), "%.*s", (int) styleSet.constants[i]->value.byteCount, (const char *) styleSet.constants[i]->value.buffer); + bool isColor = value[0] == '0' && value[1] == 'x'; + Object2 object = { .type = isColor ? OBJ_VAR_COLOR : OBJ_VAR_INT, .id = ++objectIDAllocator }; + snprintf(object.cName, sizeof(object.cName), "%.*s", (int) styleSet.constants[i]->key.byteCount, (const char *) styleSet.constants[i]->key.buffer); + ObjectAddIntegerProperty(&object, "_graphX", (i % 5) * 360); + ObjectAddIntegerProperty(&object, "_graphY", (i / 5) * 100 + y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + if (isColor) ObjectAddColorProperty(&object, "color", strtol(value, NULL, 0)); + else ObjectAddIntegerProperty(&object, "value", strtol(value, NULL, 0)); + ObjectAddIntegerProperty(&object, "isScaled", styleSet.constants[i]->scale); + ObjectAddIntegerProperty(&object, "isExported", 1); + arrput(objects, object); + } + + y += (arrlenu(styleSet.constants) / 5) * 100 + 200; + + // Styles. + + int x0 = 180 * 10 + 200; + y = 0; + + for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) { + int x = x0; + Style *style = styleSet.styles[i]; + + Object2 layerGroup = { .type = OBJ_LAYER_GROUP, .id = ++objectIDAllocator }; + Object2 metrics = { 0 }, textStyle = { 0 }, iconStyle = { 0 }; + int32_t layerCount = 0; + + for (uintptr_t i = 0; i < arrlenu(style->layers); i++) { + Layer *layer = LayerLookup(style->layers[i]); + bool addToLayerGroup = false, addToObjects = false; + Object2 object = { 0 }; + + if (layer->base.tag == LayerBase_box + 1) { + object.type = OBJ_LAYER_BOX, object.id = ++objectIDAllocator; + LayerBox *box = &layer->base.box; + ObjectAddIntegerProperty(&object, "borders0", box->borders.l); + ObjectAddIntegerProperty(&object, "borders1", box->borders.r); + ObjectAddIntegerProperty(&object, "borders2", box->borders.t); + ObjectAddIntegerProperty(&object, "borders3", box->borders.b); + ObjectAddIntegerProperty(&object, "corners0", box->corners.tl); + ObjectAddIntegerProperty(&object, "corners1", box->corners.tr); + ObjectAddIntegerProperty(&object, "corners2", box->corners.bl); + ObjectAddIntegerProperty(&object, "corners3", box->corners.br); + ObjectAddIntegerProperty(&object, "isBlurred", box->blurred); + ObjectAddIntegerProperty(&object, "autoCorners", box->autoCorners); + ObjectAddIntegerProperty(&object, "autoBorders", box->autoBorders); + ObjectAddIntegerProperty(&object, "shadowHiding", box->shadowHiding); + ObjectAddObjectProperty(&object, "mainPaint", ExportPaint2(&box->mainPaint, &x, y, &objects, &objectIDAllocator)); + ObjectAddObjectProperty(&object, "borderPaint", ExportPaint2(&box->borderPaint, &x, y, &objects, &objectIDAllocator)); + addToLayerGroup = true; + addToObjects = true; + } else if (layer->base.tag == LayerBase_text + 1) { + object.type = OBJ_LAYER_TEXT, object.id = ++objectIDAllocator; + ObjectAddObjectProperty(&object, "color", ColorLookupPointer(layer->base.text.color)->object2ID); + ObjectAddIntegerProperty(&object, "blur", layer->base.text.blur); + addToLayerGroup = true; + addToObjects = true; + } else if (layer->base.tag == LayerBase_path + 1) { + object.type = OBJ_LAYER_PATH, object.id = ++objectIDAllocator; + LayerPath *path = &layer->base.path; + ObjectAddIntegerProperty(&object, "pathFillEvenOdd", path->evenOdd); + ObjectAddIntegerProperty(&object, "pathClosed", path->closed); + ObjectAddIntegerProperty(&object, "alpha", path->alpha); + ObjectAddIntegerProperty(&object, "points_count", arrlenu(path->points)); + ObjectAddIntegerProperty(&object, "fills_count", arrlenu(path->fills)); + + for (uintptr_t i = 0; i < arrlenu(path->points); i++) { + sprintf(cPropertyName, "points_%d_x0", (int) i); + ObjectAddFloatProperty(&object, cPropertyName, path->points[i].x0); + sprintf(cPropertyName, "points_%d_y0", (int) i); + ObjectAddFloatProperty(&object, cPropertyName, path->points[i].y0); + sprintf(cPropertyName, "points_%d_x1", (int) i); + ObjectAddFloatProperty(&object, cPropertyName, path->points[i].x1); + sprintf(cPropertyName, "points_%d_y1", (int) i); + ObjectAddFloatProperty(&object, cPropertyName, path->points[i].y1); + sprintf(cPropertyName, "points_%d_x2", (int) i); + ObjectAddFloatProperty(&object, cPropertyName, path->points[i].x2); + sprintf(cPropertyName, "points_%d_y2", (int) i); + ObjectAddFloatProperty(&object, cPropertyName, path->points[i].y2); + } + + for (uintptr_t i = 0; i < arrlenu(path->fills); i++) { + sprintf(cPropertyName, "fills_%d_paint", (int) i); + ObjectAddObjectProperty(&object, cPropertyName, ExportPaint2(&path->fills[i].paint, &x, y, &objects, &objectIDAllocator)); + sprintf(cPropertyName, "fills_%d_mode", (int) i); + ObjectAddObjectProperty(&object, cPropertyName, ExportFillMode2(&path->fills[i].mode, &x, y, &objects, &objectIDAllocator)); + } + + addToLayerGroup = true; + addToObjects = true; + } else { + object.type = OBJ_LAYER_METRICS, object.id = ++objectIDAllocator; + LayerMetrics *m = &layer->base.metrics; + ObjectAddIntegerProperty(&object, "clipEnabled", m->clipEnabled); + ObjectAddIntegerProperty(&object, "wrapText", m->wrapText); + ObjectAddIntegerProperty(&object, "ellipsis", m->ellipsis); + ObjectAddIntegerProperty(&object, "insets0", m->insets.l); + ObjectAddIntegerProperty(&object, "insets1", m->insets.r); + ObjectAddIntegerProperty(&object, "insets2", m->insets.t); + ObjectAddIntegerProperty(&object, "insets3", m->insets.b); + ObjectAddIntegerProperty(&object, "clipInsets0", m->clipInsets.l); + ObjectAddIntegerProperty(&object, "clipInsets1", m->clipInsets.r); + ObjectAddIntegerProperty(&object, "clipInsets2", m->clipInsets.t); + ObjectAddIntegerProperty(&object, "clipInsets3", m->clipInsets.b); + ObjectAddIntegerProperty(&object, "preferredWidth", m->preferredSize.width); + ObjectAddIntegerProperty(&object, "preferredHeight", m->preferredSize.height); + ObjectAddIntegerProperty(&object, "minimumWidth", m->minimumSize.width); + ObjectAddIntegerProperty(&object, "minimumHeight", m->minimumSize.height); + ObjectAddIntegerProperty(&object, "maximumWidth", m->maximumSize.width); + ObjectAddIntegerProperty(&object, "maximumHeight", m->maximumSize.height); + ObjectAddIntegerProperty(&object, "gapMajor", m->gaps.major); + ObjectAddIntegerProperty(&object, "gapMinor", m->gaps.minor); + ObjectAddIntegerProperty(&object, "gapWrap", m->gaps.wrap); + ObjectAddIntegerProperty(&object, "cursor", m->cursor); + ObjectAddIntegerProperty(&object, "horizontalTextAlign", m->textHorizontalAlign + 1); + ObjectAddIntegerProperty(&object, "verticalTextAlign", m->textVerticalAlign + 1); + assert(!m->globalOffset.l && !m->globalOffset.r && !m->globalOffset.t && !m->globalOffset.b); + addToObjects = true; + metrics = object; + + textStyle.type = OBJ_VAR_TEXT_STYLE, textStyle.id = ++objectIDAllocator; + ObjectAddIntegerProperty(&textStyle, "_graphX", x); + ObjectAddIntegerProperty(&textStyle, "_graphY", y); + ObjectAddIntegerProperty(&textStyle, "_graphW", 80); + ObjectAddIntegerProperty(&textStyle, "_graphH", 60); + ObjectAddObjectProperty(&textStyle, "textColor", ColorLookupPointer(m->textColor)->object2ID); + ObjectAddObjectProperty(&textStyle, "selectedBackground", ColorLookupPointer(m->selectedBackground)->object2ID); + ObjectAddObjectProperty(&textStyle, "selectedText", ColorLookupPointer(m->selectedText)->object2ID); + ObjectAddIntegerProperty(&textStyle, "textSize", m->textSize); + ObjectAddIntegerProperty(&textStyle, "fontWeight", m->fontWeight); + ObjectAddIntegerProperty(&textStyle, "isItalic", m->italic); + ObjectAddIntegerProperty(&textStyle, "fontFamily", m->fontFamily == FONT_FAMILY_MONO ? 0xFFFD : 0xFFFF); + arrput(objects, textStyle); + x += 100; + + iconStyle.type = OBJ_VAR_ICON_STYLE, iconStyle.id = ++objectIDAllocator; + ObjectAddIntegerProperty(&iconStyle, "_graphX", x); + ObjectAddIntegerProperty(&iconStyle, "_graphY", y); + ObjectAddIntegerProperty(&iconStyle, "_graphW", 80); + ObjectAddIntegerProperty(&iconStyle, "_graphH", 60); + ObjectAddIntegerProperty(&iconStyle, "iconSize", m->iconSize); + ObjectAddObjectProperty(&iconStyle, "iconColor", ColorLookupPointer(m->iconColor)->object2ID); + arrput(objects, iconStyle); + x += 100; + } + + if (addToLayerGroup) { + sprintf(cPropertyName, "layers_%d_layer", layerCount); + ObjectAddObjectProperty(&layerGroup, cPropertyName, object.id); + sprintf(cPropertyName, "layers_%d_offset0", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.l); + sprintf(cPropertyName, "layers_%d_offset1", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.r); + sprintf(cPropertyName, "layers_%d_offset2", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.t); + sprintf(cPropertyName, "layers_%d_offset3", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->offset.b); + sprintf(cPropertyName, "layers_%d_position0", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.l); + sprintf(cPropertyName, "layers_%d_position1", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.r); + sprintf(cPropertyName, "layers_%d_position2", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.t); + sprintf(cPropertyName, "layers_%d_position3", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->position.b); + sprintf(cPropertyName, "layers_%d_mode", layerCount); + ObjectAddIntegerProperty(&layerGroup, cPropertyName, layer->mode); + layerCount++; + } + + if (addToObjects) { + ObjectAddIntegerProperty(&object, "_graphX", x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + arrput(objects, object); + x += 100; + } + } + + { + Object2 object = layerGroup; + ObjectAddIntegerProperty(&object, "_graphX", x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddIntegerProperty(&object, "layers_count", layerCount); + arrput(objects, object); + x += 100; + } + + { + Object2 object = { .type = OBJ_STYLE, .id = ++objectIDAllocator }; + snprintf(object.cName, sizeof(object.cName), "%.*s", (int) style->name.byteCount, (const char *) style->name.buffer); + ObjectAddIntegerProperty(&object, "_graphX", x); + ObjectAddIntegerProperty(&object, "_graphY", y); + ObjectAddIntegerProperty(&object, "_graphW", 80); + ObjectAddIntegerProperty(&object, "_graphH", 60); + ObjectAddIntegerProperty(&object, "isPublic", style->publicStyle); + ObjectAddObjectProperty(&object, "appearance", layerGroup.id); + ObjectAddObjectProperty(&object, "metrics", metrics.id); + ObjectAddObjectProperty(&object, "textStyle", textStyle.id); + ObjectAddObjectProperty(&object, "iconStyle", iconStyle.id); + arrput(objects, object); + x += 100; + } + + y += 200; + } + + // Saving. + + FILE *f = fopen("bin/designer2.dat", "wb"); + uint32_t version = 1; + fwrite(&version, 1, sizeof(uint32_t), f); + uint32_t objectCount = arrlenu(objects); + fwrite(&objectCount, 1, sizeof(uint32_t), f); + fwrite(&objectIDAllocator, 1, sizeof(uint64_t), f); + + for (uintptr_t i = 0; i < arrlenu(objects); i++) { + Object2 copy = objects[i]; + uint32_t propertyCount = arrlenu(copy.properties); + copy.properties = NULL; + fwrite(©, 1, sizeof(Object2), f); + fwrite(&propertyCount, 1, sizeof(uint32_t), f); + fwrite(objects[i].properties, 1, sizeof(Property2) * propertyCount, f); + arrfree(objects[i].properties); + assert(objects[i].id); + } + + fclose(f); + arrfree(objects); +} + // ------------------- Preview canvas ------------------- float Smooth(float x) { @@ -5334,6 +5808,7 @@ int main(int argc, char **argv) UIButtonCreate(0, 0, "Save", -1)->invoke = ActionSave; UIButtonCreate(0, 0, "Load", -1)->invoke = ActionLoad; UIButtonCreate(0, 0, "Export", -1)->invoke = ActionExport; + UIButtonCreate(0, 0, "Export for Designer2", -1)->invoke = ActionExportDesigner2; UIButtonCreate(0, 0, "Import font", -1)->invoke = ActionImportFont; UIParentPop(); diff --git a/util/designer/designer.rf b/util/designer/designer.rf index b4abc3d..1dd8098 100644 --- a/util/designer/designer.rf +++ b/util/designer/designer.rf @@ -350,6 +350,7 @@ struct Color Color_Type RfStructOp { rfData RfData key; rfU32 uint32_t value; rfU32 uint32_t id; + rfNone uint64_t object2ID; }; struct StyleSet StyleSet_Type StyleSetOp { diff --git a/util/designer2.cpp b/util/designer2.cpp index dbf8810..9da21c2 100644 --- a/util/designer2.cpp +++ b/util/designer2.cpp @@ -21,6 +21,8 @@ // Prototyping display: previewing state transitions. // TODO Additional features: +// Scrollbars? +// Icons for different object types (especially color overwrite objects). // Fix moving/resizing objects when zoomed in. // Path layers: dashed contours. // Picking objects: only highlight objects with an applicable type. @@ -71,6 +73,7 @@ #define EsHeap void #define EsMemoryCopy memcpy #define EsMemoryCopyReverse memmove +#define EsMemoryCompare memcmp #define EsMemoryZero(a, b) memset((a), 0, (b)) #define EsPanic(...) UI_ASSERT(false) #define EsRectangle UIRectangle @@ -153,6 +156,7 @@ void SetBit(uint32_t *value, uint32_t bit, bool on) { #include "../shared/math.cpp" #include "../shared/array.cpp" +#include "../shared/hash_table.cpp" #include "../desktop/renderer.cpp" #include "../desktop/theme.cpp" @@ -329,14 +333,14 @@ Array undoStack; Array redoStack; bool documentModified; uint64_t selectedObjectID; +HashStore objectLookup; // Document state: Array objects; uint64_t objectIDAllocator; Object *ObjectFind(uint64_t id) { - // TODO Use a hash table. - +#if 0 for (uintptr_t i = 0; i < objects.Length(); i++) { if (objects[i].id == id) { return &objects[i]; @@ -344,6 +348,13 @@ Object *ObjectFind(uint64_t id) { } return nullptr; +#else + if (!id) return nullptr; + uint64_t *index = objectLookup.Get(&id); + Object *object = index ? &objects[*index] : nullptr; + if (object) assert(object->id == id); + return object; +#endif } void ObjectSetSelected(uint64_t id, bool removeSelectedFlagFromPreviousSelection = true) { @@ -399,7 +410,9 @@ bool ObjectMatchesPreviewState(Object *object) { } Property *PropertyFindOrInherit(bool first, Object *object, const char *cName, uint8_t type = 0) { - while (object) { + uintptr_t depth = 0; + + while (object && (depth++ < 100)) { if (first || !ObjectIsConditional(object) || (canvas->previewStateActive && ObjectMatchesPreviewState(object))) { // Return the value if the object has this property. Property *property = PropertyFind(object, cName); @@ -432,6 +445,14 @@ Object *PropertyFindOrInheritReadObject(bool first, Object *object, const char * return property ? ObjectFind(property->object) : nullptr; } +void ObjectLookupRebuild() { + objectLookup.Free(); + + for (uintptr_t i = 0; i < objects.Length(); i++) { + *objectLookup.Put(&objects[i].id) = i; + } +} + void DocumentSave(void *) { #ifdef OS_ESSENCE EsBuffer buffer = { .canGrow = 1 }; @@ -491,6 +512,8 @@ void DocumentLoad() { objects.Add(object); } + ObjectLookupRebuild(); + #ifdef OS_ESSENCE EsHeapFree(buffer.out); #else @@ -506,6 +529,7 @@ void DocumentFree() { objects.Free(); undoStack.Free(); redoStack.Free(); + ObjectLookupRebuild(); } void DocumentApplyStep(Step step, StepApplyMode mode = STEP_APPLY_NORMAL) { @@ -567,6 +591,7 @@ void DocumentApplyStep(Step step, StepApplyMode mode = STEP_APPLY_NORMAL) { InspectorPopulate(); } else if (step.type == STEP_ADD_OBJECT) { objects.Add(step.object); + ObjectLookupRebuild(); UIElementRepaint(canvas, nullptr); step.objectID = step.object.id; step.type = STEP_DELETE_OBJECT; @@ -585,6 +610,7 @@ void DocumentApplyStep(Step step, StepApplyMode mode = STEP_APPLY_NORMAL) { } } + ObjectLookupRebuild(); step.object.flags = 0; UIElementRefresh(canvas); InspectorPopulate(); @@ -604,6 +630,7 @@ void DocumentApplyStep(Step step, StepApplyMode mode = STEP_APPLY_NORMAL) { } UI_ASSERT(found); + ObjectLookupRebuild(); UIElementRefresh(canvas); } else { UI_ASSERT(false); @@ -2030,6 +2057,12 @@ void CanvasDrawLayerFromData(UIPainter *painter, UIRectangle bounds, EsBuffer da } void CanvasDrawColorSwatch(Object *object, UIRectangle bounds, UIPainter *painter) { + for (int32_t y = bounds.t, y0 = 0; y < bounds.b; y += 15, y0++) { + for (int32_t x = bounds.l, x0 = 0; x < bounds.r; x += 15, x0++) { + UIDrawBlock(painter, UIRectangleIntersection(UI_RECT_4(x, x + 15, y, y + 15), bounds), ((x0 ^ y0) & 1) ? 0xFF808080 : 0xFFC0C0C0); + } + } + uint8_t buffer[4096]; EsBuffer data = { .out = buffer, .bytes = sizeof(buffer) }; ThemeLayer layer = { .position = { .r = 100, .b = 100 }, .type = THEME_LAYER_BOX }; @@ -2450,13 +2483,16 @@ int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) { canvas->lastPanPointX = element->window->cursorX; canvas->lastPanPointY = element->window->cursorY; UIElementRefresh(canvas); + } else if (message == UI_MSG_MOUSE_WHEEL && !element->window->ctrl) { + canvas->panY += di / canvas->zoom; + UIElementRefresh(canvas); } else if (message == UI_MSG_MOUSE_WHEEL && element->window->ctrl) { int divisions = -di / 72; float factor = 1, perDivision = 1.2f; while (divisions > 0) factor *= perDivision, divisions--; while (divisions < 0) factor /= perDivision, divisions++; if (canvas->zoom * factor > 4) factor = 4 / canvas->zoom; - if (canvas->zoom * factor < 1) factor = 1 / canvas->zoom; + if (canvas->zoom * factor < 0.1) factor = 0.1 / canvas->zoom; int mx = element->window->cursorX - element->bounds.l; int my = element->window->cursorY - element->bounds.t; canvas->zoom *= factor; @@ -2507,6 +2543,14 @@ void CanvasSwitchView(void *) { UIElementRefresh(canvas); } +void CanvasZoom100(void *) { + float factor = 1.0f / canvas->zoom; + canvas->zoom *= factor; + canvas->panX -= UI_RECT_WIDTH(canvas->bounds) / 2 / canvas->zoom * (1 - factor); + canvas->panY -= UI_RECT_HEIGHT(canvas->bounds) / 2 / canvas->zoom * (1 - factor); + UIElementRefresh(canvas); +} + ////////////////////////////////////////////////////////////// void ObjectAddInternal(Object object) { @@ -2587,7 +2631,7 @@ void ObjectAddCommand(void *) { } void ObjectAddInstanceCommand(void *) { - UIMenu *menu = UIMenuCreate(window->pressed, UI_MENU_NO_SCROLL | UI_MENU_PLACE_ABOVE); + UIMenu *menu = UIMenuCreate(window->pressed, 0); for (uintptr_t i = 0; i < objects.Length(); i++) { if (objects[i].type == OBJ_STYLE) { @@ -2700,14 +2744,11 @@ int main() { UIButtonCreate(0, UI_BUTTON_SMALL, "Add instance \x18", -1)->invoke = ObjectAddInstanceCommand; UIParentPop(); - canvas->resizeHandles[0] = UIElementCreate(sizeof(UIElement), canvas, 0, ResizeHandleMessage, "Resize handle"); - canvas->resizeHandles[1] = UIElementCreate(sizeof(UIElement), canvas, 0, ResizeHandleMessage, "Resize handle"); - canvas->resizeHandles[2] = UIElementCreate(sizeof(UIElement), canvas, 0, ResizeHandleMessage, "Resize handle"); - canvas->resizeHandles[3] = UIElementCreate(sizeof(UIElement), canvas, 0, ResizeHandleMessage, "Resize handle"); - canvas->resizeHandles[0]->cp = (void *) (uintptr_t) 0; - canvas->resizeHandles[1]->cp = (void *) (uintptr_t) 1; - canvas->resizeHandles[2]->cp = (void *) (uintptr_t) 2; - canvas->resizeHandles[3]->cp = (void *) (uintptr_t) 3; + for (uintptr_t i = 0; i < 4; i++) { + canvas->resizeHandles[i] = UIElementCreate(sizeof(UIElement), canvas, 0, ResizeHandleMessage, "Resize handle"); + canvas->resizeHandles[i]->cp = (void *) (uintptr_t) i; + } + canvas->zoom = canvas->swapZoom = 1.0f; canvas->previewPrimaryState = THEME_PRIMARY_STATE_IDLE; @@ -2715,6 +2756,7 @@ int main() { UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('Y'), 1 /* ctrl */, 0, 0, DocumentRedoStep, 0)); UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('D'), 1 /* ctrl */, 0, 0, ObjectDuplicateCommand, 0)); UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_FKEY(2), 0, 0, 0, CanvasSwitchView, 0)); + UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_FKEY(1), 0, 0, 0, CanvasZoom100, 0)); UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_DELETE, 0, 0, 0, ObjectDeleteCommand, 0)); #ifdef OS_ESSENCE diff --git a/util/luigi.h b/util/luigi.h index b54336d..524d470 100644 --- a/util/luigi.h +++ b/util/luigi.h @@ -1133,9 +1133,10 @@ bool UIDrawLine(UIPainter *painter, int x0, int y0, int x1, int y1, uint32_t col // Apply the clip. UIRectangle c = painter->clip; + if (!UI_RECT_VALID(c)) return false; int dx = x1 - x0, dy = y1 - y0; const int p[4] = { -dx, dx, -dy, dy }; - const int q[4] = { x0 - c.l, c.r - x0, y0 - c.t, c.b - y0 }; + const int q[4] = { x0 - c.l, c.r - 1 - x0, y0 - c.t, c.b - 1 - y0 }; float t0 = 0.0f, t1 = 1.0f; // How far along the line the points end up. for (int i = 0; i < 4; i++) {