From b702c77c05cb6891476fb231e3d26b6998bcca55 Mon Sep 17 00:00:00 2001 From: nakst <> Date: Sat, 2 Oct 2021 20:59:34 +0100 Subject: [PATCH] designer2 non-animated export --- desktop/desktop.cpp | 5 +- desktop/gui.cpp | 2 - desktop/os.header | 53 +-- desktop/theme.cpp | 84 +--- res/Theme Source.dat | Bin 53659 -> 52967 bytes res/Theme.dat | Bin 0 -> 29496 bytes res/Themes/Theme.dat | Bin 53780 -> 0 bytes res/{Themes => }/elementary Icons License.txt | 0 res/{Themes => }/elementary Icons.dat | Bin util/build.c | 2 +- util/build_core.c | 7 +- util/designer/designer.c | 144 +++--- util/designer2.cpp | 432 +++++++++++++++++- 13 files changed, 541 insertions(+), 188 deletions(-) create mode 100644 res/Theme.dat delete mode 100644 res/Themes/Theme.dat rename res/{Themes => }/elementary Icons License.txt (100%) rename res/{Themes => }/elementary Icons.dat (100%) 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 7deafaa649a8a30ba34b67e8cb24dd5b26b6a372..f28ff3cf2846ce6021917c13a1108f4bd427405b 100644 GIT binary patch delta 204 zcmbQenECl!<_)Jft|QDc`JunmElXVOnH)|T+XJTq{oXlm? z2vi!NB{4Z$nFpv`k+;c-0SF+Z)8vE7CYzN^>)Du^LMFepshTWna}LPd{MKfT)#gbR u&rK)S*8GFv%~Ln+;@n)g_X*SF@csWbhwry%VdULB{jeO@<}+u{GXnrm(@_=x delta 389 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 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