diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 48a498a..461bae8 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -2397,10 +2397,11 @@ void DesktopSetup() { // Load the theme bitmap. if (!desktop.setupDesktopUIComplete) { + size_t cursorsBitmapBytes; + const void *cursorsBitmap = EsBundleFind(&bundleDesktop, EsLiteral("Cursors.png"), &cursorsBitmapBytes); EsHandle handle = EsMemoryOpen(ES_THEME_CURSORS_WIDTH * ES_THEME_CURSORS_HEIGHT * 4, EsLiteral(ES_THEME_CURSORS_NAME), ES_FLAGS_DEFAULT); void *destination = EsObjectMap(handle, 0, ES_THEME_CURSORS_WIDTH * ES_THEME_CURSORS_HEIGHT * 4, ES_MAP_OBJECT_READ_WRITE); - LoadImage(theming.system.in + theming.system.bytes - theming.header->bitmapBytes, theming.header->bitmapBytes, - destination, ES_THEME_CURSORS_WIDTH, ES_THEME_CURSORS_HEIGHT, true); + LoadImage(cursorsBitmap, cursorsBitmapBytes, destination, ES_THEME_CURSORS_WIDTH, ES_THEME_CURSORS_HEIGHT, true); EsObjectUnmap(destination); EsHandleClose(handle); } diff --git a/desktop/gui.cpp b/desktop/gui.cpp index e353606..0cee3f8 100644 --- a/desktop/gui.cpp +++ b/desktop/gui.cpp @@ -6177,7 +6177,6 @@ EsThemeMetrics EsElementGetMetrics(EsElement *element) { #define RECTANGLE_8_TO_ES_RECTANGLE(x) { (int32_t) (x).l, (int32_t) (x).r, (int32_t) (x).t, (int32_t) (x).b } m.insets = RECTANGLE_8_TO_ES_RECTANGLE(metrics->insets); m.clipInsets = RECTANGLE_8_TO_ES_RECTANGLE(metrics->clipInsets); - m.globalOffset = RECTANGLE_8_TO_ES_RECTANGLE(metrics->globalOffset); m.clipEnabled = metrics->clipEnabled; m.cursor = metrics->cursor; m.preferredWidth = metrics->preferredWidth; @@ -6199,7 +6198,6 @@ EsThemeMetrics EsElementGetMetrics(EsElement *element) { m.fontWeight = metrics->fontWeight; m.iconSize = metrics->iconSize; m.isItalic = metrics->isItalic; - m.ellipsis = metrics->ellipsis; return m; } diff --git a/desktop/os.header b/desktop/os.header index 5a5b7ff..30c26e4 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -593,35 +593,29 @@ define ES_CELL_V_CENTER (ES_CELL_V_TOP | ES_CELL_V_BOTTOM) // Mask bits for EsThemeMetrics: define ES_THEME_METRICS_INSETS (1 << 0) define ES_THEME_METRICS_CLIP_INSETS (1 << 1) -define ES_THEME_METRICS_GLOBAL_OFFSET (1 << 2) -define ES_THEME_METRICS_CLIP_ENABLED (1 << 3) -define ES_THEME_METRICS_CURSOR (1 << 4) -define ES_THEME_METRICS_ENTRANCE_TRANSITION (1 << 5) -define ES_THEME_METRICS_EXIT_TRANSITION (1 << 6) -define ES_THEME_METRICS_ENTRANCE_DURATION (1 << 7) -define ES_THEME_METRICS_EXIT_DURATION (1 << 8) -define ES_THEME_METRICS_PREFERRED_WIDTH (1 << 9) -define ES_THEME_METRICS_PREFERRED_HEIGHT (1 << 10) -define ES_THEME_METRICS_MINIMUM_WIDTH (1 << 11) -define ES_THEME_METRICS_MINIMUM_HEIGHT (1 << 12) -define ES_THEME_METRICS_MAXIMUM_WIDTH (1 << 13) -define ES_THEME_METRICS_MAXIMUM_HEIGHT (1 << 14) -define ES_THEME_METRICS_GAP_MAJOR (1 << 15) -define ES_THEME_METRICS_GAP_MINOR (1 << 16) -define ES_THEME_METRICS_GAP_WRAP (1 << 17) +define ES_THEME_METRICS_CLIP_ENABLED (1 << 2) +define ES_THEME_METRICS_CURSOR (1 << 3) +define ES_THEME_METRICS_PREFERRED_WIDTH (1 << 4) +define ES_THEME_METRICS_PREFERRED_HEIGHT (1 << 5) +define ES_THEME_METRICS_MINIMUM_WIDTH (1 << 6) +define ES_THEME_METRICS_MINIMUM_HEIGHT (1 << 7) +define ES_THEME_METRICS_MAXIMUM_WIDTH (1 << 8) +define ES_THEME_METRICS_MAXIMUM_HEIGHT (1 << 9) +define ES_THEME_METRICS_GAP_MAJOR (1 << 10) +define ES_THEME_METRICS_GAP_MINOR (1 << 11) +define ES_THEME_METRICS_GAP_WRAP (1 << 12) define ES_THEME_METRICS_GAP_ALL (ES_THEME_METRICS_GAP_MAJOR | ES_THEME_METRICS_GAP_MINOR | ES_THEME_METRICS_GAP_WRAP) -define ES_THEME_METRICS_TEXT_COLOR (1 << 18) -define ES_THEME_METRICS_SELECTED_BACKGROUND (1 << 19) -define ES_THEME_METRICS_SELECTED_TEXT (1 << 20) -define ES_THEME_METRICS_ICON_COLOR (1 << 21) -define ES_THEME_METRICS_TEXT_ALIGN (1 << 22) -define ES_THEME_METRICS_TEXT_SIZE (1 << 23) -define ES_THEME_METRICS_FONT_FAMILY (1 << 24) -define ES_THEME_METRICS_FONT_WEIGHT (1 << 25) -define ES_THEME_METRICS_ICON_SIZE (1 << 26) -define ES_THEME_METRICS_IS_ITALIC (1 << 27) -define ES_THEME_METRICS_ELLIPSIS (1 << 28) -define ES_THEME_METRICS_LAYOUT_VERTICAL (1 << 29) +define ES_THEME_METRICS_TEXT_COLOR (1 << 13) +define ES_THEME_METRICS_SELECTED_BACKGROUND (1 << 14) +define ES_THEME_METRICS_SELECTED_TEXT (1 << 15) +define ES_THEME_METRICS_ICON_COLOR (1 << 16) +define ES_THEME_METRICS_TEXT_ALIGN (1 << 17) +define ES_THEME_METRICS_TEXT_SIZE (1 << 18) +define ES_THEME_METRICS_FONT_FAMILY (1 << 19) +define ES_THEME_METRICS_FONT_WEIGHT (1 << 20) +define ES_THEME_METRICS_ICON_SIZE (1 << 21) +define ES_THEME_METRICS_IS_ITALIC (1 << 22) +define ES_THEME_METRICS_LAYOUT_VERTICAL (1 << 23) define ES_WINDOW_MOVE_MAXIMISED (1 << 0) define ES_WINDOW_MOVE_ADJUST_TO_FIT_SCREEN (1 << 1) @@ -1310,7 +1304,6 @@ struct EsPanelBand { struct EsThemeMetrics { uint64_t mask; EsRectangle insets, clipInsets; - EsRectangle globalOffset; int clipEnabled, cursor; int preferredWidth, preferredHeight; int minimumWidth, minimumHeight; @@ -1318,7 +1311,7 @@ struct EsThemeMetrics { int gapMajor, gapMinor, gapWrap; uint32_t textColor, selectedBackground, selectedText, iconColor; int textAlign, textSize, fontFamily, fontWeight, iconSize; - bool isItalic, ellipsis, layoutVertical; + bool isItalic, layoutVertical; }; struct EsThemeAppearance { diff --git a/desktop/theme.cpp b/desktop/theme.cpp index 6a0b442..5a33f7c 100644 --- a/desktop/theme.cpp +++ b/desktop/theme.cpp @@ -14,10 +14,6 @@ #define THEME_LAYER_MODE_CONTENT (2) #define THEME_LAYER_MODE_OVERLAY (3) -#define THEME_FONT_FAMILY_SANS (0) -#define THEME_FONT_FAMILY_SERIF (1) -#define THEME_FONT_FAMILY_MONO (2) - #define THEME_OVERRIDE_I8 (1) #define THEME_OVERRIDE_I16 (2) #define THEME_OVERRIDE_F32 (3) @@ -159,12 +155,6 @@ typedef struct ThemeLayerIcon { Rectangle16 image; } ThemeLayerIcon; -typedef struct ThemeLayerImage { - uint16_t alpha; - uint16_t _unused0; - Rectangle16 image, contentRegion; -} ThemeLayerImage; - typedef struct ThemeLayerPathFillContour { float miterLimit; uint8_t internalWidth, externalWidth; @@ -200,9 +190,8 @@ typedef struct ThemeLayer { typedef struct ThemeMetrics { Rectangle16 insets, clipInsets; - Rectangle16 globalOffset; uint8_t clipEnabled, cursor; - uint16_t fontFamily; + uint16_t fontFamily; // TODO This needs to be validated when loading. int16_t preferredWidth, preferredHeight; int16_t minimumWidth, minimumHeight; int16_t maximumWidth, maximumHeight; @@ -210,7 +199,7 @@ typedef struct ThemeMetrics { uint32_t textColor, selectedBackground, selectedText, iconColor; int8_t textAlign, fontWeight; int16_t textSize, iconSize; - bool isItalic, ellipsis, layoutVertical; + bool isItalic, layoutVertical; } ThemeMetrics; typedef union ThemeVariant { @@ -236,7 +225,10 @@ typedef struct ThemeSequenceHeader { } ThemeSequenceHeader; typedef struct ThemeStyle { - uint32_t layerListOffset; // A list of uint32_t, giving offsets to ThemeLayer. First is the metrics layer. + // A list of uint32_t, giving offsets to ThemeLayer. First is the metrics layer. + // **This must be the first field in the structure; see the end of Export in util/designer2.cpp.** + uint32_t layerListOffset; + uint16_t id; uint8_t layerCount; uint8_t _unused1; @@ -245,17 +237,14 @@ typedef struct ThemeStyle { typedef struct ThemeConstant { uint64_t hash; - uint32_t valueOffset; - uint32_t valueByteCount; + char cValue[12]; bool scale; } ThemeConstant; typedef struct ThemeHeader { uint32_t signature; uint32_t styleCount, constantCount; - uint32_t bitmapBytes; // Followed by array of ThemeStyles and then an array of ThemeConstants. - // The bitmap is at the end of the theme file. } ThemeHeader; typedef struct BasicFontKerningEntry { @@ -1253,17 +1242,19 @@ const void *GetConstant(const char *cKey, size_t *byteCount, bool *scale) { } if (constant->hash == hash) { - data.position = constant->valueOffset; - const void *value = EsBufferRead(&data, (constant->valueByteCount + 3) & ~3); + size_t _byteCount = 0; - if (!value) { - EsPrint("Broken theme constant value.\n"); - return 0; + for (uintptr_t i = 0; i < sizeof(constant->cValue); i++) { + if (constant->cValue[i] == 0) { + break; + } else { + _byteCount++; + } } - *byteCount = constant->valueByteCount; + *byteCount = _byteCount; *scale = constant->scale; - return value; + return constant->cValue; } } @@ -1280,30 +1271,6 @@ int GetConstantNumber(const char *cKey) { return integer; } -EsRectangle GetConstantRectangle(const char *cKey) { - size_t byteCount; - bool scale = false; - char *value = (char *) GetConstant(cKey, &byteCount, &scale); - if (!value) return {}; - EsRectangle rectangle; - rectangle.l = EsCRTstrtol(value, &value, 10); - if (*value == ',') value++; - rectangle.r = EsCRTstrtol(value, &value, 10); - if (*value == ',') value++; - rectangle.t = EsCRTstrtol(value, &value, 10); - if (*value == ',') value++; - rectangle.b = EsCRTstrtol(value, &value, 10); - - if (scale) { - rectangle.l *= theming.scale; - rectangle.r *= theming.scale; - rectangle.t *= theming.scale; - rectangle.b *= theming.scale; - } - - return rectangle; -} - const char *GetConstantString(const char *cKey) { size_t byteCount; bool scale; @@ -1317,9 +1284,7 @@ bool ThemeInitialise() { const ThemeHeader *header = (const ThemeHeader *) EsBufferRead(&data, sizeof(ThemeHeader)); - if (!header || header->signature != THEME_HEADER_SIGNATURE - || !header->styleCount || !EsBufferRead(&data, sizeof(ThemeStyle)) - || data.bytes < header->bitmapBytes) { + if (!header || header->signature != THEME_HEADER_SIGNATURE || !header->styleCount || !EsBufferRead(&data, sizeof(ThemeStyle))) { return false; } @@ -1550,7 +1515,6 @@ void ThemeStylePrepare(UIStyle *style, UIStyleKey key) { #define ES_RECTANGLE_TO_RECTANGLE_8(x) { (int8_t) (x).l, (int8_t) (x).r, (int8_t) (x).t, (int8_t) (x).b } if (customMetrics->mask & ES_THEME_METRICS_INSETS) style->metrics->insets = ES_RECTANGLE_TO_RECTANGLE_8(customMetrics->insets); if (customMetrics->mask & ES_THEME_METRICS_CLIP_INSETS) style->metrics->clipInsets = ES_RECTANGLE_TO_RECTANGLE_8(customMetrics->clipInsets); - if (customMetrics->mask & ES_THEME_METRICS_GLOBAL_OFFSET) style->metrics->globalOffset = ES_RECTANGLE_TO_RECTANGLE_8(customMetrics->globalOffset); if (customMetrics->mask & ES_THEME_METRICS_CLIP_ENABLED) style->metrics->clipEnabled = customMetrics->clipEnabled; if (customMetrics->mask & ES_THEME_METRICS_CURSOR) style->metrics->cursor = customMetrics->cursor; if (customMetrics->mask & ES_THEME_METRICS_PREFERRED_WIDTH) style->metrics->preferredWidth = customMetrics->preferredWidth; @@ -1572,7 +1536,6 @@ void ThemeStylePrepare(UIStyle *style, UIStyleKey key) { if (customMetrics->mask & ES_THEME_METRICS_FONT_WEIGHT) style->metrics->fontWeight = customMetrics->fontWeight; if (customMetrics->mask & ES_THEME_METRICS_ICON_SIZE) style->metrics->iconSize = customMetrics->iconSize; if (customMetrics->mask & ES_THEME_METRICS_IS_ITALIC) style->metrics->isItalic = customMetrics->isItalic; - if (customMetrics->mask & ES_THEME_METRICS_ELLIPSIS) style->metrics->ellipsis = customMetrics->ellipsis; if (customMetrics->mask & ES_THEME_METRICS_LAYOUT_VERTICAL) style->metrics->layoutVertical = customMetrics->layoutVertical; } @@ -1585,7 +1548,6 @@ void ThemeStylePrepare(UIStyle *style, UIStyleKey key) { int16_t *scale16[] = { &style->metrics->insets.l, &style->metrics->insets.r, &style->metrics->insets.t, &style->metrics->insets.b, &style->metrics->clipInsets.l, &style->metrics->clipInsets.r, &style->metrics->clipInsets.t, &style->metrics->clipInsets.b, - &style->metrics->globalOffset.l, &style->metrics->globalOffset.r, &style->metrics->globalOffset.t, &style->metrics->globalOffset.b, &style->metrics->gapMajor, &style->metrics->gapMinor, &style->metrics->gapWrap, &style->metrics->preferredWidth, &style->metrics->preferredHeight, &style->metrics->minimumWidth, &style->metrics->minimumHeight, @@ -1689,7 +1651,7 @@ UIStyle *ThemeStyleInitialise(UIStyleKey key) { } if (layer->dataByteCount < sizeof(ThemeLayer)) { - EsPrint("Broken layer data byte count.\n"); + EsPrint("Broken layer data byte count (%d; %d).\n", layer->dataByteCount, *offset); return nullptr; } @@ -1933,7 +1895,7 @@ void UIStyle::PaintTextLayers(EsPainter *painter, EsTextPlan *plan, EsRectangle void UIStyle::PaintText(EsPainter *painter, EsElement *element, EsRectangle rectangle, const char *text, size_t textBytes, uint32_t iconID, uint32_t flags, EsTextSelection *selectionProperties) { - EsRectangle bounds = EsRectangleAdd(Translate(EsRectangleAddBorder(rectangle, insets), painter->offsetX, painter->offsetY), RECT16_TO_RECT(metrics->globalOffset)); + EsRectangle bounds = Translate(EsRectangleAddBorder(rectangle, insets), painter->offsetX, painter->offsetY); EsRectangle textBounds = bounds; EsRectangle oldClip = painter->clip; EsRectangleClip(painter->clip, bounds, &painter->clip); @@ -2055,10 +2017,10 @@ void UIStyle::PaintLayers(EsPainter *painter, EsRectangle location, int childTyp } EsRectangle bounds; - bounds.l = _bounds.l + (int) (scale * layer->offset.l) + THEME_RECT_WIDTH(_bounds) * layer->position.l / 100 + metrics->globalOffset.l; - bounds.r = _bounds.l + (int) (scale * layer->offset.r) + THEME_RECT_WIDTH(_bounds) * layer->position.r / 100 + metrics->globalOffset.r; - bounds.t = _bounds.t + (int) (scale * layer->offset.t) + THEME_RECT_HEIGHT(_bounds) * layer->position.t / 100 + metrics->globalOffset.t; - bounds.b = _bounds.t + (int) (scale * layer->offset.b) + THEME_RECT_HEIGHT(_bounds) * layer->position.b / 100 + metrics->globalOffset.b; + bounds.l = _bounds.l + (int) (scale * layer->offset.l) + THEME_RECT_WIDTH(_bounds) * layer->position.l / 100; + bounds.r = _bounds.l + (int) (scale * layer->offset.r) + THEME_RECT_WIDTH(_bounds) * layer->position.r / 100; + bounds.t = _bounds.t + (int) (scale * layer->offset.t) + THEME_RECT_HEIGHT(_bounds) * layer->position.t / 100; + bounds.b = _bounds.t + (int) (scale * layer->offset.b) + THEME_RECT_HEIGHT(_bounds) * layer->position.b / 100; if (layer->mode == whichLayers && THEME_RECT_WIDTH(bounds) > 0 && THEME_RECT_HEIGHT(bounds) > 0 && THEME_RECT_VALID(EsRectangleIntersection(bounds, painter->clip))) { diff --git a/res/Theme Source.dat b/res/Theme Source.dat index 7deafaa..f28ff3c 100644 Binary files a/res/Theme Source.dat and b/res/Theme Source.dat differ diff --git a/res/Theme.dat b/res/Theme.dat new file mode 100644 index 0000000..7c3c7bf Binary files /dev/null and b/res/Theme.dat differ diff --git a/res/Themes/Theme.dat b/res/Themes/Theme.dat deleted file mode 100644 index 1ec34d7..0000000 Binary files a/res/Themes/Theme.dat and /dev/null differ diff --git a/res/Themes/elementary Icons License.txt b/res/elementary Icons License.txt similarity index 100% rename from res/Themes/elementary Icons License.txt rename to res/elementary Icons License.txt diff --git a/res/Themes/elementary Icons.dat b/res/elementary Icons.dat similarity index 100% rename from res/Themes/elementary Icons.dat rename to res/elementary Icons.dat diff --git a/util/build.c b/util/build.c index eaefc15..21e9cce 100644 --- a/util/build.c +++ b/util/build.c @@ -1191,7 +1191,7 @@ void DoCommand(const char *l) { CallSystem("bin/config_editor"); } else if (0 == strcmp(l, "designer")) { BuildUtilities(); - CallSystem("bin/designer \"res/Theme Source.dat\" \"res/Themes/Theme.dat\" \"res/Cursors.png\" \"desktop/styles.header\""); + CallSystem("bin/designer \"res/Theme Source.dat\" \"res/Theme.dat\" \"desktop/styles.header\""); } else if (0 == strcmp(l, "designer2")) { BuildUtilities(); CallSystem("bin/designer2"); diff --git a/util/build_core.c b/util/build_core.c index 66fe935..cef99fd 100644 --- a/util/build_core.c +++ b/util/build_core.c @@ -575,9 +575,10 @@ void BuildDesktop(Application *application) { } } - ADD_BUNDLE_INPUT("res/Themes/Theme.dat", "Theme.dat", 16); - ADD_BUNDLE_INPUT("res/Themes/elementary Icons.dat", "Icons.dat", 16); - ADD_BUNDLE_INPUT("res/Themes/elementary Icons License.txt", "Icons License.txt", 16); + ADD_BUNDLE_INPUT("res/Theme.dat", "Theme.dat", 16); + ADD_BUNDLE_INPUT("res/elementary Icons.dat", "Icons.dat", 16); + ADD_BUNDLE_INPUT("res/elementary Icons License.txt", "Icons License.txt", 16); + ADD_BUNDLE_INPUT("res/Cursors.png", "Cursors.png", 16); ADD_BUNDLE_INPUT("bin/Desktop.no_symbols", "$Executables/x86_64", 0x1000); MakeBundle("root/" SYSTEM_FOLDER_NAME "/Desktop.esx", application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0); diff --git a/util/designer/designer.c b/util/designer/designer.c index 0b71755..c72a107 100644 --- a/util/designer/designer.c +++ b/util/designer/designer.c @@ -239,7 +239,7 @@ ModContext selected = MOD_CONTEXT(NULL, NULL, NULL, NULL); char temporaryOverride[4096]; -char *filePath, *exportPath, *embedBitmapPath, *stylesPath; +char *filePath, *exportPath, *stylesPath; void ModApply(ModData *mod); @@ -1286,7 +1286,6 @@ void LayerMetricsOp(RfState *state, RfItem *item, void *pointer) { metrics.gapMajor = layer->gaps.major; metrics.gapMinor = layer->gaps.minor; metrics.gapWrap = layer->gaps.wrap; - EXPORT_RECTANGLE16_FIELD(LayerMetrics, layer, ThemeMetrics, metrics, globalOffset, globalOffset); int fontFamily = inherit ? inherit->fontFamily : layer->fontFamily; metrics.fontFamily = fontFamily == FONT_FAMILY_SANS ? 0xFFFF : fontFamily == FONT_FAMILY_SERIF ? 0xFFFE : 0xFFFD; @@ -1359,27 +1358,12 @@ void StyleSetOp(RfState *state, RfItem *item, void *pointer) { ExportState *export = (ExportState *) state; StyleSet *styleSet = (StyleSet *) pointer; - // Load the cursors image. - - size_t bitmapBytes = 0; - char *bitmap = NULL; - - if (embedBitmapPath) { - bitmap = LoadFile(embedBitmapPath, &bitmapBytes); - - if (!bitmap) { - printf("Error: Could not load the embedded bitmap!\n"); - return; - } - } - // Write the header. ThemeHeader header = { 0 }; header.signature = THEME_HEADER_SIGNATURE; header.styleCount = arrlenu(styleSet->styles); header.constantCount = arrlenu(styleSet->constants); - header.bitmapBytes = bitmapBytes; state->access(state, &header, sizeof(header)); assert((export->buffer.data.byteCount & 3) == 0); @@ -1433,30 +1417,18 @@ void StyleSetOp(RfState *state, RfItem *item, void *pointer) { // Write the list of constants. - uint32_t constantListOffset = export->buffer.data.byteCount; - for (uintptr_t i = 0; i < header.constantCount; i++) { Constant *constant = styleSet->constants[i]; ThemeConstant entry = { 0 }; + assert(constant->value.byteCount + 1 < sizeof(entry.cValue)); entry.hash = CalculateCRC64(constant->key.buffer, constant->key.byteCount, 0); + entry.scale = constant->scale; + memcpy(entry.cValue, constant->value.buffer, constant->value.byteCount); + entry.cValue[constant->value.byteCount] = 0; state->access(state, &entry, sizeof(entry)); assert((export->buffer.data.byteCount & 3) == 0); } - for (uintptr_t i = 0; i < header.constantCount; i++) { - Constant *constant = styleSet->constants[i]; - ThemeConstant *entry = (ThemeConstant *) ((uint8_t *) export->buffer.data.buffer + constantListOffset) + i; - entry->valueOffset = export->buffer.data.byteCount; - entry->valueByteCount = constant->value.byteCount + 1; - entry->scale = constant->scale; - state->access(state, constant->value.buffer, constant->value.byteCount); - uint8_t terminate = 0; - state->access(state, &terminate, 1); - uint32_t pad = 0; - state->access(state, &pad, 4 - ((constant->value.byteCount + 1) & 3)); - assert((export->buffer.data.byteCount & 3) == 0); - } - // Write out all layers. for (uintptr_t i = 0; i < arrlenu(styleSet->layers); i++) { @@ -1571,11 +1543,6 @@ void StyleSetOp(RfState *state, RfItem *item, void *pointer) { ThemeStyle *entry = (ThemeStyle *) ((uint8_t *) export->buffer.data.buffer + styleListOffset) + i; entry->layerListOffset = layerListOffset; } - - // Write out the bitmap. - state->access(state, bitmap, bitmapBytes); - - free(bitmap); } else { RfStructOp(state, item, pointer); } @@ -1723,6 +1690,25 @@ void ObjectAddObjectProperty(Object2 *object, const char *cName, uint64_t value) arrput(object->properties, property); } +void AutoNameOverrideObject(Object2 *override, uint32_t primaryState, uint32_t stateBits) { + const char *cPrimaryStateStrings[] = { + "Any", "Idle", "Hovered", "Pressed", "Disabled", "Inactive", + }; + + const char *cStateBitStrings[] = { + "Focus", "Check", "Indtm", "DefBtn", "Sel", "FcItem", "ListFc", "BfEnt", "AfExt", + }; + + snprintf(override->cName, sizeof(override->cName), "?%s", primaryState ? cPrimaryStateStrings[primaryState] : ""); + + for (uintptr_t i = 0; i < 16; i++) { + if (stateBits & (1 << (15 - i))) { + snprintf(override->cName + strlen(override->cName), sizeof(override->cName) - strlen(override->cName), + "%s%s", i || primaryState ? "&" : "", cStateBitStrings[i]); + } + } +} + uint64_t ExportPaint2(Paint *paint, int *x, int y, Object2 **objects, uint64_t *idAllocator, Layer *layer) { char cPropertyName[PROPERTY_NAME_SIZE]; @@ -1731,19 +1717,6 @@ uint64_t ExportPaint2(Paint *paint, int *x, int y, Object2 **objects, uint64_t * } 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); @@ -1794,6 +1767,7 @@ uint64_t ExportPaint2(Paint *paint, int *x, int y, Object2 **objects, uint64_t * ObjectAddIntegerProperty(&override, "_primaryState", s->primaryState); ObjectAddIntegerProperty(&override, "_stateBits", stateBits); ObjectAddIntegerProperty(&override, "_duration", s->duration); + AutoNameOverrideObject(&override, s->primaryState, stateBits); bool addObject = false; @@ -1892,6 +1866,7 @@ uint64_t ExportPaint2(Paint *paint, int *x, int y, Object2 **objects, uint64_t * ObjectAddIntegerProperty(&override, "_primaryState", s->primaryState); ObjectAddIntegerProperty(&override, "_stateBits", stateBits); ObjectAddIntegerProperty(&override, "_duration", s->duration); + AutoNameOverrideObject(&override, s->primaryState, stateBits); bool addObject = false; @@ -2004,8 +1979,6 @@ void ExportProperty2(Layer *layer, Property *property, Object2 *override) { RfItem item = PaintSolid_Type.fields[PaintSolid_color].item; uint32_t value; item.type->op(&state.s, &item, &value); ObjectAddObjectProperty(override, "borderPaint", ColorLookupPointer(value)->object2ID); - } else if (property->path[3] == LayerBox_mainPaint && property->path[4] == Paint_overwrite && property->path[5] == PaintOverwrite_color) { - fprintf(stderr, "\t>>\n"); } else { unhandled = true; } @@ -2082,9 +2055,7 @@ void ExportProperty2(Layer *layer, Property *property, Object2 *override) { } void ActionExportDesigner2(void *cp) { - // TODO Inherited text styles. // TODO Merging identical layers and styles. - // TODO Auto-name override layers. Object2 *objects = NULL; uint64_t objectIDAllocator = 0; @@ -2138,6 +2109,8 @@ void ActionExportDesigner2(void *cp) { for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) { int x = x0; Style *style = styleSet.styles[i]; + + fprintf(stderr, "style: %.*s\n", (int) style->name.byteCount, (const char *) style->name.buffer); Object2 layerGroup = { .type = OBJ_LAYER_GROUP, .id = ++objectIDAllocator }; Object2 metrics = { 0 }, textStyle = { 0 }; @@ -2206,15 +2179,39 @@ void ActionExportDesigner2(void *cp) { } else if (layer->base.tag == LayerBase_metrics + 1) { LayerMetrics *m = &layer->base.metrics; assert(!m->globalOffset.l && !m->globalOffset.r && !m->globalOffset.t && !m->globalOffset.b); - + LayerMetrics *inherit = NULL; object.type = OBJ_VAR_TEXT_STYLE, object.id = ++objectIDAllocator; - ObjectAddObjectProperty(&object, "textColor", ColorLookupPointer(m->textColor)->object2ID); - ObjectAddObjectProperty(&object, "selectedBackground", ColorLookupPointer(m->selectedBackground)->object2ID); - ObjectAddObjectProperty(&object, "selectedText", ColorLookupPointer(m->selectedText)->object2ID); - ObjectAddIntegerProperty(&object, "textSize", m->textSize); - ObjectAddIntegerProperty(&object, "fontWeight", m->fontWeight); - ObjectAddIntegerProperty(&object, "isItalic", m->italic); - ObjectAddIntegerProperty(&object, "fontFamily", m->fontFamily == FONT_FAMILY_MONO ? 0xFFFD : 0xFFFF); + + if (m->inheritText.byteCount) { + for (uintptr_t i = 0; i < arrlenu(styleSet.styles); i++) { + Style *style = styleSet.styles[i]; + + if (m->inheritText.byteCount == style->name.byteCount + && 0 == memcmp(m->inheritText.buffer, style->name.buffer, style->name.byteCount)) { + inherit = &LayerLookup(style->layers[0])->base.metrics; + break; + } + } + } + + if (inherit) { + ObjectAddObjectProperty(&object, "textColor", ColorLookupPointer(inherit->textColor)->object2ID); + ObjectAddObjectProperty(&object, "selectedBackground", ColorLookupPointer(inherit->selectedBackground)->object2ID); + ObjectAddObjectProperty(&object, "selectedText", ColorLookupPointer(inherit->selectedText)->object2ID); + ObjectAddIntegerProperty(&object, "textSize", inherit->textSize); + ObjectAddIntegerProperty(&object, "fontWeight", inherit->fontWeight); + ObjectAddIntegerProperty(&object, "isItalic", inherit->italic); + ObjectAddIntegerProperty(&object, "fontFamily", inherit->fontFamily == FONT_FAMILY_MONO ? 0xFFFD : 0xFFFF); + } else { + ObjectAddObjectProperty(&object, "textColor", ColorLookupPointer(m->textColor)->object2ID); + ObjectAddObjectProperty(&object, "selectedBackground", ColorLookupPointer(m->selectedBackground)->object2ID); + ObjectAddObjectProperty(&object, "selectedText", ColorLookupPointer(m->selectedText)->object2ID); + ObjectAddIntegerProperty(&object, "textSize", m->textSize); + ObjectAddIntegerProperty(&object, "fontWeight", m->fontWeight); + ObjectAddIntegerProperty(&object, "isItalic", m->italic); + ObjectAddIntegerProperty(&object, "fontFamily", m->fontFamily == FONT_FAMILY_MONO ? 0xFFFD : 0xFFFF); + } + ObjectAddIntegerProperty(&object, "iconSize", m->iconSize); ObjectAddObjectProperty(&object, "iconColor", ColorLookupPointer(m->iconColor)->object2ID); textStyle = object; @@ -2267,6 +2264,7 @@ void ActionExportDesigner2(void *cp) { assert(arrlenu(s->keyframes) == 1); Keyframe *keyframe = s->keyframes[0]; +#if 0 char buffer[256]; snprintf(buffer, sizeof(buffer), "%s%s%s%s%s%s%s%s%s%s", ((StringOption *) PrimaryState_Type.fields[s->primaryState].item.options)->string, @@ -2281,6 +2279,7 @@ void ActionExportDesigner2(void *cp) { s->flagSelected ? " (selected)" : ""); fprintf(stderr, "%.*s:%.*s:%s:%d\n", (int) style->name.byteCount, (char *) style->name.buffer, (int) layer->name.byteCount, (char *) layer->name.buffer, buffer, s->duration); +#endif uint32_t stateBits = 0; if (s->flagFocused) stateBits |= THEME_STATE_FOCUSED; @@ -2302,6 +2301,7 @@ void ActionExportDesigner2(void *cp) { ObjectAddIntegerProperty(&override, "_primaryState", s->primaryState); ObjectAddIntegerProperty(&override, "_stateBits", stateBits); ObjectAddIntegerProperty(&override, "_duration", s->duration); + AutoNameOverrideObject(&override, s->primaryState, stateBits); bool addObject = false; @@ -2351,7 +2351,7 @@ void ActionExportDesigner2(void *cp) { } } - { + if (layerCount) { Object2 object = layerGroup; ObjectAddIntegerProperty(&object, "_graphX", x); ObjectAddIntegerProperty(&object, "_graphY", y); @@ -2360,6 +2360,9 @@ void ActionExportDesigner2(void *cp) { ObjectAddIntegerProperty(&object, "layers_count", layerCount); arrput(objects, object); x += 100; + } else { + arrfree(layerGroup.properties); + layerGroup.id = 0; } { @@ -2370,6 +2373,7 @@ void ActionExportDesigner2(void *cp) { ObjectAddIntegerProperty(&object, "_graphW", 80); ObjectAddIntegerProperty(&object, "_graphH", 60); ObjectAddIntegerProperty(&object, "isPublic", style->publicStyle); + ObjectAddIntegerProperty(&object, "headerID", style->id); ObjectAddObjectProperty(&object, "appearance", layerGroup.id); ObjectAddObjectProperty(&object, "metrics", metrics.id); ObjectAddObjectProperty(&object, "textStyle", textStyle.id); @@ -2593,11 +2597,10 @@ void DrawStyle(UIPainter *painter, UIRectangle generalBounds, UIRectangle *globa ThemeDrawLayer(&themePainter, bounds2, &themeData, scale, *(EsRectangle *) opaqueRegion); } else { EsBufferRead(&themeData, sizeof(ThemeLayer)); - const ThemeMetrics *metrics = (const ThemeMetrics *) EsBufferRead(&themeData, sizeof(ThemeMetrics)); - globalOffset->l = metrics->globalOffset.l * scale; - globalOffset->r = metrics->globalOffset.r * scale; - globalOffset->t = metrics->globalOffset.t * scale; - globalOffset->b = metrics->globalOffset.b * scale; + globalOffset->l = 0; + globalOffset->r = 0; + globalOffset->t = 0; + globalOffset->b = 0; } free(dataBuffer); @@ -6086,8 +6089,7 @@ int main(int argc, char **argv) filePath = argv[1]; exportPath = argv[2]; - embedBitmapPath = argc >= 4 ? argv[3] : NULL; - stylesPath = argc >= 5 ? argv[4] : NULL; + stylesPath = argc >= 4 ? argv[3] : NULL; UIInitialise(); diff --git a/util/designer2.cpp b/util/designer2.cpp index c99dcab..73b55d4 100644 --- a/util/designer2.cpp +++ b/util/designer2.cpp @@ -16,8 +16,8 @@ // x86_64-w64-mingw32-gcc -O3 -o bin/designer2.exe -D UI_WINDOWS util/designer2.cpp -DUNICODE -lgdi32 -luser32 -lkernel32 -Wl,--subsystem,windows -fno-exceptions -fno-rtti // TODO Needed to replace the old designer: -// Import old theming data. -// Export. +// Exporting sequences. +// Calculating additional metric rectangles (paintOutsets, opaqueInsets and approximateBorders). // Prototyping display: previewing state transitions. // TODO Additional features: @@ -37,6 +37,7 @@ // Proper bezier path editor. // Path boolean operations. // Timeline editor for applying a given state change, with rows for possibly many different layers. +// Metrics: layoutVertical. // TODO Reorganize old theming data! @@ -160,6 +161,7 @@ void SetBit(uint32_t *value, uint32_t bit, bool on) { #include "../shared/math.cpp" #include "../shared/array.cpp" +#include "../shared/hash.cpp" #include "../shared/hash_table.cpp" #include "../desktop/renderer.cpp" #include "../desktop/theme.cpp" @@ -332,11 +334,18 @@ struct Step { }; }; +struct ExportOffset { + uint64_t objectID; + uintptr_t offset; + char cPropertyName[PROPERTY_NAME_SIZE]; +}; + Array undoStack; Array redoStack; bool documentModified; uint64_t selectedObjectID; HashStore objectLookup; +Array exportOffsets; // Document state: Array objects; @@ -360,6 +369,16 @@ Object *ObjectFind(uint64_t id) { #endif } +ExportOffset *ExportOffsetFindObject(uint64_t id) { + for (uintptr_t i = 0; i < exportOffsets.Length(); i++) { + if (exportOffsets[i].objectID == id) { + return &exportOffsets[i]; + } + } + + return nullptr; +} + void ObjectSetSelected(uint64_t id, bool removeSelectedFlagFromPreviousSelection = true) { if (selectedObjectID && removeSelectedFlagFromPreviousSelection) { Object *object = ObjectFind(selectedObjectID); @@ -416,7 +435,7 @@ Property *PropertyFindOrInherit(Object *object, const char *cName, uint8_t type uintptr_t depth = 0; while (object && (depth++ < 100)) { - if (!ObjectIsConditional(object) || (canvas->previewStateActive && ObjectMatchesPreviewState(object))) { + if (!ObjectIsConditional(object) || (canvas && canvas->previewStateActive && ObjectMatchesPreviewState(object))) { // Return the value if the object has this property. Property *property = PropertyFind(object, cName); if (property) return type && property->type != type ? nullptr : property; @@ -488,6 +507,8 @@ void DocumentSave(void *) { } void DocumentLoad() { + // TODO Check names are zero-terminated. + #ifdef OS_ESSENCE EsBuffer buffer = {}; buffer.out = (uint8_t *) EsFileStoreReadAll(fileStore, &buffer.bytes); @@ -1424,6 +1445,10 @@ void InspectorPopulate() { InspectorAddLink(object, "Metrics:", "metrics"); InspectorAddLink(object, "Text style:", "textStyle"); InspectorAddBooleanToggle(object, "Public style", "isPublic"); + + char buffer[128]; + snprintf(buffer, sizeof(buffer), "Header ID: %d", PropertyReadInt32(object, "headerID")); + UILabelCreate(0, 0, buffer, -1); } else if (object->type == OBJ_VAR_COLOR) { InspectorBind(&UIColorPickerCreate(&UIPanelCreate(0, 0)->e, UI_COLOR_PICKER_HAS_OPACITY)->e, object->id, "color", INSPECTOR_COLOR_PICKER); InspectorBind(&UITextboxCreate(0, 0)->e, object->id, "color", INSPECTOR_COLOR_TEXTBOX); @@ -2075,10 +2100,10 @@ void CanvasDrawLayer(Object *object, UIRectangle bounds, UIPainter *painter, int } if (object->type == OBJ_LAYER_BOX) { - bounds.l += PropertyFindOrInheritReadInt32(object, "offset0") * canvas->zoom; - bounds.r += PropertyFindOrInheritReadInt32(object, "offset1") * canvas->zoom; - bounds.t += PropertyFindOrInheritReadInt32(object, "offset2") * canvas->zoom; - bounds.b += PropertyFindOrInheritReadInt32(object, "offset3") * canvas->zoom; + bounds.l += GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "offset0")) * canvas->zoom; + bounds.r += GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "offset1")) * canvas->zoom; + bounds.t += GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "offset2")) * canvas->zoom; + bounds.b += GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "offset3")) * canvas->zoom; uint8_t buffer[4096]; EsBuffer data = { .out = buffer, .bytes = sizeof(buffer) }; @@ -2103,8 +2128,8 @@ void CanvasDrawLayer(Object *object, UIRectangle bounds, UIPainter *painter, int textStyle.font.family = ES_FONT_SANS; textStyle.font.weight = 5; textStyle.size = 10; - textStyle.color = GraphGetColorFromProperty(PropertyFindOrInherit(false, object, "color")); - textStyle.blur = GraphGetIntegerFromProperty(PropertyFindOrInherit(false, object, "blur")); + textStyle.color = GraphGetColorFromProperty(PropertyFindOrInherit(object, "color")); + textStyle.blur = GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "blur")); if (textStyle.blur > 10) textStyle.blur = 10; EsDrawTextSimple((_EsPainter *) &themePainter, ui.instance->window, bounds, "Sample", -1, textStyle, ES_TEXT_H_CENTER | ES_TEXT_V_CENTER); #endif @@ -2138,6 +2163,7 @@ void CanvasDrawLayer(Object *object, UIRectangle bounds, UIPainter *painter, int LAYER_READ_INT32(position1); LAYER_READ_INT32(position2); LAYER_READ_INT32(position3); +#undef LAYER_READ_INT32 UIRectangle outBounds; outBounds.l = bounds.l + offset0 * canvas->zoom + position0 * inWidth / 100; @@ -2170,11 +2196,11 @@ void CanvasDrawStyle(Object *object, UIRectangle bounds, UIPainter *painter, int if (object->type == OBJ_VAR_TEXT_STYLE) { EsTextStyle textStyle = {}; - textStyle.font.family = GraphGetIntegerFromProperty(PropertyFindOrInherit(false, object, "fontFamily")); - textStyle.font.weight = GraphGetIntegerFromProperty(PropertyFindOrInherit(false, object, "fontWeight")); - textStyle.font.italic = GraphGetIntegerFromProperty(PropertyFindOrInherit(false, object, "isItalic")); - textStyle.size = GraphGetIntegerFromProperty(PropertyFindOrInherit(false, object, "textSize")) * canvas->zoom; - textStyle.color = GraphGetColorFromProperty(PropertyFindOrInherit(false, object, "textColor")); + textStyle.font.family = GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "fontFamily")); + textStyle.font.weight = GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "fontWeight")); + textStyle.font.italic = GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "isItalic")); + textStyle.size = GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "textSize")) * canvas->zoom; + textStyle.color = GraphGetColorFromProperty(PropertyFindOrInherit(object, "textColor")); EsDrawTextSimple((_EsPainter *) &themePainter, ui.instance->window, bounds, "Sample", -1, textStyle, ES_TEXT_H_CENTER | ES_TEXT_V_CENTER); #if 0 EsDrawStandardIcon((_EsPainter *) &themePainter, ES_ICON_GO_NEXT_SYMBOLIC, @@ -2182,6 +2208,10 @@ void CanvasDrawStyle(Object *object, UIRectangle bounds, UIPainter *painter, int GraphGetColorFromProperty(PropertyFindOrInherit(false, object, "iconColor"))); #endif } +#else + if (object->type == OBJ_VAR_TEXT_STYLE && depth == 0) { + UIDrawString(painter, bounds, "TxtStyle", -1, 0xFF000000, UI_ALIGN_CENTER, nullptr); + } #endif if (object->type == OBJ_STYLE) { @@ -2325,8 +2355,10 @@ int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) { Property *style = PropertyFind(object, "style", PROP_OBJECT); canvas->previewStateActive = object->id == selectedObjectID; CanvasDrawStyle(ObjectFind(style ? style->object : 0), bounds, painter); + } else if (object->type == OBJ_LAYER_METRICS) { + // TODO Visually show the preferred size, insets and gaps? + UIDrawString(painter, bounds, "Metrics", -1, 0xFF000000, UI_ALIGN_CENTER, nullptr); } else { - // TODO Preview for the metrics layer. Show the preferred size, insets and gaps? // TODO Preview for OBJ_VAR_CONTOUR_STYLE. } @@ -2340,7 +2372,6 @@ int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) { if (canvas->showArrows && !canvas->showPrototype) { // Draw object connections. - // TODO This will be awfully slow when there's many objects... for (uintptr_t i = 0; i < objects.Length(); i++) { Object *object = &objects[i]; @@ -2592,6 +2623,39 @@ void ObjectAddCommandInternal(void *cp) { p = { .type = PROP_INT, .integer = y }; strcpy(p.cName, "_graphY"); object.properties.Add(p); p = { .type = PROP_INT, .integer = w }; strcpy(p.cName, "_graphW"); object.properties.Add(p); p = { .type = PROP_INT, .integer = h }; strcpy(p.cName, "_graphH"); object.properties.Add(p); + + if (object.type == OBJ_STYLE) { + // TODO Prevent taking IDs of style objects in the clipboard? + + uint8_t allocatedIDs[32768 / 8] = {}; + + for (uintptr_t i = 0; i < objects.Length(); i++) { + if (objects[i].type == OBJ_STYLE) { + int32_t id = PropertyReadInt32(&objects[i], "headerID"); + + if (id > 0 && id < 32768) { + allocatedIDs[id / 8] |= 1 << (id % 8); + } + } + } + + bool foundID = false; + + for (int32_t i = 1; i < 32768; i++) { + if (!(allocatedIDs[i / 8] & (1 << (i % 8)))) { + p = { .type = PROP_INT, .integer = i }; strcpy(p.cName, "headerID"); object.properties.Add(p); + foundID = true; + break; + } + } + + if (!foundID) { + UIDialogShow(window, 0, "Error: No free header IDs.\n%f%b", "OK"); + object.properties.Free(); + return; + } + } + ObjectAddInternal(object); } @@ -2697,6 +2761,325 @@ void ObjectDuplicateCommand(void *) { ////////////////////////////////////////////////////////////// +Rectangle8 ExportCalculatePaintOutsets(Object *object) { + return {}; // TODO; +} + +Rectangle8 ExportCalculateOpaqueInsets(Object *object) { + return {}; // TODO; +} + +Rectangle8 ExportCalculateApproximateBorders(Object *object) { + return {}; // TODO; +} + +#ifndef OS_ESSENCE +void Export() { + DocumentLoad(); + + // TODO Output the new styles.header. + // TODO Export conditional objects into sequences. + + // TODO Exporting modified integers and colors. + // TODO Recursively exporting nested groups. + // TODO Handling styles that don't have metrics/textStyle. + + // Create the list of styles. + + FILE *output = fopen("desktop/styles.header", "wb"); + + for (uintptr_t i = 0; i < objects.Length(); i++) { + Object *object = &objects[i]; + + if (object->type == OBJ_STYLE) { + bool isPublic = PropertyReadInt32(object, "isPublic"); + int32_t headerID = PropertyReadInt32(object, "headerID"); + + if (!headerID) { + continue; + } + + fprintf(output, "%sdefine ES_STYLE_", !isPublic ? "private " : ""); + + bool dot = false; + + for (uintptr_t j = 0; object->cName[j]; j++) { + char c = object->cName[j]; + + if (c == '.') { + fprintf(output, "_"); + dot = true; + } else if (c >= 'A' && c <= 'Z' && j && !dot) { + fprintf(output, "_%c", c); + } else if (c >= 'a' && c <= 'z') { + fprintf(output, "%c", c - 'a' + 'A'); + dot = false; + } else { + fprintf(output, "%c", c); + dot = false; + } + } + + fprintf(output, " (ES_STYLE_CAST(%d))\n", (headerID << 1) | 1); + } + } + + fclose(output); + + output = fopen("res/Theme.dat", "wb"); + + // Write the header. + + ThemeHeader header = { 0 }; + header.signature = THEME_HEADER_SIGNATURE; + + for (uintptr_t i = 0; i < objects.Length(); i++) { + if (objects[i].type == OBJ_STYLE) { + header.styleCount++; + } else if ((objects[i].type == OBJ_VAR_COLOR || objects[i].type == OBJ_VAR_INT) && PropertyReadInt32(&objects[i], "isExported")) { + header.constantCount++; + } + } + + fwrite(&header, 1, sizeof(header), output); + + // Write the list of styles. + + for (uintptr_t i = 0; i < objects.Length(); i++) { + Object *object = &objects[i]; + + if (object->type != OBJ_STYLE) { + continue; + } + + int32_t headerID = PropertyReadInt32(object, "headerID"); + ThemeStyle entry = {}; + entry.id = (headerID << 1) | 1; + entry.layerCount = 1; + + Object *appearance = PropertyFindOrInheritReadObject(object, "appearance"); + + if (appearance && appearance->type == OBJ_LAYER_GROUP) { + entry.layerCount += PropertyReadInt32(appearance, "layers_count"); + entry.paintOutsets = ExportCalculatePaintOutsets(appearance); + entry.opaqueInsets = ExportCalculateOpaqueInsets(appearance); + entry.approximateBorders = ExportCalculateApproximateBorders(appearance); + } + + fwrite(&entry, 1, sizeof(entry), output); + } + + // Write the list of constants. + + for (uintptr_t i = 0; i < objects.Length(); i++) { + Object *object = &objects[i]; + + if ((object->type != OBJ_VAR_COLOR && object->type != OBJ_VAR_INT) || !PropertyReadInt32(object, "isExported")) { + continue; + } + + ThemeConstant constant = {}; + constant.scale = PropertyReadInt32(object, "isScaled"); + constant.hash = CalculateCRC64(object->cName, strlen(object->cName), 0); + + if (object->type == OBJ_VAR_COLOR) { + snprintf(constant.cValue, sizeof(constant.cValue), "0x%.8X", (uint32_t) PropertyReadInt32(object, "color")); + } else if (object->type == OBJ_VAR_INT) { + snprintf(constant.cValue, sizeof(constant.cValue), "%d", (int32_t) PropertyReadInt32(object, "value")); + } + + fwrite(&constant, 1, sizeof(constant), output); + } + + // Write out all layers. + + for (uintptr_t i = 0; i < objects.Length(); i++) { + Object *object = &objects[i]; + + if (object->type != OBJ_STYLE) { + continue; + } + + Object *metrics = PropertyFindOrInheritReadObject(object, "metrics"); + Object *textStyle = PropertyFindOrInheritReadObject(object, "textStyle"); + Object *appearance = PropertyFindOrInheritReadObject(object, "appearance"); + + if (metrics && textStyle) { + ExportOffset exportOffset = {}; + exportOffset.objectID = textStyle->id; + exportOffset.offset = ftell(output); + exportOffsets.Add(exportOffset); + + ThemeLayer layer = {}; + layer.type = THEME_LAYER_METRICS; + layer.dataByteCount = sizeof(ThemeLayer) + sizeof(ThemeMetrics); + + ThemeMetrics _metrics = {}; + + _metrics.insets.l = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "insets0")); + _metrics.insets.r = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "insets1")); + _metrics.insets.t = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "insets2")); + _metrics.insets.b = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "insets3")); + _metrics.clipInsets.l = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "clipInsets0")); + _metrics.clipInsets.r = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "clipInsets1")); + _metrics.clipInsets.t = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "clipInsets2")); + _metrics.clipInsets.b = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "clipInsets3")); + _metrics.clipEnabled = PropertyFindOrInheritReadInt32(metrics, "clipEnabled"); + _metrics.cursor = PropertyFindOrInheritReadInt32(metrics, "cursor"); + _metrics.preferredWidth = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "preferredWidth")); + _metrics.preferredHeight = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "preferredHeight")); + _metrics.minimumWidth = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "minimumWidth")); + _metrics.minimumHeight = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "minimumHeight")); + _metrics.maximumWidth = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "maximumWidth")); + _metrics.maximumHeight = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "maximumHeight")); + _metrics.gapMajor = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "gapMajor")); + _metrics.gapMinor = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "gapMinor")); + _metrics.gapWrap = GraphGetIntegerFromProperty(PropertyFindOrInherit(metrics, "gapWrap")); + + int32_t horizontalTextAlign = PropertyFindOrInheritReadInt32(metrics, "horizontalTextAlign"); + int32_t verticalTextAlign = PropertyFindOrInheritReadInt32(metrics, "verticalTextAlign"); + int32_t wrapText = PropertyFindOrInheritReadInt32(metrics, "wrapText"); + int32_t ellipsis = PropertyFindOrInheritReadInt32(metrics, "ellipsis"); + _metrics.textAlign = (wrapText ? ES_TEXT_WRAP : 0) | (ellipsis ? ES_TEXT_ELLIPSIS : 0) + | (horizontalTextAlign == 1 ? ES_TEXT_H_LEFT : horizontalTextAlign == 3 ? ES_TEXT_H_RIGHT : ES_TEXT_H_CENTER) + | (verticalTextAlign == 1 ? ES_TEXT_V_TOP : verticalTextAlign == 3 ? ES_TEXT_V_BOTTOM : ES_TEXT_V_CENTER); + + _metrics.fontFamily = PropertyFindOrInheritReadInt32(textStyle, "fontFamily"); + _metrics.fontWeight = GraphGetIntegerFromProperty(PropertyFind(textStyle, "fontWeight")); + _metrics.textSize = GraphGetIntegerFromProperty(PropertyFindOrInherit(textStyle, "textSize")); + _metrics.iconSize = GraphGetIntegerFromProperty(PropertyFindOrInherit(textStyle, "iconSize")); + _metrics.isItalic = PropertyFindOrInheritReadInt32(textStyle, "isItalic"); + _metrics.textColor = GraphGetColorFromProperty(PropertyFindOrInherit(textStyle, "textColor")); + _metrics.selectedBackground = GraphGetColorFromProperty(PropertyFindOrInherit(textStyle, "selectedBackground")); + _metrics.selectedText = GraphGetColorFromProperty(PropertyFindOrInherit(textStyle, "selectedText")); + _metrics.iconColor = GraphGetColorFromProperty(PropertyFindOrInherit(textStyle, "iconColor")); + + fwrite(&layer, 1, sizeof(layer), output); + fwrite(&_metrics, 1, sizeof(_metrics), output); + } else { + assert(false); // TODO. + } + + if (appearance && appearance->type == OBJ_LAYER_GROUP) { + int32_t layerCount = PropertyReadInt32(appearance, "layers_count"); + if (layerCount < 0) layerCount = 0; + if (layerCount > 100) layerCount = 100; + + for (int32_t i = 0; i < layerCount; i++) { + char cPropertyName[PROPERTY_NAME_SIZE]; + sprintf(cPropertyName, "layers_%d_layer", i); + Property *layerProperty = PropertyFind(appearance, cPropertyName, PROP_OBJECT); + Object *layerObject = ObjectFind(layerProperty ? layerProperty->object : 0); + if (!layerObject) continue; + + ExportOffset exportOffset = {}; + exportOffset.objectID = layerObject->id; + exportOffset.offset = ftell(output); + exportOffsets.Add(exportOffset); + +#define LAYER_READ_INT32(x) sprintf(cPropertyName, "layers_%d_" #x, i); int8_t x = PropertyReadInt32(appearance, cPropertyName) + LAYER_READ_INT32(offset0); + LAYER_READ_INT32(offset1); + LAYER_READ_INT32(offset2); + LAYER_READ_INT32(offset3); + LAYER_READ_INT32(position0); + LAYER_READ_INT32(position1); + LAYER_READ_INT32(position2); + LAYER_READ_INT32(position3); +#undef LAYER_READ_INT32 + + uint8_t buffer[4096]; + EsBuffer data = { .out = buffer, .bytes = sizeof(buffer) }; + ThemeLayer layer = {}; + layer.position = { position0, position1, position2, position3 }; + layer.offset = { offset0, offset1, offset2, offset3 }; + + if (layerObject->type == OBJ_LAYER_PATH) { + layer.type = THEME_LAYER_PATH; + ExportLayerPath(layerObject, &data); + } else if (layerObject->type == OBJ_LAYER_BOX) { + layer.type = THEME_LAYER_BOX; + layer.offset.l += GraphGetIntegerFromProperty(PropertyFindOrInherit(layerObject, "offset0")); + layer.offset.r += GraphGetIntegerFromProperty(PropertyFindOrInherit(layerObject, "offset1")); + layer.offset.t += GraphGetIntegerFromProperty(PropertyFindOrInherit(layerObject, "offset2")); + layer.offset.b += GraphGetIntegerFromProperty(PropertyFindOrInherit(layerObject, "offset3")); + ExportLayerBox(layerObject, &data); + } else if (layerObject->type == OBJ_LAYER_TEXT) { + layer.type = THEME_LAYER_TEXT; + ThemeLayerText text = {}; + text.blur = GraphGetIntegerFromProperty(PropertyFindOrInherit(object, "blur")); + text.color = GraphGetColorFromProperty(PropertyFindOrInherit(object, "color")); + EsBufferWrite(&data, &text, sizeof(text)); + } else { + assert(false); + } + + layer.dataByteCount = data.position + sizeof(layer); + fwrite(&layer, 1, sizeof(layer), output); + fwrite(data.out, 1, data.position, output); + assert(!data.error); + } + } + } + + // Write out layer lists for styles. + + for (uintptr_t i = 0; i < objects.Length(); i++) { + Object *object = &objects[i]; + + if (object->type != OBJ_STYLE) { + continue; + } + + { + ExportOffset exportOffset = {}; + exportOffset.objectID = object->id; + exportOffset.offset = ftell(output); + exportOffsets.Add(exportOffset); + } + + { + Object *textStyle = PropertyFindOrInheritReadObject(object, "textStyle"); + uint32_t exportOffset = ExportOffsetFindObject(textStyle->id)->offset; + fwrite(&exportOffset, 1, sizeof(exportOffset), output); + } + + Object *appearance = PropertyFindOrInheritReadObject(object, "appearance"); + + if (appearance && appearance->type == OBJ_LAYER_GROUP) { + int32_t layerCount = PropertyReadInt32(appearance, "layers_count"); + if (layerCount < 0) layerCount = 0; + if (layerCount > 100) layerCount = 100; + + for (int32_t i = 0; i < layerCount; i++) { + char cPropertyName[PROPERTY_NAME_SIZE]; + sprintf(cPropertyName, "layers_%d_layer", i); + Property *layerProperty = PropertyFind(appearance, cPropertyName, PROP_OBJECT); + Object *layerObject = ObjectFind(layerProperty ? layerProperty->object : 0); + if (!layerObject) continue; + uint32_t exportOffset = ExportOffsetFindObject(layerObject->id)->offset; + fwrite(&exportOffset, 1, sizeof(exportOffset), output); + } + } + } + + // Update the style list to point to the layer lists. + + uintptr_t writeOffset = sizeof(ThemeHeader); + + for (uintptr_t i = 0; i < objects.Length(); i++) { + Object *object = &objects[i]; + if (object->type != OBJ_STYLE) continue; + uint32_t exportOffset = ExportOffsetFindObject(object->id)->offset; + fseek(output, writeOffset, SEEK_SET); + fwrite(&exportOffset, 1, sizeof(exportOffset), output); + writeOffset += sizeof(ThemeStyle); + } +} +#endif + +////////////////////////////////////////////////////////////// + int WindowMessage(UIElement *element, UIMessage message, int di, void *dp) { if (message == UI_MSG_WINDOW_CLOSE) { #ifndef OS_ESSENCE @@ -2720,7 +3103,20 @@ void DocumentFileMenu(void *) { } #endif -int main() { +int main(int argc, char **argv) { +#ifndef OS_ESSENCE + if (argc == 2) { + if (0 == strcmp(argv[1], "export")) { + Export(); + } else { + fprintf(stderr, "Error: Unknown action '%s'.\n", argv[1]); + return 1; + } + + return 0; + } +#endif + UIInitialise(); ui.theme = _uiThemeClassic; window = UIWindowCreate(0, UI_ELEMENT_PARENT_PUSH | UI_WINDOW_MAXIMIZE, "Designer", 0, 0); @@ -2802,6 +3198,6 @@ void _UIMessageProcess(EsMessage *message) { void _start() { _init(); - main(); + main(0, nullptr); } #endif