designer2 zooming and preview state; bugfixes

This commit is contained in:
nakst 2021-09-30 16:47:26 +01:00
parent d5271bec8f
commit 5fae7f2d13
3 changed files with 153 additions and 87 deletions

View File

@ -1803,8 +1803,6 @@ void ApplicationInstanceCrashed(EsMessage *message) {
// Restart the installer.
ApplicationInstanceCreate(desktop.installer->id, nullptr, nullptr, true /* hidden */);
}
ApplicationTemporaryDestroy(application);
}
for (uintptr_t i = 0; i < desktop.allApplicationInstances.Length(); i++) {

View File

@ -585,11 +585,12 @@ void GraphicsDebugPutBlock32(uintptr_t x, uintptr_t y, bool toggle,
void GraphicsDebugClearScreen32(unsigned screenWidth, unsigned screenHeight, unsigned stride, volatile uint8_t *linearBuffer) {
for (uintptr_t i = 0; i < screenHeight; i++) {
for (uintptr_t j = 0; j < screenWidth * 4; j += 4) {
#if 0
linearBuffer[i * stride + j + 2] = 0x18;
linearBuffer[i * stride + j + 1] = 0x7E;
linearBuffer[i * stride + j + 0] = 0xCF;
#if 0
#else
if (graphics.debuggerActive) {
linearBuffer[i * stride + j + 2] = 0x18;
linearBuffer[i * stride + j + 1] = 0x7E;

View File

@ -15,15 +15,17 @@
// 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:
// Prototyping display: previewing state transitions.
// Implement path layers, and test radial gradients with them.
// Prototyping display: previewing state transitions.
// Import and reorganize old theming data.
// Export.
// TODO Additional features:
// Resizing objects?
// Fix moving/resizing objects when zoomed in.
// Resizing graph objects?
// Find object in graph by name.
// Auto-layout in the prototype display.
// Importing SVGs and TTFs.
//////////////////////////////////////////////////////////////
@ -180,19 +182,30 @@ const EsInstanceClassEditorSettings instanceClassEditorSettings = {
#endif
struct Canvas : UIElement {
bool showArrows;
bool showPrototype;
float zoom;
float panX, panY;
float swapZoom;
float swapPanX, swapPanY;
float lastPanPointX, lastPanPointY;
bool dragging, canDrag, selecting;
int32_t dragDeltaX, dragDeltaY;
int32_t selectX, selectY;
int32_t dragOffsetX, dragOffsetY;
int32_t leftDownX, leftDownY;
bool showArrows;
bool showPrototype;
UIRectangle originalBounds;
UIRectangle resizeOffsets;
bool resizing;
UIElement *resizeHandles[4];
int32_t previewPrimaryState;
int32_t previewStateBits;
bool previewStateActive;
};
struct Prototype : UIElement {
@ -208,6 +221,7 @@ void InspectorAnnouncePropertyChanged(uint64_t objectID, const char *cPropertyNa
void InspectorPopulate();
void InspectorPickTargetEnd();
void CanvasSelectObject(struct Object *object);
void CanvasSwitchView(void *cp);
//////////////////////////////////////////////////////////////
@ -331,6 +345,11 @@ void ObjectSetSelected(uint64_t id, bool removeSelectedFlagFromPreviousSelection
Object *object = ObjectFind(selectedObjectID);
if (object) object->flags |= OBJECT_IS_SELECTED;
}
if (!selectedObjectID) {
canvas->previewPrimaryState = THEME_PRIMARY_STATE_IDLE;
canvas->previewStateBits = 0;
}
}
Property *PropertyFind(Object *object, const char *cName, uint8_t type = 0) {
@ -355,21 +374,20 @@ int32_t PropertyReadInt32(Object *object, const char *cName, int32_t defaultValu
}
bool ObjectIsConditional(Object *object) {
return PropertyReadInt32(object, "_primaryState")
|| PropertyReadInt32(object, "_stateFocused")
|| PropertyReadInt32(object, "_stateChecked")
|| PropertyReadInt32(object, "_stateIndeterminate")
|| PropertyReadInt32(object, "_stateDefaultButton")
|| PropertyReadInt32(object, "_stateSelected")
|| PropertyReadInt32(object, "_stateFocusedItem")
|| PropertyReadInt32(object, "_stateListFocused")
|| PropertyReadInt32(object, "_stateBeforeEnter")
|| PropertyReadInt32(object, "_stateAfterExit");
return PropertyReadInt32(object, "_primaryState") || PropertyReadInt32(object, "_stateBits");
}
bool ObjectMatchesPreviewState(Object *object) {
int32_t primaryState = PropertyReadInt32(object, "_primaryState");
int32_t stateBits = PropertyReadInt32(object, "_stateBits");
return (primaryState == canvas->previewPrimaryState || !primaryState)
&& ((stateBits & canvas->previewStateBits) == stateBits);
}
Property *PropertyFindOrInherit(bool first, Object *object, const char *cName, uint8_t type = 0) {
while (object) {
if (first || !ObjectIsConditional(object)) {
if (first || !ObjectIsConditional(object) || (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;
@ -681,6 +699,7 @@ enum InspectorElementType {
INSPECTOR_LINK,
INSPECTOR_LINK_BROADCAST,
INSPECTOR_BOOLEAN_TOGGLE,
INSPECTOR_MASK_BIT_TOGGLE,
INSPECTOR_RADIO_SWITCH,
INSPECTOR_CURSOR_DROP_DOWN,
INSPECTOR_ADD_ARRAY_ITEM,
@ -694,7 +713,7 @@ struct InspectorBindingData {
char cPropertyName[PROPERTY_NAME_SIZE];
const char *cEnablePropertyName;
InspectorElementType elementType;
int32_t radioValue;
int32_t choiceValue;
};
Array<UIElement *> inspectorBoundElements;
@ -734,10 +753,14 @@ void InspectorUpdateSingleElement(InspectorBindingData *data) {
box->check = PropertyReadInt32(ObjectFind(data->objectID), data->cPropertyName, 2);
if ((~box->e.flags & UI_CHECKBOX_ALLOW_INDETERMINATE)) box->check &= 1;
UIElementRefresh(&box->e);
} else if (data->elementType == INSPECTOR_MASK_BIT_TOGGLE) {
UICheckbox *box = (UICheckbox *) data->element;
box->check = (PropertyReadInt32(ObjectFind(data->objectID), data->cPropertyName) & data->choiceValue) ? UI_CHECK_CHECKED : UI_CHECK_UNCHECKED;
UIElementRefresh(&box->e);
} else if (data->elementType == INSPECTOR_RADIO_SWITCH) {
UIButton *button = (UIButton *) data->element;
int32_t value = PropertyReadInt32(ObjectFind(data->objectID), data->cPropertyName);
if (value == data->radioValue) button->e.flags |= UI_BUTTON_CHECKED;
if (value == data->choiceValue) button->e.flags |= UI_BUTTON_CHECKED;
else button->e.flags &= ~UI_BUTTON_CHECKED;
UIElementRefresh(&button->e);
} else if (data->elementType == INSPECTOR_CURSOR_DROP_DOWN) {
@ -925,7 +948,7 @@ int InspectorBoundMessage(UIElement *element, UIMessage message, int di, void *d
free(name);
} else if (data->elementType == INSPECTOR_RADIO_SWITCH) {
step.property.type = PROP_INT;
step.property.integer = data->radioValue;
step.property.integer = data->choiceValue;
DocumentApplyStep(step);
} else if (data->elementType == INSPECTOR_BOOLEAN_TOGGLE) {
UICheckbox *box = (UICheckbox *) element;
@ -933,6 +956,14 @@ int InspectorBoundMessage(UIElement *element, UIMessage message, int di, void *d
step.property.type = step.property.integer == UI_CHECK_INDETERMINATE ? PROP_NONE : PROP_INT;
DocumentApplyStep(step);
return 1; // InspectorUpdateSingleElement will update the check.
} else if (data->elementType == INSPECTOR_MASK_BIT_TOGGLE) {
UICheckbox *box = (UICheckbox *) element;
step.property.type = PROP_INT;
step.property.integer = PropertyReadInt32(ObjectFind(data->objectID), data->cPropertyName);
if (box->check) step.property.integer &= ~data->choiceValue;
else step.property.integer |= data->choiceValue;
DocumentApplyStep(step);
return 1; // InspectorUpdateSingleElement will update the check.
} else if (data->elementType == INSPECTOR_CURSOR_DROP_DOWN) {
UIMenu *menu = UIMenuCreate(window->pressed, UI_MENU_NO_SCROLL);
UIMenuAddItem(menu, 0, "Inherit", -1, InspectorCursorDropDownMenuInvoke, (void *) (intptr_t) -1);
@ -1032,13 +1063,13 @@ int InspectorBoundMessage(UIElement *element, UIMessage message, int di, void *d
}
InspectorBindingData *InspectorBind(UIElement *element, uint64_t objectID, const char *cPropertyName, InspectorElementType elementType,
int32_t radioValue = 0, const char *cEnablePropertyName = nullptr) {
int32_t choiceValue = 0, const char *cEnablePropertyName = nullptr) {
InspectorBindingData *data = (InspectorBindingData *) calloc(1, sizeof(InspectorBindingData));
data->element = element;
data->objectID = objectID;
strcpy(data->cPropertyName, cPropertyName);
data->elementType = elementType;
data->radioValue = radioValue;
data->choiceValue = choiceValue;
data->cEnablePropertyName = cEnablePropertyName;
element->cp = data;
element->messageUser = InspectorBoundMessage;
@ -1077,18 +1108,7 @@ void InspectorAutoNameObject(void *) {
}
int32_t primaryState = PropertyReadInt32(object, "_primaryState");
int32_t stateBits[] = {
PropertyReadInt32(object, "_stateFocused"),
PropertyReadInt32(object, "_stateChecked"),
PropertyReadInt32(object, "_stateIndeterminate"),
PropertyReadInt32(object, "_stateDefaultButton"),
PropertyReadInt32(object, "_stateSelected"),
PropertyReadInt32(object, "_stateFocusedItem"),
PropertyReadInt32(object, "_stateListFocused"),
PropertyReadInt32(object, "_stateBeforeEnter"),
PropertyReadInt32(object, "_stateAfterExit"),
};
int32_t stateBits = PropertyReadInt32(object, "_stateBits");
Step step = {};
step.type = STEP_RENAME_OBJECT;
@ -1096,8 +1116,8 @@ void InspectorAutoNameObject(void *) {
snprintf(step.cName, sizeof(step.cName), "?%s", primaryState ? cPrimaryStateStrings[primaryState] : "");
for (uintptr_t i = 0; i < sizeof(stateBits) / sizeof(stateBits[0]); i++) {
if (stateBits[i]) {
for (uintptr_t i = 0; i < 16; i++) {
if (stateBits & (1 << (15 - i))) {
snprintf(step.cName + strlen(step.cName), sizeof(step.cName) - strlen(step.cName), "%s%s", i || primaryState ? "&" : "", cStateBitStrings[i]);
}
}
@ -1236,13 +1256,52 @@ void InspectorAddFourGroup(Object *object, const char *cLabel, const char *cProp
UIParentPop();
}
void InspectorAddBooleanToggle(Object *object, const char *cLabel, const char *cPropertyName, bool allowIndeterminate = true) {
InspectorBind(&UICheckboxCreate(0, allowIndeterminate ? UI_CHECKBOX_ALLOW_INDETERMINATE : 0, cLabel, -1)->e,
object->id, cPropertyName, INSPECTOR_BOOLEAN_TOGGLE);
void InspectorAddBooleanToggle(Object *object, const char *cLabel, const char *cPropertyName) {
InspectorBind(&UICheckboxCreate(0, UI_CHECKBOX_ALLOW_INDETERMINATE, cLabel, -1)->e, object->id, cPropertyName, INSPECTOR_BOOLEAN_TOGGLE);
}
void InspectorAddRadioSwitch(Object *object, const char *cLabel, const char *cPropertyName, int32_t radioValue) {
InspectorBind(&UIButtonCreate(0, UI_BUTTON_SMALL, cLabel, -1)->e, object->id, cPropertyName, INSPECTOR_RADIO_SWITCH, radioValue);
void InspectorAddRadioSwitch(Object *object, const char *cLabel, const char *cPropertyName, int32_t choiceValue) {
InspectorBind(&UIButtonCreate(0, UI_BUTTON_SMALL, cLabel, -1)->e, object->id, cPropertyName, INSPECTOR_RADIO_SWITCH, choiceValue);
}
void InspectorAddMaskBitToggle(Object *object, const char *cLabel, const char *cPropertyName, int32_t bit) {
InspectorBind(&UICheckboxCreate(0, 0, cLabel, -1)->e, object->id, cPropertyName, INSPECTOR_MASK_BIT_TOGGLE, bit);
}
int InspectorPreviewPrimaryStateButtonMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_CLICKED) {
UIElement *sibling = element->parent->children;
canvas->previewPrimaryState = (uintptr_t) element->cp;
UIElementRefresh(canvas);
while (sibling) {
if (sibling == element) sibling->flags |= UI_BUTTON_CHECKED;
else sibling->flags &= ~UI_BUTTON_CHECKED;
UIElementRefresh(sibling);
sibling = sibling->next;
}
}
return 0;
}
void InspectorAddPreviewPrimaryStateButton(const char *cLabel, int32_t value) {
UIButton *button = UIButtonCreate(0, UI_BUTTON_SMALL, cLabel, -1);
button->e.cp = (void *) (uintptr_t) value;
button->e.messageUser = InspectorPreviewPrimaryStateButtonMessage;
if (canvas->previewPrimaryState == value) button->e.flags |= UI_BUTTON_CHECKED;
}
void InspectorPreviewStateBitsCheckboxInvoke(void *cp) {
canvas->previewStateBits ^= (uintptr_t) cp;
UIElementRefresh(canvas);
}
void InspectorAddPreviewStateBitsCheckbox(const char *cLabel, int32_t bit) {
UICheckbox *box = UICheckboxCreate(0, 0, cLabel, -1);
box->e.cp = (void *) (uintptr_t) bit;
box->invoke = InspectorPreviewStateBitsCheckboxInvoke;
if (canvas->previewStateBits & bit) box->check = UI_CHECK_CHECKED;
}
void InspectorPopulate() {
@ -1280,21 +1339,20 @@ void InspectorPopulate() {
InspectorBind(&UIButtonCreate(0, UI_BUTTON_SMALL, "X", 1)->e, object->id, "_primaryState", INSPECTOR_REMOVE_BUTTON);
UIParentPop();
// TODO Change these to be stored internally as a mask of bits.
UILabelCreate(0, 0, "State bits:", -1);
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_EXPAND)->gap = -5;
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL)->gap = 8;
InspectorAddBooleanToggle(object, cStateBitStrings[0], "_stateFocused", false);
InspectorAddBooleanToggle(object, cStateBitStrings[1], "_stateChecked", false);
InspectorAddBooleanToggle(object, cStateBitStrings[2], "_stateIndeterminate", false);
InspectorAddBooleanToggle(object, cStateBitStrings[3], "_stateDefaultButton", false);
InspectorAddBooleanToggle(object, cStateBitStrings[4], "_stateSelected", false);
InspectorAddMaskBitToggle(object, cStateBitStrings[0], "_stateBits", THEME_STATE_FOCUSED);
InspectorAddMaskBitToggle(object, cStateBitStrings[1], "_stateBits", THEME_STATE_CHECKED);
InspectorAddMaskBitToggle(object, cStateBitStrings[2], "_stateBits", THEME_STATE_INDETERMINATE);
InspectorAddMaskBitToggle(object, cStateBitStrings[3], "_stateBits", THEME_STATE_DEFAULT_BUTTON);
InspectorAddMaskBitToggle(object, cStateBitStrings[4], "_stateBits", THEME_STATE_SELECTED);
UIParentPop();
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL)->gap = 8;
InspectorAddBooleanToggle(object, cStateBitStrings[5], "_stateFocusedItem", false);
InspectorAddBooleanToggle(object, cStateBitStrings[6], "_stateListFocused", false);
InspectorAddBooleanToggle(object, cStateBitStrings[7], "_stateBeforeEnter", false);
InspectorAddBooleanToggle(object, cStateBitStrings[8], "_stateAfterExit", false);
InspectorAddMaskBitToggle(object, cStateBitStrings[5], "_stateBits", THEME_STATE_FOCUSED_ITEM);
InspectorAddMaskBitToggle(object, cStateBitStrings[6], "_stateBits", THEME_STATE_LIST_FOCUSED);
InspectorAddMaskBitToggle(object, cStateBitStrings[7], "_stateBits", THEME_STATE_BEFORE_ENTER);
InspectorAddMaskBitToggle(object, cStateBitStrings[8], "_stateBits", THEME_STATE_AFTER_EXIT);
UIParentPop();
UIParentPop();
@ -1544,25 +1602,25 @@ void InspectorPopulate() {
UILabelCreate(0, 0, "Preview state:", -1);
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL);
UIButtonCreate(0, UI_BUTTON_SMALL | UI_BUTTON_CHECKED, "Idle", -1);
UIButtonCreate(0, UI_BUTTON_SMALL, "Hovered", -1);
UIButtonCreate(0, UI_BUTTON_SMALL, "Pressed", -1);
UIButtonCreate(0, UI_BUTTON_SMALL, "Disabled", -1);
UIButtonCreate(0, UI_BUTTON_SMALL, "Inactive", -1);
InspectorAddPreviewPrimaryStateButton(cPrimaryStateStrings[1], THEME_PRIMARY_STATE_IDLE);
InspectorAddPreviewPrimaryStateButton(cPrimaryStateStrings[2], THEME_PRIMARY_STATE_HOVERED);
InspectorAddPreviewPrimaryStateButton(cPrimaryStateStrings[3], THEME_PRIMARY_STATE_PRESSED);
InspectorAddPreviewPrimaryStateButton(cPrimaryStateStrings[4], THEME_PRIMARY_STATE_DISABLED);
InspectorAddPreviewPrimaryStateButton(cPrimaryStateStrings[5], THEME_PRIMARY_STATE_INACTIVE);
UIParentPop();
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_EXPAND)->gap = -5;
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL)->gap = 8;
UICheckboxCreate(0, 0, cStateBitStrings[0], -1);
UICheckboxCreate(0, 0, cStateBitStrings[1], -1);
UICheckboxCreate(0, 0, cStateBitStrings[2], -1);
UICheckboxCreate(0, 0, cStateBitStrings[3], -1);
UICheckboxCreate(0, 0, cStateBitStrings[4], -1);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[0], 1 << 15);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[1], 1 << 14);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[2], 1 << 13);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[3], 1 << 12);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[4], 1 << 11);
UIParentPop();
UIPanelCreate(0, UI_ELEMENT_PARENT_PUSH | UI_PANEL_HORIZONTAL)->gap = 8;
UICheckboxCreate(0, 0, cStateBitStrings[5], -1);
UICheckboxCreate(0, 0, cStateBitStrings[6], -1);
UICheckboxCreate(0, 0, cStateBitStrings[7], -1);
UICheckboxCreate(0, 0, cStateBitStrings[8], -1);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[5], 1 << 10);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[6], 1 << 9);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[7], 1 << 8);
InspectorAddPreviewStateBitsCheckbox(cStateBitStrings[8], 1 << 7);
UIParentPop();
UIParentPop();
} else {
@ -1767,10 +1825,10 @@ void ExportPaintAsLayerBox(Object *object, EsBuffer *data) {
#define CANVAS_ALIGN (20)
UIRectangle CanvasGetObjectBounds(Object *object) {
int32_t x = PropertyReadInt32(object, "_graphX") - canvas->panX + canvas->bounds.l;
int32_t y = PropertyReadInt32(object, "_graphY") - canvas->panY + canvas->bounds.t;
int32_t w = PropertyReadInt32(object, "_graphW");
int32_t h = PropertyReadInt32(object, "_graphH");
int32_t x = (PropertyReadInt32(object, "_graphX") - canvas->panX) * canvas->zoom + canvas->bounds.l;
int32_t y = (PropertyReadInt32(object, "_graphY") - canvas->panY) * canvas->zoom + canvas->bounds.t;
int32_t w = PropertyReadInt32(object, "_graphW") * canvas->zoom;
int32_t h = PropertyReadInt32(object, "_graphH") * canvas->zoom;
UIRectangle bounds = UI_RECT_4(x, x + w, y, y + h);
@ -1792,11 +1850,11 @@ void CanvasSelectObject(Object *object) {
objects[i].flags &= ~OBJECT_IS_SELECTED;
}
if (canvas->showPrototype) CanvasSwitchView(nullptr);
UIRectangle bounds = CanvasGetObjectBounds(object);
canvas->panX += bounds.l - UI_RECT_WIDTH(canvas->bounds) / 2;
canvas->panY += bounds.t - UI_RECT_HEIGHT(canvas->bounds) / 2;
ObjectSetSelected(object->id);
canvas->showPrototype = false;
UIElementRefresh(canvas);
InspectorPopulate();
}
@ -1824,7 +1882,7 @@ void CanvasDrawLayerFromData(UIPainter *painter, UIRectangle bounds, EsBuffer da
data.bytes = data.position;
data.position = 0;
ThemeDrawLayer(&themePainter, bounds, &data, 1 /* TODO preview scale */, UI_RECT_1(0) /* TODO opaqueRegion */);
ThemeDrawLayer(&themePainter, bounds, &data, canvas->zoom, UI_RECT_1(0) /* TODO opaqueRegion */);
}
void CanvasDrawColorSwatch(Object *object, UIRectangle bounds, UIPainter *painter) {
@ -1935,7 +1993,7 @@ void CanvasDrawStyle(Object *object, UIRectangle bounds, UIPainter *painter, int
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"));
textStyle.size = GraphGetIntegerFromProperty(PropertyFindOrInherit(false, object, "textSize")) * canvas->zoom;
textStyle.color = GraphGetColorFromProperty(PropertyFindOrInherit(false, object, "textColor"));
EsDrawTextSimple((_EsPainter *) &themePainter, ui.instance->window, bounds, "Sample", -1, textStyle, ES_TEXT_H_CENTER | ES_TEXT_V_CENTER);
} else if (object->type == OBJ_VAR_ICON_STYLE) {
@ -2078,7 +2136,9 @@ int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) {
CanvasDrawStyle(object, bounds, painter);
} else if (object->type == OBJ_INSTANCE) {
Property *style = PropertyFind(object, "style", PROP_OBJECT);
canvas->previewStateActive = object->id == selectedObjectID;
CanvasDrawStyle(ObjectFind(style ? style->object : 0), bounds, painter);
canvas->previewStateActive = false;
} else {
// TODO Preview for the metrics layer. Show the preferred size, insets and gaps?
}
@ -2234,11 +2294,24 @@ int CanvasMessage(UIElement *element, UIMessage message, int di, void *dp) {
} else if (message == UI_MSG_MIDDLE_UP) {
_UIWindowSetCursor(element->window, UI_CURSOR_ARROW);
} else if (message == UI_MSG_MOUSE_DRAG && element->window->pressedButton == 2) {
canvas->panX -= element->window->cursorX - canvas->lastPanPointX;
canvas->panY -= element->window->cursorY - canvas->lastPanPointY;
canvas->panX -= (element->window->cursorX - canvas->lastPanPointX) / canvas->zoom;
canvas->panY -= (element->window->cursorY - canvas->lastPanPointY) / canvas->zoom;
canvas->lastPanPointX = element->window->cursorX;
canvas->lastPanPointY = element->window->cursorY;
UIElementRefresh(canvas);
} else if (message == UI_MSG_MOUSE_WHEEL && element->window->ctrl) {
int divisions = -di / 72;
float factor = 1, perDivision = 1.2f;
while (divisions > 0) factor *= perDivision, divisions--;
while (divisions < 0) factor /= perDivision, divisions++;
if (canvas->zoom * factor > 4) factor = 4 / canvas->zoom;
if (canvas->zoom * factor < 1) factor = 1 / canvas->zoom;
int mx = element->window->cursorX - element->bounds.l;
int my = element->window->cursorY - element->bounds.t;
canvas->zoom *= factor;
canvas->panX -= mx / canvas->zoom * (1 - factor);
canvas->panY -= my / canvas->zoom * (1 - factor);
UIElementRefresh(canvas);
} else if (message == UI_MSG_LAYOUT) {
UIElement *controls = canvas->showPrototype ? &prototypeControls->e : &graphControls->e;
(canvas->showPrototype ? &graphControls->e : &prototypeControls->e)->flags |= UI_ELEMENT_HIDE;
@ -2279,23 +2352,15 @@ void CanvasToggleArrows(void *) {
}
void CanvasSwitchView(void *) {
float z = canvas->swapZoom, x = canvas->swapPanX, y = canvas->swapPanY;
canvas->swapZoom = canvas->zoom, canvas->swapPanX = canvas->panX, canvas->swapPanY = canvas->panY;
canvas->zoom = z, canvas->panX = x, canvas->panY = y;
canvas->showPrototype = !canvas->showPrototype;
UIElementRefresh(canvas);
}
//////////////////////////////////////////////////////////////
int PrototypeMessage(UIElement *element, UIMessage message, int di, void *dp) {
if (message == UI_MSG_PAINT) {
UIPainter *painter = (UIPainter *) dp;
UIDrawBlock(painter, element->bounds, 0xFFC0C0C0);
}
return 0;
}
//////////////////////////////////////////////////////////////
void ObjectAddInternal(Object object) {
Step step = {};
step.type = STEP_ADD_OBJECT;
@ -2493,6 +2558,8 @@ int main() {
canvas->resizeHandles[1]->cp = (void *) (uintptr_t) 1;
canvas->resizeHandles[2]->cp = (void *) (uintptr_t) 2;
canvas->resizeHandles[3]->cp = (void *) (uintptr_t) 3;
canvas->zoom = canvas->swapZoom = 1.0f;
canvas->previewPrimaryState = THEME_PRIMARY_STATE_IDLE;
UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('Z'), 1 /* ctrl */, 0, 0, DocumentUndoStep, 0));
UIWindowRegisterShortcut(window, UI_SHORTCUT(UI_KEYCODE_LETTER('Y'), 1 /* ctrl */, 0, 0, DocumentRedoStep, 0));