1 /** 2 Dplug-Wren stdlib. This API is accessed from Wren with `import "ui"`. 3 4 Copyright: Guillaume Piolat 2021. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 module dplug.wren.wren_ui; 8 9 import core.stdc.string : strcmp; 10 import wren.vm; 11 import wren.common; 12 13 import dplug.gui.element; 14 import dplug.wren.wrensupport; 15 import dplug.wren.describe; 16 17 18 private static immutable string uiModuleSource = import("ui.wren"); 19 20 nothrow @nogc: 21 22 // UI 23 24 void ui_width(WrenVM* vm) 25 { 26 WrenSupport ws = cast(WrenSupport) vm.config.userData; 27 IUIContext context = ws.uiContext(); 28 vec2i userSize = context.getUISizeInPixelsUser(); 29 wrenSetSlotDouble(vm, 0, cast(double)(userSize.x)); 30 } 31 32 void ui_height(WrenVM* vm) 33 { 34 WrenSupport ws = cast(WrenSupport) vm.config.userData; 35 IUIContext context = ws.uiContext(); 36 vec2i userSize = context.getUISizeInPixelsUser(); 37 wrenSetSlotDouble(vm, 0, cast(double)(userSize.y)); 38 } 39 40 void ui_defaultWidth(WrenVM* vm) 41 { 42 WrenSupport ws = cast(WrenSupport) vm.config.userData; 43 IUIContext context = ws.uiContext(); 44 wrenSetSlotDouble(vm, 0, context.getDefaultUIWidth()); 45 } 46 47 void ui_defaultHeight(WrenVM* vm) 48 { 49 WrenSupport ws = cast(WrenSupport) vm.config.userData; 50 IUIContext context = ws.uiContext(); 51 wrenSetSlotDouble(vm, 0, context.getDefaultUIHeight()); 52 } 53 54 // Elements 55 56 void element_allocate(WrenVM* vm) 57 { 58 UIElementBridge* bridge = cast(UIElementBridge*)wrenSetSlotNewForeign(vm, 0, 0, UIElementBridge.sizeof); 59 60 WrenSupport ws = cast(WrenSupport) vm.config.userData; 61 IUIContext context = ws.uiContext(); 62 bridge.elem = null; 63 } 64 65 void element_findIdAndBecomeThat(WrenVM* vm) 66 { 67 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 68 const(char)* id = wrenGetSlotString(vm, 1); 69 70 WrenSupport ws = cast(WrenSupport) vm.config.userData; 71 IUIContext context = ws.uiContext(); 72 bridge.elem = context.getElementById(id); // Note: could be null 73 } 74 75 void element_width(WrenVM* vm) 76 { 77 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 78 double width = bridge.elem.position.width; 79 wrenSetSlotDouble(vm, 0, width); 80 } 81 82 void element_height(WrenVM* vm) 83 { 84 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 85 double height = bridge.elem.position.height; 86 wrenSetSlotDouble(vm, 0, height); 87 } 88 89 void element_setposition(WrenVM* vm) 90 { 91 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 92 assert(bridge.elem); // TODO error 93 94 // Avoid crash if passed Rectangle.new(false, 1, 4, 4), just do nothing in that case 95 if ( wrenGetSlotType(vm, 1) != WrenType.WREN_TYPE_NUM ) 96 return; 97 if ( wrenGetSlotType(vm, 2) != WrenType.WREN_TYPE_NUM ) 98 return; 99 if ( wrenGetSlotType(vm, 3) != WrenType.WREN_TYPE_NUM ) 100 return; 101 if ( wrenGetSlotType(vm, 4) != WrenType.WREN_TYPE_NUM ) 102 return; 103 104 double x = wrenGetSlotDouble(vm, 1); 105 double y = wrenGetSlotDouble(vm, 2); 106 double w = wrenGetSlotDouble(vm, 3); 107 double h = wrenGetSlotDouble(vm, 4); 108 bridge.elem.position = box2i.rectangle(cast(int)x, cast(int)y, cast(int)w, cast(int)h); 109 } 110 111 void element_setvisibility(WrenVM* vm) 112 { 113 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 114 assert(bridge.elem); // TODO error 115 116 // If not passed a bool, do nothing 117 if (wrenGetSlotType(vm, 1) == WrenType.WREN_TYPE_BOOL) 118 { 119 bool visibleFlag = wrenGetSlotBool(vm, 1); 120 bridge.elem.visibility = visibleFlag; 121 } 122 } 123 124 void element_setzorder(WrenVM* vm) 125 { 126 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 127 assert(bridge.elem); // TODO error 128 129 // If not passed a Num, do nothing 130 if (wrenGetSlotType(vm, 1) == WrenType.WREN_TYPE_NUM) 131 { 132 double z = wrenGetSlotDouble(vm, 1); 133 134 if (z > int.max) 135 return; 136 if (z < int.min) 137 return; 138 139 // Just truncate => fractional zOrder does nothing. 140 int iz = cast(int)z; 141 142 bridge.elem.zOrder = iz; 143 } 144 } 145 146 void element_setProperty(WrenVM* vm) 147 { 148 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 149 assert(bridge.elem); // TODO error 150 151 int classIndex = cast(int) wrenGetSlotDouble(vm, 1); 152 int propIndex = cast(int) wrenGetSlotDouble(vm, 2); 153 WrenSupport ws = cast(WrenSupport) vm.config.userData; 154 ScriptPropertyDesc* desc = ws.getScriptProperty(classIndex, propIndex); 155 assert(desc !is null); 156 157 ubyte* raw = cast(ubyte*)(cast(void*)bridge.elem) + desc.offset; 158 159 bool changed = false; 160 161 // Note: we check the property type, if it's the wrong type then nothing happens. Styling failure is not an error. 162 // Visual error like this are silent but will have a visual effect. 163 WrenType slot3_t = wrenGetSlotType(vm, 3); 164 165 final switch(desc.type) 166 { 167 case ScriptPropertyType.bool_: 168 { 169 if (slot3_t != WrenType.WREN_TYPE_BOOL) 170 return; 171 bool* valuePtr = cast(bool*)raw; 172 bool current = *valuePtr; 173 bool newValue = wrenGetSlotBool(vm, 3); 174 changed = newValue != current; 175 *valuePtr = newValue; 176 break; 177 } 178 case ScriptPropertyType.byte_: 179 { 180 if (slot3_t != WrenType.WREN_TYPE_NUM) 181 return; 182 byte* valuePtr = cast(byte*)raw; 183 byte current = *valuePtr; 184 byte newValue = cast(byte) wrenGetSlotDouble(vm, 3); 185 changed = newValue != current; 186 *valuePtr = newValue; 187 break; 188 } 189 case ScriptPropertyType.ubyte_: 190 { 191 if (slot3_t != WrenType.WREN_TYPE_NUM) 192 return; 193 ubyte* valuePtr = cast(ubyte*)raw; 194 ubyte current = *valuePtr; 195 ubyte newValue = cast(ubyte) wrenGetSlotDouble(vm, 3); 196 changed = newValue != current; 197 *valuePtr = newValue; 198 break; 199 } 200 case ScriptPropertyType.short_: 201 { 202 if (slot3_t != WrenType.WREN_TYPE_NUM) 203 return; 204 short* valuePtr = cast(short*)raw; 205 short current = *valuePtr; 206 short newValue = cast(short) wrenGetSlotDouble(vm, 3); 207 changed = newValue != current; 208 *valuePtr = newValue; 209 break; 210 } 211 case ScriptPropertyType.ushort_: 212 { 213 if (slot3_t != WrenType.WREN_TYPE_NUM) 214 return; 215 ushort* valuePtr = cast(ushort*)raw; 216 ushort current = *valuePtr; 217 ushort newValue = cast(ushort) wrenGetSlotDouble(vm, 3); 218 changed = newValue != current; 219 *valuePtr = newValue; 220 break; 221 } 222 case ScriptPropertyType.int_: 223 { 224 if (slot3_t != WrenType.WREN_TYPE_NUM) 225 return; 226 int* valuePtr = cast(int*)raw; 227 int current = *valuePtr; 228 int newValue = cast(int) wrenGetSlotDouble(vm, 3); 229 changed = newValue != current; 230 *valuePtr = newValue; 231 break; 232 } 233 case ScriptPropertyType.uint_: 234 { 235 if (slot3_t != WrenType.WREN_TYPE_NUM) 236 return; 237 uint* valuePtr = cast(uint*)raw; 238 uint current = *valuePtr; 239 uint newValue = cast(uint) wrenGetSlotDouble(vm, 3); 240 changed = newValue != current; 241 *valuePtr = newValue; 242 break; 243 } 244 case ScriptPropertyType.float_: 245 { 246 if (slot3_t != WrenType.WREN_TYPE_NUM) 247 return; 248 float* valuePtr = cast(float*)raw; 249 float current = *valuePtr; 250 float newValue = cast(float) wrenGetSlotDouble(vm, 3); 251 changed = !(newValue == current); // so that NaN don't provoke redraw 252 *valuePtr = newValue; 253 break; 254 } 255 case ScriptPropertyType.double_: 256 { 257 if (slot3_t != WrenType.WREN_TYPE_NUM) 258 return; 259 double* valuePtr = cast(double*)raw; 260 double current = *valuePtr; 261 double newValue = wrenGetSlotDouble(vm, 3); 262 changed = !(newValue == current); // so that NaN don't provoke redraw 263 *valuePtr = newValue; 264 break; 265 } 266 case ScriptPropertyType.RGBA: assert(false); 267 } 268 269 // Changing a @ScriptProperty calls setDirtyWhole on the UIElement if the property changed 270 if (changed) 271 bridge.elem.setDirtyWhole(); 272 } 273 274 void element_setPropertyRGBA(WrenVM* vm) 275 { 276 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 277 assert(bridge.elem); // TODO error 278 279 int classIndex = cast(int) wrenGetSlotDouble(vm, 1); 280 int propIndex = cast(int) wrenGetSlotDouble(vm, 2); 281 WrenSupport ws = cast(WrenSupport) vm.config.userData; 282 ScriptPropertyDesc* desc = ws.getScriptProperty(classIndex, propIndex); 283 assert(desc !is null); 284 assert(desc.type == ScriptPropertyType.RGBA); 285 286 ubyte* raw = cast(ubyte*)(cast(void*)bridge.elem) + desc.offset; 287 RGBA* pRGBA = cast(RGBA*)(raw); 288 289 // check for right type, else ignore line 290 if (wrenGetSlotType(vm, 3) != WrenType.WREN_TYPE_NUM) 291 return; 292 if (wrenGetSlotType(vm, 4) != WrenType.WREN_TYPE_NUM) 293 return; 294 if (wrenGetSlotType(vm, 5) != WrenType.WREN_TYPE_NUM) 295 return; 296 if (wrenGetSlotType(vm, 6) != WrenType.WREN_TYPE_NUM) 297 return; 298 299 double r = wrenGetSlotDouble(vm, 3); 300 double g = wrenGetSlotDouble(vm, 4); 301 double b = wrenGetSlotDouble(vm, 5); 302 double a = wrenGetSlotDouble(vm, 6); 303 if (r < 0) r = 0; 304 if (g < 0) g = 0; 305 if (b < 0) b = 0; 306 if (a < 0) a = 0; 307 if (r > 255) r = 255; 308 if (g > 255) g = 255; 309 if (b > 255) b = 255; 310 if (a > 255) a = 255; 311 RGBA newColor = RGBA(cast(ubyte)r, cast(ubyte)g, cast(ubyte)b, cast(ubyte)a); 312 bool changed = newColor != *pRGBA; 313 *pRGBA = newColor; 314 315 // Changing a @ScriptProperty calls setDirtyWhole on the UIElement if the property changed 316 if (changed) 317 bridge.elem.setDirtyWhole(); 318 } 319 320 void element_getProperty(WrenVM* vm) 321 { 322 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 323 assert(bridge.elem); // TODO error 324 325 int classIndex = cast(int) wrenGetSlotDouble(vm, 1); 326 int propIndex = cast(int) wrenGetSlotDouble(vm, 2); 327 WrenSupport ws = cast(WrenSupport) vm.config.userData; 328 ScriptPropertyDesc* desc = ws.getScriptProperty(classIndex, propIndex); 329 assert(desc !is null); 330 331 ubyte* raw = cast(ubyte*)(cast(void*)bridge.elem) + desc.offset; 332 333 final switch(desc.type) 334 { 335 case ScriptPropertyType.bool_: wrenSetSlotBool(vm, 0, *cast(bool*)raw); break; 336 case ScriptPropertyType.byte_: wrenSetSlotDouble(vm, 0, *cast(byte*)raw); break; 337 case ScriptPropertyType.ubyte_: wrenSetSlotDouble(vm, 0, *cast(ubyte*)raw); break; 338 case ScriptPropertyType.short_: wrenSetSlotDouble(vm, 0, *cast(short*)raw); break; 339 case ScriptPropertyType.ushort_: wrenSetSlotDouble(vm, 0, *cast(ushort*)raw); break; 340 case ScriptPropertyType.int_: wrenSetSlotDouble(vm, 0, *cast(int*)raw); break; 341 case ScriptPropertyType.uint_: wrenSetSlotDouble(vm, 0, *cast(uint*)raw); break; 342 case ScriptPropertyType.float_: 343 { 344 float f = *cast(float*)raw; 345 wrenSetSlotDouble(vm, 0, f); break; 346 } 347 case ScriptPropertyType.double_: wrenSetSlotDouble(vm, 0, *cast(double*)raw); break; 348 case ScriptPropertyType.RGBA: assert(false); 349 } 350 } 351 352 void element_getPropertyRGBA(WrenVM* vm) 353 { 354 UIElementBridge* bridge = cast(UIElementBridge*) wrenGetSlotForeign(vm, 0); 355 assert(bridge.elem); // TODO error 356 357 int classIndex = cast(int) wrenGetSlotDouble(vm, 1); 358 int propIndex = cast(int) wrenGetSlotDouble(vm, 2); 359 WrenSupport ws = cast(WrenSupport) vm.config.userData; 360 ScriptPropertyDesc* desc = ws.getScriptProperty(classIndex, propIndex); 361 assert(desc !is null); 362 363 ubyte* raw = cast(ubyte*)(cast(void*)bridge.elem) + desc.offset; 364 int channel = cast(int) wrenGetSlotDouble(vm, 3); 365 assert(channel >= 0 && channel < 4); 366 wrenSetSlotDouble(vm, 0, raw[channel]); 367 } 368 369 struct UIElementBridge 370 { 371 UIElement elem; 372 } 373 374 const(char)* wrenUIModuleSource() 375 { 376 return assumeZeroTerminated(uiModuleSource); 377 } 378 379 WrenForeignMethodFn wrenUIBindForeignMethod(WrenVM* vm, const(char)* className, bool isStatic, const(char)* signature) nothrow 380 { 381 if (strcmp(className, "UI") == 0) 382 { 383 if (isStatic && strcmp(signature, "width") == 0) return &ui_width; 384 if (isStatic && strcmp(signature, "height") == 0) return &ui_height; 385 if (isStatic && strcmp(signature, "defaultWidth") == 0) return &ui_defaultWidth; 386 if (isStatic && strcmp(signature, "defaultHeight") == 0) return &ui_defaultHeight; 387 } 388 389 if (strcmp(className, "Element") == 0) 390 { 391 if (strcmp(signature, "<allocate>") == 0) return &element_allocate; 392 if (strcmp(signature, "width") == 0) return &element_width; 393 if (strcmp(signature, "height") == 0) return &element_height; 394 if (strcmp(signature, "setPosition_(_,_,_,_)") == 0) return &element_setposition; 395 if (strcmp(signature, "setVisibility_(_)") == 0) return &element_setvisibility; 396 if (strcmp(signature, "setZOrder_(_)") == 0) return &element_setzorder; 397 if (strcmp(signature, "findIdAndBecomeThat_(_)") == 0) return &element_findIdAndBecomeThat; 398 if (strcmp(signature, "setProp_(_,_,_)") == 0) return &element_setProperty; 399 if (strcmp(signature, "setPropRGBA_(_,_,_,_,_,_)") == 0) return &element_setPropertyRGBA; 400 if (strcmp(signature, "getProp_(_,_)") == 0) return &element_getProperty; 401 if (strcmp(signature, "getPropRGBA_(_,_,_)") == 0) return &element_getPropertyRGBA; 402 } 403 return null; 404 } 405 406 WrenForeignClassMethods wrenUIForeignClass(WrenVM* vm, const(char)* className) nothrow 407 { 408 WrenForeignClassMethods methods; 409 methods.allocate = null; 410 methods.finalize = null; 411 412 if (strcmp(className, "Element") == 0) 413 { 414 methods.allocate = &element_allocate; 415 } 416 return methods; 417 }