layer group object; property arrays

This commit is contained in:
nakst 2021-09-29 11:26:33 +01:00
parent 079667b97e
commit 55602bca69
2 changed files with 321 additions and 100 deletions

View File

@ -10,7 +10,6 @@
// 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
// Needed to replace the old designer:
// TODO Layers containing arrays.
// TODO Export.
// TODO Proper rendering using theme.cpp.
// TODO Implement remaining objects.
@ -152,72 +151,6 @@ void BlendPixel(uint32_t *destinationPixel, uint32_t modified, bool fullAlpha) {
//////////////////////////////////////////////////////////////
#define UI_SIZE_CHECKBOX_BOX (14)
#define UI_SIZE_CHECKBOX_GAP (8)
typedef struct UICheckbox {
#define UI_CHECKBOX_ALLOW_INDETERMINATE (1 << 0)
UIElement e;
#define UI_CHECK_UNCHECKED (0)
#define UI_CHECK_CHECKED (1)
#define UI_CHECK_INDETERMINATE (2)
uint8_t check;
char *label;
ptrdiff_t labelBytes;
void (*invoke)(void *cp);
} UICheckbox;
UICheckbox *UICheckboxCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes);
int _UICheckboxMessage(UIElement *element, UIMessage message, int di, void *dp) {
UICheckbox *box = (UICheckbox *) element;
if (message == UI_MSG_GET_HEIGHT) {
return UI_SIZE_BUTTON_HEIGHT * element->window->scale;
} else if (message == UI_MSG_GET_WIDTH) {
int labelSize = UIMeasureStringWidth(box->label, box->labelBytes);
return (labelSize + UI_SIZE_CHECKBOX_BOX + UI_SIZE_CHECKBOX_GAP) * element->window->scale;
} else if (message == UI_MSG_PAINT) {
UIPainter *painter = (UIPainter *) dp;
uint32_t color, textColor;
_UIButtonCalculateColors(element, &color, &textColor);
int midY = (element->bounds.t + element->bounds.b) / 2;
UIRectangle boxBounds = UI_RECT_4(element->bounds.l, element->bounds.l + UI_SIZE_CHECKBOX_BOX,
midY - UI_SIZE_CHECKBOX_BOX / 2, midY + UI_SIZE_CHECKBOX_BOX / 2);
UIDrawRectangle(painter, boxBounds, color, ui.theme.border, UI_RECT_1(1));
UIDrawString(painter, UIRectangleAdd(boxBounds, UI_RECT_4(1, 0, 0, 0)),
box->check == UI_CHECK_CHECKED ? "*" : box->check == UI_CHECK_INDETERMINATE ? "-" : " ", -1,
textColor, UI_ALIGN_CENTER, NULL);
UIDrawString(painter, UIRectangleAdd(element->bounds, UI_RECT_4(UI_SIZE_CHECKBOX_BOX + UI_SIZE_CHECKBOX_GAP, 0, 0, 0)),
box->label, box->labelBytes, textColor, UI_ALIGN_LEFT, NULL);
} else if (message == UI_MSG_UPDATE) {
UIElementRepaint(element, NULL);
} else if (message == UI_MSG_DESTROY) {
UI_FREE(box->label);
} else if (message == UI_MSG_KEY_TYPED) {
UIKeyTyped *m = (UIKeyTyped *) dp;
if (m->textBytes == 1 && m->text[0] == ' ') {
UIElementMessage(element, UI_MSG_CLICKED, 0, 0);
UIElementRepaint(element, NULL);
}
} else if (message == UI_MSG_CLICKED) {
box->check = (box->check + 1) % ((element->flags & UI_CHECKBOX_ALLOW_INDETERMINATE) ? 3 : 2);
UIElementRepaint(element, NULL);
if (box->invoke) box->invoke(element->cp);
}
return 0;
}
UICheckbox *UICheckboxCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes) {
UICheckbox *box = (UICheckbox *) UIElementCreate(sizeof(UICheckbox), parent, flags | UI_ELEMENT_TAB_STOP, _UICheckboxMessage, "Checkbox");
box->label = UIStringCopy(label, (box->labelBytes = labelBytes));
return box;
}
//////////////////////////////////////////////////////////////
#ifdef OS_ESSENCE
EsFileStore *fileStore;
@ -278,10 +211,10 @@ enum ObjectType : uint8_t {
OBJ_LAYER_BOX = 0x80,
OBJ_LAYER_METRICS,
OBJ_LAYER_TEXT,
OBJ_LAYER_GROUP,
// OBJ_LAYER_PATH,
// OBJ_LAYER_GROUP,
// OBJ_LAYER_SEQUENCE,
// OBJ_LAYER_SELECTOR,
// OBJ_LAYER_SEQUENCE,
OBJ_MOD_COLOR = 0xC0,
OBJ_MOD_MULTIPLY,
@ -312,6 +245,8 @@ enum StepApplyMode {
struct Step {
StepType type;
#define STEP_UPDATE_INSPECTOR (1 << 0)
uint32_t flags;
uint64_t objectID;
union {
@ -490,7 +425,12 @@ void DocumentApplyStep(Step step, StepApplyMode mode = STEP_APPLY_NORMAL) {
}
UIElementRepaint(canvas, nullptr);
InspectorAnnouncePropertyChanged(step.objectID, step.property.cName);
if (step.flags & STEP_UPDATE_INSPECTOR) {
InspectorPopulate();
} else {
InspectorAnnouncePropertyChanged(step.objectID, step.property.cName);
}
} else if (step.type == STEP_RENAME_OBJECT) {
Object *object = ObjectFind(step.objectID);
UI_ASSERT(object);
@ -530,7 +470,7 @@ void DocumentApplyStep(Step step, StepApplyMode mode = STEP_APPLY_NORMAL) {
if (mode == STEP_APPLY_NORMAL || mode == STEP_APPLY_GROUPED) {
bool merge = false;
if (allowMerge && undoStack.Length() > 2 && !redoStack.Length()) {
if (allowMerge && undoStack.Length() > 2 && !redoStack.Length() && (~step.flags & STEP_UPDATE_INSPECTOR)) {
Step last = undoStack[undoStack.Length() - 2];
if (step.type == STEP_MODIFY_PROPERTY && last.type == STEP_MODIFY_PROPERTY
@ -596,6 +536,42 @@ void DocumentRedoStep(void *) {
undoStack.Add(marker);
}
void DocumentSwapPropertyPrefixes(Object *object, Step step, const char *cPrefix0, const char *cPrefix1, bool last, bool moveOnly) {
char cNewName[PROPERTY_NAME_SIZE];
Array<Step> steps = {};
for (uintptr_t i = 0; i < object->properties.Length(); i++) {
if (0 == memcmp(object->properties[i].cName, cPrefix0, strlen(cPrefix0))
|| 0 == memcmp(object->properties[i].cName, cPrefix1, strlen(cPrefix1))) {
strcpy(step.property.cName, object->properties[i].cName);
step.property.type = PROP_NONE;
steps.Add(step);
}
}
for (uintptr_t i = 0; i < object->properties.Length(); i++) {
if (!moveOnly && 0 == memcmp(object->properties[i].cName, cPrefix0, strlen(cPrefix0))) {
strcpy(cNewName, cPrefix1);
strcat(cNewName, object->properties[i].cName + strlen(cPrefix0));
step.property = object->properties[i];
strcpy(step.property.cName, cNewName);
steps.Add(step);
} else if (0 == memcmp(object->properties[i].cName, cPrefix1, strlen(cPrefix1))) {
strcpy(cNewName, cPrefix0);
strcat(cNewName, object->properties[i].cName + strlen(cPrefix1));
step.property = object->properties[i];
strcpy(step.property.cName, cNewName);
steps.Add(step);
}
}
for (uintptr_t i = 0; i < steps.Length(); i++) {
DocumentApplyStep(steps[i], (i == steps.Length() - 1 && last) ? STEP_APPLY_NORMAL : STEP_APPLY_GROUPED);
}
steps.Free();
}
//////////////////////////////////////////////////////////////
enum InspectorElementType {
@ -611,12 +587,15 @@ enum InspectorElementType {
INSPECTOR_BOOLEAN_TOGGLE,
INSPECTOR_RADIO_SWITCH,
INSPECTOR_CURSOR_DROP_DOWN,
INSPECTOR_ADD_ARRAY_ITEM,
INSPECTOR_SWAP_ARRAY_ITEMS,
INSPECTOR_DELETE_ARRAY_ITEM,
};
struct InspectorBindingData {
UIElement *element;
uint64_t objectID;
const char *cPropertyName;
char cPropertyName[PROPERTY_NAME_SIZE];
const char *cEnablePropertyName;
InspectorElementType elementType;
int32_t radioValue;
@ -645,6 +624,10 @@ void InspectorUpdateSingleElementEnable(InspectorBindingData *data) {
}
void InspectorUpdateSingleElement(InspectorBindingData *data) {
if (data->element->flags & UI_ELEMENT_DESTROY) {
return;
}
if (data->elementType == INSPECTOR_REMOVE_BUTTON || data->elementType == INSPECTOR_REMOVE_BUTTON_BROADCAST) {
UIButton *button = (UIButton *) data->element;
Property *property = PropertyFind(ObjectFind(data->objectID), data->cPropertyName);
@ -699,6 +682,8 @@ void InspectorUpdateSingleElement(InspectorBindingData *data) {
UI_FREE(button->label);
button->label = UIStringCopy(string, (button->labelBytes = -1));
UIElementRefresh(&button->e);
} else if (data->elementType == INSPECTOR_ADD_ARRAY_ITEM || data->elementType == INSPECTOR_SWAP_ARRAY_ITEMS
|| data->elementType == INSPECTOR_DELETE_ARRAY_ITEM) {
} else {
UI_ASSERT(false);
}
@ -845,6 +830,80 @@ int InspectorBoundMessage(UIElement *element, UIMessage message, int di, void *d
inspectorMenuData = data;
UIMenuShow(menu);
} else if (data->elementType == INSPECTOR_ADD_ARRAY_ITEM) {
step.property.type = PROP_INT;
step.property.integer = 1 + PropertyReadInt32(ObjectFind(data->objectID), data->cPropertyName);
step.flags |= STEP_UPDATE_INSPECTOR;
DocumentApplyStep(step);
} else if (data->elementType == INSPECTOR_SWAP_ARRAY_ITEMS) {
char cPrefix0[PROPERTY_NAME_SIZE];
char cPrefix1[PROPERTY_NAME_SIZE];
Object *object = ObjectFind(data->objectID);
strcpy(cPrefix0, data->cPropertyName);
strcpy(cPrefix1, data->cPropertyName);
for (intptr_t i = strlen(cPrefix0) - 2; i >= 0; i--) {
if (cPrefix0[i] == '_') {
int32_t index = atoi(cPrefix0 + i + 1);
sprintf(cPrefix1 + i + 1, "%d_", index + 1);
break;
}
}
DocumentSwapPropertyPrefixes(object, step, cPrefix0, cPrefix1, true /* last */, false);
} else if (data->elementType == INSPECTOR_DELETE_ARRAY_ITEM) {
char cPrefix0[PROPERTY_NAME_SIZE];
char cPrefix1[PROPERTY_NAME_SIZE];
int32_t index = -1;
int32_t count = -1;
intptr_t offset = strlen(data->cPropertyName) - 2;
Object *object = ObjectFind(data->objectID);
for (; offset >= 0; offset--) {
if (data->cPropertyName[offset] == '_') {
index = atoi(data->cPropertyName + offset + 1);
break;
}
}
strcpy(cPrefix0, data->cPropertyName);
strcpy(cPrefix0 + offset + 1, "count");
count = PropertyReadInt32(ObjectFind(data->objectID), cPrefix0);
for (int32_t i = index; i < count - 1; i++) {
strcpy(cPrefix0, data->cPropertyName);
strcpy(cPrefix1, data->cPropertyName);
sprintf(cPrefix0 + offset + 1, "%d_", i + 0);
sprintf(cPrefix1 + offset + 1, "%d_", i + 1);
DocumentSwapPropertyPrefixes(object, step, cPrefix0, cPrefix1, false, true /* moveOnly */);
}
strcpy(cPrefix0, data->cPropertyName);
sprintf(cPrefix0 + offset + 1, "%d_", count - 1);
Array<Step> steps = {};
for (uintptr_t i = 0; i < object->properties.Length(); i++) {
if (0 == memcmp(object->properties[i].cName, cPrefix0, strlen(cPrefix0))) {
strcpy(step.property.cName, object->properties[i].cName);
step.property.type = PROP_NONE;
steps.Add(step);
}
}
for (uintptr_t i = 0; i < steps.Length(); i++) {
DocumentApplyStep(steps[i], STEP_APPLY_GROUPED);
}
steps.Free();
strcpy(step.property.cName, data->cPropertyName);
strcpy(step.property.cName + offset + 1, "count");
step.property.type = PROP_INT;
step.property.integer = count - 1;
step.flags |= STEP_UPDATE_INSPECTOR;
DocumentApplyStep(step);
}
} else if (message == UI_MSG_UPDATE) {
if (di == UI_UPDATE_FOCUSED && element->window->focused == element
@ -864,7 +923,7 @@ InspectorBindingData *InspectorBind(UIElement *element, uint64_t objectID, const
InspectorBindingData *data = (InspectorBindingData *) calloc(1, sizeof(InspectorBindingData));
data->element = element;
data->objectID = objectID;
data->cPropertyName = cPropertyName;
strcpy(data->cPropertyName, cPropertyName);
data->elementType = elementType;
data->radioValue = radioValue;
data->cEnablePropertyName = cEnablePropertyName;
@ -965,8 +1024,12 @@ void InspectorAddInteger(Object *object, const char *cLabel, const char *cProper
UIParentPop();
}
void InspectorAddFourGroup(Object *object, const char *cLabel, const char *cPropertyName0, const char *cPropertyName1,
const char *cPropertyName2, const char *cPropertyName3, const char *cEnablePropertyName = nullptr) {
void InspectorAddFourGroup(Object *object, const char *cLabel, const char *cPropertyNamePrefix,
const char *cEnablePropertyName = nullptr, bool defaultToIndividualTab = false) {
char cPropertyName0[PROPERTY_NAME_SIZE]; strcpy(cPropertyName0, cPropertyNamePrefix); strcat(cPropertyName0, "0");
char cPropertyName1[PROPERTY_NAME_SIZE]; strcpy(cPropertyName1, cPropertyNamePrefix); strcat(cPropertyName1, "1");
char cPropertyName2[PROPERTY_NAME_SIZE]; strcpy(cPropertyName2, cPropertyNamePrefix); strcat(cPropertyName2, "2");
char cPropertyName3[PROPERTY_NAME_SIZE]; strcpy(cPropertyName3, cPropertyNamePrefix); strcat(cPropertyName3, "3");
if (cLabel) UILabelCreate(0, 0, cLabel, -1);
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_ELEMENT_H_FILL | UI_PANEL_HORIZONTAL);
UITabPane *tabPane = UITabPaneCreate(0, UI_ELEMENT_PARENT_PUSH | UI_ELEMENT_H_FILL, "Single\tIndividual\tLink");
@ -985,7 +1048,7 @@ void InspectorAddFourGroup(Object *object, const char *cLabel, const char *cProp
int32_t b2 = PropertyReadInt32(object, cPropertyName2);
int32_t b3 = PropertyReadInt32(object, cPropertyName3);
if (property && property->type == PROP_OBJECT) tabPane->active = 2;
else if (b0 != b1 || b1 != b2 || b2 != b3) tabPane->active = 1;
else if (defaultToIndividualTab || b0 != b1 || b1 != b2 || b2 != b3) tabPane->active = 1;
InspectorBind(&UIButtonCreate(0, UI_BUTTON_SMALL, "X", 1)->e, object->id, cPropertyName0, INSPECTOR_REMOVE_BUTTON_BROADCAST);
UIParentPop();
}
@ -1015,10 +1078,12 @@ void InspectorPopulate() {
UIParentPop();
if (object->type != OBJ_VAR_COLOR && object->type != OBJ_VAR_INT
&& object->type != OBJ_MOD_COLOR && object->type != OBJ_MOD_MULTIPLY) {
&& object->type != OBJ_MOD_COLOR && object->type != OBJ_MOD_MULTIPLY
&& object->type != OBJ_LAYER_GROUP) {
InspectorAddLink(object, "Inherit from:", "_parent");
}
UIParentPop();
UISpacerCreate(0, 0, 0, 10);
if (object->type == OBJ_STYLE) {
@ -1043,8 +1108,8 @@ void InspectorPopulate() {
InspectorAddBooleanToggle(object, "Ellipsis", "ellipsis");
UIParentPop();
InspectorAddFourGroup(object, "Insets:", "insets0", "insets1", "insets2", "insets3");
InspectorAddFourGroup(object, "Clip insets:", "clipInsets0", "clipInsets1", "clipInsets2", "clipInsets3", "clipEnabled");
InspectorAddFourGroup(object, "Insets:", "insets");
InspectorAddFourGroup(object, "Clip insets:", "clipInsets", "clipEnabled");
UILabelCreate(0, 0, "Preferred size:", -1);
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL);
@ -1112,12 +1177,55 @@ void InspectorPopulate() {
InspectorAddLink(object, "Icon color:", "iconColor");
InspectorAddInteger(object, "Icon size:", "iconSize");
} else if (object->type == OBJ_LAYER_BOX) {
InspectorAddFourGroup(object, "Border sizes:", "borders0", "borders1", "borders2", "borders3");
InspectorAddFourGroup(object, "Border sizes:", "borders");
InspectorAddFourGroup(object, "Corner radii:", "corners");
InspectorAddLink(object, "Fill paint:", "mainPaint");
InspectorAddLink(object, "Border paint:", "borderPaint");
InspectorAddBooleanToggle(object, "Blurred", "isBlurred");
InspectorAddBooleanToggle(object, "Auto-corners", "autoCorners");
InspectorAddBooleanToggle(object, "Auto-borders", "autoBorders");
InspectorAddBooleanToggle(object, "Shadow hiding", "shadowHiding");
} else if (object->type == OBJ_LAYER_TEXT) {
InspectorAddLink(object, "Text color:", "color");
InspectorAddInteger(object, "Blur radius:", "blur");
} else if (object->type == OBJ_LAYER_GROUP) {
int32_t layerCount = PropertyReadInt32(object, "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];
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_BORDER | UI_PANEL_MEDIUM_SPACING | UI_PANEL_EXPAND);
sprintf(cPropertyName, "layers_%d_", i);
UIPanel *row = UIPanelCreate(0, UI_PANEL_HORIZONTAL);
UISpacerCreate(&row->e, UI_ELEMENT_H_FILL, 0, 0);
InspectorBind(&UIButtonCreate(&row->e, UI_BUTTON_SMALL, "Delete", -1)->e, object->id, cPropertyName, INSPECTOR_DELETE_ARRAY_ITEM);
sprintf(cPropertyName, "layers_%d_layer", i);
InspectorAddLink(object, "Layer:", cPropertyName);
sprintf(cPropertyName, "layers_%d_offset", i);
InspectorAddFourGroup(object, "Offset (dpx):", cPropertyName, nullptr, true /* defaultToIndividualTab */);
sprintf(cPropertyName, "layers_%d_position", i);
InspectorAddFourGroup(object, "Position (%):", cPropertyName, nullptr, true /* defaultToIndividualTab */);
sprintf(cPropertyName, "layers_%d_mode", i);
UILabelCreate(0, 0, "Mode:", -1);
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL);
InspectorAddRadioSwitch(object, "Background", cPropertyName, 0);
InspectorAddRadioSwitch(object, "Shadow", cPropertyName, 1);
InspectorAddRadioSwitch(object, "Content", cPropertyName, 2);
InspectorAddRadioSwitch(object, "Overlay", cPropertyName, 3);
InspectorBind(&UIButtonCreate(0, UI_BUTTON_SMALL, "X", 1)->e, object->id, cPropertyName, INSPECTOR_REMOVE_BUTTON);
UIParentPop();
UIParentPop();
if (i != layerCount - 1) {
sprintf(cPropertyName, "layers_%d_", i);
InspectorBind(&UIButtonCreate(&UIPanelCreate(0, 0)->e, UI_BUTTON_SMALL, "Swap", -1)->e,
object->id, cPropertyName, INSPECTOR_SWAP_ARRAY_ITEMS);
}
}
InspectorBind(&UIButtonCreate(0, 0, "Add layer", -1)->e, object->id, "layers_count", INSPECTOR_ADD_ARRAY_ITEM);
} else if (object->type == OBJ_PAINT_OVERWRITE) {
InspectorAddLink(object, "Color:", "color");
} else if (object->type == OBJ_MOD_COLOR) {
@ -1254,6 +1362,71 @@ uint32_t CanvasGetColorFromPaint(Object *object, int depth = 0) {
}
}
void CanvasDrawLayer(Object *object, UIRectangle bounds, UIPainter *painter, int depth = 0) {
// TODO Proper rendering.
if (!object || depth == 10) {
return;
}
if (object->type == OBJ_LAYER_BOX) {
UIRectangle borders;
borders.l = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders0"));
borders.r = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders1"));
borders.t = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders2"));
borders.b = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders3"));
Property *mainPaintProperty = PropertyFindOrInherit(object, "mainPaint", PROP_OBJECT);
Object *mainPaint = ObjectFind(mainPaintProperty ? mainPaintProperty->object : 0);
uint32_t mainPaintColor = CanvasGetColorFromPaint(mainPaint);
Property *borderPaintProperty = PropertyFindOrInherit(object, "borderPaint", PROP_OBJECT);
Object *borderPaint = ObjectFind(borderPaintProperty ? borderPaintProperty->object : 0);
uint32_t borderPaintColor = CanvasGetColorFromPaint(borderPaint);
UIDrawRectangle(painter, bounds, mainPaintColor, borderPaintColor, borders);
} else if (object->type == OBJ_LAYER_GROUP) {
int32_t layerCount = PropertyReadInt32(object, "layers_count");
if (layerCount < 0) layerCount = 0;
if (layerCount > 100) layerCount = 100;
int32_t inWidth = UI_RECT_WIDTH(bounds);
int32_t inHeight = UI_RECT_HEIGHT(bounds);
for (int32_t i = 0; i < layerCount; i++) {
char cPropertyName[PROPERTY_NAME_SIZE];
sprintf(cPropertyName, "layers_%d_layer", i);
Property *layerProperty = PropertyFind(object, cPropertyName, PROP_OBJECT);
Object *layerObject = ObjectFind(layerProperty ? layerProperty->object : 0);
sprintf(cPropertyName, "layers_%d_offset0", i);
int32_t offset0 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_offset1", i);
int32_t offset1 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_offset2", i);
int32_t offset2 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_offset3", i);
int32_t offset3 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_position0", i);
int32_t position0 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_position1", i);
int32_t position1 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_position2", i);
int32_t position2 = PropertyReadInt32(object, cPropertyName);
sprintf(cPropertyName, "layers_%d_position3", i);
int32_t position3 = PropertyReadInt32(object, cPropertyName);
UIRectangle outBounds;
outBounds.l = bounds.l + offset0 + position0 * inWidth / 100;
outBounds.r = bounds.l + offset1 + position1 * inWidth / 100;
outBounds.t = bounds.t + offset2 + position2 * inHeight / 100;
outBounds.b = bounds.t + offset3 + position3 * inHeight / 100;
CanvasDrawLayer(layerObject, outBounds, painter, depth + 1);
}
}
}
int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_PAINT) {
UIPainter *painter = (UIPainter *) dp;
@ -1291,26 +1464,10 @@ int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) {
} else if (object->type == OBJ_VAR_TEXT_STYLE) {
// TODO Proper rendering.
UIDrawString(painter, bounds, "Text", -1, 0xFF000000, UI_ALIGN_CENTER, nullptr);
} else if (object->type == OBJ_LAYER_BOX) {
// TODO Proper rendering.
UIRectangle borders;
borders.l = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders0"));
borders.r = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders1"));
borders.t = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders2"));
borders.b = CanvasGetIntegerFromProperty(PropertyFindOrInherit(object, "borders3"));
Property *mainPaintProperty = PropertyFindOrInherit(object, "mainPaint", PROP_OBJECT);
Object *mainPaint = ObjectFind(mainPaintProperty ? mainPaintProperty->object : 0);
uint32_t mainPaintColor = CanvasGetColorFromPaint(mainPaint);
Property *borderPaintProperty = PropertyFindOrInherit(object, "borderPaint", PROP_OBJECT);
Object *borderPaint = ObjectFind(borderPaintProperty ? borderPaintProperty->object : 0);
uint32_t borderPaintColor = CanvasGetColorFromPaint(borderPaint);
UIDrawRectangle(painter, bounds, mainPaintColor, borderPaintColor, borders);
} else if (object->type == OBJ_LAYER_BOX || object->type == OBJ_LAYER_GROUP) {
CanvasDrawLayer(object, bounds, painter);
} else {
// TODO.
// TODO Preview for more object types.
}
}
@ -1460,6 +1617,7 @@ void ObjectAddCommand(void *) {
UIMenuAddItem(menu, 0, "Overwrite paint", -1, ObjectAddCommandInternal, (void *) (uintptr_t) OBJ_PAINT_OVERWRITE);
UIMenuAddItem(menu, 0, "Box layer", -1, ObjectAddCommandInternal, (void *) (uintptr_t) OBJ_LAYER_BOX);
UIMenuAddItem(menu, 0, "Text layer", -1, ObjectAddCommandInternal, (void *) (uintptr_t) OBJ_LAYER_TEXT);
UIMenuAddItem(menu, 0, "Layer group", -1, ObjectAddCommandInternal, (void *) (uintptr_t) OBJ_LAYER_GROUP);
UIMenuAddItem(menu, 0, "Modify color", -1, ObjectAddCommandInternal, (void *) (uintptr_t) OBJ_MOD_COLOR);
UIMenuAddItem(menu, 0, "Modify integer", -1, ObjectAddCommandInternal, (void *) (uintptr_t) OBJ_MOD_MULTIPLY);
UIMenuShow(menu);
@ -1500,7 +1658,7 @@ void ObjectDuplicateCommand(void *) {
int WindowMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_WINDOW_CLOSE) {
#ifndef OS_ESSENCE
if (documentModified) {
if (documentModified && !window->dialog) {
const char *dialog = "Document modified. Save changes?\n%f%b%b";
const char *result = UIDialogShow(window, 0, dialog, "Save", "Discard");

View File

@ -88,6 +88,9 @@ typedef struct UITheme {
#define UI_SIZE_BUTTON_HEIGHT (27)
#define UI_SIZE_BUTTON_CHECKED_AREA (4)
#define UI_SIZE_CHECKBOX_BOX (14)
#define UI_SIZE_CHECKBOX_GAP (8)
#define UI_SIZE_MENU_ITEM_HEIGHT (24)
#define UI_SIZE_MENU_ITEM_MINIMUM_WIDTH (160)
#define UI_SIZE_MENU_ITEM_MARGIN (9)
@ -420,6 +423,18 @@ typedef struct UIButton {
void (*invoke)(void *cp);
} UIButton;
typedef struct UICheckbox {
#define UI_CHECKBOX_ALLOW_INDETERMINATE (1 << 0)
UIElement e;
#define UI_CHECK_UNCHECKED (0)
#define UI_CHECK_CHECKED (1)
#define UI_CHECK_INDETERMINATE (2)
uint8_t check;
char *label;
ptrdiff_t labelBytes;
void (*invoke)(void *cp);
} UICheckbox;
typedef struct UILabel {
UIElement e;
char *label;
@ -562,6 +577,7 @@ UIElement *UIElementCreate(size_t bytes, UIElement *parent, uint32_t flags,
int (*messageClass)(UIElement *, UIMessage, int, void *), const char *cClassName);
UIButton *UIButtonCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes);
UICheckbox *UICheckboxCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes);
UIColorPicker *UIColorPickerCreate(UIElement *parent, uint32_t flags);
UIExpandPane *UIExpandPaneCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes, uint32_t panelFlags);
UIGauge *UIGaugeCreate(UIElement *parent, uint32_t flags);
@ -1784,6 +1800,53 @@ UIButton *UIButtonCreate(UIElement *parent, uint32_t flags, const char *label, p
return button;
}
int _UICheckboxMessage(UIElement *element, UIMessage message, int di, void *dp) {
UICheckbox *box = (UICheckbox *) element;
if (message == UI_MSG_GET_HEIGHT) {
return UI_SIZE_BUTTON_HEIGHT * element->window->scale;
} else if (message == UI_MSG_GET_WIDTH) {
int labelSize = UIMeasureStringWidth(box->label, box->labelBytes);
return (labelSize + UI_SIZE_CHECKBOX_BOX + UI_SIZE_CHECKBOX_GAP) * element->window->scale;
} else if (message == UI_MSG_PAINT) {
UIPainter *painter = (UIPainter *) dp;
uint32_t color, textColor;
_UIButtonCalculateColors(element, &color, &textColor);
int midY = (element->bounds.t + element->bounds.b) / 2;
UIRectangle boxBounds = UI_RECT_4(element->bounds.l, element->bounds.l + UI_SIZE_CHECKBOX_BOX,
midY - UI_SIZE_CHECKBOX_BOX / 2, midY + UI_SIZE_CHECKBOX_BOX / 2);
UIDrawRectangle(painter, boxBounds, color, ui.theme.border, UI_RECT_1(1));
UIDrawString(painter, UIRectangleAdd(boxBounds, UI_RECT_4(1, 0, 0, 0)),
box->check == UI_CHECK_CHECKED ? "*" : box->check == UI_CHECK_INDETERMINATE ? "-" : " ", -1,
textColor, UI_ALIGN_CENTER, NULL);
UIDrawString(painter, UIRectangleAdd(element->bounds, UI_RECT_4(UI_SIZE_CHECKBOX_BOX + UI_SIZE_CHECKBOX_GAP, 0, 0, 0)),
box->label, box->labelBytes, textColor, UI_ALIGN_LEFT, NULL);
} else if (message == UI_MSG_UPDATE) {
UIElementRepaint(element, NULL);
} else if (message == UI_MSG_DESTROY) {
UI_FREE(box->label);
} else if (message == UI_MSG_KEY_TYPED) {
UIKeyTyped *m = (UIKeyTyped *) dp;
if (m->textBytes == 1 && m->text[0] == ' ') {
UIElementMessage(element, UI_MSG_CLICKED, 0, 0);
UIElementRepaint(element, NULL);
}
} else if (message == UI_MSG_CLICKED) {
box->check = (box->check + 1) % ((element->flags & UI_CHECKBOX_ALLOW_INDETERMINATE) ? 3 : 2);
UIElementRepaint(element, NULL);
if (box->invoke) box->invoke(element->cp);
}
return 0;
}
UICheckbox *UICheckboxCreate(UIElement *parent, uint32_t flags, const char *label, ptrdiff_t labelBytes) {
UICheckbox *box = (UICheckbox *) UIElementCreate(sizeof(UICheckbox), parent, flags | UI_ELEMENT_TAB_STOP, _UICheckboxMessage, "Checkbox");
box->label = UIStringCopy(label, (box->labelBytes = labelBytes));
return box;
}
int _UILabelMessage(UIElement *element, UIMessage message, int di, void *dp) {
UILabel *label = (UILabel *) element;