diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp index 02a2a89..9ad7488 100644 --- a/desktop/desktop.cpp +++ b/desktop/desktop.cpp @@ -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++) { diff --git a/kernel/graphics.cpp b/kernel/graphics.cpp index a599378..256469f 100644 --- a/kernel/graphics.cpp +++ b/kernel/graphics.cpp @@ -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; diff --git a/util/designer2.cpp b/util/designer2.cpp index 713af05..27e0d0d 100644 --- a/util/designer2.cpp +++ b/util/designer2.cpp @@ -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 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));